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

Version Description

  • Feat: Show creator user name of staging site
  • Enh: Show notice if sending mails are disabled
  • Enh: Show message and stop execution if php version is lower than 5.5
  • Enh: Abort cloning process if table already exists in external database
  • Fix: Can not update database credentials in staging sites wp-config.php under rare circumstances
  • Fix: During the update process if options table was not selected it didn't get skipped
  • Fix: Error if WP is lower than 4.6
  • Fix: Can not delete entire staging site on error
  • Fix: Activating pro version does not properly disable free version
Download this release

Release Info

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

Code changes from version 2.8.0 to 2.8.1

Files changed (98) hide show
  1. Backend/Administrator.php +99 -132
  2. Backend/Modules/Jobs/Cleaners/WpContentCleaner.php +136 -0
  3. Backend/Modules/Jobs/Cloning.php +12 -3
  4. Backend/Modules/Jobs/Database.php +17 -2
  5. Backend/Modules/Jobs/Delete.php +12 -5
  6. Backend/Modules/Jobs/Files.php +74 -15
  7. Backend/Modules/Jobs/Finish.php +2 -2
  8. Backend/Modules/Jobs/Job.php +8 -2
  9. Backend/Modules/Jobs/Multisite/Files.php +66 -42
  10. Backend/Modules/Jobs/Multisite/Finish.php +4 -3
  11. Backend/Modules/Jobs/Updating.php +15 -1
  12. Backend/Notices/BooleanNotice.php +48 -0
  13. Backend/Notices/DisabledCacheNotice.php +4 -27
  14. Backend/Notices/Notices.php +56 -2
  15. Backend/Optimizer/Optimizer.php +3 -1
  16. Backend/Optimizer/wp-staging-optimizer.php +12 -12
  17. Backend/Upgrade/Upgrade.php +0 -6
  18. Backend/public/css/wpstg-admin.css +34 -14
  19. Backend/public/js/wpstg-admin-beta.js +1 -1
  20. Backend/public/js/wpstg-admin-poll.js +1 -1
  21. Backend/public/js/wpstg-admin.js +27 -7
  22. Backend/views/clone/ajax/external-database.php +26 -15
  23. Backend/views/clone/ajax/mail-setting.php +13 -14
  24. Backend/views/clone/ajax/scan.php +19 -3
  25. Backend/views/clone/ajax/single-overview.php +12 -3
  26. Backend/views/clone/ajax/start.php +6 -0
  27. Backend/views/clone/ajax/update.php +6 -0
  28. Backend/views/notices/directory-listing-could-not-be-prevented.php +14 -0
  29. Backend/views/settings/main-settings.php +33 -427
  30. Backend/views/settings/tabs/general.php +401 -0
  31. Backend/views/settings/tabs/mail-settings.php +20 -0
  32. Backend/views/tools/index.php +28 -28
  33. Bootstrap/V1/Requirements/WpstgFreeRequirements.php +0 -183
  34. Bootstrap/V1/Requirements/WpstgProRequirements.php +0 -40
  35. Bootstrap/V1/Requirements/WpstgRequirements.php +0 -36
  36. Bootstrap/V1/WpstgBootstrap.php +0 -61
  37. Component/Job/AbstractJob.php +0 -3
  38. Component/Job/AbstractQueueJob.php +30 -44
  39. Component/Job/QueueJobDto.php +2 -2
  40. Component/Task/Filesystem/DirectoryScannerRequestDto.php +6 -6
  41. Component/Task/Filesystem/DirectoryScannerTask.php +29 -22
  42. Component/Task/Filesystem/FileScannerRequestDto.php +22 -7
  43. Component/Task/Filesystem/FileScannerTask.php +20 -19
  44. Core/Cron/Cron.php +10 -5
  45. Core/Utils/Cache.php +10 -10
  46. Core/Utils/Filesystem.php +0 -56
  47. Core/Utils/Htaccess.php +1 -1
  48. Core/Utils/IISWebConfig.php +1 -1
  49. Core/Utils/Logger.php +7 -12
  50. Core/Utils/RobotsTxt.php +1 -1
  51. Core/Utils/functions.php +0 -61
  52. Core/Utils/requirements-check.php +0 -211
  53. Core/WPStaging.php +67 -20
  54. Entity/DatabaseSettings.php +0 -54
  55. Entity/FilesystemSettings.php +0 -93
  56. Entity/Settings.php +0 -289
  57. Framework/Adapter/Directory.php +5 -12
  58. Framework/CloningProcess/Data/DBCloningService.php +12 -8
  59. Framework/CloningProcess/Data/UpdateStagingOptionsTable.php +1 -1
  60. Framework/CloningProcess/Data/UpdateWpConfigTablePrefix.php +3 -2
  61. Framework/CloningProcess/Database/DatabaseCloningService.php +14 -1
  62. Framework/DI/Container.php +48 -0
  63. Framework/Filesystem/DirectoryListing.php +172 -0
  64. Framework/Filesystem/DirectoryScanner.php +44 -117
  65. Framework/Filesystem/DirectoryScannerControl.php +146 -0
  66. Framework/Filesystem/DirectoryService.php +0 -65
  67. Framework/Filesystem/FileScanner.php +73 -105
  68. Framework/Filesystem/FileScannerControl.php +126 -0
  69. Framework/Filesystem/FileService.php +0 -115
  70. Framework/Filesystem/Filesystem.php +354 -16
  71. Framework/Filesystem/Permissions.php +35 -0
  72. Framework/Filesystem/WpUploadsFolderSymlinker.php +3 -20
  73. Framework/Mails/Report/Report.php +10 -3
  74. Framework/Security/Auth.php +67 -0
  75. Framework/Staging/FirstRun.php +12 -2
  76. Framework/Traits/BenchmarkTrait.php +42 -0
  77. Framework/Traits/HydrateTrait.php +11 -2
  78. Framework/Traits/ResourceTrait.php +36 -39
  79. Framework/Traits/TimerTrait.php +1 -4
  80. Framework/Utils/Cache/AbstractCache.php +4 -1
  81. Framework/Utils/Cache/Cache.php +0 -1
  82. Framework/Utils/FileSystem.php +0 -57
  83. Framework/Utils/WpDefaultDirectories.php +44 -2
  84. Repository/SettingsRepository.php +0 -43
  85. _init.php +0 -57
  86. Bootstrap/autoloader.php → autoloader.php +3 -7
  87. bootstrap.php +73 -0
  88. constants.php +0 -25
  89. constantsFree.php +10 -0
  90. freeBootstrap.php +59 -0
  91. install.php +25 -71
  92. readme.txt +36 -23
  93. runtimeRequirements.php +79 -0
  94. template/.gitkeep +0 -0
  95. template/Component/Backend/Snapshot/listing.php +0 -102
  96. uninstall.php +1 -0
  97. vendor_wpstg/autoload/src.php +8 -9
  98. wp-staging.php +15 -46
Backend/Administrator.php CHANGED
@@ -7,10 +7,13 @@ if (!defined("WPINC")) {
7
  die;
8
  }
9
 
 
10
  use WPStaging\Framework\Database\DbInfo;
 
11
  use WPStaging\Framework\Security\AccessToken;
12
  use WPStaging\Framework\Security\Capabilities;
13
- use WPStaging\Core\WPStaging;
 
14
  use WPStaging\Backend\Modules\Jobs\Cancel;
15
  use WPStaging\Backend\Modules\Jobs\CancelUpdate;
16
  use WPStaging\Backend\Modules\Jobs\Cloning;
@@ -27,9 +30,8 @@ use WPStaging\Backend\Modules\Views\Forms\Settings as FormSettings;
27
  use WPStaging\Backend\Activation;
28
  use WPStaging\Backend\Feedback;
29
  use WPStaging\Backend\Pro\Modules\Jobs\Processing;
 
30
  use WPStaging\Pro\Database\CompareExternalDatabase;
31
- use WPStaging\Framework\Mails\Report\Report;
32
- use WPStaging\Framework\Security\Nonce;
33
 
34
  /**
35
  * Class Administrator
@@ -60,19 +62,9 @@ class Administrator
60
  private $url;
61
 
62
  /**
63
- * @var AccessToken
64
- */
65
- private $accessToken;
66
-
67
- /**
68
- * @var Capabilities
69
  */
70
- private $capabilities;
71
-
72
- /**
73
- * @var Nonce
74
- */
75
- private $nonce;
76
 
77
  /**
78
  * @var array
@@ -86,9 +78,7 @@ class Administrator
86
  public function __construct()
87
  {
88
  // Todo: Inject using DI
89
- $this->accessToken = new AccessToken;
90
- $this->capabilities = new Capabilities;
91
- $this->nonce = new Nonce;
92
 
93
  $this->defineHooks();
94
 
@@ -107,7 +97,7 @@ class Administrator
107
  */
108
  public function loadMeta()
109
  {
110
- $run = new \WPStaging\Backend\Pluginmeta\Pluginmeta();
111
  }
112
 
113
  /**
@@ -305,18 +295,15 @@ class Administrator
305
  public function getSettingsPage()
306
  {
307
  // Tabs
308
- $tabs = new Tabs([
309
  "general" => __("General", "wp-staging")
310
- ]);
311
-
312
 
313
  WPStaging::getInstance()
314
  // Set tabs
315
  ->set("tabs", $tabs)
316
  // Forms
317
- ->set("forms", new FormSettings($tabs))
318
- ;
319
-
320
 
321
  require_once "{$this->path}views/settings/main-settings.php";
322
  }
@@ -435,7 +422,7 @@ class Administrator
435
  ignore_user_abort(true);
436
 
437
  // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
438
- if (!in_array("set_time_limit", explode(',', ini_get("disable_functions"))) && !@ini_get("safe_mode")) {
439
  set_time_limit(0);
440
  }
441
 
@@ -484,15 +471,9 @@ class Administrator
484
  /**
485
  * @return bool Whether the current request is considered to be authenticated.
486
  */
487
- private function isAuthenticated() {
488
- if (
489
- current_user_can($this->capabilities->manageWPSTG()) &&
490
- $this->nonce->requestHasValidNonce(Nonce::WPSTG_NONCE)
491
- ) {
492
- return true;
493
- } else {
494
- return $this->accessToken->requestHasValidToken();
495
- }
496
  }
497
 
498
  /**
@@ -500,8 +481,8 @@ class Administrator
500
  */
501
  public function ajaxRestart()
502
  {
503
- if ( ! $this->isAuthenticated()) {
504
- return false;
505
  }
506
 
507
  $process = new ProcessLock();
@@ -513,8 +494,8 @@ class Administrator
513
  */
514
  public function ajaxOverview()
515
  {
516
- if ( ! $this->isAuthenticated()) {
517
- return false;
518
  }
519
 
520
  // Existing clones
@@ -538,8 +519,8 @@ class Administrator
538
  */
539
  public function ajaxCloneScan()
540
  {
541
- if ( ! $this->isAuthenticated()) {
542
- return false;
543
  }
544
 
545
  // Check first if there is already a process running
@@ -565,8 +546,8 @@ class Administrator
565
  */
566
  public function ajaxCheckCloneName()
567
  {
568
- if ( ! $this->isAuthenticated()) {
569
- return false;
570
  }
571
 
572
  $cloneName = sanitize_key($_POST["cloneID"]);
@@ -601,14 +582,14 @@ class Administrator
601
  */
602
  public function ajaxUpdateProcess()
603
  {
604
- if ( ! $this->isAuthenticated()) {
605
- return false;
606
  }
607
 
608
  $cloning = new Updating();
609
 
610
  if (!$cloning->save()) {
611
- wp_die('can not save clone data');
612
  }
613
 
614
  require_once "{$this->path}views/clone/ajax/update.php";
@@ -622,7 +603,7 @@ class Administrator
622
  public function ajaxStartClone()
623
  {
624
  if (!$this->isAuthenticated()) {
625
- return false;
626
  }
627
 
628
  // Check first if there is already a process running
@@ -645,17 +626,11 @@ class Administrator
645
  */
646
  public function ajaxCloneDatabase()
647
  {
648
- if ( ! $this->isAuthenticated()) {
649
- return false;
650
  }
651
 
652
- $cloning = new Cloning();
653
-
654
- // Uncomment these lines to test different error codes
655
- //http_response_code(504);
656
- //wp_send_json( '<html><body><head></head><body>test</body></html>' );
657
-
658
- wp_send_json($cloning->start());
659
  }
660
 
661
  /**
@@ -663,13 +638,11 @@ class Administrator
663
  */
664
  public function ajaxPrepareDirectories()
665
  {
666
- if ( ! $this->isAuthenticated()) {
667
- return false;
668
  }
669
 
670
- $cloning = new Cloning();
671
-
672
- wp_send_json($cloning->start());
673
  }
674
 
675
  /**
@@ -677,13 +650,11 @@ class Administrator
677
  */
678
  public function ajaxCopyFiles()
679
  {
680
- if ( ! $this->isAuthenticated()) {
681
- return false;
682
  }
683
 
684
- $cloning = new Cloning();
685
-
686
- wp_send_json($cloning->start());
687
  }
688
 
689
  /**
@@ -691,13 +662,11 @@ class Administrator
691
  */
692
  public function ajaxReplaceData()
693
  {
694
- if ( ! $this->isAuthenticated()) {
695
- return false;
696
  }
697
 
698
- $cloning = new Cloning();
699
-
700
- wp_send_json($cloning->start());
701
  }
702
 
703
  /**
@@ -705,13 +674,11 @@ class Administrator
705
  */
706
  public function ajaxFinish()
707
  {
708
- if ( ! $this->isAuthenticated()) {
709
- return false;
710
  }
711
 
712
- $cloning = new Cloning();
713
-
714
- wp_send_json($cloning->start());
715
  }
716
 
717
  /**
@@ -720,10 +687,11 @@ class Administrator
720
  public function ajaxDeleteConfirmation()
721
  {
722
  if (!$this->isAuthenticated()) {
723
- return false;
724
  }
725
 
726
  $delete = new Delete();
 
727
  $isDatabaseConnected = $delete->setData();
728
 
729
  $clone = $delete->getClone();
@@ -740,13 +708,11 @@ class Administrator
740
  */
741
  public function ajaxDeleteClone()
742
  {
743
- if ( ! $this->isAuthenticated()) {
744
- return false;
745
  }
746
 
747
- $delete = new Delete();
748
-
749
- wp_send_json($delete->start());
750
  }
751
 
752
  /**
@@ -754,13 +720,11 @@ class Administrator
754
  */
755
  public function ajaxCancelClone()
756
  {
757
- if ( ! $this->isAuthenticated()) {
758
- return false;
759
  }
760
 
761
- $cancel = new Cancel();
762
-
763
- wp_send_json($cancel->start());
764
  }
765
 
766
  /**
@@ -768,12 +732,11 @@ class Administrator
768
  */
769
  public function ajaxCancelUpdate()
770
  {
771
- if ( ! $this->isAuthenticated()) {
772
- return false;
773
  }
774
 
775
- $cancel = new CancelUpdate();
776
- wp_send_json($cancel->start());
777
  }
778
 
779
  /**
@@ -781,45 +744,40 @@ class Administrator
781
  */
782
  public function messages()
783
  {
784
- $notice = new Notices($this->path, $this->url);
785
-
786
- $run = $notice->messages();
787
  }
788
 
789
  /**
790
  * Ajax Hide Poll
791
  * @todo check if this is being used, remove otherwise.
792
- * @return mixed boolean | json
793
  */
794
  public function ajaxHidePoll()
795
  {
796
  if (update_option("wpstg_poll", "no") !== false) {
797
  wp_send_json(true);
798
  }
799
- return wp_send_json(null);
 
800
  }
801
 
802
  /**
803
  * Ajax Hide Rating
804
  *
805
  * Runs when the user dismisses the notice to rate the plugin.
806
- *
807
- * @return mixed bool | json
808
  */
809
  public function ajaxHideRating()
810
  {
811
  if (update_option("wpstg_rating", "no") !== false) {
812
  wp_send_json(true);
813
  }
814
- return wp_send_json(null);
 
815
  }
816
 
817
  /**
818
  * Ajax Hide Rating and show it again after one week
819
  *
820
  * Runs when the user chooses to rate the plugin later.
821
- *
822
- * @return mixed bool | json
823
  */
824
  public function ajaxHideLaterRating()
825
  {
@@ -827,7 +785,8 @@ class Administrator
827
  if (update_option('wpstg_rating', $date) !== false) {
828
  wp_send_json(true);
829
  }
830
- return wp_send_json(false);
 
831
  }
832
 
833
  /**
@@ -840,14 +799,15 @@ class Administrator
840
 
841
  /**
842
  * Ajax Hide Cache Notice shown on staging site
843
- * @return mixed boolean | json
844
  */
845
  public function ajaxHideCacheNotice()
846
  {
 
847
  if ((new DisabledCacheNotice())->disable() !== false) {
848
  wp_send_json(true);
849
  }
850
- return wp_send_json(null);
 
851
  }
852
 
853
  /**
@@ -855,12 +815,11 @@ class Administrator
855
  */
856
  public function ajaxLogs()
857
  {
858
- if ( ! $this->isAuthenticated()) {
859
- return false;
860
  }
861
 
862
- $logs = new Logs();
863
- wp_send_json($logs->start());
864
  }
865
 
866
  /**
@@ -868,7 +827,7 @@ class Administrator
868
  */
869
  public function ajaxCheckFreeSpace()
870
  {
871
- if ( ! $this->isAuthenticated()) {
872
  return false;
873
  }
874
 
@@ -881,8 +840,8 @@ class Administrator
881
  */
882
  public function ajaxEditCloneData()
883
  {
884
- if ( ! $this->isAuthenticated()) {
885
- return false;
886
  }
887
 
888
  $listOfClones = get_option("wpstg_existing_clones_beta", []);
@@ -901,8 +860,8 @@ class Administrator
901
  */
902
  public function ajaxSaveCloneData()
903
  {
904
- if ( ! $this->isAuthenticated()) {
905
- return false;
906
  }
907
 
908
  $listOfClones = get_option("wpstg_existing_clones_beta", []);
@@ -913,21 +872,21 @@ class Administrator
913
  }
914
 
915
  // Use directory name as new array key to maintain existing structure
916
- $keys = array_keys($listOfClones);
917
  $keys[array_search($_POST["clone"], $keys)] = $_POST["directoryName"];
918
- $listOfClones = array_combine($keys, $listOfClones);
919
 
920
  $cloneId = $_POST["directoryName"];
921
 
922
- $listOfClones[$cloneId]["directoryName"] = stripslashes($_POST["directoryName"]);
923
- $listOfClones[$cloneId]["path"] = stripslashes($_POST["path"]);
924
- $listOfClones[$cloneId]["url"] = stripslashes($_POST["url"]);
925
- $listOfClones[$cloneId]["prefix"] = stripslashes($_POST["prefix"]);
926
- $listOfClones[$cloneId]["databaseUser"] = stripslashes($_POST["externalDBUser"]);
927
  $listOfClones[$cloneId]["databasePassword"] = stripslashes($_POST["externalDBPassword"]);
928
  $listOfClones[$cloneId]["databaseDatabase"] = stripslashes($_POST["externalDBDatabase"]);
929
- $listOfClones[$cloneId]["databaseServer"] = stripslashes($_POST["externalDBHost"]);
930
- $listOfClones[$cloneId]["databasePrefix"] = stripslashes($_POST["externalDBPrefix"]);
931
 
932
  update_option("wpstg_existing_clones_beta", $listOfClones);
933
 
@@ -947,7 +906,7 @@ class Administrator
947
  */
948
  public function ajaxPushScan()
949
  {
950
- if ( ! $this->isAuthenticated()) {
951
  return false;
952
  }
953
 
@@ -973,7 +932,7 @@ class Administrator
973
  */
974
  public function ajaxPushProcessing()
975
  {
976
- if ( ! $this->isAuthenticated()) {
977
  return false;
978
  }
979
 
@@ -982,8 +941,7 @@ class Administrator
982
  }
983
 
984
  // Start the process
985
- $processing = new Processing();
986
- wp_send_json($processing->start());
987
  }
988
 
989
  /**
@@ -991,7 +949,6 @@ class Administrator
991
  */
992
  public function getLicensePage()
993
  {
994
-
995
  // Get license data
996
  $license = get_option('wpstg_license_status');
997
 
@@ -1004,8 +961,8 @@ class Administrator
1004
  */
1005
  public function ajaxSendReport($args = [])
1006
  {
1007
- if ( ! $this->isAuthenticated()) {
1008
- return false;
1009
  }
1010
 
1011
  // Set params
@@ -1058,7 +1015,7 @@ class Administrator
1058
  public function ajaxDatabaseConnect()
1059
  {
1060
  if (!$this->isAuthenticated()) {
1061
- return false;
1062
  }
1063
 
1064
  $args = $_POST;
@@ -1066,15 +1023,25 @@ class Administrator
1066
  $password = !empty($args['databasePassword']) ? $args['databasePassword'] : '';
1067
  $database = !empty($args['databaseDatabase']) ? $args['databaseDatabase'] : '';
1068
  $server = !empty($args['databaseServer']) ? $args['databaseServer'] : 'localhost';
 
1069
 
1070
  $dbInfo = new DbInfo($server, $user, stripslashes($password), $database);
 
1071
  $error = $dbInfo->getError();
 
1072
  // Can not connect to mysql database
1073
  if ($error !== null) {
1074
  echo json_encode(['success' => 'false', 'errors' => $error]);
1075
  exit;
1076
  }
1077
 
 
 
 
 
 
 
 
1078
  echo json_encode(['success' => 'true']);
1079
  exit;
1080
  }
@@ -1085,11 +1052,11 @@ class Administrator
1085
  public function ajaxDatabaseVerification()
1086
  {
1087
  if (!$this->isAuthenticated()) {
1088
- return false;
1089
  }
1090
 
1091
  if (!$this->isPro()) {
1092
- return false;
1093
  }
1094
 
1095
  $user = !empty($_POST['databaseUser']) ? $_POST['databaseUser'] : '';
7
  die;
8
  }
9
 
10
+ use WPStaging\Core\WPStaging;
11
  use WPStaging\Framework\Database\DbInfo;
12
+ use WPStaging\Framework\Security\Auth;
13
  use WPStaging\Framework\Security\AccessToken;
14
  use WPStaging\Framework\Security\Capabilities;
15
+ use WPStaging\Framework\Security\Nonce;
16
+ use WPStaging\Framework\Mails\Report\Report;
17
  use WPStaging\Backend\Modules\Jobs\Cancel;
18
  use WPStaging\Backend\Modules\Jobs\CancelUpdate;
19
  use WPStaging\Backend\Modules\Jobs\Cloning;
30
  use WPStaging\Backend\Activation;
31
  use WPStaging\Backend\Feedback;
32
  use WPStaging\Backend\Pro\Modules\Jobs\Processing;
33
+ use WPStaging\Backend\Pluginmeta\Pluginmeta;
34
  use WPStaging\Pro\Database\CompareExternalDatabase;
 
 
35
 
36
  /**
37
  * Class Administrator
62
  private $url;
63
 
64
  /**
65
+ * @var Auth
 
 
 
 
 
66
  */
67
+ private $auth;
 
 
 
 
 
68
 
69
  /**
70
  * @var array
78
  public function __construct()
79
  {
80
  // Todo: Inject using DI
81
+ $this->auth = new Auth(new Capabilities, new AccessToken, new Nonce);
 
 
82
 
83
  $this->defineHooks();
84
 
97
  */
98
  public function loadMeta()
99
  {
100
+ $run = new Pluginmeta();
101
  }
102
 
103
  /**
295
  public function getSettingsPage()
296
  {
297
  // Tabs
298
+ $tabs = new Tabs(apply_filters('wpstg_main_settings_tabs', [
299
  "general" => __("General", "wp-staging")
300
+ ]));
 
301
 
302
  WPStaging::getInstance()
303
  // Set tabs
304
  ->set("tabs", $tabs)
305
  // Forms
306
+ ->set("forms", new FormSettings($tabs));
 
 
307
 
308
  require_once "{$this->path}views/settings/main-settings.php";
309
  }
422
  ignore_user_abort(true);
423
 
424
  // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
425
+ if (!in_array("set_time_limit", explode(',', ini_get("disable_functions")))) {
426
  set_time_limit(0);
427
  }
428
 
471
  /**
472
  * @return bool Whether the current request is considered to be authenticated.
473
  */
474
+ private function isAuthenticated()
475
+ {
476
+ return $this->auth->isValid();
 
 
 
 
 
 
477
  }
478
 
479
  /**
481
  */
482
  public function ajaxRestart()
483
  {
484
+ if (!$this->isAuthenticated()) {
485
+ return;
486
  }
487
 
488
  $process = new ProcessLock();
494
  */
495
  public function ajaxOverview()
496
  {
497
+ if (!$this->isAuthenticated()) {
498
+ return;
499
  }
500
 
501
  // Existing clones
519
  */
520
  public function ajaxCloneScan()
521
  {
522
+ if (!$this->isAuthenticated()) {
523
+ return;
524
  }
525
 
526
  // Check first if there is already a process running
546
  */
547
  public function ajaxCheckCloneName()
548
  {
549
+ if (!$this->isAuthenticated()) {
550
+ return;
551
  }
552
 
553
  $cloneName = sanitize_key($_POST["cloneID"]);
582
  */
583
  public function ajaxUpdateProcess()
584
  {
585
+ if (!$this->isAuthenticated()) {
586
+ return;
587
  }
588
 
589
  $cloning = new Updating();
590
 
591
  if (!$cloning->save()) {
592
+ wp_die('Can not save clone data');
593
  }
594
 
595
  require_once "{$this->path}views/clone/ajax/update.php";
603
  public function ajaxStartClone()
604
  {
605
  if (!$this->isAuthenticated()) {
606
+ return;
607
  }
608
 
609
  // Check first if there is already a process running
626
  */
627
  public function ajaxCloneDatabase()
628
  {
629
+ if (!$this->isAuthenticated()) {
630
+ return;
631
  }
632
 
633
+ wp_send_json((new Cloning)->start());
 
 
 
 
 
 
634
  }
635
 
636
  /**
638
  */
639
  public function ajaxPrepareDirectories()
640
  {
641
+ if (!$this->isAuthenticated()) {
642
+ return;
643
  }
644
 
645
+ wp_send_json((new Cloning)->start());
 
 
646
  }
647
 
648
  /**
650
  */
651
  public function ajaxCopyFiles()
652
  {
653
+ if (!$this->isAuthenticated()) {
654
+ return;
655
  }
656
 
657
+ wp_send_json((new Cloning)->start());
 
 
658
  }
659
 
660
  /**
662
  */
663
  public function ajaxReplaceData()
664
  {
665
+ if (!$this->isAuthenticated()) {
666
+ return;
667
  }
668
 
669
+ wp_send_json((new Cloning)->start());
 
 
670
  }
671
 
672
  /**
674
  */
675
  public function ajaxFinish()
676
  {
677
+ if (!$this->isAuthenticated()) {
678
+ return;
679
  }
680
 
681
+ wp_send_json((new Cloning)->start());
 
 
682
  }
683
 
684
  /**
687
  public function ajaxDeleteConfirmation()
688
  {
689
  if (!$this->isAuthenticated()) {
690
+ return;
691
  }
692
 
693
  $delete = new Delete();
694
+
695
  $isDatabaseConnected = $delete->setData();
696
 
697
  $clone = $delete->getClone();
708
  */
709
  public function ajaxDeleteClone()
710
  {
711
+ if (!$this->isAuthenticated()) {
712
+ return;
713
  }
714
 
715
+ wp_send_json((new Delete)->start());
 
 
716
  }
717
 
718
  /**
720
  */
721
  public function ajaxCancelClone()
722
  {
723
+ if (!$this->isAuthenticated()) {
724
+ return;
725
  }
726
 
727
+ wp_send_json((new Cancel)->start());
 
 
728
  }
729
 
730
  /**
732
  */
733
  public function ajaxCancelUpdate()
734
  {
735
+ if (!$this->isAuthenticated()) {
736
+ return;
737
  }
738
 
739
+ wp_send_json((new CancelUpdate)->start());
 
740
  }
741
 
742
  /**
744
  */
745
  public function messages()
746
  {
747
+ (new Notices($this->path, $this->url))->messages();
 
 
748
  }
749
 
750
  /**
751
  * Ajax Hide Poll
752
  * @todo check if this is being used, remove otherwise.
 
753
  */
754
  public function ajaxHidePoll()
755
  {
756
  if (update_option("wpstg_poll", "no") !== false) {
757
  wp_send_json(true);
758
  }
759
+
760
+ wp_send_json(null);
761
  }
762
 
763
  /**
764
  * Ajax Hide Rating
765
  *
766
  * Runs when the user dismisses the notice to rate the plugin.
 
 
767
  */
768
  public function ajaxHideRating()
769
  {
770
  if (update_option("wpstg_rating", "no") !== false) {
771
  wp_send_json(true);
772
  }
773
+
774
+ wp_send_json(null);
775
  }
776
 
777
  /**
778
  * Ajax Hide Rating and show it again after one week
779
  *
780
  * Runs when the user chooses to rate the plugin later.
 
 
781
  */
782
  public function ajaxHideLaterRating()
783
  {
785
  if (update_option('wpstg_rating', $date) !== false) {
786
  wp_send_json(true);
787
  }
788
+
789
+ wp_send_json(false);
790
  }
791
 
792
  /**
799
 
800
  /**
801
  * Ajax Hide Cache Notice shown on staging site
 
802
  */
803
  public function ajaxHideCacheNotice()
804
  {
805
+ // @todo inject with dependency injection
806
  if ((new DisabledCacheNotice())->disable() !== false) {
807
  wp_send_json(true);
808
  }
809
+
810
+ wp_send_json(null);
811
  }
812
 
813
  /**
815
  */
816
  public function ajaxLogs()
817
  {
818
+ if (!$this->isAuthenticated()) {
819
+ return;
820
  }
821
 
822
+ wp_send_json((new Logs)->start());
 
823
  }
824
 
825
  /**
827
  */
828
  public function ajaxCheckFreeSpace()
829
  {
830
+ if (!$this->isAuthenticated()) {
831
  return false;
832
  }
833
 
840
  */
841
  public function ajaxEditCloneData()
842
  {
843
+ if (!$this->isAuthenticated()) {
844
+ return;
845
  }
846
 
847
  $listOfClones = get_option("wpstg_existing_clones_beta", []);
860
  */
861
  public function ajaxSaveCloneData()
862
  {
863
+ if (!$this->isAuthenticated()) {
864
+ return;
865
  }
866
 
867
  $listOfClones = get_option("wpstg_existing_clones_beta", []);
872
  }
873
 
874
  // Use directory name as new array key to maintain existing structure
875
+ $keys = array_keys($listOfClones);
876
  $keys[array_search($_POST["clone"], $keys)] = $_POST["directoryName"];
877
+ $listOfClones = array_combine($keys, $listOfClones);
878
 
879
  $cloneId = $_POST["directoryName"];
880
 
881
+ $listOfClones[$cloneId]["directoryName"] = stripslashes($_POST["directoryName"]);
882
+ $listOfClones[$cloneId]["path"] = stripslashes($_POST["path"]);
883
+ $listOfClones[$cloneId]["url"] = stripslashes($_POST["url"]);
884
+ $listOfClones[$cloneId]["prefix"] = stripslashes($_POST["prefix"]);
885
+ $listOfClones[$cloneId]["databaseUser"] = stripslashes($_POST["externalDBUser"]);
886
  $listOfClones[$cloneId]["databasePassword"] = stripslashes($_POST["externalDBPassword"]);
887
  $listOfClones[$cloneId]["databaseDatabase"] = stripslashes($_POST["externalDBDatabase"]);
888
+ $listOfClones[$cloneId]["databaseServer"] = stripslashes($_POST["externalDBHost"]);
889
+ $listOfClones[$cloneId]["databasePrefix"] = stripslashes($_POST["externalDBPrefix"]);
890
 
891
  update_option("wpstg_existing_clones_beta", $listOfClones);
892
 
906
  */
907
  public function ajaxPushScan()
908
  {
909
+ if (!$this->isAuthenticated()) {
910
  return false;
911
  }
912
 
932
  */
933
  public function ajaxPushProcessing()
934
  {
935
+ if (!$this->isAuthenticated()) {
936
  return false;
937
  }
938
 
941
  }
942
 
943
  // Start the process
944
+ wp_send_json((new Processing)->start());
 
945
  }
946
 
947
  /**
949
  */
950
  public function getLicensePage()
951
  {
 
952
  // Get license data
953
  $license = get_option('wpstg_license_status');
954
 
961
  */
962
  public function ajaxSendReport($args = [])
963
  {
964
+ if (!$this->isAuthenticated()) {
965
+ return;
966
  }
967
 
968
  // Set params
1015
  public function ajaxDatabaseConnect()
1016
  {
1017
  if (!$this->isAuthenticated()) {
1018
+ return;
1019
  }
1020
 
1021
  $args = $_POST;
1023
  $password = !empty($args['databasePassword']) ? $args['databasePassword'] : '';
1024
  $database = !empty($args['databaseDatabase']) ? $args['databaseDatabase'] : '';
1025
  $server = !empty($args['databaseServer']) ? $args['databaseServer'] : 'localhost';
1026
+ $prefix = !empty($args['databasePrefix']) ? $args['databasePrefix'] : 'wp_';
1027
 
1028
  $dbInfo = new DbInfo($server, $user, stripslashes($password), $database);
1029
+ $wpdb = $dbInfo->connect();
1030
  $error = $dbInfo->getError();
1031
+
1032
  // Can not connect to mysql database
1033
  if ($error !== null) {
1034
  echo json_encode(['success' => 'false', 'errors' => $error]);
1035
  exit;
1036
  }
1037
 
1038
+ // Check if any table with provided prefix already exist
1039
+ $existingTables = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $prefix . '%'));
1040
+ if ($existingTables !== null) {
1041
+ echo json_encode(['success' => 'false', 'errors' => 'Tables with prefix ' . $prefix . ' already exist in database. Select another prefix.']);
1042
+ exit;
1043
+ }
1044
+
1045
  echo json_encode(['success' => 'true']);
1046
  exit;
1047
  }
1052
  public function ajaxDatabaseVerification()
1053
  {
1054
  if (!$this->isAuthenticated()) {
1055
+ return;
1056
  }
1057
 
1058
  if (!$this->isPro()) {
1059
+ return;
1060
  }
1061
 
1062
  $user = !empty($_POST['databaseUser']) ? $_POST['databaseUser'] : '';
Backend/Modules/Jobs/Cleaners/WpContentCleaner.php ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs\Cleaners;
4
+
5
+ use WPStaging\Backend\Modules\Jobs\Files;
6
+ use WPStaging\Framework\Utils\WpDefaultDirectories;
7
+ use WPStaging\Framework\Filesystem\Filesystem;
8
+ use WPStaging\Core\Utils\Logger;
9
+
10
+ /**
11
+ * This class is used to delete all uploads, themes and plugins
12
+ * Currently it is used during push process
13
+ * It will delete uploads, themes and plugins according to the options user selected.
14
+ */
15
+ class WpContentCleaner
16
+ {
17
+ /**
18
+ * @var array
19
+ */
20
+ private $logs = [];
21
+
22
+ /**
23
+ * @var Object
24
+ */
25
+ private $job;
26
+
27
+ /**
28
+ * @param Files $job
29
+ */
30
+ public function __construct($job)
31
+ {
32
+ $this->job = $job;
33
+ }
34
+
35
+ /**
36
+ * Return logs of this cleaning process
37
+ * @return array
38
+ */
39
+ public function getLogs()
40
+ {
41
+ return $this->logs;
42
+ }
43
+
44
+ /**
45
+ * Remove Plugins/Themes/Uploads according to option selected
46
+ * $directory param used in this method is mainly for mocking purpose but,
47
+ * can also be used to give path of staging site
48
+ * @param string $directory Root directory of target WordPress Installation
49
+ * @return bool
50
+ *
51
+ * @todo update for clone when clone is network after merging that PR
52
+ */
53
+ public function tryCleanWpContent($directory)
54
+ {
55
+ $options = $this->job->getOptions();
56
+
57
+ if ($options->statusContentCleaner === 'finished' || $options->statusContentCleaner === 'skipped') {
58
+ return true;
59
+ }
60
+
61
+ // Skip cleaning if staging site is broken and not complete
62
+ if (!is_dir($directory)) {
63
+ return true;
64
+ }
65
+
66
+ $wpDirectories = new WpDefaultDirectories();
67
+ $directory = trailingslashit($directory);
68
+ $paths = [];
69
+ if ($options->deleteUploadsFolder && !$options->backupUploadsFolder && $options->statusContentCleaner = 'pending') {
70
+ $paths[] = trailingslashit($directory . $wpDirectories->getRelativeUploadPath());
71
+ }
72
+
73
+ if ($options->deletePluginsAndThemes) {
74
+ $paths[] = trailingslashit($directory . $wpDirectories->getRelativeThemePath());
75
+ $paths[] = trailingslashit($directory . $wpDirectories->getRelativePluginPath());
76
+ }
77
+
78
+ if (count($paths) === 0) {
79
+ $options->statusContentCleaner = 'skipped';
80
+ $this->job->saveOptions($options);
81
+ return true;
82
+ }
83
+
84
+ if ($options->statusContentCleaner === 'pending') {
85
+ $this->logs[] = [
86
+ "msg" => __("Files: Cleaning up directories: Plugins, Themes, Uploads!", "wp-staging"),
87
+ "type" => Logger::TYPE_INFO
88
+ ];
89
+
90
+ $options->statusContentCleaner = 'cleaning';
91
+ $this->job->saveOptions($options);
92
+ }
93
+
94
+ $excludePaths = [
95
+ "wp-staging",
96
+ "wp-staging-1",
97
+ "wp-staging-pro",
98
+ "wp-staging-pro-1",
99
+ "wp-staging-dev",
100
+ 'cache',
101
+ 'wps-hide-login',
102
+ 'wp-staging-hooks',
103
+ ];
104
+ $fs = (new Filesystem())
105
+ ->setShouldStop([$this->job, 'isOverThreshold'])
106
+ ->setExcludePaths($excludePaths)
107
+ ->setRecursive();
108
+ try {
109
+ if (!$fs->deletePaths($paths)) {
110
+ return false;
111
+ }
112
+ } catch (\RuntimeException $ex) {
113
+ $this->logs[] = [
114
+ "msg" => sprintf(__("Files: Error - %s. Content cleaning.", "wp-staging"), $ex->getMessage()),
115
+ "type" => Logger::TYPE_ERROR
116
+ ];
117
+ return false;
118
+ }
119
+
120
+ $options->statusContentCleaner = 'finished';
121
+ $this->job->saveOptions($options);
122
+ if (!$options->deletePluginsAndThemes) {
123
+ $this->logs[] = [
124
+ "msg" => __("Files: Skipped cleaning Plugins and Themes directories!", "wp-staging"),
125
+ "type" => Logger::TYPE_INFO
126
+ ];
127
+ }
128
+
129
+ $this->logs[] = [
130
+ "msg" => __("Files: Finished cleaning!", "wp-staging"),
131
+ "type" => Logger::TYPE_INFO
132
+ ];
133
+
134
+ return true;
135
+ }
136
+ }
Backend/Modules/Jobs/Cloning.php CHANGED
@@ -168,7 +168,12 @@ class Cloning extends Job
168
  $this->options->cloneHostname = trim($_POST["cloneHostname"]);
169
  }
170
 
171
- $this->options->emailsDisabled = isset( $_POST['emailsDisabled'] ) && $_POST['emailsDisabled'] !== "false";
 
 
 
 
 
172
 
173
  $this->options->destinationHostname = $this->getDestinationHostname();
174
  $this->options->destinationDir = $this->getDestinationDir();
@@ -179,6 +184,9 @@ class Cloning extends Job
179
  // Process lock state
180
  $this->options->isRunning = true;
181
 
 
 
 
182
  // Save Clone data
183
  $this->saveClone();
184
 
@@ -216,8 +224,9 @@ class Cloning extends Job
216
  "databaseDatabase" => $this->options->databaseDatabase,
217
  "databaseServer" => $this->options->databaseServer,
218
  "databasePrefix" => $this->options->databasePrefix,
219
- "emailsDisabled" => (bool) $this->options->emailsDisabled,
220
- "uploadsSymlinked" => (bool)$this->options->uploadsSymlinked
 
221
  ];
222
 
223
  if (update_option("wpstg_existing_clones_beta", $this->options->existingClones) === false) {
168
  $this->options->cloneHostname = trim($_POST["cloneHostname"]);
169
  }
170
 
171
+ // Make sure it is always enabled for free version
172
+ $this->options->emailsAllowed = true;
173
+ if (defined('WPSTGPRO_VERSION')) {
174
+ $this->options->emailsAllowed = apply_filters('wpstg_cloning_email_allowed',
175
+ isset($_POST['emailsAllowed']) && $_POST['emailsAllowed'] !== "false");
176
+ }
177
 
178
  $this->options->destinationHostname = $this->getDestinationHostname();
179
  $this->options->destinationDir = $this->getDestinationDir();
184
  // Process lock state
185
  $this->options->isRunning = true;
186
 
187
+ // id of the user creating the clone
188
+ $this->options->ownerId = get_current_user_id();
189
+
190
  // Save Clone data
191
  $this->saveClone();
192
 
224
  "databaseDatabase" => $this->options->databaseDatabase,
225
  "databaseServer" => $this->options->databaseServer,
226
  "databasePrefix" => $this->options->databasePrefix,
227
+ "emailsAllowed" => (bool)$this->options->emailsAllowed,
228
+ "uploadsSymlinked" => (bool)$this->options->uploadsSymlinked,
229
+ "ownerId" => $this->options->ownerId,
230
  ];
231
 
232
  if (update_option("wpstg_existing_clones_beta", $this->options->existingClones) === false) {
Backend/Modules/Jobs/Database.php CHANGED
@@ -104,7 +104,7 @@ class Database extends CloningProcess
104
  * @param string $name
105
  * @return bool
106
  */
107
- private function shouldDropTable($name)
108
  {
109
  $old = $this->stagingDb->get_var($this->stagingDb->prepare("SHOW TABLES LIKE %s", $name));
110
  return (
@@ -115,6 +115,16 @@ class Database extends CloningProcess
115
  );
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
118
  /**
119
  * Finish the step
120
  */
@@ -193,7 +203,12 @@ class Database extends CloningProcess
193
  }
194
  }
195
 
196
- if ($this->shouldDropTable($newTableName)) {
 
 
 
 
 
197
  $this->databaseCloningService->dropTable($newTableName);
198
  }
199
 
104
  * @param string $name
105
  * @return bool
106
  */
107
+ private function isTableExist($name)
108
  {
109
  $old = $this->stagingDb->get_var($this->stagingDb->prepare("SHOW TABLES LIKE %s", $name));
110
  return (
115
  );
116
  }
117
 
118
+ /**
119
+ * Check if table already exists and the main job is not updating
120
+ * @param string $name
121
+ * @return bool
122
+ */
123
+ private function shouldAbortIfTableExist($name)
124
+ {
125
+ return isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && $this->isTableExist($name);
126
+ }
127
+
128
  /**
129
  * Finish the step
130
  */
203
  }
204
  }
205
 
206
+ if ($this->shouldAbortIfTableExist($newTableName)) {
207
+ $this->returnException(sprintf(__("Can not proceed. Tables beginning with the prefix '%s' already exist in the database. Choose another table prefix and try again.", "wp-staging"), $this->getStagingPrefix()));
208
+ return true;
209
+ }
210
+
211
+ if ($this->isTableExist($newTableName)) {
212
  $this->databaseCloningService->dropTable($newTableName);
213
  }
214
 
Backend/Modules/Jobs/Delete.php CHANGED
@@ -390,23 +390,30 @@ class Delete extends Job {
390
  return;
391
  }
392
 
 
 
 
393
  if ($this->isNotEmpty($this->deleteDir)) {
394
  $fs = (new Filesystem())
395
  ->setShouldStop([$this, 'isOverThreshold'])
396
  ->setRecursive();
397
- if (!$fs->deleteNew($this->deleteDir)) {
398
- return;
 
 
 
 
 
399
  }
400
  }
401
 
402
  // Throw fatal error if the folder has still not been deleted and there are files in it
403
  if ($this->isNotEmpty($this->deleteDir)) {
404
- $clone = (string) $this->clone->path;
405
  $response = [
406
  'job' => 'delete',
407
  'status' => true,
408
- 'delete' => 'finished',
409
- 'message' => "Could not delete the entire staging site. The folder {$clone} still exists and is not empty. <br/> Try to empty this folder manually by using FTP or file manager plugin and then try to delete again the staging site here.<br/> If this happens again please contact us at support@wp-staging.com",
410
  'error' => true,
411
  ];
412
  wp_die(json_encode($response));
390
  return;
391
  }
392
 
393
+ $clone = (string)$this->clone->path;
394
+ $errorMessage = __(sprintf("Could not delete the entire staging site. The folder %s still exists and is not empty. <br/> Try to empty this folder manually by using FTP or file manager plugin and then try to delete again the staging site here.<br/> If this happens again please contact us at support@wp-staging.com", $clone), "wp-staging");
395
+ $deleteStatus = "finished";
396
  if ($this->isNotEmpty($this->deleteDir)) {
397
  $fs = (new Filesystem())
398
  ->setShouldStop([$this, 'isOverThreshold'])
399
  ->setRecursive();
400
+ try {
401
+ if (!$fs->deleteNew($this->deleteDir)) {
402
+ return;
403
+ }
404
+ } catch (\RuntimeException $ex) {
405
+ $errorMessage = $ex->getMessage();
406
+ $deleteStatus = "unfinished";
407
  }
408
  }
409
 
410
  // Throw fatal error if the folder has still not been deleted and there are files in it
411
  if ($this->isNotEmpty($this->deleteDir)) {
 
412
  $response = [
413
  'job' => 'delete',
414
  'status' => true,
415
+ 'delete' => $deleteStatus,
416
+ 'message' => $errorMessage,
417
  'error' => true,
418
  ];
419
  wp_die(json_encode($response));
Backend/Modules/Jobs/Files.php CHANGED
@@ -3,11 +3,13 @@
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  use SplFileObject;
6
- use WPStaging\Framework\Filesystem\FileService;
7
  use WPStaging\Core\Utils\Logger;
 
 
 
8
  use WPStaging\Framework\Filesystem\WpUploadsFolderSymlinker;
9
  use WPStaging\Framework\Utils\WpDefaultDirectories;
10
-
11
  /**
12
  * Class Files
13
  *
@@ -37,11 +39,19 @@ class Files extends JobExecutable
37
  */
38
  private $destination;
39
 
 
 
 
 
 
40
  /**
41
  * Initialization
42
  */
43
  public function initialize()
44
  {
 
 
 
45
  $this->destination = $this->options->destinationDir;
46
 
47
  $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
@@ -51,13 +61,12 @@ class Files extends JobExecutable
51
  }
52
 
53
  // Informational logs
54
- if ($this->options->currentStep == 0) {
55
  $this->log("Copying files...");
56
  }
57
 
58
  $this->settings->batchSize = $this->settings->batchSize * 1000000;
59
  $this->maxFilesPerRun = $this->settings->fileLimit;
60
- //$this->maxFilesPerRun = ($this->settings->cpuLoad === 'low') ? 50 : 1;
61
  }
62
 
63
  /**
@@ -67,6 +76,8 @@ class Files extends JobExecutable
67
  protected function calculateTotalSteps()
68
  {
69
  $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
 
 
70
  }
71
 
72
  /**
@@ -84,6 +95,12 @@ class Files extends JobExecutable
84
  return false;
85
  }
86
 
 
 
 
 
 
 
87
  // Get files and copy'em
88
  if (!$this->getFilesAndCopy()) {
89
  $this->prepareResponse(false, false);
@@ -97,12 +114,50 @@ class Files extends JobExecutable
97
  return true;
98
  }
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  /**
101
  * Get files and copy
102
  * @return bool
103
  */
104
  private function getFilesAndCopy()
105
  {
 
 
 
 
 
106
  // Over limits threshold
107
  if ($this->isOverThreshold()) {
108
  // Prepare response and save current progress
@@ -112,7 +167,6 @@ class Files extends JobExecutable
112
  }
113
 
114
  // Go to last copied line and than to next one
115
- //if ($this->options->copiedFiles != 0) {
116
  if (isset($this->options->copiedFiles) && $this->options->copiedFiles != 0) {
117
  $this->file->seek($this->options->copiedFiles - 1);
118
  }
@@ -151,6 +205,11 @@ class Files extends JobExecutable
151
  */
152
  private function symlinkUploadFolder()
153
  {
 
 
 
 
 
154
  if (!$this->options->uploadsSymlinked) {
155
  $this->log("Skipped symlinking Wp Uploads Folder");
156
  return true;
@@ -184,7 +243,7 @@ class Files extends JobExecutable
184
  */
185
  private function copyFile($file)
186
  {
187
- $file = trim(\WPStaging\Core\WPStaging::getWPpath() . $file);
188
 
189
  $file = wpstg_replace_windows_directory_separator($file);
190
 
@@ -247,7 +306,7 @@ class Files extends JobExecutable
247
  }
248
 
249
  // Set file permissions
250
- @chmod($destination, wpstg_get_permissions_for_file());
251
 
252
  $this->setDirPermissions($destination);
253
 
@@ -289,14 +348,14 @@ class Files extends JobExecutable
289
 
290
  /**
291
  * Set directory permissions
292
- * @param type $file
293
  * @return boolean
294
  */
295
  private function setDirPermissions($file)
296
  {
297
  $dir = dirname($file);
298
  if (is_dir($dir)) {
299
- @chmod($dir, wpstg_get_permissions_for_directory());
300
  }
301
  return false;
302
  }
@@ -310,12 +369,12 @@ class Files extends JobExecutable
310
  private function getDestination($file)
311
  {
312
  $file = wpstg_replace_windows_directory_separator($file);
313
- $rootPath = wpstg_replace_windows_directory_separator(\WPStaging\Core\WPStaging::getWPpath());
314
  $relativePath = str_replace($rootPath, null, $file);
315
  $destinationPath = $this->destination . $relativePath;
316
  $destinationDirectory = dirname($destinationPath);
317
 
318
- if (!is_dir($destinationDirectory) && !mkdir($destinationDirectory, wpstg_get_permissions_for_directory(), true) && !is_dir($destinationDirectory)) {
319
  $this->log("Files: Can not create directory {$destinationDirectory}. Possible write permission error!", Logger::TYPE_ERROR);
320
  return false;
321
  }
@@ -332,8 +391,8 @@ class Files extends JobExecutable
332
  */
333
  private function copyBig($src, $dst, $buffersize)
334
  {
335
- $src = fopen($src, 'r');
336
- $dest = fopen($dst, 'w');
337
 
338
  if (!$src || !$dest) {
339
  return false;
@@ -380,13 +439,13 @@ class Files extends JobExecutable
380
  );
381
  }
382
 
383
- if ((new FileService)->isFilenameExcluded($file, $excludedFiles)) {
384
  return true;
385
  }
386
 
387
  // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
388
  // because if the updating process fails, the staging site would not be accessable any longer
389
- if (isset($this->options->mainJob) && $this->options->mainJob == "updating"
390
  && stripos(strrev($file), strrev("wp-config.php")) === 0) {
391
  return true;
392
  }
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  use SplFileObject;
6
+ use WPStaging\Backend\Modules\Jobs\Cleaners\WpContentCleaner;
7
  use WPStaging\Core\Utils\Logger;
8
+ use WPStaging\Core\WPStaging;
9
+ use WPStaging\Framework\Filesystem\Filesystem;
10
+ use WPStaging\Framework\Filesystem\Permissions;
11
  use WPStaging\Framework\Filesystem\WpUploadsFolderSymlinker;
12
  use WPStaging\Framework\Utils\WpDefaultDirectories;
 
13
  /**
14
  * Class Files
15
  *
39
  */
40
  private $destination;
41
 
42
+ /**
43
+ * @var Permissions
44
+ */
45
+ private $permissions;
46
+
47
  /**
48
  * Initialization
49
  */
50
  public function initialize()
51
  {
52
+
53
+ $this->permissions = new Permissions();
54
+
55
  $this->destination = $this->options->destinationDir;
56
 
57
  $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
61
  }
62
 
63
  // Informational logs
64
+ if ($this->options->currentStep === 1) {
65
  $this->log("Copying files...");
66
  }
67
 
68
  $this->settings->batchSize = $this->settings->batchSize * 1000000;
69
  $this->maxFilesPerRun = $this->settings->fileLimit;
 
70
  }
71
 
72
  /**
76
  protected function calculateTotalSteps()
77
  {
78
  $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
79
+ // Add an extra step for cleaning content in themes, plugins and uploads dir
80
+ $this->options->totalSteps++;
81
  }
82
 
83
  /**
95
  return false;
96
  }
97
 
98
+ // Cleaning wp-content directories: uploads, themes and plugins
99
+ if (!$this->cleanWpContent()) {
100
+ $this->prepareResponse(false, false);
101
+ return false;
102
+ }
103
+
104
  // Get files and copy'em
105
  if (!$this->getFilesAndCopy()) {
106
  $this->prepareResponse(false, false);
114
  return true;
115
  }
116
 
117
+ /**
118
+ * @return bool
119
+ */
120
+ private function cleanWpContent()
121
+ {
122
+ if ($this->options->mainJob !== 'updating') {
123
+ return true;
124
+ }
125
+
126
+ if ($this->options->currentStep !== 0) {
127
+ return true;
128
+ }
129
+
130
+ // @todo inject using DI if possible
131
+ $contentCleaner = new WpContentCleaner($this);
132
+
133
+ $result = $contentCleaner->tryCleanWpContent($this->destination);
134
+ foreach ($contentCleaner->getLogs() as $log) {
135
+ if ($log['type'] === Logger::TYPE_ERROR) {
136
+ $this->log($log['msg'], $log['type']);
137
+ $this->returnException($log['msg']);
138
+ } else {
139
+ $this->log($log['msg'], $log['type']);
140
+ }
141
+ }
142
+
143
+ if (!$result) {
144
+ return false;
145
+ }
146
+
147
+ return true;
148
+ }
149
+
150
  /**
151
  * Get files and copy
152
  * @return bool
153
  */
154
  private function getFilesAndCopy()
155
  {
156
+ // dont do this step if step is 0
157
+ if ($this->options->currentStep === 0) {
158
+ return true;
159
+ }
160
+
161
  // Over limits threshold
162
  if ($this->isOverThreshold()) {
163
  // Prepare response and save current progress
167
  }
168
 
169
  // Go to last copied line and than to next one
 
170
  if (isset($this->options->copiedFiles) && $this->options->copiedFiles != 0) {
171
  $this->file->seek($this->options->copiedFiles - 1);
172
  }
205
  */
206
  private function symlinkUploadFolder()
207
  {
208
+ // Don't symlink if the site is updated because the folder or symlink already exists
209
+ if ($this->options->mainJob === 'updating') {
210
+ return true;
211
+ }
212
+
213
  if (!$this->options->uploadsSymlinked) {
214
  $this->log("Skipped symlinking Wp Uploads Folder");
215
  return true;
243
  */
244
  private function copyFile($file)
245
  {
246
+ $file = trim(WPStaging::getWPpath() . $file);
247
 
248
  $file = wpstg_replace_windows_directory_separator($file);
249
 
306
  }
307
 
308
  // Set file permissions
309
+ @chmod($destination, $this->permissions->getFilesOctal());
310
 
311
  $this->setDirPermissions($destination);
312
 
348
 
349
  /**
350
  * Set directory permissions
351
+ * @param string $file
352
  * @return boolean
353
  */
354
  private function setDirPermissions($file)
355
  {
356
  $dir = dirname($file);
357
  if (is_dir($dir)) {
358
+ @chmod($dir, $this->permissions->getDirectoryOctal());
359
  }
360
  return false;
361
  }
369
  private function getDestination($file)
370
  {
371
  $file = wpstg_replace_windows_directory_separator($file);
372
+ $rootPath = wpstg_replace_windows_directory_separator(WPStaging::getWPpath());
373
  $relativePath = str_replace($rootPath, null, $file);
374
  $destinationPath = $this->destination . $relativePath;
375
  $destinationDirectory = dirname($destinationPath);
376
 
377
+ if (!is_dir($destinationDirectory) && !(new Filesystem)->mkdir($destinationDirectory) && !is_dir($destinationDirectory)) {
378
  $this->log("Files: Can not create directory {$destinationDirectory}. Possible write permission error!", Logger::TYPE_ERROR);
379
  return false;
380
  }
391
  */
392
  private function copyBig($src, $dst, $buffersize)
393
  {
394
+ $src = fopen($src, 'rb');
395
+ $dest = fopen($dst, 'wb');
396
 
397
  if (!$src || !$dest) {
398
  return false;
439
  );
440
  }
441
 
442
+ if ((new Filesystem)->isFilenameExcluded($file, $excludedFiles)) {
443
  return true;
444
  }
445
 
446
  // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
447
  // because if the updating process fails, the staging site would not be accessable any longer
448
+ if (isset($this->options->mainJob) && $this->options->mainJob === "updating"
449
  && stripos(strrev($file), strrev("wp-config.php")) === 0) {
450
  return true;
451
  }
Backend/Modules/Jobs/Finish.php CHANGED
@@ -80,7 +80,7 @@ class Finish extends Job {
80
  $this->options->existingClones[$this->options->clone]['datetime'] = time();
81
  $this->options->existingClones[$this->options->clone]['status'] = 'finished';
82
  $this->options->existingClones[$this->options->clone]['prefix'] = $this->options->prefix;
83
- $this->options->existingClones[$this->options->clone]['emailsDisabled'] = (bool) $this->options->emailsDisabled;
84
  $this->options->existingClones[$this->options->clone]['uploadsSymlinked'] = (bool) $this->options->uploadsSymlinked;
85
  update_option( "wpstg_existing_clones_beta", $this->options->existingClones );
86
  $this->log( "Finish: The job finished!" );
@@ -108,7 +108,7 @@ class Finish extends Job {
108
  "databaseDatabase" => $this->options->databaseDatabase,
109
  "databaseServer" => $this->options->databaseServer,
110
  "databasePrefix" => $this->options->databasePrefix,
111
- "emailsDisabled" => (bool) $this->options->emailsDisabled,
112
  "uploadsSymlinked" => (bool) $this->options->uploadsSymlinked
113
  ];
114
 
80
  $this->options->existingClones[$this->options->clone]['datetime'] = time();
81
  $this->options->existingClones[$this->options->clone]['status'] = 'finished';
82
  $this->options->existingClones[$this->options->clone]['prefix'] = $this->options->prefix;
83
+ $this->options->existingClones[$this->options->clone]['emailsAllowed'] = (bool) $this->options->emailsAllowed;
84
  $this->options->existingClones[$this->options->clone]['uploadsSymlinked'] = (bool) $this->options->uploadsSymlinked;
85
  update_option( "wpstg_existing_clones_beta", $this->options->existingClones );
86
  $this->log( "Finish: The job finished!" );
108
  "databaseDatabase" => $this->options->databaseDatabase,
109
  "databaseServer" => $this->options->databaseServer,
110
  "databasePrefix" => $this->options->databasePrefix,
111
+ "emailsAllowed" => (bool) $this->options->emailsAllowed,
112
  "uploadsSymlinked" => (bool) $this->options->uploadsSymlinked
113
  ];
114
 
Backend/Modules/Jobs/Job.php CHANGED
@@ -186,7 +186,13 @@ abstract class Job implements JobInterface
186
  public function __destruct()
187
  {
188
  // Commit logs
189
- $this->logger->commit();
 
 
 
 
 
 
190
  }
191
 
192
  /**
@@ -238,7 +244,7 @@ abstract class Job implements JobInterface
238
  * @param null|array|object $options
239
  * @return bool
240
  */
241
- protected function saveOptions($options = null)
242
  {
243
  // Get default options
244
  if ($options === null) {
186
  public function __destruct()
187
  {
188
  // Commit logs
189
+ if ($this->logger instanceof Logger) {
190
+ $this->logger->commit();
191
+ } else {
192
+ if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
193
+ error_log('Tried to commit log, but $this->logger was not a logger. May be a __destruct problem.');
194
+ }
195
+ }
196
  }
197
 
198
  /**
244
  * @param null|array|object $options
245
  * @return bool
246
  */
247
+ public function saveOptions($options = null)
248
  {
249
  // Get default options
250
  if ($options === null) {
Backend/Modules/Jobs/Multisite/Files.php CHANGED
@@ -3,9 +3,12 @@
3
  namespace WPStaging\Backend\Modules\Jobs\Multisite;
4
 
5
  use WPStaging\Backend\Modules\Jobs\JobExecutable;
6
- use WPStaging\Framework\Filesystem\FileService;
7
  use WPStaging\Core\Utils\Logger;
 
 
8
  use WPStaging\Framework\Filesystem\WpUploadsFolderSymlinker;
 
9
 
10
  /**
11
  * Class Files
@@ -31,15 +34,18 @@ class Files extends JobExecutable
31
  private $destination;
32
 
33
  /**
34
- * @var object
35
  */
36
- private $fileSystem;
 
37
 
38
  /**
39
  * Initialization
40
  */
41
  public function initialize()
42
  {
 
 
43
  $this->destination = $this->options->destinationDir;
44
 
45
  $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
@@ -49,7 +55,7 @@ class Files extends JobExecutable
49
  }
50
 
51
  // Informational logs
52
- if ($this->options->currentStep == 0) {
53
  $this->log("Copying files...");
54
  }
55
 
@@ -64,6 +70,8 @@ class Files extends JobExecutable
64
  protected function calculateTotalSteps()
65
  {
66
  $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
 
 
67
  }
68
 
69
  /**
@@ -81,6 +89,12 @@ class Files extends JobExecutable
81
  return false;
82
  }
83
 
 
 
 
 
 
 
84
  // Get files and copy'em
85
  if (!$this->getFilesAndCopy()) {
86
  $this->prepareResponse(false, false);
@@ -94,12 +108,50 @@ class Files extends JobExecutable
94
  return true;
95
  }
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  /**
98
  * Get files and copy
99
  * @return bool
100
  */
101
  private function getFilesAndCopy()
102
  {
 
 
 
 
 
103
  // Over limits threshold
104
  if ($this->isOverThreshold()) {
105
  // Prepare response and save current progress
@@ -148,10 +200,8 @@ class Files extends JobExecutable
148
  */
149
  private function symlinkUploadFolder()
150
  {
151
-
152
  // Don't symlink if the site is updated because the folder or symlink already exists
153
- if($this->options->mainJob === 'updating')
154
- {
155
  return true;
156
  }
157
 
@@ -186,7 +236,7 @@ class Files extends JobExecutable
186
  */
187
  private function copyFile($file)
188
  {
189
- $file = trim(\WPStaging\Core\WPStaging::getWPpath() . $file);
190
 
191
  $file = wpstg_replace_windows_directory_separator($file);
192
 
@@ -259,7 +309,7 @@ class Files extends JobExecutable
259
  }
260
 
261
  // Set file permissions
262
- @chmod($destination, wpstg_get_permissions_for_file());
263
 
264
  $this->setDirPermissions($destination);
265
 
@@ -275,7 +325,7 @@ class Files extends JobExecutable
275
  {
276
  $dir = dirname($file);
277
  if (is_dir($dir)) {
278
- @chmod($dir, wpstg_get_permissions_for_directory());
279
  }
280
  return false;
281
  }
@@ -289,12 +339,12 @@ class Files extends JobExecutable
289
  private function getDestination($file)
290
  {
291
  $file = wpstg_replace_windows_directory_separator($file);
292
- $rootPath = wpstg_replace_windows_directory_separator(\WPStaging\Core\WPStaging::getWPpath());
293
  $relativePath = str_replace($rootPath, null, $file);
294
  $destinationPath = $this->destination . $relativePath;
295
  $destinationDirectory = dirname($destinationPath);
296
 
297
- if (!is_dir($destinationDirectory) && !@mkdir($destinationDirectory, wpstg_get_permissions_for_directory(), true)) {
298
  $this->log(
299
  "Files: Can not create directory {$destinationDirectory}",
300
  Logger::TYPE_ERROR
@@ -305,32 +355,6 @@ class Files extends JobExecutable
305
  return $this->sanitizeDirectorySeparator($destinationPath);
306
  }
307
 
308
- /**
309
- * Replace relative path of file if its located in multisite upload folder
310
- * wp-content/uploads/sites/SITEID or old wordpress structure wp-content/blogs.dir/SITEID/files
311
- * @return boolean
312
- */
313
- private function getMultisiteUploadFolder($file)
314
- {
315
- // Check first which method is used
316
- $uploads = wp_upload_dir();
317
- $basedir = $uploads['basedir'];
318
-
319
- if (strpos($basedir, 'blogs.dir') === false) {
320
- // Since WP 3.5
321
- $search = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id();
322
- $replace = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
323
- $uploadsFolder = str_replace($search, $replace, $file);
324
- } else {
325
- // old blog structure
326
- $search = 'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files';
327
- $replace = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
328
- $uploadsFolder = str_replace($search, $replace, $file);
329
- }
330
-
331
- return $uploadsFolder;
332
- }
333
-
334
  /**
335
  * Copy bigger files than $this->settings->batchSize
336
  * @param string $src
@@ -340,8 +364,8 @@ class Files extends JobExecutable
340
  */
341
  private function copyBig($src, $dst, $buffersize)
342
  {
343
- $src = fopen($src, 'r');
344
- $dest = fopen($dst, 'w');
345
 
346
  if (!$src || !$dest) {
347
  return false;
@@ -388,13 +412,13 @@ class Files extends JobExecutable
388
  );
389
  }
390
 
391
- if ((new FileService)->isFilenameExcluded($file, $excludedFiles)) {
392
  return true;
393
  }
394
 
395
  // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
396
  // because if the updating process fails, the staging site would not be accessable any longer
397
- if (isset($this->options->mainJob) && $this->options->mainJob == "updating"
398
  && stripos(strrev($file), strrev("wp-config.php")) === 0) {
399
  return true;
400
  }
3
  namespace WPStaging\Backend\Modules\Jobs\Multisite;
4
 
5
  use WPStaging\Backend\Modules\Jobs\JobExecutable;
6
+ use WPStaging\Backend\Modules\Jobs\Cleaners\WpContentCleaner;
7
  use WPStaging\Core\Utils\Logger;
8
+ use WPStaging\Core\WPStaging;
9
+ use WPStaging\Framework\Filesystem\Filesystem;
10
  use WPStaging\Framework\Filesystem\WpUploadsFolderSymlinker;
11
+ use WPStaging\Framework\Filesystem\Permissions;
12
 
13
  /**
14
  * Class Files
34
  private $destination;
35
 
36
  /**
37
+ * @var Permissions
38
  */
39
+ private $permissions;
40
+
41
 
42
  /**
43
  * Initialization
44
  */
45
  public function initialize()
46
  {
47
+ $this->permissions = new Permissions();
48
+
49
  $this->destination = $this->options->destinationDir;
50
 
51
  $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
55
  }
56
 
57
  // Informational logs
58
+ if ($this->options->currentStep === 1) {
59
  $this->log("Copying files...");
60
  }
61
 
70
  protected function calculateTotalSteps()
71
  {
72
  $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
73
+ // Add an extra step for cleaning content in themes, plugins and uploads dir
74
+ $this->options->totalSteps++;
75
  }
76
 
77
  /**
89
  return false;
90
  }
91
 
92
+ // Cleaning wp-content directories: uploads, themes and plugins
93
+ if (!$this->cleanWpContent()) {
94
+ $this->prepareResponse(false, false);
95
+ return false;
96
+ }
97
+
98
  // Get files and copy'em
99
  if (!$this->getFilesAndCopy()) {
100
  $this->prepareResponse(false, false);
108
  return true;
109
  }
110
 
111
+ /**
112
+ * @return bool
113
+ */
114
+ private function cleanWpContent()
115
+ {
116
+ if ($this->options->mainJob !== 'updating') {
117
+ return true;
118
+ }
119
+
120
+ if ($this->options->currentStep !== 0) {
121
+ return true;
122
+ }
123
+
124
+ // @todo inject using DI if possible
125
+ $contentCleaner = new WpContentCleaner($this);
126
+
127
+ $result = $contentCleaner->tryCleanWpContent($this->destination);
128
+ foreach ($contentCleaner->getLogs() as $log) {
129
+ if ($log['type'] === Logger::TYPE_ERROR) {
130
+ $this->log($log['msg'], $log['type']);
131
+ $this->returnException($log['msg']);
132
+ } else {
133
+ $this->debugLog($log['msg'], $log['type']);
134
+ }
135
+ }
136
+
137
+ if (!$result) {
138
+ return false;
139
+ }
140
+
141
+ return true;
142
+ }
143
+
144
  /**
145
  * Get files and copy
146
  * @return bool
147
  */
148
  private function getFilesAndCopy()
149
  {
150
+ // dont do this step if step is 0
151
+ if ($this->options->currentStep === 0) {
152
+ return true;
153
+ }
154
+
155
  // Over limits threshold
156
  if ($this->isOverThreshold()) {
157
  // Prepare response and save current progress
200
  */
201
  private function symlinkUploadFolder()
202
  {
 
203
  // Don't symlink if the site is updated because the folder or symlink already exists
204
+ if ($this->options->mainJob === 'updating') {
 
205
  return true;
206
  }
207
 
236
  */
237
  private function copyFile($file)
238
  {
239
+ $file = trim(WPStaging::getWPpath() . $file);
240
 
241
  $file = wpstg_replace_windows_directory_separator($file);
242
 
309
  }
310
 
311
  // Set file permissions
312
+ @chmod($destination, $this->permissions->getFilesOctal());
313
 
314
  $this->setDirPermissions($destination);
315
 
325
  {
326
  $dir = dirname($file);
327
  if (is_dir($dir)) {
328
+ @chmod($dir, $this->permissions->getDirectoryOctal());
329
  }
330
  return false;
331
  }
339
  private function getDestination($file)
340
  {
341
  $file = wpstg_replace_windows_directory_separator($file);
342
+ $rootPath = wpstg_replace_windows_directory_separator(WPStaging::getWPpath());
343
  $relativePath = str_replace($rootPath, null, $file);
344
  $destinationPath = $this->destination . $relativePath;
345
  $destinationDirectory = dirname($destinationPath);
346
 
347
+ if (!is_dir($destinationDirectory) && !(new Filesystem)->mkdir($destinationDirectory)) {
348
  $this->log(
349
  "Files: Can not create directory {$destinationDirectory}",
350
  Logger::TYPE_ERROR
355
  return $this->sanitizeDirectorySeparator($destinationPath);
356
  }
357
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  /**
359
  * Copy bigger files than $this->settings->batchSize
360
  * @param string $src
364
  */
365
  private function copyBig($src, $dst, $buffersize)
366
  {
367
+ $src = fopen($src, 'rb');
368
+ $dest = fopen($dst, 'wb');
369
 
370
  if (!$src || !$dest) {
371
  return false;
412
  );
413
  }
414
 
415
+ if ((new Filesystem)->isFilenameExcluded($file, $excludedFiles)) {
416
  return true;
417
  }
418
 
419
  // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
420
  // because if the updating process fails, the staging site would not be accessable any longer
421
+ if (isset($this->options->mainJob) && $this->options->mainJob === "updating"
422
  && stripos(strrev($file), strrev("wp-config.php")) === 0) {
423
  return true;
424
  }
Backend/Modules/Jobs/Multisite/Finish.php CHANGED
@@ -80,7 +80,8 @@ class Finish extends Job {
80
  $this->options->existingClones[$this->options->clone]['url'] = $this->getDestinationUrl();
81
  $this->options->existingClones[$this->options->clone]['status'] = 'finished';
82
  $this->options->existingClones[$this->options->clone]['prefix'] = $this->options->prefix;
83
- $this->options->existingClones[$this->options->clone]['uploadsSymlinked'] = $this->options->uploadsSymlinked;
 
84
  update_option( "wpstg_existing_clones_beta", $this->options->existingClones );
85
  $this->log( "Finish: The job finished!" );
86
  return true;
@@ -103,8 +104,8 @@ class Finish extends Job {
103
  "databaseDatabase" => $this->options->databaseDatabase,
104
  "databaseServer" => $this->options->databaseServer,
105
  "databasePrefix" => $this->options->databasePrefix,
106
- "emailsDisabled" => (bool) $this->options->emailsDisabled,
107
- "uploadsSymlinked" => (bool)$this->options->uploadsSymlinked
108
  ];
109
 
110
  if( update_option( "wpstg_existing_clones_beta", $this->options->existingClones ) === false ) {
80
  $this->options->existingClones[$this->options->clone]['url'] = $this->getDestinationUrl();
81
  $this->options->existingClones[$this->options->clone]['status'] = 'finished';
82
  $this->options->existingClones[$this->options->clone]['prefix'] = $this->options->prefix;
83
+ $this->options->existingClones[$this->options->clone]['emailsAllowed'] = (bool) $this->options->emailsAllowed;
84
+ $this->options->existingClones[$this->options->clone]['uploadsSymlinked'] = (bool) $this->options->uploadsSymlinked;
85
  update_option( "wpstg_existing_clones_beta", $this->options->existingClones );
86
  $this->log( "Finish: The job finished!" );
87
  return true;
104
  "databaseDatabase" => $this->options->databaseDatabase,
105
  "databaseServer" => $this->options->databaseServer,
106
  "databasePrefix" => $this->options->databasePrefix,
107
+ "emailsAllowed" => (bool) $this->options->emailsAllowed,
108
+ "uploadsSymlinked" => (bool)$this->options->uploadsSymlinked
109
  ];
110
 
111
  if( update_option( "wpstg_existing_clones_beta", $this->options->existingClones ) === false ) {
Backend/Modules/Jobs/Updating.php CHANGED
@@ -119,6 +119,16 @@ class Updating extends Job
119
  $excludedDirectories[] = rtrim($wpUploadsFolder, '/\\');
120
  }
121
 
 
 
 
 
 
 
 
 
 
 
122
  // Excluded Directories
123
  if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"])) {
124
  $this->options->excludedDirectories = wpstg_urldecode($_POST["excludedDirectories"]);
@@ -154,7 +164,11 @@ class Updating extends Job
154
 
155
  $this->options->cloneHostname = $this->options->destinationHostname;
156
 
157
- $this->options->emailsDisabled = isset( $_POST['emailsDisabled'] ) && $_POST['emailsDisabled'] !== "false";
 
 
 
 
158
 
159
  // Directories to Copy
160
  $this->options->directoriesToCopy = array_merge(
119
  $excludedDirectories[] = rtrim($wpUploadsFolder, '/\\');
120
  }
121
 
122
+ // delete uploads folder before copying if uploads is not symlinked
123
+ $this->options->deleteUploadsFolder = !$this->options->uploadsSymlinked && isset($_POST['cleanUploadsDir']) && $_POST['cleanUploadsDir'] === 'true';
124
+ // should not backup uploads during update process
125
+ $this->options->backupUploadsFolder = false;
126
+ // clean plugins and themes dir before updating
127
+ $this->options->deletePluginsAndThemes = isset($_POST['cleanPluginsThemes']) && $_POST['cleanPluginsThemes'] === 'true';
128
+ // set default statuses for backup of uploads dir and cleaning of uploads, themes and plugins dirs
129
+ $this->options->statusBackupUploadsDir = 'skipped';
130
+ $this->options->statusContentCleaner = 'pending';
131
+
132
  // Excluded Directories
133
  if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"])) {
134
  $this->options->excludedDirectories = wpstg_urldecode($_POST["excludedDirectories"]);
164
 
165
  $this->options->cloneHostname = $this->options->destinationHostname;
166
 
167
+ // Make sure it is always enabled for free version
168
+ $this->options->emailsAllowed = true;
169
+ if (defined('WPSTGPRO_VERSION')) {
170
+ $this->options->emailsAllowed = isset($_POST['emailsAllowed']) && $_POST['emailsAllowed'] !== "false";
171
+ }
172
 
173
  // Directories to Copy
174
  $this->options->directoriesToCopy = array_merge(
Backend/Notices/BooleanNotice.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Notices;
4
+
5
+ /**
6
+ * Class BooleanNotice
7
+ *
8
+ * This class is used to reduce the boilerplate code for boolean notices
9
+ *
10
+ * @package WPStaging\Backend\Notices;
11
+ */
12
+ abstract class BooleanNotice
13
+ {
14
+ /**
15
+ * The name of option on which the visibility of this notice is stored in db
16
+ *
17
+ * @return string
18
+ */
19
+ abstract function getOptionName();
20
+
21
+ /**
22
+ * Enable the option in database to show this notice
23
+ */
24
+ public function enable()
25
+ {
26
+ return add_option($this->getOptionName(), true);
27
+ }
28
+
29
+ /**
30
+ * Check whether to show this notice or not
31
+ *
32
+ * @return bool
33
+ */
34
+ public function isEnabled()
35
+ {
36
+ return get_option($this->getOptionName(), false);
37
+ }
38
+
39
+ /**
40
+ * Delete the option in database to disable showing the notice
41
+ *
42
+ * @return bool
43
+ */
44
+ public function disable()
45
+ {
46
+ return delete_option($this->getOptionName());
47
+ }
48
+ }
Backend/Notices/DisabledCacheNotice.php CHANGED
@@ -9,38 +9,15 @@ namespace WPStaging\Backend\Notices;
9
  *
10
  * @package WPStaging\Backend\Notices;
11
  */
12
- class DisabledCacheNotice
13
  {
14
  /**
15
- * The option name to detect whether to show this notice or not.
16
  */
17
  const OPTION_NAME = 'wpstg_disabled_cache_notice';
18
 
19
- /**
20
- * Enable the option in database to show this notice
21
- */
22
- public function enable()
23
- {
24
- return add_option(self::OPTION_NAME, true);
25
- }
26
-
27
- /**
28
- * Check whether to show this notice or not
29
- *
30
- * @return bool
31
- */
32
- public function isEnabled()
33
- {
34
- return get_option(self::OPTION_NAME, false);
35
- }
36
-
37
- /**
38
- * Delete the option in database to disable showing the notice
39
- *
40
- * @return bool
41
- */
42
- public function disable()
43
  {
44
- return delete_option(self::OPTION_NAME);
45
  }
46
  }
9
  *
10
  * @package WPStaging\Backend\Notices;
11
  */
12
+ class DisabledCacheNotice extends BooleanNotice
13
  {
14
  /**
15
+ * The option name to store the visibility of disabled cache notice
16
  */
17
  const OPTION_NAME = 'wpstg_disabled_cache_notice';
18
 
19
+ public function getOptionName()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  {
21
+ return self::OPTION_NAME;
22
  }
23
  }
Backend/Notices/Notices.php CHANGED
@@ -32,6 +32,11 @@ class Notices
32
  */
33
  private $url;
34
 
 
 
 
 
 
35
  public function __construct($path, $url)
36
  {
37
  $this->path = $path;
@@ -170,7 +175,7 @@ class Notices
170
  require_once "{$viewsNoticesPath}/staging-directory-permission-problem.php";
171
  }
172
 
173
- // Version Control for Free
174
  if(!$this->isPro() && version_compare(WPStaging::getInstance()->get('WPSTG_COMPATIBLE'), get_bloginfo("version"), "<")) {
175
  require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
176
  }
@@ -179,6 +184,37 @@ class Notices
179
  if ($this->isDifferentScheme()) {
180
  require_once "{$viewsNoticesPath}wrong-scheme.php";
181
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  }
183
 
184
  /**
@@ -193,4 +229,22 @@ class Notices
193
  return !($siteurlScheme === $homeScheme);
194
  }
195
 
196
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  */
33
  private $url;
34
 
35
+ /**
36
+ * @var string The key that holds directory listing errors in the container.
37
+ */
38
+ public static $directoryListingErrors = 'directoryListingErrors';
39
+
40
  public function __construct($path, $url)
41
  {
42
  $this->path = $path;
175
  require_once "{$viewsNoticesPath}/staging-directory-permission-problem.php";
176
  }
177
 
178
+ // WPSTAGING is not tested with current WordPress version
179
  if(!$this->isPro() && version_compare(WPStaging::getInstance()->get('WPSTG_COMPATIBLE'), get_bloginfo("version"), "<")) {
180
  require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
181
  }
184
  if ($this->isDifferentScheme()) {
185
  require_once "{$viewsNoticesPath}wrong-scheme.php";
186
  }
187
+
188
+ $this->showDirectoryListingWarningNotice($viewsNoticesPath);
189
+ }
190
+
191
+ /**
192
+ * Displays the notice that we could not prevent
193
+ * directory listing on a sensitive folder for some reason.
194
+ *
195
+ * @see \WPStaging\Framework\Filesystem\Filesystem::mkdir The place where all errors are enqueued
196
+ * to be displayed as a single notice here.
197
+ *
198
+ * Note: When refactoring this, keep in mind this code should be
199
+ * called only once, otherwise the message would be enqueued multiple times.
200
+ *
201
+ * @param string $viewsNoticesPath The path to the views folder.
202
+ */
203
+ private function showDirectoryListingWarningNotice($viewsNoticesPath)
204
+ {
205
+ $directoryListingErrors = WPStaging::getInstance()->getContainer()->getFromArray(static::$directoryListingErrors);
206
+
207
+ // Early bail: No errors to show
208
+ if (empty($directoryListingErrors)) {
209
+ return;
210
+ }
211
+
212
+ // Early bail: These warnings were disabled by the user.
213
+ if ((bool)apply_filters('wpstg.notices.hideDirectoryListingWarnings', false)) {
214
+ return;
215
+ }
216
+
217
+ require_once "{$viewsNoticesPath}directory-listing-could-not-be-prevented.php";
218
  }
219
 
220
  /**
229
  return !($siteurlScheme === $homeScheme);
230
  }
231
 
232
+ /**
233
+ * Get the path of plugin
234
+ * @return string
235
+ */
236
+ public function getPluginPath()
237
+ {
238
+ return $this->path;
239
+ }
240
+
241
+ /**
242
+ * Get the path of plugin
243
+ * @return string
244
+ */
245
+ public function getPluginUrl()
246
+ {
247
+ return $this->url;
248
+ }
249
+
250
+ }
Backend/Optimizer/Optimizer.php CHANGED
@@ -2,6 +2,8 @@
2
  namespace WPStaging\Backend\Optimizer;
3
 
4
  // No Direct Access
 
 
5
  if( !defined( "WPINC" ) ) {
6
  die;
7
  }
@@ -33,7 +35,7 @@ class Optimizer {
33
  return false;
34
  }
35
 
36
- if( wp_mkdir_p( $this->mudir ) ) {
37
  $this->copy();
38
  }
39
  return false;
2
  namespace WPStaging\Backend\Optimizer;
3
 
4
  // No Direct Access
5
+ use WPStaging\Framework\Filesystem\Filesystem;
6
+
7
  if( !defined( "WPINC" ) ) {
8
  die;
9
  }
35
  return false;
36
  }
37
 
38
+ if( (new Filesystem)->mkdir( $this->mudir ) ) {
39
  $this->copy();
40
  }
41
  return false;
Backend/Optimizer/wp-staging-optimizer.php CHANGED
@@ -86,7 +86,7 @@ function wpstgExcludePlugins($plugins)
86
  return $plugins;
87
  }
88
 
89
- if (!wpstgIsCompatibilityModeRequest()) {
90
  return $plugins;
91
  }
92
 
@@ -118,7 +118,7 @@ function wpstgExcludeSitePlugins($plugins)
118
  return $plugins;
119
  }
120
 
121
- if (!wpstgIsCompatibilityModeRequest()) {
122
  return $plugins;
123
  }
124
 
@@ -148,7 +148,7 @@ function wpstgDisableTheme($dir)
148
  {
149
  $enableTheme = apply_filters('wpstg_optimizer_enable_theme', false);
150
 
151
- if (wpstgIsCompatibilityModeRequest() && $enableTheme === false) {
152
  $wpstgRootPro = wpstgGetPluginsDir() . 'wp-staging-pro';
153
  $wpstgRoot = wpstgGetPluginsDir() . 'wp-staging';
154
 
@@ -173,28 +173,28 @@ add_filter('stylesheet_directory', 'wpstgDisableTheme');
173
  add_filter('template_directory', 'wpstgDisableTheme');
174
 
175
  /**
176
- * Should the current request be processed by Compatibility Mode?
177
  *
178
  * @return bool
179
  */
180
- function wpstgIsCompatibilityModeRequest()
181
  {
182
 
183
- // Optimizer not enabled
184
  if (!wpstgIsEnabledOptimizer()) {
185
  return false;
186
  }
187
 
188
- if (!defined('DOING_AJAX') ||
189
- !DOING_AJAX ||
190
- !isset($_POST['action']) ||
191
- strpos($_POST['action'], 'wpstg') === false
 
192
  ) {
193
 
194
- return false;
195
  }
196
 
197
- return true;
198
  }
199
 
200
  /**
86
  return $plugins;
87
  }
88
 
89
+ if (!wpstgIsOptimizerRequest()) {
90
  return $plugins;
91
  }
92
 
118
  return $plugins;
119
  }
120
 
121
+ if (!wpstgIsOptimizerRequest()) {
122
  return $plugins;
123
  }
124
 
148
  {
149
  $enableTheme = apply_filters('wpstg_optimizer_enable_theme', false);
150
 
151
+ if (wpstgIsOptimizerRequest() && $enableTheme === false) {
152
  $wpstgRootPro = wpstgGetPluginsDir() . 'wp-staging-pro';
153
  $wpstgRoot = wpstgGetPluginsDir() . 'wp-staging';
154
 
173
  add_filter('template_directory', 'wpstgDisableTheme');
174
 
175
  /**
176
+ * Should the current request be processed by optimizer?
177
  *
178
  * @return bool
179
  */
180
+ function wpstgIsOptimizerRequest()
181
  {
182
 
 
183
  if (!wpstgIsEnabledOptimizer()) {
184
  return false;
185
  }
186
 
187
+ if (defined('DOING_AJAX') &&
188
+ DOING_AJAX &&
189
+ isset($_POST['action']) &&
190
+ strpos($_POST['action'], 'wpstg_send_report') === false &&
191
+ strpos($_POST['action'], 'wpstg') === 0
192
  ) {
193
 
194
+ return true;
195
  }
196
 
197
+ return false;
198
  }
199
 
200
  /**
Backend/Upgrade/Upgrade.php CHANGED
@@ -6,7 +6,6 @@ use WPStaging\Core\WPStaging;
6
  use WPStaging\Core\Utils\Logger;
7
  use WPStaging\Core\Utils\IISWebConfig;
8
  use WPStaging\Core\Utils\Htaccess;
9
- use WPStaging\Core\Utils\Filesystem;
10
 
11
  /**
12
  * Upgrade Class
@@ -117,11 +116,6 @@ class Upgrade {
117
  $htaccess->createLitespeed( ABSPATH . '.htaccess' );
118
  }
119
 
120
- // Create empty index.php in wp staging uploads folder
121
- $filesystem = new Filesystem();
122
- $filesystem->create( trailingslashit( \WPStaging\Core\WPStaging::getContentDir() ) . 'index.php', "<?php // silence" );
123
- $filesystem->create( trailingslashit( \WPStaging\Core\WPStaging::getContentDir() ) . 'logs/index.php', "<?php // silence" );
124
-
125
  // create web.config file for IIS in wp staging uploads folder
126
  $webconfig = new IISWebConfig();
127
  $webconfig->create( trailingslashit( \WPStaging\Core\WPStaging::getContentDir() ) . 'web.config' );
6
  use WPStaging\Core\Utils\Logger;
7
  use WPStaging\Core\Utils\IISWebConfig;
8
  use WPStaging\Core\Utils\Htaccess;
 
9
 
10
  /**
11
  * Upgrade Class
116
  $htaccess->createLitespeed( ABSPATH . '.htaccess' );
117
  }
118
 
 
 
 
 
 
119
  // create web.config file for IIS in wp staging uploads folder
120
  $webconfig = new IISWebConfig();
121
  $webconfig->create( trailingslashit( \WPStaging\Core\WPStaging::getContentDir() ) . 'web.config' );
Backend/public/css/wpstg-admin.css CHANGED
@@ -34,7 +34,7 @@
34
  padding-bottom: 12px;
35
  }
36
 
37
- #wpstg-tools .nav-tab-wrapper {
38
  padding: 0;
39
  }
40
 
@@ -56,12 +56,7 @@
56
  display: none;
57
  }
58
 
59
-
60
- /* Layout of admin table and rows
61
- */
62
-
63
- #wpstg-tab-container .panel-container {
64
- background: #FFF;
65
  padding: 0 20px 20px 20px;
66
  overflow: auto;
67
  }
@@ -758,20 +753,35 @@
758
 
759
  .wpstg-button.green {
760
  display: inline-block;
761
- background-color: #83c11f;
762
  padding: 10px;
763
  min-width: 170px;
764
- color: white;
765
  font-size: 16px;
766
  text-decoration: none;
767
  text-align: center;
768
  margin-top: 20px;
 
 
769
  }
770
 
771
  .wpstg-button.green:hover {
772
  background-color: #8ed122;
773
  }
774
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775
  #wpstg-welcome li {
776
  font-size: 18px;
777
  line-height: 29px;
@@ -1073,6 +1083,12 @@
1073
  text-align: center;
1074
  }
1075
 
 
 
 
 
 
 
1076
  .wpstg-success {
1077
  color: #3c763d;
1078
  background-color: #dff0d8;
@@ -1295,11 +1311,11 @@ Tooltip top arrow
1295
  color: #f56363;
1296
  }
1297
 
1298
- .swal2-modal .swal2-cancel.wpstg--btn--cancel {
1299
  margin-bottom: 0;
1300
  }
1301
 
1302
- .swal2-modal .swal2-confirm.wpstg--btn--confirm {
1303
  font-size: 16px;
1304
  padding: .5em;
1305
  line-height: normal;
@@ -1307,7 +1323,7 @@ Tooltip top arrow
1307
  margin-right: 10px;
1308
  }
1309
 
1310
- .swal2-modal .wpstg-loader {
1311
  display: inline-block !important;
1312
  }
1313
 
@@ -1657,7 +1673,7 @@ span.wpstg--drag-or-upload {
1657
  color: #a8a8a8;
1658
  }
1659
 
1660
- #wpstg_disable_emails {
1661
  margin-left: 10px;
1662
  }
1663
 
@@ -1930,4 +1946,8 @@ span.wpstg--drag-or-upload {
1930
 
1931
  .wpstg-issue-resubmit-confirmation.swal2-container {
1932
  z-index: 10500;
1933
- }
 
 
 
 
34
  padding-bottom: 12px;
35
  }
36
 
37
+ .wpstg-tabs-container .nav-tab-wrapper {
38
  padding: 0;
39
  }
40
 
56
  display: none;
57
  }
58
 
59
+ #wpstg-tab-container .wpstg-settings-panel {
 
 
 
 
 
60
  padding: 0 20px 20px 20px;
61
  overflow: auto;
62
  }
753
 
754
  .wpstg-button.green {
755
  display: inline-block;
 
756
  padding: 10px;
757
  min-width: 170px;
 
758
  font-size: 16px;
759
  text-decoration: none;
760
  text-align: center;
761
  margin-top: 20px;
762
+ background-color: #83c11f;
763
+ color: white;
764
  }
765
 
766
  .wpstg-button.green:hover {
767
  background-color: #8ed122;
768
  }
769
 
770
+ .wpstg-button.wpstg-save {
771
+ background-color: #1687A7;
772
+ color: white;
773
+ }
774
+
775
+ .wpstg-button.wpstg-save:hover {
776
+ background-color: #276678;
777
+ }
778
+
779
+ .wpstg-button.wpstg-bordered {
780
+ border-radius:3px;
781
+ font-size: 14px;
782
+ border: 1px solid white;
783
+ }
784
+
785
  #wpstg-welcome li {
786
  font-size: 18px;
787
  line-height: 29px;
1083
  text-align: center;
1084
  }
1085
 
1086
+ .wpstg-text-field>#wpstg-db-status {
1087
+ margin-top: 8px;
1088
+ margin-left: 25%;
1089
+ min-width: 350px;
1090
+ }
1091
+
1092
  .wpstg-success {
1093
  color: #3c763d;
1094
  background-color: #dff0d8;
1311
  color: #f56363;
1312
  }
1313
 
1314
+ body.toplevel_page_wpstg_clone .swal2-modal .swal2-cancel.wpstg--btn--cancel {
1315
  margin-bottom: 0;
1316
  }
1317
 
1318
+ body.toplevel_page_wpstg_clone .swal2-modal .swal2-confirm.wpstg--btn--confirm {
1319
  font-size: 16px;
1320
  padding: .5em;
1321
  line-height: normal;
1323
  margin-right: 10px;
1324
  }
1325
 
1326
+ body.toplevel_page_wpstg_clone .swal2-modal .wpstg-loader {
1327
  display: inline-block !important;
1328
  }
1329
 
1673
  color: #a8a8a8;
1674
  }
1675
 
1676
+ #wpstg_allow_emails {
1677
  margin-left: 10px;
1678
  }
1679
 
1946
 
1947
  .wpstg-issue-resubmit-confirmation.swal2-container {
1948
  z-index: 10500;
1949
+ }
1950
+
1951
+ body.toplevel_page_wpstg_clone .swal2-container .swal2-content {
1952
+ z-index:2;
1953
+ }
Backend/public/js/wpstg-admin-beta.js CHANGED
@@ -2,7 +2,7 @@ jQuery(document).ready(function($){
2
  $(".wpstg_hide_beta").click(function(e) {
3
  e.preventDefault();
4
 
5
- WPStaging.ajax(
6
  {action: "wpstg_hide_beta"},
7
  function(response)
8
  {
2
  $(".wpstg_hide_beta").click(function(e) {
3
  e.preventDefault();
4
 
5
+ window.WPStaging.ajax(
6
  {action: "wpstg_hide_beta"},
7
  function(response)
8
  {
Backend/public/js/wpstg-admin-poll.js CHANGED
@@ -2,7 +2,7 @@ jQuery(document).ready(function ($) {
2
  $(".wpstg_hide_poll").click(function (e) {
3
  e.preventDefault();
4
 
5
- WPStaging.ajax(
6
  {action: "wpstg_hide_poll"},
7
  function (response)
8
  {
2
  $(".wpstg_hide_poll").click(function (e) {
3
  e.preventDefault();
4
 
5
+ window.WPStaging.ajax(
6
  {action: "wpstg_hide_poll"},
7
  function (response)
8
  {
Backend/public/js/wpstg-admin.js CHANGED
@@ -857,9 +857,10 @@ var WPStaging = (function ($) {
857
  var cloneDir = $("#wpstg_clone_dir").val();
858
  that.data.cloneDir = encodeURIComponent($.trim(cloneDir));
859
  that.data.cloneHostname = $("#wpstg_clone_hostname").val();
860
- that.data.emailsDisabled = $("#wpstg_disable_emails").is(':checked');
861
  that.data.uploadsSymlinked = $("#wpstg_symlink_upload").is(':checked');
862
-
 
863
  };
864
 
865
  var proceedCloning = function($this, workflow) {
@@ -1001,11 +1002,13 @@ var WPStaging = (function ($) {
1001
  showAjaxFatalError(response);
1002
 
1003
  // Finished
1004
- if ("undefined" !== typeof response.delete && response.delete === 'finished') {
1005
 
1006
  cache.get("#wpstg-removing-clone").removeClass("loading").html('');
1007
 
1008
- $(".wpstg-clone#" + clone).remove();
 
 
1009
 
1010
  if ($(".wpstg-clone").length < 1) {
1011
  cache.get("#wpstg-existing-clones").find("h3").text('');
@@ -1992,16 +1995,27 @@ var WPStaging = (function ($) {
1992
 
1993
  if (data.type === 'database') {
1994
  that.snapshots.type = data.type;
 
 
 
1995
  delete requestData['includedDirectories'];
1996
  delete requestData['wpContentDir'];
1997
  delete requestData['availableDirectories'];
1998
  delete requestData['wpStagingDir'];
 
 
 
1999
  requestData = that.snapshots.requestData(
2000
  'tasks.snapshot.database.create',
2001
  { ...requestData, type: 'manual' }
2002
  );
2003
  } else if (data.type === 'site') {
2004
  that.snapshots.type = data.type;
 
 
 
 
 
2005
  requestData = that.snapshots.requestData(
2006
  'jobs.snapshot.site.create',
2007
  requestData
@@ -2013,10 +2027,15 @@ var WPStaging = (function ($) {
2013
  requestData.jobs.snapshot.site.create.excludedDirectories = data.availableDirectories
2014
  .split('|')
2015
  .filter(item => !data.includedDirectories.includes(item))
2016
- .map(item => `#${item}*#`)
2017
  ;
 
 
 
2018
 
2019
- requestData.jobs.snapshot.site.create.excludedDirectories.push(`#${data.wpStagingDir}*#`);
 
 
2020
 
2021
  // delete requestData.jobs.snapshot.site.create.includedDirectories;
2022
  delete requestData.jobs.snapshot.site.create.wpContentDir;
@@ -2228,6 +2247,7 @@ var WPStaging = (function ($) {
2228
  availableDirectories: container.querySelector('input[name="availableDirectories"]').value || null,
2229
  wpStagingDir: container.querySelector('input[name="wpStagingDir"]').value || null,
2230
  exportDatabase: container.querySelector('input[name="export_database"]:checked') !== null,
 
2231
  };
2232
  },
2233
  });
@@ -3055,4 +3075,4 @@ jQuery(document).ready(function ($) {
3055
  e.preventDefault();
3056
  });
3057
 
3058
- });
857
  var cloneDir = $("#wpstg_clone_dir").val();
858
  that.data.cloneDir = encodeURIComponent($.trim(cloneDir));
859
  that.data.cloneHostname = $("#wpstg_clone_hostname").val();
860
+ that.data.emailsAllowed = $("#wpstg_allow_emails").is(':checked');
861
  that.data.uploadsSymlinked = $("#wpstg_symlink_upload").is(':checked');
862
+ that.data.cleanPluginsThemes = $('#wpstg-clean-plugins-themes').is(':checked');
863
+ that.data.cleanUploadsDir = $('#wpstg-clean-uploads').is(':checked');
864
  };
865
 
866
  var proceedCloning = function($this, workflow) {
1002
  showAjaxFatalError(response);
1003
 
1004
  // Finished
1005
+ if ("undefined" !== typeof response.delete && (response.delete === 'finished' || response.delete === 'unfinished')) {
1006
 
1007
  cache.get("#wpstg-removing-clone").removeClass("loading").html('');
1008
 
1009
+ if (response.delete === 'finished') {
1010
+ $(".wpstg-clone#" + clone).remove();
1011
+ }
1012
 
1013
  if ($(".wpstg-clone").length < 1) {
1014
  cache.get("#wpstg-existing-clones").find("h3").text('');
1995
 
1996
  if (data.type === 'database') {
1997
  that.snapshots.type = data.type;
1998
+
1999
+ // Only send to back-end what BE is expecting to receive.
2000
+ // Prevent error: Trying to hydrate DTO with value that does not exist.
2001
  delete requestData['includedDirectories'];
2002
  delete requestData['wpContentDir'];
2003
  delete requestData['availableDirectories'];
2004
  delete requestData['wpStagingDir'];
2005
+ delete requestData['exportDatabase'];
2006
+ delete requestData['includeOtherFilesInWpContent'];
2007
+
2008
  requestData = that.snapshots.requestData(
2009
  'tasks.snapshot.database.create',
2010
  { ...requestData, type: 'manual' }
2011
  );
2012
  } else if (data.type === 'site') {
2013
  that.snapshots.type = data.type;
2014
+
2015
+ // Only send to back-end what BE is expecting to receive.
2016
+ // Prevent error: Trying to hydrate DTO with value that does not exist.
2017
+ delete requestData['type'];
2018
+
2019
  requestData = that.snapshots.requestData(
2020
  'jobs.snapshot.site.create',
2021
  requestData
2027
  requestData.jobs.snapshot.site.create.excludedDirectories = data.availableDirectories
2028
  .split('|')
2029
  .filter(item => !data.includedDirectories.includes(item))
2030
+ .map(item => item)
2031
  ;
2032
+ requestData.jobs.snapshot.site.create.includeOtherFilesInWpContent = [
2033
+ data.includeOtherFilesInWpContent,
2034
+ ];
2035
 
2036
+ // Do not exclude the wp-content/uploads/wp-staging using regex by default
2037
+ // This folder is excluded by PHP without REGEX.
2038
+ //requestData.jobs.snapshot.site.create.excludedDirectories.push(`#${data.wpStagingDir}*#`);
2039
 
2040
  // delete requestData.jobs.snapshot.site.create.includedDirectories;
2041
  delete requestData.jobs.snapshot.site.create.wpContentDir;
2247
  availableDirectories: container.querySelector('input[name="availableDirectories"]').value || null,
2248
  wpStagingDir: container.querySelector('input[name="wpStagingDir"]').value || null,
2249
  exportDatabase: container.querySelector('input[name="export_database"]:checked') !== null,
2250
+ includeOtherFilesInWpContent: container.querySelector('input[name="includeOtherFilesInWpContent"]:checked') !== null,
2251
  };
2252
  },
2253
  });
3075
  e.preventDefault();
3076
  });
3077
 
3078
+ });
Backend/views/clone/ajax/external-database.php CHANGED
@@ -11,20 +11,31 @@
11
  <?php _e('Copy Staging Site to Separate Database', 'wp-staging'); ?></strong>
12
  <br><?php _e('Database must be created manually in advance!', 'wp-staging'); ?>
13
  </p>
14
- <table cellspacing="0" id="wpstg-external-db">
15
- <tbody>
16
- <tr><th>Server</th><td><input type="text" name="wpstg_db_server" id="wpstg_db_server" value="" title="wpstg_db_server" placeholder="localhost" autocapitalize="off" readonly>
17
- </td></tr>
18
- <tr><th>User</th><td><input type="text" name="wpstg_db_username" id="wpstg_db_username" value="" autocapitalize="off" class="" readonly>
19
- </td></tr>
20
- <tr><th>Password</th><td><input type="password" name="wpstg_db_password" id="wpstg_db_password" class="" readonly>
21
- </td></tr>
22
- <tr><th>Database</th><td><input type="text" name="wpstg_db_database" id="wpstg_db_database" value="" autocapitalize="off" readonly>
23
- </td></tr>
24
- <tr><th>Database Prefix</th><td><input type="text" name="wpstg_db_prefix" id="wpstg_db_prefix" value="" placeholder="<?php echo $db->prefix; ?>" autocapitalize="off" readonly>
25
- </td></tr>
26
- <tr><th></th> <td><a href="#" id="wpstg-db-connect"><?php _e("Test Database Connection", "wp-staging")?></a></td></tr>
27
- </tbody>
28
- </table>
 
 
 
 
 
 
 
 
 
 
 
29
  </fieldset>
30
 
11
  <?php _e('Copy Staging Site to Separate Database', 'wp-staging'); ?></strong>
12
  <br><?php _e('Database must be created manually in advance!', 'wp-staging'); ?>
13
  </p>
14
+ <div id="wpstg-external-db">
15
+ <div class="wpstg-form-group wpstg-text-field">
16
+ <label><?php _e('Server: ', 'wp-staging'); ?> </label>
17
+ <input type="text" name="wpstg_db_server" id="wpstg_db_server" value="" title="wpstg_db_server" placeholder="localhost" autocapitalize="off" readonly>
18
+ </div>
19
+ <div class="wpstg-form-group wpstg-text-field">
20
+ <label><?php _e('User: ', 'wp-staging'); ?></label>
21
+ <input type="text" name="wpstg_db_username" id="wpstg_db_username" value="" autocapitalize="off" class="" readonly>
22
+ </div>
23
+ <div class="wpstg-form-group wpstg-text-field">
24
+ <label><?php _e('Password: ', 'wp-staging'); ?></label>
25
+ <input type="password" name="wpstg_db_password" id="wpstg_db_password" class="" readonly>
26
+ </div>
27
+ <div class="wpstg-form-group wpstg-text-field">
28
+ <label><?php _e('Database: ', 'wp-staging'); ?></label>
29
+ <input type="text" name="wpstg_db_database" id="wpstg_db_database" value="" autocapitalize="off" readonly>
30
+ </div>
31
+ <div class="wpstg-form-group wpstg-text-field">
32
+ <label><?php _e('Database Prefix: ', 'wp-staging'); ?></label>
33
+ <input type="text" name="wpstg_db_prefix" id="wpstg_db_prefix" value="" placeholder="<?php echo $db->prefix; ?>" autocapitalize="off" readonly>
34
+ </div>
35
+ <div class="wpstg-form-group wpstg-text-field">
36
+ <label>&nbsp;</label>
37
+ <a href="#" id="wpstg-db-connect"><?php _e("Test Database Connection", "wp-staging"); ?></a>
38
+ </div>
39
+ </div>
40
  </fieldset>
41
 
Backend/views/clone/ajax/mail-setting.php CHANGED
@@ -1,15 +1,14 @@
1
- <hr>
2
- <p>
3
- <strong style="font-size: 14px;"> <?php _e( 'Mail Delivery Setting', 'wp-staging' ); ?></strong>
4
- <br/>
5
- <?php _e( 'Prevent staging site from sending emails.', 'wp-staging' ); ?>
6
- </p>
7
- <div class="wpstg-form-group">
8
- <label for="wpstg_disable_emails">
9
- <?php _e( 'Disable Emails:', 'wp-staging' ); ?> <input type="checkbox" id="wpstg_disable_emails" disabled>
10
- </label>
11
- </div>
12
- <p style="font-weight:bold;background-color:#e6e6e6;padding:15px;border-top: 1px solid white;margin-top: 20px;"><?php _e('That\'s a Pro Feature', 'wp-staging'); ?>
13
  <br>
14
- <a href="https://wp-staging.com/?utm_source=wp-admin&utm_medium=wp-admin&utm_campaign=db-external&utm_term=db-external" target="_blank" class="quads-button green wpstg-button" style="border-radius:3px;font-size: 14px;border: 1px solid white;"><?php _e("Get WP STAGING Pro", "wp-staging"); ?></a>
15
- </p>
1
+ <fieldset disabled class="wpstg-fieldset">
2
+ <p>
3
+ <?php _e('Toggle emails sending for the staging site.', 'wp-staging'); ?>
4
+ </p>
5
+ <div class="wpstg-form-group">
6
+ <label class="wpstg-checkbox" for="wpstg_allow_emails">
7
+ <?php _e('Allow Emails Sending:', 'wp-staging'); ?> <input type="checkbox" id="wpstg_allow_emails" disabled checked>
8
+ </label>
9
+ </div>
10
+ </fieldset>
11
+ <p class="wpstg-light-alert"><?php _e('That\'s a Pro Feature', 'wp-staging'); ?>
 
12
  <br>
13
+ <a href="https://wp-staging.com/?utm_source=wp-admin&utm_medium=wp-admin&utm_campaign=db-external&utm_term=db-external" target="_blank" class="quads-button green wpstg-button wpstg-bordered"><?php _e("Get WP STAGING Pro", "wp-staging"); ?></a>
14
+ </p>
Backend/views/clone/ajax/scan.php CHANGED
@@ -109,22 +109,38 @@
109
  </a>
110
 
111
  <div class="wpstg-tab-section" id="wpstg-advanced-settings">
112
-
113
  <?php
114
  if (defined('WPSTGPRO_VERSION')) {
115
  require_once(WPSTG_PLUGIN_DIR . 'Backend/Pro/views/clone/ajax/external-database.php');
116
  require_once(WPSTG_PLUGIN_DIR . 'Backend/Pro/views/clone/ajax/custom-directory.php');
117
- require_once(WPSTG_PLUGIN_DIR . 'Backend/Pro/views/clone/ajax/mail-setting.php');
118
  } else {
119
  require_once(__DIR__ . DIRECTORY_SEPARATOR . 'external-database.php');
120
  require_once(__DIR__ . DIRECTORY_SEPARATOR . 'custom-directory.php');
121
  require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mail-setting.php');
122
  }
123
  ?>
124
-
125
  </div>
126
  </div>
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  <strong>Important:</strong><a href="#" id="wpstg-check-space"><?php _e( 'Check required disk space', 'wp-staging' ); ?></a>
129
  <p></p>
130
 
109
  </a>
110
 
111
  <div class="wpstg-tab-section" id="wpstg-advanced-settings">
 
112
  <?php
113
  if (defined('WPSTGPRO_VERSION')) {
114
  require_once(WPSTG_PLUGIN_DIR . 'Backend/Pro/views/clone/ajax/external-database.php');
115
  require_once(WPSTG_PLUGIN_DIR . 'Backend/Pro/views/clone/ajax/custom-directory.php');
 
116
  } else {
117
  require_once(__DIR__ . DIRECTORY_SEPARATOR . 'external-database.php');
118
  require_once(__DIR__ . DIRECTORY_SEPARATOR . 'custom-directory.php');
119
  require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mail-setting.php');
120
  }
121
  ?>
 
122
  </div>
123
  </div>
124
 
125
+ <?php
126
+ if (defined('WPSTGPRO_VERSION')) {
127
+ require_once(WPSTG_PLUGIN_DIR . 'Backend/Pro/views/clone/ajax/mail-setting.php');
128
+ }
129
+
130
+ if( $options->current !== null ) {
131
+ $uploadsSymlinked = isset($options->existingClones[$options->current]['uploadsSymlinked']) ? (bool)$options->existingClones[$options->current]['uploadsSymlinked'] : false;
132
+ ?>
133
+ <p><label>
134
+ <input type="checkbox" id="wpstg-clean-plugins-themes" name="wpstg-clean-plugins-themes">
135
+ <?php echo __("Delete all plugins & themes on staging site before starting copy process.", "wp-staging"); ?>
136
+ </label></p>
137
+ <p><label> <?php echo ($uploadsSymlinked ? "<b>" . __("Note: This option is disabled as uploads directory is symlinked", "wp-staging") . "</b><br/>": '') ?>
138
+ <input type="checkbox" id="wpstg-clean-uploads" name="wpstg-clean-uploads" <?php echo ($uploadsSymlinked ? 'disabled' : '') ?>>
139
+ <?php echo __("Delete entire folder wp-content/uploads on staging site including all images before starting copy process.", "wp-staging"); ?>
140
+ </label></p>
141
+ <?php
142
+ }
143
+ ?>
144
  <strong>Important:</strong><a href="#" id="wpstg-check-space"><?php _e( 'Check required disk space', 'wp-staging' ); ?></a>
145
  <p></p>
146
 
Backend/views/clone/ajax/single-overview.php CHANGED
@@ -30,7 +30,10 @@
30
  do_action('wpstg.views.single_overview.before_existing_clones_buttons', $name, $data, $license);
31
 
32
  // Todo: Remove in future versions
33
- echo apply_filters_deprecated("wpstg_before_stage_buttons", [$html = '', $name, $data], '2.7.6', 'wpstg.views.single_overview.before_existing_clones_buttons', 'The replacement filter uses do_action()');
 
 
 
34
  ?>
35
 
36
  <a href="<?php echo $urlLogin ?>" class="wpstg-open-clone wpstg-clone-action" target="_blank" title="<?php echo __( "Open the staging site in a new tab", "wp-staging" ) ?>">
@@ -49,7 +52,10 @@
49
  do_action('wpstg.views.single_overview.after_existing_clones_buttons', $name, $data, $license);
50
 
51
  // Todo: Remove in future versions
52
- echo apply_filters_deprecated("wpstg_after_stage_buttons", [$html = '', $name, $data], '2.7.6', 'wpstg.views.single_overview.after_existing_clones_buttons', 'The replacement filter uses do_action()');
 
 
 
53
  ?>
54
 
55
  <div class="wpstg-staging-info">
@@ -59,7 +65,8 @@
59
  $cloneDir = ! empty($data['path']) ? $data['path'] : '';
60
  $url = ! empty($data['url']) ? sprintf('<a href="%1$s" target="_blank">%1$s</a>', $data['url']) : '';
61
  $datetime = ! empty($data['datetime']) ? date("D, d M Y H:i:s T", $data['datetime']) : '&nbsp;&nbsp;&nbsp;';
62
-
 
63
  $statusTooltip = "This clone is incomplete and does not work. Clone or update it again! \n\n".
64
  "Important: Keep the browser open until the cloning is finished. \n".
65
  "It will not proceed if your browser is not open.\n\n".
@@ -86,6 +93,8 @@
86
  echo '</br>';
87
  echo sprintf(__('URL: <span class="wpstg-bold">%s</span>', 'wp-staging'), $url);
88
  echo '</br>';
 
 
89
  echo $status;
90
  echo '</br>';
91
  echo sprintf(__('Updated: <span>%s</span>', 'wp-staging'), $datetime);
30
  do_action('wpstg.views.single_overview.before_existing_clones_buttons', $name, $data, $license);
31
 
32
  // Todo: Remove in future versions
33
+ if (function_exists('apply_filters_deprecated')) {
34
+ // apply_filters_deprecated exists since WP 4.6
35
+ echo apply_filters_deprecated("wpstg_before_stage_buttons", [$html = '', $name, $data], '2.7.6', 'wpstg.views.single_overview.before_existing_clones_buttons', 'The replacement filter uses do_action()');
36
+ }
37
  ?>
38
 
39
  <a href="<?php echo $urlLogin ?>" class="wpstg-open-clone wpstg-clone-action" target="_blank" title="<?php echo __( "Open the staging site in a new tab", "wp-staging" ) ?>">
52
  do_action('wpstg.views.single_overview.after_existing_clones_buttons', $name, $data, $license);
53
 
54
  // Todo: Remove in future versions
55
+ if (function_exists('apply_filters_deprecated')) {
56
+ // apply_filters_deprecated exists since WP 4.6
57
+ echo apply_filters_deprecated("wpstg_after_stage_buttons", [$html = '', $name, $data], '2.7.6', 'wpstg.views.single_overview.after_existing_clones_buttons', 'The replacement filter uses do_action()');
58
+ }
59
  ?>
60
 
61
  <div class="wpstg-staging-info">
65
  $cloneDir = ! empty($data['path']) ? $data['path'] : '';
66
  $url = ! empty($data['url']) ? sprintf('<a href="%1$s" target="_blank">%1$s</a>', $data['url']) : '';
67
  $datetime = ! empty($data['datetime']) ? date("D, d M Y H:i:s T", $data['datetime']) : '&nbsp;&nbsp;&nbsp;';
68
+ $owner = ! empty($data['ownerId']) ? get_userdata($data['ownerId']) : null;
69
+ $ownerName = ($owner !== null) ? $owner->user_login : 'N/A';
70
  $statusTooltip = "This clone is incomplete and does not work. Clone or update it again! \n\n".
71
  "Important: Keep the browser open until the cloning is finished. \n".
72
  "It will not proceed if your browser is not open.\n\n".
93
  echo '</br>';
94
  echo sprintf(__('URL: <span class="wpstg-bold">%s</span>', 'wp-staging'), $url);
95
  echo '</br>';
96
+ echo sprintf(__('Created By: <span class="wpstg-bold">%s</span>', 'wp-staging'), $ownerName);
97
+ echo '</br>';
98
  echo $status;
99
  echo '</br>';
100
  echo sprintf(__('Updated: <span>%s</span>', 'wp-staging'), $datetime);
Backend/views/clone/ajax/start.php CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  <div class=successfullying-section">
2
  <h2 id="wpstg-processing-header"><?php echo __("Processing, please wait...", "wp-staging")?></h2>
3
  <div class="wpstg-progress-bar">
1
+ <?php
2
+ /**
3
+ * @see \WPStaging\Backend\Administrator::ajaxStartClone A place where this view is being called.
4
+ * @var \WPStaging\Backend\Modules\Jobs\Cloning $cloning
5
+ */
6
+ ?>
7
  <div class=successfullying-section">
8
  <h2 id="wpstg-processing-header"><?php echo __("Processing, please wait...", "wp-staging")?></h2>
9
  <div class="wpstg-progress-bar">
Backend/views/clone/ajax/update.php CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  <div class=successfullying-section">
2
  <h2 id="wpstg-processing-header"><?php echo __("Processing, please wait...", "wp-staging")?></h2>
3
  <div class="wpstg-progress-bar">
1
+ <?php
2
+ /**
3
+ * @see \WPStaging\Backend\Administrator::ajaxUpdateProcess A place where this view is being called.
4
+ * @var \WPStaging\Backend\Modules\Jobs\Cloning $cloning
5
+ */
6
+ ?>
7
  <div class=successfullying-section">
8
  <h2 id="wpstg-processing-header"><?php echo __("Processing, please wait...", "wp-staging")?></h2>
9
  <div class="wpstg-progress-bar">
Backend/views/notices/directory-listing-could-not-be-prevented.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @see \WPStaging\Backend\Notices\Notices::showDirectoryListingWarningNotice
4
+ * @var array $directoryListingErrors An array of directory listing errors to display.
5
+ */
6
+ ?>
7
+
8
+ <div class='notice-warning notice is-dismissible'>
9
+ <p style='font-weight: bold;'><?php _e('WPSTAGING - Failed to prevent directory listing', 'wp-staging'); ?></p>
10
+ <p><?php _e('Following the best development practices, WPSTAGING tries to prevent directory listing on it\'s own directories
11
+ that might contain sensitive data. This warning tells you that we could not prevent directory listing on one
12
+ of the directories.'); ?></p>
13
+ <p><?php echo implode('<br>', esc_html($directoryListingErrors)); ?></p>
14
+ </div>
Backend/views/settings/main-settings.php CHANGED
@@ -1,436 +1,42 @@
1
  <div class="wpstg_admin">
2
  <?php require_once(WPSTG_PLUGIN_DIR . 'Backend/views/_main/header.php'); ?>
3
 
4
- <ul class="nav-tab-wrapper">
5
- <?php
6
- $tabs = \WPStaging\Core\WPStaging::getInstance()->get("tabs")->get();
7
- $activeTab = (isset($_GET["tab"]) && array_key_exists($_GET["tab"], $tabs)) ? $_GET["tab"] : "general";
8
-
9
- # Loop through tabs
10
- foreach ($tabs as $id => $name):
11
- $url = esc_url(
12
- add_query_arg(
13
- [
14
- "settings-updated" => false,
15
- "tab" => $id
16
- ]
17
- )
18
- );
19
-
20
- $activeClass = ($activeTab === $id) ? " nav-tab-active" : '';
21
- ?>
22
- <li>
23
- <a href="<?php
24
- echo $url ?>" title="<?php
25
- echo esc_attr($name) ?>" class="nav-tab<?php
26
- echo $activeClass ?>">
27
- <?php
28
- echo esc_html($name) ?>
29
- </a>
30
- </li>
31
  <?php
32
- unset($url, $activeClass);
33
- endforeach;
34
- ?>
35
- </ul>
36
- <h2 class="nav-tab-wrapper"></h2>
37
-
38
- <div id="wpstg-tab-container" class="tab_container">
39
- <div class="panel-container">
40
- <form method="post" action="options.php">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  <?php
42
- settings_fields("wpstg_settings");
43
-
44
- foreach ($tabs as $id => $name):
45
- $form = \WPStaging\Core\WPStaging::getInstance()->get("forms")->get($id);
46
-
47
- if ($form === null) {
48
- continue;
49
- }
50
- ?>
51
- <div id="<?php
52
- echo $id ?>__wpstg_header">
53
- <table class="wpstg-form-table">
54
- <thead>
55
- <tr class="row">
56
- <th class="row th" colspan="2">
57
- <div class="col-title">
58
- <strong><?php
59
- echo $name ?></strong>
60
- <span class="description"></span>
61
- </div>
62
- </th>
63
- </tr>
64
- </thead>
65
-
66
- <tbody>
67
- <tr class="row">
68
- <td class="row th">
69
- <div class="col-title">
70
- <?php
71
- echo $form->label("wpstg_settings[queryLimit]")
72
- ?>
73
- <span class="description">
74
- <?php
75
- _e(
76
- "Number of DB rows, that are copied within one ajax query.
77
- The higher the value the faster the database copy process.
78
- To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it
79
- until you get no more errors during copying process.",
80
- "wp-staging"
81
- ); ?>
82
- <br>
83
- <strong> Default: 10000 </strong>
84
- </span>
85
- </div>
86
- </td>
87
- <td>
88
- <?php
89
- echo $form->render("wpstg_settings[queryLimit]") ?>
90
- </td>
91
- </tr>
92
- <tr class="row">
93
- <td class="row th">
94
- <div class="col-title">
95
- <?php
96
- echo $form->label("wpstg_settings[querySRLimit]")
97
- ?>
98
- <span class="description">
99
- <?php
100
- _e(
101
- "Number of DB rows, that are processed within one ajax query.
102
- The higher the value the faster the database search & replace process.
103
- This is a high memory consumptive process. If you get timeouts lower this value!",
104
- "wp-staging"
105
- ); ?>
106
- <br>
107
- <strong> Default: 5000 </strong>
108
- </span>
109
- </div>
110
- </td>
111
- <td>
112
- <?php
113
- echo $form->render("wpstg_settings[querySRLimit]") ?>
114
- </td>
115
- </tr>
116
-
117
- <tr class="row">
118
- <td class="row th">
119
- <div class="col-title">
120
- <?php
121
- echo $form->label("wpstg_settings[fileLimit]")
122
- ?>
123
- <span class="description">
124
- <?php
125
- _e(
126
- "Number of files to copy that will be copied within one ajax request.
127
- The higher the value the faster the file copy process.
128
- To find out the highest possible values try a high value like 500 or more. If you get timeout issues, lower it
129
- until you get no more errors during copying process.",
130
- "wp-staging"
131
- ); ?>
132
- <br>
133
- <br>
134
- <?php
135
- _e(
136
- "<strong>Important:</strong> If CPU Load Priority is <strong>Low</strong> set a file copy limit value of 50 or higher! Otherwise file copying process takes a lot of time.",
137
- "wp-staging"
138
- ); ?>
139
- <br>
140
- <br>
141
- <strong> Default: 50 </strong>
142
- </span>
143
- </div>
144
- </td>
145
- <td>
146
- <?php
147
- echo $form->render("wpstg_settings[fileLimit]") ?>
148
- </td>
149
- </tr>
150
-
151
- <tr class="row">
152
- <td class="row th">
153
- <div class="col-title">
154
- <?php
155
- echo $form->label("wpstg_settings[maxFileSize]") ?>
156
- <span class="description">
157
- <?php
158
- _e(
159
- "Maximum size of the files which are allowed to copy. All files larger than this value will be skipped.
160
- Note: Increase this option only if you have a good reason. Files larger than a few megabytes are in 99% of all cases log and backup files which are not needed on a staging site.",
161
- "wp-staging"
162
- ); ?>
163
- <br>
164
- <strong>Default:</strong> 8 MB
165
- </span>
166
- </div>
167
- </td>
168
- <td>
169
- <?php
170
- echo $form->render("wpstg_settings[maxFileSize]") ?>
171
- </td>
172
- </tr>
173
- <tr class="row">
174
- <td class="row th">
175
- <div class="col-title">
176
- <?php
177
- echo $form->label("wpstg_settings[batchSize]") ?>
178
- <span class="description">
179
- <?php
180
- _e(
181
- "Buffer size for the file copy process in megabyte.
182
- The higher the value the faster large files are copied.
183
- To find out the highest possible values try a high one and lower it until
184
- you get no errors during file copy process. Usually this value correlates directly
185
- with the memory consumption of php so make sure that
186
- it does not exceed any php.ini max_memory limits.",
187
- "wp-staging"
188
- ); ?>
189
- <br>
190
- <strong>Default:</strong> 2 MB
191
- </span>
192
- </div>
193
- </td>
194
- <td>
195
- <?php
196
- echo $form->render("wpstg_settings[batchSize]") ?>
197
- </td>
198
- </tr>
199
-
200
- <tr class="row">
201
- <td class="row th">
202
- <div class="col-title">
203
- <?php
204
- echo $form->label("wpstg_settings[cpuLoad]") ?>
205
- <span class="description">
206
- <?php
207
- _e(
208
- "Using high will result in fast as possible processing but the cpu load
209
- increases and it's also possible that staging process gets interrupted because of too many ajax requests
210
- (e.g. <strong>authorization error</strong>).
211
- Using a lower value results in lower cpu load on your server but also slower staging site creation.",
212
- "wp-staging"
213
- ); ?>
214
- <br>
215
- <strong>Default: </strong> Low
216
- </span>
217
- </div>
218
- </td>
219
- <td>
220
- <?php
221
- echo $form->render("wpstg_settings[cpuLoad]") ?>
222
- </td>
223
- </tr>
224
- <tr class="row">
225
- <td class="row th">
226
- <div class="col-title">
227
- <?php
228
- echo $form->label("wpstg_settings[delayRequests]") ?>
229
- <span class="description">
230
- <?php
231
- _e(
232
- "If your server uses rate limits it blocks requests and WP Staging can be interrupted. You can resolve that by adding one or more seconds of delay between the processing requests. ",
233
- "wp-staging"
234
- ); ?>
235
- <br>
236
- <strong>Default: </strong> 0s
237
- </span>
238
- </div>
239
- </td>
240
- <td>
241
- <?php
242
- echo $form->render("wpstg_settings[delayRequests]") ?>
243
- </td>
244
- </tr>
245
- <?php
246
- if (!defined('WPSTGPRO_VERSION')) {
247
- ?>
248
- <tr class="row">
249
- <td class="row th">
250
- <div class="col-title">
251
- <?php
252
- echo $form->label("wpstg_settings[disableAdminLogin]") ?>
253
- <span class="description">
254
- If you want to remove the requirement to login to the staging site you can deactivate it here.
255
- <strong>Note:</strong> The staging site discourages search engines from indexing the site by setting the 'noindex' tag into header of the staging site.
256
- </span>
257
- </div>
258
- </td>
259
- <td>
260
- <?php
261
- echo $form->render("wpstg_settings[disableAdminLogin]") ?>
262
- </td>
263
- </tr>
264
- <?php
265
- }
266
- ?>
267
- <?php
268
- if (defined('WPSTGPRO_VERSION')) {
269
- ?>
270
- <tr class="row">
271
- <td class="row th">
272
- <div class="col-title">
273
- <?php
274
- echo $form->label("wpstg_settings[keepPermalinks]") ?>
275
- <span class="description">
276
- <?php
277
- echo sprintf(
278
- __(
279
- 'Keep permalinks original setting activated and do not disable permalinks on staging site. <br/>Read more: <a href="%1$s" target="_blank">Permalink Settings</a> ',
280
- 'wp-staging'
281
- ),
282
- 'https://wp-staging.com/docs/activate-permalinks-staging-site/'
283
- ); ?>
284
- </span>
285
- </div>
286
- </td>
287
- <td>
288
- <?php
289
- echo $form->render("wpstg_settings[keepPermalinks]") ?>
290
- </td>
291
- </tr>
292
- <?php
293
- }
294
- ?>
295
- <tr class="row">
296
- <td class="row th">
297
- <div class="col-title">
298
- <?php
299
- echo $form->label("wpstg_settings[debugMode]") ?>
300
- <span class="description">
301
- <?php
302
- _e(
303
- "This will enable an extended debug mode which creates additional entries
304
- in <strong>wp-content/uploads/wp-staging/logs/logfile.log</strong>.
305
- <strong>Do NOT activate this until we ask you to do so!</strong>",
306
- "wp-staging"
307
- ); ?>
308
- </span>
309
- </div>
310
- </td>
311
- <td>
312
- <?php
313
- echo $form->render("wpstg_settings[debugMode]") ?>
314
- </td>
315
- </tr>
316
- <tr class="row">
317
- <td class="row th">
318
- <div class="col-title">
319
- <?php
320
- echo $form->label("wpstg_settings[optimizer]") ?>
321
- <span class="description">
322
- <?php
323
- _e(
324
- "The Optimizer is a mu plugin which disables all other plugins during WP Staging processing. Usually this makes the cloning process more reliable. If you experience issues, disable the Optimizer.",
325
- "wp-staging"
326
- ); ?>
327
- </span>
328
- </div>
329
- </td>
330
- <td>
331
- <?php
332
- echo $form->render("wpstg_settings[optimizer]") ?>
333
- </td>
334
- </tr>
335
-
336
- <tr class="row">
337
- <td class="row th">
338
- <div class="col-title">
339
- <?php
340
- echo $form->label("wpstg_settings[unInstallOnDelete]") ?>
341
- <span class="description">
342
- <?php
343
- _e(
344
- "Check this box if you like WP Staging to completely remove all of its data when the plugin is deleted.
345
- This will not remove staging sites files or database tables.",
346
- "wp-staging"
347
- ); ?>
348
- </span>
349
- </div>
350
- </td>
351
- <td>
352
- <?php
353
- echo $form->render("wpstg_settings[unInstallOnDelete]") ?>
354
- </td>
355
- </tr>
356
 
357
- <tr class="row">
358
- <td class="row th">
359
- <div class="col-title">
360
- <?php
361
- echo $form->label("wpstg_settings[checkDirectorySize]") ?>
362
- <span class="description">
363
- <?php
364
- _e(
365
- "Check this box if you like WP Staging to check sizes of each directory on scanning process.
366
- <br>
367
- Warning this may cause timeout problems in big directory / file structures.",
368
- "wp-staging"
369
- ); ?>
370
- </span>
371
- </div>
372
- </td>
373
- <td>
374
- <?php
375
- echo $form->render("wpstg_settings[checkDirectorySize]") ?>
376
- </td>
377
- </tr>
378
- <?php
379
- if (defined('WPSTGPRO_VERSION')) {
380
- ?>
381
- <tr class="row">
382
- <td class="row th">
383
- <div class="col-title">
384
- <?php
385
- echo $form->label("wpstg_settings[userRoles][]") ?>
386
- <span class="description">
387
- <?php
388
- _e(
389
- 'Select the user role you want to give access to the staging site. You can select multiple roles by holding CTRL or ⌘ Cmd key while clicking. <strong>Change this option on the staging site if you want to change the authentication behavior there.</strong>',
390
- 'wp-staging'
391
- ); ?>
392
- </span>
393
- </div>
394
- </td>
395
- <td>
396
- <?php
397
- echo $form->render("wpstg_settings[userRoles][]") ?>
398
- </td>
399
- </tr>
400
- <tr class="row">
401
- <td class="row th">
402
- <div class="col-title">
403
- <?php
404
- echo $form->label("wpstg_settings[usersWithStagingAccess]") ?>
405
- <span class="description">
406
- <?php
407
- _e(
408
- 'Specify users who will have access to the staging site regardless of their role. You can enter multiple user names separated by a comma. <strong>Change this option on the staging site if you want to change the authentication behavior there.</strong>',
409
- 'wp-staging'
410
- ); ?>
411
- </span>
412
- </div>
413
- </td>
414
- <td>
415
- <?php
416
- echo $form->render("wpstg_settings[usersWithStagingAccess]") ?>
417
- </td>
418
- </tr>
419
- <?php
420
- }
421
- ?>
422
- </tbody>
423
- </table>
424
- </div>
425
- <?php
426
- endforeach;
427
- // Show submit button any tab but add-ons
428
- if ($activeTab !== "add-ons") {
429
- submit_button();
430
- }
431
- unset($tabs);
432
- ?>
433
- </form>
434
  </div>
435
  </div>
436
  </div>
1
  <div class="wpstg_admin">
2
  <?php require_once(WPSTG_PLUGIN_DIR . 'Backend/views/_main/header.php'); ?>
3
 
4
+ <div class="wpstg-tabs-container" id="wpstg-settings">
5
+ <ul class="nav-tab-wrapper">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  <?php
7
+ $tabs = \WPStaging\Core\WPStaging::getInstance()->get("tabs")->get();
8
+ $activeTab = (isset($_GET["tab"]) && array_key_exists($_GET["tab"], $tabs)) ? $_GET["tab"] : "general";
9
+
10
+ # Loop through tabs
11
+ foreach ($tabs as $id => $name):
12
+ $url = esc_url(
13
+ add_query_arg(
14
+ [
15
+ "settings-updated" => false,
16
+ "tab" => $id
17
+ ]
18
+ )
19
+ );
20
+
21
+ $activeClass = ($activeTab === $id) ? " nav-tab-active" : '';
22
+ ?>
23
+ <li>
24
+ <a href="<?php
25
+ echo $url ?>" title="<?php
26
+ echo esc_attr($name) ?>" class="nav-tab<?php
27
+ echo $activeClass ?>">
28
+ <?php
29
+ echo esc_html($name) ?>
30
+ </a>
31
+ </li>
32
  <?php
33
+ unset($url, $activeClass);
34
+ endforeach;
35
+ ?>
36
+ </ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
+ <div class="wpstg-metabox-holder">
39
+ <?php require_once $this->path . "views/settings/tabs/" . $activeTab . ".php"?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  </div>
41
  </div>
42
  </div>
Backend/views/settings/tabs/general.php ADDED
@@ -0,0 +1,401 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- General Settings -->
2
+ <div id="wpstg-tab-container" class="tab_container">
3
+ <form class="wpstg-settings-panel" method="post" action="options.php">
4
+ <?php
5
+ settings_fields("wpstg_settings");
6
+
7
+ foreach ($tabs as $id => $name):
8
+ if ($id === 'mail-settings') {
9
+ continue;
10
+ }
11
+
12
+ $form = \WPStaging\Core\WPStaging::getInstance()->get("forms")->get($id);
13
+
14
+ if ($form === null) {
15
+ continue;
16
+ }
17
+ ?>
18
+ <div id="<?php
19
+ echo $id ?>__wpstg_header">
20
+ <table class="wpstg-form-table">
21
+ <thead>
22
+ <tr class="row">
23
+ <th class="row th" colspan="2">
24
+ <div class="col-title">
25
+ <strong><?php
26
+ echo $name ?></strong>
27
+ <span class="description"></span>
28
+ </div>
29
+ </th>
30
+ </tr>
31
+ </thead>
32
+
33
+ <tbody>
34
+ <tr class="row">
35
+ <td class="row th">
36
+ <div class="col-title">
37
+ <?php
38
+ echo $form->label("wpstg_settings[queryLimit]")
39
+ ?>
40
+ <span class="description">
41
+ <?php
42
+ _e(
43
+ "Number of DB rows, that are copied within one ajax query.
44
+ The higher the value the faster the database copy process.
45
+ To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it
46
+ until you get no more errors during copying process.",
47
+ "wp-staging"
48
+ ); ?>
49
+ <br>
50
+ <strong> Default: 10000 </strong>
51
+ </span>
52
+ </div>
53
+ </td>
54
+ <td>
55
+ <?php
56
+ echo $form->render("wpstg_settings[queryLimit]") ?>
57
+ </td>
58
+ </tr>
59
+ <tr class="row">
60
+ <td class="row th">
61
+ <div class="col-title">
62
+ <?php
63
+ echo $form->label("wpstg_settings[querySRLimit]")
64
+ ?>
65
+ <span class="description">
66
+ <?php
67
+ _e(
68
+ "Number of DB rows, that are processed within one ajax query.
69
+ The higher the value the faster the database search & replace process.
70
+ This is a high memory consumptive process. If you get timeouts lower this value!",
71
+ "wp-staging"
72
+ ); ?>
73
+ <br>
74
+ <strong> Default: 5000 </strong>
75
+ </span>
76
+ </div>
77
+ </td>
78
+ <td>
79
+ <?php
80
+ echo $form->render("wpstg_settings[querySRLimit]") ?>
81
+ </td>
82
+ </tr>
83
+
84
+ <tr class="row">
85
+ <td class="row th">
86
+ <div class="col-title">
87
+ <?php
88
+ echo $form->label("wpstg_settings[fileLimit]")
89
+ ?>
90
+ <span class="description">
91
+ <?php
92
+ _e(
93
+ "Number of files to copy that will be copied within one ajax request.
94
+ The higher the value the faster the file copy process.
95
+ To find out the highest possible values try a high value like 500 or more. If you get timeout issues, lower it
96
+ until you get no more errors during copying process.",
97
+ "wp-staging"
98
+ ); ?>
99
+ <br>
100
+ <br>
101
+ <?php
102
+ _e(
103
+ "<strong>Important:</strong> If CPU Load Priority is <strong>Low</strong> set a file copy limit value of 50 or higher! Otherwise file copying process takes a lot of time.",
104
+ "wp-staging"
105
+ ); ?>
106
+ <br>
107
+ <br>
108
+ <strong> Default: 50 </strong>
109
+ </span>
110
+ </div>
111
+ </td>
112
+ <td>
113
+ <?php
114
+ echo $form->render("wpstg_settings[fileLimit]") ?>
115
+ </td>
116
+ </tr>
117
+
118
+ <tr class="row">
119
+ <td class="row th">
120
+ <div class="col-title">
121
+ <?php
122
+ echo $form->label("wpstg_settings[maxFileSize]") ?>
123
+ <span class="description">
124
+ <?php
125
+ _e(
126
+ "Maximum size of the files which are allowed to copy. All files larger than this value will be skipped.
127
+ Note: Increase this option only if you have a good reason. Files larger than a few megabytes are in 99% of all cases log and backup files which are not needed on a staging site.",
128
+ "wp-staging"
129
+ ); ?>
130
+ <br>
131
+ <strong>Default:</strong> 8 MB
132
+ </span>
133
+ </div>
134
+ </td>
135
+ <td>
136
+ <?php
137
+ echo $form->render("wpstg_settings[maxFileSize]") ?>
138
+ </td>
139
+ </tr>
140
+ <tr class="row">
141
+ <td class="row th">
142
+ <div class="col-title">
143
+ <?php
144
+ echo $form->label("wpstg_settings[batchSize]") ?>
145
+ <span class="description">
146
+ <?php
147
+ _e(
148
+ "Buffer size for the file copy process in megabyte.
149
+ The higher the value the faster large files are copied.
150
+ To find out the highest possible values try a high one and lower it until
151
+ you get no errors during file copy process. Usually this value correlates directly
152
+ with the memory consumption of php so make sure that
153
+ it does not exceed any php.ini max_memory limits.",
154
+ "wp-staging"
155
+ ); ?>
156
+ <br>
157
+ <strong>Default:</strong> 2 MB
158
+ </span>
159
+ </div>
160
+ </td>
161
+ <td>
162
+ <?php
163
+ echo $form->render("wpstg_settings[batchSize]") ?>
164
+ </td>
165
+ </tr>
166
+
167
+ <tr class="row">
168
+ <td class="row th">
169
+ <div class="col-title">
170
+ <?php
171
+ echo $form->label("wpstg_settings[cpuLoad]") ?>
172
+ <span class="description">
173
+ <?php
174
+ _e(
175
+ "Using high will result in fast as possible processing but the cpu load
176
+ increases and it's also possible that staging process gets interrupted because of too many ajax requests
177
+ (e.g. <strong>authorization error</strong>).
178
+ Using a lower value results in lower cpu load on your server but also slower staging site creation.",
179
+ "wp-staging"
180
+ ); ?>
181
+ <br>
182
+ <strong>Default: </strong> Low
183
+ </span>
184
+ </div>
185
+ </td>
186
+ <td>
187
+ <?php
188
+ echo $form->render("wpstg_settings[cpuLoad]") ?>
189
+ </td>
190
+ </tr>
191
+ <tr class="row">
192
+ <td class="row th">
193
+ <div class="col-title">
194
+ <?php
195
+ echo $form->label("wpstg_settings[delayRequests]") ?>
196
+ <span class="description">
197
+ <?php
198
+ _e(
199
+ "If your server uses rate limits it blocks requests and WP Staging can be interrupted. You can resolve that by adding one or more seconds of delay between the processing requests. ",
200
+ "wp-staging"
201
+ ); ?>
202
+ <br>
203
+ <strong>Default: </strong> 0s
204
+ </span>
205
+ </div>
206
+ </td>
207
+ <td>
208
+ <?php
209
+ echo $form->render("wpstg_settings[delayRequests]") ?>
210
+ </td>
211
+ </tr>
212
+ <?php
213
+ if (!defined('WPSTGPRO_VERSION')) {
214
+ ?>
215
+ <tr class="row">
216
+ <td class="row th">
217
+ <div class="col-title">
218
+ <?php
219
+ echo $form->label("wpstg_settings[disableAdminLogin]") ?>
220
+ <span class="description">
221
+ If you want to remove the requirement to login to the staging site you can deactivate it here.
222
+ <strong>Note:</strong> The staging site discourages search engines from indexing the site by setting the 'noindex' tag into header of the staging site.
223
+ </span>
224
+ </div>
225
+ </td>
226
+ <td>
227
+ <?php
228
+ echo $form->render("wpstg_settings[disableAdminLogin]") ?>
229
+ </td>
230
+ </tr>
231
+ <?php
232
+ }
233
+ ?>
234
+ <?php
235
+ if (defined('WPSTGPRO_VERSION')) {
236
+ ?>
237
+ <tr class="row">
238
+ <td class="row th">
239
+ <div class="col-title">
240
+ <?php
241
+ echo $form->label("wpstg_settings[keepPermalinks]") ?>
242
+ <span class="description">
243
+ <?php
244
+ echo sprintf(
245
+ __(
246
+ 'Keep permalinks original setting activated and do not disable permalinks on staging site. <br/>Read more: <a href="%1$s" target="_blank">Permalink Settings</a> ',
247
+ 'wp-staging'
248
+ ),
249
+ 'https://wp-staging.com/docs/activate-permalinks-staging-site/'
250
+ ); ?>
251
+ </span>
252
+ </div>
253
+ </td>
254
+ <td>
255
+ <?php
256
+ echo $form->render("wpstg_settings[keepPermalinks]") ?>
257
+ </td>
258
+ </tr>
259
+ <?php
260
+ }
261
+ ?>
262
+ <tr class="row">
263
+ <td class="row th">
264
+ <div class="col-title">
265
+ <?php
266
+ echo $form->label("wpstg_settings[debugMode]") ?>
267
+ <span class="description">
268
+ <?php
269
+ _e(
270
+ "This will enable an extended debug mode which creates additional entries
271
+ in <strong>wp-content/uploads/wp-staging/logs/logfile.log</strong>.
272
+ <strong>Do NOT activate this until we ask you to do so!</strong>",
273
+ "wp-staging"
274
+ ); ?>
275
+ </span>
276
+ </div>
277
+ </td>
278
+ <td>
279
+ <?php
280
+ echo $form->render("wpstg_settings[debugMode]") ?>
281
+ </td>
282
+ </tr>
283
+ <tr class="row">
284
+ <td class="row th">
285
+ <div class="col-title">
286
+ <?php
287
+ echo $form->label("wpstg_settings[optimizer]") ?>
288
+ <span class="description">
289
+ <?php
290
+ _e(
291
+ "The Optimizer is a mu plugin which disables all other plugins during WP Staging processing. Usually this makes the cloning process more reliable. If you experience issues, disable the Optimizer.",
292
+ "wp-staging"
293
+ ); ?>
294
+ </span>
295
+ </div>
296
+ </td>
297
+ <td>
298
+ <?php
299
+ echo $form->render("wpstg_settings[optimizer]") ?>
300
+ </td>
301
+ </tr>
302
+
303
+ <tr class="row">
304
+ <td class="row th">
305
+ <div class="col-title">
306
+ <?php
307
+ echo $form->label("wpstg_settings[unInstallOnDelete]") ?>
308
+ <span class="description">
309
+ <?php
310
+ _e(
311
+ "Check this box if you like WP Staging to completely remove all of its data when the plugin is deleted.
312
+ This will not remove staging sites files or database tables.",
313
+ "wp-staging"
314
+ ); ?>
315
+ </span>
316
+ </div>
317
+ </td>
318
+ <td>
319
+ <?php
320
+ echo $form->render("wpstg_settings[unInstallOnDelete]") ?>
321
+ </td>
322
+ </tr>
323
+
324
+ <tr class="row">
325
+ <td class="row th">
326
+ <div class="col-title">
327
+ <?php
328
+ echo $form->label("wpstg_settings[checkDirectorySize]") ?>
329
+ <span class="description">
330
+ <?php
331
+ _e(
332
+ "Check this box if you like WP Staging to check sizes of each directory on scanning process.
333
+ <br>
334
+ Warning this may cause timeout problems in big directory / file structures.",
335
+ "wp-staging"
336
+ ); ?>
337
+ </span>
338
+ </div>
339
+ </td>
340
+ <td>
341
+ <?php
342
+ echo $form->render("wpstg_settings[checkDirectorySize]") ?>
343
+ </td>
344
+ </tr>
345
+ <?php
346
+ if (defined('WPSTGPRO_VERSION')) {
347
+ ?>
348
+ <tr class="row">
349
+ <td class="row th">
350
+ <div class="col-title">
351
+ <?php
352
+ echo $form->label("wpstg_settings[userRoles][]") ?>
353
+ <span class="description">
354
+ <?php
355
+ _e(
356
+ 'Select the user role you want to give access to the staging site. You can select multiple roles by holding CTRL or ⌘ Cmd key while clicking. <strong>Change this option on the staging site if you want to change the authentication behavior there.</strong>',
357
+ 'wp-staging'
358
+ ); ?>
359
+ </span>
360
+ </div>
361
+ </td>
362
+ <td>
363
+ <?php
364
+ echo $form->render("wpstg_settings[userRoles][]") ?>
365
+ </td>
366
+ </tr>
367
+ <tr class="row">
368
+ <td class="row th">
369
+ <div class="col-title">
370
+ <?php
371
+ echo $form->label("wpstg_settings[usersWithStagingAccess]") ?>
372
+ <span class="description">
373
+ <?php
374
+ _e(
375
+ 'Specify users who will have access to the staging site regardless of their role. You can enter multiple user names separated by a comma. <strong>Change this option on the staging site if you want to change the authentication behavior there.</strong>',
376
+ 'wp-staging'
377
+ ); ?>
378
+ </span>
379
+ </div>
380
+ </td>
381
+ <td>
382
+ <?php
383
+ echo $form->render("wpstg_settings[usersWithStagingAccess]") ?>
384
+ </td>
385
+ </tr>
386
+ <?php
387
+ }
388
+ ?>
389
+ </tbody>
390
+ </table>
391
+ </div>
392
+ <?php
393
+ endforeach;
394
+ // Show submit button any tab but add-ons
395
+ if ($activeTab !== "add-ons") {
396
+ submit_button();
397
+ }
398
+ unset($tabs);
399
+ ?>
400
+ </form>
401
+ </div>
Backend/views/settings/tabs/mail-settings.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <form class="wpstg-mail-settings-form" method="post">
2
+ <?php $emailsAllowed = !((bool)get_option(\WPStaging\Framework\Staging\FirstRun::MAILS_DISABLED_KEY, false)); ?>
3
+ <p>
4
+ <strong class="wpstg-fs-14"> <?php _e('Mail Delivery Setting', 'wp-staging'); ?></strong>
5
+ <br/>
6
+ <?php _e('Toggle mails sending for this staging site', 'wp-staging'); ?>
7
+ </p>
8
+
9
+ <div class="wpstg-form-group">
10
+ <label class="wpstg-checkbox" for="wpstg_allow_emails">
11
+ <?php _e('Allow Mails Sending:', 'wp-staging'); ?> <input type="checkbox" name="wpstg_allow_emails" id="wpstg_allow_emails" <?php echo $emailsAllowed === true ? 'checked' : '' ?>>
12
+ </label>
13
+ </div>
14
+
15
+ <p>
16
+ <b><?php _e('Note', 'wp-staging') ?>: </b> <?php echo sprintf(__('Some plugins might still be able to send out mails if they don\'t depend upon %s.', 'wp-staging'), '<code>wp_mail()</code>', '<code>wp_mail()</code>', '<strong>WP STAGING</strong>'); ?>
17
+ </p>
18
+
19
+ <button type="button" id="wpstg-update-mail-settings" class="wpstg-link-btn wpstg-blue-primary"><?php _e("Update Settings", "wp-staging") ?></button>
20
+ </form>
Backend/views/tools/index.php CHANGED
@@ -1,34 +1,34 @@
1
- <?php require_once(WPSTG_PLUGIN_DIR . 'Backend/views/_main/header.php'); ?>
2
-
3
- <div class="wrap" id="wpstg-tools">
4
- <ul class="nav-tab-wrapper">
5
- <?php
6
- $tabs = \WPStaging\Core\WPStaging::getInstance()->get("tabs")->get();
7
- $activeTab = (isset($_GET["tab"]) && array_key_exists($_GET["tab"], $tabs)) ? $_GET["tab"] : "system_info";
8
 
9
- # Loop through tabs
10
- foreach ($tabs as $id => $name):
11
- $url = esc_url(add_query_arg([
12
- "settings-updated" => false,
13
- "tab" => $id
14
- ]));
15
-
16
- $activeClass = ($activeTab === $id) ? " nav-tab-active" : '';
17
- ?>
18
- <li>
19
- <a href="<?php echo $url?>" title="<?php echo esc_attr($name)?>" class="nav-tab<?php echo $activeClass?>">
20
- <?php echo esc_html($name)?>
21
- </a>
22
- </li>
23
  <?php
24
- unset($url, $activeClass);
25
- endforeach;
26
- ?>
27
- </ul>
28
 
29
- <h2 class="wpstg-nav-tab-wrapper"></h2>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
- <div class="metabox-holder">
32
- <?php require_once $this->path . "views/tools/tabs/" . $activeTab . ".php"?>
 
33
  </div>
34
  </div>
1
+ <div class="wpstg_admin">
2
+ <?php require_once(WPSTG_PLUGIN_DIR . 'Backend/views/_main/header.php'); ?>
 
 
 
 
 
3
 
4
+ <div class="wpstg-tabs-container" id="wpstg-tools">
5
+ <ul class="nav-tab-wrapper">
 
 
 
 
 
 
 
 
 
 
 
 
6
  <?php
7
+ $tabs = \WPStaging\Core\WPStaging::getInstance()->get("tabs")->get();
8
+ $activeTab = (isset($_GET["tab"]) && array_key_exists($_GET["tab"], $tabs)) ? $_GET["tab"] : "system_info";
 
 
9
 
10
+ # Loop through tabs
11
+ foreach ($tabs as $id => $name):
12
+ $url = esc_url(add_query_arg([
13
+ "settings-updated" => false,
14
+ "tab" => $id
15
+ ]));
16
+
17
+ $activeClass = ($activeTab === $id) ? " nav-tab-active" : '';
18
+ ?>
19
+ <li>
20
+ <a href="<?php echo $url?>" title="<?php echo esc_attr($name)?>" class="nav-tab<?php echo $activeClass?>">
21
+ <?php echo esc_html($name)?>
22
+ </a>
23
+ </li>
24
+ <?php
25
+ unset($url, $activeClass);
26
+ endforeach;
27
+ ?>
28
+ </ul>
29
 
30
+ <div class="wpstg-metabox-holder">
31
+ <?php require_once $this->path . "views/tools/tabs/" . $activeTab . ".php"?>
32
+ </div>
33
  </div>
34
  </div>
Bootstrap/V1/Requirements/WpstgFreeRequirements.php DELETED
@@ -1,183 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Bootstrap\V1;
4
-
5
- if (!class_exists(WpstgFreeRequirements::class)) {
6
- require_once 'WpstgRequirements.php';
7
-
8
- class WpstgFreeRequirements extends WpstgRequirements
9
- {
10
- /** @var string */
11
- private $proPlugin;
12
-
13
- /** @var string */
14
- private $freePlugin;
15
-
16
- public function checkRequirements()
17
- {
18
- $this->proVersionMustNotBeEnabled();
19
- $this->anotherInstanceOfWpstagingMustNotBeEnabled();
20
- }
21
-
22
- /**
23
- * Prevent conflicts when WP STAGING Pro is active.
24
- */
25
- private function proVersionMustNotBeEnabled()
26
- {
27
- // Early bail: Pro is not loaded, therefore there's no need for further checks.
28
- if (!defined('WPSTG_PRO_LOADED')) {
29
- return;
30
- }
31
-
32
- $this->proPlugin = plugin_basename(WPSTG_PRO_LOADED);
33
- $this->freePlugin = plugin_basename($this->pluginFile);
34
-
35
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
36
-
37
- if (is_multisite()) {
38
- if (is_network_admin()) {
39
- $this->deactivateFreePluginOnMultisiteNetworkLevel();
40
- } else {
41
- $this->deactivateFreeOrProPluginOnMultisite();
42
- }
43
- } else {
44
- $this->deactivateFreePluginIfProIsActiveOnSingleSite();
45
- }
46
- }
47
-
48
- /**
49
- * Catch-all to prevent conflicts when another instance of WP STAGING is active.
50
- */
51
- private function anotherInstanceOfWpstagingMustNotBeEnabled()
52
- {
53
- $oldVersionsLoaded = defined('WPSTG_PLUGIN_DIR') || defined('WPSTG_PLUGIN_FILE');
54
- $proVersionLoaded = defined('WPSTG_PRO_LOADED');
55
- $anotherFreeVersionLoaded = defined('WPSTG_FREE_LOADED') && $this->pluginFile !== WPSTG_FREE_LOADED;
56
-
57
- if ($oldVersionsLoaded || $proVersionLoaded || $anotherFreeVersionLoaded) {
58
- $this->notificationMessage = __('Another instance of WP STAGING is activated, therefore other instances of WP STAGING were automatically prevented from running to avoid errors. Please leave only one instance of the WP STAGING plugin active.', 'wp-staging');
59
-
60
- if (is_network_admin() && current_user_can('manage_network_plugins')) {
61
- add_action('network_admin_notices', [$this, '_displayWarning']);
62
- } elseif (!is_network_admin() && current_user_can('activate_plugins')) {
63
- add_action('admin_notices', [$this, '_displayWarning']);
64
- } elseif (!is_network_admin() && !current_user_can('activate_plugins')) {
65
- $this->notificationMessage = __('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Please ask the site administrator to leave only one instance of WP STAGING active.', 'wp-staging');
66
- add_action('admin_notices', [$this, '_displayWarning']);
67
- }
68
- if (defined('WPSTG_DEBUG') && WPSTG_DEBUG === true) {
69
- throw new \RuntimeException(sprintf("Another instance of WP STAGING is activated, therefore other instances of WP STAGING were automatically prevented from running to avoid errors. Please leave only one instance of the WP STAGING plugin active. Plugin that was prevented from loading: %s", $this->pluginFile));
70
- }
71
- }
72
- }
73
-
74
- /**
75
- * Pro active
76
- * Free active
77
- *
78
- * Result: Deactivate Free
79
- */
80
- private function deactivateFreePluginIfProIsActiveOnSingleSite()
81
- {
82
- if (is_plugin_active($this->proPlugin)) {
83
- if (current_user_can('deactivate_plugin', $this->freePlugin)) {
84
- unset($_GET['activate']);
85
- deactivate_plugins($this->freePlugin);
86
-
87
- $this->notificationMessage = __('WP STAGING Pro is activated, therefore WP STAGING Basic was automatically disabled.', 'wp-staging');
88
- add_action('admin_notices', [$this, '_displayWarning']);
89
-
90
- throw new \RuntimeException(sprintf("WP STAGING Pro is activated, therefore WP STAGING Basic was automatically disabled. Pro version that was active: %s, Basic version that was disabled: %s", $this->proPlugin, $this->freePlugin));
91
- }
92
-
93
- $this->notificationMessage = __('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Please ask the site administrator to leave only one instance of WP STAGING active.', 'wp-staging');
94
- add_action('admin_notices', [$this, '_displayWarning']);
95
-
96
- throw new \RuntimeException(sprintf('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Plugin that was loaded: %s, Plugin that was prevented from loading: %s', $this->freePlugin, $this->proPlugin));
97
- }
98
- }
99
-
100
- private function deactivateFreeOrProPluginOnMultisite()
101
- {
102
- /*
103
- * Pro active site-level
104
- * Free active site-level
105
- * Free deactivated network-level
106
- *
107
- * Result: Deactivate Free site-level
108
- */
109
- if (
110
- is_plugin_active($this->proPlugin) &&
111
- is_plugin_active($this->freePlugin) &&
112
- !is_plugin_active_for_network($this->freePlugin)
113
- ) {
114
- if (current_user_can('deactivate_plugin', $this->freePlugin)) {
115
- unset($_GET['activate']);
116
- deactivate_plugins($this->freePlugin);
117
-
118
- $this->notificationMessage = __('WP STAGING Pro is activated, therefore WP STAGING Basic was automatically disabled.', 'wp-staging');
119
- add_action('admin_notices', [$this, '_displayWarning']);
120
-
121
- throw new \RuntimeException(sprintf("WP STAGING Pro is activated, therefore WP STAGING Basic was automatically disabled. Pro version that was active: %s, Basic version that was disabled: %s", $this->proPlugin, $this->freePlugin));
122
- }
123
-
124
- $this->notificationMessage = __('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Please ask the site administrator to leave only one instance of WP STAGING active.', 'wp-staging');
125
- add_action('admin_notices', [$this, '_displayWarning']);
126
-
127
- throw new \RuntimeException(sprintf('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Plugin that was loaded: %s, Plugin that was prevented from loading: %s', $this->freePlugin, $this->proPlugin));
128
- }
129
-
130
- /*
131
- * Pro active site-level
132
- * Free active network-level
133
- *
134
- * Result: Deactivate Pro site-level
135
- */
136
- if (
137
- is_plugin_active($this->proPlugin) &&
138
- is_plugin_active_for_network($this->freePlugin)
139
- ) {
140
- if (current_user_can('deactivate_plugin', $this->proPlugin)) {
141
- unset($_GET['activate']);
142
- deactivate_plugins($this->proPlugin);
143
-
144
- $this->notificationMessage = __('WP STAGING Basic is activated network-wide, therefore WP STAGING Pro was automatically disabled. Please disable WP STAGING Basic network-wide to enable WP STAGING Pro.', 'wp-staging');
145
- add_action('admin_notices', [$this, '_displayWarning']);
146
-
147
- throw new \RuntimeException(sprintf("WP STAGING Basic is activated networkwide, therefore WP STAGING Pro was automatically disabled. Basic version that was activate networkwide: %s, Pro version that was disabled: %s", $this->freePlugin, $this->proPlugin));
148
- }
149
-
150
- $this->notificationMessage = __('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Please ask the site administrator to leave only one instance of WP STAGING active.', 'wp-staging');
151
- add_action('admin_notices', [$this, '_displayWarning']);
152
-
153
- throw new \RuntimeException(sprintf('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Plugin that was loaded: %s, Plugin that was prevented from loading: %s', $this->freePlugin, $this->proPlugin));
154
- }
155
- }
156
-
157
- /*
158
- * Pro active network-level
159
- * Free active network-level
160
- *
161
- * Result: Deactivate Free network-level
162
- */
163
- private function deactivateFreePluginOnMultisiteNetworkLevel()
164
- {
165
- if (is_plugin_active_for_network($this->proPlugin)) {
166
- if (current_user_can('manage_network_plugins')) {
167
- unset($_GET['activate']);
168
- deactivate_plugins($this->freePlugin, null, true);
169
-
170
- $this->notificationMessage = __('WP STAGING Pro is activated network-wide, therefore WP STAGING Basic was disabled network-wide.', 'wp-staging');
171
- add_action('network_admin_notices', [$this, '_displayWarning']);
172
-
173
- throw new \RuntimeException(sprintf("WP STAGING Pro is activated networkwide, therefore WP STAGING Basic networkwide was automatically disabled. Pro version that was activate networkwide: %s, Basic version that was disabled networkwide: %s", $this->proPlugin, $this->freePlugin));
174
- }
175
-
176
- $this->notificationMessage = __('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Please ask the site administrator to leave only one instance of WP STAGING active.', 'wp-staging');
177
- add_action('admin_notices', [$this, '_displayWarning']);
178
-
179
- throw new \RuntimeException(sprintf('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Plugin that was loaded: %s, Plugin that was prevented from loading: %s', $this->freePlugin, $this->proPlugin));
180
- }
181
- }
182
- }
183
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Bootstrap/V1/Requirements/WpstgProRequirements.php DELETED
@@ -1,40 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Bootstrap\V1;
4
-
5
- if (!class_exists(WpstgProRequirements::class)) {
6
- require_once 'WpstgRequirements.php';
7
-
8
- class WpstgProRequirements extends WpstgRequirements
9
- {
10
- public function checkRequirements()
11
- {
12
- $this->anotherInstanceOfWpstagingMustNotBeEnabled();
13
- }
14
-
15
- /**
16
- * Catch-all to prevent conflicts when another instance of WPSTAGING is active.
17
- */
18
- private function anotherInstanceOfWpstagingMustNotBeEnabled()
19
- {
20
- $oldVersionsLoaded = defined('WPSTG_PLUGIN_DIR') || defined('WPSTG_PLUGIN_FILE');
21
- $anotherProVersionLoaded = defined('WPSTG_PRO_LOADED') && $this->pluginFile !== WPSTG_PRO_LOADED;
22
-
23
- if ($oldVersionsLoaded || $anotherProVersionLoaded) {
24
- $this->notificationMessage = __('Another instance of WP STAGING is activated, therefore other instances of WP STAGING were automatically prevented from running to avoid errors. Please leave only one instance of the WP STAGING plugin active.', 'wp-staging');
25
-
26
- if (is_network_admin() && current_user_can('manage_network_plugins')) {
27
- add_action('network_admin_notices', [$this, '_displayWarning']);
28
- } elseif (!is_network_admin() && current_user_can('activate_plugins')) {
29
- add_action('admin_notices', [$this, '_displayWarning']);
30
- } elseif (!is_network_admin() && !current_user_can('activate_plugins')) {
31
- $this->notificationMessage = __('Another instance of WP STAGING was activated, therefore other instances of the WP STAGING plugin were prevented from loading. Please ask the site administrator to leave only one instance of WP STAGING active.', 'wp-staging');
32
- add_action('admin_notices', [$this, '_displayWarning']);
33
- }
34
- if (defined('WPSTG_DEBUG') && WPSTG_DEBUG === true){
35
- throw new \RuntimeException(sprintf("Another instance of WP STAGING is activated, therefore other instances of WP STAGING were automatically prevented from running to avoid errors. Please leave only one instance of the WP STAGING plugin active. Plugin that was prevented from loading: %s", $this->pluginFile));
36
- }
37
- }
38
- }
39
- }
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Bootstrap/V1/Requirements/WpstgRequirements.php DELETED
@@ -1,36 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Bootstrap\V1;
4
-
5
- if (!class_exists(WpstgRequirements::class)) {
6
- abstract class WpstgRequirements
7
- {
8
- protected $notificationTitle = '';
9
- protected $notificationMessage = '';
10
- protected $pluginFile;
11
-
12
- public function __construct($pluginFile)
13
- {
14
- $this->pluginFile = $pluginFile;
15
- }
16
-
17
- abstract public function checkRequirements();
18
-
19
- public function _displayWarning()
20
- {
21
- $title = esc_html($this->notificationTitle ?: __('WP STAGING', 'wp-staging'));
22
- $message = wp_kses_post($this->notificationMessage);
23
-
24
- echo <<<MESSAGE
25
- <div class="notice-warning notice is-dismissible">
26
- <p style="font-weight: bold;">$title</p>
27
- <p>$message</p>
28
- </div>
29
- MESSAGE;
30
-
31
- // Cleanup the state.
32
- $this->notificationTitle = '';
33
- $this->notificationMessage = '';
34
- }
35
- }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Bootstrap/V1/WpstgBootstrap.php DELETED
@@ -1,61 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Bootstrap\V1;
4
-
5
- if (!class_exists(WpstgBootstrap::class)) {
6
- abstract class WpstgBootstrap
7
- {
8
- private $shouldBootstrap = true;
9
- private $rootPath;
10
- private $requirements;
11
-
12
- /**
13
- * WpstgBootstrap constructor.
14
- *
15
- * @param string $rootPath
16
- * @param WpstgRequirements $requirements
17
- */
18
- public function __construct($rootPath, WpstgRequirements $requirements)
19
- {
20
- $this->rootPath = $rootPath;
21
- $this->requirements = $requirements;
22
- }
23
-
24
- abstract protected function afterBootstrap();
25
-
26
- public function checkRequirements()
27
- {
28
- try {
29
- $this->requirements->checkRequirements();
30
- } catch (\Exception $e) {
31
- $this->shouldBootstrap = false;
32
-
33
- if (defined('WP_DEBUG') && WP_DEBUG) {
34
- error_log(sprintf("[Activation] WP STAGING: %s", $e->getMessage()));
35
- }
36
- }
37
- }
38
-
39
- public function bootstrap()
40
- {
41
- // Early bail: Requirements not met.
42
- if (!$this->shouldBootstrap) {
43
- return;
44
- }
45
-
46
- if (file_exists(__DIR__ . '/../autoloader_dev.php')) {
47
- require_once __DIR__ . '/../autoloader_dev.php';
48
- } else {
49
- require_once __DIR__ . '/../autoloader.php';
50
- }
51
-
52
- $this->afterBootstrap();
53
- }
54
-
55
- public function passedRequirements()
56
- {
57
- return $this->shouldBootstrap;
58
- }
59
- }
60
- }
61
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Job/AbstractJob.php CHANGED
@@ -1,8 +1,5 @@
1
  <?php
2
 
3
- // If child class has `init` method, it will be executed with constructor to prepare the class for job execution
4
- // Such as setting -if needed- total steps, current step etc.
5
-
6
  namespace WPStaging\Component\Job;
7
 
8
  use WPStaging\Component\Task\TaskResponseDto;
1
  <?php
2
 
 
 
 
3
  namespace WPStaging\Component\Job;
4
 
5
  use WPStaging\Component\Task\TaskResponseDto;
Component/Job/AbstractQueueJob.php CHANGED
@@ -10,15 +10,13 @@ use WPStaging\Component\Task\TaskInterface;
10
  use WPStaging\Component\Task\TaskResponseDto;
11
  use WPStaging\Framework\Queue\Queue;
12
  use WPStaging\Framework\Queue\Storage\CacheStorage;
13
- use WPStaging\Framework\Queue\Storage\StorageInterface;
14
  use WPStaging\Framework\Utils\Cache\Cache;
15
- use WPStaging\Repository\SettingsRepository;
16
  use WPStaging\Core\WPStaging;
17
 
18
  abstract class AbstractQueueJob extends AbstractJob
19
  {
20
  /** @var Cache */
21
- protected $cache;
22
 
23
  /** @var QueueJobDto */
24
  protected $dto;
@@ -26,17 +24,20 @@ abstract class AbstractQueueJob extends AbstractJob
26
  /** @var Queue */
27
  protected $queue;
28
 
 
 
 
 
 
29
  /** @var TaskInterface */
30
  protected $currentTask;
31
 
32
- public function __construct()
33
  {
34
- $this->setQueue();
35
-
36
- $this->cache = clone WPStaging::getInstance()->get(Cache::class);
37
- $this->cache->setLifetime(HOUR_IN_SECONDS);
38
- $this->cache->setFilename('job_' . $this->getJobName());
39
- $this->cache->setPath(trailingslashit($this->cache->getPath() . $this->getJobName()));
40
 
41
  $this->setUp();
42
  }
@@ -49,10 +50,11 @@ abstract class AbstractQueueJob extends AbstractJob
49
 
50
  if ($this->dto->isFinished()) {
51
  $this->clean();
 
52
  return;
53
  }
54
 
55
- $this->cache->save($this->dto->toArray());
56
  }
57
 
58
  abstract protected function initiateTasks();
@@ -71,14 +73,6 @@ abstract class AbstractQueueJob extends AbstractJob
71
  $this->queue->reset();
72
  }
73
 
74
- /**
75
- * @return string
76
- */
77
- protected function getDtoClass()
78
- {
79
- return QueueJobDto::class;
80
- }
81
-
82
  protected function prepare()
83
  {
84
  if (method_exists($this, 'provideRequestDto')) {
@@ -94,18 +88,21 @@ abstract class AbstractQueueJob extends AbstractJob
94
 
95
  protected function setUp()
96
  {
 
 
 
 
 
 
97
  if (!empty($_POST['reset']) && ($_POST['reset'] === true || $_POST['reset'] === 'true')) {
98
  /** @noinspection DynamicInvocationViaScopeResolutionInspection */
99
  AbstractJob::clean();
100
  }
101
 
102
- $dtoClass = $this->getDtoClass();
103
- /** @var QueueJobDto $dto */
104
- $this->dto = (new $dtoClass);
105
  $this->dto->setInit(true);
106
  $this->dto->setFinished(false);
107
 
108
- $data = $this->cache->get([]);
109
  if ($data) {
110
  $this->dto->hydrate($data);
111
  }
@@ -138,31 +135,18 @@ abstract class AbstractQueueJob extends AbstractJob
138
  }
139
  $this->currentTask->setJobId($this->dto->getId());
140
  $this->currentTask->setJobName($this->getJobName());
141
-
142
- /** @var SettingsRepository $repoSettings */
143
- $repoSettings = WPStaging::getInstance()->get(SettingsRepository::class);
144
- $settings = $repoSettings->find();
145
- $this->currentTask->setDebug($settings ? $settings->isDebug() : false);
146
  }
147
 
148
  protected function setQueue()
149
  {
150
- $this->queue = new Queue;
151
  $this->queue->setName($this->findCurrentJob());
152
- $this->queue->setStorage($this->provideQueueStorage());
153
- }
154
 
155
- /**
156
- * @return StorageInterface
157
- */
158
- protected function provideQueueStorage()
159
- {
160
- /** @var CacheStorage $cacheStorage */
161
- $cacheStorage = WPStaging::getInstance()->get(CacheStorage::class);
162
- $cache = $cacheStorage->getCache();
163
- /** @noinspection NullPointerExceptionInspection */
164
- $cache->setPath(trailingslashit($cache->getPath() . $this->getJobName()));
165
- return $cacheStorage;
166
  }
167
 
168
  protected function addTasks(array $tasks = [])
@@ -189,10 +173,12 @@ abstract class AbstractQueueJob extends AbstractJob
189
 
190
  if ($response->isStatus() && $this->queue->count() === 0) {
191
  $this->dto->setFinished(true);
 
192
  return $response;
193
  }
194
 
195
  $response->setStatus(false);
 
196
  return $response;
197
  }
198
 
@@ -213,9 +199,9 @@ abstract class AbstractQueueJob extends AbstractJob
213
  return;
214
  }
215
 
216
- $args = $this->findCurrentStatusTitleArgs();
217
  $title = $this->currentTask->getStatusTitle($args);
218
  $this->dto->setCurrentStatusTitle($title);
219
- $this->cache->save($this->dto->toArray());
220
  }
221
  }
10
  use WPStaging\Component\Task\TaskResponseDto;
11
  use WPStaging\Framework\Queue\Queue;
12
  use WPStaging\Framework\Queue\Storage\CacheStorage;
 
13
  use WPStaging\Framework\Utils\Cache\Cache;
 
14
  use WPStaging\Core\WPStaging;
15
 
16
  abstract class AbstractQueueJob extends AbstractJob
17
  {
18
  /** @var Cache */
19
+ private $jobCache;
20
 
21
  /** @var QueueJobDto */
22
  protected $dto;
24
  /** @var Queue */
25
  protected $queue;
26
 
27
+ /**
28
+ * @var CacheStorage $queueCache Controls the cache file for the queue.
29
+ */
30
+ protected $queueCache;
31
+
32
  /** @var TaskInterface */
33
  protected $currentTask;
34
 
35
+ public function __construct(Cache $jobCache, Queue $queue, CacheStorage $queueCache, QueueJobDto $dto)
36
  {
37
+ $this->queue = $queue;
38
+ $this->jobCache = $jobCache;
39
+ $this->queueCache = $queueCache;
40
+ $this->dto = $dto;
 
 
41
 
42
  $this->setUp();
43
  }
50
 
51
  if ($this->dto->isFinished()) {
52
  $this->clean();
53
+
54
  return;
55
  }
56
 
57
+ $this->jobCache->save($this->dto->toArray());
58
  }
59
 
60
  abstract protected function initiateTasks();
73
  $this->queue->reset();
74
  }
75
 
 
 
 
 
 
 
 
 
76
  protected function prepare()
77
  {
78
  if (method_exists($this, 'provideRequestDto')) {
88
 
89
  protected function setUp()
90
  {
91
+ $this->setQueue();
92
+
93
+ $this->jobCache->setLifetime(HOUR_IN_SECONDS);
94
+ $this->jobCache->setFilename('job_' . $this->getJobName());
95
+ $this->jobCache->setPath(trailingslashit($this->jobCache->getPath() . $this->getJobName()));
96
+
97
  if (!empty($_POST['reset']) && ($_POST['reset'] === true || $_POST['reset'] === 'true')) {
98
  /** @noinspection DynamicInvocationViaScopeResolutionInspection */
99
  AbstractJob::clean();
100
  }
101
 
 
 
 
102
  $this->dto->setInit(true);
103
  $this->dto->setFinished(false);
104
 
105
+ $data = $this->jobCache->get([]);
106
  if ($data) {
107
  $this->dto->hydrate($data);
108
  }
135
  }
136
  $this->currentTask->setJobId($this->dto->getId());
137
  $this->currentTask->setJobName($this->getJobName());
138
+ $this->currentTask->setDebug(defined('WPSTG_DEBUG') && WPSTG_DEBUG);
 
 
 
 
139
  }
140
 
141
  protected function setQueue()
142
  {
 
143
  $this->queue->setName($this->findCurrentJob());
 
 
144
 
145
+ $this->queueCache->getCache()->setPath(
146
+ trailingslashit($this->queueCache->getCache()->getPath() . $this->getJobName())
147
+ );
148
+
149
+ $this->queue->setStorage($this->queueCache);
 
 
 
 
 
 
150
  }
151
 
152
  protected function addTasks(array $tasks = [])
173
 
174
  if ($response->isStatus() && $this->queue->count() === 0) {
175
  $this->dto->setFinished(true);
176
+
177
  return $response;
178
  }
179
 
180
  $response->setStatus(false);
181
+
182
  return $response;
183
  }
184
 
199
  return;
200
  }
201
 
202
+ $args = $this->findCurrentStatusTitleArgs();
203
  $title = $this->currentTask->getStatusTitle($args);
204
  $this->dto->setCurrentStatusTitle($title);
205
+ $this->jobCache->save($this->dto->toArray());
206
  }
207
  }
Component/Job/QueueJobDto.php CHANGED
@@ -4,7 +4,7 @@ namespace WPStaging\Component\Job;
4
 
5
  use WPStaging\Component\Dto\AbstractDto;
6
 
7
- class QueueJobDto extends AbstractDto
8
  {
9
  /** @var string|int|null */
10
  protected $id;
@@ -100,4 +100,4 @@ class QueueJobDto extends AbstractDto
100
  {
101
  $this->currentStatusTitle = $currentStatusTitle;
102
  }
103
- }
4
 
5
  use WPStaging\Component\Dto\AbstractDto;
6
 
7
+ abstract class QueueJobDto extends AbstractDto
8
  {
9
  /** @var string|int|null */
10
  protected $id;
100
  {
101
  $this->currentStatusTitle = $currentStatusTitle;
102
  }
103
+ }
Component/Task/Filesystem/DirectoryScannerRequestDto.php CHANGED
@@ -10,20 +10,20 @@ use WPStaging\Component\Dto\AbstractRequestDto;
10
  class DirectoryScannerRequestDto extends AbstractRequestDto
11
  {
12
  /** @var array */
13
- private $included;
14
 
15
  /** @var array */
16
- private $excluded;
17
 
18
  /**
19
  * @return array
20
  */
21
  public function getIncluded()
22
  {
23
- return $this->included;
24
  }
25
 
26
- public function setIncluded(array $included = null)
27
  {
28
  $this->included = $included;
29
  }
@@ -33,7 +33,7 @@ class DirectoryScannerRequestDto extends AbstractRequestDto
33
  */
34
  public function getExcluded()
35
  {
36
- return $this->excluded;
37
  }
38
 
39
  public function setExcluded(array $excluded = null)
@@ -41,6 +41,6 @@ class DirectoryScannerRequestDto extends AbstractRequestDto
41
  // $this->excluded = array_map(static function($dir) {
42
  // return str_replace(ABSPATH, null, $dir);
43
  // }, $excluded);
44
- $this->excluded = $excluded;
45
  }
46
  }
10
  class DirectoryScannerRequestDto extends AbstractRequestDto
11
  {
12
  /** @var array */
13
+ private $included = [];
14
 
15
  /** @var array */
16
+ private $excluded = [];
17
 
18
  /**
19
  * @return array
20
  */
21
  public function getIncluded()
22
  {
23
+ return (array)$this->included;
24
  }
25
 
26
+ public function setIncluded(array $included = [])
27
  {
28
  $this->included = $included;
29
  }
33
  */
34
  public function getExcluded()
35
  {
36
+ return (array)$this->excluded;
37
  }
38
 
39
  public function setExcluded(array $excluded = null)
41
  // $this->excluded = array_map(static function($dir) {
42
  // return str_replace(ABSPATH, null, $dir);
43
  // }, $excluded);
44
+ $this->excluded = (array)$excluded;
45
  }
46
  }
Component/Task/Filesystem/DirectoryScannerTask.php CHANGED
@@ -14,8 +14,15 @@ use WPStaging\Framework\Queue\FinishedQueueException;
14
  use WPStaging\Framework\Traits\ResourceTrait;
15
  use WPStaging\Framework\Utils\Cache\BufferedCache;
16
  use WPStaging\Framework\Utils\Cache\Cache;
17
- use WPStaging\Framework\Filesystem\DirectoryScanner;
18
-
 
 
 
 
 
 
 
19
  class DirectoryScannerTask extends AbstractTask
20
  {
21
  use ResourceTrait;
@@ -28,8 +35,8 @@ class DirectoryScannerTask extends AbstractTask
28
  /** @var DirectoryScannerRequestDto */
29
  public $requestDto;
30
 
31
- /** @var DirectoryScanner */
32
- private $scanner;
33
 
34
  /** @var BufferedCache */
35
  private $directoryCache;
@@ -37,17 +44,17 @@ class DirectoryScannerTask extends AbstractTask
37
  /** @var array */
38
  private $directories;
39
 
40
- public function __construct(DirectoryScanner $scanner, LoggerInterface $logger, Cache $cache)
41
  {
42
  parent::__construct($logger, $cache);
43
  $this->directories = [];
44
 
45
- $scanner->setQueueByName();
46
- $this->scanner = $scanner;
47
 
48
- $this->directoryCache = clone $scanner->getCache();
49
  $this->directoryCache->setLifetime(DAY_IN_SECONDS);
50
- $this->directoryCache->setFilename(DirectoryScanner::DATA_CACHE_FILE);
51
  }
52
 
53
  public function __destruct()
@@ -109,7 +116,7 @@ class DirectoryScannerTask extends AbstractTask
109
  {
110
  $directories = null;
111
  try {
112
- $directories = $this->scanner->scanCurrentPath($this->requestDto->getExcluded());
113
  } catch (FinishedQueueException $e) {
114
  $this->logger->info('Finished scanning directories');
115
  $this->requestDto->getSteps()->finish();
@@ -121,7 +128,7 @@ class DirectoryScannerTask extends AbstractTask
121
  }
122
 
123
  // No directories found here, skip it
124
- if ($directories === null || $directories->count() < 1) {
125
  return;
126
  }
127
 
@@ -129,10 +136,11 @@ class DirectoryScannerTask extends AbstractTask
129
  if ($this->isThreshold()) {
130
  return;
131
  }
132
- $relativePath = str_replace(ABSPATH, null, $directory->getPathname());
133
- $this->scanner->addToNewQueue($relativePath);
134
  $this->directories[] = $relativePath;
135
  }
 
136
  }
137
 
138
  protected function findRequestDto()
@@ -152,8 +160,8 @@ class DirectoryScannerTask extends AbstractTask
152
  }, $this->requestDto->getIncluded());
153
 
154
  /** @noinspection NullPointerExceptionInspection */
155
- if ($this->scanner->getQueue()->count() < 1) {
156
- $this->scanner->setNewQueueItems($this->directories);
157
  }
158
 
159
  $totalSteps = count($this->requestDto->getIncluded());
@@ -166,10 +174,9 @@ class DirectoryScannerTask extends AbstractTask
166
  // There is no need to backup WP Staging as you shouldn't restore WPSTG backup without WPSTG having installed
167
  // Don't backup / restore cache as it can be problematic
168
  $excludedDirs = $this->requestDto->getExcluded();
169
- if (!$excludedDirs) {
170
- $excludedDirs = [];
171
- }
172
- $adapter = $this->scanner->getService()->getDirectory();
173
  $excludedDirs[] = WPSTG_PLUGIN_DIR;
174
  $excludedDirs[] = $adapter->getPluginUploadsDirectory();
175
  $excludedDirs[] = WP_CONTENT_DIR . '/cache';
@@ -180,9 +187,9 @@ class DirectoryScannerTask extends AbstractTask
180
  {
181
  $caches = parent::getCaches();
182
  $caches[] = $this->directoryCache;
183
- $caches[] = $this->scanner->getCache();
184
  /** @noinspection NullPointerExceptionInspection */
185
- $caches[] = $this->scanner->getQueue()->getStorage()->getCache();
186
  return $caches;
187
  }
188
 
@@ -201,7 +208,7 @@ class DirectoryScannerTask extends AbstractTask
201
  $steps->setTotal($steps->getTotal() + count($this->directories));
202
 
203
  /** @noinspection NullPointerExceptionInspection */
204
- if ($this->scanner->getQueue()->count() > 0) {
205
  return;
206
  }
207
 
14
  use WPStaging\Framework\Traits\ResourceTrait;
15
  use WPStaging\Framework\Utils\Cache\BufferedCache;
16
  use WPStaging\Framework\Utils\Cache\Cache;
17
+ use WPStaging\Framework\Filesystem\DirectoryScannerControl;
18
+
19
+ /**
20
+ * Class DirectoryScannerTask
21
+ *
22
+ * @see DirectoryScannerControl
23
+ *
24
+ * @package WPStaging\Component\Task\Filesystem
25
+ */
26
  class DirectoryScannerTask extends AbstractTask
27
  {
28
  use ResourceTrait;
35
  /** @var DirectoryScannerRequestDto */
36
  public $requestDto;
37
 
38
+ /** @var DirectoryScannerControl */
39
+ private $scannerControl;
40
 
41
  /** @var BufferedCache */
42
  private $directoryCache;
44
  /** @var array */
45
  private $directories;
46
 
47
+ public function __construct(DirectoryScannerControl $scannerControl, LoggerInterface $logger, Cache $cache)
48
  {
49
  parent::__construct($logger, $cache);
50
  $this->directories = [];
51
 
52
+ $scannerControl->setQueueByName();
53
+ $this->scannerControl = $scannerControl;
54
 
55
+ $this->directoryCache = clone $scannerControl->getCache();
56
  $this->directoryCache->setLifetime(DAY_IN_SECONDS);
57
+ $this->directoryCache->setFilename(DirectoryScannerControl::DATA_CACHE_FILE);
58
  }
59
 
60
  public function __destruct()
116
  {
117
  $directories = null;
118
  try {
119
+ $directories = $this->scannerControl->scanCurrentPath($this->requestDto->getExcluded());
120
  } catch (FinishedQueueException $e) {
121
  $this->logger->info('Finished scanning directories');
122
  $this->requestDto->getSteps()->finish();
128
  }
129
 
130
  // No directories found here, skip it
131
+ if (empty($directories)) {
132
  return;
133
  }
134
 
136
  if ($this->isThreshold()) {
137
  return;
138
  }
139
+ $relativePath = str_replace(ABSPATH, null, $directory);
140
+ $this->scannerControl->addToNewQueue($relativePath);
141
  $this->directories[] = $relativePath;
142
  }
143
+ $this->directories = array_unique($this->directories);
144
  }
145
 
146
  protected function findRequestDto()
160
  }, $this->requestDto->getIncluded());
161
 
162
  /** @noinspection NullPointerExceptionInspection */
163
+ if ($this->scannerControl->getQueue()->count() < 1) {
164
+ $this->scannerControl->setNewQueueItems($this->directories);
165
  }
166
 
167
  $totalSteps = count($this->requestDto->getIncluded());
174
  // There is no need to backup WP Staging as you shouldn't restore WPSTG backup without WPSTG having installed
175
  // Don't backup / restore cache as it can be problematic
176
  $excludedDirs = $this->requestDto->getExcluded();
177
+
178
+ $adapter = $this->scannerControl->getDirectory();
179
+
 
180
  $excludedDirs[] = WPSTG_PLUGIN_DIR;
181
  $excludedDirs[] = $adapter->getPluginUploadsDirectory();
182
  $excludedDirs[] = WP_CONTENT_DIR . '/cache';
187
  {
188
  $caches = parent::getCaches();
189
  $caches[] = $this->directoryCache;
190
+ $caches[] = $this->scannerControl->getCache();
191
  /** @noinspection NullPointerExceptionInspection */
192
+ $caches[] = $this->scannerControl->getQueue()->getStorage()->getCache();
193
  return $caches;
194
  }
195
 
208
  $steps->setTotal($steps->getTotal() + count($this->directories));
209
 
210
  /** @noinspection NullPointerExceptionInspection */
211
+ if ($this->scannerControl->getQueue()->count() > 0) {
212
  return;
213
  }
214
 
Component/Task/Filesystem/FileScannerRequestDto.php CHANGED
@@ -9,22 +9,24 @@ use WPStaging\Component\Dto\AbstractRequestDto;
9
 
10
  class FileScannerRequestDto extends AbstractRequestDto
11
  {
12
-
13
  /** @var array */
14
- private $included;
15
 
16
  /** @var array */
17
- private $excluded;
 
 
 
18
 
19
  /**
20
  * @return array
21
  */
22
  public function getIncluded()
23
  {
24
- return $this->included?: [];
25
  }
26
 
27
- public function setIncluded(array $included = null)
28
  {
29
  $this->included = $included;
30
  }
@@ -34,11 +36,24 @@ class FileScannerRequestDto extends AbstractRequestDto
34
  */
35
  public function getExcluded()
36
  {
37
- return $this->excluded;
38
  }
39
 
40
- public function setExcluded(array $excluded = null)
41
  {
42
  $this->excluded = $excluded;
43
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
9
 
10
  class FileScannerRequestDto extends AbstractRequestDto
11
  {
 
12
  /** @var array */
13
+ private $included = [];
14
 
15
  /** @var array */
16
+ private $excluded = [];
17
+
18
+ /** @var bool */
19
+ private $includeOtherFilesInWpContent;
20
 
21
  /**
22
  * @return array
23
  */
24
  public function getIncluded()
25
  {
26
+ return (array)$this->included;
27
  }
28
 
29
+ public function setIncluded(array $included = [])
30
  {
31
  $this->included = $included;
32
  }
36
  */
37
  public function getExcluded()
38
  {
39
+ return (array)$this->excluded;
40
  }
41
 
42
+ public function setExcluded(array $excluded = [])
43
  {
44
  $this->excluded = $excluded;
45
  }
46
+
47
+ /**
48
+ * @return bool
49
+ */
50
+ public function getIncludeOtherFilesInWpContent()
51
+ {
52
+ return (bool)$this->includeOtherFilesInWpContent;
53
+ }
54
+
55
+ public function setIncludeOtherFilesInWpContent($includeOtherFilesInWpContent)
56
+ {
57
+ $this->includeOtherFilesInWpContent = $includeOtherFilesInWpContent;
58
+ }
59
  }
Component/Task/Filesystem/FileScannerTask.php CHANGED
@@ -16,8 +16,8 @@ use WPStaging\Framework\Traits\ResourceTrait;
16
  use WPStaging\Framework\Utils\Cache\AbstractCache;
17
  use WPStaging\Framework\Utils\Cache\BufferedCache;
18
  use WPStaging\Framework\Utils\Cache\Cache;
19
- use WPStaging\Framework\Filesystem\DirectoryScanner;
20
- use WPStaging\Framework\Filesystem\FileScanner;
21
  use WPStaging\Framework\Filesystem\Filesystem;
22
 
23
  class FileScannerTask extends AbstractTask
@@ -32,8 +32,8 @@ class FileScannerTask extends AbstractTask
32
  /** @var FileScannerRequestDto */
33
  public $requestDto;
34
 
35
- /** @var FileScanner */
36
- private $scanner;
37
 
38
  /** @var BufferedCache */
39
  private $fileCache;
@@ -41,17 +41,17 @@ class FileScannerTask extends AbstractTask
41
  /** @var array */
42
  private $files;
43
 
44
- public function __construct(FileScanner $scanner, LoggerInterface $logger, Cache $cache)
45
  {
46
  parent::__construct($logger, $cache);
47
  $this->files = [];
48
 
49
- $scanner->setQueueByName();
50
- $this->scanner = $scanner;
51
 
52
- $this->fileCache = clone $scanner->getCache();
53
  $this->fileCache->setLifetime(DAY_IN_SECONDS);
54
- $this->fileCache->setFilename(FileScanner::DATA_CACHE_FILE);
55
  }
56
 
57
  public function __destruct()
@@ -118,7 +118,7 @@ class FileScannerTask extends AbstractTask
118
  {
119
  $files = null;
120
  try {
121
- $files = $this->scanner->scanCurrentPath($this->requestDto->getExcluded());
122
  } catch (FinishedQueueException $e) {
123
  $this->logger->info('Finished scanning files');
124
  $this->requestDto->getSteps()->finish();
@@ -127,8 +127,8 @@ class FileScannerTask extends AbstractTask
127
  $this->logger->warning($e->getMessage());
128
  }
129
 
130
- // No directories found here, skip it
131
- if ($files === null || $files->count() < 1) {
132
  return;
133
  }
134
 
@@ -136,9 +136,10 @@ class FileScannerTask extends AbstractTask
136
  if ($this->isThreshold()) {
137
  return;
138
  }
139
- $relativePath = str_replace(ABSPATH, null, $file->getPathname());
140
  $this->files[] = $relativePath;
141
  }
 
142
  }
143
 
144
  protected function findRequestDto()
@@ -154,7 +155,7 @@ class FileScannerTask extends AbstractTask
154
  }, $this->requestDto->getIncluded()));
155
 
156
  /** @noinspection NullPointerExceptionInspection */
157
- if ($this->scanner->getQueue()->count() < 1) {
158
  $this->initiateQueue();
159
  }
160
 
@@ -166,17 +167,17 @@ class FileScannerTask extends AbstractTask
166
  $caches = parent::getCaches();
167
  $caches[] = $this->fileCache;
168
  /** @noinspection NullPointerExceptionInspection */
169
- $caches[] = $this->scanner->getQueue()->getStorage()->getCache();
170
  return $caches;
171
  }
172
 
173
  private function initiateQueue()
174
  {
175
  if ($this->requestDto->getIncluded()) {
176
- $this->scanner->setNewQueueItems($this->requestDto->getIncluded());
177
  }
178
 
179
- $directoryData = $this->cache->getPath() . DirectoryScanner::DATA_CACHE_FILE . '.' . AbstractCache::EXTENSION;
180
  if (!file_exists($directoryData)) {
181
  throw new RuntimeException(sprintf(
182
  'File %s does not exists. Need to Scan Directories first',
@@ -184,7 +185,7 @@ class FileScannerTask extends AbstractTask
184
  ));
185
  }
186
 
187
- $queueFile = $this->cache->getPath() . BufferedCacheStorage::FILE_PREFIX . FileScanner::QUEUE_CACHE_FILE;
188
  $queueFile .= '.' . AbstractCache::EXTENSION;
189
 
190
  if (!(new Filesystem)->copy($directoryData, $queueFile)) {
@@ -207,7 +208,7 @@ class FileScannerTask extends AbstractTask
207
  $steps->setTotal($steps->getTotal() + count($this->files));
208
 
209
  /** @noinspection NullPointerExceptionInspection */
210
- if ($this->scanner->getQueue()->count() > 0) {
211
  return;
212
  }
213
 
16
  use WPStaging\Framework\Utils\Cache\AbstractCache;
17
  use WPStaging\Framework\Utils\Cache\BufferedCache;
18
  use WPStaging\Framework\Utils\Cache\Cache;
19
+ use WPStaging\Framework\Filesystem\DirectoryScannerControl;
20
+ use WPStaging\Framework\Filesystem\FileScannerControl;
21
  use WPStaging\Framework\Filesystem\Filesystem;
22
 
23
  class FileScannerTask extends AbstractTask
32
  /** @var FileScannerRequestDto */
33
  public $requestDto;
34
 
35
+ /** @var FileScannerControl */
36
+ private $scannerControl;
37
 
38
  /** @var BufferedCache */
39
  private $fileCache;
41
  /** @var array */
42
  private $files;
43
 
44
+ public function __construct(FileScannerControl $scannerControl, LoggerInterface $logger, Cache $cache)
45
  {
46
  parent::__construct($logger, $cache);
47
  $this->files = [];
48
 
49
+ $scannerControl->setQueueByName();
50
+ $this->scannerControl = $scannerControl;
51
 
52
+ $this->fileCache = clone $scannerControl->getCache();
53
  $this->fileCache->setLifetime(DAY_IN_SECONDS);
54
+ $this->fileCache->setFilename(FileScannerControl::DATA_CACHE_FILE);
55
  }
56
 
57
  public function __destruct()
118
  {
119
  $files = null;
120
  try {
121
+ $files = $this->scannerControl->scanCurrentPath($this->requestDto->getIncludeOtherFilesInWpContent());
122
  } catch (FinishedQueueException $e) {
123
  $this->logger->info('Finished scanning files');
124
  $this->requestDto->getSteps()->finish();
127
  $this->logger->warning($e->getMessage());
128
  }
129
 
130
+ // No files found here, skip it
131
+ if (empty($files)) {
132
  return;
133
  }
134
 
136
  if ($this->isThreshold()) {
137
  return;
138
  }
139
+ $relativePath = str_replace(ABSPATH, null, $file);
140
  $this->files[] = $relativePath;
141
  }
142
+ $this->files = array_unique($this->files);
143
  }
144
 
145
  protected function findRequestDto()
155
  }, $this->requestDto->getIncluded()));
156
 
157
  /** @noinspection NullPointerExceptionInspection */
158
+ if ($this->scannerControl->getQueue()->count() < 1) {
159
  $this->initiateQueue();
160
  }
161
 
167
  $caches = parent::getCaches();
168
  $caches[] = $this->fileCache;
169
  /** @noinspection NullPointerExceptionInspection */
170
+ $caches[] = $this->scannerControl->getQueue()->getStorage()->getCache();
171
  return $caches;
172
  }
173
 
174
  private function initiateQueue()
175
  {
176
  if ($this->requestDto->getIncluded()) {
177
+ $this->scannerControl->setNewQueueItems($this->requestDto->getIncluded());
178
  }
179
 
180
+ $directoryData = $this->cache->getPath() . DirectoryScannerControl::DATA_CACHE_FILE . '.' . AbstractCache::EXTENSION;
181
  if (!file_exists($directoryData)) {
182
  throw new RuntimeException(sprintf(
183
  'File %s does not exists. Need to Scan Directories first',
185
  ));
186
  }
187
 
188
+ $queueFile = $this->cache->getPath() . BufferedCacheStorage::FILE_PREFIX . FileScannerControl::QUEUE_CACHE_FILE;
189
  $queueFile .= '.' . AbstractCache::EXTENSION;
190
 
191
  if (!(new Filesystem)->copy($directoryData, $queueFile)) {
208
  $steps->setTotal($steps->getTotal() + count($this->files));
209
 
210
  /** @noinspection NullPointerExceptionInspection */
211
+ if ($this->scannerControl->getQueue()->count() > 0) {
212
  return;
213
  }
214
 
Core/Cron/Cron.php CHANGED
@@ -16,15 +16,15 @@ class Cron
16
 
17
  public function __construct()
18
  {
19
- add_filter('cron_schedules', [$this, 'add_new_intervals']);
20
  }
21
 
22
  /**
23
  * Add new intervals for wp cron jobs
24
- * @param type $schedules
25
- * @return type
26
  */
27
- public function add_new_intervals($schedules)
28
  {
29
  // add weekly and monthly intervals
30
  $schedules['weekly'] = [
@@ -40,7 +40,10 @@ class Cron
40
  return $schedules;
41
  }
42
 
43
- public function schedule_event()
 
 
 
44
  {
45
  if (!wp_next_scheduled('wpstg_weekly_event')) {
46
  wp_schedule_event(time(), 'weekly', 'wpstg_weekly_event');
@@ -48,6 +51,8 @@ class Cron
48
  if (!wp_next_scheduled('wpstg_daily_event')) {
49
  wp_schedule_event(time(), 'daily', 'wpstg_daily_event');
50
  }
 
 
51
  }
52
 
53
  }
16
 
17
  public function __construct()
18
  {
19
+ add_filter('cron_schedules', [$this, 'addIntervals']);
20
  }
21
 
22
  /**
23
  * Add new intervals for wp cron jobs
24
+ * @param array $schedules
25
+ * @return array
26
  */
27
+ public function addIntervals($schedules)
28
  {
29
  // add weekly and monthly intervals
30
  $schedules['weekly'] = [
40
  return $schedules;
41
  }
42
 
43
+ /**
44
+ * @return bool
45
+ */
46
+ public function scheduleEvent()
47
  {
48
  if (!wp_next_scheduled('wpstg_weekly_event')) {
49
  wp_schedule_event(time(), 'weekly', 'wpstg_weekly_event');
51
  if (!wp_next_scheduled('wpstg_daily_event')) {
52
  wp_schedule_event(time(), 'daily', 'wpstg_daily_event');
53
  }
54
+
55
+ return true;
56
  }
57
 
58
  }
Core/Utils/Cache.php CHANGED
@@ -8,6 +8,7 @@ if( !defined( "WPINC" ) ) {
8
  }
9
 
10
  use Exception;
 
11
 
12
  /**
13
  * Class Cache
@@ -69,16 +70,15 @@ class Cache {
69
  $this->cacheExtension = $cacheExtension;
70
  }
71
 
72
- // If cache directory doesn't exists, create it
73
- if( !is_dir( $this->cacheDir ) && !@mkdir( $this->cacheDir, 0775, true ) ) {
74
- /**
75
- * There's no need to throw this exception, which causes a fatal,
76
- * as a warning is already displayed on:
77
- *
78
- * @see \WPStaging\Backend\Notices\Notices::messages
79
- */
80
- //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.' );
81
- }
82
  }
83
 
84
  /**
8
  }
9
 
10
  use Exception;
11
+ use WPStaging\Framework\Filesystem\Filesystem;
12
 
13
  /**
14
  * Class Cache
70
  $this->cacheExtension = $cacheExtension;
71
  }
72
 
73
+ /**
74
+ * If cache directory doesn't exists, create it.
75
+ *
76
+ * There's no need to handle failure to create here,
77
+ * as a warning is already displayed on:
78
+ *
79
+ * @see \WPStaging\Backend\Notices\Notices::messages
80
+ */
81
+ (new Filesystem)->mkdir($this->cacheDir);
 
82
  }
83
 
84
  /**
Core/Utils/Filesystem.php DELETED
@@ -1,56 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Core\Utils;
4
-
5
- // No Direct Access
6
- if( !defined( "WPINC" ) ) {
7
- die;
8
- }
9
-
10
- class Filesystem {
11
-
12
- /**
13
- * Create a file with content
14
- *
15
- * @param string $path Path to the file
16
- * @param string $content Content of the file
17
- * @return boolean
18
- */
19
- public function create( $path, $content ) {
20
- if( !@file_exists( $path ) ) {
21
- if( !@is_writable( dirname( $path ) ) ) {
22
- return false;
23
- }
24
-
25
- if( !@touch( $path ) ) {
26
- return false;
27
- }
28
- } elseif( !@is_writable( $path ) ) {
29
- return false;
30
- }
31
-
32
- $written = false;
33
- if( ( $handle = @fopen( $path, 'w' ) ) !== false ) {
34
- if( @fwrite( $handle, $content ) !== false ) {
35
- $written = true;
36
- }
37
-
38
- @fclose( $handle );
39
- }
40
-
41
- return $written;
42
- }
43
-
44
- /**
45
- * Create a file with marker and content
46
- *
47
- * @param string $path Path to the file
48
- * @param string $marker Name of the marker
49
- * @param string $content Content of the file
50
- * @return boolean
51
- */
52
- public function createWithMarkers( $path, $marker, $content ) {
53
- return @insert_with_markers( $path, $marker, $content );
54
- }
55
-
56
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Core/Utils/Htaccess.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace WPStaging\Core\Utils;
4
 
5
- use WPStaging\Core\Utils\Filesystem;
6
 
7
  // No Direct Access
8
  if (!defined("WPINC"))
2
 
3
  namespace WPStaging\Core\Utils;
4
 
5
+ use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
  if (!defined("WPINC"))
Core/Utils/IISWebConfig.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace WPStaging\Core\Utils;
4
 
5
- use WPStaging\Core\Utils\Filesystem;
6
 
7
  // No Direct Access
8
  if( !defined( "WPINC" ) ) {
2
 
3
  namespace WPStaging\Core\Utils;
4
 
5
+ use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
  if( !defined( "WPINC" ) ) {
Core/Utils/Logger.php CHANGED
@@ -9,6 +9,7 @@
9
 
10
  namespace WPStaging\Core\Utils;
11
 
 
12
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
13
  use WPStaging\Vendor\Psr\Log\LogLevel;
14
 
@@ -83,17 +84,11 @@ class Logger implements LoggerInterface
83
  $this->logExtension = $logExtension;
84
  }
85
 
86
- // If cache directory doesn't exists, create it
87
- if (!is_dir($this->logDir) && !@mkdir($this->logDir, 0755, true))
88
- {
89
- /**
90
- * There's no need to throw this exception, which causes a fatal,
91
- * as a warning is already displayed on:
92
- *
93
- * @see \WPStaging\Backend\Notices\Notices::messages
94
- */
95
- // throw new \Exception("Failed to create log directory!");
96
- }
97
  }
98
 
99
  public function __destruct()
@@ -135,7 +130,7 @@ class Logger implements LoggerInterface
135
  }
136
 
137
  /**
138
- * @param Strings $fileName
139
  */
140
  public function setFileName($fileName)
141
  {
9
 
10
  namespace WPStaging\Core\Utils;
11
 
12
+ use WPStaging\Framework\Filesystem\Filesystem;
13
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
14
  use WPStaging\Vendor\Psr\Log\LogLevel;
15
 
84
  $this->logExtension = $logExtension;
85
  }
86
 
87
+ /**
88
+ * If log directory doesn't exists, create it.
89
+ * @see \WPStaging\Backend\Notices\Notices::messages Notice that shows if log directory couldn't be created.
90
+ */
91
+ (new Filesystem)->mkdir($this->logDir);
 
 
 
 
 
 
92
  }
93
 
94
  public function __destruct()
130
  }
131
 
132
  /**
133
+ * @param string $fileName
134
  */
135
  public function setFileName($fileName)
136
  {
Core/Utils/RobotsTxt.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace WPStaging\Core\Utils;
4
 
5
- use WPStaging\Core\Utils\Filesystem;
6
 
7
  // No Direct Access
8
  if (!defined("WPINC"))
2
 
3
  namespace WPStaging\Core\Utils;
4
 
5
+ use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
  if (!defined("WPINC"))
Core/Utils/functions.php CHANGED
@@ -9,35 +9,6 @@
9
  *
10
  */
11
 
12
- /**
13
- * Get directory permissions
14
- *
15
- * @return int
16
- */
17
- function wpstg_get_permissions_for_directory()
18
- {
19
- $octal = 0755;
20
- if (defined('FS_CHMOD_DIR')) {
21
- $octal = FS_CHMOD_DIR;
22
- }
23
-
24
- return apply_filters('wpstg_folder_permission', $octal);
25
- }
26
-
27
- /**
28
- * Get file permissions
29
- *
30
- * @return int
31
- */
32
- function wpstg_get_permissions_for_file()
33
- {
34
- if (defined('FS_CHMOD_FILE')) {
35
- return FS_CHMOD_FILE;
36
- }
37
-
38
- return 0644;
39
- }
40
-
41
  /**
42
  * PHP setup environment
43
  *
@@ -442,38 +413,6 @@ function wpstg_chmod($file, $mode = false)
442
  return true;
443
  }
444
 
445
- /**
446
- * Create file if it does not exist
447
- *
448
- * @param string $path
449
- * @param (int|false) $chmod The permissions as octal number (or false to skip chmod)
450
- * @param (string|int) $chown A user name or number (or false to skip chown).
451
- * @return boolean true on success, false on failure.
452
- */
453
- function wpstg_mkdir($path, $chmod = false, $chown = false)
454
- {
455
- // Safe mode fails with a trailing slash under certain PHP versions.
456
- $path = untrailingslashit($path);
457
- if (empty($path)) {
458
- return false;
459
- }
460
-
461
- if (!$chmod) {
462
- $chmod = FS_CHMOD_DIR;
463
- }
464
-
465
- if (!@mkdir($path)) {
466
- return false;
467
- }
468
- wpstg_chmod($path, $chmod);
469
-
470
- if ($chown) {
471
- wpstg_chown($path, $chown);
472
- }
473
-
474
- return true;
475
- }
476
-
477
  /**
478
  * Changes the owner of a file or directory.
479
  *
9
  *
10
  */
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  /**
13
  * PHP setup environment
14
  *
413
  return true;
414
  }
415
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  /**
417
  * Changes the owner of a file or directory.
418
  *
Core/Utils/requirements-check.php DELETED
@@ -1,211 +0,0 @@
1
- <?php
2
- /**
3
- * Simple requirements checking class.
4
- *
5
- * @package WP_Requirements_Check
6
- */
7
-
8
- /**
9
- * Simple requirements checking class.
10
- */
11
- class Wpstg_Requirements_Check {
12
- /**
13
- * @since 1.1.0
14
- *
15
- * @var array $args {
16
- * Requirement arguments.
17
- *
18
- * @type string $title Name of the plugin.
19
- * @type string $php Minimum required PHP version.
20
- * @type string $wp Minimum required WordPress version.
21
- * @type string $file Path to the main plugin file.
22
- * @type array $i18n {
23
- * @type string $php PHP version mismatch error message.
24
- * @type string $wp WP version mismatch error message.
25
- * }
26
- * }
27
- */
28
- private $args;
29
-
30
- /**
31
- * Constructor.
32
- *
33
- * @since 1.0.0
34
- * @access public
35
- *
36
- * @param array $args {
37
- * An array of arguments to overwrite the default requirements.
38
- *
39
- * @type string $title Name of the plugin.
40
- * @type string $php Minimum required PHP version.
41
- * @type string $wp Minimum required WordPress version.
42
- * @type string $file Path to the main plugin file.
43
- * @type array $i18n {
44
- * @type string $php PHP version mismatch error message.
45
- * @type string $wp WP version mismatch error message.
46
- * }
47
- * }
48
- */
49
- public function __construct( $args ) {
50
- $args = (array) $args;
51
-
52
- $this->args = wp_parse_args(
53
- $args,
54
- [
55
- 'title' => '',
56
- 'php' => '5.2.4',
57
- 'wp' => '3.8',
58
- 'file' => null,
59
- 'i18n' => []
60
- ]
61
- );
62
-
63
- $this->args['i18n'] = wp_parse_args(
64
- $this->args['i18n'],
65
- [
66
- 'php' => 'The &#8220;%1$s&#8221; plugin cannot run on PHP versions older than %2$s. Please contact your host and ask them to upgrade.',
67
- 'wp' => 'The &#8220;%1$s&#8221; plugin cannot run on WordPress versions older than %2$s. Please update your WordPress.',
68
- ]
69
- );
70
- }
71
-
72
- /**
73
- * Check if the install passes the requirements.
74
- *
75
- * @since 1.0.0
76
- * @access public
77
- *
78
- * @return bool True if the install passes the requirements, false otherwise.
79
- */
80
- public function passes() {
81
- $passes = $this->php_passes() && $this->wp_passes();
82
-
83
- if ( ! $passes ) {
84
- add_action( 'admin_notices', [ $this, 'deactivate' ] );
85
- }
86
-
87
- return $passes;
88
- }
89
-
90
- /**
91
- * Deactivates the plugin again.
92
- *
93
- * @since 1.0.0
94
- * @access public
95
- */
96
- public function deactivate() {
97
- if ( $this->args['file'] !== null ) {
98
- deactivate_plugins( plugin_basename( $this->args['file'] ) );
99
- }
100
- }
101
-
102
- /**
103
- * Checks if the PHP version passes the requirement.
104
- *
105
- * @since 1.0.0
106
- * @access protected
107
- *
108
- * @return bool True if the PHP version is high enough, false otherwise.
109
- */
110
- protected function php_passes() {
111
- if ( self::_php_at_least( $this->args['php'] ) ) {
112
- return true;
113
- }
114
-
115
- add_action( 'admin_notices', [ $this, 'php_version_notice' ] );
116
-
117
- return false;
118
- }
119
-
120
- /**
121
- * Compares the current PHP version with the minimum required version.
122
- *
123
- * @since 1.0.0
124
- * @access protected
125
- *
126
- * @param string $min_version The minimum required version.
127
- * @return bool True if the PHP version is high enough, false otherwise.
128
- */
129
- protected static function _php_at_least( $min_version ) {
130
- return version_compare( PHP_VERSION, $min_version, '>=' );
131
- }
132
-
133
- /**
134
- * Displays the PHP version notice.
135
- *
136
- * @since 1.0.0
137
- * @access public
138
- */
139
- public function php_version_notice() {
140
- /**
141
- * Filters the notice for outdated PHP versions.
142
- *
143
- * @since 1.1.0
144
- *
145
- * @param string $message The error message.
146
- * @param string $title The plugin name.
147
- * @param string $php The WordPress version.
148
- */
149
- $message = apply_filters( 'wp_requirements_check_php_notice', $this->args['i18n']['php'], $this->args['title'], $this->args['php'] );
150
- ?>
151
- <div class="error">
152
- <p><?php printf( $message, esc_html( $this->args['title'] ), $this->args['php'] ); ?></p>
153
- </div>
154
- <?php
155
- }
156
-
157
- /**
158
- * Check if the WordPress version passes the requirement.
159
- *
160
- * @since 1.0.0
161
- * @access protected
162
- *
163
- * @return bool True if the WordPress version is high enough, false otherwise.
164
- */
165
- protected function wp_passes() {
166
- if ( self::_wp_at_least( $this->args['wp'] ) ) {
167
- return true;
168
- }
169
-
170
- add_action( 'admin_notices', [ $this, 'wp_version_notice' ] );
171
-
172
- return false;
173
- }
174
-
175
- /**
176
- * Compare the current WordPress version with the minimum required version.
177
- *
178
- * @since 1.0.0
179
- * @access protected
180
- *
181
- * @param string $min_version Minimum required WordPress version.
182
- * @return bool True if the WordPress version is high enough, false otherwise.
183
- */
184
- protected static function _wp_at_least( $min_version ) {
185
- return version_compare( get_bloginfo( 'version' ), $min_version, '>=' );
186
- }
187
-
188
- /**
189
- * Show the WordPress version notice.
190
- *
191
- * @since 1.0.0
192
- * @access public
193
- */
194
- public function wp_version_notice() {
195
- /**
196
- * Filters the notice for outdated WordPress versions.
197
- *
198
- * @since 1.1.0
199
- *
200
- * @param string $message The error message.
201
- * @param string $title The plugin name.
202
- * @param string $php The WordPress version.
203
- */
204
- $message = apply_filters( 'wp_requirements_check_wordpress_notice', $this->args['i18n']['wp'], $this->args['title'], $this->args['wp'] );
205
- ?>
206
- <div class="error">
207
- <p><?php printf( $message, esc_html( $this->args['title'] ), $this->args['wp'] ); ?></p>
208
- </div>
209
- <?php
210
- }
211
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Core/WPStaging.php CHANGED
@@ -7,6 +7,8 @@ if( !defined( "WPINC" ) ) {
7
  die;
8
  }
9
 
 
 
10
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
11
  use WPStaging\Framework\DI\Container;
12
  use WPStaging\Backend\Administrator;
@@ -70,16 +72,28 @@ final class WPStaging {
70
  */
71
  private $accessToken;
72
 
 
 
 
73
  private $container;
74
 
 
 
 
 
 
75
  /**
76
  * WPStaging constructor.
77
  */
78
  private function __construct(Container $container) {
79
- $this->container = $container;
 
80
 
81
- // Todo: Move this to a common service Provider for both Free and Pro. Do not register anything else here.
82
- $this->container->bind(LoggerInterface::class, Logger::class);
 
 
 
83
 
84
  /*
85
  * @todo Before injecting these using DI, we have to register them in a Service Provider.
@@ -95,6 +109,7 @@ final class WPStaging {
95
  $this->cloneSiteFirstRun();
96
  $this->maybeLoadPro();
97
  $this->handleCacheIssues();
 
98
  }
99
 
100
  /**
@@ -283,27 +298,24 @@ final class WPStaging {
283
  public static function getContentDir() {
284
  $wp_upload_dir = wp_upload_dir();
285
  $path = $wp_upload_dir['basedir'] . '/wp-staging';
286
- wp_mkdir_p( $path );
287
  return apply_filters( 'wpstg_get_upload_dir', $path . '/' );
288
  }
289
 
290
  /**
291
  * Get Instance
292
  *
293
- * @param Container|null $container
294
- *
295
  * @return WPStaging
296
  */
297
- public static function getInstance(Container $container = null) {
298
- if( static::$instance === null ) {
299
- if ($container === null) {
300
- $container = new Container;
301
- if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
302
- error_log('A Container instance should be set when requiring this class for the first time.');
303
- }
304
- }
305
 
306
- static::$instance = new static($container);
 
307
  }
308
 
309
  return static::$instance;
@@ -402,19 +414,42 @@ final class WPStaging {
402
  }
403
 
404
  /**
405
- * @return string
 
 
 
 
 
 
 
 
406
  */
407
- public static function getVersion() {
 
 
 
408
 
409
- if(defined('WPSTGPRO_VERSION'))
410
- {
 
 
 
 
411
  return WPSTGPRO_VERSION;
412
  }
413
 
414
  return WPSTG_VERSION;
415
-
416
  }
417
 
 
 
 
 
 
 
 
 
 
418
  /**
419
  * @return array|mixed|object
420
  */
@@ -477,4 +512,16 @@ final class WPStaging {
477
  add_action( 'wp_loaded', [ $permalinksPurge, 'purgePermalinks' ], $permalinksPurge::PLUGINS_LOADED_PRIORITY);
478
  }
479
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  }
7
  die;
8
  }
9
 
10
+ use WPStaging\Framework\Filesystem\DirectoryListing;
11
+ use WPStaging\Framework\Filesystem\Filesystem;
12
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
13
  use WPStaging\Framework\DI\Container;
14
  use WPStaging\Backend\Administrator;
72
  */
73
  private $accessToken;
74
 
75
+ /**
76
+ * @var Container
77
+ */
78
  private $container;
79
 
80
+ /**
81
+ * @var bool Whether this Singleton instance has bootstrapped already.
82
+ */
83
+ private $isBootstrapped = false;
84
+
85
  /**
86
  * WPStaging constructor.
87
  */
88
  private function __construct(Container $container) {
89
+ $this->container = $container;
90
+ }
91
 
92
+ public function bootstrap() {
93
+ $this->isBootstrapped = true;
94
+
95
+ // Todo: Move this to a common service Provider for both Free and Pro. Do not register anything else here.
96
+ $this->container->bind(LoggerInterface::class, Logger::class);
97
 
98
  /*
99
  * @todo Before injecting these using DI, we have to register them in a Service Provider.
109
  $this->cloneSiteFirstRun();
110
  $this->maybeLoadPro();
111
  $this->handleCacheIssues();
112
+ $this->preventDirectoryListing();
113
  }
114
 
115
  /**
298
  public static function getContentDir() {
299
  $wp_upload_dir = wp_upload_dir();
300
  $path = $wp_upload_dir['basedir'] . '/wp-staging';
301
+ (new Filesystem)->mkdir($path);
302
  return apply_filters( 'wpstg_get_upload_dir', $path . '/' );
303
  }
304
 
305
  /**
306
  * Get Instance
307
  *
 
 
308
  * @return WPStaging
309
  */
310
+ public static function getInstance()
311
+ {
312
+ if (static::$instance === null) {
313
+ static::$instance = new WPStaging(new Container);
314
+ self::getInstance();
315
+ }
 
 
316
 
317
+ if (!static::$instance->isBootstrapped) {
318
+ static::$instance->bootstrap();
319
  }
320
 
321
  return static::$instance;
414
  }
415
 
416
  /**
417
+ * Returns the Container. Use this wisely!
418
+ *
419
+ * Acceptable example:
420
+ * - Using the Container as a cache that lives during the request to avoid doing multiple operations.
421
+ *
422
+ * Avoid example:
423
+ * - Using the Container to build instances of classes outside the __construct
424
+ *
425
+ * @return Container
426
  */
427
+ public function getContainer()
428
+ {
429
+ return $this->container;
430
+ }
431
 
432
+ /**
433
+ * @return string
434
+ */
435
+ public static function getVersion()
436
+ {
437
+ if (self::isPro()) {
438
  return WPSTGPRO_VERSION;
439
  }
440
 
441
  return WPSTG_VERSION;
 
442
  }
443
 
444
+ /**
445
+ * @return bool
446
+ *
447
+ * @todo find a better place to make it mockable or add filter for mocking
448
+ */
449
+ public static function isPro()
450
+ {
451
+ return defined('WPSTGPRO_VERSION');
452
+ }
453
  /**
454
  * @return array|mixed|object
455
  */
512
  add_action( 'wp_loaded', [ $permalinksPurge, 'purgePermalinks' ], $permalinksPurge::PLUGINS_LOADED_PRIORITY);
513
  }
514
 
515
+ /**
516
+ * @todo Move this to a base service provider shared between Free and Pro
517
+ */
518
+ private function preventDirectoryListing()
519
+ {
520
+ if (is_admin() && !wp_doing_ajax()) {
521
+ /** @var DirectoryListing $directoryListing */
522
+ $directoryListing = $this->getContainer()->make(DirectoryListing::class);
523
+ $directoryListing->protectPluginUploadDirectory();
524
+ }
525
+ }
526
+
527
  }
Entity/DatabaseSettings.php DELETED
@@ -1,54 +0,0 @@
1
- <?php
2
-
3
- //TODO PHP7.x; declare(strict_types=1);
4
- //TODO PHP7.x; type-hints and return types
5
- // TODO PHP7.1; constant visibility
6
-
7
- namespace WPStaging\Entity;
8
-
9
- use WPStaging\Framework\Entity\AbstractEntity;
10
-
11
- class DatabaseSettings extends AbstractEntity
12
- {
13
- const DEFAULT_COPY_QUERY_LIMIT = 10000;
14
-
15
- const DEFAULT_SEARCH_REPLACE_LIMIT = 5000;
16
-
17
- /** @var int */
18
- private $copyQueryLimit;
19
-
20
- /** @var int */
21
- private $searchReplaceLimit;
22
-
23
- /**
24
- * @return int
25
- */
26
- public function getCopyQueryLimit()
27
- {
28
- return $this->copyQueryLimit?: self::DEFAULT_COPY_QUERY_LIMIT;
29
- }
30
-
31
- /**
32
- * @param int $copyQueryLimit
33
- */
34
- public function setCopyQueryLimit($copyQueryLimit)
35
- {
36
- $this->copyQueryLimit = $copyQueryLimit;
37
- }
38
-
39
- /**
40
- * @return int
41
- */
42
- public function getSearchReplaceLimit()
43
- {
44
- return $this->searchReplaceLimit?: self::DEFAULT_SEARCH_REPLACE_LIMIT;
45
- }
46
-
47
- /**
48
- * @param int $searchReplaceLimit
49
- */
50
- public function setSearchReplaceLimit($searchReplaceLimit)
51
- {
52
- $this->searchReplaceLimit = $searchReplaceLimit;
53
- }
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Entity/FilesystemSettings.php DELETED
@@ -1,93 +0,0 @@
1
- <?php
2
-
3
- //TODO PHP7.x; declare(strict_types=1);
4
- //TODO PHP7.x; type-hints and return types
5
- // TODO PHP7.1; constant visibility
6
-
7
- namespace WPStaging\Entity;
8
-
9
- use WPStaging\Framework\Entity\AbstractEntity;
10
-
11
- class FilesystemSettings extends AbstractEntity
12
- {
13
- const DEFAULT_FILE_COPY_LIMIT = 50;
14
- const DEFAULT_MAX_FILE_SIZE = 8; // MB
15
- const DEAULT_FILE_COPY_BATCH_SIZE = 2;
16
-
17
- /** @var int */
18
- private $fileCopyLimit;
19
-
20
- // In MB
21
- /** @var int */
22
- private $maximumFileSize;
23
-
24
- /** @var int */
25
- private $fileCopyBatchSize;
26
-
27
- /** @var bool */
28
- private $checkDirectorySize;
29
-
30
- /**
31
- * @return int
32
- */
33
- public function getFileCopyLimit()
34
- {
35
- return $this->fileCopyLimit?: self::DEFAULT_FILE_COPY_LIMIT;
36
- }
37
-
38
- /**
39
- * @param int $fileCopyLimit
40
- */
41
- public function setFileCopyLimit($fileCopyLimit)
42
- {
43
- $this->fileCopyLimit = $fileCopyLimit;
44
- }
45
-
46
- /**
47
- * @return int
48
- */
49
- public function getMaximumFileSize()
50
- {
51
- return $this->maximumFileSize?: self::DEFAULT_MAX_FILE_SIZE;
52
- }
53
-
54
- /**
55
- * @param int $maximumFileSize
56
- */
57
- public function setMaximumFileSize($maximumFileSize)
58
- {
59
- $this->maximumFileSize = $maximumFileSize;
60
- }
61
-
62
- /**
63
- * @return int
64
- */
65
- public function getFileCopyBatchSize()
66
- {
67
- return $this->fileCopyBatchSize?: self::DEAULT_FILE_COPY_BATCH_SIZE;
68
- }
69
-
70
- /**
71
- * @param int $fileCopyBatchSize
72
- */
73
- public function setFileCopyBatchSize($fileCopyBatchSize)
74
- {
75
- $this->fileCopyBatchSize = $fileCopyBatchSize;
76
- }
77
-
78
- /**
79
- * @return bool
80
- */
81
- public function isCheckDirectorySize()
82
- {
83
- return $this->checkDirectorySize;
84
- }
85
-
86
- /**
87
- * @param bool $checkDirectorySize
88
- */
89
- public function setCheckDirectorySize($checkDirectorySize)
90
- {
91
- $this->checkDirectorySize = $checkDirectorySize;
92
- }
93
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Entity/Settings.php DELETED
@@ -1,289 +0,0 @@
1
- <?php
2
-
3
- //TODO PHP7.x; declare(strict_types=1);
4
- //TODO PHP7.x; type-hints and return types
5
- // TODO PHP7.1; constant visibility
6
-
7
- namespace WPStaging\Entity;
8
-
9
- use WPStaging\Framework\Entity\AbstractEntity;
10
- use WPStaging\Framework\Traits\ArrayableTrait;
11
- use WPStaging\Framework\Traits\HydrateTrait;
12
-
13
- class Settings extends AbstractEntity
14
- {
15
- use ArrayableTrait {
16
- toArray as traitToArray;
17
- }
18
-
19
- use HydrateTrait {
20
- hydrate as traitHydrate;
21
- }
22
-
23
- const CPU_LOAD_HIGH = 'fast'; // 0 ms;
24
- const CPU_LOAD_MEDIUM = 'medium'; // 250 ms
25
- const CPU_LOAD_LOW = 'low'; // 500 ms
26
-
27
- const DEFAULT_DELAY_BETWEEN_REQUESTS_IN_MS = 0;
28
-
29
- const DEFAULT_MAX_EXECUTION_TIME_IN_SECONDS = 30;
30
- const EXECUTION_TIME_GAP_IN_SECONDS = 5;
31
-
32
- /** @var DatabaseSettings */
33
- private $databaseSettings;
34
-
35
- /** @var FilesystemSettings */
36
- private $filesystemSettings;
37
-
38
- // This is in milliseconds
39
- /** @var int */
40
- private $delayBetweenRequests;
41
-
42
- /** @var bool */
43
- private $keepPermaLinks;
44
-
45
- /** @var bool */
46
- private $debug;
47
-
48
- /** @var bool */
49
- private $optimizer;
50
-
51
- /** @var bool */
52
- private $removeDataOnUninstall;
53
-
54
- /** @var array */
55
- private $accessPermissions;
56
-
57
- public function hydrate(array $data = [])
58
- {
59
- $compatible = [
60
- 'databaseSettings' => [
61
- 'copyQueryLimit' => !empty($data['queryLimit']) ? $data['queryLimit'] : null,
62
- 'searchReplaceLimit' => !empty($data['querySRLimit']) ? $data['querySRLimit'] : null,
63
- ],
64
- 'filesystemSettings' => [
65
- 'fileCopyLimit' => !empty($data['fileLimit']) ? $data['fileLimit'] : null,
66
- 'maximumFileSize' => !empty($data['maxFileSize']) ? $data['maxFileSize'] : null,
67
- 'fileCopyBatchSize' => !empty($data['batchSize']) ? $data['batchSize'] : null,
68
- 'checkDirectorySize' => !empty($data['checkDirectorySize']) ? $data['checkDirectorySize'] : null,
69
- ],
70
- 'delayBetweenRequests' => !empty($data['delayRequests']) ? $data['delayRequests'] : null,
71
- 'keepPermaLinks' => !empty($data['keepPermalinks']) ? $data['keepPermalinks'] : null,
72
- 'debug' => !empty($data['debugMode']) ? $data['debugMode'] : null,
73
- 'optimizer' => !empty($data['optimizer']) ? $data['optimizer'] : null,
74
- 'removeDataOnUninstall' => !empty($data['unInstallOnDelete']) ? $data['unInstallOnDelete'] : null,
75
- 'accessPermissions' => !empty($data['userRoles']) ? $data['userRoles'] : [],
76
- ];
77
-
78
- return $this->traitHydrate($compatible);
79
- }
80
-
81
- public function toArray()
82
- {
83
- $data = $this->traitToArray();
84
- if (isset($data['databaseSettings']) && $data['databaseSettings']) {
85
- $data = array_merge($data, $data['databaseSettings']);
86
- }
87
- if (isset($data['filesystemSettings']) && $data['filesystemSettings']) {
88
- $data = array_merge($data, $data['filesystemSettings']);
89
- }
90
- unset($data['databaseSettings'], $data['filesystemSettings']);
91
-
92
- $compatibleKeys = [
93
- 'copyQueryLimit' => 'queryLimit',
94
- 'searchReplaceLimit' => 'querySRLimit',
95
-
96
- 'fileCopyLimit' => 'fileLimit',
97
- 'maximumFileSize' => 'maxFileSize',
98
- 'fileCopyBatchSize' => 'batchSize',
99
- 'checkDirectorySize' => 'checkDirectorySize',
100
-
101
- 'delayBetweenRequests' => 'delayRequests',
102
- 'keepPermaLinks' => 'keepPermalinks',
103
- 'debug' => 'debugMode',
104
- 'optimizer' => 'optimizer',
105
- 'removeDataOnUninstall' => 'unInstallOnDelete',
106
- 'accessPermissions' => 'userRoles',
107
- ];
108
-
109
- foreach ($data as $key => $value) {
110
- $compatibleKey = isset($compatibleKeys[$key])? $compatibleKeys[$key] : $key;
111
- unset($data[$key]);
112
- $data[$compatibleKey] = is_bool($value)? (string) (int) $value : $value;
113
- }
114
-
115
- return $data;
116
- }
117
-
118
- /**
119
- * @return float|int
120
- */
121
- public function findExecutionTimeLimit()
122
- {
123
- $executionTime = (int) ini_get('max_execution_time');
124
- // TODO don't overwrite when CLI / SAPI and / or add setting to not overwrite for devs
125
- if (!$executionTime || $executionTime > self::DEFAULT_MAX_EXECUTION_TIME_IN_SECONDS) {
126
- $executionTime = self::DEFAULT_MAX_EXECUTION_TIME_IN_SECONDS;
127
- }
128
- return $executionTime - self::EXECUTION_TIME_GAP_IN_SECONDS;
129
- }
130
-
131
- /**
132
- * @return DatabaseSettings
133
- */
134
- public function getDatabaseSettings()
135
- {
136
- if (!$this->databaseSettings) {
137
- $this->databaseSettings = new DatabaseSettings;
138
- }
139
- return $this->databaseSettings;
140
- }
141
-
142
- /**
143
- * @param DatabaseSettings $databaseSettings
144
- */
145
- public function setDatabaseSettings(DatabaseSettings $databaseSettings)
146
- {
147
- $this->databaseSettings = $databaseSettings;
148
- }
149
-
150
- /**
151
- * @return FilesystemSettings
152
- */
153
- public function getFilesystemSettings()
154
- {
155
- if (!$this->filesystemSettings) {
156
- $this->filesystemSettings = new FilesystemSettings;
157
- }
158
- return $this->filesystemSettings;
159
- }
160
-
161
- /**
162
- * @param FilesystemSettings $filesystemSettings
163
- */
164
- public function setFilesystemSettings(FilesystemSettings $filesystemSettings)
165
- {
166
- $this->filesystemSettings = $filesystemSettings;
167
- }
168
-
169
- /**
170
- * @return int
171
- */
172
- public function getDelayBetweenRequests()
173
- {
174
- return $this->delayBetweenRequests?: self::DEFAULT_DELAY_BETWEEN_REQUESTS_IN_MS;
175
- }
176
-
177
- /**
178
- * @param int $delayBetweenRequests
179
- */
180
- public function setDelayBetweenRequests($delayBetweenRequests)
181
- {
182
- $this->delayBetweenRequests = $delayBetweenRequests;
183
- }
184
-
185
- /**
186
- * @return bool
187
- */
188
- public function isKeepPermaLinks()
189
- {
190
- return $this->keepPermaLinks;
191
- }
192
-
193
- /**
194
- * @param bool $keepPermaLinks
195
- */
196
- public function setKeepPermaLinks($keepPermaLinks)
197
- {
198
- if (!is_bool($keepPermaLinks)) {
199
- $keepPermaLinks = $keepPermaLinks === 'true' || $keepPermaLinks === '1' || $keepPermaLinks === 1;
200
- }
201
- $this->keepPermaLinks = $keepPermaLinks;
202
- }
203
-
204
- /**
205
- * @return bool
206
- */
207
- public function isDebug()
208
- {
209
- return $this->debug;
210
- }
211
-
212
- /**
213
- * @param bool $debug
214
- */
215
- public function setDebug($debug)
216
- {
217
- if (!is_bool($debug)) {
218
- $debug = $debug === 'true' || $debug === '1' || $debug === 1;
219
- }
220
- $this->debug = $debug;
221
- }
222
-
223
- /**
224
- * @return bool
225
- */
226
- public function isOptimizer()
227
- {
228
- return $this->optimizer;
229
- }
230
-
231
- /**
232
- * @param bool $optimizer
233
- */
234
- public function setOptimizer($optimizer)
235
- {
236
- if (!is_bool($optimizer)) {
237
- $optimizer = $optimizer === 'true' || $optimizer === '1' || $optimizer === 1;
238
- }
239
- $this->optimizer = $optimizer;
240
- }
241
-
242
- /**
243
- * @return bool
244
- */
245
- public function isRemoveDataOnUninstall()
246
- {
247
- return $this->removeDataOnUninstall;
248
- }
249
-
250
- /**
251
- * @param bool $removeDataOnUninstall
252
- */
253
- public function setRemoveDataOnUninstall($removeDataOnUninstall)
254
- {
255
- if (!is_bool($removeDataOnUninstall)) {
256
- $removeDataOnUninstall = $removeDataOnUninstall === 'true' || $removeDataOnUninstall === '1' || $removeDataOnUninstall === 1;
257
- }
258
- $this->removeDataOnUninstall = $removeDataOnUninstall;
259
- }
260
-
261
- /**
262
- * @return array
263
- */
264
- public function getAccessPermissions()
265
- {
266
- return $this->accessPermissions?: [];
267
- }
268
-
269
- /**
270
- * @param array $accessPermissions
271
- */
272
- public function setAccessPermissions(array $accessPermissions = null)
273
- {
274
- $this->accessPermissions = $accessPermissions;
275
- }
276
-
277
- // TODO PHP5.6; const array
278
- /**
279
- * @return array
280
- */
281
- public function getAvailableCpuLoads()
282
- {
283
- return [
284
- self::CPU_LOAD_HIGH,
285
- self::CPU_LOAD_MEDIUM,
286
- self::CPU_LOAD_LOW,
287
- ];
288
- }
289
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Adapter/Directory.php CHANGED
@@ -2,8 +2,6 @@
2
 
3
  namespace WPStaging\Framework\Adapter;
4
 
5
- use RuntimeException;
6
-
7
  class Directory
8
  {
9
  /** @var string|null */
@@ -42,18 +40,13 @@ class Directory
42
  return $this->uploadDir;
43
  }
44
 
45
- // Get upload directory information. Default is ABSPATH . 'wp-content/uploads'
46
- // Can be customized by populating the db option upload_path or the constant UPLOADS
47
- // If both are defined WordPress will uses the value of the UPLOADS constant
48
- $dir = wp_upload_dir();
49
 
50
- // TODO RPoC
51
- if ($dir['error']) {
52
- throw new RuntimeException($dir['error']);
53
- }
54
 
55
- // Get absolute path to wordpress uploads directory e.g /var/www/wp-content/uploads/
56
- $this->uploadDir = trailingslashit($dir['basedir']);
57
  return $this->uploadDir;
58
  }
59
 
2
 
3
  namespace WPStaging\Framework\Adapter;
4
 
 
 
5
  class Directory
6
  {
7
  /** @var string|null */
40
  return $this->uploadDir;
41
  }
42
 
43
+ // Get absolute path to wordpress uploads directory e.g /var/www/wp-content/uploads/
44
+ // Default is ABSPATH . 'wp-content/uploads', but it can be customized by the db option upload_path or the constant UPLOADS
45
+ $uploadDir = wp_upload_dir(null, false, null)['basedir'];
46
+ $uploadDir = trim(trailingslashit($uploadDir));
47
 
48
+ $this->uploadDir = $uploadDir;
 
 
 
49
 
 
 
50
  return $this->uploadDir;
51
  }
52
 
Framework/CloningProcess/Data/DBCloningService.php CHANGED
@@ -38,18 +38,22 @@ abstract class DBCloningService extends CloningService
38
  */
39
  protected function skipTable($table)
40
  {
 
 
 
 
41
  // Skip - Table does not exist
42
- if (!$this->tableExists($this->dto->getPrefix() . $table)) {
43
- $this->log("Table " . $this->dto->getPrefix() . $table . ' not found. Skipping');
44
  return true;
45
  }
46
- //TODO: This check may not be necessary because a non-selected table isn't copied and thus shouldn't exist
47
- // Skip - Table is not selected or updated.
48
- //Removed due to issue https://github.com/WP-Staging/wp-staging-pro/issues/385. @todo Delete this later!
49
- /* if (!in_array($this->dto->getPrefix() . $table, $this->dto->getTables())) {
50
- $this->log("Table " . $this->dto->getPrefix() . $table . ' not selected/updated. Skipping');
51
  return true;
52
- }*/
53
 
54
  return false;
55
  }
38
  */
39
  protected function skipTable($table)
40
  {
41
+ // during cloning process we are appending underscore to the staging prefix if it is not there
42
+ // @see WPStaging/Backend/Modules/Jobs/Cloning.php#L293
43
+ // code below solves this issue https://github.com/WP-Staging/wp-staging-pro/issues/385
44
+ $prefix = rtrim($this->dto->getPrefix(), '_') . '_';
45
  // Skip - Table does not exist
46
+ if (!$this->tableExists($prefix . $table)) {
47
+ $this->log("Table " . $prefix . $table . ' not found. Skipping');
48
  return true;
49
  }
50
+ // during update process option table was not skipped even though it was not selected
51
+ // that was causing problem if staging site prefix is basically something string appended to,
52
+ // production site prefix i.e. staging prefix: wp_stagging_ and production prefix: wp_
53
+ if (!in_array($prefix . $table, $this->dto->getTables())) {
54
+ $this->log("Table " . $prefix . $table . ' not selected/updated. Skipping');
55
  return true;
56
+ }
57
 
58
  return false;
59
  }
Framework/CloningProcess/Data/UpdateStagingOptionsTable.php CHANGED
@@ -23,7 +23,7 @@ class UpdateStagingOptionsTable extends DBCloningService
23
  'wpstg_rmpermalinks_executed' => ' ',
24
  'blog_public' => 0,
25
  FirstRun::FIRST_RUN_KEY => 'true',
26
- 'wpstg_emails_disabled' => (bool) $this->dto->getJob()->getOptions()->emailsDisabled,
27
  ];
28
  if(!$this->keepPermalinks()) {
29
  $updateOrInsert['rewrite_rules'] = null;
23
  'wpstg_rmpermalinks_executed' => ' ',
24
  'blog_public' => 0,
25
  FirstRun::FIRST_RUN_KEY => 'true',
26
+ FirstRun::MAILS_DISABLED_KEY => !((bool) $this->dto->getJob()->getOptions()->emailsAllowed),
27
  ];
28
  if(!$this->keepPermalinks()) {
29
  $updateOrInsert['rewrite_rules'] = null;
Framework/CloningProcess/Data/UpdateWpConfigTablePrefix.php CHANGED
@@ -21,8 +21,9 @@ class UpdateWpConfigTablePrefix extends FileCloningService
21
 
22
  $oldUrl = (!$this->dto->isMultisite()) ? $this->dto->getHomeUrl() : $this->dto->getBaseUrl();
23
 
24
- // Replace table prefix
25
- $pattern = '/\$table_prefix\s*=\s*(.*)/';
 
26
  $replacement = '$table_prefix = \'' . $prefix . '\'; // Changed by WP Staging';
27
  $content = preg_replace($pattern, $replacement, $content);
28
 
21
 
22
  $oldUrl = (!$this->dto->isMultisite()) ? $this->dto->getHomeUrl() : $this->dto->getBaseUrl();
23
 
24
+ // Don't update the table prefix if the line starts with //, /* or * (ignoring space before them),
25
+ // Otherwise replace table prefix
26
+ $pattern = '/^\s*((?!\/\/|\/\*|\*))\$table_prefix\s*=\s*(.*)/m';
27
  $replacement = '$table_prefix = \'' . $prefix . '\'; // Changed by WP Staging';
28
  $content = preg_replace($pattern, $replacement, $content);
29
 
Framework/CloningProcess/Database/DatabaseCloningService.php CHANGED
@@ -53,8 +53,10 @@ class DatabaseCloningService
53
  foreach ($rows as $row) {
54
  $escaped_values = $this->mysqlEscapeMimic(array_values($row));
55
  $values = implode("', '", $escaped_values);
56
- if ($stagingDb->query("INSERT INTO `{$new}` VALUES ('{$values}')") === false) {
 
57
  $this->log("Can not insert data into table {$new}");
 
58
  }
59
  }
60
  // Commit transaction
@@ -166,6 +168,17 @@ class DatabaseCloningService
166
  $this->dto->getJob()->log($prependString . $message, $type);
167
  }
168
 
 
 
 
 
 
 
 
 
 
 
 
169
  /**
170
  * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
171
  * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
53
  foreach ($rows as $row) {
54
  $escaped_values = $this->mysqlEscapeMimic(array_values($row));
55
  $values = implode("', '", $escaped_values);
56
+ $query = "INSERT INTO `{$new}` VALUES ('{$values}')";
57
+ if ($stagingDb->query($query) === false) {
58
  $this->log("Can not insert data into table {$new}");
59
+ $this->debugLog("Failed Query: " . $query . " Error: " . $stagingDb->last_error);
60
  }
61
  }
62
  // Commit transaction
168
  $this->dto->getJob()->log($prependString . $message, $type);
169
  }
170
 
171
+
172
+ /**
173
+ * @param string $message
174
+ * @param string $type
175
+ */
176
+ protected function debugLog($message, $type = Logger::TYPE_INFO)
177
+ {
178
+ $prependString = $this->dto->isExternal() ? "DB External Copy: " : "DB Copy: ";
179
+ $this->dto->getJob()->debugLog($prependString . $message, $type);
180
+ }
181
+
182
  /**
183
  * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
184
  * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
Framework/DI/Container.php CHANGED
@@ -31,6 +31,54 @@ class Container extends \WPStaging\Vendor\tad_DI52_Container
31
  }
32
  }
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  /**
36
  * Overloads bind definition binding the prefix as well so that the DI container works locally.
31
  }
32
  }
33
 
34
+ /**
35
+ * You can use this to store an array of data in the container, without having to worry
36
+ * if the array was already initialized or not.
37
+ *
38
+ * @param $arrayName string The name of the array. If it doesn't exist yet, it will be created.
39
+ * @param $value mixed The value to add to the array.
40
+ *
41
+ * @return bool True if the value was added to the array. False if value already existed in the array.
42
+ */
43
+ public function pushToArray($arrayName, $value)
44
+ {
45
+ try {
46
+ $arrayValues = (array)$this->offsetGet($arrayName);
47
+
48
+ if (in_array($value, $arrayValues)) {
49
+ // Do nothing, as the item already exists in this array.
50
+ return false;
51
+ }
52
+ } catch (\Exception $e) {
53
+ // If nothing is set in the container yet, create an empty one.
54
+ $this->setVar($arrayName, []);
55
+ $arrayValues = [];
56
+ }
57
+
58
+ // Add this value to the array.
59
+ $arrayValues[] = $value;
60
+
61
+ $this->setVar($arrayName, $arrayValues);
62
+
63
+ return true;
64
+ }
65
+
66
+ /**
67
+ * You can use this to get an array of data in the container, without having to worry
68
+ * if the array was already initialized or not.
69
+ *
70
+ * @param $arrayName string The name of the array. If it doesn't exist yet, an empty array will be returned.
71
+ *
72
+ * @return array The array of data requested, or an empty array if it's not set.
73
+ */
74
+ public function getFromArray($arrayName)
75
+ {
76
+ try {
77
+ return (array)$this->offsetGet($arrayName);
78
+ } catch (\Exception $e) {
79
+ return [];
80
+ }
81
+ }
82
 
83
  /**
84
  * Overloads bind definition binding the prefix as well so that the DI container works locally.
Framework/Filesystem/DirectoryListing.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Filesystem;
4
+
5
+ use RuntimeException;
6
+ use WPStaging\Backend\Notices\Notices;
7
+ use WPStaging\Core\Utils\Htaccess;
8
+ use WPStaging\Core\Utils\IISWebConfig;
9
+ use WPStaging\Core\WPStaging;
10
+ use WPStaging\Framework\Adapter\Directory;
11
+
12
+ /**
13
+ * Class DirectoryListing
14
+ *
15
+ * Protect sensitive folders from directory listings.
16
+ *
17
+ * @package WPStaging\Framework\Filesystem
18
+ */
19
+ class DirectoryListing
20
+ {
21
+ private $directory;
22
+ private $htaccess;
23
+ private $webConfig;
24
+
25
+ public function __construct(Directory $directory, Htaccess $htaccess, IISWebConfig $webConfig)
26
+ {
27
+ $this->directory = $directory;
28
+ $this->htaccess = $htaccess;
29
+ $this->webConfig = $webConfig;
30
+ }
31
+
32
+ /**
33
+ * Protect the WPStaging upload folder from directory listing.
34
+ */
35
+ public function protectPluginUploadDirectory()
36
+ {
37
+ $lastChecked = get_transient('wpstg.directory_listing.last_checked');
38
+ $now = current_time('timestamp');
39
+
40
+ if (!empty($lastChecked)) {
41
+ if (($now - $lastChecked) < $this->getInterval()) {
42
+ // Early bail: Last check happened not long ago...
43
+ return;
44
+ }
45
+ }
46
+
47
+ set_transient('wpstg.directory_listing.last_checked', $now);
48
+
49
+ try {
50
+ $it = new \RecursiveDirectoryIterator($this->directory->getPluginUploadsDirectory());
51
+ $it = new \RecursiveIteratorIterator($it);
52
+
53
+ $dirsToProtect = [];
54
+
55
+ /** @var \SplFileInfo $item */
56
+ foreach ($it as $item) {
57
+ if ($item->isDir() && $item->getBasename() !== '..') {
58
+ $dirsToProtect[] = $item->getRealPath();
59
+ }
60
+ }
61
+
62
+ $dirsToProtect = array_unique($dirsToProtect);
63
+
64
+ foreach ($dirsToProtect as $dir) {
65
+ try {
66
+ $this->preventDirectoryListing($dir);
67
+ } catch (\Exception $e) {
68
+ /**
69
+ * Enqueue this error. All enqueued errors will be shown as a single notice.
70
+ *
71
+ * @see \WPStaging\Backend\Notices\Notices::showDirectoryListingWarningNotice
72
+ */
73
+ WPStaging::getInstance()->getContainer()->pushToArray(Notices::$directoryListingErrors, $e->getMessage());
74
+ }
75
+ }
76
+ } catch (\Exception $e) {
77
+ if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
78
+ error_log('WPSTAGING: Could not open plugin upload directory to protect from directory listing. ' . $e->getMessage());
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Directory listing protection is very fast. In a high-end computer
85
+ * with a NVMe hard-drive running Linux, it completes in 0.0057 seconds.
86
+ *
87
+ * However, with slow HDDs and more folders this could take longer.
88
+ *
89
+ * Since this runs on every request, and disks are the slowest thing in computing,
90
+ * we micro-optimize this code for performance, avoiding hitting the disk while we can.
91
+ *
92
+ * Thus we only run these checks after an interval, which is by default, once every 15 minutes.
93
+ *
94
+ * @return int How many seconds to wait between each check for directory listing protection.
95
+ */
96
+ private function getInterval()
97
+ {
98
+ return (int)apply_filters('wpstg.directory_listing.interval_check', 15 * 60);
99
+ }
100
+
101
+ /**
102
+ * @param string $path The path to prevent directory listing.
103
+ *
104
+ * @throws RuntimeException When could not prevent directory listing on the given path.
105
+ *
106
+ * @return void
107
+ */
108
+ public function preventDirectoryListing($path)
109
+ {
110
+ $path = trailingslashit(wp_normalize_path($path));
111
+
112
+ // Earliest bail: Directory listing is already prevented.
113
+ if (file_exists($path . 'index.php')) {
114
+ return;
115
+ }
116
+
117
+ // Early bail: Not a directory.
118
+ if (!is_dir($path)) {
119
+ return;
120
+ }
121
+
122
+ // If it's not writable, check if directory listing is prevented. If both fail, bail.
123
+ if (!is_writable($path) && !file_exists($path . 'index.php')) {
124
+ throw new RuntimeException(__(sprintf("Could not prevent directory listing on %s (Reason: Directory is not writable and does not contain an index file)", untrailingslashit($path)), 'wp-staging'));
125
+ }
126
+
127
+ // index.php
128
+ if (!file_exists($path . 'index.php')) {
129
+ $indexPhpCreated = file_put_contents($path . 'index.php', <<<PHP
130
+ <?php
131
+ /**
132
+ * WPSTAGING automatically places this index file on all folders it creates to prevent
133
+ * directory listing on servers that might have directory listing enabled.
134
+ *
135
+ * You might have Directory Listing disabled already. If you do, feel free to ignore this file.
136
+ *
137
+ * @link https://www.google.com/search?q=directory+listing+vulnerability
138
+ * Read more about why Directory Listing can be a security risk.
139
+ *
140
+ * @link https://www.google.com/search?q=disable+directory+listing+apache
141
+ * How to disable Directory Listing on Apache.
142
+ *
143
+ * @link https://www.google.com/search?q=disable+directory+listing+nginx
144
+ * How to disable Directory Listing on Nginx.
145
+ */
146
+ PHP
147
+ );
148
+
149
+ if ($indexPhpCreated === false) {
150
+ throw new RuntimeException(__(sprintf('Could not prevent directory listing on %s (Reason: Failed to create index.php)', untrailingslashit($path)), 'wp-staging'));
151
+ }
152
+ }
153
+
154
+ // index.html
155
+ if (!file_exists($path . 'index.html')) {
156
+ file_put_contents($path . 'index.html', '');
157
+ // We'll not throw if index.html fails to write, as this is just an additional protection layer.
158
+ }
159
+
160
+ // .htaccess
161
+ if (!file_exists($path . '.htaccess')) {
162
+ $this->htaccess->create($path . '.htaccess');
163
+ // We'll not throw if .htaccess fails to write, as this is just an additional protection layer.
164
+ }
165
+
166
+ // web.config
167
+ if (!file_exists($path . 'web.config')) {
168
+ $this->webConfig->create($path . 'web.config');
169
+ // We'll not throw if web.config fails to write, as this is just an additional protection layer.
170
+ }
171
+ }
172
+ }
Framework/Filesystem/DirectoryScanner.php CHANGED
@@ -1,133 +1,60 @@
1
  <?php
2
 
3
- // TODO PHP7.x; declare(strict_type=1);
4
- // TODO PHP7.x; type hints & return types
5
- // TODO PHP7.1; constant visibility
6
 
7
  namespace WPStaging\Framework\Filesystem;
8
 
9
- use RuntimeException;
10
- use WPStaging\Vendor\Symfony\Component\Finder\Finder;
11
- use WPStaging\Framework\Queue\FinishedQueueException;
12
- use WPStaging\Framework\Queue\Queue;
13
- use WPStaging\Framework\Queue\Storage\BufferedCacheStorage;
14
- use WPStaging\Framework\Utils\Cache\BufferedCache;
15
-
16
  class DirectoryScanner
17
  {
18
- const DATA_CACHE_FILE = 'filesystem_scanner_directory_data';
19
- const QUEUE_CACHE_FILE = 'directory_scanner';
20
-
21
- /** @var BufferedCache */
22
- private $cache;
23
-
24
- /** @var BufferedCacheStorage */
25
- private $storage;
26
-
27
- /** @var DirectoryService */
28
- private $service;
29
-
30
- /** @var Queue|null */
31
- private $queue;
32
-
33
- /** @var array */
34
- private $newQueueItems;
35
-
36
- public function __construct(BufferedCache $cache, BufferedCacheStorage $storage, DirectoryService $service)
37
- {
38
- $this->newQueueItems = [];
39
- $this->cache = clone $cache;
40
- $this->storage = clone $storage;
41
- $this->service = $service;
42
- }
43
-
44
- public function __destruct()
45
- {
46
- if ($this->newQueueItems && $this->queue) {
47
- $this->queue->pushAsArray($this->newQueueItems);
48
- }
49
- }
50
-
51
- /**
52
- * @param string $name
53
- */
54
- public function setQueueByName($name = self::QUEUE_CACHE_FILE)
55
- {
56
- $this->queue = new Queue;
57
- $this->queue->setName($name);
58
- $this->queue->setStorage($this->storage);
59
- }
60
-
61
- /**
62
- * @param array|null $excluded
63
- * @param int $depth
64
- * @return Finder|null
65
- */
66
- public function scanCurrentPath(array $excluded = null, $depth = 0)
67
- {
68
- $path = $this->getPathFromQueue();
69
- if ($path === null) {
70
- throw new FinishedQueueException('Directory Scanner Queue is Finished');
71
- }
72
-
73
- $path = ABSPATH . $path;
74
- return $this->service->scan($path, $depth, $excluded);
75
- }
76
-
77
  /**
78
- * @return string|null
 
 
 
79
  */
80
- public function getPathFromQueue()
81
  {
82
- if ($this->queue->count() > 0) {
83
- return $this->queue->pop();
 
 
84
  }
85
 
86
- if ($this->newQueueItems) {
87
- return array_shift($this->newQueueItems);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
- return null;
91
- }
92
-
93
- /**
94
- * @param string $item
95
- */
96
- public function addToNewQueue($item)
97
- {
98
- $this->newQueueItems[] = $item;
99
- }
100
-
101
- /**
102
- * @return BufferedCache
103
- */
104
- public function getCache()
105
- {
106
- return $this->cache;
107
- }
108
-
109
- /**
110
- * @return Queue
111
- */
112
- public function getQueue()
113
- {
114
- if (!$this->queue) {
115
- // TODO Custom Exception
116
- throw new RuntimeException('DirectoryScanner Queue is not set');
117
- }
118
- return $this->queue;
119
- }
120
-
121
- public function setNewQueueItems(array $items = null)
122
- {
123
- $this->newQueueItems = $items;
124
- }
125
-
126
- /**
127
- * @return DirectoryService
128
- */
129
- public function getService()
130
- {
131
- return $this->service;
132
  }
133
  }
1
  <?php
2
 
3
+ // TODO PHP7.x; declare(strict_types=1);
4
+ // TODO PHP7.x; return types && type-hints
 
5
 
6
  namespace WPStaging\Framework\Filesystem;
7
 
 
 
 
 
 
 
 
8
  class DirectoryScanner
9
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  /**
11
+ * @param string $directory
12
+ * @param array $excludedDirectories
13
+ *
14
+ * @return array
15
  */
16
+ public function scan($directory, array $excludedDirectories = [])
17
  {
18
+ try {
19
+ $it = new \DirectoryIterator($directory);
20
+ } catch (\Exception $e) {
21
+ return [];
22
  }
23
 
24
+ /*
25
+ * Normalize the excluded directories to maximize the chances
26
+ * of matching a excluded directory if we mean to.
27
+ *
28
+ * Exclusion: /var/www/single/wp-content/plugins/WooCommerce/
29
+ * Becomes: /var/www/single/wp-content/plugins/woocommerce
30
+ *
31
+ * So that any of these exclusions matches:
32
+ * /var/www/single/wp-content/plugins/woocommerce
33
+ * /var/www/single/wp-content/plugins/woocommerce/
34
+ * /var/www/single/wp-content/plugins/WooCommerce
35
+ * /var/www/single/wp-content/plugins/WooCommerce/
36
+ */
37
+ $excludedDirectories = array_map(function ($item) {
38
+ return untrailingslashit(strtolower($item));
39
+ }, $excludedDirectories);
40
+
41
+ /**
42
+ * Allow user to filter the excluded directories in a site export.
43
+ * @todo Should we add UI for this?
44
+ */
45
+ $excludedDirectories = (array)apply_filters('wpstg.export.site.directory.excluded', $excludedDirectories);
46
+
47
+ $dirs = [];
48
+
49
+ /** @var \SplFileInfo $item */
50
+ foreach ($it as $item) {
51
+ if ($item->isDir() && $item->getFilename() != "." && $item->getFilename() != "..") {
52
+ if (!in_array(untrailingslashit(strtolower($item->getRealPath())), $excludedDirectories)) {
53
+ $dirs[] = $item->getRealPath();
54
+ }
55
+ }
56
  }
57
 
58
+ return $dirs;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
60
  }
Framework/Filesystem/DirectoryScannerControl.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // TODO PHP7.x; declare(strict_type=1);
4
+ // TODO PHP7.x; type hints & return types
5
+ // TODO PHP7.1; constant visibility
6
+
7
+ namespace WPStaging\Framework\Filesystem;
8
+
9
+ use RuntimeException;
10
+ use WPStaging\Component\Task\Filesystem\DirectoryScannerTask;
11
+ use WPStaging\Framework\Adapter\Directory;
12
+ use WPStaging\Framework\Queue\FinishedQueueException;
13
+ use WPStaging\Framework\Queue\Queue;
14
+ use WPStaging\Framework\Queue\Storage\BufferedCacheStorage;
15
+ use WPStaging\Framework\Utils\Cache\BufferedCache;
16
+
17
+ /**
18
+ * Class DirectoryScannerControl
19
+ *
20
+ * @see DirectoryScannerTask
21
+ *
22
+ * @package WPStaging\Framework\Filesystem
23
+ */
24
+ class DirectoryScannerControl
25
+ {
26
+ const DATA_CACHE_FILE = 'filesystem_scanner_directory_data';
27
+ const QUEUE_CACHE_FILE = 'directory_scanner';
28
+
29
+ /** @var BufferedCache */
30
+ private $cache;
31
+
32
+ /** @var BufferedCacheStorage */
33
+ private $storage;
34
+
35
+ /** @var DirectoryScanner */
36
+ private $scanner;
37
+
38
+ /** @var Queue|null */
39
+ private $queue;
40
+
41
+ /** @var array */
42
+ private $newQueueItems;
43
+
44
+ /** @var Directory */
45
+ private $directory;
46
+
47
+ public function __construct(BufferedCache $cache, BufferedCacheStorage $storage, DirectoryScanner $scanner, Directory $directory)
48
+ {
49
+ $this->newQueueItems = [];
50
+ $this->cache = clone $cache;
51
+ $this->storage = clone $storage;
52
+ $this->scanner = $scanner;
53
+ $this->directory = $directory;
54
+ }
55
+
56
+ public function __destruct()
57
+ {
58
+ if ($this->newQueueItems && $this->queue) {
59
+ $this->queue->pushAsArray($this->newQueueItems);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @param string $name
65
+ */
66
+ public function setQueueByName($name = self::QUEUE_CACHE_FILE)
67
+ {
68
+ $this->queue = new Queue;
69
+ $this->queue->setName($name);
70
+ $this->queue->setStorage($this->storage);
71
+ }
72
+
73
+ /**
74
+ * @param array $excluded
75
+ *
76
+ * @return array
77
+ */
78
+ public function scanCurrentPath(array $excluded = [])
79
+ {
80
+ $path = $this->getPathFromQueue();
81
+ if ($path === null) {
82
+ throw new FinishedQueueException('Directory Scanner Queue is Finished');
83
+ }
84
+
85
+ $path = ABSPATH . $path;
86
+
87
+ return $this->scanner->scan($path, $excluded);
88
+ }
89
+
90
+ /**
91
+ * @return string|null
92
+ */
93
+ public function getPathFromQueue()
94
+ {
95
+ if ($this->queue->count() > 0) {
96
+ return $this->queue->pop();
97
+ }
98
+
99
+ if ($this->newQueueItems) {
100
+ return array_shift($this->newQueueItems);
101
+ }
102
+
103
+ return null;
104
+ }
105
+
106
+ /**
107
+ * @param string $item
108
+ */
109
+ public function addToNewQueue($item)
110
+ {
111
+ $this->newQueueItems[] = $item;
112
+ }
113
+
114
+ /**
115
+ * @return BufferedCache
116
+ */
117
+ public function getCache()
118
+ {
119
+ return $this->cache;
120
+ }
121
+
122
+ /**
123
+ * @return Queue
124
+ */
125
+ public function getQueue()
126
+ {
127
+ if (!$this->queue) {
128
+ // TODO Custom Exception
129
+ throw new RuntimeException('DirectoryScannerControl Queue is not set');
130
+ }
131
+ return $this->queue;
132
+ }
133
+
134
+ public function setNewQueueItems(array $items = null)
135
+ {
136
+ $this->newQueueItems = $items;
137
+ }
138
+
139
+ /**
140
+ * @return Directory
141
+ */
142
+ public function getDirectory()
143
+ {
144
+ return $this->directory;
145
+ }
146
+ }
Framework/Filesystem/DirectoryService.php DELETED
@@ -1,65 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_types=1);
4
- // TODO PHP7.x; return types && type-hints
5
-
6
- namespace WPStaging\Framework\Filesystem;
7
-
8
- use WPStaging\Vendor\Symfony\Component\Finder\Finder;
9
- use WPStaging\Framework\Adapter\Directory;
10
-
11
- class DirectoryService
12
- {
13
-
14
- /** @var Directory */
15
- private $directory;
16
-
17
- public function __construct(Directory $directory)
18
- {
19
- $this->directory = $directory;
20
- }
21
-
22
- /**
23
- * @param string $directory
24
- * @param string $depth
25
- * @param array|null $excludedDirectories
26
- *
27
- * @return Finder|null
28
- */
29
- public function scan($directory, $depth = null, array $excludedDirectories = null)
30
- {
31
- $finder = (new Finder)
32
- ->ignoreUnreadableDirs()
33
- ->directories()
34
- ->in($directory)
35
- ;
36
-
37
- if ($excludedDirectories) {
38
- foreach($excludedDirectories as $excludedDirectory) {
39
- $notPath = str_replace($directory, null, $excludedDirectory);
40
- $notPath = '#' . trim($notPath, '#') . '#';
41
- $finder->notPath($notPath);
42
- }
43
- }
44
-
45
- if ($depth !== null) {
46
- $finder->depth($depth);
47
- }
48
-
49
- $finderHasResults = count($finder) > 0;
50
-
51
- if (!$finderHasResults) {
52
- return null;
53
- }
54
-
55
- return $finder;
56
- }
57
-
58
- /**
59
- * @return Directory
60
- */
61
- public function getDirectory()
62
- {
63
- return $this->directory;
64
- }
65
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Filesystem/FileScanner.php CHANGED
@@ -6,125 +6,93 @@
6
 
7
  namespace WPStaging\Framework\Filesystem;
8
 
9
- use InvalidArgumentException;
10
- use RuntimeException;
11
- use WPStaging\Vendor\Symfony\Component\Finder\Finder;
12
- use WPStaging\Framework\Queue\FinishedQueueException;
13
- use WPStaging\Framework\Queue\Queue;
14
- use WPStaging\Framework\Queue\Storage\BufferedCacheStorage;
15
- use WPStaging\Framework\Utils\Cache\BufferedCache;
16
 
17
  class FileScanner
18
  {
19
- const DATA_CACHE_FILE = 'filesystem_scanner_file_data';
20
- const QUEUE_CACHE_FILE = 'file_scanner';
21
 
22
- /** @var BufferedCache */
23
- private $cache;
24
-
25
- /** @var BufferedCacheStorage */
26
- private $storage;
27
-
28
- /** @var DirectoryService */
29
- private $service;
30
-
31
- /** @var Queue|null */
32
- private $queue;
33
-
34
- /** @var array */
35
- private $newQueueItems;
36
-
37
- public function __construct(BufferedCache $cache, BufferedCacheStorage $storage, FileService $service)
38
  {
39
- $this->newQueueItems = [];
40
- $this->cache = clone $cache;
41
- $this->storage = clone $storage;
42
- $this->service = $service;
43
- }
44
-
45
- public function __destruct()
46
- {
47
- if ($this->newQueueItems && $this->queue) {
48
- $this->queue->pushAsArray($this->newQueueItems);
49
- }
50
  }
51
 
52
  /**
53
- * @param string $name
 
 
 
54
  */
55
- public function setQueueByName($name = self::QUEUE_CACHE_FILE)
56
  {
57
- $this->queue = new Queue;
58
- $this->queue->setName($name);
59
- $this->queue->setStorage($this->storage);
60
- }
61
-
62
- /**
63
- * @param array|null $excluded
64
- * @param int $depth
65
- * @return Finder|null
66
- */
67
- public function scanCurrentPath(array $excluded = null, $depth = 0)
68
- {
69
- $path = $this->getPathFromQueue();
70
- if ($path === null) {
71
- throw new FinishedQueueException('File Scanner Queue is Finished');
72
- }
73
-
74
- $path = ABSPATH . $path;
75
  try {
76
- return $this->service->scan($path, $depth, $excluded);
77
- } catch(InvalidArgumentException $e) {
78
- return null;
79
  }
80
- }
81
 
82
- /**
83
- * @return string|null
84
- */
85
- public function getPathFromQueue()
86
- {
87
- if ($this->queue->count() > 0) {
88
- return $this->queue->pop();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
- if ($this->newQueueItems) {
92
- return array_shift($this->newQueueItems);
93
- }
94
-
95
- return null;
96
- }
97
-
98
- /**
99
- * @param string $item
100
- */
101
- public function addToNewQueue($item)
102
- {
103
- $this->newQueueItems[] = $item;
104
- }
105
-
106
- /**
107
- * @return BufferedCache
108
- */
109
- public function getCache()
110
- {
111
- return $this->cache;
112
- }
113
-
114
- /**
115
- * @return Queue
116
- */
117
- public function getQueue()
118
- {
119
- if (!$this->queue) {
120
- // TODO Custom Exception
121
- throw new RuntimeException('FileScanner Queue is not set');
122
- }
123
- return $this->queue;
124
- }
125
-
126
- public function setNewQueueItems(array $items = null)
127
- {
128
- $this->newQueueItems = $items;
129
  }
130
  }
6
 
7
  namespace WPStaging\Framework\Filesystem;
8
 
9
+ use WPStaging\Framework\Adapter\Directory;
 
 
 
 
 
 
10
 
11
  class FileScanner
12
  {
13
+ private $directory;
14
+ private $filesystem;
15
 
16
+ public function __construct(Directory $directory, Filesystem $filesystem)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  {
18
+ $this->directory = $directory;
19
+ $this->filesystem = $filesystem;
 
 
 
 
 
 
 
 
 
20
  }
21
 
22
  /**
23
+ * @param string $directory
24
+ * @param bool $includeOtherFilesInWpContent
25
+ *
26
+ * @return array
27
  */
28
+ public function scan($directory, $includeOtherFilesInWpContent)
29
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  try {
31
+ $it = new \DirectoryIterator($directory);
32
+ } catch (\Exception $e) {
33
+ return [];
34
  }
 
35
 
36
+ /**
37
+ * Allow user to exclude certain file extensions from being exported.
38
+ */
39
+ $excludedFileExtensions = (array)apply_filters('wpstg.export.site.file_extension.excluded', []);
40
+
41
+ /**
42
+ * Allow user to exclude files larger than given size from being exported.
43
+ */
44
+ $ignoreFilesBiggerThan = (int)apply_filters('wpstg.export.site.file.max_size_in_bytes', PHP_INT_MAX);
45
+
46
+ /*
47
+ * If "Include Other Files in WP Content" is false, only the files inside
48
+ * these folders will be added to the export.
49
+ *
50
+ * @todo: Do we want to add the commitment of a filter for this?
51
+ */
52
+ $defaultWpContentFolders = [
53
+ WP_PLUGIN_DIR,
54
+ $this->directory->getUploadsDirectory(),
55
+ get_theme_root(),
56
+ WPMU_PLUGIN_DIR,
57
+ ];
58
+
59
+ $files = [];
60
+
61
+ /** @var \SplFileInfo $item */
62
+ foreach ($it as $item) {
63
+ $shouldScan = $item->isFile() &&
64
+ !$item->isLink() &&
65
+ $item->getFilename() != "." &&
66
+ $item->getFilename() != "..";
67
+
68
+ if ($shouldScan) {
69
+ if (in_array($item->getExtension(), $excludedFileExtensions)) {
70
+ // Early bail: File has an ignored extension
71
+ continue;
72
+ }
73
+
74
+ if ($item->getSize() > $ignoreFilesBiggerThan) {
75
+ // Early bail: File is larger than max allowed size.
76
+ continue;
77
+ }
78
+
79
+ $path = $this->filesystem->safePath($item->getPathname());
80
+
81
+ if (!$includeOtherFilesInWpContent) {
82
+ foreach ($defaultWpContentFolders as $defaultFolder) {
83
+ /*
84
+ * Only include files that are inside allowed folders.
85
+ */
86
+ if (strpos($path, trailingslashit($defaultFolder)) === 0) {
87
+ $files[] = $path;
88
+ }
89
+ }
90
+ } else {
91
+ $files[] = $path;
92
+ }
93
+ }
94
  }
95
 
96
+ return $files;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  }
98
  }
Framework/Filesystem/FileScannerControl.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // TODO PHP7.x; declare(strict_type=1);
4
+ // TODO PHP7.x; type hints & return types
5
+ // TODO PHP7.1; constant visibility
6
+
7
+ namespace WPStaging\Framework\Filesystem;
8
+
9
+ use RuntimeException;
10
+ use WPStaging\Framework\Queue\FinishedQueueException;
11
+ use WPStaging\Framework\Queue\Queue;
12
+ use WPStaging\Framework\Queue\Storage\BufferedCacheStorage;
13
+ use WPStaging\Framework\Utils\Cache\BufferedCache;
14
+
15
+ class FileScannerControl
16
+ {
17
+ const DATA_CACHE_FILE = 'filesystem_scanner_file_data';
18
+ const QUEUE_CACHE_FILE = 'file_scanner';
19
+
20
+ /** @var BufferedCache */
21
+ private $cache;
22
+
23
+ /** @var BufferedCacheStorage */
24
+ private $storage;
25
+
26
+ /** @var FileScanner */
27
+ private $scanner;
28
+
29
+ /** @var Queue|null */
30
+ private $queue;
31
+
32
+ /** @var array */
33
+ private $newQueueItems;
34
+
35
+ public function __construct(BufferedCache $cache, BufferedCacheStorage $storage, FileScanner $scanner)
36
+ {
37
+ $this->newQueueItems = [];
38
+ $this->cache = clone $cache;
39
+ $this->storage = clone $storage;
40
+ $this->scanner = $scanner;
41
+ }
42
+
43
+ public function __destruct()
44
+ {
45
+ if ($this->newQueueItems && $this->queue) {
46
+ $this->queue->pushAsArray($this->newQueueItems);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @param string $name
52
+ */
53
+ public function setQueueByName($name = self::QUEUE_CACHE_FILE)
54
+ {
55
+ $this->queue = new Queue;
56
+ $this->queue->setName($name);
57
+ $this->queue->setStorage($this->storage);
58
+ }
59
+
60
+ /**
61
+ * @param bool $includeOtherFilesInWpContent
62
+ *
63
+ * @return array
64
+ */
65
+ public function scanCurrentPath($includeOtherFilesInWpContent)
66
+ {
67
+ $path = $this->getPathFromQueue();
68
+ if ($path === null) {
69
+ throw new FinishedQueueException('File Scanner Queue is Finished');
70
+ }
71
+
72
+ $path = ABSPATH . $path;
73
+
74
+ return $this->scanner->scan($path, $includeOtherFilesInWpContent);
75
+ }
76
+
77
+ /**
78
+ * @return string|null
79
+ */
80
+ public function getPathFromQueue()
81
+ {
82
+ if ($this->queue->count() > 0) {
83
+ return $this->queue->pop();
84
+ }
85
+
86
+ if ($this->newQueueItems) {
87
+ return array_shift($this->newQueueItems);
88
+ }
89
+
90
+ return null;
91
+ }
92
+
93
+ /**
94
+ * @param string $item
95
+ */
96
+ public function addToNewQueue($item)
97
+ {
98
+ $this->newQueueItems[] = $item;
99
+ }
100
+
101
+ /**
102
+ * @return BufferedCache
103
+ */
104
+ public function getCache()
105
+ {
106
+ return $this->cache;
107
+ }
108
+
109
+ /**
110
+ * @return Queue
111
+ */
112
+ public function getQueue()
113
+ {
114
+ if (!$this->queue) {
115
+ // TODO Custom Exception
116
+ throw new RuntimeException('FileScannerControl Queue is not set');
117
+ }
118
+
119
+ return $this->queue;
120
+ }
121
+
122
+ public function setNewQueueItems(array $items = null)
123
+ {
124
+ $this->newQueueItems = $items;
125
+ }
126
+ }
Framework/Filesystem/FileService.php DELETED
@@ -1,115 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_types=1);
4
- // TODO PHP7.x; return types && type-hints
5
-
6
- namespace WPStaging\Framework\Filesystem;
7
-
8
- use WPStaging\Vendor\Symfony\Component\Finder\Finder;
9
-
10
- class FileService
11
- {
12
- /**
13
- * @param string $directory
14
- * @param string $depth
15
- * @param array|null $excludedFileNames
16
- *
17
- * @return Finder|null
18
- */
19
- public function scan($directory, $depth = null, array $excludedFileNames = null)
20
- {
21
- $finder = (new Finder)
22
- ->files()
23
- ->in($directory)
24
- ;
25
-
26
- if ($excludedFileNames) {
27
- /** @noinspection PhpParamsInspection */
28
- $finder->notName($excludedFileNames);
29
- }
30
-
31
- if ($depth !== null) {
32
- $finder->depth($depth);
33
- }
34
-
35
- $finderHasResults = count($finder) > 0;
36
-
37
- if (!$finderHasResults) {
38
- return null;
39
- }
40
-
41
- return $finder;
42
- }
43
-
44
- /**
45
- * @param string $file full path + filename
46
- * @param array $excludedFiles List of filenames. Can be wildcard pattern like data.php, data*.php, *.php, .php
47
- * @return boolean
48
- */
49
- public function isFilenameExcluded($file, $excludedFiles)
50
- {
51
- $filename = basename($file);
52
-
53
- // Regular filenames
54
- if (in_array($filename, $excludedFiles, true)) {
55
- return true;
56
- }
57
-
58
- // Wildcards
59
- foreach ($excludedFiles as $pattern) {
60
- if ($this->fnmatch($pattern, $filename)) {
61
- return true;
62
- }
63
- }
64
- return false;
65
- }
66
-
67
- /**
68
- * Checks if the passed string would match the given shell wildcard pattern.
69
- * This function emulates [[fnmatch()]], which may be unavailable at certain environment, using PCRE.
70
- * @param string $pattern the shell wildcard pattern.
71
- * @param string $string the tested string.
72
- * @param array $options options for matching. Valid options are:
73
- *
74
- * - caseSensitive: bool, whether pattern should be case sensitive. Defaults to `true`.
75
- * - escape: bool, whether backslash escaping is enabled. Defaults to `true`.
76
- * - filePath: bool, whether slashes in string only matches slashes in the given pattern. Defaults to `false`.
77
- *
78
- * @return bool whether the string matches pattern or not.
79
- */
80
- protected function fnmatch($pattern, $string, $options = [])
81
- {
82
- if ($pattern === '*' && empty($options['filePath'])) {
83
- return true;
84
- }
85
-
86
- $replacements = [
87
- '\\\\\\\\' => '\\\\',
88
- '\\\\\\*' => '[*]',
89
- '\\\\\\?' => '[?]',
90
- '\*' => '.*',
91
- '\?' => '.',
92
- '\[\!' => '[^',
93
- '\[' => '[',
94
- '\]' => ']',
95
- '\-' => '-',
96
- ];
97
-
98
- if (isset($options['escape']) && !$options['escape']) {
99
- unset($replacements['\\\\\\\\'], $replacements['\\\\\\*'], $replacements['\\\\\\?']);
100
- }
101
-
102
- if (!empty($options['filePath'])) {
103
- $replacements['\*'] = '[^/\\\\]*';
104
- $replacements['\?'] = '[^/\\\\]';
105
- }
106
-
107
- $pattern = strtr(preg_quote($pattern, '#'), $replacements);
108
- $pattern = '#^' . $pattern . '$#us';
109
- if (isset($options['caseSensitive']) && !$options['caseSensitive']) {
110
- $pattern .= 'i';
111
- }
112
-
113
- return preg_match($pattern, $string) === 1;
114
- }
115
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Filesystem/Filesystem.php CHANGED
@@ -2,9 +2,13 @@
2
 
3
  namespace WPStaging\Framework\Filesystem;
4
 
 
 
 
 
 
5
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
6
  use RuntimeException;
7
- use WPStaging\Vendor\Symfony\Component\Filesystem\Exception\IOException;
8
  use WPStaging\Vendor\Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
9
  use WPStaging\Vendor\Symfony\Component\Finder\Finder;
10
 
@@ -45,11 +49,11 @@ class Filesystem extends FilterableDirectoryIterator
45
  }
46
 
47
  foreach ($this->getNotPath() as $exclude) {
48
- $finder->notPath($exclude);
49
  }
50
 
51
  foreach ($this->getFileNames() as $fileName) {
52
- $finder->name($fileName);
53
  }
54
 
55
  $finder_has_results = count($finder) > 0;
@@ -139,6 +143,76 @@ class Filesystem extends FilterableDirectoryIterator
139
  return $this->delete(null, false);
140
  }
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  /**
143
  * @param string $source
144
  * @param string $target
@@ -162,20 +236,49 @@ class Filesystem extends FilterableDirectoryIterator
162
  }
163
 
164
  /**
165
- * @param string|null $path
166
  *
167
- * @return string
168
  */
169
- public function mkdir($path)
170
  {
171
  $path = $this->findPath($path);
 
172
  if (!wp_mkdir_p($path)) {
173
- throw new IOException('Failed to create directory ' . $path);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  }
175
 
176
  return trailingslashit($path);
177
  }
178
 
 
 
 
 
 
 
 
 
 
179
  public function copy($source, $destination)
180
  {
181
  $fs = new SymfonyFilesystem;
@@ -184,6 +287,70 @@ class Filesystem extends FilterableDirectoryIterator
184
  return $fs->exists($destination);
185
  }
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  /**
188
  * @param string
189
  * @return bool
@@ -247,12 +414,12 @@ class Filesystem extends FilterableDirectoryIterator
247
 
248
  if ($isUseNotPath) {
249
  foreach ($this->getNotPath() as $exclude) {
250
- $iterator->notPath($exclude);
251
  }
252
  }
253
 
254
  foreach ($this->getFileNames() as $fileName) {
255
- $iterator->name($fileName);
256
  }
257
 
258
  $iteratorHasResults = count($iterator) > 0;
@@ -292,6 +459,27 @@ class Filesystem extends FilterableDirectoryIterator
292
  $this->log('Permission Error: Can not delete file ' . $path);
293
  return false;
294
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  return true;
296
  }
297
 
@@ -325,7 +513,7 @@ class Filesystem extends FilterableDirectoryIterator
325
  if (!$this->isEmptyDir($path)) {
326
  return false;
327
  }
328
-
329
  // Delete the empty directory itself and finish execution
330
  if (is_dir($path)){
331
  if (!rmdir($path)){
@@ -336,6 +524,105 @@ class Filesystem extends FilterableDirectoryIterator
336
  return true;
337
  }
338
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  /**
340
  * @param $path
341
  * @return string|null
@@ -473,17 +760,23 @@ class Filesystem extends FilterableDirectoryIterator
473
  protected function deleteItem($item)
474
  {
475
  $path = $item->getPathname();
476
- // Checks whether that file or directory exists
477
- if (!file_exists($path)) {
478
- return true;
479
- }
480
 
481
  if ($item->isLink()) {
482
  if(!unlink($path)) {
483
  $this->log('Permission Error: Can not delete link ' . $path);
484
  throw new RuntimeException('Permission Error: Can not delete link ' . $path);
485
  }
486
- } elseif ($item->isDir()) {
 
 
 
 
 
 
 
487
  if (!$this->isEmptyDir($path)) {
488
  return false;
489
  }
@@ -505,11 +798,56 @@ class Filesystem extends FilterableDirectoryIterator
505
  /**
506
  * @param $string
507
  */
508
- protected function log($string){
 
509
  if ($this->logger instanceof LoggerInterface) {
510
  $this->logger->warning($string);
511
  }
512
  }
513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514
 
515
  }
2
 
3
  namespace WPStaging\Framework\Filesystem;
4
 
5
+ use WPStaging\Backend\Notices\Notices;
6
+ use WPStaging\Core\Utils\Htaccess;
7
+ use WPStaging\Core\Utils\IISWebConfig;
8
+ use WPStaging\Core\WPStaging;
9
+ use WPStaging\Framework\DI\Container;
10
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
11
  use RuntimeException;
 
12
  use WPStaging\Vendor\Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
13
  use WPStaging\Vendor\Symfony\Component\Finder\Finder;
14
 
49
  }
50
 
51
  foreach ($this->getNotPath() as $exclude) {
52
+ $finder->notPath(untrailingslashit($exclude));
53
  }
54
 
55
  foreach ($this->getFileNames() as $fileName) {
56
+ $finder->name(untrailingslashit($fileName));
57
  }
58
 
59
  $finder_has_results = count($finder) > 0;
143
  return $this->delete(null, false);
144
  }
145
 
146
+ /**
147
+ * Move content from one path to another
148
+ * This is better than $this->rename() method as this use custom fileiterator and $this->deleteNew()
149
+ * @param string $source
150
+ * @param string $target
151
+ *
152
+ * @return boolean Whether the move was successful or not.
153
+ */
154
+ public function move($source, $target)
155
+ {
156
+ // if $source is link or file, move it and stop execution
157
+ if(is_link($source) || is_file($source)) {
158
+ return $this->renameDirect($source, $target);
159
+ }
160
+
161
+ // if $source is empty dir
162
+ if ($this->isEmptyDir($source)) {
163
+ return wp_mkdir_p($target) && @rmdir($source);
164
+ }
165
+
166
+ $this->setDirectory($source);
167
+ $iterator = null;
168
+ try {
169
+ $iterator = $this->setIteratorMode(\RecursiveIteratorIterator::CHILD_FIRST)->get();
170
+ } catch (FilesystemExceptions $e) {
171
+ $this->log('Permission Error: Can not create recursive iterator for ' . $source);
172
+ return false;
173
+ }
174
+
175
+ $basePath = trailingslashit($target);
176
+ foreach ($iterator as $item) {
177
+ if ($item->isDir() && !$this->isEmptyDir($item->getPathname())) {
178
+ continue;
179
+ }
180
+
181
+ $relativeFilePath = $iterator->getFilename();
182
+ if ($this->isIteratorRecursive()) {
183
+ $relativeFilePath = $iterator->getSubPathName();
184
+ }
185
+
186
+ $destination = $basePath . $relativeFilePath;
187
+ if (file_exists($destination)) {
188
+ continue;
189
+ }
190
+
191
+ $result = false;
192
+ // if empty dir
193
+ if ($item->isDir()) {
194
+ $result = wp_mkdir_p($destination) && @rmdir($item->getPathname());
195
+ } else { // if file or link
196
+ $result = $this->renameDirect($item->getPathname(), $destination);
197
+ }
198
+
199
+ if (!$result || !is_callable($this->shouldStop)) {
200
+ continue;
201
+ }
202
+
203
+ if (call_user_func($this->shouldStop)) {
204
+ return false;
205
+ }
206
+ }
207
+
208
+ $deleteSelf = true;
209
+ if (count($this->getExcludePaths()) > 0 || !$this->isIteratorRecursive()) {
210
+ $deleteSelf = false;
211
+ }
212
+
213
+ return $this->deleteNew($source, $deleteSelf);
214
+ }
215
+
216
  /**
217
  * @param string $source
218
  * @param string $target
236
  }
237
 
238
  /**
239
+ * @param string|null $path The path to the new folder, or null to use FileSystem's path.
240
  *
241
+ * @return string Path to newly created folder, or empty string if couldn't create it.
242
  */
243
+ public function mkdir($path, $preventDirectoryListing = false)
244
  {
245
  $path = $this->findPath($path);
246
+
247
  if (!wp_mkdir_p($path)) {
248
+ if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
249
+ error_log("Failed to create directory $path");
250
+ }
251
+
252
+ return '';
253
+ }
254
+
255
+ if ($preventDirectoryListing) {
256
+ /** @var DirectoryListing $directoryListing */
257
+ $directoryListing = WPStaging::getInstance()->getContainer()->make(DirectoryListing::class);
258
+ try {
259
+ $directoryListing->preventDirectoryListing($path);
260
+ } catch(\Exception $e) {
261
+ /**
262
+ * Enqueue this error. All enqueued errors will be shown as a single notice.
263
+ *
264
+ * @see \WPStaging\Backend\Notices\Notices::showDirectoryListingWarningNotice
265
+ */
266
+ WPStaging::getInstance()->getContainer()->pushToArray(Notices::$directoryListingErrors, $e->getMessage());
267
+ }
268
  }
269
 
270
  return trailingslashit($path);
271
  }
272
 
273
+ /**
274
+ * Copy file
275
+ *
276
+ * @param string $source
277
+ * @param string $destination
278
+ * @return boolean
279
+ *
280
+ * @todo update this to allow copying big files
281
+ */
282
  public function copy($source, $destination)
283
  {
284
  $fs = new SymfonyFilesystem;
287
  return $fs->exists($destination);
288
  }
289
 
290
+ /**
291
+ * The new copy method which works for files, links and directories
292
+ *
293
+ * @param string $source
294
+ * @param string $target
295
+ * @return boolean
296
+ */
297
+ public function copyNew($source, $target)
298
+ {
299
+ // if $source is link or file, move it and stop execution
300
+ if(is_link($source) || is_file($source)) {
301
+ return $this->copy($source, $target);
302
+ }
303
+
304
+ // if $source is empty dir
305
+ if ($this->isEmptyDir($source)) {
306
+ return wp_mkdir_p($target);
307
+ }
308
+
309
+ $this->setDirectory($source);
310
+ $iterator = null;
311
+ try {
312
+ $iterator = $this->setIteratorMode(\RecursiveIteratorIterator::CHILD_FIRST)->get();
313
+ } catch (FilesystemExceptions $e) {
314
+ $this->log('Permission Error: Can not create recursive iterator for ' . $source);
315
+ return false;
316
+ }
317
+
318
+ $basePath = trailingslashit($target);
319
+ foreach ($iterator as $item) {
320
+ if ($item->isDir() && !$this->isEmptyDir($item->getPathname())) {
321
+ continue;
322
+ }
323
+
324
+ $relativeFilePath = $iterator->getFilename();
325
+ if ($this->isIteratorRecursive()) {
326
+ $relativeFilePath = $iterator->getSubPathName();
327
+ }
328
+
329
+ $destination = $basePath . $relativeFilePath;
330
+ if (file_exists($destination)) {
331
+ continue;
332
+ }
333
+
334
+ $result = false;
335
+ // if empty dir
336
+ if ($item->isDir()) {
337
+ $result = wp_mkdir_p($destination);
338
+ } else { // if file or link
339
+ $result = $this->copy($item->getPathname(), $destination);
340
+ }
341
+
342
+ if (!$result || !is_callable($this->shouldStop)) {
343
+ continue;
344
+ }
345
+
346
+ if (call_user_func($this->shouldStop)) {
347
+ return false;
348
+ }
349
+ }
350
+
351
+ return true;
352
+ }
353
+
354
  /**
355
  * @param string
356
  * @return bool
414
 
415
  if ($isUseNotPath) {
416
  foreach ($this->getNotPath() as $exclude) {
417
+ $iterator->notPath(untrailingslashit($exclude));
418
  }
419
  }
420
 
421
  foreach ($this->getFileNames() as $fileName) {
422
+ $iterator->name(untrailingslashit($fileName));
423
  }
424
 
425
  $iteratorHasResults = count($iterator) > 0;
459
  $this->log('Permission Error: Can not delete file ' . $path);
460
  return false;
461
  }
462
+
463
+ return true;
464
+ }
465
+
466
+ // Assume it is already deleted
467
+ if (!is_dir($path)){
468
+ return true;
469
+ }
470
+
471
+ // delete the directory if it is empty and deleteSelf was true
472
+ if (is_dir($path) && $this->isEmptyDir($path) && $deleteSelf) {
473
+ if (!@rmdir($path)){
474
+ $this->log('Permission Error: Can not delete directory ' . $path);
475
+ return false;
476
+ }
477
+
478
+ return true;
479
+ }
480
+
481
+ // return since directory was empty and deleteSelf was false
482
+ if (is_dir($path) && $this->isEmptyDir($path) && !$deleteSelf) {
483
  return true;
484
  }
485
 
513
  if (!$this->isEmptyDir($path)) {
514
  return false;
515
  }
516
+
517
  // Delete the empty directory itself and finish execution
518
  if (is_dir($path)){
519
  if (!rmdir($path)){
524
  return true;
525
  }
526
 
527
+ /**
528
+ * @param string $file full path + filename
529
+ * @param array $excludedFiles List of filenames. Can be wildcard pattern like data.php, data*.php, *.php, .php
530
+ * @return boolean
531
+ */
532
+ public function isFilenameExcluded($file, $excludedFiles)
533
+ {
534
+ $filename = basename($file);
535
+
536
+ // Regular filenames
537
+ if (in_array($filename, $excludedFiles, true)) {
538
+ return true;
539
+ }
540
+
541
+ // Wildcards
542
+ foreach ($excludedFiles as $pattern) {
543
+ if ($this->fnmatch($pattern, $filename)) {
544
+ return true;
545
+ }
546
+ }
547
+ return false;
548
+ }
549
+
550
+ /**
551
+ * Checks if the passed string would match the given shell wildcard pattern.
552
+ * This function emulates [[fnmatch()]], which may be unavailable at certain environment, using PCRE.
553
+ * @param string $pattern the shell wildcard pattern.
554
+ * @param string $string the tested string.
555
+ * @param array $options options for matching. Valid options are:
556
+ *
557
+ * - caseSensitive: bool, whether pattern should be case sensitive. Defaults to `true`.
558
+ * - escape: bool, whether backslash escaping is enabled. Defaults to `true`.
559
+ * - filePath: bool, whether slashes in string only matches slashes in the given pattern. Defaults to `false`.
560
+ *
561
+ * @return bool whether the string matches pattern or not.
562
+ */
563
+ protected function fnmatch($pattern, $string, $options = [])
564
+ {
565
+ if ($pattern === '*' && empty($options['filePath'])) {
566
+ return true;
567
+ }
568
+
569
+ $replacements = [
570
+ '\\\\\\\\' => '\\\\',
571
+ '\\\\\\*' => '[*]',
572
+ '\\\\\\?' => '[?]',
573
+ '\*' => '.*',
574
+ '\?' => '.',
575
+ '\[\!' => '[^',
576
+ '\[' => '[',
577
+ '\]' => ']',
578
+ '\-' => '-',
579
+ ];
580
+
581
+ if (isset($options['escape']) && !$options['escape']) {
582
+ unset($replacements['\\\\\\\\'], $replacements['\\\\\\*'], $replacements['\\\\\\?']);
583
+ }
584
+
585
+ if (!empty($options['filePath'])) {
586
+ $replacements['\*'] = '[^/\\\\]*';
587
+ $replacements['\?'] = '[^/\\\\]';
588
+ }
589
+
590
+ $pattern = strtr(preg_quote($pattern, '#'), $replacements);
591
+ $pattern = '#^' . $pattern . '$#us';
592
+ if (isset($options['caseSensitive']) && !$options['caseSensitive']) {
593
+ $pattern .= 'i';
594
+ }
595
+
596
+ return preg_match($pattern, $string) === 1;
597
+ }
598
+
599
+ /**
600
+ * @param array $paths
601
+ * @return boolean
602
+ */
603
+ public function deletePaths($paths)
604
+ {
605
+ foreach ($paths as $path) {
606
+ // only delete the dir if empty
607
+ // helpful when we exclude path(s) during delete
608
+ if (is_dir($path) && $this->isEmptyDir($path)) {
609
+ if (!@rmdir($path)){
610
+ $this->log('Permission Error: Can not delete directory ' . $path);
611
+ throw new RuntimeException('Permission Error: Can not delete directory ' . $path);
612
+ }
613
+
614
+ continue;
615
+ }
616
+
617
+ // force to not delete the parent path itself
618
+ if (!$this->deleteNew($path, false)) {
619
+ return false;
620
+ }
621
+ }
622
+
623
+ return true;
624
+ }
625
+
626
  /**
627
  * @param $path
628
  * @return string|null
760
  protected function deleteItem($item)
761
  {
762
  $path = $item->getPathname();
763
+
764
+ $perms = substr(sprintf('%o', fileperms($path)), -4);
765
+ $this->log('Permission Error: Can not delete link ' . $perms);
 
766
 
767
  if ($item->isLink()) {
768
  if(!unlink($path)) {
769
  $this->log('Permission Error: Can not delete link ' . $path);
770
  throw new RuntimeException('Permission Error: Can not delete link ' . $path);
771
  }
772
+ }
773
+
774
+ // Checks whether that file or directory exists
775
+ if (!file_exists($path)) {
776
+ return true;
777
+ }
778
+
779
+ if ($item->isDir()) {
780
  if (!$this->isEmptyDir($path)) {
781
  return false;
782
  }
798
  /**
799
  * @param $string
800
  */
801
+ protected function log($string)
802
+ {
803
  if ($this->logger instanceof LoggerInterface) {
804
  $this->logger->warning($string);
805
  }
806
  }
807
 
808
+ /**
809
+ * Create a file with content
810
+ *
811
+ * @param string $path Path to the file
812
+ * @param string $content Content of the file
813
+ * @return boolean
814
+ */
815
+ public function create( $path, $content ) {
816
+ if( !@file_exists( $path ) ) {
817
+ if( !@is_writable( dirname( $path ) ) ) {
818
+ return false;
819
+ }
820
+
821
+ if( !@touch( $path ) ) {
822
+ return false;
823
+ }
824
+ } elseif( !@is_writable( $path ) ) {
825
+ return false;
826
+ }
827
+
828
+ $written = false;
829
+ if( ( $handle = @fopen( $path, 'w' ) ) !== false ) {
830
+ if( @fwrite( $handle, $content ) !== false ) {
831
+ $written = true;
832
+ }
833
+
834
+ @fclose( $handle );
835
+ }
836
+
837
+ return $written;
838
+ }
839
+
840
+ /**
841
+ * Create a file with marker and content
842
+ *
843
+ * @param string $path Path to the file
844
+ * @param string $marker Name of the marker
845
+ * @param string $content Content of the file
846
+ * @return boolean
847
+ */
848
+ public function createWithMarkers( $path, $marker, $content ) {
849
+ return @insert_with_markers( $path, $marker, $content );
850
+ }
851
+
852
 
853
  }
Framework/Filesystem/Permissions.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace WPStaging\Framework\Filesystem;
5
+
6
+
7
+ class Permissions
8
+ {
9
+ /**
10
+ * @return int
11
+ */
12
+ public function getDirectoryOctal()
13
+ {
14
+ $octal = 0755;
15
+ if (defined('FS_CHMOD_DIR')) {
16
+ $octal = FS_CHMOD_DIR;
17
+ }
18
+
19
+ return apply_filters('wpstg_folder_permission', $octal);
20
+ }
21
+
22
+ /**
23
+ * @return int
24
+ */
25
+ public function getFilesOctal()
26
+ {
27
+ if (defined('FS_CHMOD_FILE')) {
28
+ return FS_CHMOD_FILE;
29
+ }
30
+
31
+ return 0644;
32
+ }
33
+
34
+
35
+ }
Framework/Filesystem/WpUploadsFolderSymlinker.php CHANGED
@@ -60,9 +60,9 @@ class WpUploadsFolderSymlinker
60
 
61
  $uploadPath = $this->wpDirectories->getUploadPath();
62
 
63
- $this->createDirectory($this->stagingUploadPath);
64
 
65
- if (false === symlink($uploadPath, $this->stagingUploadPath)) {
66
  $this->error = "Can not symlink " . $uploadPath . "to " . $this->stagingUploadPath;
67
  return false;
68
  }
@@ -78,21 +78,4 @@ class WpUploadsFolderSymlinker
78
  {
79
  return $this->error;
80
  }
81
-
82
- /**
83
- * Create staging sites upload directory ready to be connected via symlink to the production site
84
- * Directory must exist before i
85
- */
86
- protected function createDirectory($path)
87
- {
88
- $dirname = dirname($path);
89
- $directories = explode(DIRECTORY_SEPARATOR, $dirname);
90
- $currentDirectory = '';
91
- foreach ($directories as $directory) {
92
- $currentDirectory .= $directory . DIRECTORY_SEPARATOR;
93
- if (!file_exists($currentDirectory)) {
94
- mkdir($currentDirectory, 0755);
95
- }
96
- }
97
- }
98
- }
60
 
61
  $uploadPath = $this->wpDirectories->getUploadPath();
62
 
63
+ (new Filesystem)->mkdir(dirname($this->stagingUploadPath));
64
 
65
+ if (!symlink($uploadPath, $this->stagingUploadPath)) {
66
  $this->error = "Can not symlink " . $uploadPath . "to " . $this->stagingUploadPath;
67
  return false;
68
  }
78
  {
79
  return $this->error;
80
  }
81
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Mails/Report/Report.php CHANGED
@@ -20,6 +20,14 @@ class Report
20
  */
21
  const EMAIL_SUBJECT = "Report Issue!";
22
 
 
 
 
 
 
 
 
 
23
  /**
24
  * Send customer issue report
25
  *
@@ -55,7 +63,6 @@ class Report
55
  }
56
 
57
  $attachments = [];
58
- $maxFileSize = 512 * 1024;
59
  if ($provider) {
60
  $message .= "\n\n'Hosting provider: " . $provider;
61
  }
@@ -63,13 +70,13 @@ class Report
63
  if (!empty($syslog)) {
64
  $message .= "\n\n'" . $this->getSyslog();
65
  $debugLogFile = WP_CONTENT_DIR . '/debug.log';
66
- if (filesize($debugLogFile) && $maxFileSize > filesize($debugLogFile)) {
67
  $attachments[] = $debugLogFile;
68
  }
69
  }
70
 
71
  $transient = new ReportSubmitTransient();
72
- if ($transient->getTransient() && !$forceSend) {
73
  // to show alert using js
74
  $errors[] = [
75
  "status" => 'already_submitted',
20
  */
21
  const EMAIL_SUBJECT = "Report Issue!";
22
 
23
+ /**
24
+ * Maximum attachment file size of 5MB
25
+ *
26
+ * @var int
27
+ *
28
+ */
29
+ const ATTACHMENT_MAX_FILE_SIZE = 5242880;
30
+
31
  /**
32
  * Send customer issue report
33
  *
63
  }
64
 
65
  $attachments = [];
 
66
  if ($provider) {
67
  $message .= "\n\n'Hosting provider: " . $provider;
68
  }
70
  if (!empty($syslog)) {
71
  $message .= "\n\n'" . $this->getSyslog();
72
  $debugLogFile = WP_CONTENT_DIR . '/debug.log';
73
+ if (file_exists($debugLogFile) && self::ATTACHMENT_MAX_FILE_SIZE > filesize($debugLogFile)) {
74
  $attachments[] = $debugLogFile;
75
  }
76
  }
77
 
78
  $transient = new ReportSubmitTransient();
79
+ if (!$forceSend && $transient->getTransient() ) {
80
  // to show alert using js
81
  $errors[] = [
82
  "status" => 'already_submitted',
Framework/Security/Auth.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Security;
4
+
5
+ /**
6
+ * Class Auth
7
+ *
8
+ * This class provide helping methods for authentication different kinds of request
9
+ * like ajax, non-ajax etc
10
+ *
11
+ * @package WPStaging\Framework\Security
12
+ */
13
+ class Auth
14
+ {
15
+ /**
16
+ * @var Capabilities
17
+ */
18
+ protected $capabilities;
19
+
20
+ /**
21
+ * @var AccessToken
22
+ */
23
+ protected $accessToken;
24
+
25
+ /**
26
+ * @var Nonce
27
+ */
28
+ protected $nonce;
29
+
30
+ /**
31
+ * @param Capabilities $capabilities
32
+ * @param AccessToken $accessToken
33
+ * @param Nonce $nonce
34
+ */
35
+ public function __construct(Capabilities $capabilities, AccessToken $accessToken, Nonce $nonce)
36
+ {
37
+ $this->capabilities = $capabilities;
38
+ $this->accessToken = $accessToken;
39
+ $this->nonce = $nonce;
40
+ }
41
+
42
+ /**
43
+ * Validate (ajax) request with wpstg nonce or access token.
44
+ * Criteria to be a valid request should satisfy any point below:
45
+ *
46
+ * A. User must be logged in and must have capability to manage WP Staging.
47
+ * WP Staging Nonce must be valid
48
+ * or
49
+ * B. WP Staging Access Token must be valid
50
+ *
51
+ *
52
+ * @param string $nonce
53
+ * @return bool
54
+ */
55
+ public function isValid($nonce = Nonce::WPSTG_NONCE)
56
+ {
57
+ if (
58
+ $this->nonce->requestHasValidNonce($nonce) &&
59
+ current_user_can($this->capabilities->manageWPSTG())
60
+
61
+ ) {
62
+ return true;
63
+ }
64
+
65
+ return $this->accessToken->requestHasValidToken();
66
+ }
67
+ }
Framework/Staging/FirstRun.php CHANGED
@@ -20,14 +20,19 @@ class FirstRun
20
  */
21
  const FIRST_RUN_KEY = 'wpstg_execute';
22
 
 
 
 
 
 
23
 
24
  public function init()
25
  {
26
- if ( ! (new SiteInfo)->isStaging()) {
27
  return;
28
  }
29
 
30
- if ( ! get_option(self::FIRST_RUN_KEY)) {
31
  return;
32
  }
33
 
@@ -48,6 +53,11 @@ class FirstRun
48
  // Enable the disabled cache notice to be shown on the staging site admin.
49
  (new DisabledCacheNotice())->enable();
50
 
 
 
 
 
 
51
  // Allow users to attach custom actions by using this hook
52
  do_action('wpstg.clone_first_run');
53
 
20
  */
21
  const FIRST_RUN_KEY = 'wpstg_execute';
22
 
23
+ /**
24
+ * The option_name that is stored in the database to check whether mails are disabled or not
25
+ */
26
+ const MAILS_DISABLED_KEY = 'wpstg_emails_disabled';
27
+
28
 
29
  public function init()
30
  {
31
+ if (!(new SiteInfo)->isStaging()) {
32
  return;
33
  }
34
 
35
+ if (!get_option(self::FIRST_RUN_KEY)) {
36
  return;
37
  }
38
 
53
  // Enable the disabled cache notice to be shown on the staging site admin.
54
  (new DisabledCacheNotice())->enable();
55
 
56
+ // Enable the disabled mail notice to be shown on the staging site admin.
57
+ if (defined('WPSTGPRO_VERSION')) {
58
+ (new \WPStaging\Backend\Pro\Notices\DisabledMailNotice())->enable();
59
+ }
60
+
61
  // Allow users to attach custom actions by using this hook
62
  do_action('wpstg.clone_first_run');
63
 
Framework/Traits/BenchmarkTrait.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Traits;
4
+
5
+ use WPStaging\Core\Utils\Logger;
6
+ use WPStaging\Core\WPStaging;
7
+ use WPStaging\Vendor\Psr\Log\LoggerInterface;
8
+
9
+ trait BenchmarkTrait
10
+ {
11
+ private $benchmarkLogger;
12
+ private $benchmarkStart = 0;
13
+
14
+ private function startBenchmark()
15
+ {
16
+ if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
17
+ /** @var Logger $logger */
18
+ $this->benchmarkLogger = clone WPStaging::getInstance()->get(Logger::class);
19
+
20
+ // Eg: JobSiteExport_Benchmark
21
+ $filename = sanitize_file_name(sprintf('%s_Benchmark',
22
+ (new \ReflectionClass($this))->getShortName()
23
+ ));
24
+
25
+ $this->benchmarkLogger->setFileName($filename);
26
+ $this->benchmarkStart = microtime(true);
27
+ }
28
+ }
29
+
30
+ private function finishBenchmark($context)
31
+ {
32
+ if (defined('WPSTG_DEBUG') && WPSTG_DEBUG && $this->benchmarkLogger instanceof LoggerInterface) {
33
+ $message = sprintf(
34
+ 'Finished %s in %s seconds',
35
+ $context,
36
+ microtime(true) - $this->benchmarkStart
37
+ );
38
+
39
+ $this->benchmarkLogger->info($message);
40
+ }
41
+ }
42
+ }
Framework/Traits/HydrateTrait.php CHANGED
@@ -25,7 +25,13 @@ trait HydrateTrait
25
  {
26
  foreach ($data as $key => $value) {
27
  /** @noinspection PhpUnhandledExceptionInspection */
28
- $this->hydrateByMethod('set' . ucfirst($key), $value);
 
 
 
 
 
 
29
  }
30
 
31
  return $this;
@@ -40,7 +46,10 @@ trait HydrateTrait
40
  private function hydrateByMethod($method, $value)
41
  {
42
  if (!method_exists($this, $method)) {
43
- return;
 
 
 
44
  }
45
 
46
  /** @noinspection CallableParameterUseCaseInTypeContextInspection */
25
  {
26
  foreach ($data as $key => $value) {
27
  /** @noinspection PhpUnhandledExceptionInspection */
28
+ try {
29
+ $this->hydrateByMethod('set' . ucfirst($key), $value);
30
+ } catch(Exception $e) {
31
+ if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
32
+ error_log($e->getMessage());
33
+ }
34
+ }
35
  }
36
 
37
  return $this;
46
  private function hydrateByMethod($method, $value)
47
  {
48
  if (!method_exists($this, $method)) {
49
+ if (!is_string($value)) {
50
+ $value = wp_json_encode($value);
51
+ }
52
+ throw new Exception(sprintf('Trying to hydrate DTO with value that does not exist. \n %s \n %s \n %s', get_class($this), $method, $value));
53
  }
54
 
55
  /** @noinspection CallableParameterUseCaseInTypeContextInspection */
Framework/Traits/ResourceTrait.php CHANGED
@@ -2,23 +2,18 @@
2
 
3
  namespace WPStaging\Framework\Traits;
4
 
5
- use WPStaging\Entity\Settings;
6
  use WPStaging\Framework\Utils\Size;
7
- use WPStaging\Repository\SettingsRepository;
8
 
9
  trait ResourceTrait
10
  {
11
  use TimerTrait;
12
 
13
- /** @var SettingsRepository */
14
- protected $settingsRepository;
15
-
16
- /** @var Settings */
17
- protected $settings;
18
-
19
  /** @var int|null */
20
  protected $timeLimit;
21
 
 
 
 
22
  /**
23
  * @return bool
24
  */
@@ -32,6 +27,15 @@ trait ResourceTrait
32
  */
33
  public function isMemoryLimit()
34
  {
 
 
 
 
 
 
 
 
 
35
  $limit = (new Size)->toBytes(ini_get('memory_limit'));
36
 
37
  if (!is_int($limit) || $limit < 64000000){
@@ -46,7 +50,17 @@ trait ResourceTrait
46
  */
47
  public function isTimeLimit()
48
  {
49
- $timeLimit = $this->getSettings()->findExecutionTimeLimit();
 
 
 
 
 
 
 
 
 
 
50
 
51
  if ($this->timeLimit !== null) {
52
  $timeLimit = $this->timeLimit;
@@ -55,6 +69,19 @@ trait ResourceTrait
55
  }
56
  // TODO Recursion for xDebug? Recursion is bad idea will cause more resource usage, need to avoid it.
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  /**
59
  * @param bool $realUsage
60
  * @return int
@@ -73,36 +100,6 @@ trait ResourceTrait
73
  return memory_get_peak_usage($realUsage);
74
  }
75
 
76
- /**
77
- * @return SettingsRepository
78
- */
79
- public function getSettingsRepository()
80
- {
81
- if (!$this->settingsRepository) {
82
- $this->settingsRepository = new SettingsRepository;
83
- }
84
- return $this->settingsRepository;
85
- }
86
-
87
- /**
88
- * @param SettingsRepository $settingsRepository
89
- */
90
- public function setSettingsRepository(SettingsRepository $settingsRepository)
91
- {
92
- $this->settingsRepository = $settingsRepository;
93
- }
94
-
95
- /**
96
- * @return Settings
97
- */
98
- public function getSettings()
99
- {
100
- if (!$this->settings) {
101
- $this->settings = $this->getSettingsRepository()->find();
102
- }
103
- return $this->settings;
104
- }
105
-
106
  /**
107
  * @return int|null
108
  */
2
 
3
  namespace WPStaging\Framework\Traits;
4
 
 
5
  use WPStaging\Framework\Utils\Size;
 
6
 
7
  trait ResourceTrait
8
  {
9
  use TimerTrait;
10
 
 
 
 
 
 
 
11
  /** @var int|null */
12
  protected $timeLimit;
13
 
14
+ private static $default_max_execution_time_in_seconds = 30;
15
+ private static $execution_time_gap_in_seconds = 5;
16
+
17
  /**
18
  * @return bool
19
  */
27
  */
28
  public function isMemoryLimit()
29
  {
30
+ /**
31
+ * Overriding this filter to return true allows someone to ignore memory limits.
32
+ */
33
+ $ignoreTimeLimit = (bool)apply_filters('wpstg.resources.ignoreMemoryLimit', false);
34
+
35
+ if ($ignoreTimeLimit) {
36
+ return false;
37
+ }
38
+
39
  $limit = (new Size)->toBytes(ini_get('memory_limit'));
40
 
41
  if (!is_int($limit) || $limit < 64000000){
50
  */
51
  public function isTimeLimit()
52
  {
53
+ /**
54
+ * Overriding this filter to return true allows someone to ignore time limits.
55
+ * Useful for developers using xdebug, for instance.
56
+ */
57
+ $ignoreTimeLimit = (bool)apply_filters('wpstg.resources.ignoreTimeLimit', false);
58
+
59
+ if ($ignoreTimeLimit) {
60
+ return false;
61
+ }
62
+
63
+ $timeLimit = $this->findExecutionTimeLimit();
64
 
65
  if ($this->timeLimit !== null) {
66
  $timeLimit = $this->timeLimit;
69
  }
70
  // TODO Recursion for xDebug? Recursion is bad idea will cause more resource usage, need to avoid it.
71
 
72
+ /**
73
+ * @return float|int
74
+ */
75
+ public function findExecutionTimeLimit()
76
+ {
77
+ $executionTime = (int) ini_get('max_execution_time');
78
+ // TODO don't overwrite when CLI / SAPI and / or add setting to not overwrite for devs
79
+ if (!$executionTime || $executionTime > static::$default_max_execution_time_in_seconds) {
80
+ $executionTime = static::$default_max_execution_time_in_seconds;
81
+ }
82
+ return $executionTime - static::$execution_time_gap_in_seconds;
83
+ }
84
+
85
  /**
86
  * @param bool $realUsage
87
  * @return int
100
  return memory_get_peak_usage($realUsage);
101
  }
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  /**
104
  * @return int|null
105
  */
Framework/Traits/TimerTrait.php CHANGED
@@ -2,9 +2,6 @@
2
 
3
  namespace WPStaging\Framework\Traits;
4
 
5
- use WPStaging\Entity\Settings;
6
- use WPStaging\Repository\SettingsRepository;
7
-
8
  trait TimerTrait
9
  {
10
  /** @var float */
@@ -23,4 +20,4 @@ trait TimerTrait
23
  {
24
  return time() - $this->startTime;
25
  }
26
- }
2
 
3
  namespace WPStaging\Framework\Traits;
4
 
 
 
 
5
  trait TimerTrait
6
  {
7
  /** @var float */
20
  {
21
  return time() - $this->startTime;
22
  }
23
+ }
Framework/Utils/Cache/AbstractCache.php CHANGED
@@ -6,7 +6,7 @@
6
 
7
  namespace WPStaging\Framework\Utils\Cache;
8
 
9
-
10
  use WPStaging\Vendor\Symfony\Component\Filesystem\Exception\IOException;
11
  use WPStaging\Framework\Adapter\Directory;
12
 
@@ -90,6 +90,9 @@ abstract class AbstractCache
90
  public function setPath($path)
91
  {
92
  $this->path = $path;
 
 
 
93
  $this->initializeFilePath();
94
  }
95
 
6
 
7
  namespace WPStaging\Framework\Utils\Cache;
8
 
9
+ use WPStaging\Framework\Filesystem\Filesystem;
10
  use WPStaging\Vendor\Symfony\Component\Filesystem\Exception\IOException;
11
  use WPStaging\Framework\Adapter\Directory;
12
 
90
  public function setPath($path)
91
  {
92
  $this->path = $path;
93
+
94
+ (new Filesystem)->mkdir($path);
95
+
96
  $this->initializeFilePath();
97
  }
98
 
Framework/Utils/Cache/Cache.php CHANGED
@@ -7,7 +7,6 @@
7
  namespace WPStaging\Framework\Utils\Cache;
8
 
9
  use WPStaging\Framework\Filesystem\File;
10
- use WPStaging\Framework\Filesystem\Filesystem;
11
 
12
  class Cache extends AbstractCache
13
  {
7
  namespace WPStaging\Framework\Utils\Cache;
8
 
9
  use WPStaging\Framework\Filesystem\File;
 
10
 
11
  class Cache extends AbstractCache
12
  {
Framework/Utils/FileSystem.php DELETED
@@ -1,57 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_types=1);
4
-
5
- namespace WPStaging\Framework\Utils;
6
-
7
- use FilesystemIterator;
8
-
9
- /**
10
- * Class FileSystem
11
- *
12
- * @todo Remove this class in favor of:
13
- * @see \WPStaging\Framework\Filesystem\Filesystem
14
- *
15
- * @package WPStaging\Framework\Utils
16
- */
17
- class FileSystem
18
- {
19
- /*
20
- * Makes sure all paths contain linux directory separator forward slash (/). Windows supports backslash and slash where linux only understands forward slash
21
- * Windows understands both / and \
22
- *
23
- */
24
- public function replaceWindowsDirSeparator($path)
25
- {
26
- if (DIRECTORY_SEPARATOR === '/') {
27
- return $path;
28
- }
29
-
30
- return str_replace('/', DIRECTORY_SEPARATOR, $path);
31
- }
32
-
33
- /*
34
- * Makes sure all paths contain linux directory separator forward slash (/). Windows supports backslash and slash where linux only understands forward slash
35
- * Windows understands both / and \
36
- *
37
- * This does the same as replaceWindowsDirSeparator() but is probably not as performant as variant 1.
38
- * Not tested it, yet. Keep it here for todo testing purposes
39
- */
40
- public function replaceWindowsDirSeparator2($path)
41
- {
42
- return preg_replace('/[\\\\]+/', '/', $path);
43
- }
44
-
45
- /**
46
- * @param string $dir
47
- * @return bool True if directory is empty. False if empty or does not exist
48
- */
49
- public function isEmptyDir($dir)
50
- {
51
- if (!is_dir($dir)) {
52
- return false;
53
- }
54
-
55
- return (new FilesystemIterator($dir))->valid() === false;
56
- }
57
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Utils/WpDefaultDirectories.php CHANGED
@@ -14,6 +14,17 @@ class WpDefaultDirectories
14
  const MULTI_OLD_UPLOADS_DIR = 'blogs.dir';
15
  const MULTI_UPLOADS_DIR = 'sites';
16
 
 
 
 
 
 
 
 
 
 
 
 
17
  /**
18
  * Get path to the uploads folder, relatively to the wp root folder.
19
  * Allows custom uploads folders.
@@ -29,7 +40,7 @@ class WpDefaultDirectories
29
  */
30
  public function getRelativeUploadPath()
31
  {
32
- $relPath = str_replace(wpstg_replace_windows_directory_separator(ABSPATH), null, $this->getUploadPath());
33
 
34
  return trim($relPath, '/');
35
  }
@@ -50,8 +61,39 @@ class WpDefaultDirectories
50
  // Adding slashes at before and end of absolute path to WordPress uploads directory
51
  $uploadsAbsPath = trailingslashit($uploads['basedir']);
52
 
53
- return wpstg_replace_windows_directory_separator($uploadsAbsPath);
 
 
 
 
 
 
 
 
 
 
 
54
  }
55
 
 
 
 
 
 
 
 
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
14
  const MULTI_OLD_UPLOADS_DIR = 'blogs.dir';
15
  const MULTI_UPLOADS_DIR = 'sites';
16
 
17
+ /**
18
+ * @var Strings
19
+ */
20
+ private $strUtils;
21
+
22
+ public function __construct()
23
+ {
24
+ // @todo inject using DI
25
+ $this->strUtils = new Strings();
26
+ }
27
+
28
  /**
29
  * Get path to the uploads folder, relatively to the wp root folder.
30
  * Allows custom uploads folders.
40
  */
41
  public function getRelativeUploadPath()
42
  {
43
+ $relPath = str_replace($this->strUtils->sanitizeDirectorySeparator(ABSPATH), null, $this->getUploadPath());
44
 
45
  return trim($relPath, '/');
46
  }
61
  // Adding slashes at before and end of absolute path to WordPress uploads directory
62
  $uploadsAbsPath = trailingslashit($uploads['basedir']);
63
 
64
+ return $this->strUtils->sanitizeDirectorySeparator($uploadsAbsPath);
65
+ }
66
+
67
+ /**
68
+ * Get the relative path of wp content directory
69
+ * @return string
70
+ */
71
+ public function getRelativeWpContentPath()
72
+ {
73
+ $relPath = str_replace($this->strUtils->sanitizeDirectorySeparator(ABSPATH), null, WP_CONTENT_DIR);
74
+
75
+ return trim($relPath, '/');
76
  }
77
 
78
+ /**
79
+ * Get the relative path of plugins directory
80
+ * @return string
81
+ */
82
+ public function getRelativePluginPath()
83
+ {
84
+ $relPath = str_replace($this->strUtils->sanitizeDirectorySeparator(ABSPATH), null, WP_PLUGIN_DIR);
85
 
86
+ return trim($relPath, '/');
87
+ }
88
+
89
+ /**
90
+ * Get the relative path of themes directory
91
+ * @return string
92
+ */
93
+ public function getRelativeThemePath()
94
+ {
95
+ $relWpContentPath = $this->getRelativeWpContentPath();
96
+ $relPath = $relWpContentPath . "/" . "themes";
97
+ return $this->strUtils->sanitizeDirectorySeparator($relPath);
98
+ }
99
  }
Repository/SettingsRepository.php DELETED
@@ -1,43 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_types=1);
4
- // TODO PHP7.x type hints & return types
5
- // TODO PHP7.1; constant visibility
6
-
7
- namespace WPStaging\Repository;
8
-
9
- use WPStaging\Entity\Settings;
10
-
11
- class SettingsRepository
12
- {
13
- const OPTION_NAME = 'wpstg_settings';
14
-
15
- /**
16
- * @return Settings
17
- */
18
- public function find()
19
- {
20
- $records = get_option(self::OPTION_NAME, []);
21
- if (!$records || !is_array($records)) {
22
- return new Settings;
23
- }
24
-
25
- /** @var Settings|null $settings */
26
- $settings = (new Settings)->hydrate($records);
27
- return $settings;
28
- }
29
-
30
- /**
31
- * @param Settings $settings
32
- * @return bool
33
- */
34
- public function save(Settings $settings)
35
- {
36
- $data = $settings->toArray();
37
- $existing = $this->find();
38
- if ($existing && $data === $existing->toArray()) {
39
- return true;
40
- }
41
- return update_option(self::OPTION_NAME, $data, false);
42
- }
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
_init.php DELETED
@@ -1,57 +0,0 @@
1
- <?php
2
-
3
- use WPStaging\Core\WPStaging;
4
-
5
- /**
6
- * Do not show update notifications for WP STAGING Pro on the staging site
7
- * @param object
8
- * @return object
9
- * @todo Move this to separate class, e.g. wpstaging/Core/Wpcore/PluginUpdateNotify()
10
- */
11
- if (!function_exists('wpstg_filter_plugin_updates')) {
12
- function wpstg_filter_plugin_updates($value)
13
- {
14
- if (wpstg_is_stagingsite()) {
15
- if (isset($value->response['wp-staging-pro/wp-staging-pro.php'])) {
16
- unset($value->response['wp-staging-pro/wp-staging-pro.php']);
17
- }
18
- }
19
- return $value;
20
- }
21
- }
22
- add_filter('site_transient_update_plugins', 'wpstg_filter_plugin_updates');
23
-
24
- // Todo: Rename class to WPStaging\Core\WPStaging to comply with PSR-4
25
- if (!class_exists('WPStaging\Core\WPStaging')) {
26
- require_once plugin_dir_path(__FILE__) . "Core/WPStaging.php";
27
- }
28
-
29
- // Todo: Rename class to WPStaging\Core\Utils\WPSTG_Requirements_Check to comply with PSR-4
30
- if (!class_exists('Wpstg_Requirements_Check')) {
31
- include(__DIR__ . '/Core/Utils/requirements-check.php');
32
- }
33
-
34
- $pluginRequirements = new Wpstg_Requirements_Check([
35
- 'title' => 'WP STAGING',
36
- 'php' => '5.5',
37
- 'wp' => '4.0',
38
- 'file' => __FILE__,
39
- ]);
40
-
41
- if ($pluginRequirements->passes()) {
42
-
43
- $wpStaging = WPStaging::getInstance(new \WPStaging\Framework\DI\Container);
44
-
45
- /*
46
- * Set the WPSTG_COMPATIBLE constant in the container,
47
- * so that we can change it for testing purposes.
48
- */
49
- $wpStaging->set('WPSTG_COMPATIBLE', WPSTG_COMPATIBLE);
50
-
51
- // Wordpress DB Object
52
- global $wpdb;
53
-
54
- if ($wpdb instanceof \wpdb) {
55
- $wpStaging->set("wpdb", $wpdb);
56
- }
57
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Bootstrap/autoloader.php → autoloader.php RENAMED
@@ -1,18 +1,14 @@
1
  <?php
2
- /**
3
- * @var $this \WPStaging\Bootstrap\V1\WpstgBootstrap The context where this file is included.
4
- */
5
-
6
  // Register the autoloader for the plugin source code, and for the prefixed vendors.
7
  $class_map = array_merge(
8
- require_once $this->rootPath . '/vendor_wpstg/autoload/src.php',
9
- require_once $this->rootPath . '/vendor_wpstg/autoload/vendor.php'
10
  );
11
 
12
  spl_autoload_register(
13
  function ($class) use ($class_map) {
14
  if (isset($class_map[$class])) {
15
- require_once $class_map[$class];
16
 
17
  return true;
18
  }
1
  <?php
 
 
 
 
2
  // Register the autoloader for the plugin source code, and for the prefixed vendors.
3
  $class_map = array_merge(
4
+ include_once __DIR__ . '/vendor_wpstg/autoload/src.php',
5
+ include_once __DIR__ . '/vendor_wpstg/autoload/vendor.php'
6
  );
7
 
8
  spl_autoload_register(
9
  function ($class) use ($class_map) {
10
  if (isset($class_map[$class])) {
11
+ include_once $class_map[$class];
12
 
13
  return true;
14
  }
bootstrap.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var string $pluginFilePath The absolute path to the main file of this plugin.
4
+ */
5
+
6
+ use WPStaging\Core\WPStaging;
7
+
8
+ if (file_exists(__DIR__ . '/autoloader_dev.php')) {
9
+ include_once __DIR__ . '/autoloader_dev.php';
10
+ } else {
11
+ include_once __DIR__ . '/autoloader.php';
12
+ }
13
+
14
+ // Early bail: Unexpected behavior from the autoloader
15
+ if (!class_exists('\WPStaging\Core\WPStaging')) {
16
+ return;
17
+ }
18
+
19
+ // Register common constants.
20
+ if (!defined('WPSTG_PLUGIN_FILE')) {
21
+ define('WPSTG_PLUGIN_FILE', $pluginFilePath);
22
+ }
23
+
24
+ // Absolute path to plugin dir /var/www/.../plugins/wp-staging(-pro)
25
+ if (!defined('WPSTG_PLUGIN_DIR')) {
26
+ define('WPSTG_PLUGIN_DIR', plugin_dir_path($pluginFilePath));
27
+ }
28
+
29
+ // URL of the base folder
30
+ if (!defined('WPSTG_PLUGIN_URL')) {
31
+ define('WPSTG_PLUGIN_URL', plugin_dir_url($pluginFilePath));
32
+ }
33
+
34
+ // Expected version number of the must-use plugin 'optimizer'. Used for automatic updates of the mu-plugin
35
+ if (!defined('WPSTG_OPTIMIZER_MUVERSION')) {
36
+ define('WPSTG_OPTIMIZER_MUVERSION', 1.4);
37
+ }
38
+
39
+ // /var/www/single/wp-content/plugins/wp-staging-pro/wp-staging-pro.php => wp-staging-pro
40
+ if (!defined('WPSTG_PLUGIN_SLUG')) {
41
+ define('WPSTG_PLUGIN_SLUG', basename(dirname($pluginFilePath)));
42
+ }
43
+
44
+ // An identifier that is the same both for WPSTAGING Free and WPSTAGING Pro
45
+ if (!defined('WPSTG_PLUGIN_DOMAIN')) {
46
+ define('WPSTG_PLUGIN_DOMAIN', 'wp-staging');
47
+ }
48
+
49
+ /**
50
+ * Register specific Pro and Free constants. We register them here instead of on the
51
+ * entrypoint because we want to make sure we are defining constants for the plugins
52
+ * actually being bootstrapped.
53
+ */
54
+ if (file_exists(__DIR__ . '/constantsPro.php')) {
55
+ include_once __DIR__ . '/constantsPro.php';
56
+ } elseif (file_exists(__DIR__ . '/constantsFree.php')) {
57
+ include_once __DIR__ . '/constantsFree.php';
58
+ }
59
+
60
+ $wpStaging = WPStaging::getInstance();
61
+
62
+ /*
63
+ * Set the WPSTG_COMPATIBLE constant in the container,
64
+ * so that we can change it for testing purposes.
65
+ */
66
+ $wpStaging->set('WPSTG_COMPATIBLE', WPSTG_COMPATIBLE);
67
+
68
+ // Wordpress DB Object
69
+ global $wpdb;
70
+
71
+ if ($wpdb instanceof wpdb) {
72
+ $wpStaging->set("wpdb", $wpdb);
73
+ }
constants.php DELETED
@@ -1,25 +0,0 @@
1
- <?php
2
-
3
- // Absolute path to plugin dir /var/www/.../plugins/wp-staging(-pro)
4
- if (!defined('WPSTG_PLUGIN_DIR')) {
5
- define('WPSTG_PLUGIN_DIR', plugin_dir_path(WPSTG_PLUGIN_FILE));
6
- }
7
-
8
- // URL of the base folder
9
- if (!defined('WPSTG_PLUGIN_URL')) {
10
- define('WPSTG_PLUGIN_URL', plugin_dir_url(WPSTG_PLUGIN_FILE));
11
- }
12
-
13
- // Expected version number of the must-use plugin 'optimizer'. Used for automatic updates of the mu-plugin
14
- if (!defined('WPSTG_OPTIMIZER_MUVERSION')) {
15
- define('WPSTG_OPTIMIZER_MUVERSION', 1.4);
16
- }
17
-
18
- if (!defined('WPSTG_PLUGIN_SLUG')) {
19
- define('WPSTG_PLUGIN_SLUG', dirname(WPSTG_PLUGIN_FILE));
20
- }
21
-
22
- if (!defined('WPSTG_PLUGIN_DOMAIN')) {
23
- // An identifier that is the same both for WPSTAGING Free and WPSTAGING Pro
24
- define('WPSTG_PLUGIN_DOMAIN', 'wp-staging');
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
constantsFree.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // WP STAGING version number
3
+ if (!defined('WPSTG_VERSION')) {
4
+ define('WPSTG_VERSION', '2.8.1');
5
+ }
6
+
7
+ // Compatible up to WordPress Version
8
+ if (!defined('WPSTG_COMPATIBLE')) {
9
+ define('WPSTG_COMPATIBLE', '5.6');
10
+ }
freeBootstrap.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The purpose of the pre-bootstrap process is to make sure the environment is able to run
4
+ * the plugin without any errors, such as making sure there are no other WPSTAGING instances
5
+ * active at the same time.
6
+ *
7
+ * It works at a low level, without the autoloader, using anonymous callbacks and local variables
8
+ * to make sure we always use and execute the expected code.
9
+ *
10
+ * Since it uses closures, you can't dequeue those actions, but this is expected.
11
+ *
12
+ * @var string $pluginFilePath The absolute path to the main file of this plugin.
13
+ */
14
+
15
+ add_action('plugins_loaded', function () use ($pluginFilePath) {
16
+ try {
17
+ require __DIR__ . '/runtimeRequirements.php';
18
+ require_once __DIR__ . '/bootstrap.php';
19
+ } catch (Exception $e) {
20
+ if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
21
+ error_log('WPSTAGING: ' . $e->getMessage());
22
+ }
23
+ }
24
+ }, 11, 0); // The priority of this hook must be larger than 10 for the runtime requirement check to detect older versions of WPSTAGING.
25
+
26
+ register_activation_hook($pluginFilePath, function () use ($pluginFilePath) {
27
+ // Prevent WPSTAGING Free activation when Pro is active
28
+ if (is_multisite()) {
29
+ foreach (wp_get_active_network_plugins() as $networkActivePlugin) {
30
+ if (strpos($networkActivePlugin, 'wp-staging-pro.php') !== false) {
31
+ set_site_transient('wpstgActivatingFreeWhileProIsActive', true);
32
+ wp_safe_redirect(self_admin_url('plugins.php'));
33
+ exit;
34
+ }
35
+ }
36
+ }
37
+ foreach (wp_get_active_and_valid_plugins() as $sitewidePlugin) {
38
+ if (strpos($sitewidePlugin, 'wp-staging-pro.php') !== false) {
39
+ // Set a transient that Pro picks up to render a notice to the user.
40
+ set_site_transient('wpstgActivatingFreeWhileProIsActive', true);
41
+
42
+ // Redirects to prevent "Plugin could not be activated because it triggered a fatal error notice".
43
+ wp_safe_redirect(self_admin_url('plugins.php'));
44
+
45
+ // Prevents the activation of this plugin.
46
+ exit;
47
+ }
48
+ }
49
+
50
+ try {
51
+ require __DIR__ . '/runtimeRequirements.php';
52
+ require_once __DIR__ . '/bootstrap.php';
53
+ require_once __DIR__ . '/install.php';
54
+ } catch (Exception $e) {
55
+ if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
56
+ error_log('WPSTAGING: ' . $e->getMessage());
57
+ }
58
+ }
59
+ });
install.php CHANGED
@@ -1,82 +1,36 @@
1
  <?php
2
-
3
- namespace WPStaging;
 
 
 
 
4
 
5
  use WPStaging\Backend\Optimizer\Optimizer;
6
- use WPStaging\Bootstrap\V1\WpstgBootstrap;
7
- use WPStaging\Core\Utils\IISWebConfig;
8
  use WPStaging\Core\Utils\Htaccess;
9
- use WPStaging\Core\Utils\Filesystem;
10
 
11
  /**
12
- * Install Class
13
- *
14
  */
15
- class Install
16
- {
17
- private $bootstrap;
18
-
19
- public function __construct(WpstgBootstrap $bootstrap)
20
- {
21
- $this->bootstrap = $bootstrap;
22
- }
23
-
24
- public function activation()
25
- {
26
- $this->bootstrap->checkRequirements();
27
- $this->bootstrap->bootstrap();
28
-
29
- if ($this->bootstrap->passedRequirements()) {
30
- $this->initCron();
31
- $this->installOptimizer();
32
- $this->createHtaccess();
33
- $this->createIndex();
34
- $this->createWebConfig();
35
- }
36
- }
37
-
38
- private function initCron()
39
- {
40
- // Register cron job.
41
- $cron = new \WPStaging\Core\Cron\Cron;
42
- $cron->schedule_event();
43
- }
44
-
45
- private function installOptimizer()
46
- {
47
 
48
- // Install Optimizer
49
- $optimizer = new Optimizer();
50
- $optimizer->installOptimizer();
51
-
52
- if (!defined('WPSTGPRO_VERSION')) {
53
- // Add the transient to redirect for class Welcome (not for multisites) and not for Pro version
54
- set_transient('wpstg_activation_redirect', true, 3600);
55
- }
56
- }
57
-
58
- private function createHtaccess()
59
- {
60
- $htaccess = new Htaccess();
61
- $htaccess->create(trailingslashit(\WPStaging\Core\WPStaging::getContentDir()) . '.htaccess');
62
- $htaccess->create(trailingslashit(\WPStaging\Core\WPStaging::getContentDir()) . 'logs/.htaccess');
63
-
64
- if (extension_loaded('litespeed')) {
65
- $htaccess->createLitespeed(ABSPATH . '.htaccess');
66
- }
67
- }
68
 
69
- private function createIndex()
70
- {
71
- $filesystem = new Filesystem();
72
- $filesystem->create(trailingslashit(\WPStaging\Core\WPStaging::getContentDir()) . 'index.php', "<?php // silence");
73
- $filesystem->create(trailingslashit(\WPStaging\Core\WPStaging::getContentDir()) . 'logs/index.php', "<?php // silence");
74
- }
75
 
76
- private function createWebConfig()
77
- {
78
- $webconfig = new IISWebConfig();
79
- $webconfig->create(trailingslashit(\WPStaging\Core\WPStaging::getContentDir()) . 'web.config');
80
- $webconfig->create(trailingslashit(\WPStaging\Core\WPStaging::getContentDir()) . 'logs/web.config');
81
- }
82
  }
1
  <?php
2
+ /**
3
+ * This file is hooked as the \register_activation_hook of the plugin,
4
+ * therefore it runs as a standalone script that needs to be bootstrapped.
5
+ *
6
+ * @var string $pluginFilePath The absolute path to the main file of this plugin.
7
+ */
8
 
9
  use WPStaging\Backend\Optimizer\Optimizer;
10
+ use WPStaging\Core\Cron\Cron;
 
11
  use WPStaging\Core\Utils\Htaccess;
 
12
 
13
  /**
14
+ * Register Cron Events
 
15
  */
16
+ $cron = (new Cron)->scheduleEvent();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ /**
19
+ * Install the Optimizer
20
+ */
21
+ $optimizer = (new Optimizer)->installOptimizer();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ /**
24
+ * Add the transient to redirect for class Welcome (Not for multisites) and not for Pro version
25
+ */
26
+ if (!defined('WPSTGPRO_VERSION')) {
27
+ set_transient('wpstg_activation_redirect', true, 3600);
28
+ }
29
 
30
+ /**
31
+ * Create Htaccess
32
+ */
33
+ $htaccess = new Htaccess();
34
+ if (extension_loaded('litespeed')) {
35
+ $htaccess->createLitespeed(ABSPATH . '.htaccess');
36
  }
readme.txt CHANGED
@@ -1,4 +1,4 @@
1
- === WP STAGING - DB & File Duplicator & Migration ===
2
 
3
  Author URL: https://wordpress.org/plugins/wp-staging
4
  Plugin URL: https://wordpress.org/plugins/wp-staging
@@ -6,13 +6,13 @@ Contributors: ReneHermi, WP-Staging
6
  Donate link: https://wordpress.org/plugins/wp-staging
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
- Tags: clone, backup, staging, duplication, migration
10
  Requires at least: 3.6+
11
  Tested up to: 5.6
12
- Stable tag: 2.8.0
13
  Requires PHP: 5.5
14
 
15
- A duplicator plugin - clone/move, duplicate & migrate websites to staging, backup and development sites that only authorized users can access.
16
 
17
  == Description ==
18
 
@@ -34,7 +34,7 @@ WP STAGING can help you to prevent your website from being broken or unavailable
34
 
35
  = Main Features =
36
 
37
- * WP STAGING clones the whole production site into a subfolder like example.com/staging-site.
38
  * Easy to use! Create a clone of your site by clicking one button "CREATE NEW STAGING SITE".
39
  * No Software as a Service - No account needed! All data belong to you only and stay on your server.
40
  * No server timeouts on huge websites or/and small hosting servers
@@ -132,6 +132,17 @@ After installation, go to the settings page 'Staging' and do your adjustments th
132
 
133
  == Frequently Asked Questions ==
134
 
 
 
 
 
 
 
 
 
 
 
 
135
  * I can not log in to the staging / backup site
136
  If you are using a security plugin like All In One WP Security & Firewall you need to install the latest version of WP STAGING to access your cloned backup site.
137
  Go to WP STAGING > Settings and add the slug to the custom login page which you set up in All In One WP Security & Firewall plugin.
@@ -155,6 +166,17 @@ https://wp-staging.com
155
 
156
  == Changelog ==
157
 
 
 
 
 
 
 
 
 
 
 
 
158
  = 2.8.0 =
159
  * Fix: Latest supported WP version is not reflected on wordpress.org plugin repo
160
  * Enh: Show confirmation popup if user tries to send another support request within an hour
@@ -292,25 +314,16 @@ SKIP VERSION
292
  * Fix: PDO instances can not be serialized or unserialized
293
  * Fix: Can not update staging site db table if there are constraints in it
294
 
295
- = 2.6.0 =
296
- * New: Compatible up to WordPress 5.2.2
297
- * New: Performance improvement for directory iterator using less server ressources
298
- * New: Add filter wpstg_folder_permission to set a custom folder permission like 0755, allows to overwrite FS_CHMOD_DIR if it has been defined.
299
- * Fix: Error conditions in class Data does not compare type strict (== vs. ==) resulting in interruption of clone process
300
- * Fix: Excluded folders under wp-content level are not take into account on microsoft IIS servers
301
-
302
  Full changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
303
 
304
  == Upgrade Notice ==
305
- * New: Compatible up to WordPress 5.6
306
- * Fix: Uninstall function can throw fatal error
307
- * Fix: Do not write sensitive information into debug.log if debug mode is active
308
- * Fix: Update notification shown even if there is no more recent version
309
- * Enh: Change authentication to a combination of nonces and access tokens
310
- * Enh: Show confirmation popup if user tries to send another support request within an hour
311
- * Dev: Improve tests performance
312
- * Dev: Add tests for database export and restore
313
- * Dev: Memory exhausted during tests
314
- * Dev: Prefix composer vendor libraries with PHP-Scoper
315
- * Dev: Add more unit and webdriver tests to improve QA
316
 
1
+ === WP STAGING - Backup Duplicator & Migration ===
2
 
3
  Author URL: https://wordpress.org/plugins/wp-staging
4
  Plugin URL: https://wordpress.org/plugins/wp-staging
6
  Donate link: https://wordpress.org/plugins/wp-staging
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
+ Tags: backup, database backup, staging, duplication, clone
10
  Requires at least: 3.6+
11
  Tested up to: 5.6
12
+ Stable tag: 2.8.1
13
  Requires PHP: 5.5
14
 
15
+ A backup & duplicator plugin - clone, move, duplicate & migrate websites to staging, backup and development sites for authorized users only.
16
 
17
  == Description ==
18
 
34
 
35
  = Main Features =
36
 
37
+ * WPSTAGING clones the whole production site into a subfolder like example.com/staging-site.
38
  * Easy to use! Create a clone of your site by clicking one button "CREATE NEW STAGING SITE".
39
  * No Software as a Service - No account needed! All data belong to you only and stay on your server.
40
  * No server timeouts on huge websites or/and small hosting servers
132
 
133
  == Frequently Asked Questions ==
134
 
135
+ * What is the Difference between WP STAGING and a Regular Backup Plugin?
136
+ You may have heard about other popular backup plugins like BackWPUp, BackupWordPress, Simple Backup, WordPress Backup to Dropbox or similar WordPress backup plugins and now wonder about the difference between WP STAGING and those backup tools.
137
+ Other backup plugins usually create a backup of your WordPress filesystem and a database backup which you can use to restore your website in case it became corrupted or you want to go back in time to a previous state.
138
+ The backup files are compressed and can not be executed directly. WP STAGING on the other hand creates a full backup of the whole file system and the database in a working state that you can open like your original production website.
139
+
140
+ Even though WP STAGING comes with some backup capabilities it's main purpose is to create a clone of your website which you can work on. It harmonies very well with all the mentioned backup plugins above and we recommend that you use it in conjunction with these backup plugins.
141
+
142
+ Note, that some free backup plugins are not able to support custom tables. (For instance the free version of Updraft plus backup plugin). In that case, your backup plugin is not able to create a backup of your staging site when it is executed on the production site.
143
+ The reason is that the tables created by WP STAGING are kind of custom tables beginning with another table prefix.
144
+ To bypass this limitation and to be able to create a backup of your staging site, you can setup your backup plugin on the staging site and create the backup from that location. This works well with every available WordPress backup plugin.
145
+
146
  * I can not log in to the staging / backup site
147
  If you are using a security plugin like All In One WP Security & Firewall you need to install the latest version of WP STAGING to access your cloned backup site.
148
  Go to WP STAGING > Settings and add the slug to the custom login page which you set up in All In One WP Security & Firewall plugin.
166
 
167
  == Changelog ==
168
 
169
+ = 2.8.1 =
170
+ * Feat: Show creator user name of staging site
171
+ * Enh: Show notice if sending mails are disabled
172
+ * Enh: Show message and stop execution if php version is lower than 5.5
173
+ * Enh: Abort cloning process if table already exists in external database
174
+ * Fix: Can not update database credentials in staging sites wp-config.php under rare circumstances
175
+ * Fix: During the update process if options table was not selected it didn't get skipped
176
+ * Fix: Error if WP is lower than 4.6
177
+ * Fix: Can not delete entire staging site on error
178
+ * Fix: Activating pro version does not properly disable free version
179
+
180
  = 2.8.0 =
181
  * Fix: Latest supported WP version is not reflected on wordpress.org plugin repo
182
  * Enh: Show confirmation popup if user tries to send another support request within an hour
314
  * Fix: PDO instances can not be serialized or unserialized
315
  * Fix: Can not update staging site db table if there are constraints in it
316
 
 
 
 
 
 
 
 
317
  Full changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
318
 
319
  == Upgrade Notice ==
320
+ * Feat: Show creator user name of staging site
321
+ * Enh: Show notice if sending mails are disabled
322
+ * Enh: Show message and stop execution if php version is lower than 5.5
323
+ * Enh: Abort cloning process if table already exists in external database
324
+ * Fix: Can not update database credentials in staging sites wp-config.php under rare circumstances
325
+ * Fix: During the update process if options table was not selected it didn't get skipped
326
+ * Fix: Error if WP is lower than 4.6
327
+ * Fix: Can not delete entire staging site on error
328
+ * Fix: Activating pro version does not properly disable free version
 
 
329
 
runtimeRequirements.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var string $pluginFilePath The absolute path to the main file of this plugin.
4
+ */
5
+
6
+ /**
7
+ * Early bail: Activating another WPSTAGING Plugin.
8
+ * This is the only scenario where the plugin would be included after "plugins_loaded",
9
+ * therefore we need to detect earlier, from the context of the request, whether this is going to happen,
10
+ * to disable this plugin early and bail the bootstrap process to not conflict with the one being activated.
11
+ *
12
+ * Covers both clicking on the "Activate" button and selecting the "Activate" bulk-action.
13
+ */
14
+ if (isset($_REQUEST['action'])) {
15
+ switch ($_REQUEST['action']):
16
+ case 'activate':
17
+ case 'error_scrape':
18
+ if (isset($_REQUEST['plugin'])) {
19
+ $plugin = (string)wp_unslash($_REQUEST['plugin']);
20
+
21
+ $isActivatingWpStaging = strpos($plugin, 'wp-staging.php') || strpos($plugin, 'wp-staging-pro.php');
22
+ $isActivatingAnotherWpStaging = plugin_basename($plugin) !== plugin_basename($pluginFilePath);
23
+
24
+ if ($isActivatingWpStaging && $isActivatingAnotherWpStaging && current_user_can('deactivate_plugin', plugin_basename($pluginFilePath))) {
25
+ throw new Exception("Activating another WPSTAGING Plugin. Plugin that bailed bootstrapping: $pluginFilePath");
26
+ }
27
+ }
28
+ break;
29
+ case 'activate-selected':
30
+ case 'activate-multi':
31
+ if (isset($_REQUEST['checked'])) {
32
+ $plugins = (array)wp_unslash($_REQUEST['checked']);
33
+
34
+ foreach ($plugins as $i => $plugin) {
35
+ $isActivatingWpStaging = strpos($plugin, 'wp-staging.php') || strpos($plugin, 'wp-staging-pro.php');
36
+ $isActivatingAnotherWpStaging = plugin_basename($plugin) !== plugin_basename($pluginFilePath);
37
+
38
+ if ($isActivatingWpStaging && $isActivatingAnotherWpStaging && current_user_can('deactivate_plugin', plugin_basename($pluginFilePath))) {
39
+ throw new Exception("Activating another WPSTAGING Plugin. Plugin that bailed bootstrapping: $pluginFilePath");
40
+ }
41
+ }
42
+ }
43
+ break;
44
+ endswitch;
45
+ }
46
+
47
+ /**
48
+ * Early bail: Another instance of WPSTAGING active.
49
+ */
50
+ if (// WPSTAGING <= 2.7.5
51
+ class_exists('\WPStaging\WPStaging') ||
52
+ // WPSTAGING >= 2.7.6
53
+ class_exists('\WPStaging\Core\WPStaging')
54
+ ) {
55
+ add_action(is_network_admin() ? 'network_admin_notices' : 'admin_notices', function () {
56
+ echo '<div class="notice-warning notice is-dismissible another-wpstaging-active">';
57
+ echo '<p style="font-weight: bold;">' . esc_html__('WP STAGING Already Active') . '</p>';
58
+ echo '<p>' . esc_html__('Another WP STAGING is already activated, please leave only one instance of the WP STAGING plugin active at the same time.', 'wp-staging') . '</p>';
59
+ echo '</div>';
60
+ });
61
+
62
+ throw new Exception("Another instance of WPSTAGING active. Plugin that bailed bootstrapping: $pluginFilePath");
63
+ }
64
+
65
+ /**
66
+ * Early bail: Unsupported WordPress version.
67
+ * We check on runtime instead of activation so we can display the notice.
68
+ */
69
+ if (!version_compare($currentWordPressVersion = get_bloginfo('version'), $minimumWordPressVersion = '4.0', '>=')) {
70
+ add_action(is_network_admin() ? 'network_admin_notices' : 'admin_notices', function () use ($currentWordPressVersion, $minimumWordPressVersion) {
71
+ echo '<div class="notice-warning notice is-dismissible">';
72
+ echo '<p style="font-weight: bold;">' . esc_html__('WP STAGING') . '</p>';
73
+ echo '<p>' . esc_html__(sprintf('WPSTAGING requires at least WordPress %s to run. You have WordPress %s.', $minimumWordPressVersion, $currentWordPressVersion), 'wp-staging') . '</p>';
74
+ echo '</div>';
75
+ });
76
+
77
+ throw new Exception("Unsupported WordPress version. Plugin that bailed bootstrapping: $pluginFilePath");
78
+ }
79
+
template/.gitkeep DELETED
File without changes
template/Component/Backend/Snapshot/listing.php DELETED
@@ -1,102 +0,0 @@
1
- <?php
2
-
3
- use WPStaging\Framework\Collection\OptionCollection;
4
- use WPStaging\Framework\TemplateEngine\TemplateEngine;
5
-
6
- /** @var TemplateEngine $this */
7
- /** @var OptionCollection $snapshots */
8
-
9
- /**
10
- * @todo See if we can unify this file with src/Pro/Snapshot/template/listing.php:12
11
- */
12
- ?>
13
- <div class="wpstg-beta-notice">
14
- <?php _e('WP Staging snapshots work well but is a new feature in beta status. Please use it on your own risk and have a backup of your site before using it. <br>If you experience an issue or you have a feature request <a href="#" id="wpstg-snapshots-report-issue-button">please give us some feedback.</a>','wp-staging')?>
15
- </div>
16
- <div id="wpstg-step-1">
17
- <button id="wpstg-new-snapshot" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button"
18
- data-action="wpstg--snapshots--create">
19
- <?php _e('Take New Snapshot', 'wp-staging') ?>
20
- </button>
21
- <div class="wpstg--tooltip"> <?php _e('What is this?', 'wp-staging'); ?>
22
-
23
- <span class="wpstg--tooltiptext wpstg--tooltiptext-snapshots">
24
- <?php _e('Snapshots are copies of the WordPress database tables at a particular point in time.
25
- They can restore WordPress and roll back the database to another state.<br><br>
26
- This is useful if you need to reset WordPress to the state before you\'ve pushed a staging site to live or if you want to revert other database changes
27
- like activating a new theme or updating its settings.<br><br>
28
- WP Staging Snapshots include all WordPress core tables and custom ones created by other plugins.
29
- Restoring a snapshot will not affect other staging sites or snapshots. <br><br>
30
- No files are included in snapshots! WP Staging snapshots are a quick way to roll back your site in time but for a full site backup it is recommended to use a dedicated backup plugin!
31
- ', 'wp-staging') ?>
32
- <p></p>
33
- <?php if (is_multisite()) {
34
- echo '<strong>' . __('Multisite Users Only: ', 'wp-staging') . '</strong>';
35
- echo '<p></p>';
36
- echo __("- If you run the snapshot function on a multisite network site the snapshot will contain only the tables belonging to the particular network site. <p></p>It will not store all database tables of all network sites. So you are able to restore all network sites independently. <p></p>- If you create a snapshot on a multisite main site it will create a snapshot of <strong>all database tables</strong>.</p></p><strong>Take care:</strong> Restoring a multisite main snapshot will <strong>restore all children sites including the mainsite.</strong>", 'wp-staging');
37
- } ?>
38
- </span>
39
- </div>
40
- </div>
41
-
42
- <div id="wpstg-existing-snapshots">
43
- <h3>
44
- <?php echo $snapshots ? __('Your Snapshots:', 'wp-staging') : '' ?>
45
- </h3>
46
- <?php foreach ($snapshots as $snapshot): ?>
47
- <div id="<?php echo $snapshot->getId() ?>" class="wpstg-clone">
48
- <span class="wpstg-clone-title"><?php echo $snapshot->getName() ?></span>
49
-
50
- <a href="#" class="wpstg--snapshot--export wpstg-merge-clone wpstg-clone-action"
51
- data-id="<?php echo $snapshot->getId() ?>"
52
- title="<?php _e('Download backup as sql file to local system', 'wp-staging') ?>">
53
- <?php _e('Export', 'wp-staging') ?>
54
- </a>
55
-
56
- <a href="#" class="wpstg--snapshot--restore wpstg-merge-clone wpstg-clone-action"
57
- data-id="<?php echo $snapshot->getId() ?>"
58
- title="<?php _e('Restore this snapshot to your live website!', 'wp-staging') ?>">
59
- <?php _e('Restore', 'wp-staging') ?>
60
- </a>
61
-
62
- <a href="#" class="wpstg-remove-clone wpstg-clone-action wpstg-delete-snapshot"
63
- data-id="<?php echo $snapshot->getId() ?>"
64
- title="<?php _e('Delete this snapshot. This action can not be undone!', 'wp-staging') ?>">
65
- <?php _e('Delete', 'wp-staging') ?>
66
- </a>
67
-
68
- <a href="#" class="wpstg--snapshot--edit wpstg-clone-action"
69
- data-id="<?php echo $snapshot->getId() ?>"
70
- data-name="<?php echo $snapshot->getName() ?>"
71
- data-notes="<?php echo $snapshot->getNotes() ?>"
72
- title="<?php _e('Edit backup name and / or notes', 'wp-staging') ?>">
73
- <?php _e('Edit', 'wp-staging') ?>
74
- </a>
75
-
76
- <div class="wpstg-staging-info">
77
- <ul>
78
- <li>
79
- <strong><?php _e('Table Prefix:', 'wp-staging') ?></strong>
80
- <?php echo $snapshot->getId() ?>
81
- </li>
82
- <li>
83
- <strong><?php _e('Created on:', 'wp-staging') ?></strong>
84
- <?php echo $this->transformToWpFormat($snapshot->getCreatedAt()) ?>
85
- <?php if ($snapshot->getUpdatedAt()): ?>
86
- &nbsp; | &nbsp;<strong><?php _e('Updated on:', 'wp-staging') ?></strong>
87
- <?php echo $this->transformToWpFormat($snapshot->getUpdatedAt()) ?>
88
- <?php endif ?>
89
- </li>
90
- <?php if ($snapshot->getNotes()):?>
91
- <li>
92
- <strong><?php _e('Notes:', 'wp-staging') ?></strong><br/>
93
- <?php echo nl2br($snapshot->getNotes()) ?>
94
- </li>
95
- <?php endif ?>
96
- </ul>
97
- </div>
98
- </div>
99
- <?php endforeach ?>
100
- </div>
101
-
102
- <div id="wpstg-delete-confirmation"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
uninstall.php CHANGED
@@ -44,6 +44,7 @@ class uninstall
44
  delete_option("wpstg_rmpermalinks_executed");
45
  delete_option("wpstg_activation_redirect");
46
  delete_option("wpstg_emails_disabled");
 
47
 
48
 
49
  /* Do not delete these fields without actually deleting the staging site
44
  delete_option("wpstg_rmpermalinks_executed");
45
  delete_option("wpstg_activation_redirect");
46
  delete_option("wpstg_emails_disabled");
47
+ delete_option("wpstg_disabled_cache_notice");
48
 
49
 
50
  /* Do not delete these fields without actually deleting the staging site
vendor_wpstg/autoload/src.php CHANGED
@@ -12,6 +12,7 @@ return array(
12
  'WPStaging\\Backend\\Feedback\\Feedback' => $baseDir . '/Backend/Feedback/Feedback.php',
13
  'WPStaging\\Backend\\Modules\\Jobs\\Cancel' => $baseDir . '/Backend/Modules/Jobs/Cancel.php',
14
  'WPStaging\\Backend\\Modules\\Jobs\\CancelUpdate' => $baseDir . '/Backend/Modules/Jobs/CancelUpdate.php',
 
15
  'WPStaging\\Backend\\Modules\\Jobs\\Cloning' => $baseDir . '/Backend/Modules/Jobs/Cloning.php',
16
  'WPStaging\\Backend\\Modules\\Jobs\\CloningProcess' => $baseDir . '/Backend/Modules/Jobs/CloningProcess.php',
17
  'WPStaging\\Backend\\Modules\\Jobs\\Data' => $baseDir . '/Backend/Modules/Jobs/Data.php',
@@ -41,13 +42,13 @@ return array(
41
  'WPStaging\\Backend\\Modules\\SystemInfo' => $baseDir . '/Backend/Modules/SystemInfo.php',
42
  'WPStaging\\Backend\\Modules\\Views\\Forms\\Settings' => $baseDir . '/Backend/Modules/Views/Forms/Settings.php',
43
  'WPStaging\\Backend\\Modules\\Views\\Tabs\\Tabs' => $baseDir . '/Backend/Modules/Views/Tabs/Tabs.php',
 
44
  'WPStaging\\Backend\\Notices\\DisabledCacheNotice' => $baseDir . '/Backend/Notices/DisabledCacheNotice.php',
45
  'WPStaging\\Backend\\Notices\\Notices' => $baseDir . '/Backend/Notices/Notices.php',
46
  'WPStaging\\Backend\\Optimizer\\Optimizer' => $baseDir . '/Backend/Optimizer/Optimizer.php',
47
  'WPStaging\\Backend\\Pluginmeta\\Meta' => $baseDir . '/Backend/Pluginmeta/Meta.php',
48
  'WPStaging\\Backend\\Pluginmeta\\Pluginmeta' => $baseDir . '/Backend/Pluginmeta/Pluginmeta.php',
49
  'WPStaging\\Backend\\Upgrade\\Upgrade' => $baseDir . '/Backend/Upgrade/Upgrade.php',
50
- 'WPStaging\\Bootstrap\\V1\\WpstgBootstrap' => $baseDir . '/Bootstrap/V1/WpstgBootstrap.php',
51
  'WPStaging\\Command\\Database\\Export\\ExportCommand' => $baseDir . '/Command/Database/Export/ExportCommand.php',
52
  'WPStaging\\Command\\Database\\Export\\ExportDto' => $baseDir . '/Command/Database/Export/ExportDto.php',
53
  'WPStaging\\Command\\Database\\Export\\ExportException' => $baseDir . '/Command/Database/Export/ExportException.php',
@@ -100,7 +101,6 @@ return array(
100
  'WPStaging\\Core\\Utils\\Browser' => $baseDir . '/Core/Utils/Browser.php',
101
  'WPStaging\\Core\\Utils\\Cache' => $baseDir . '/Core/Utils/Cache.php',
102
  'WPStaging\\Core\\Utils\\Directories' => $baseDir . '/Core/Utils/Directories.php',
103
- 'WPStaging\\Core\\Utils\\Filesystem' => $baseDir . '/Core/Utils/Filesystem.php',
104
  'WPStaging\\Core\\Utils\\Hash' => $baseDir . '/Core/Utils/Hash.php',
105
  'WPStaging\\Core\\Utils\\Helper' => $baseDir . '/Core/Utils/Helper.php',
106
  'WPStaging\\Core\\Utils\\Htaccess' => $baseDir . '/Core/Utils/Htaccess.php',
@@ -114,9 +114,6 @@ return array(
114
  'WPStaging\\Core\\Utils\\Strings' => $baseDir . '/Core/Utils/Strings.php',
115
  'WPStaging\\Core\\WPStaging' => $baseDir . '/Core/WPStaging.php',
116
  'WPStaging\\Core\\thirdParty\\thirdPartyCompatibility' => $baseDir . '/Core/thirdParty/thirdPartyCompatibility.php',
117
- 'WPStaging\\Entity\\DatabaseSettings' => $baseDir . '/Entity/DatabaseSettings.php',
118
- 'WPStaging\\Entity\\FilesystemSettings' => $baseDir . '/Entity/FilesystemSettings.php',
119
- 'WPStaging\\Entity\\Settings' => $baseDir . '/Entity/Settings.php',
120
  'WPStaging\\Framework\\Adapter\\Database' => $baseDir . '/Framework/Adapter/Database.php',
121
  'WPStaging\\Framework\\Adapter\\Database\\AbstractDatabase' => $baseDir . '/Framework/Adapter/Database/AbstractDatabase.php',
122
  'WPStaging\\Framework\\Adapter\\Database\\DatabaseException' => $baseDir . '/Framework/Adapter/Database/DatabaseException.php',
@@ -167,12 +164,13 @@ return array(
167
  'WPStaging\\Framework\\Entity\\AbstractEntity' => $baseDir . '/Framework/Entity/AbstractEntity.php',
168
  'WPStaging\\Framework\\Entity\\EntityException' => $baseDir . '/Framework/Entity/EntityException.php',
169
  'WPStaging\\Framework\\Entity\\IdentifyableEntityInterface' => $baseDir . '/Framework/Entity/IdentifyableEntityInterface.php',
 
170
  'WPStaging\\Framework\\Filesystem\\DirectoryScanner' => $baseDir . '/Framework/Filesystem/DirectoryScanner.php',
171
- 'WPStaging\\Framework\\Filesystem\\DirectoryService' => $baseDir . '/Framework/Filesystem/DirectoryService.php',
172
  'WPStaging\\Framework\\Filesystem\\DiskFullException' => $baseDir . '/Framework/Filesystem/DiskFullException.php',
173
  'WPStaging\\Framework\\Filesystem\\File' => $baseDir . '/Framework/Filesystem/File.php',
174
  'WPStaging\\Framework\\Filesystem\\FileScanner' => $baseDir . '/Framework/Filesystem/FileScanner.php',
175
- 'WPStaging\\Framework\\Filesystem\\FileService' => $baseDir . '/Framework/Filesystem/FileService.php',
176
  'WPStaging\\Framework\\Filesystem\\Filesystem' => $baseDir . '/Framework/Filesystem/Filesystem.php',
177
  'WPStaging\\Framework\\Filesystem\\FilesystemExceptions' => $baseDir . '/Framework/Filesystem/FilesystemExceptions.php',
178
  'WPStaging\\Framework\\Filesystem\\FilterableDirectoryIterator' => $baseDir . '/Framework/Filesystem/FilterableDirectoryIterator.php',
@@ -180,6 +178,7 @@ return array(
180
  'WPStaging\\Framework\\Filesystem\\Filters\\ExtensionExcludeFilter' => $baseDir . '/Framework/Filesystem/Filters/ExtensionExcludeFilter.php',
181
  'WPStaging\\Framework\\Filesystem\\Filters\\PathExcludeFilter' => $baseDir . '/Framework/Filesystem/Filters/PathExcludeFilter.php',
182
  'WPStaging\\Framework\\Filesystem\\Filters\\RecursiveExtensionExcludeFilter' => $baseDir . '/Framework/Filesystem/Filters/RecursiveExtensionExcludeFilter.php',
 
183
  'WPStaging\\Framework\\Filesystem\\WpUploadsFolderSymlinker' => $baseDir . '/Framework/Filesystem/WpUploadsFolderSymlinker.php',
184
  'WPStaging\\Framework\\Interfaces\\ArrayableInterface' => $baseDir . '/Framework/Interfaces/ArrayableInterface.php',
185
  'WPStaging\\Framework\\Interfaces\\HydrateableInterface' => $baseDir . '/Framework/Interfaces/HydrateableInterface.php',
@@ -196,6 +195,7 @@ return array(
196
  'WPStaging\\Framework\\Queue\\Storage\\QueueStorageException' => $baseDir . '/Framework/Queue/Storage/QueueStorageException.php',
197
  'WPStaging\\Framework\\Queue\\Storage\\StorageInterface' => $baseDir . '/Framework/Queue/Storage/StorageInterface.php',
198
  'WPStaging\\Framework\\Security\\AccessToken' => $baseDir . '/Framework/Security/AccessToken.php',
 
199
  'WPStaging\\Framework\\Security\\Capabilities' => $baseDir . '/Framework/Security/Capabilities.php',
200
  'WPStaging\\Framework\\Security\\Nonce' => $baseDir . '/Framework/Security/Nonce.php',
201
  'WPStaging\\Framework\\SiteInfo' => $baseDir . '/Framework/SiteInfo.php',
@@ -204,6 +204,7 @@ return array(
204
  'WPStaging\\Framework\\TemplateEngine\\TemplateEngineException' => $baseDir . '/Framework/TemplateEngine/TemplateEngineException.php',
205
  'WPStaging\\Framework\\TemplateEngine\\TemplateEngineInterface' => $baseDir . '/Framework/TemplateEngine/TemplateEngineInterface.php',
206
  'WPStaging\\Framework\\Traits\\ArrayableTrait' => $baseDir . '/Framework/Traits/ArrayableTrait.php',
 
207
  'WPStaging\\Framework\\Traits\\BooleanTransientTrait' => $baseDir . '/Framework/Traits/BooleanTransientTrait.php',
208
  'WPStaging\\Framework\\Traits\\HydrateTrait' => $baseDir . '/Framework/Traits/HydrateTrait.php',
209
  'WPStaging\\Framework\\Traits\\MaintenanceTrait' => $baseDir . '/Framework/Traits/MaintenanceTrait.php',
@@ -213,7 +214,6 @@ return array(
213
  'WPStaging\\Framework\\Utils\\Cache\\AbstractCache' => $baseDir . '/Framework/Utils/Cache/AbstractCache.php',
214
  'WPStaging\\Framework\\Utils\\Cache\\BufferedCache' => $baseDir . '/Framework/Utils/Cache/BufferedCache.php',
215
  'WPStaging\\Framework\\Utils\\Cache\\Cache' => $baseDir . '/Framework/Utils/Cache/Cache.php',
216
- 'WPStaging\\Framework\\Utils\\FileSystem' => $baseDir . '/Framework/Utils/FileSystem.php',
217
  'WPStaging\\Framework\\Utils\\Size' => $baseDir . '/Framework/Utils/Size.php',
218
  'WPStaging\\Framework\\Utils\\Strings' => $baseDir . '/Framework/Utils/Strings.php',
219
  'WPStaging\\Framework\\Utils\\Urls' => $baseDir . '/Framework/Utils/Urls.php',
@@ -221,5 +221,4 @@ return array(
221
  'WPStaging\\Frontend\\Frontend' => $baseDir . '/Frontend/Frontend.php',
222
  'WPStaging\\Frontend\\LoginForm' => $baseDir . '/Frontend/LoginForm.php',
223
  'WPStaging\\Frontend\\LoginNotice' => $baseDir . '/Frontend/LoginNotice.php',
224
- 'WPStaging\\Repository\\SettingsRepository' => $baseDir . '/Repository/SettingsRepository.php',
225
  );
12
  'WPStaging\\Backend\\Feedback\\Feedback' => $baseDir . '/Backend/Feedback/Feedback.php',
13
  'WPStaging\\Backend\\Modules\\Jobs\\Cancel' => $baseDir . '/Backend/Modules/Jobs/Cancel.php',
14
  'WPStaging\\Backend\\Modules\\Jobs\\CancelUpdate' => $baseDir . '/Backend/Modules/Jobs/CancelUpdate.php',
15
+ 'WPStaging\\Backend\\Modules\\Jobs\\Cleaners\\WpContentCleaner' => $baseDir . '/Backend/Modules/Jobs/Cleaners/WpContentCleaner.php',
16
  'WPStaging\\Backend\\Modules\\Jobs\\Cloning' => $baseDir . '/Backend/Modules/Jobs/Cloning.php',
17
  'WPStaging\\Backend\\Modules\\Jobs\\CloningProcess' => $baseDir . '/Backend/Modules/Jobs/CloningProcess.php',
18
  'WPStaging\\Backend\\Modules\\Jobs\\Data' => $baseDir . '/Backend/Modules/Jobs/Data.php',
42
  'WPStaging\\Backend\\Modules\\SystemInfo' => $baseDir . '/Backend/Modules/SystemInfo.php',
43
  'WPStaging\\Backend\\Modules\\Views\\Forms\\Settings' => $baseDir . '/Backend/Modules/Views/Forms/Settings.php',
44
  'WPStaging\\Backend\\Modules\\Views\\Tabs\\Tabs' => $baseDir . '/Backend/Modules/Views/Tabs/Tabs.php',
45
+ 'WPStaging\\Backend\\Notices\\BooleanNotice' => $baseDir . '/Backend/Notices/BooleanNotice.php',
46
  'WPStaging\\Backend\\Notices\\DisabledCacheNotice' => $baseDir . '/Backend/Notices/DisabledCacheNotice.php',
47
  'WPStaging\\Backend\\Notices\\Notices' => $baseDir . '/Backend/Notices/Notices.php',
48
  'WPStaging\\Backend\\Optimizer\\Optimizer' => $baseDir . '/Backend/Optimizer/Optimizer.php',
49
  'WPStaging\\Backend\\Pluginmeta\\Meta' => $baseDir . '/Backend/Pluginmeta/Meta.php',
50
  'WPStaging\\Backend\\Pluginmeta\\Pluginmeta' => $baseDir . '/Backend/Pluginmeta/Pluginmeta.php',
51
  'WPStaging\\Backend\\Upgrade\\Upgrade' => $baseDir . '/Backend/Upgrade/Upgrade.php',
 
52
  'WPStaging\\Command\\Database\\Export\\ExportCommand' => $baseDir . '/Command/Database/Export/ExportCommand.php',
53
  'WPStaging\\Command\\Database\\Export\\ExportDto' => $baseDir . '/Command/Database/Export/ExportDto.php',
54
  'WPStaging\\Command\\Database\\Export\\ExportException' => $baseDir . '/Command/Database/Export/ExportException.php',
101
  'WPStaging\\Core\\Utils\\Browser' => $baseDir . '/Core/Utils/Browser.php',
102
  'WPStaging\\Core\\Utils\\Cache' => $baseDir . '/Core/Utils/Cache.php',
103
  'WPStaging\\Core\\Utils\\Directories' => $baseDir . '/Core/Utils/Directories.php',
 
104
  'WPStaging\\Core\\Utils\\Hash' => $baseDir . '/Core/Utils/Hash.php',
105
  'WPStaging\\Core\\Utils\\Helper' => $baseDir . '/Core/Utils/Helper.php',
106
  'WPStaging\\Core\\Utils\\Htaccess' => $baseDir . '/Core/Utils/Htaccess.php',
114
  'WPStaging\\Core\\Utils\\Strings' => $baseDir . '/Core/Utils/Strings.php',
115
  'WPStaging\\Core\\WPStaging' => $baseDir . '/Core/WPStaging.php',
116
  'WPStaging\\Core\\thirdParty\\thirdPartyCompatibility' => $baseDir . '/Core/thirdParty/thirdPartyCompatibility.php',
 
 
 
117
  'WPStaging\\Framework\\Adapter\\Database' => $baseDir . '/Framework/Adapter/Database.php',
118
  'WPStaging\\Framework\\Adapter\\Database\\AbstractDatabase' => $baseDir . '/Framework/Adapter/Database/AbstractDatabase.php',
119
  'WPStaging\\Framework\\Adapter\\Database\\DatabaseException' => $baseDir . '/Framework/Adapter/Database/DatabaseException.php',
164
  'WPStaging\\Framework\\Entity\\AbstractEntity' => $baseDir . '/Framework/Entity/AbstractEntity.php',
165
  'WPStaging\\Framework\\Entity\\EntityException' => $baseDir . '/Framework/Entity/EntityException.php',
166
  'WPStaging\\Framework\\Entity\\IdentifyableEntityInterface' => $baseDir . '/Framework/Entity/IdentifyableEntityInterface.php',
167
+ 'WPStaging\\Framework\\Filesystem\\DirectoryListing' => $baseDir . '/Framework/Filesystem/DirectoryListing.php',
168
  'WPStaging\\Framework\\Filesystem\\DirectoryScanner' => $baseDir . '/Framework/Filesystem/DirectoryScanner.php',
169
+ 'WPStaging\\Framework\\Filesystem\\DirectoryScannerControl' => $baseDir . '/Framework/Filesystem/DirectoryScannerControl.php',
170
  'WPStaging\\Framework\\Filesystem\\DiskFullException' => $baseDir . '/Framework/Filesystem/DiskFullException.php',
171
  'WPStaging\\Framework\\Filesystem\\File' => $baseDir . '/Framework/Filesystem/File.php',
172
  'WPStaging\\Framework\\Filesystem\\FileScanner' => $baseDir . '/Framework/Filesystem/FileScanner.php',
173
+ 'WPStaging\\Framework\\Filesystem\\FileScannerControl' => $baseDir . '/Framework/Filesystem/FileScannerControl.php',
174
  'WPStaging\\Framework\\Filesystem\\Filesystem' => $baseDir . '/Framework/Filesystem/Filesystem.php',
175
  'WPStaging\\Framework\\Filesystem\\FilesystemExceptions' => $baseDir . '/Framework/Filesystem/FilesystemExceptions.php',
176
  'WPStaging\\Framework\\Filesystem\\FilterableDirectoryIterator' => $baseDir . '/Framework/Filesystem/FilterableDirectoryIterator.php',
178
  'WPStaging\\Framework\\Filesystem\\Filters\\ExtensionExcludeFilter' => $baseDir . '/Framework/Filesystem/Filters/ExtensionExcludeFilter.php',
179
  'WPStaging\\Framework\\Filesystem\\Filters\\PathExcludeFilter' => $baseDir . '/Framework/Filesystem/Filters/PathExcludeFilter.php',
180
  'WPStaging\\Framework\\Filesystem\\Filters\\RecursiveExtensionExcludeFilter' => $baseDir . '/Framework/Filesystem/Filters/RecursiveExtensionExcludeFilter.php',
181
+ 'WPStaging\\Framework\\Filesystem\\Permissions' => $baseDir . '/Framework/Filesystem/Permissions.php',
182
  'WPStaging\\Framework\\Filesystem\\WpUploadsFolderSymlinker' => $baseDir . '/Framework/Filesystem/WpUploadsFolderSymlinker.php',
183
  'WPStaging\\Framework\\Interfaces\\ArrayableInterface' => $baseDir . '/Framework/Interfaces/ArrayableInterface.php',
184
  'WPStaging\\Framework\\Interfaces\\HydrateableInterface' => $baseDir . '/Framework/Interfaces/HydrateableInterface.php',
195
  'WPStaging\\Framework\\Queue\\Storage\\QueueStorageException' => $baseDir . '/Framework/Queue/Storage/QueueStorageException.php',
196
  'WPStaging\\Framework\\Queue\\Storage\\StorageInterface' => $baseDir . '/Framework/Queue/Storage/StorageInterface.php',
197
  'WPStaging\\Framework\\Security\\AccessToken' => $baseDir . '/Framework/Security/AccessToken.php',
198
+ 'WPStaging\\Framework\\Security\\Auth' => $baseDir . '/Framework/Security/Auth.php',
199
  'WPStaging\\Framework\\Security\\Capabilities' => $baseDir . '/Framework/Security/Capabilities.php',
200
  'WPStaging\\Framework\\Security\\Nonce' => $baseDir . '/Framework/Security/Nonce.php',
201
  'WPStaging\\Framework\\SiteInfo' => $baseDir . '/Framework/SiteInfo.php',
204
  'WPStaging\\Framework\\TemplateEngine\\TemplateEngineException' => $baseDir . '/Framework/TemplateEngine/TemplateEngineException.php',
205
  'WPStaging\\Framework\\TemplateEngine\\TemplateEngineInterface' => $baseDir . '/Framework/TemplateEngine/TemplateEngineInterface.php',
206
  'WPStaging\\Framework\\Traits\\ArrayableTrait' => $baseDir . '/Framework/Traits/ArrayableTrait.php',
207
+ 'WPStaging\\Framework\\Traits\\BenchmarkTrait' => $baseDir . '/Framework/Traits/BenchmarkTrait.php',
208
  'WPStaging\\Framework\\Traits\\BooleanTransientTrait' => $baseDir . '/Framework/Traits/BooleanTransientTrait.php',
209
  'WPStaging\\Framework\\Traits\\HydrateTrait' => $baseDir . '/Framework/Traits/HydrateTrait.php',
210
  'WPStaging\\Framework\\Traits\\MaintenanceTrait' => $baseDir . '/Framework/Traits/MaintenanceTrait.php',
214
  'WPStaging\\Framework\\Utils\\Cache\\AbstractCache' => $baseDir . '/Framework/Utils/Cache/AbstractCache.php',
215
  'WPStaging\\Framework\\Utils\\Cache\\BufferedCache' => $baseDir . '/Framework/Utils/Cache/BufferedCache.php',
216
  'WPStaging\\Framework\\Utils\\Cache\\Cache' => $baseDir . '/Framework/Utils/Cache/Cache.php',
 
217
  'WPStaging\\Framework\\Utils\\Size' => $baseDir . '/Framework/Utils/Size.php',
218
  'WPStaging\\Framework\\Utils\\Strings' => $baseDir . '/Framework/Utils/Strings.php',
219
  'WPStaging\\Framework\\Utils\\Urls' => $baseDir . '/Framework/Utils/Urls.php',
221
  'WPStaging\\Frontend\\Frontend' => $baseDir . '/Frontend/Frontend.php',
222
  'WPStaging\\Frontend\\LoginForm' => $baseDir . '/Frontend/LoginForm.php',
223
  'WPStaging\\Frontend\\LoginNotice' => $baseDir . '/Frontend/LoginNotice.php',
 
224
  );
wp-staging.php CHANGED
@@ -7,7 +7,7 @@
7
  * Author: WP-STAGING
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi
10
- * Version: 2.8.0
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
  *
@@ -24,59 +24,28 @@
24
  * You should have received a copy of the GNU General Public License
25
  * along with Staging. If not, see <http://www.gnu.org/licenses/>.
26
  *
27
- * @package WPSTG
28
  * @category Development, Migrating, Staging
29
- * @author WP STAGING
30
  */
31
 
32
- namespace WPStaging\Bootstrap\V1;
33
-
34
  if (!defined("WPINC")) {
35
  die;
36
  }
37
 
38
- if (!defined('WPSTG_FREE_LOADED')) {
39
- define('WPSTG_FREE_LOADED', __FILE__);
40
- }
41
-
42
- require_once __DIR__ . '/Bootstrap/V1/Requirements/WpstgFreeRequirements.php';
43
- require_once __DIR__ . '/Bootstrap/V1/WpstgBootstrap.php';
44
-
45
- if (!class_exists(WpstgFreeBootstrap::class)) {
46
- class WpstgFreeBootstrap extends WpstgBootstrap
47
- {
48
- protected function afterBootstrap()
49
  {
50
- if (!defined('WPSTG_PLUGIN_FILE')) {
51
- define('WPSTG_PLUGIN_FILE', __FILE__);
52
- }
53
-
54
- // WP STAGING version number
55
- if (!defined('WPSTG_VERSION')) {
56
- define('WPSTG_VERSION', '2.8.0');
57
- }
58
-
59
- // Compatible up to WordPress Version
60
- if (!defined('WPSTG_COMPATIBLE')) {
61
- define('WPSTG_COMPATIBLE', '5.6');
62
- }
63
-
64
- require_once __DIR__ . '/constants.php';
65
-
66
- require_once(__DIR__ . '/_init.php');
67
  }
68
  }
69
- }
70
-
71
- $bootstrap = new WpstgFreeBootstrap(__DIR__, new WpstgFreeRequirements(__FILE__));
72
-
73
- add_action('plugins_loaded', [$bootstrap, 'checkRequirements'], 5);
74
- add_action('plugins_loaded', [$bootstrap, 'bootstrap'], 10);
75
-
76
- /** Installation Hooks */
77
- if (!class_exists('WPStaging\Install')) {
78
- require_once __DIR__ . "/install.php";
79
-
80
- $install = new \WPStaging\Install($bootstrap);
81
- register_activation_hook(__FILE__, [$install, 'activation']);
82
  }
7
  * Author: WP-STAGING
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi
10
+ * Version: 2.8.1
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
  *
24
  * You should have received a copy of the GNU General Public License
25
  * along with Staging. If not, see <http://www.gnu.org/licenses/>.
26
  *
27
+ * @package WPSTG
28
  * @category Development, Migrating, Staging
29
+ * @author WP STAGING
30
  */
31
 
 
 
32
  if (!defined("WPINC")) {
33
  die;
34
  }
35
 
36
+ if (version_compare(phpversion(), '5.5.0', '>=')) {
37
+ // The absolute path to the main file of this plugin.
38
+ $pluginFilePath = __FILE__;
39
+ include_once dirname(__FILE__) . '/freeBootstrap.php';
40
+ } else {
41
+ if (!function_exists('wpstg_unsupported_php_version')) {
42
+ function wpstg_unsupported_php_version()
 
 
 
 
43
  {
44
+ echo '<div class="notice-warning notice is-dismissible">';
45
+ echo '<p style="font-weight: bold;">' . esc_html__('WP STAGING') . '</p>';
46
+ echo '<p>' . esc_html__(sprintf('WPSTAGING requires PHP %s or higher. Your site is running an outdated version of PHP (%s), which requires an update.', '5.5', phpversion()), 'wp-staging') . '</p>';
47
+ echo '</div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
  }
50
+ add_action('admin_notices', 'wpstg_unsupported_php_version');
 
 
 
 
 
 
 
 
 
 
 
 
51
  }