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

Version Description

  • Feat: Compatible up to WP 5.7
  • Feat: Check database connection in clone data edit #650
  • Feat: Exclude .wp-staging-cloneable file from cloning and update #718
  • Feat: Show notice if a user is using an outdated version of WP Staging Hooks plugin #716
  • Feat: Add single disabled items notice with better message #717
  • Feat: Add options to enable/disable staging sites cloning from UI #722
  • Enh: Use included directories instead of excluded directories to increase cloning speed #671
  • Enh: Stringify directories array var in $_POST to reduce $_POST size during cloning #671
  • Enh: Replace relative paths exclude to absolute and wildcard paths exclude during cloning #671
  • Enh: Detect snapshot tables using regex #694
  • Enh: Enable disable save button in clone data edit during database connection #700
  • Enh: Improve exclude filters for Push process #720
  • Enh: Move Backend/public/img to assets/img #719
  • Enh: Unify Single and Multisite Classes #713
  • Enh: Keep other staging behavior when the staging site is cloneable #722
  • Enh: Refactor search and replace jobs to use memory and time-consumption aware trait #702
  • Fix: Lost password link generation in staging sites #697
  • Fix: Fix cloning on multisite for PHP 5 #725
  • Fix: Skip symlink scanning during directory scan #736
  • Fix: Replace deprecated jQuery click method #730
  • Fix: Fix overlapping of sweetalert confirmation on push with sidebar #742
  • Fix: Exclude wp staging content folder during staging #741
  • Fix: Add sanitizing for path to fix comparing for Windows paths #751
  • Fix: Uninstall not possible if "delete all settings is activated" #756
  • Dev: Internal refactoring of database backup to avoid long-lived branch #624
  • Dev: Enforced changelog entries in CI #695
  • Dev: Refactored webdriver tests to make them faster #656
  • Dev: Refactor how the automated test workflows are generated. Add new webdriver test before release with default settings #712
  • Dev: Increased default file batch limits for faster development environment and CI #706
  • Dev: Renamed all code and UI references of Snapshot to Backup #715
  • Dev: Add helper to manage clone settings #717
  • Dev: Internal code refactoring, renaming classes for better readability #721
  • Dev: Add infrastructure support for wp-cli and background processing #728
  • Dev: Update php-scoper and other development dependencies #744
  • Dev: Build javascript when building the distributable version of the plugin #750
Download this release

Release Info

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

Code changes from version 2.8.1 to 2.8.2

Files changed (296) hide show
  1. Backend/Activation/Welcome.php +0 -2
  2. Backend/Administrator.php +187 -39
  3. Backend/Feedback/Feedback.php +24 -22
  4. Backend/Modules/Jobs/Cancel.php +47 -43
  5. Backend/Modules/Jobs/CancelUpdate.php +13 -10
  6. Backend/Modules/Jobs/Cleaners/WpContentCleaner.php +13 -12
  7. Backend/Modules/Jobs/Cloning.php +26 -44
  8. Backend/Modules/Jobs/CloningProcess.php +1 -18
  9. Backend/Modules/Jobs/Data.php +1 -2
  10. Backend/Modules/Jobs/Database.php +79 -14
  11. Backend/Modules/Jobs/Delete.php +145 -138
  12. Backend/Modules/Jobs/Directories.php +128 -135
  13. Backend/Modules/Jobs/Exceptions/CloneNotFoundException.php +2 -1
  14. Backend/Modules/Jobs/Exceptions/FatalException.php +1 -3
  15. Backend/Modules/Jobs/Exceptions/JobNotFoundException.php +1 -0
  16. Backend/Modules/Jobs/Files.php +96 -16
  17. Backend/Modules/Jobs/Finish.php +44 -33
  18. Backend/Modules/Jobs/Interfaces/JobInterface.php +0 -22
  19. Backend/Modules/Jobs/Job.php +48 -36
  20. Backend/Modules/Jobs/JobExecutable.php +18 -12
  21. Backend/Modules/Jobs/Logs.php +3 -3
  22. Backend/Modules/Jobs/Multisite/Directories.php +0 -606
  23. Backend/Modules/Jobs/Multisite/Files.php +0 -527
  24. Backend/Modules/Jobs/Multisite/Finish.php +0 -135
  25. Backend/Modules/Jobs/PreserveDataFirstStep.php +11 -10
  26. Backend/Modules/Jobs/PreserveDataSecondStep.php +10 -9
  27. Backend/Modules/Jobs/ProcessLock.php +15 -14
  28. Backend/Modules/Jobs/Scan.php +172 -122
  29. Backend/Modules/Jobs/SearchReplace.php +86 -23
  30. Backend/Modules/Jobs/TotalStepsAreNumberOfTables.php +1 -3
  31. Backend/Modules/Jobs/Updating.php +124 -101
  32. Backend/Modules/Jobs/Verify.php +19 -11
  33. Backend/Modules/SystemInfo.php +13 -12
  34. Backend/Modules/Views/Forms/Settings.php +215 -193
  35. Backend/Modules/Views/Tabs/Tabs.php +3 -3
  36. Backend/Notices/BooleanNotice.php +9 -9
  37. Backend/Notices/DisabledCacheNotice.php +0 -23
  38. Backend/Notices/DisabledItemsNotice.php +41 -0
  39. Backend/Notices/Notices.php +66 -19
  40. Backend/Optimizer/Optimizer.php +51 -46
  41. Backend/Optimizer/blank-theme/functions.php +2 -3
  42. Backend/Optimizer/wp-staging-optimizer.php +37 -11
  43. Backend/Pluginmeta/Meta.php +15 -13
  44. Backend/Pluginmeta/Pluginmeta.php +16 -15
  45. Backend/helpers/wp-config.php +14 -12
  46. Backend/public/js/wpstg-admin-beta.js +0 -22
  47. Backend/public/js/wpstg-admin-cache-notice.js +0 -32
  48. Backend/public/js/wpstg-admin-plugins.js +0 -80
  49. Backend/public/js/wpstg-admin-poll.js +0 -23
  50. Backend/public/js/wpstg-admin-rating.js +0 -163
  51. Backend/public/js/wpstg-admin.js +0 -3078
  52. Backend/public/vendor/sweetalert2/LICENSE +0 -22
  53. Backend/views/_main/footer.php +2 -2
  54. Backend/views/_main/header.php +4 -3
  55. Backend/views/_main/report-issue.php +4 -4
  56. Backend/views/backup/listing-single-backup.php +116 -0
  57. Backend/views/backup/listing.php +58 -0
  58. Backend/views/backup/modal/download.php +7 -0
  59. Backend/views/backup/modal/export.php +68 -0
  60. Backend/views/backup/modal/import.php +20 -0
  61. Backend/views/backup/modal/partials/import-configure.php +33 -0
  62. Backend/views/backup/modal/partials/import-filesystem.php +17 -0
  63. Backend/views/backup/modal/partials/import-upload.php +59 -0
  64. Backend/views/backup/modal/progress.php +28 -0
  65. Backend/views/backup/site/info.php +59 -0
  66. Backend/views/clone/ajax/custom-directory.php +8 -6
  67. Backend/views/clone/ajax/delete-confirmation.php +12 -11
  68. Backend/views/clone/ajax/external-database.php +2 -0
  69. Backend/views/clone/ajax/process-lock.php +1 -1
  70. Backend/views/clone/ajax/scan.php +36 -29
  71. Backend/views/clone/ajax/single-overview.php +69 -50
  72. Backend/views/clone/ajax/start.php +3 -3
  73. Backend/views/clone/ajax/update.php +9 -2
  74. Backend/views/clone/index.php +18 -7
  75. Backend/views/clone/single-site/index.php +6 -6
  76. Backend/views/clone/staging-site/index.php +6 -2
  77. Backend/views/database/legacy/confirm-delete.php +31 -0
  78. Backend/views/database/legacy/confirm-restore.php +114 -0
  79. Backend/views/database/legacy/listing.php +331 -0
  80. Backend/views/feedback/deactivate-feedback.php +20 -20
  81. Backend/views/notices/beta.php +1 -1
  82. Backend/views/notices/disabled-cache.php +0 -32
  83. Backend/views/notices/disabled-items-notice.php +62 -0
  84. Backend/views/notices/low-memory-limit.php +1 -1
  85. Backend/views/notices/no-license-key.php +5 -4
  86. Backend/views/notices/outdated-wp-staging-hooks.php +12 -0
  87. Backend/views/notices/poll.php +1 -1
  88. Backend/views/notices/rating.php +3 -16
  89. Backend/views/notices/staging-directory-permission-problem.php +2 -2
  90. Backend/views/notices/transient.php +1 -1
  91. Backend/views/notices/wp-version-compatible-message.php +4 -3
  92. Backend/views/notices/wrong-scheme.php +3 -3
  93. Backend/views/settings/main-settings.php +2 -1
  94. Backend/views/settings/tabs/general.php +17 -2
  95. Backend/views/settings/tabs/mail-settings.php +1 -1
  96. Backend/views/tools/index.php +1 -1
  97. Backend/views/tools/tabs/import_export.php +2 -2
  98. Backend/views/tools/tabs/system_info.php +1 -1
  99. Backend/views/welcome/welcome.php +8 -8
  100. Command/.gitkeep +0 -0
  101. Command/Database/Export/ExportCommand.php +0 -86
  102. Command/Database/Export/ExportDto.php +0 -261
  103. Command/Database/Export/ExportException.php +0 -13
  104. Command/Database/Snapshot/AbstractSnapshotCommand.php +0 -71
  105. Command/Database/Snapshot/CreateSnapshotCommand.php +0 -101
  106. Command/Database/Snapshot/SnapshotCommandException.php +0 -8
  107. Command/Database/Snapshot/SnapshotDto.php +0 -126
  108. Command/Database/Snapshot/SnapshotHandler.php +0 -48
  109. Command/Database/Snapshot/UpdateSnapshotCommand.php +0 -55
  110. Command/Database/SnapshotFactory.php +0 -55
  111. Component/Dto/AbstractDto.php +0 -41
  112. Component/Dto/AbstractRequestDto.php +0 -33
  113. Component/Dto/InitiableDtoInterface.php +0 -20
  114. Component/Dto/StepsDto.php +0 -77
  115. Component/Job/AbstractJob.php +0 -83
  116. Component/Job/AbstractQueueJob.php +0 -207
  117. Component/Job/JobInterface.php +0 -13
  118. Component/Job/ProcessLock.php +0 -52
  119. Component/Job/QueueJobDto.php +0 -103
  120. Component/Task/AbstractTask.php +0 -232
  121. Component/Task/Database/RenameTablesRequestDto.php +0 -50
  122. Component/Task/Database/RenameTablesTask.php +0 -120
  123. Component/Task/Filesystem/DirectoryScannerRequestDto.php +0 -46
  124. Component/Task/Filesystem/DirectoryScannerTask.php +0 -221
  125. Component/Task/Filesystem/FileScannerRequestDto.php +0 -59
  126. Component/Task/Filesystem/FileScannerTask.php +0 -222
  127. Component/Task/TaskInterface.php +0 -54
  128. Component/Task/TaskResponseDto.php +0 -210
  129. Core/Cron/Cron.php +0 -1
  130. Core/DTO/Settings.php +3 -4
  131. Core/Forms/Elements.php +9 -16
  132. Core/Forms/Elements/Check.php +4 -7
  133. Core/Forms/Elements/Date.php +1 -0
  134. Core/Forms/Elements/DateTime.php +1 -0
  135. Core/Forms/Elements/Email.php +1 -0
  136. Core/Forms/Elements/File.php +1 -0
  137. Core/Forms/Elements/Hidden.php +1 -1
  138. Core/Forms/Elements/Interfaces/InterfaceElement.php +1 -0
  139. Core/Forms/Elements/Interfaces/InterfaceElementWithOptions.php +1 -0
  140. Core/Forms/Elements/Numerical.php +1 -0
  141. Core/Forms/Elements/Password.php +1 -0
  142. Core/Forms/Elements/Radio.php +2 -3
  143. Core/Forms/Elements/Select.php +7 -8
  144. Core/Forms/Elements/SelectMultiple.php +8 -9
  145. Core/Forms/Elements/Text.php +1 -0
  146. Core/Forms/Elements/TextArea.php +1 -0
  147. Core/Forms/ElementsWithOptions.php +3 -4
  148. Core/Forms/Form.php +4 -7
  149. Core/Iterators/RecursiveDirectoryIterator.php +52 -44
  150. Core/Iterators/RecursiveFilterExclude.php +4 -3
  151. Core/Utils/Cache.php +45 -40
  152. Core/Utils/Directories.php +6 -10
  153. Core/Utils/Helper.php +36 -34
  154. Core/Utils/Htaccess.php +16 -14
  155. Core/Utils/IISWebConfig.php +10 -8
  156. Core/Utils/Info.php +4 -6
  157. Core/Utils/Logger.php +16 -25
  158. Core/Utils/Multisite.php +27 -22
  159. Core/Utils/MySQL.php +33 -23
  160. Core/Utils/MySQLi.php +31 -21
  161. Core/Utils/RobotsTxt.php +12 -11
  162. Core/Utils/Strings.php +20 -15
  163. Core/Utils/functions.php +7 -7
  164. Core/WPStaging.php +92 -316
  165. Framework/Adapter/Database.php +30 -1
  166. Framework/Adapter/Database/DatabaseQueryDto.php +0 -1
  167. Framework/Adapter/Database/InterfaceDatabase.php +0 -1
  168. Framework/Adapter/Database/InterfaceDatabaseClient.php +1 -0
  169. Framework/Adapter/Database/MysqlAdapter.php +1 -1
  170. Framework/Adapter/Database/MysqliAdapter.php +1 -1
  171. Framework/Adapter/Database/WpDbAdapter.php +3 -4
  172. Framework/Adapter/DateTimeAdapter.php +1 -1
  173. Framework/Adapter/Directory.php +1 -1
  174. Framework/Adapter/Maintenance.php +2 -2
  175. Framework/Adapter/SourceDatabase.php +0 -1
  176. Framework/AssetServiceProvider.php +21 -0
  177. Framework/Assets/Assets.php +321 -0
  178. Framework/CloningProcess/CloningDto.php +0 -3
  179. Framework/CloningProcess/Data/CloningService.php +0 -2
  180. Framework/CloningProcess/Data/CopyWpConfig.php +0 -2
  181. Framework/CloningProcess/Data/DBCloningService.php +1 -2
  182. Framework/CloningProcess/Data/DataCloningDto.php +0 -2
  183. Framework/CloningProcess/Data/FileCloningService.php +0 -2
  184. Framework/CloningProcess/Data/MultisiteAddNetworkAdministrators.php +0 -2
  185. Framework/CloningProcess/Data/MultisiteUpdateActivePlugins.php +0 -2
  186. Framework/CloningProcess/Data/MultisiteUpdateTablePrefix.php +0 -2
  187. Framework/CloningProcess/Data/ResetIndexPhp.php +0 -2
  188. Framework/CloningProcess/Data/UpdateSiteUrlAndHome.php +0 -2
  189. Framework/CloningProcess/Data/UpdateStagingOptionsTable.php +13 -8
  190. Framework/CloningProcess/Data/UpdateTablePrefix.php +0 -2
  191. Framework/CloningProcess/Data/UpdateWpConfigConstants.php +1 -4
  192. Framework/CloningProcess/Data/UpdateWpConfigTablePrefix.php +0 -2
  193. Framework/CloningProcess/Data/UpdateWpOptionsTablePrefix.php +0 -2
  194. Framework/CloningProcess/Database/DatabaseCloningService.php +2 -5
  195. Framework/CloningProcess/ExcludedPlugins.php +116 -0
  196. Framework/CloningProcess/SearchReplace/SearchReplaceService.php +1 -3
  197. Framework/Collection/Collection.php +1 -1
  198. Framework/Collection/OptionCollection.php +0 -72
  199. Framework/Component/AbstractTemplateComponent.php +2 -2
  200. Framework/DI/Container.php +6 -0
  201. Framework/DI/FeatureProviderInterface.php +50 -0
  202. Framework/DI/FeatureServiceProvider.php +64 -0
  203. Framework/DI/ServiceProvider.php +9 -3
  204. Framework/Database/DatabaseDumper.php +0 -635
  205. Framework/Database/DatabaseRestore.php +0 -438
  206. Framework/Database/DbInfo.php +1 -1
  207. Framework/Database/LegacyDatabaseInfo.php +24 -0
  208. Framework/Database/TableDto.php +4 -5
  209. Framework/Database/TableService.php +108 -2
  210. Framework/Database/WpDbInfo.php +1 -1
  211. Framework/Database/iDbInfo.php +1 -3
  212. Framework/Dto/AbstractDto.php +0 -22
  213. Framework/Entity/AbstractEntity.php +0 -42
  214. Framework/Entity/EntityException.php +0 -12
  215. Framework/Entity/IdentifyableEntityInterface.php +0 -13
  216. Framework/Filesystem/DirectoryScanner.php +19 -22
  217. Framework/Filesystem/DirectoryScannerControl.php +2 -2
  218. Framework/Filesystem/File.php +1 -1
  219. Framework/Filesystem/FileScanner.php +64 -8
  220. Framework/Filesystem/FileScannerControl.php +6 -4
  221. Framework/Filesystem/Filesystem.php +42 -41
  222. Framework/Filesystem/FilesystemExceptions.php +0 -2
  223. Framework/Filesystem/FilterableDirectoryIterator.php +31 -73
  224. Framework/Filesystem/Filters/DirectoryDotFilter.php +3 -3
  225. Framework/Filesystem/Filters/ExtensionExcludeFilter.php +0 -25
  226. Framework/Filesystem/Filters/PathExcludeFilter.php +12 -18
  227. Framework/Filesystem/Filters/RecursiveExtensionExcludeFilter.php +0 -34
  228. Framework/Filesystem/Filters/RecursivePathExcludeFilter.php +33 -0
  229. Framework/Filesystem/Permissions.php +1 -5
  230. Framework/Filesystem/WpUploadsFolderSymlinker.php +5 -5
  231. Framework/Interfaces/TransientInterface.php +3 -3
  232. Framework/Mails/Report/Report.php +8 -9
  233. Framework/Mails/Report/ReportSubmitTransient.php +2 -2
  234. Framework/Queue/FinishedQueueException.php +2 -1
  235. Framework/Queue/Storage/ArrayStorage.php +5 -0
  236. Framework/Queue/Storage/BufferedCacheStorage.php +10 -1
  237. Framework/Queue/Storage/CacheStorage.php +14 -2
  238. Framework/Queue/Storage/StorageInterface.php +7 -0
  239. Framework/Security/AccessToken.php +2 -2
  240. Framework/Security/Auth.php +2 -3
  241. Framework/SiteInfo.php +105 -5
  242. Framework/Staging/CloneOptions.php +114 -0
  243. Framework/Staging/FirstRun.php +4 -10
  244. Framework/TemplateEngine/TemplateEngine.php +3 -2
  245. Framework/TemplateEngine/TemplateEngineException.php +1 -0
  246. Framework/TemplateEngine/TemplateEngineInterface.php +1 -0
  247. Framework/Traits/ArrayableTrait.php +1 -1
  248. Framework/Traits/BenchmarkTrait.php +7 -3
  249. Framework/Traits/BooleanTransientTrait.php +3 -3
  250. Framework/Traits/DbRowsGeneratorTrait.php +89 -0
  251. Framework/Traits/ExcludeFilterTrait.php +141 -0
  252. Framework/Traits/FileScanToCacheTrait.php +113 -0
  253. Framework/Traits/HydrateTrait.php +4 -4
  254. Framework/Traits/MaintenanceTrait.php +2 -2
  255. Framework/Traits/RequestNotationTrait.php +0 -46
  256. Framework/Traits/ResourceTrait.php +2 -4
  257. Framework/Traits/TimerTrait.php +10 -3
  258. Framework/Utils/Cache/AbstractCache.php +11 -1
  259. Framework/Utils/Cache/BufferedCache.php +5 -5
  260. Framework/Utils/Cache/Cache.php +6 -5
  261. Framework/Utils/Size.php +0 -27
  262. Framework/Utils/Strings.php +30 -12
  263. Framework/Utils/Urls.php +24 -21
  264. Framework/Utils/WpDefaultDirectories.php +213 -8
  265. Frontend/Frontend.php +6 -24
  266. Frontend/LoginForm.php +39 -23
  267. Frontend/LoginNotice.php +0 -1
  268. Frontend/public/css/wpstg-login-ui.css +0 -156
  269. Frontend/views/header.php +158 -2
  270. Frontend/views/loginForm.php +12 -12
  271. {Backend/public/css → assets/css/src/frontend}/wpstg-admin-bar.css +0 -0
  272. {Frontend/public/css → assets/css/src}/wpstg-admin-bar.css +0 -0
  273. {Backend/public/css → assets/css/src}/wpstg-admin-feedback.css +0 -0
  274. {Backend/public/css → assets/css/src}/wpstg-admin.css +286 -72
  275. assets/css/vendor/notyf.min.css +1 -0
  276. Backend/public/vendor/sweetalert2/wordpress-admin.css → assets/css/vendor/sweetalert2.css +0 -0
  277. Backend/public/vendor/sweetalert2/wordpress-admin.min.css → assets/css/vendor/sweetalert2.min.css +0 -0
  278. {Backend/public → assets}/img/admin_dashboard.png +0 -0
  279. {Backend/public → assets}/img/contribute.txt +0 -0
  280. {Backend/public → assets}/img/loading.gif +0 -0
  281. {Backend/public → assets}/img/loading.svg +0 -0
  282. {Backend/public → assets}/img/loading_v1.gif +0 -0
  283. {Backend/public → assets}/img/logo_clean_small_212_25.png +0 -0
  284. {Backend/public → assets}/img/tail-spin.svg +0 -0
  285. {Backend/public → assets}/img/thumbnail.jpg +0 -0
  286. {Backend/public → assets}/img/upload.svg +0 -0
  287. {Backend/public → assets}/img/wpstaging-banner200x400-tryout.gif +0 -0
  288. {Backend/public → assets}/img/wpstaging-banner200x400.gif +0 -0
  289. assets/js/dist/wpstg-admin-beta.js +15 -0
  290. assets/js/dist/wpstg-admin-plugins.js +77 -0
  291. assets/js/dist/wpstg-admin-poll.js +15 -0
  292. assets/js/dist/wpstg-admin-rating.js +153 -0
  293. assets/js/dist/wpstg-admin.js +2837 -0
  294. assets/js/dist/wpstg-clone-staging.js +93 -0
  295. assets/js/dist/wpstg-dom-utils.js +26 -0
  296. assets/js/dist/wpstg-legacy-database.js +1243 -0
Backend/Activation/Welcome.php CHANGED
@@ -42,6 +42,4 @@ class Welcome
42
  wp_safe_redirect(admin_url('admin.php?page=wpstg-welcome'));
43
  exit;
44
  }
45
-
46
-
47
  }
42
  wp_safe_redirect(admin_url('admin.php?page=wpstg-welcome'));
43
  exit;
44
  }
 
 
45
  }
Backend/Administrator.php CHANGED
@@ -8,7 +8,10 @@ if (!defined("WPINC")) {
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;
@@ -25,12 +28,13 @@ use WPStaging\Backend\Modules\Jobs\ProcessLock;
25
  use WPStaging\Backend\Modules\SystemInfo;
26
  use WPStaging\Backend\Modules\Views\Tabs\Tabs;
27
  use WPStaging\Backend\Notices\Notices;
28
- use WPStaging\Backend\Notices\DisabledCacheNotice;
29
  use WPStaging\Backend\Modules\Views\Forms\Settings as FormSettings;
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
  /**
@@ -52,20 +56,26 @@ class Administrator
52
 
53
 
54
  /**
 
55
  * @var string
56
  */
57
  private $path;
58
 
59
  /**
60
- * @var string
61
  */
62
- private $url;
63
 
64
  /**
65
  * @var Auth
66
  */
67
  private $auth;
68
 
 
 
 
 
 
69
  /**
70
  * @var array
71
  * All options here will only be stored in the database as integers. Decimal points and separators will be removed
@@ -77,17 +87,19 @@ class Administrator
77
 
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
 
85
  // Path to backend
86
  $this->path = plugin_dir_path(__FILE__);
87
 
88
- // URL to public backend folder
89
- $this->url = plugin_dir_url(__FILE__) . "public/";
90
-
91
  // Load plugins meta data
92
  $this->loadMeta();
93
  }
@@ -127,6 +139,7 @@ class Administrator
127
  add_action("wp_ajax_wpstg_check_clone", [$this, "ajaxcheckCloneName"]);
128
  add_action("wp_ajax_wpstg_restart", [$this, "ajaxRestart"]);
129
  add_action("wp_ajax_wpstg_update", [$this, "ajaxUpdateProcess"]);
 
130
  add_action("wp_ajax_wpstg_cloning", [$this, "ajaxStartClone"]);
131
  add_action("wp_ajax_wpstg_processing", [$this, "ajaxCloneDatabase"]);
132
  add_action("wp_ajax_wpstg_database_connect", [$this, "ajaxDatabaseConnect"]);
@@ -147,10 +160,12 @@ class Administrator
147
  add_action("wp_ajax_wpstg_check_disk_space", [$this, "ajaxCheckFreeSpace"]);
148
  add_action("wp_ajax_wpstg_send_report", [$this, "ajaxSendReport"]);
149
  add_action("wp_ajax_wpstg_send_feedback", [$this, "sendFeedback"]);
150
- add_action("wp_ajax_wpstg_hide_cache_notice", [$this, "ajaxHideCacheNotice"]);
 
151
 
152
 
153
  // Ajax hooks pro Version
 
154
  add_action("wp_ajax_wpstg_edit_clone_data", [$this, "ajaxEditCloneData"]);
155
  add_action("wp_ajax_wpstg_save_clone_data", [$this, "ajaxSaveCloneData"]);
156
  add_action("wp_ajax_wpstg_scan", [$this, "ajaxPushScan"]);
@@ -206,9 +221,21 @@ class Administrator
206
  */
207
  public function sanitizeOptions($data = [])
208
  {
 
 
 
 
 
 
 
 
209
  $sanitized = $this->sanitizeData($data);
210
 
211
- add_settings_error("wpstg-notices", '', __("Settings updated.", "wp-staging"), "updated");
 
 
 
 
212
 
213
  return apply_filters("wpstg-settings", $sanitized, $data);
214
  }
@@ -225,7 +252,7 @@ class Administrator
225
  if (is_array($value)) {
226
  $sanitized[$key] = $this->sanitizeData($value);
227
  } //Removing comma separators and decimal points
228
- else if (in_array($key, self::$integerOptions, true)) {
229
  $sanitized[$key] = preg_replace('/\D/', '', htmlspecialchars($value));
230
  } else {
231
  $sanitized[$key] = htmlspecialchars($value);
@@ -256,35 +283,66 @@ class Administrator
256
 
257
  // Main WP Staging Menu
258
  add_menu_page(
259
- "WP-Staging", __("WP Staging " . $pro, "wp-staging"), "manage_options", "wpstg_clone", [$this, "getClonePage"], $logo, $pos
 
 
 
 
 
 
260
  );
261
 
262
  // Page: Clone
263
  add_submenu_page(
264
- "wpstg_clone", __("WP Staging Jobs", "wp-staging"), __("Sites / Start", "wp-staging"), "manage_options", "wpstg_clone", [$this, "getClonePage"]
 
 
 
 
 
265
  );
266
 
267
  // Page: Settings
268
  add_submenu_page(
269
- "wpstg_clone", __("WP Staging Settings", "wp-staging"), __("Settings", "wp-staging"), "manage_options", "wpstg-settings", [$this, "getSettingsPage"]
 
 
 
 
 
270
  );
271
 
272
  // Page: Tools
273
  add_submenu_page(
274
- "wpstg_clone", __("WP Staging Tools", "wp-staging"), __("Tools", "wp-staging"), "manage_options", "wpstg-tools", [$this, "getToolsPage"]
 
 
 
 
 
275
  );
276
 
277
  if (!defined('WPSTGPRO_VERSION')) {
278
  // Page: Tools
279
  add_submenu_page(
280
- "wpstg_clone", __("WP Staging Welcome", "wp-staging"), __("Get WP Staging Pro", "wp-staging"), "manage_options", "wpstg-welcome", [$this, "getWelcomePage"]
 
 
 
 
 
281
  );
282
  }
283
 
284
- if (defined('WPSTGPRO_VERSION') && wpstg_is_stagingsite() === false) {
285
  // Page: License
286
  add_submenu_page(
287
- "wpstg_clone", __("WP Staging License", "wp-staging"), __("License", "wp-staging"), "manage_options", "wpstg-license", [$this, "getLicensePage"]
 
 
 
 
 
288
  );
289
  }
290
  }
@@ -458,8 +516,8 @@ class Administrator
458
  $vars = array_combine(
459
  array_map(function ($key) {
460
  return "{{" . $key . "}}";
461
- }, array_keys($vars)
462
- ), $vars
463
  );
464
 
465
  $contents = str_replace(array_keys($vars), array_values($vars), $contents);
@@ -473,7 +531,7 @@ class Administrator
473
  */
474
  private function isAuthenticated()
475
  {
476
- return $this->auth->isValid();
477
  }
478
 
479
  /**
@@ -597,6 +655,30 @@ class Administrator
597
  wp_die();
598
  }
599
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
  /**
601
  * Ajax Start Clone (Basically just layout and saving data)
602
  */
@@ -630,7 +712,7 @@ class Administrator
630
  return;
631
  }
632
 
633
- wp_send_json((new Cloning)->start());
634
  }
635
 
636
  /**
@@ -642,7 +724,7 @@ class Administrator
642
  return;
643
  }
644
 
645
- wp_send_json((new Cloning)->start());
646
  }
647
 
648
  /**
@@ -654,7 +736,7 @@ class Administrator
654
  return;
655
  }
656
 
657
- wp_send_json((new Cloning)->start());
658
  }
659
 
660
  /**
@@ -666,7 +748,7 @@ class Administrator
666
  return;
667
  }
668
 
669
- wp_send_json((new Cloning)->start());
670
  }
671
 
672
  /**
@@ -678,7 +760,7 @@ class Administrator
678
  return;
679
  }
680
 
681
- wp_send_json((new Cloning)->start());
682
  }
683
 
684
  /**
@@ -712,7 +794,7 @@ class Administrator
712
  return;
713
  }
714
 
715
- wp_send_json((new Delete)->start());
716
  }
717
 
718
  /**
@@ -724,7 +806,7 @@ class Administrator
724
  return;
725
  }
726
 
727
- wp_send_json((new Cancel)->start());
728
  }
729
 
730
  /**
@@ -736,7 +818,7 @@ class Administrator
736
  return;
737
  }
738
 
739
- wp_send_json((new CancelUpdate)->start());
740
  }
741
 
742
  /**
@@ -744,7 +826,7 @@ class Administrator
744
  */
745
  public function messages()
746
  {
747
- (new Notices($this->path, $this->url))->messages();
748
  }
749
 
750
  /**
@@ -798,13 +880,14 @@ class Administrator
798
  }
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);
@@ -819,7 +902,7 @@ class Administrator
819
  return;
820
  }
821
 
822
- wp_send_json((new Logs)->start());
823
  }
824
 
825
  /**
@@ -941,7 +1024,7 @@ class Administrator
941
  }
942
 
943
  // Start the process
944
- wp_send_json((new Processing)->start());
945
  }
946
 
947
  /**
@@ -990,13 +1073,13 @@ class Administrator
990
  // Set syslog
991
  $syslog = false;
992
  if (isset($args['wpstg_syslog'])) {
993
- $syslog = ( bool )$args['wpstg_syslog'];
994
  }
995
 
996
  // Set terms
997
  $terms = false;
998
  if (isset($args['wpstg_terms'])) {
999
- $terms = ( bool )$args['wpstg_terms'];
1000
  }
1001
 
1002
  // Set forceSend
@@ -1024,6 +1107,8 @@ class Administrator
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();
@@ -1037,8 +1122,38 @@ class Administrator
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
 
@@ -1071,6 +1186,40 @@ class Administrator
1071
  exit();
1072
  }
1073
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1074
  /**
1075
  * Check if Plugin is Pro version
1076
  * @return bool
@@ -1083,5 +1232,4 @@ class Administrator
1083
 
1084
  return true;
1085
  }
1086
-
1087
  }
8
  }
9
 
10
  use WPStaging\Core\WPStaging;
11
+ use WPStaging\Core\Utils\Cache;
12
+ use WPStaging\Framework\Assets\Assets;
13
  use WPStaging\Framework\Database\DbInfo;
14
+ use WPStaging\Framework\SiteInfo;
15
  use WPStaging\Framework\Security\Auth;
16
  use WPStaging\Framework\Security\AccessToken;
17
  use WPStaging\Framework\Security\Capabilities;
28
  use WPStaging\Backend\Modules\SystemInfo;
29
  use WPStaging\Backend\Modules\Views\Tabs\Tabs;
30
  use WPStaging\Backend\Notices\Notices;
31
+ use WPStaging\Backend\Notices\DisabledItemsNotice;
32
  use WPStaging\Backend\Modules\Views\Forms\Settings as FormSettings;
33
  use WPStaging\Backend\Activation;
34
  use WPStaging\Backend\Feedback;
35
  use WPStaging\Backend\Pro\Modules\Jobs\Processing;
36
  use WPStaging\Backend\Pluginmeta\Pluginmeta;
37
+ use WPStaging\Core\DTO\Settings;
38
  use WPStaging\Pro\Database\CompareExternalDatabase;
39
 
40
  /**
56
 
57
 
58
  /**
59
+ * Path to plugin's Backend Dir
60
  * @var string
61
  */
62
  private $path;
63
 
64
  /**
65
+ * @var Assets
66
  */
67
+ private $assets;
68
 
69
  /**
70
  * @var Auth
71
  */
72
  private $auth;
73
 
74
+ /**
75
+ * @var SiteInfo
76
+ */
77
+ private $siteInfo;
78
+
79
  /**
80
  * @var array
81
  * All options here will only be stored in the database as integers. Decimal points and separators will be removed
87
 
88
  public function __construct()
89
  {
90
+ // TODO: Inject using DI
91
+ $this->auth = new Auth(new Capabilities(), new AccessToken(), new Nonce());
92
+ // TODO: Inject using DI
93
+ $this->assets = new Assets(new AccessToken(), new Settings());
94
+
95
  // Todo: Inject using DI
96
+ $this->siteInfo = new SiteInfo();
97
 
98
  $this->defineHooks();
99
 
100
  // Path to backend
101
  $this->path = plugin_dir_path(__FILE__);
102
 
 
 
 
103
  // Load plugins meta data
104
  $this->loadMeta();
105
  }
139
  add_action("wp_ajax_wpstg_check_clone", [$this, "ajaxcheckCloneName"]);
140
  add_action("wp_ajax_wpstg_restart", [$this, "ajaxRestart"]);
141
  add_action("wp_ajax_wpstg_update", [$this, "ajaxUpdateProcess"]);
142
+ add_action("wp_ajax_wpstg_reset", [$this, "ajaxResetProcess"]);
143
  add_action("wp_ajax_wpstg_cloning", [$this, "ajaxStartClone"]);
144
  add_action("wp_ajax_wpstg_processing", [$this, "ajaxCloneDatabase"]);
145
  add_action("wp_ajax_wpstg_database_connect", [$this, "ajaxDatabaseConnect"]);
160
  add_action("wp_ajax_wpstg_check_disk_space", [$this, "ajaxCheckFreeSpace"]);
161
  add_action("wp_ajax_wpstg_send_report", [$this, "ajaxSendReport"]);
162
  add_action("wp_ajax_wpstg_send_feedback", [$this, "sendFeedback"]);
163
+ add_action("wp_ajax_wpstg_hide_disabled_items_notice", [$this, "ajaxHideDisabledItemsNotice"]);
164
+ add_action("wp_ajax_wpstg_enable_staging_cloning", [$this, "ajaxEnableStagingCloning"]);
165
 
166
 
167
  // Ajax hooks pro Version
168
+ // TODO: move all below actions to pro service provider?
169
  add_action("wp_ajax_wpstg_edit_clone_data", [$this, "ajaxEditCloneData"]);
170
  add_action("wp_ajax_wpstg_save_clone_data", [$this, "ajaxSaveCloneData"]);
171
  add_action("wp_ajax_wpstg_scan", [$this, "ajaxPushScan"]);
221
  */
222
  public function sanitizeOptions($data = [])
223
  {
224
+ $error = false;
225
+ // is_array() is required otherwise new clone will fail.
226
+ if ($this->siteInfo->isStaging() && is_array($data)) {
227
+ $isStagingCloneable = $data['isStagingSiteCloneable'];
228
+ unset($data['isStagingSiteCloneable']);
229
+ $error = !$this->toggleStagingSiteCloning($isStagingCloneable === 'true');
230
+ }
231
+
232
  $sanitized = $this->sanitizeData($data);
233
 
234
+ if ($error) {
235
+ add_settings_error("wpstg-notices", '', __("Settings updated. But unable to toggle cloning feature!", "wp-staging"), "warning");
236
+ } else {
237
+ add_settings_error("wpstg-notices", '', __("Settings updated.", "wp-staging"), "updated");
238
+ }
239
 
240
  return apply_filters("wpstg-settings", $sanitized, $data);
241
  }
252
  if (is_array($value)) {
253
  $sanitized[$key] = $this->sanitizeData($value);
254
  } //Removing comma separators and decimal points
255
+ elseif (in_array($key, self::$integerOptions, true)) {
256
  $sanitized[$key] = preg_replace('/\D/', '', htmlspecialchars($value));
257
  } else {
258
  $sanitized[$key] = htmlspecialchars($value);
283
 
284
  // Main WP Staging Menu
285
  add_menu_page(
286
+ "WP-Staging",
287
+ __("WP Staging " . $pro, "wp-staging"),
288
+ "manage_options",
289
+ "wpstg_clone",
290
+ [$this, "getClonePage"],
291
+ $logo,
292
+ $pos
293
  );
294
 
295
  // Page: Clone
296
  add_submenu_page(
297
+ "wpstg_clone",
298
+ __("WP Staging Jobs", "wp-staging"),
299
+ __("Sites / Start", "wp-staging"),
300
+ "manage_options",
301
+ "wpstg_clone",
302
+ [$this, "getClonePage"]
303
  );
304
 
305
  // Page: Settings
306
  add_submenu_page(
307
+ "wpstg_clone",
308
+ __("WP Staging Settings", "wp-staging"),
309
+ __("Settings", "wp-staging"),
310
+ "manage_options",
311
+ "wpstg-settings",
312
+ [$this, "getSettingsPage"]
313
  );
314
 
315
  // Page: Tools
316
  add_submenu_page(
317
+ "wpstg_clone",
318
+ __("WP Staging Tools", "wp-staging"),
319
+ __("Tools", "wp-staging"),
320
+ "manage_options",
321
+ "wpstg-tools",
322
+ [$this, "getToolsPage"]
323
  );
324
 
325
  if (!defined('WPSTGPRO_VERSION')) {
326
  // Page: Tools
327
  add_submenu_page(
328
+ "wpstg_clone",
329
+ __("WP Staging Welcome", "wp-staging"),
330
+ __("Get WP Staging Pro", "wp-staging"),
331
+ "manage_options",
332
+ "wpstg-welcome",
333
+ [$this, "getWelcomePage"]
334
  );
335
  }
336
 
337
+ if (defined('WPSTGPRO_VERSION') && $this->siteInfo->isStaging() === false) {
338
  // Page: License
339
  add_submenu_page(
340
+ "wpstg_clone",
341
+ __("WP Staging License", "wp-staging"),
342
+ __("License", "wp-staging"),
343
+ "manage_options",
344
+ "wpstg-license",
345
+ [$this, "getLicensePage"]
346
  );
347
  }
348
  }
516
  $vars = array_combine(
517
  array_map(function ($key) {
518
  return "{{" . $key . "}}";
519
+ }, array_keys($vars)),
520
+ $vars
521
  );
522
 
523
  $contents = str_replace(array_keys($vars), array_values($vars), $contents);
531
  */
532
  private function isAuthenticated()
533
  {
534
+ return $this->auth->isAuthenticatedRequest();
535
  }
536
 
537
  /**
655
  wp_die();
656
  }
657
 
658
+ /**
659
+ * Ajax Start Resetting Clone
660
+ */
661
+ public function ajaxResetProcess()
662
+ {
663
+ if (!$this->isAuthenticated()) {
664
+ return;
665
+ }
666
+
667
+ // TODO: inject this using DI
668
+ // remove clone options cache before initializing reset clone process
669
+ $cache = new Cache(-1, WPStaging::getContentDir());
670
+ $cache->delete("clone_options");
671
+
672
+ $cloning = new Updating();
673
+ $cloning->setMainJob(Updating::RESET_UPDATE);
674
+ if (!$cloning->save()) {
675
+ wp_die('can not save clone data');
676
+ }
677
+
678
+ require_once "{$this->path}views/clone/ajax/update.php";
679
+ wp_die();
680
+ }
681
+
682
  /**
683
  * Ajax Start Clone (Basically just layout and saving data)
684
  */
712
  return;
713
  }
714
 
715
+ wp_send_json((new Cloning())->start());
716
  }
717
 
718
  /**
724
  return;
725
  }
726
 
727
+ wp_send_json((new Cloning())->start());
728
  }
729
 
730
  /**
736
  return;
737
  }
738
 
739
+ wp_send_json((new Cloning())->start());
740
  }
741
 
742
  /**
748
  return;
749
  }
750
 
751
+ wp_send_json((new Cloning())->start());
752
  }
753
 
754
  /**
760
  return;
761
  }
762
 
763
+ wp_send_json((new Cloning())->start());
764
  }
765
 
766
  /**
794
  return;
795
  }
796
 
797
+ wp_send_json((new Delete())->start());
798
  }
799
 
800
  /**
806
  return;
807
  }
808
 
809
+ wp_send_json((new Cancel())->start());
810
  }
811
 
812
  /**
818
  return;
819
  }
820
 
821
+ wp_send_json((new CancelUpdate())->start());
822
  }
823
 
824
  /**
826
  */
827
  public function messages()
828
  {
829
+ (new Notices($this->path, $this->assets))->messages();
830
  }
831
 
832
  /**
880
  }
881
 
882
  /**
883
+ * Ajax hide disabled items notice shown on staging site
884
  */
885
+ public function ajaxHideDisabledItemsNotice()
886
  {
887
  // @todo inject with dependency injection
888
+ if ((new DisabledItemsNotice())->disable() !== false) {
889
  wp_send_json(true);
890
+ return;
891
  }
892
 
893
  wp_send_json(null);
902
  return;
903
  }
904
 
905
+ wp_send_json((new Logs())->start());
906
  }
907
 
908
  /**
1024
  }
1025
 
1026
  // Start the process
1027
+ wp_send_json((new Processing())->start());
1028
  }
1029
 
1030
  /**
1073
  // Set syslog
1074
  $syslog = false;
1075
  if (isset($args['wpstg_syslog'])) {
1076
+ $syslog = (bool)$args['wpstg_syslog'];
1077
  }
1078
 
1079
  // Set terms
1080
  $terms = false;
1081
  if (isset($args['wpstg_terms'])) {
1082
+ $terms = (bool)$args['wpstg_terms'];
1083
  }
1084
 
1085
  // Set forceSend
1107
  $database = !empty($args['databaseDatabase']) ? $args['databaseDatabase'] : '';
1108
  $server = !empty($args['databaseServer']) ? $args['databaseServer'] : 'localhost';
1109
  $prefix = !empty($args['databasePrefix']) ? $args['databasePrefix'] : 'wp_';
1110
+ // ensure tables with the given prefix exist, default false
1111
+ $ensurePrefixTableExist = !empty($args['databaseEnsurePrefixTableExist']) ? filter_var($args['databaseEnsurePrefixTableExist'], FILTER_VALIDATE_BOOLEAN) : false;
1112
 
1113
  $dbInfo = new DbInfo($server, $user, stripslashes($password), $database);
1114
  $wpdb = $dbInfo->connect();
1122
 
1123
  // Check if any table with provided prefix already exist
1124
  $existingTables = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $prefix . '%'));
1125
+ // used in new clone
1126
+ if ($existingTables !== null && !$ensurePrefixTableExist) {
1127
+ echo json_encode(['success' => 'false', 'errors' => __('Tables with prefix ' . $prefix . ' already exist in database. Select another prefix.', 'wp-staging')]);
1128
+ exit;
1129
+ }
1130
+
1131
+ // used in edit and update of clone
1132
+ if ($existingTables === null && $ensurePrefixTableExist) {
1133
+ echo json_encode(['success' => 'true', 'errors' => __('Tables with prefix "' . $prefix . '" not exist in database. Make sure it exists.', 'wp-staging')]);
1134
+ exit;
1135
+ }
1136
+
1137
+ // get production db
1138
+ $productionDb = WPStaging::getInstance()->get('wpdb');
1139
+
1140
+ $queryToFindAddress = "SELECT SUBSTRING_INDEX(host,':',1) AS 'ip' FROM information_schema.processlist WHERE ID=connection_id();";
1141
+ $queryToFindPort = "SHOW VARIABLES WHERE Variable_name = 'port';";
1142
+
1143
+ $stagingSiteAddress = $wpdb->get_var($wpdb->prepare($queryToFindAddress));
1144
+ $productionSiteAddress = $productionDb->get_var($productionDb->prepare($queryToFindAddress));
1145
+ if ($stagingSiteAddress === null || $productionSiteAddress === null) {
1146
+ echo json_encode(['success' => 'false', 'errors' => __('Unable to find database server hostname of the staging or the production site.', 'wp-staging')]);
1147
+ exit;
1148
+ }
1149
+
1150
+ $isSameAddress = $productionSiteAddress === $stagingSiteAddress;
1151
+ $isSamePort = $wpdb->get_var($wpdb->prepare($queryToFindPort)) === $productionDb->get_var($productionDb->prepare($queryToFindPort));
1152
+
1153
+ $isSameServer = ($isSameAddress && $isSamePort) || $server === DB_HOST;
1154
+
1155
+ if ($database === DB_NAME && $prefix === $productionDb->prefix && $isSameServer) {
1156
+ echo json_encode(['success' => 'false', 'errors' => __('Cannot use production site database. Use another database.', 'wp-staging')]);
1157
  exit;
1158
  }
1159
 
1186
  exit();
1187
  }
1188
 
1189
+ /**
1190
+ * Enable cloning on staging site if it is not enabled already
1191
+ */
1192
+ public function ajaxEnableStagingCloning()
1193
+ {
1194
+ if ($this->siteInfo->enableStagingSiteCloning()) {
1195
+ echo json_encode(['success' => 'true']);
1196
+ exit();
1197
+ }
1198
+
1199
+ echo json_encode(['success' => 'false', 'message' => __('Unable to enable cloning in the staging site', 'wp-staging')]);
1200
+ exit();
1201
+ }
1202
+
1203
+ /**
1204
+ * Toggle staging site cloning
1205
+ *
1206
+ * @param bool $isCloneable
1207
+ *
1208
+ * @return bool
1209
+ */
1210
+ protected function toggleStagingSiteCloning($isCloneable)
1211
+ {
1212
+ if ($isCloneable && $this->siteInfo->enableStagingSiteCloning()) {
1213
+ return true;
1214
+ }
1215
+
1216
+ if (!$isCloneable && $this->siteInfo->disableStagingSiteCloning()) {
1217
+ return true;
1218
+ }
1219
+
1220
+ return false;
1221
+ }
1222
+
1223
  /**
1224
  * Check if Plugin is Pro version
1225
  * @return bool
1232
 
1233
  return true;
1234
  }
 
1235
  }
Backend/Feedback/Feedback.php CHANGED
@@ -4,14 +4,16 @@ namespace WPStaging\Backend\Feedback;
4
 
5
  use WP_User;
6
 
7
- class Feedback {
 
8
 
9
  /**
10
  * Current page is plugins.php
11
  * @global array $pagenow
12
  * @return bool
13
  */
14
- private function isPluginsPage() {
 
15
  global $pagenow;
16
  return ( $pagenow === 'plugins.php' );
17
  }
@@ -23,55 +25,56 @@ class Feedback {
23
  *
24
  * @return string
25
  */
26
- public function loadForm() {
 
27
 
28
  $screen = get_current_screen();
29
- if( !is_admin() && !$this->isPluginsPage() ) {
30
  return;
31
  }
32
 
33
  $current_user = wp_get_current_user();
34
- if( !($current_user instanceof WP_User) ) {
35
  $email = '';
36
  } else {
37
- $email = trim( $current_user->user_email );
38
  }
39
 
40
  include WPSTG_PLUGIN_DIR . 'Backend/views/feedback/deactivate-feedback.php';
41
  }
42
 
43
- public function sendMail() {
 
44
 
45
- if( isset( $_POST['data'] ) ) {
46
- parse_str( $_POST['data'], $form );
47
  }
48
 
49
  $text = '';
50
- if( isset( $form['wpstg_disable_text'] ) ) {
51
- $text = implode( "\n\r", $form['wpstg_disable_text'] );
52
  }
53
 
54
  $headers = [];
55
 
56
- $from = isset( $form['wpstg_disable_from'] ) ? $form['wpstg_disable_from'] : '';
57
- if( $from ) {
58
  $headers[] = "From: $from";
59
  $headers[] = "Reply-To: $from";
60
  }
61
 
62
- $subject = isset( $form['wpstg_disable_reason'] ) ? 'WP Staging Free: '. $form['wpstg_disable_reason'] : 'WP Staging Free: (no reason given)';
63
 
64
- $success = wp_mail( 'feedback@wp-staging.com', $subject, $text, $headers );
65
 
66
  //error_log(print_r($success, true));
67
  //error_log($from . $subject . var_dump($form));
68
 
69
- if( $success ) {
70
- wp_die( 1 );
71
  }
72
- wp_die( 0 );
73
  }
74
-
75
  }
76
 
77
  /**
@@ -90,7 +93,7 @@ class Feedback {
90
 
91
  /**
92
  * display deactivation logic on plugins page
93
- *
94
  * @since 3.3.7
95
  */
96
  //function mashsb_add_deactivation_feedback_modal() {
@@ -112,7 +115,7 @@ class Feedback {
112
 
113
  /**
114
  * send feedback via email
115
- *
116
  * @since 1.4.0
117
  */
118
  //function wpstg_send_feedback() {
@@ -148,4 +151,3 @@ class Feedback {
148
  //}
149
  //
150
  //add_action( 'wp_ajax_wpstg_send_feedback', 'wpstg_send_feedback' );
151
-
4
 
5
  use WP_User;
6
 
7
+ class Feedback
8
+ {
9
 
10
  /**
11
  * Current page is plugins.php
12
  * @global array $pagenow
13
  * @return bool
14
  */
15
+ private function isPluginsPage()
16
+ {
17
  global $pagenow;
18
  return ( $pagenow === 'plugins.php' );
19
  }
25
  *
26
  * @return string
27
  */
28
+ public function loadForm()
29
+ {
30
 
31
  $screen = get_current_screen();
32
+ if (!is_admin() && !$this->isPluginsPage()) {
33
  return;
34
  }
35
 
36
  $current_user = wp_get_current_user();
37
+ if (!($current_user instanceof WP_User)) {
38
  $email = '';
39
  } else {
40
+ $email = trim($current_user->user_email);
41
  }
42
 
43
  include WPSTG_PLUGIN_DIR . 'Backend/views/feedback/deactivate-feedback.php';
44
  }
45
 
46
+ public function sendMail()
47
+ {
48
 
49
+ if (isset($_POST['data'])) {
50
+ parse_str($_POST['data'], $form);
51
  }
52
 
53
  $text = '';
54
+ if (isset($form['wpstg_disable_text'])) {
55
+ $text = implode("\n\r", $form['wpstg_disable_text']);
56
  }
57
 
58
  $headers = [];
59
 
60
+ $from = isset($form['wpstg_disable_from']) ? $form['wpstg_disable_from'] : '';
61
+ if ($from) {
62
  $headers[] = "From: $from";
63
  $headers[] = "Reply-To: $from";
64
  }
65
 
66
+ $subject = isset($form['wpstg_disable_reason']) ? 'WP Staging Free: ' . $form['wpstg_disable_reason'] : 'WP Staging Free: (no reason given)';
67
 
68
+ $success = wp_mail('feedback@wp-staging.com', $subject, $text, $headers);
69
 
70
  //error_log(print_r($success, true));
71
  //error_log($from . $subject . var_dump($form));
72
 
73
+ if ($success) {
74
+ wp_die(1);
75
  }
76
+ wp_die(0);
77
  }
 
78
  }
79
 
80
  /**
93
 
94
  /**
95
  * display deactivation logic on plugins page
96
+ *
97
  * @since 3.3.7
98
  */
99
  //function mashsb_add_deactivation_feedback_modal() {
115
 
116
  /**
117
  * send feedback via email
118
+ *
119
  * @since 1.4.0
120
  */
121
  //function wpstg_send_feedback() {
151
  //}
152
  //
153
  //add_action( 'wp_ajax_wpstg_send_feedback', 'wpstg_send_feedback' );
 
Backend/Modules/Jobs/Cancel.php CHANGED
@@ -6,77 +6,81 @@ namespace WPStaging\Backend\Modules\Jobs;
6
  * Class Cancel Processing
7
  * @package WPStaging\Backend\Modules\Jobs
8
  */
9
- class Cancel extends Job {
 
10
 
11
  /**
12
  * Start Module
13
  * @return bool
14
  */
15
- public function start() {
16
- $cloneData = $this->createCloneData();
 
17
 
18
- if( empty( $cloneData ) ) {
19
- return true;
20
- }
21
- // Delete data in external database
22
- if( empty( $this->options->databaseUser ) ) {
23
- $delete = new Delete();
24
- } else {
25
- $delete = new Delete( true );
26
- }
27
- return $delete->start( $cloneData );
28
- }
29
 
30
  /**
31
  * @return array
32
  */
33
- protected function createCloneData() {
34
- $clone = [];
 
35
 
36
- if( !$this->check() ) {
37
- return $clone;
38
- }
39
 
40
- $clone["name"] = $this->options->clone;
41
- $clone["number"] = $this->options->cloneNumber;
42
- $clone["path"] = ABSPATH . $this->options->cloneDirectoryName;
43
- $clone["prefix"] = $this->options->prefix;
44
- $clone["databaseServer"] = $this->options->databaseServer;
45
- $clone["databaseUser"] = $this->options->databaseUser;
46
- $clone["databasePassword"] = $this->options->databasePassword;
47
- $clone["databasePrefix"] = $this->options->databasePrefix;
48
- $clone["databaseDatabase"] = $this->options->databaseDatabase;
49
 
50
- return $clone;
51
- }
52
 
53
  /**
54
  * @return bool
55
  */
56
- public function check() {
57
- return (
58
- isset( $this->options ) &&
59
- isset( $this->options->clone ) &&
60
- isset( $this->options->cloneNumber ) &&
61
- isset( $this->options->cloneDirectoryName ) &&
62
- isset( $_POST["clone"] ) &&
 
63
  $_POST["clone"] === $this->options->clone
64
  );
65
- }
66
 
67
  /**
68
  * Get json response
69
  * return json
70
  */
71
- private function returnFinish( $message = '' ) {
 
72
 
73
- wp_die( json_encode( [
74
  'job' => 'delete',
75
  'status' => true,
76
  'message' => $message,
77
  'error' => false,
78
  'delete' => 'finished'
79
- ] ) );
80
- }
81
-
82
  }
6
  * Class Cancel Processing
7
  * @package WPStaging\Backend\Modules\Jobs
8
  */
9
+ class Cancel extends Job
10
+ {
11
 
12
  /**
13
  * Start Module
14
  * @return bool
15
  */
16
+ public function start()
17
+ {
18
+ $cloneData = $this->createCloneData();
19
 
20
+ if (empty($cloneData)) {
21
+ return true;
22
+ }
23
+ // Delete data in external database
24
+ if (empty($this->options->databaseUser)) {
25
+ $delete = new Delete();
26
+ } else {
27
+ $delete = new Delete(true);
28
+ }
29
+ return $delete->start($cloneData);
30
+ }
31
 
32
  /**
33
  * @return array
34
  */
35
+ protected function createCloneData()
36
+ {
37
+ $clone = [];
38
 
39
+ if (!$this->check()) {
40
+ return $clone;
41
+ }
42
 
43
+ $clone["name"] = $this->options->clone;
44
+ $clone["number"] = $this->options->cloneNumber;
45
+ $clone["path"] = ABSPATH . $this->options->cloneDirectoryName;
46
+ $clone["prefix"] = $this->options->prefix;
47
+ $clone["databaseServer"] = $this->options->databaseServer;
48
+ $clone["databaseUser"] = $this->options->databaseUser;
49
+ $clone["databasePassword"] = $this->options->databasePassword;
50
+ $clone["databasePrefix"] = $this->options->databasePrefix;
51
+ $clone["databaseDatabase"] = $this->options->databaseDatabase;
52
 
53
+ return $clone;
54
+ }
55
 
56
  /**
57
  * @return bool
58
  */
59
+ public function check()
60
+ {
61
+ return (
62
+ isset($this->options) &&
63
+ isset($this->options->clone) &&
64
+ isset($this->options->cloneNumber) &&
65
+ isset($this->options->cloneDirectoryName) &&
66
+ isset($_POST["clone"]) &&
67
  $_POST["clone"] === $this->options->clone
68
  );
69
+ }
70
 
71
  /**
72
  * Get json response
73
  * return json
74
  */
75
+ private function returnFinish($message = '')
76
+ {
77
 
78
+ wp_die(json_encode([
79
  'job' => 'delete',
80
  'status' => true,
81
  'message' => $message,
82
  'error' => false,
83
  'delete' => 'finished'
84
+ ]));
85
+ }
 
86
  }
Backend/Modules/Jobs/CancelUpdate.php CHANGED
@@ -6,13 +6,15 @@ namespace WPStaging\Backend\Modules\Jobs;
6
  * Class Cancel Update Processing
7
  * @package WPStaging\Backend\Modules\Jobs
8
  */
9
- class CancelUpdate extends Job {
 
10
 
11
  /**
12
  * Start Module
13
  * @return bool
14
  */
15
- public function start() {
 
16
  $cloneData = $this->createCloneData();
17
 
18
  if (empty($cloneData)) {
@@ -20,15 +22,15 @@ class CancelUpdate extends Job {
20
  }
21
  // Delete Cache Files
22
  $this->deleteCacheFiles();
23
-
24
  $this->returnFinish();
25
-
26
  }
27
 
28
  /**
29
  * @return array
30
  */
31
- protected function createCloneData() {
 
32
  $clone = [];
33
 
34
  if (!$this->check()) {
@@ -46,7 +48,8 @@ class CancelUpdate extends Job {
46
  /**
47
  * @return bool
48
  */
49
- public function check() {
 
50
  return (
51
  isset($this->options) &&
52
  isset($this->options->clone) &&
@@ -61,7 +64,8 @@ class CancelUpdate extends Job {
61
  * Get json response
62
  * return json
63
  */
64
- private function returnFinish($message = '') {
 
65
 
66
  wp_die(json_encode([
67
  'job' => 'delete',
@@ -71,8 +75,8 @@ class CancelUpdate extends Job {
71
  'delete' => 'finished'
72
  ]));
73
  }
74
-
75
-
76
  /**
77
  * Delete Cache Files
78
  */
@@ -86,5 +90,4 @@ class CancelUpdate extends Job {
86
 
87
  $this->log("Updating process canceled");
88
  }
89
-
90
  }
6
  * Class Cancel Update Processing
7
  * @package WPStaging\Backend\Modules\Jobs
8
  */
9
+ class CancelUpdate extends Job
10
+ {
11
 
12
  /**
13
  * Start Module
14
  * @return bool
15
  */
16
+ public function start()
17
+ {
18
  $cloneData = $this->createCloneData();
19
 
20
  if (empty($cloneData)) {
22
  }
23
  // Delete Cache Files
24
  $this->deleteCacheFiles();
25
+
26
  $this->returnFinish();
 
27
  }
28
 
29
  /**
30
  * @return array
31
  */
32
+ protected function createCloneData()
33
+ {
34
  $clone = [];
35
 
36
  if (!$this->check()) {
48
  /**
49
  * @return bool
50
  */
51
+ public function check()
52
+ {
53
  return (
54
  isset($this->options) &&
55
  isset($this->options->clone) &&
64
  * Get json response
65
  * return json
66
  */
67
+ private function returnFinish($message = '')
68
+ {
69
 
70
  wp_die(json_encode([
71
  'job' => 'delete',
75
  'delete' => 'finished'
76
  ]));
77
  }
78
+
79
+
80
  /**
81
  * Delete Cache Files
82
  */
90
 
91
  $this->log("Updating process canceled");
92
  }
 
93
  }
Backend/Modules/Jobs/Cleaners/WpContentCleaner.php CHANGED
@@ -47,8 +47,6 @@ class WpContentCleaner
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
  {
@@ -62,7 +60,7 @@ class WpContentCleaner
62
  if (!is_dir($directory)) {
63
  return true;
64
  }
65
-
66
  $wpDirectories = new WpDefaultDirectories();
67
  $directory = trailingslashit($directory);
68
  $paths = [];
@@ -92,14 +90,17 @@ class WpContentCleaner
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'])
@@ -133,4 +134,4 @@ class WpContentCleaner
133
 
134
  return true;
135
  }
136
- }
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
  public function tryCleanWpContent($directory)
52
  {
60
  if (!is_dir($directory)) {
61
  return true;
62
  }
63
+
64
  $wpDirectories = new WpDefaultDirectories();
65
  $directory = trailingslashit($directory);
66
  $paths = [];
90
  }
91
 
92
  $excludePaths = [
93
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . "wp-staging",
94
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . "wp-staging_1",
95
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . "wp-staging_2",
96
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . "wp-staging-pro",
97
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . "wp-staging-pro_1",
98
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . "wp-staging-pro_2",
99
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . "wp-staging-dev",
100
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . 'wp-staging-hooks',
101
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . 'wp-staging-hooks_1',
102
+ trailingslashit($directory . $wpDirectories->getRelativePluginPath()) . 'wp-staging-hooks_2',
103
+ trailingslashit($directory . $wpDirectories->getRelativeUploadPath()) . 'wp-staging', // exclude wp-staging from uploads dir too.
104
  ];
105
  $fs = (new Filesystem())
106
  ->setShouldStop([$this->job, 'isOverThreshold'])
134
 
135
  return true;
136
  }
137
+ }
Backend/Modules/Jobs/Cloning.php CHANGED
@@ -2,13 +2,10 @@
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
- use WPStaging\Framework\Security\AccessToken;
6
- use WPStaging\Core\WPStaging;
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
8
- use WPStaging\Backend\Modules\Jobs\Multisite\Finish as muFinish;
9
- use WPStaging\Backend\Modules\Jobs\Multisite\Directories as muDirectories;
10
- use WPStaging\Backend\Modules\Jobs\Multisite\Files as muFiles;
11
  use WPStaging\Core\Utils\Helper;
 
 
12
  use WPStaging\Framework\Utils\WpDefaultDirectories;
13
 
14
  /**
@@ -64,7 +61,8 @@ class Cloning extends Job
64
  '.gitignore',
65
  '*.log',
66
  'web.config', // Important: Windows IIS configuration file. Must not be in the staging site!
67
- '.wp-staging' // Determines if a site is a staging site
 
68
  ];
69
  $this->options->excludedFilesFullPath = [
70
  'wp-content' . DIRECTORY_SEPARATOR . 'db.php',
@@ -97,11 +95,6 @@ class Cloning extends Job
97
  $this->options->tables = [];
98
  }
99
 
100
- // Excluded Directories
101
- if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"])) {
102
- $this->options->excludedDirectories = wpstg_urldecode($_POST["excludedDirectories"]);
103
- }
104
-
105
  $this->options->uploadsSymlinked = isset($_POST['uploadsSymlinked']) && $_POST['uploadsSymlinked'] === 'true';
106
 
107
  // Excluded Directories TOTAL
@@ -116,29 +109,29 @@ class Cloning extends Job
116
 
117
  // Add upload folder to list of excluded directories for push if symlink option is enabled
118
  if ($this->options->uploadsSymlinked) {
119
- $wpUploadsFolder = (new WpDefaultDirectories())->getUploadPath();
120
  $excludedDirectories[] = rtrim($wpUploadsFolder, '/\\');
121
  }
122
 
123
- $this->options->excludedDirectories = array_merge($excludedDirectories, wpstg_urldecode($this->options->excludedDirectories));
124
 
125
- array_unshift($this->options->directoriesToCopy, WPStaging::getWPpath());
126
 
 
127
  // Included Directories
128
- if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"])) {
129
- $this->options->includedDirectories = wpstg_urldecode($_POST["includedDirectories"]);
 
 
130
  }
131
 
 
 
132
  // Extra Directories
133
- if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"])) {
134
- $this->options->extraDirectories = wpstg_urldecode($_POST["extraDirectories"]);
135
  }
136
 
137
- // Directories to Copy
138
- $this->options->directoriesToCopy = array_merge(
139
- $this->options->includedDirectories,
140
- $this->options->extraDirectories
141
- );
142
  $this->options->databaseServer = 'localhost';
143
  if (isset($_POST["databaseServer"]) && !empty($_POST["databaseServer"])) {
144
  $this->options->databaseServer = $_POST["databaseServer"];
@@ -171,8 +164,10 @@ class Cloning extends Job
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();
@@ -280,7 +275,7 @@ class Cloning extends Job
280
  private function getDestinationDir()
281
  {
282
  // Throw fatal error
283
- if (!empty($this->options->cloneDir) & (trailingslashit($this->options->cloneDir) === ( string )trailingslashit(WPStaging::getWPpath()))) {
284
  $this->returnException('Error: Target Directory must be different from the root of the production website.');
285
  die();
286
  }
@@ -294,7 +289,7 @@ class Cloning extends Job
294
  }
295
 
296
  /**
297
- * Make sure prefix contains appending underscore
298
  *
299
  * @param string $string
300
  * @return string
@@ -427,11 +422,7 @@ class Cloning extends Job
427
  */
428
  public function jobDirectories()
429
  {
430
- if (defined('WPSTGPRO_VERSION') && is_multisite()) {
431
- $directories = new muDirectories();
432
- } else {
433
- $directories = new Directories();
434
- }
435
  return $this->handleJobResponse($directories->start(), "files");
436
  }
437
 
@@ -441,11 +432,7 @@ class Cloning extends Job
441
  */
442
  public function jobFiles()
443
  {
444
- if (defined('WPSTGPRO_VERSION') && is_multisite()) {
445
- $files = new muFiles();
446
- } else {
447
- $files = new Files();
448
- }
449
  return $this->handleJobResponse($files->start(), "data");
450
  }
451
 
@@ -468,15 +455,10 @@ class Cloning extends Job
468
  // Re-generate the token when the Clone is complete.
469
  // Todo: Consider adding a do_action() on jobFinish to hook here.
470
  // Todo: Inject using DI
471
- $accessToken = new AccessToken;
472
  $accessToken->generateNewToken();
473
 
474
- if (defined('WPSTGPRO_VERSION') && is_multisite()) {
475
- $finish = new muFinish();
476
- } else {
477
- $finish = new Finish();
478
- }
479
  return $this->handleJobResponse($finish->start(), '');
480
  }
481
-
482
  }
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
 
 
 
6
  use WPStaging\Core\Utils\Helper;
7
+ use WPStaging\Core\WPStaging;
8
+ use WPStaging\Framework\Security\AccessToken;
9
  use WPStaging\Framework\Utils\WpDefaultDirectories;
10
 
11
  /**
61
  '.gitignore',
62
  '*.log',
63
  'web.config', // Important: Windows IIS configuration file. Must not be in the staging site!
64
+ '.wp-staging', // Determines if a site is a staging site
65
+ '.wp-staging-cloneable', // File which make staging site to be cloneable
66
  ];
67
  $this->options->excludedFilesFullPath = [
68
  'wp-content' . DIRECTORY_SEPARATOR . 'db.php',
95
  $this->options->tables = [];
96
  }
97
 
 
 
 
 
 
98
  $this->options->uploadsSymlinked = isset($_POST['uploadsSymlinked']) && $_POST['uploadsSymlinked'] === 'true';
99
 
100
  // Excluded Directories TOTAL
109
 
110
  // Add upload folder to list of excluded directories for push if symlink option is enabled
111
  if ($this->options->uploadsSymlinked) {
112
+ $wpUploadsFolder = (new WpDefaultDirectories())->getUploadsPath();
113
  $excludedDirectories[] = rtrim($wpUploadsFolder, '/\\');
114
  }
115
 
116
+ $this->options->excludedDirectories = $excludedDirectories;
117
 
118
+ $this->options->areDirectoriesIncluded = isset($_POST['areDirectoriesIncluded']) && $_POST['areDirectoriesIncluded'] === 'true';
119
 
120
+ $directories = '';
121
  // Included Directories
122
+ if ($this->options->areDirectoriesIncluded) {
123
+ $directories = isset($_POST["includedDirectories"]) ? $_POST["includedDirectories"] : '';
124
+ } else { // Get Included Directories from Excluded Directories
125
+ $directories = isset($_POST["excludedDirectories"]) ? $_POST["excludedDirectories"] : '';
126
  }
127
 
128
+ $this->options->includedDirectories = (new WpDefaultDirectories())->getSelectedDirectories($directories, $this->options->areDirectoriesIncluded);
129
+
130
  // Extra Directories
131
+ if (isset($_POST["extraDirectories"])) {
132
+ $this->options->extraDirectories = wpstg_urldecode(explode(Scan::DIRECTORIES_SEPARATOR, $_POST["extraDirectories"]));
133
  }
134
 
 
 
 
 
 
135
  $this->options->databaseServer = 'localhost';
136
  if (isset($_POST["databaseServer"]) && !empty($_POST["databaseServer"])) {
137
  $this->options->databaseServer = $_POST["databaseServer"];
164
  // Make sure it is always enabled for free version
165
  $this->options->emailsAllowed = true;
166
  if (defined('WPSTGPRO_VERSION')) {
167
+ $this->options->emailsAllowed = apply_filters(
168
+ 'wpstg_cloning_email_allowed',
169
+ isset($_POST['emailsAllowed']) && $_POST['emailsAllowed'] !== "false"
170
+ );
171
  }
172
 
173
  $this->options->destinationHostname = $this->getDestinationHostname();
275
  private function getDestinationDir()
276
  {
277
  // Throw fatal error
278
+ if (!empty($this->options->cloneDir) & (trailingslashit($this->options->cloneDir) === (string)trailingslashit(WPStaging::getWPpath()))) {
279
  $this->returnException('Error: Target Directory must be different from the root of the production website.');
280
  die();
281
  }
289
  }
290
 
291
  /**
292
+ * Make sure prefix ends with underscore
293
  *
294
  * @param string $string
295
  * @return string
422
  */
423
  public function jobDirectories()
424
  {
425
+ $directories = new Directories();
 
 
 
 
426
  return $this->handleJobResponse($directories->start(), "files");
427
  }
428
 
432
  */
433
  public function jobFiles()
434
  {
435
+ $files = new Files();
 
 
 
 
436
  return $this->handleJobResponse($files->start(), "data");
437
  }
438
 
455
  // Re-generate the token when the Clone is complete.
456
  // Todo: Consider adding a do_action() on jobFinish to hook here.
457
  // Todo: Inject using DI
458
+ $accessToken = new AccessToken();
459
  $accessToken->generateNewToken();
460
 
461
+ $finish = new Finish();
 
 
 
 
462
  return $this->handleJobResponse($finish->start(), '');
463
  }
 
464
  }
Backend/Modules/Jobs/CloningProcess.php CHANGED
@@ -2,7 +2,6 @@
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
-
6
  use WPStaging\Core\WPStaging;
7
 
8
  abstract class CloningProcess extends JobExecutable
@@ -40,7 +39,7 @@ abstract class CloningProcess extends JobExecutable
40
  */
41
  protected function setExternalDatabase()
42
  {
43
- $this->stagingDb = new \wpdb($this->options->databaseUser, $this->options->databasePassword, $this->options->databaseDatabase, $this->options->databaseServer);
44
 
45
  // Check if there were any error when connecting
46
  if (
@@ -79,20 +78,4 @@ abstract class CloningProcess extends JobExecutable
79
  }
80
  return true;
81
  }
82
-
83
- /**
84
- * @return bool
85
- */
86
- protected function isExternalDatabase()
87
- {
88
- return !(empty($this->options->databaseUser) && empty($this->options->databasePassword));
89
- }
90
-
91
- /**
92
- * @return bool
93
- */
94
- protected function isMultisiteAndPro()
95
- {
96
- return defined('WPSTGPRO_VERSION') && is_multisite();
97
- }
98
  }
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
 
5
  use WPStaging\Core\WPStaging;
6
 
7
  abstract class CloningProcess extends JobExecutable
39
  */
40
  protected function setExternalDatabase()
41
  {
42
+ $this->stagingDb = new \wpdb($this->options->databaseUser, str_replace("\\\\", "\\", $this->options->databasePassword), $this->options->databaseDatabase, $this->options->databaseServer);
43
 
44
  // Check if there were any error when connecting
45
  if (
78
  }
79
  return true;
80
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
Backend/Modules/Jobs/Data.php CHANGED
@@ -2,7 +2,6 @@
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
-
6
  use WPStaging\Framework\CloningProcess\Data\DataCloningDto;
7
  use WPStaging\Framework\CloningProcess\Data\CopyWpConfig;
8
  use WPStaging\Framework\CloningProcess\Data\MultisiteAddNetworkAdministrators;
@@ -82,7 +81,7 @@ class Data extends CloningProcess
82
  // Save option, progress
83
  $this->saveOptions();
84
 
85
- return ( object )$this->response;
86
  }
87
 
88
  /**
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
 
5
  use WPStaging\Framework\CloningProcess\Data\DataCloningDto;
6
  use WPStaging\Framework\CloningProcess\Data\CopyWpConfig;
7
  use WPStaging\Framework\CloningProcess\Data\MultisiteAddNetworkAdministrators;
81
  // Save option, progress
82
  $this->saveOptions();
83
 
84
+ return (object)$this->response;
85
  }
86
 
87
  /**
Backend/Modules/Jobs/Database.php CHANGED
@@ -2,10 +2,11 @@
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
-
6
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
7
  use WPStaging\Framework\CloningProcess\CloningDto;
8
  use WPStaging\Framework\CloningProcess\Database\DatabaseCloningService;
 
 
9
 
10
  /**
11
  * Class Database
@@ -41,6 +42,10 @@ class Database extends CloningProcess
41
  $this->generateDto();
42
  $this->addMissingTables();
43
  $this->total = count($this->options->tables);
 
 
 
 
44
  }
45
 
46
  /**
@@ -84,8 +89,22 @@ class Database extends CloningProcess
84
  return false;
85
  }
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  // Copy table
88
- if (isset($this->options->tables[$this->options->currentStep]) && !$this->copyTable($this->options->tables[$this->options->currentStep])) {
89
  // Prepare Response
90
  $this->prepareResponse(false, false);
91
 
@@ -99,6 +118,50 @@ class Database extends CloningProcess
99
  return true;
100
  }
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  /**
103
  * Check if table already exists
104
  * @param string $name
@@ -235,15 +298,17 @@ class Database extends CloningProcess
235
 
236
  $excludedtables = array_merge($excludedCustomTables, $excludedCoreTables);
237
 
238
- if (in_array(
239
- $table,
240
- array_map(
241
- function ($tableName) {
242
- return $this->options->prefix . $tableName;
243
- },
244
- $excludedtables
 
 
245
  )
246
- )) {
247
  return true;
248
  }
249
  return false;
@@ -305,7 +370,7 @@ class Database extends CloningProcess
305
  private function abortIfStagingPrefixEqualsProdPrefix()
306
  {
307
  if ($this->productionDb->prefix === $this->getStagingPrefix()) {
308
- $error = 'Fatal error 7: The destination database table prefix ' . $this->getStagingPrefix() . ' would be identical to the table prefix of the production site. Please open a support ticket at support@wp-staging.com';
309
  $this->returnException($error);
310
  return true;
311
  }
@@ -317,7 +382,7 @@ class Database extends CloningProcess
317
  * Get new prefix for the staging site
318
  * @return string
319
  */
320
- private function getStagingPrefix()
321
  {
322
  if ($this->isExternalDatabase()) {
323
  $this->options->prefix = !empty($this->options->databasePrefix) ? $this->options->databasePrefix : $this->productionDb->prefix;
@@ -329,13 +394,13 @@ class Database extends CloningProcess
329
 
330
  /**
331
  * Return fatal error and stops here if subfolder already exists
332
- * and mainJob is not updating the clone
333
  * @return boolean
334
  */
335
  private function abortIfDirectoryNotEmpty()
336
  {
337
  $path = trailingslashit($this->options->cloneDir);
338
- if (isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && is_dir($path) && !wpstg_is_empty_dir($path)) {
339
  $this->returnException(" Can not continue for security purposes. Directory {$path} is not empty! Use FTP or a file manager plugin and make sure it does not contain any files. ");
340
  return true;
341
  }
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
  use WPStaging\Framework\CloningProcess\CloningDto;
7
  use WPStaging\Framework\CloningProcess\Database\DatabaseCloningService;
8
+ use WPStaging\Framework\Adapter\Database as DatabaseAdapter;
9
+ use WPStaging\Framework\Database\TableService;
10
 
11
  /**
12
  * Class Database
42
  $this->generateDto();
43
  $this->addMissingTables();
44
  $this->total = count($this->options->tables);
45
+ // if mainJob is resetting add one extra pre step for deleting all tables
46
+ if ($this->options->mainJob === 'resetting') {
47
+ $this->total++;
48
+ }
49
  }
50
 
51
  /**
89
  return false;
90
  }
91
 
92
+ if (!$this->deleteAllTables()) {
93
+ // Prepare Response
94
+ $this->prepareResponse(false, false);
95
+
96
+ // Not finished
97
+ return true;
98
+ }
99
+
100
+ // decrement the tableIndex if mainJob was resetting
101
+ $tableIndex = $this->options->currentStep;
102
+ if ($this->options->mainJob === 'resetting') {
103
+ $tableIndex--;
104
+ }
105
+
106
  // Copy table
107
+ if (isset($this->options->tables[$tableIndex]) && !$this->copyTable($this->options->tables[$tableIndex])) {
108
  // Prepare Response
109
  $this->prepareResponse(false, false);
110
 
118
  return true;
119
  }
120
 
121
+ /**
122
+ * Delete all tables in staging site if the mainJob is resetting
123
+ *
124
+ * @return bool
125
+ */
126
+ private function deleteAllTables()
127
+ {
128
+ if ($this->options->mainJob !== 'resetting') {
129
+ return true;
130
+ }
131
+
132
+ if ($this->options->currentStep !== 0) {
133
+ return true;
134
+ }
135
+
136
+ if (!isset($this->options->databaseResettingStatus)) {
137
+ $this->options->databaseResettingStatus = 'pending';
138
+ $this->saveOptions();
139
+ }
140
+
141
+ if ($this->options->databaseResettingStatus === 'finished') {
142
+ return true;
143
+ }
144
+
145
+ if ($this->options->databaseResettingStatus === 'pending') {
146
+ $this->log(__('DB: Removing all clone database tables.', 'wp-staging'));
147
+ $this->options->databaseResettingStatus = 'processing';
148
+ $this->saveOptions();
149
+ }
150
+
151
+ // TODO: inject using DI
152
+ $tableService = new TableService(new DatabaseAdapter($this->stagingDb));
153
+ $tableService->setShouldStop([$this, 'isOverThreshold']);
154
+ if (!$tableService->deleteTablesStartWith($this->getStagingPrefix())) {
155
+ return false;
156
+ }
157
+
158
+ $this->options->databaseResettingStatus = 'finished';
159
+ $this->saveOptions();
160
+
161
+ $this->prepareResponse();
162
+ return true;
163
+ }
164
+
165
  /**
166
  * Check if table already exists
167
  * @param string $name
298
 
299
  $excludedtables = array_merge($excludedCustomTables, $excludedCoreTables);
300
 
301
+ if (
302
+ in_array(
303
+ $table,
304
+ array_map(
305
+ function ($tableName) {
306
+ return $this->options->prefix . $tableName;
307
+ },
308
+ $excludedtables
309
+ )
310
  )
311
+ ) {
312
  return true;
313
  }
314
  return false;
370
  private function abortIfStagingPrefixEqualsProdPrefix()
371
  {
372
  if ($this->productionDb->prefix === $this->getStagingPrefix()) {
373
+ $error = 'Fatal error 7: The destination database table prefix ' . $this->getStagingPrefix() . ' is identical to the table prefix of the production site. Go to Sites > Actions > Edit Data and correct the table prefix or contact us.';
374
  $this->returnException($error);
375
  return true;
376
  }
382
  * Get new prefix for the staging site
383
  * @return string
384
  */
385
+ protected function getStagingPrefix()
386
  {
387
  if ($this->isExternalDatabase()) {
388
  $this->options->prefix = !empty($this->options->databasePrefix) ? $this->options->databasePrefix : $this->productionDb->prefix;
394
 
395
  /**
396
  * Return fatal error and stops here if subfolder already exists
397
+ * and mainJob is not updating and resetting the clone
398
  * @return boolean
399
  */
400
  private function abortIfDirectoryNotEmpty()
401
  {
402
  $path = trailingslashit($this->options->cloneDir);
403
+ if (isset($this->options->mainJob) && $this->options->mainJob !== 'resetting' && $this->options->mainJob !== 'updating' && is_dir($path) && !wpstg_is_empty_dir($path)) {
404
  $this->returnException(" Can not continue for security purposes. Directory {$path} is not empty! Use FTP or a file manager plugin and make sure it does not contain any files. ");
405
  return true;
406
  }
Backend/Modules/Jobs/Delete.php CHANGED
@@ -6,13 +6,14 @@ use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
6
  use WPStaging\Core\Utils\Logger;
7
  use WPStaging\Core\WPStaging;
8
  use WPStaging\Framework\Filesystem\Filesystem;
9
- use WPStaging\Framework\Filesystem\DirectoryDeleter;
10
 
11
  /**
12
  * Class Delete
13
  * @package WPStaging\Backend\Modules\Jobs
14
  */
15
- class Delete extends Job {
 
16
 
17
  /**
18
  * @var \stdClass
@@ -42,7 +43,7 @@ class Delete extends Job {
42
 
43
  /**
44
  *
45
- * @var object
46
  */
47
  public $wpdb;
48
 
@@ -50,13 +51,14 @@ class Delete extends Job {
50
  *
51
  * @var bool
52
  */
53
- private $isExternal;
54
 
55
- public function __construct( $isExternal = false ) {
 
56
  parent::__construct();
57
- $this->isExternal = $isExternal;
58
 
59
- $this->deleteDir = !empty( $_POST['deleteDir'] ) ? urldecode( $_POST['deleteDir'] ) : '';
60
  }
61
 
62
  /**
@@ -64,25 +66,25 @@ class Delete extends Job {
64
  * @param null|array $clone
65
  * @return bool
66
  */
67
- public function setData($clone = null)
68
  {
69
- if(!is_array($clone)) {
70
  $this->getCloneRecords();
71
  } else {
72
- $this->clone = (object) $clone;
73
  $this->forceDeleteDirectories = true;
74
  }
75
 
76
- if(!$this->isExternalDatabase()) {
77
  $this->wpdb = WPStaging::getInstance()->get("wpdb");
78
  $this->getTableRecords();
79
- return true;
80
  }
81
-
82
  if ($this->isExternalDatabaseError()) {
83
  return false;
84
  }
85
-
86
  $this->wpdb = $this->getStagingDb();
87
  $this->getTableRecords();
88
  return true;
@@ -91,15 +93,17 @@ class Delete extends Job {
91
  /**
92
  * Get database object to interact with
93
  */
94
- private function getStagingDb() {
95
- return new \wpdb( $this->clone->databaseUser, $this->clone->databasePassword, $this->clone->databaseDatabase, $this->clone->databaseServer );
 
96
  }
97
 
98
  /**
99
  * Date database name
100
  * @return string
101
  */
102
- public function getDbName() {
 
103
  return $this->wpdb->dbname;
104
  }
105
 
@@ -107,14 +111,16 @@ class Delete extends Job {
107
  * Check if external database is used
108
  * @return boolean
109
  */
110
- private function isExternalDatabase() {
111
- if( $this->isExternal ) {
 
112
  return true;
113
  }
114
 
115
- if( !empty( $this->clone->databaseUser ) ) {
116
  return true;
117
  }
 
118
  return false;
119
  }
120
 
@@ -123,97 +129,95 @@ class Delete extends Job {
123
  * @param null|string $name
124
  * @throws CloneNotFoundException
125
  */
126
- private function getCloneRecords( $name = null ) {
127
- if( $name === null && !isset( $_POST["clone"] ) ) {
128
- $this->log( "Clone name is not set", Logger::TYPE_FATAL );
129
- $this->returnException( "Clone name is not set" );
 
130
  }
131
 
132
- if( $name === null ) {
133
  $name = (string)$_POST["clone"];
134
  }
135
 
136
- $clones = get_option( "wpstg_existing_clones_beta", [] );
137
 
138
- if( empty( $clones ) || !isset( $clones[$name] ) ) {
139
- $this->log( "Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL );
140
- $this->returnException( "Couldn't find clone name {$name} or no existing clone" );
141
  }
142
 
143
- $this->clone = $clones[$name];
144
  $this->clone["name"] = $name;
145
 
146
- $this->clone = ( object ) $this->clone;
147
 
148
- unset( $clones );
149
  }
150
 
151
  /**
152
  * Get Tables
153
  */
154
- private function getTableRecords() {
 
155
 
156
- $stagingPrefix = $this->getStagingPrefix();
157
 
158
  // Escape "_" to allow searching for that character
159
  $prefix = wpstg_replace_last_match('_', '\_', $stagingPrefix);
160
 
161
- $tables = $this->wpdb->get_results( "SHOW TABLE STATUS LIKE '{$prefix}%'" );
162
 
163
  $this->tables = [];
164
 
165
  // no results
166
- if( $tables !== null ) {
167
- foreach ( $tables as $table ) {
168
  $this->tables[] = [
169
  "name" => $table->Name,
170
- "size" => $this->formatSize( ($table->Data_length + $table->Index_length ) )
171
  ];
172
  }
173
  }
174
 
175
- $this->tables = json_decode( json_encode( $this->tables ) );
176
  }
177
 
178
  /**
179
  * Check and return prefix of the staging site
180
  */
181
- public function getStagingPrefix() {
182
-
183
- if( $this->isExternalDatabase() && !empty( $this->clone->prefix ) ) {
184
  return $this->clone->prefix;
185
  }
186
 
187
  // Prefix not defined! Happens if staging site has been generated with older version of wpstg
188
  // Try to get staging prefix from wp-config.php of staging site
189
- if( empty( $this->clone->prefix ) ) {
190
- // Throw error
191
- $path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
192
- if( ($content = @file_get_contents( $path )) === false ) {
193
- $this->log( "Can not open {$path}. Can't read contents", Logger::TYPE_ERROR );
194
- } else {
195
- // Try to get prefix from wp-config.php
196
- preg_match( "/table_prefix\s*=\s*'(\w*)';/", $content, $matches );
197
 
198
- if( !empty( $matches[1] ) ) {
199
- $this->clone->prefix = $matches[1];
200
- } else {
201
- $this->returnException( "Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com" );
202
- }
203
  }
204
  }
205
 
206
- if( empty( $this->clone->prefix ) ) {
207
- $this->returnException( "Fatal Error: Can not delete staging site. Can not find table prefix. Contact support@wp-staging.com" );
208
  }
209
 
210
  // Check if staging prefix is the same as the live prefix
211
- if( empty( $this->options->databaseUser ) && $this->wpdb->prefix === $this->clone->prefix ) {
212
- $this->log( "Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com" );
213
- $this->returnException( "Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com" );
214
  }
215
 
216
- // Else
217
  return $this->clone->prefix;
218
  }
219
 
@@ -223,31 +227,34 @@ class Delete extends Job {
223
  * @param int $precision
224
  * @return string
225
  */
226
- public function formatSize( $bytes, $precision = 2 ) {
227
- if( ( int ) $bytes < 1 ) {
 
228
  return '';
229
  }
230
 
231
  $units = ['B', "KB", "MB", "GB", "TB"];
232
 
233
- $bytes = ( int ) $bytes;
234
- $base = log( $bytes ) / log( 1000 ); // 1024 would be for MiB KiB etc
235
- $pow = pow( 1000, $base - floor( $base ) ); // Same rule for 1000
236
 
237
- return round( $pow, $precision ) . ' ' . $units[( int ) floor( $base )];
238
  }
239
 
240
  /**
241
  * @return false
242
  */
243
- public function getClone() {
 
244
  return $this->clone;
245
  }
246
 
247
  /**
248
  * @return null|object
249
  */
250
- public function getTables() {
 
251
  return $this->tables;
252
  }
253
 
@@ -256,87 +263,95 @@ class Delete extends Job {
256
  * @param null|array $clone
257
  * @return bool
258
  */
259
- public function start( $clone = null ) {
 
260
  // Set data
261
- $this->setData( $clone );
262
 
263
  // Get the job first
264
  $this->getJob();
265
 
266
- $method = "delete" . ucwords( $this->job->current );
267
  return $this->{$method}();
268
  }
269
 
270
  /**
271
  * Get job data
272
  */
273
- private function getJob() {
274
- $this->job = $this->cache->get( "delete_job_{$this->clone->name}" );
 
275
 
276
 
277
- if( $this->job !== null ) {
278
  return;
279
  }
280
 
281
  // Generate JOB
282
- $this->job = ( object ) [
283
- "current" => "tables",
284
- "nextDirectoryToDelete" => $this->clone->path,
285
- "name" => $this->clone->name
286
  ];
287
 
288
- $this->cache->save( "delete_job_{$this->clone->name}", $this->job );
289
  }
290
 
291
  /**
292
  * @return bool
293
  */
294
- private function updateJob() {
295
- $this->job->nextDirectoryToDelete = trim( $this->job->nextDirectoryToDelete );
296
- return $this->cache->save( "delete_job_{$this->clone->name}", $this->job );
 
297
  }
298
 
299
  /**
300
  * @return array
301
  */
302
- private function getTablesToRemove() {
 
303
  $tables = $this->getTableNames();
304
 
305
- if( !isset( $_POST["excludedTables"] ) || !is_array( $_POST["excludedTables"] ) || empty( $_POST["excludedTables"] ) ) {
306
  return $tables;
307
  }
308
 
309
- return array_diff( $tables, $_POST["excludedTables"] );
310
  }
311
 
312
  /**
313
  * @return array
314
  */
315
- private function getTableNames() {
316
- return (!is_array( $this->tables )) ? [] : array_map( function($value) {
317
- return ($value->name);
318
- }, $this->tables );
 
319
  }
320
 
321
  /**
322
  * Delete Tables
 
 
323
  */
324
- public function deleteTables() {
325
- if( $this->isOverThreshold() ) {
326
- $this->log( "Deleting: Is over threshold", Logger::TYPE_INFO );
 
327
  return;
328
  }
329
 
330
  $tables = $this->getTablesToRemove();
331
 
332
- foreach ( $tables as $table ) {
333
  // PROTECTION: Never delete any table that beginns with wp prefix of live site
334
- if( !$this->isExternalDatabase() && $this->startsWith( $table, $this->wpdb->prefix ) ) {
335
- $this->log( "Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL );
336
  return false;
337
  }
338
 
339
- $this->wpdb->query( "DROP TABLE {$table}" );
340
  }
341
 
342
  // Move on to the next
@@ -344,23 +359,12 @@ class Delete extends Job {
344
  $this->updateJob();
345
  }
346
 
347
- /**
348
- * Check if a strings start with a specific string
349
- * @param string $haystack
350
- * @param string $needle
351
- * @return bool
352
- */
353
- protected function startsWith( $haystack, $needle ) {
354
- $length = strlen( $needle );
355
- return ($needle === substr( $haystack, 0, $length ));
356
- }
357
-
358
  /**
359
  *
360
  * Delete complete directory including all files and sub folders
361
  * @throws \Exception
362
  */
363
- public function deleteDirectory()
364
  {
365
  if ($this->isFatalError()) {
366
  $this->returnException('Can not delete directory: ' . $this->deleteDir . '. This seems to be the root directory. Exclude this directory from deleting and try again.');
@@ -368,10 +372,11 @@ class Delete extends Job {
368
  }
369
 
370
  // Finished or path does not exist
371
- if (empty( $this->deleteDir ) ||
 
372
  $this->deleteDir == get_home_path() ||
373
- !is_dir( $this->deleteDir)) {
374
-
375
  $this->job->current = "finish";
376
  $this->updateJob();
377
  return $this->deleteFinish();
@@ -380,7 +385,7 @@ class Delete extends Job {
380
  $this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
381
 
382
  // Make sure the root dir is never deleted!
383
- if($this->deleteDir == get_home_path()) {
384
  $this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
385
  $this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
386
  }
@@ -410,11 +415,11 @@ class Delete extends Job {
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));
420
  }
@@ -428,64 +433,67 @@ class Delete extends Job {
428
  * @param string $dir
429
  * @return bool
430
  */
431
- private function isNotEmpty( $dir ) {
 
432
  // Throw fatal error if the folder has still not been deleted and there are files in it
433
  $isDirNotEmpty = false;
434
- if( is_dir( $dir ) ) {
435
- $iterator = new \FilesystemIterator( $dir );
436
  $isDirNotEmpty = $iterator->valid();
437
  }
438
  return $isDirNotEmpty;
439
  }
440
 
441
  /**
442
- *
443
  * @return boolean
444
  */
445
- public function isFatalError() {
446
- $homePath = rtrim( get_home_path(), "/" );
 
447
  return $homePath == rtrim($this->deleteDir, "/");
448
  }
449
 
450
  /**
451
  * Finish / Update Existing Clones
452
  */
453
- public function deleteFinish() {
 
454
 
455
  $response = [
456
  'delete' => 'finished',
457
  ];
458
 
459
- $existingClones = get_option( "wpstg_existing_clones_beta", [] );
460
 
461
  // Check if clone exist and then remove it from options
462
- $this->log( "Verifying existing clones..." );
463
- foreach ( $existingClones as $name => $clone ) {
464
- if( $clone["path"] == $this->clone->path ) {
465
- unset( $existingClones[$name] );
466
  }
467
  }
468
 
469
- if( update_option( "wpstg_existing_clones_beta", $existingClones ) === false ) {
470
- $this->log( "Delete: Nothing to save.'" );
471
  }
472
 
473
  // Delete cached file
474
- $this->cache->delete( "delete_job_{$this->clone->name}" );
475
- $this->cache->delete( "delete_directories_{$this->clone->name}" );
476
- $this->cache->delete( "clone_options" );
477
 
478
- wp_die( json_encode( $response ) );
479
  }
480
 
481
  /**
482
  * Check if there is error in external database connection
483
  * can happen if the external database does not exist or stored credentials are wrong
484
  * @return bool
485
- *
486
  * @todo replace it logic with DbInfo once collation check PR is merged.
487
  */
488
- private function isExternalDatabaseError()
489
  {
490
  $db = new \mysqli($this->clone->databaseServer, $this->clone->databaseUser, $this->clone->databasePassword, $this->clone->databaseDatabase);
491
  if ($db->connect_error) {
@@ -494,5 +502,4 @@ class Delete extends Job {
494
 
495
  return false;
496
  }
497
-
498
  }
6
  use WPStaging\Core\Utils\Logger;
7
  use WPStaging\Core\WPStaging;
8
  use WPStaging\Framework\Filesystem\Filesystem;
9
+ use WPStaging\Framework\Utils\Strings;
10
 
11
  /**
12
  * Class Delete
13
  * @package WPStaging\Backend\Modules\Jobs
14
  */
15
+ class Delete extends Job
16
+ {
17
 
18
  /**
19
  * @var \stdClass
43
 
44
  /**
45
  *
46
+ * @var object
47
  */
48
  public $wpdb;
49
 
51
  *
52
  * @var bool
53
  */
54
+ private $isExternalDb;
55
 
56
+ public function __construct($isExternal = false)
57
+ {
58
  parent::__construct();
59
+ $this->isExternalDb = $isExternal;
60
 
61
+ $this->deleteDir = !empty($_POST['deleteDir']) ? urldecode($_POST['deleteDir']) : '';
62
  }
63
 
64
  /**
66
  * @param null|array $clone
67
  * @return bool
68
  */
69
+ public function setData($clone = null)
70
  {
71
+ if (!is_array($clone)) {
72
  $this->getCloneRecords();
73
  } else {
74
+ $this->clone = (object)$clone;
75
  $this->forceDeleteDirectories = true;
76
  }
77
 
78
+ if (!$this->isExternalDatabase()) {
79
  $this->wpdb = WPStaging::getInstance()->get("wpdb");
80
  $this->getTableRecords();
81
+ return true;
82
  }
83
+
84
  if ($this->isExternalDatabaseError()) {
85
  return false;
86
  }
87
+
88
  $this->wpdb = $this->getStagingDb();
89
  $this->getTableRecords();
90
  return true;
93
  /**
94
  * Get database object to interact with
95
  */
96
+ private function getStagingDb()
97
+ {
98
+ return new \wpdb($this->clone->databaseUser, $this->clone->databasePassword, $this->clone->databaseDatabase, $this->clone->databaseServer);
99
  }
100
 
101
  /**
102
  * Date database name
103
  * @return string
104
  */
105
+ public function getDbName()
106
+ {
107
  return $this->wpdb->dbname;
108
  }
109
 
111
  * Check if external database is used
112
  * @return boolean
113
  */
114
+ protected function isExternalDatabase()
115
+ {
116
+ if ($this->isExternalDb) {
117
  return true;
118
  }
119
 
120
+ if (!empty($this->clone->databaseUser)) {
121
  return true;
122
  }
123
+
124
  return false;
125
  }
126
 
129
  * @param null|string $name
130
  * @throws CloneNotFoundException
131
  */
132
+ private function getCloneRecords($name = null)
133
+ {
134
+ if ($name === null && !isset($_POST["clone"])) {
135
+ $this->log("Clone name is not set", Logger::TYPE_FATAL);
136
+ $this->returnException("Clone name is not set");
137
  }
138
 
139
+ if ($name === null) {
140
  $name = (string)$_POST["clone"];
141
  }
142
 
143
+ $clones = get_option("wpstg_existing_clones_beta", []);
144
 
145
+ if (empty($clones) || !isset($clones[$name])) {
146
+ $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
147
+ $this->returnException("Couldn't find clone name {$name} or no existing clone");
148
  }
149
 
150
+ $this->clone = $clones[$name];
151
  $this->clone["name"] = $name;
152
 
153
+ $this->clone = (object)$this->clone;
154
 
155
+ unset($clones);
156
  }
157
 
158
  /**
159
  * Get Tables
160
  */
161
+ private function getTableRecords()
162
+ {
163
 
164
+ $stagingPrefix = $this->getStagingPrefix($this->isExternalDatabase(), $this->clone);
165
 
166
  // Escape "_" to allow searching for that character
167
  $prefix = wpstg_replace_last_match('_', '\_', $stagingPrefix);
168
 
169
+ $tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$prefix}%'");
170
 
171
  $this->tables = [];
172
 
173
  // no results
174
+ if ($tables !== null) {
175
+ foreach ($tables as $table) {
176
  $this->tables[] = [
177
  "name" => $table->Name,
178
+ "size" => $this->formatSize(($table->Data_length + $table->Index_length))
179
  ];
180
  }
181
  }
182
 
183
+ $this->tables = json_decode(json_encode($this->tables));
184
  }
185
 
186
  /**
187
  * Check and return prefix of the staging site
188
  */
189
+ private function getStagingPrefix()
190
+ {
191
+ if ($this->isExternalDatabase() && !empty($this->clone->prefix)) {
192
  return $this->clone->prefix;
193
  }
194
 
195
  // Prefix not defined! Happens if staging site has been generated with older version of wpstg
196
  // Try to get staging prefix from wp-config.php of staging site
197
+ if (empty($this->clone->prefix)) {
198
+ $path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
199
+ if (($content = @file_get_contents($path)) === false) {
200
+ $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
201
+ }
202
+ preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
 
 
203
 
204
+ if (!empty($matches[1])) {
205
+ $this->clone->prefix = $matches[1];
206
+ } else {
207
+ $this->returnException("Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
 
208
  }
209
  }
210
 
211
+ if (empty($this->clone->prefix)) {
212
+ $this->returnException("Fatal Error: Can not delete staging site. Can not find table prefix. Contact support@wp-staging.com");
213
  }
214
 
215
  // Check if staging prefix is the same as the live prefix
216
+ if (empty($this->options->databaseUser) && $this->wpdb->prefix === $this->clone->prefix) {
217
+ $this->log("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the production site. Stopping for security reasons. Go to Sites > Actions > Edit Data and correct the table prefix or contact us.");
218
+ $this->returnException("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the production site. Stopping for security reasons. Go to Sites > Actions > Edit Data and correct the table prefix or contact us");
219
  }
220
 
 
221
  return $this->clone->prefix;
222
  }
223
 
227
  * @param int $precision
228
  * @return string
229
  */
230
+ public function formatSize($bytes, $precision = 2)
231
+ {
232
+ if ((int)$bytes < 1) {
233
  return '';
234
  }
235
 
236
  $units = ['B', "KB", "MB", "GB", "TB"];
237
 
238
+ $bytes = (int)$bytes;
239
+ $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
240
+ $pow = pow(1000, $base - floor($base)); // Same rule for 1000
241
 
242
+ return round($pow, $precision) . ' ' . $units[(int)floor($base)];
243
  }
244
 
245
  /**
246
  * @return false
247
  */
248
+ public function getClone()
249
+ {
250
  return $this->clone;
251
  }
252
 
253
  /**
254
  * @return null|object
255
  */
256
+ public function getTables()
257
+ {
258
  return $this->tables;
259
  }
260
 
263
  * @param null|array $clone
264
  * @return bool
265
  */
266
+ public function start($clone = null)
267
+ {
268
  // Set data
269
+ $this->setData($clone);
270
 
271
  // Get the job first
272
  $this->getJob();
273
 
274
+ $method = "delete" . ucwords($this->job->current);
275
  return $this->{$method}();
276
  }
277
 
278
  /**
279
  * Get job data
280
  */
281
+ private function getJob()
282
+ {
283
+ $this->job = $this->cache->get("delete_job_{$this->clone->name}");
284
 
285
 
286
+ if ($this->job !== null) {
287
  return;
288
  }
289
 
290
  // Generate JOB
291
+ $this->job = (object)[
292
+ "current" => "tables",
293
+ "nextDirectoryToDelete" => $this->clone->path,
294
+ "name" => $this->clone->name
295
  ];
296
 
297
+ $this->cache->save("delete_job_{$this->clone->name}", $this->job);
298
  }
299
 
300
  /**
301
  * @return bool
302
  */
303
+ private function updateJob()
304
+ {
305
+ $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
306
+ return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
307
  }
308
 
309
  /**
310
  * @return array
311
  */
312
+ private function getTablesToRemove()
313
+ {
314
  $tables = $this->getTableNames();
315
 
316
+ if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"])) {
317
  return $tables;
318
  }
319
 
320
+ return array_diff($tables, $_POST["excludedTables"]);
321
  }
322
 
323
  /**
324
  * @return array
325
  */
326
+ private function getTableNames()
327
+ {
328
+ return (!is_array($this->tables)) ? [] : array_map(function ($value) {
329
+ return ($value->name);
330
+ }, $this->tables);
331
  }
332
 
333
  /**
334
  * Delete Tables
335
+ *
336
+ * @todo DRY the code by implementing through WPStaging\Framework\Database\TableService::deleteTablesStartWith
337
  */
338
+ public function deleteTables()
339
+ {
340
+ if ($this->isOverThreshold()) {
341
+ $this->log("Deleting: Is over threshold", Logger::TYPE_INFO);
342
  return;
343
  }
344
 
345
  $tables = $this->getTablesToRemove();
346
 
347
+ foreach ($tables as $table) {
348
  // PROTECTION: Never delete any table that beginns with wp prefix of live site
349
+ if (!$this->isExternalDatabase() && (new Strings())->startsWith($table, $this->wpdb->prefix)) {
350
+ $this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
351
  return false;
352
  }
353
 
354
+ $this->wpdb->query("DROP TABLE {$table}");
355
  }
356
 
357
  // Move on to the next
359
  $this->updateJob();
360
  }
361
 
 
 
 
 
 
 
 
 
 
 
 
362
  /**
363
  *
364
  * Delete complete directory including all files and sub folders
365
  * @throws \Exception
366
  */
367
+ public function deleteDirectory()
368
  {
369
  if ($this->isFatalError()) {
370
  $this->returnException('Can not delete directory: ' . $this->deleteDir . '. This seems to be the root directory. Exclude this directory from deleting and try again.');
372
  }
373
 
374
  // Finished or path does not exist
375
+ if (
376
+ empty($this->deleteDir) ||
377
  $this->deleteDir == get_home_path() ||
378
+ !is_dir($this->deleteDir)
379
+ ) {
380
  $this->job->current = "finish";
381
  $this->updateJob();
382
  return $this->deleteFinish();
385
  $this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
386
 
387
  // Make sure the root dir is never deleted!
388
+ if ($this->deleteDir == get_home_path()) {
389
  $this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
390
  $this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
391
  }
415
  // Throw fatal error if the folder has still not been deleted and there are files in it
416
  if ($this->isNotEmpty($this->deleteDir)) {
417
  $response = [
418
+ 'job' => 'delete',
419
+ 'status' => true,
420
+ 'delete' => $deleteStatus,
421
  'message' => $errorMessage,
422
+ 'error' => true,
423
  ];
424
  wp_die(json_encode($response));
425
  }
433
  * @param string $dir
434
  * @return bool
435
  */
436
+ private function isNotEmpty($dir)
437
+ {
438
  // Throw fatal error if the folder has still not been deleted and there are files in it
439
  $isDirNotEmpty = false;
440
+ if (is_dir($dir)) {
441
+ $iterator = new \FilesystemIterator($dir);
442
  $isDirNotEmpty = $iterator->valid();
443
  }
444
  return $isDirNotEmpty;
445
  }
446
 
447
  /**
448
+ *
449
  * @return boolean
450
  */
451
+ public function isFatalError()
452
+ {
453
+ $homePath = rtrim(get_home_path(), "/");
454
  return $homePath == rtrim($this->deleteDir, "/");
455
  }
456
 
457
  /**
458
  * Finish / Update Existing Clones
459
  */
460
+ public function deleteFinish()
461
+ {
462
 
463
  $response = [
464
  'delete' => 'finished',
465
  ];
466
 
467
+ $existingClones = get_option("wpstg_existing_clones_beta", []);
468
 
469
  // Check if clone exist and then remove it from options
470
+ $this->log("Verifying existing clones...");
471
+ foreach ($existingClones as $name => $clone) {
472
+ if ($clone["path"] == $this->clone->path) {
473
+ unset($existingClones[$name]);
474
  }
475
  }
476
 
477
+ if (update_option("wpstg_existing_clones_beta", $existingClones) === false) {
478
+ $this->log("Delete: Nothing to save.'");
479
  }
480
 
481
  // Delete cached file
482
+ $this->cache->delete("delete_job_{$this->clone->name}");
483
+ $this->cache->delete("delete_directories_{$this->clone->name}");
484
+ $this->cache->delete("clone_options");
485
 
486
+ wp_die(json_encode($response));
487
  }
488
 
489
  /**
490
  * Check if there is error in external database connection
491
  * can happen if the external database does not exist or stored credentials are wrong
492
  * @return bool
493
+ *
494
  * @todo replace it logic with DbInfo once collation check PR is merged.
495
  */
496
+ private function isExternalDatabaseError()
497
  {
498
  $db = new \mysqli($this->clone->databaseServer, $this->clone->databaseUser, $this->clone->databasePassword, $this->clone->databaseDatabase);
499
  if ($db->connect_error) {
502
 
503
  return false;
504
  }
 
505
  }
Backend/Modules/Jobs/Directories.php CHANGED
@@ -2,16 +2,15 @@
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
- // No Direct Access
6
- if (!defined("WPINC")) {
7
- die;
8
- }
9
-
10
  use Exception;
11
- use WPStaging\Core\WPStaging;
12
- use WPStaging\Framework\Utils\Strings;
13
  use WPStaging\Core\Iterators\RecursiveDirectoryIterator;
14
  use WPStaging\Core\Iterators\RecursiveFilterExclude;
 
 
 
 
 
15
 
16
  /**
17
  * Class Files
@@ -19,6 +18,7 @@ use WPStaging\Core\Iterators\RecursiveFilterExclude;
19
  */
20
  class Directories extends JobExecutable
21
  {
 
22
 
23
  /**
24
  * @var array
@@ -51,6 +51,10 @@ class Directories extends JobExecutable
51
  */
52
  protected function calculateTotalSteps()
53
  {
 
 
 
 
54
 
55
  $this->options->totalSteps = $this->total + count($this->options->extraDirectories);
56
  }
@@ -61,14 +65,13 @@ class Directories extends JobExecutable
61
  */
62
  public function start()
63
  {
64
-
65
  // Execute steps
66
  $this->run();
67
 
68
  // Save option, progress
69
  $this->saveProgress();
70
 
71
- return ( object )$this->response;
72
  }
73
 
74
  /**
@@ -78,25 +81,14 @@ class Directories extends JobExecutable
78
  */
79
  private function getWpRootFiles()
80
  {
81
-
82
  // open file handle
83
  $files = $this->open($this->filename, 'a');
84
 
85
- try {
86
 
 
87
  // Iterate over wp root directory
88
- $iterator = new \DirectoryIterator(WPStaging::getWPpath());
89
-
90
- $this->log("Scanning / for files");
91
-
92
- // Write path line
93
- foreach ($iterator as $item) {
94
- if (!$item->isDot() && $item->isFile()) {
95
- if ($this->write($files, $iterator->getFilename() . PHP_EOL)) {
96
- $this->options->totalFiles++;
97
- }
98
- }
99
- }
100
  } catch (Exception $e) {
101
  $this->returnException('Error: ' . $e->getMessage());
102
  }
@@ -106,66 +98,43 @@ class Directories extends JobExecutable
106
  }
107
 
108
  /**
109
- * Step 2
110
  * Get WP Content Files
111
  */
112
  private function getWpContentFiles()
113
  {
114
-
115
  // Skip it
116
- if ($this->isDirectoryExcluded(WP_CONTENT_DIR)) {
117
- $this->log("Skip " . WPStaging::getWPpath() . WP_CONTENT_DIR);
118
  return true;
119
  }
120
  // open file handle
121
  $files = $this->open($this->filename, 'a');
122
 
123
- /**
124
- * Excluded folders relative to the folder to iterate
125
- */
126
- $excludePaths = [
127
- 'cache',
128
- 'plugins/wps-hide-login',
129
- 'uploads/sites'
130
- ];
131
 
132
- /**
133
- * Get user excluded folders
134
- */
135
- $directory = [];
136
- foreach ($this->options->excludedDirectories as $dir) {
137
- $dir = wpstg_replace_windows_directory_separator($dir);
138
- $wpContentDir = wpstg_replace_windows_directory_separator(WP_CONTENT_DIR);
139
 
140
- if (strpos($dir, $wpContentDir) !== false) {
141
- $directory[] = ltrim(str_replace($wpContentDir, '', $dir), '/\\');
142
- }
 
 
 
 
 
 
 
 
 
 
143
  }
144
 
145
- $excludePaths = array_merge($excludePaths, $directory);
146
-
147
  try {
148
-
149
- // Iterate over content directory
150
- $iterator = new RecursiveDirectoryIterator(WP_CONTENT_DIR);
151
-
152
- // Exclude uploads, plugins or themes
153
- $iterator = new RecursiveFilterExclude($iterator, apply_filters('wpstg_clone_excl_folders', $excludePaths));
154
-
155
- // Recursively iterate over content directory
156
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
157
-
158
- $this->log("Scanning /wp-content folder " . WP_CONTENT_DIR);
159
-
160
- // Write path line
161
- foreach ($iterator as $item) {
162
- if ($item->isFile()) {
163
- $wpContentDir = str_replace(ABSPATH, '', WP_CONTENT_DIR);
164
- $file = $wpContentDir . '/' . $iterator->getSubPathName() . PHP_EOL;
165
- if ($this->write($files, $file)) {
166
- $this->options->totalFiles++;
167
- }
168
- }
169
  }
170
  } catch (Exception $e) {
171
  $this->returnException('Error: ' . $e->getMessage());
@@ -183,33 +152,22 @@ class Directories extends JobExecutable
183
  */
184
  private function getWpIncludesFiles()
185
  {
186
-
187
  // Skip it
188
- if ($this->isDirectoryExcluded(WPStaging::getWPpath() . 'wp-includes/')) {
189
- $this->log("Skip " . WPStaging::getWPpath() . 'wp-includes/');
190
  return true;
191
  }
192
-
193
- // open file handle and attach data to end of file
194
  $files = $this->open($this->filename, 'a');
195
 
196
- try {
197
-
198
- // Iterate over wp-admin directory
199
- $iterator = new RecursiveDirectoryIterator(WPStaging::getWPpath() . 'wp-includes/');
200
-
201
- // Recursively iterate over wp-includes directory
202
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
203
-
204
- $this->log("Scanning /wp-includes for its sub-directories and files");
205
 
206
- // Write files
207
- foreach ($iterator as $item) {
208
- if ($item->isFile()) {
209
- if ($this->write($files, 'wp-includes/' . $iterator->getSubPathName() . PHP_EOL)) {
210
- $this->options->totalFiles++;
211
- }
212
- }
213
  }
214
  } catch (Exception $e) {
215
  $this->returnException('Error: ' . $e->getMessage());
@@ -227,36 +185,74 @@ class Directories extends JobExecutable
227
  */
228
  private function getWpAdminFiles()
229
  {
230
-
231
  // Skip it
232
- if ($this->isDirectoryExcluded(WPStaging::getWPpath() . 'wp-admin/')) {
233
- $this->log("Skip " . WPStaging::getWPpath() . 'wp-admin/');
234
  return true;
235
  }
236
-
237
- // open file handle and attach data to end of file
238
  $files = $this->open($this->filename, 'a');
239
 
 
 
 
 
240
  try {
 
 
 
 
 
 
241
 
242
- // Iterate over wp-admin directory
243
- $iterator = new RecursiveDirectoryIterator(WPStaging::getWPpath() . 'wp-admin/');
 
 
244
 
245
- // Recursively iterate over content directory
246
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
 
 
 
 
 
 
 
 
247
 
248
- $this->log("Scanning /wp-admin for its sub-directories and files");
 
 
 
249
 
250
- // Write path line
251
- foreach ($iterator as $item) {
252
- if ($item->isFile()) {
253
- if ($this->write($files, 'wp-admin/' . $iterator->getSubPathName() . PHP_EOL)) {
254
- $this->options->totalFiles++;
255
- // Too much cpu time
256
- //$this->options->totalFileSize += $iterator->getSize();
257
- }
258
- }
259
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  } catch (Exception $e) {
261
  $this->returnException('Error: ' . $e->getMessage());
262
  }
@@ -267,9 +263,12 @@ class Directories extends JobExecutable
267
  }
268
 
269
  /**
270
- * Step 4 - x
 
271
  * Get extra folders of the wp root level
272
  * Does not collect wp-includes, wp-admin and wp-content folder
 
 
273
  */
274
  private function getExtraFiles($folder)
275
  {
@@ -282,7 +281,6 @@ class Directories extends JobExecutable
282
  $files = $this->open($this->filename, 'a');
283
 
284
  try {
285
-
286
  // Iterate over extra directory
287
  $iterator = new RecursiveDirectoryIterator($folder);
288
 
@@ -290,15 +288,14 @@ class Directories extends JobExecutable
290
 
291
  $iterator = new RecursiveFilterExclude($iterator, $exclude);
292
  // Recursively iterate over content directory
293
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
294
 
295
- $strings = new Strings();
296
- $this->log("Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files");
297
 
298
  // Write path line
299
  foreach ($iterator as $item) {
300
  if ($item->isFile()) {
301
- $path = str_replace(wpstg_replace_windows_directory_separator(WPStaging::getWPpath()), '', wpstg_replace_windows_directory_separator($folder)) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL;
302
  if ($this->write($files, $path)) {
303
  $this->options->totalFiles++;
304
  }
@@ -404,6 +401,12 @@ class Directories extends JobExecutable
404
  return false;
405
  }
406
 
 
 
 
 
 
 
407
  if (isset($this->options->extraDirectories[$this->options->currentStep - $this->total])) {
408
  $this->getExtraFiles($this->options->extraDirectories[$this->options->currentStep - $this->total]);
409
  $this->prepareResponse(false, true);
@@ -455,19 +458,6 @@ class Directories extends JobExecutable
455
  $this->files = explode(PHP_EOL, $this->files);
456
  }
457
 
458
- /**
459
- * Replace forward slash with current directory separator
460
- *
461
- * @param string $path Path
462
- *
463
- * @return string
464
- */
465
- private function sanitizeDirectorySeparator($path)
466
- {
467
- $string = str_replace("/", "\\", $path);
468
- return str_replace('\\\\', '\\', $string);
469
- }
470
-
471
  /**
472
  * Check if directory is excluded
473
  * @param string $directory
@@ -475,15 +465,18 @@ class Directories extends JobExecutable
475
  */
476
  protected function isDirectoryExcluded($directory)
477
  {
478
- $directory = $this->sanitizeDirectorySeparator($directory);
479
- foreach ($this->options->excludedDirectories as $excludedDirectory) {
480
- $excludedDirectory = $this->sanitizeDirectorySeparator($excludedDirectory);
481
- if (strpos(trailingslashit($directory), trailingslashit($excludedDirectory)) === 0) {
482
- return true;
 
 
 
 
483
  }
484
  }
485
 
486
- return false;
487
  }
488
-
489
  }
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
 
 
 
 
 
5
  use Exception;
6
+ use RecursiveIteratorIterator;
 
7
  use WPStaging\Core\Iterators\RecursiveDirectoryIterator;
8
  use WPStaging\Core\Iterators\RecursiveFilterExclude;
9
+ use WPStaging\Core\WPStaging;
10
+ use WPStaging\Framework\CloningProcess\ExcludedPlugins;
11
+ use WPStaging\Framework\Traits\FileScanToCacheTrait;
12
+ use WPStaging\Framework\Utils\Strings;
13
+ use WPStaging\Framework\Utils\WpDefaultDirectories;
14
 
15
  /**
16
  * Class Files
18
  */
19
  class Directories extends JobExecutable
20
  {
21
+ use FileScanToCacheTrait;
22
 
23
  /**
24
  * @var array
51
  */
52
  protected function calculateTotalSteps()
53
  {
54
+ // Set total to 5 for multisite
55
+ if ($this->isMultisiteAndPro()) {
56
+ $this->total = 5;
57
+ }
58
 
59
  $this->options->totalSteps = $this->total + count($this->options->extraDirectories);
60
  }
65
  */
66
  public function start()
67
  {
 
68
  // Execute steps
69
  $this->run();
70
 
71
  // Save option, progress
72
  $this->saveProgress();
73
 
74
+ return (object)$this->response;
75
  }
76
 
77
  /**
81
  */
82
  private function getWpRootFiles()
83
  {
 
84
  // open file handle
85
  $files = $this->open($this->filename, 'a');
86
 
87
+ $this->log("Scanning / for its files");
88
 
89
+ try {
90
  // Iterate over wp root directory
91
+ $this->options->totalFiles = $this->scanToCacheFile($files, ABSPATH);
 
 
 
 
 
 
 
 
 
 
 
92
  } catch (Exception $e) {
93
  $this->returnException('Error: ' . $e->getMessage());
94
  }
98
  }
99
 
100
  /**
101
+ * Step 1
102
  * Get WP Content Files
103
  */
104
  private function getWpContentFiles()
105
  {
106
+ $directory = WP_CONTENT_DIR;
107
  // Skip it
108
+ if ($this->isDirectoryExcluded($directory)) {
109
+ $this->log("Skip " . $directory);
110
  return true;
111
  }
112
  // open file handle
113
  $files = $this->open($this->filename, 'a');
114
 
115
+ $relativeDirectory = str_replace(ABSPATH, '', $directory);
116
+ $this->log("Scanning " . $relativeDirectory . " for its sub-directories and files");
 
 
 
 
 
 
117
 
118
+ $paths = $this->filteredSelectedDirectories($directory, $this->options->includedDirectories);
 
 
 
 
 
 
119
 
120
+ $excludePaths = [
121
+ trailingslashit(WP_CONTENT_DIR) . 'uploads/sites',
122
+ trailingslashit(WP_CONTENT_DIR) . 'cache',
123
+ rtrim(WPStaging::getContentDir(), '/'),
124
+ '**/node_modules',
125
+ ];
126
+ // add excluded plugins defined by WP Staging
127
+ $excludePaths = array_merge((new ExcludedPlugins())->getPluginsToExcludeWithAbsolutePaths(), $excludePaths);
128
+ $excludePaths = array_merge($this->options->excludedDirectories, $excludePaths);
129
+ if ($this->isMultisiteAndPro()) {
130
+ $excludePaths = apply_filters('wpstg_clone_mu_excl_folders', $excludePaths);
131
+ } else {
132
+ $excludePaths = apply_filters('wpstg_clone_excl_folders', $excludePaths);
133
  }
134
 
 
 
135
  try {
136
+ foreach ($paths as $path) {
137
+ $this->options->totalFiles += $this->scanToCacheFile($files, $path->path, $path->flag === Scan::IS_RECURSIVE, $excludePaths);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
139
  } catch (Exception $e) {
140
  $this->returnException('Error: ' . $e->getMessage());
152
  */
153
  private function getWpIncludesFiles()
154
  {
155
+ $directory = ABSPATH . 'wp-includes';
156
  // Skip it
157
+ if ($this->isDirectoryExcluded($directory)) {
158
+ $this->log("Skip " . $directory);
159
  return true;
160
  }
161
+ // open file handle
 
162
  $files = $this->open($this->filename, 'a');
163
 
164
+ $relativeDirectory = str_replace(ABSPATH, '', $directory);
165
+ $this->log("Scanning " . $relativeDirectory . " for its sub-directories and files");
 
 
 
 
 
 
 
166
 
167
+ $paths = $this->filteredSelectedDirectories($directory, $this->options->includedDirectories);
168
+ try {
169
+ foreach ($paths as $path) {
170
+ $this->options->totalFiles += $this->scanToCacheFile($files, $path->path, $path->flag === Scan::IS_RECURSIVE);
 
 
 
171
  }
172
  } catch (Exception $e) {
173
  $this->returnException('Error: ' . $e->getMessage());
185
  */
186
  private function getWpAdminFiles()
187
  {
188
+ $directory = ABSPATH . 'wp-admin';
189
  // Skip it
190
+ if ($this->isDirectoryExcluded($directory)) {
191
+ $this->log("Skip " . $directory);
192
  return true;
193
  }
194
+ // open file handle
 
195
  $files = $this->open($this->filename, 'a');
196
 
197
+ $relativeDirectory = str_replace(ABSPATH, '', $directory);
198
+ $this->log("Scanning " . $relativeDirectory . " for its sub-directories and files");
199
+
200
+ $paths = $this->filteredSelectedDirectories($directory, $this->options->includedDirectories);
201
  try {
202
+ foreach ($paths as $path) {
203
+ $this->options->totalFiles += $this->scanToCacheFile($files, $path->path, $path->flag === Scan::IS_RECURSIVE);
204
+ }
205
+ } catch (Exception $e) {
206
+ $this->returnException('Error: ' . $e->getMessage());
207
+ }
208
 
209
+ // close the file handler
210
+ $this->close($files);
211
+ return true;
212
+ }
213
 
214
+ /**
215
+ * Step 4 (Multisite Only)
216
+ * Get WP Content Uploads Files multisite folder wp-content/uploads/sites or wp-content/blogs.dir/ID/files
217
+ */
218
+ private function getWpContentUploadsSites()
219
+ {
220
+ // Skip if main site is cloned
221
+ if (is_main_site()) {
222
+ return true;
223
+ }
224
 
225
+ // Skip if symlink option selected
226
+ if ($this->options->uploadsSymlinked) {
227
+ return true;
228
+ }
229
 
230
+ // Absolute path to uploads folder
231
+ $directory = (new WpDefaultDirectories())->getUploadsPath();
232
+
233
+ // Skip it
234
+ if (!is_dir($directory)) {
235
+ $this->log("Skipping: {$directory} does not exist.");
236
+ return true;
237
+ }
238
+
239
+ // Skip it
240
+ if ($this->isDirectoryExcluded($directory)) {
241
+ $this->log("Skipping: {$directory}");
242
+ return true;
243
+ }
244
+
245
+
246
+ // open file handle
247
+ $files = $this->open($this->filename, 'a');
248
+
249
+ $excludePaths = [
250
+ '**/node_modules',
251
+ ];
252
+ $excludePaths = array_merge($this->options->excludedDirectories, $excludePaths);
253
+
254
+ try {
255
+ $this->options->totalFiles += $this->scanToCacheFile($files, $directory, true, $excludePaths);
256
  } catch (Exception $e) {
257
  $this->returnException('Error: ' . $e->getMessage());
258
  }
263
  }
264
 
265
  /**
266
+ * Step 4 - x (Single Site)
267
+ * Step 5 - x (Multisite)
268
  * Get extra folders of the wp root level
269
  * Does not collect wp-includes, wp-admin and wp-content folder
270
+ * @param string $folder
271
+ * @return boolean
272
  */
273
  private function getExtraFiles($folder)
274
  {
281
  $files = $this->open($this->filename, 'a');
282
 
283
  try {
 
284
  // Iterate over extra directory
285
  $iterator = new RecursiveDirectoryIterator($folder);
286
 
288
 
289
  $iterator = new RecursiveFilterExclude($iterator, $exclude);
290
  // Recursively iterate over content directory
291
+ $iterator = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD);
292
 
293
+ $this->log("Scanning {$this->strUtil->getLastElemAfterString( '/', $folder )} for its sub-directories and files");
 
294
 
295
  // Write path line
296
  foreach ($iterator as $item) {
297
  if ($item->isFile()) {
298
+ $path = str_replace($this->strUtil->sanitizeDirectorySeparator(ABSPATH), '', $this->strUtil->sanitizeDirectorySeparator($folder)) . DIRECTORY_SEPARATOR . $item->getSubPathname() . PHP_EOL;
299
  if ($this->write($files, $path)) {
300
  $this->options->totalFiles++;
301
  }
401
  return false;
402
  }
403
 
404
+ if ($this->isMultisiteAndPro() && $this->options->currentStep == 4) {
405
+ $this->getWpContentUploadsSites();
406
+ $this->prepareResponse(false, true);
407
+ return false;
408
+ }
409
+
410
  if (isset($this->options->extraDirectories[$this->options->currentStep - $this->total])) {
411
  $this->getExtraFiles($this->options->extraDirectories[$this->options->currentStep - $this->total]);
412
  $this->prepareResponse(false, true);
458
  $this->files = explode(PHP_EOL, $this->files);
459
  }
460
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  /**
462
  * Check if directory is excluded
463
  * @param string $directory
465
  */
466
  protected function isDirectoryExcluded($directory)
467
  {
468
+ $directory = (new Strings())->sanitizeDirectorySeparator($directory);
469
+ // check if directory is in selected included directory
470
+ foreach ($this->options->includedDirectories as $includedDirectory) {
471
+ $includedDirectory = trim($includedDirectory, ' ');
472
+ $directoryPath = explode(Scan::DIRECTORY_PATH_FLAG_SEPARATOR, $includedDirectory)[0];
473
+ $directoryPath = trim($directoryPath, ' ');
474
+ $directoryPath = (new Strings())->sanitizeDirectorySeparator($directoryPath);
475
+ if (strpos(trailingslashit($directoryPath), trailingslashit($directory)) === 0) {
476
+ return false;
477
  }
478
  }
479
 
480
+ return true;
481
  }
 
482
  }
Backend/Modules/Jobs/Exceptions/CloneNotFoundException.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Backend\Modules\Jobs\Exceptions;
3
 
4
  /**
@@ -11,4 +12,4 @@ class CloneNotFoundException extends \Exception
11
  * @var string
12
  */
13
  protected $message = "Clone name is not set or clone not found";
14
- }
1
  <?php
2
+
3
  namespace WPStaging\Backend\Modules\Jobs\Exceptions;
4
 
5
  /**
12
  * @var string
13
  */
14
  protected $message = "Clone name is not set or clone not found";
15
+ }
Backend/Modules/Jobs/Exceptions/FatalException.php CHANGED
@@ -1,10 +1,8 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Backend\Modules\Jobs\Exceptions;
5
 
6
-
7
  class FatalException extends \RuntimeException
8
  {
9
 
10
- }
1
  <?php
2
 
 
3
  namespace WPStaging\Backend\Modules\Jobs\Exceptions;
4
 
 
5
  class FatalException extends \RuntimeException
6
  {
7
 
8
+ }
Backend/Modules/Jobs/Exceptions/JobNotFoundException.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Backend\Modules\Jobs\Exceptions;
3
 
4
  /**
1
  <?php
2
+
3
  namespace WPStaging\Backend\Modules\Jobs\Exceptions;
4
 
5
  /**
Backend/Modules/Jobs/Files.php CHANGED
@@ -9,15 +9,15 @@ 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
  *
16
  * @todo Can we unify these?
17
- * \WPStaging\Backend\Modules\Jobs\Files (Free single site files)
18
- * \WPStaging\Backend\Modules\Jobs\Multisite\Files (Free multisite files)
19
- * \WPStaging\Backend\Pro\Modules\Jobs\Files (Pro single site files)
20
- * \WPStaging\Backend\Pro\Modules\Jobs\Multisite\Files (Pro multisite files)
21
  *
22
  * @package WPStaging\Backend\Modules\Jobs
23
  */
@@ -60,8 +60,13 @@ class Files extends JobExecutable
60
  $this->file = new SplFileObject($filePath, 'r');
61
  }
62
 
 
 
 
 
 
63
  // Informational logs
64
- if ($this->options->currentStep === 1) {
65
  $this->log("Copying files...");
66
  }
67
 
@@ -77,7 +82,10 @@ class Files extends JobExecutable
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,7 +103,13 @@ class Files extends JobExecutable
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;
@@ -115,6 +129,72 @@ class Files extends JobExecutable
115
  }
116
 
117
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  * @return bool
119
  */
120
  private function cleanWpContent()
@@ -136,7 +216,7 @@ class Files extends JobExecutable
136
  $this->log($log['msg'], $log['type']);
137
  $this->returnException($log['msg']);
138
  } else {
139
- $this->log($log['msg'], $log['type']);
140
  }
141
  }
142
 
@@ -153,8 +233,7 @@ class Files extends JobExecutable
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
 
@@ -209,7 +288,7 @@ class Files extends JobExecutable
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;
@@ -374,7 +453,7 @@ class Files extends JobExecutable
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
  }
@@ -439,14 +518,16 @@ class Files extends JobExecutable
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
  }
452
 
@@ -548,5 +629,4 @@ class Files extends JobExecutable
548
 
549
  return false;
550
  }
551
-
552
  }
9
  use WPStaging\Framework\Filesystem\Filesystem;
10
  use WPStaging\Framework\Filesystem\Permissions;
11
  use WPStaging\Framework\Filesystem\WpUploadsFolderSymlinker;
12
+ use WPStaging\Framework\Utils\Strings;
13
  use WPStaging\Framework\Utils\WpDefaultDirectories;
14
+
15
  /**
16
  * Class Files
17
  *
18
  * @todo Can we unify these?
19
+ * \WPStaging\Backend\Modules\Jobs\Files (Cloning copying files)
20
+ * \WPStaging\Backend\Pro\Modules\Jobs\Files (Pro push copying files)
 
 
21
  *
22
  * @package WPStaging\Backend\Modules\Jobs
23
  */
60
  $this->file = new SplFileObject($filePath, 'r');
61
  }
62
 
63
+ $logStep = 0;
64
+ if ($this->options->mainJob === 'resetting' || $this->options->mainJob === 'updating') {
65
+ $logStep = 1;
66
+ }
67
+
68
  // Informational logs
69
+ if ($this->options->currentStep === $logStep) {
70
  $this->log("Copying files...");
71
  }
72
 
82
  {
83
  $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
84
  // Add an extra step for cleaning content in themes, plugins and uploads dir
85
+ // or for deleting whole dir if resetting
86
+ if ($this->options->mainJob === 'resetting' || $this->options->mainJob === 'updating') {
87
+ $this->options->totalSteps++;
88
+ }
89
  }
90
 
91
  /**
103
  return false;
104
  }
105
 
106
+ // Clean Staging Directory if job is resetting
107
+ if (!$this->cleanStagingDirectory()) {
108
+ $this->prepareResponse(false, false);
109
+ return false;
110
+ }
111
+
112
+ // Cleaning wp-content directories: uploads, themes and plugins if selected during update
113
  if (!$this->cleanWpContent()) {
114
  $this->prepareResponse(false, false);
115
  return false;
129
  }
130
 
131
  /**
132
+ * Clean staging site directory if the mainJob is resetting
133
+ *
134
+ * @return bool
135
+ */
136
+ private function cleanStagingDirectory()
137
+ {
138
+ if ($this->options->mainJob !== 'resetting') {
139
+ return true;
140
+ }
141
+
142
+ if ($this->options->currentStep !== 0) {
143
+ return true;
144
+ }
145
+
146
+ if (rtrim($this->destination, '/') === rtrim(get_home_path(), '/')) {
147
+ $this->returnException('Can not delete directory: ' . $this->destination . '. This seems to be the root directory. Exclude this directory from deleting and try again.');
148
+ throw new \Exception('Can not delete directory: ' . $this->destination . ' This seems to be the root directory. Exclude this directory from deleting and try again.');
149
+ }
150
+
151
+ // Make sure destination is inside WordPress
152
+ $wpRoot = (new Strings())->sanitizeDirectorySeparator(ABSPATH);
153
+ $this->destination = $wpRoot . str_replace($wpRoot, '', $this->destination);
154
+
155
+ // Finished or path does not exist
156
+ if (empty($this->destination) || !is_dir($this->destination)) {
157
+ error_log('Destination is not a directory: ' . $this->destination);
158
+ $this->log(sprintf(__('Fail! Destination is not a directory! %s', 'wp-staging'), $this->destination));
159
+ return true;
160
+ }
161
+
162
+ if (!isset($this->options->filesResettingStatus)) {
163
+ $this->options->filesResettingStatus = 'pending';
164
+ $this->saveOptions();
165
+ }
166
+
167
+ if ($this->options->filesResettingStatus === 'finished') {
168
+ return true;
169
+ }
170
+
171
+ if ($this->options->filesResettingStatus === 'pending') {
172
+ $this->log(sprintf(__('Files: Resetting staging site: %s.', 'wp-staging'), $this->destination));
173
+ $this->options->filesResettingStatus = 'processing';
174
+ $this->saveOptions();
175
+ }
176
+
177
+ $fs = (new Filesystem())
178
+ ->setShouldStop([$this, 'isOverThreshold'])
179
+ ->setRecursive(true);
180
+ try {
181
+ if (!$fs->deleteNew($this->destination)) {
182
+ return false;
183
+ }
184
+ } catch (\RuntimeException $ex) {
185
+ }
186
+
187
+ $this->options->filesResettingStatus = 'finished';
188
+ $this->saveOptions();
189
+
190
+ $this->prepareResponse();
191
+ return true;
192
+ }
193
+
194
+
195
+ /**
196
+ * Clean WP Content According to option selected
197
+ *
198
  * @return bool
199
  */
200
  private function cleanWpContent()
216
  $this->log($log['msg'], $log['type']);
217
  $this->returnException($log['msg']);
218
  } else {
219
+ $this->debugLog($log['msg'], $log['type']);
220
  }
221
  }
222
 
233
  */
234
  private function getFilesAndCopy()
235
  {
236
+ if ($this->options->currentStep === 0 && ($this->options->mainJob === 'resetting' || $this->options->mainJob === 'updating')) {
 
237
  return true;
238
  }
239
 
288
  if ($this->options->mainJob === 'updating') {
289
  return true;
290
  }
291
+
292
  if (!$this->options->uploadsSymlinked) {
293
  $this->log("Skipped symlinking Wp Uploads Folder");
294
  return true;
453
  $destinationPath = $this->destination . $relativePath;
454
  $destinationDirectory = dirname($destinationPath);
455
 
456
+ if (!is_dir($destinationDirectory) && !(new Filesystem())->mkdir($destinationDirectory) && !is_dir($destinationDirectory)) {
457
  $this->log("Files: Can not create directory {$destinationDirectory}. Possible write permission error!", Logger::TYPE_ERROR);
458
  return false;
459
  }
518
  );
519
  }
520
 
521
+ if ((new Filesystem())->isFilenameExcluded($file, $excludedFiles)) {
522
  return true;
523
  }
524
 
525
  // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
526
  // because if the updating process fails, the staging site would not be accessable any longer
527
+ if (
528
+ isset($this->options->mainJob) && $this->options->mainJob === "updating"
529
+ && stripos(strrev($file), strrev("wp-config.php")) === 0
530
+ ) {
531
  return true;
532
  }
533
 
629
 
630
  return false;
631
  }
 
632
  }
Backend/Modules/Jobs/Finish.php CHANGED
@@ -3,28 +3,31 @@
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  use WPStaging\Core\WPStaging;
 
6
 
7
  /**
8
  * Class Finish
9
  * @package WPStaging\Backend\Modules\Jobs
10
  */
11
- class Finish extends Job {
 
12
 
13
  /**
14
  * Clone Key
15
- * @var string
16
  */
17
  private $clone = '';
18
 
19
  /**
20
  * Start Module
21
  * @return object
 
22
  */
23
- public function start() {
 
24
  // sanitize the clone name before saving
25
- $this->clone = preg_replace( "#\W+#", '-', strtolower( $this->options->clone ) );
26
 
27
- // Delete Cache Files
28
  $this->deleteCacheFiles();
29
 
30
  // Prepare clone records & save scanned directories for delete job later
@@ -34,7 +37,7 @@ class Finish extends Job {
34
 
35
  $return = [
36
  "directoryName" => $this->options->cloneDirectoryName,
37
- "path" => trailingslashit( $this->options->destinationDir ),
38
  "url" => $this->getDestinationUrl(),
39
  "number" => $this->options->cloneNumber,
40
  "version" => WPStaging::getVersion(),
@@ -45,23 +48,23 @@ class Finish extends Job {
45
  "percentage" => 100
46
  ];
47
 
48
- //$this->flush();
49
  do_action('wpstg_cloning_complete', $this->options);
50
 
51
- return ( object ) $return;
52
  }
53
 
54
  /**
55
  * Delete Cache Files
 
56
  */
57
- protected function deleteCacheFiles() {
58
- $this->log( "Finish: Deleting clone job's cache files..." );
 
59
 
60
- // Clean cache files
61
- $this->cache->delete( "clone_options" );
62
- $this->cache->delete( "files_to_copy" );
63
 
64
- $this->log( "Finish: Clone job's cache files have been deleted!" );
65
  }
66
 
67
  /**
@@ -71,35 +74,35 @@ class Finish extends Job {
71
  *
72
  * @return bool
73
  */
74
- protected function prepareCloneDataRecords() {
 
75
  // Check if clones still exist
76
- $this->log( "Finish: Verifying existing clones..." );
77
 
78
  // Clone data already exists
79
- if( isset( $this->options->existingClones[$this->options->clone] ) ) {
 
 
 
 
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!" );
87
  return true;
88
  }
89
 
90
- // Save new clone data
91
- $this->log( "Finish: {$this->options->clone}'s clone job's data is not in database, generating data" );
92
-
93
- // sanitize the clone name before saving
94
- //$clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
95
 
96
  $this->options->existingClones[$this->clone] = [
97
  "directoryName" => $this->options->cloneDirectoryName,
98
- "path" => trailingslashit( $this->options->destinationDir ),
99
  "url" => $this->getDestinationUrl(),
100
  "number" => $this->options->cloneNumber,
101
  "version" => WPStaging::getVersion(),
102
- //"status" => false,
103
  "status" => "finished",
104
  "prefix" => $this->options->prefix,
105
  "datetime" => time(),
@@ -112,8 +115,8 @@ class Finish extends Job {
112
  "uploadsSymlinked" => (bool) $this->options->uploadsSymlinked
113
  ];
114
 
115
- if( update_option( "wpstg_existing_clones_beta", $this->options->existingClones ) === false ) {
116
- $this->log( "Finish: Failed to save {$this->options->clone}'s clone job data to database'" );
117
  return false;
118
  }
119
 
@@ -121,15 +124,23 @@ class Finish extends Job {
121
  }
122
 
123
  /**
124
- * Get destination Hostname depending on wheather WP has been installed in sub dir or not
125
- * @return type
126
  */
127
- private function getDestinationUrl() {
 
128
 
129
- if( !empty( $this->options->cloneHostname ) ) {
130
  return $this->options->cloneHostname;
131
  }
132
 
133
- return trailingslashit( get_site_url() ) . $this->options->cloneDirectoryName;
 
 
 
 
 
 
 
134
  }
135
  }
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  use WPStaging\Core\WPStaging;
6
+ use WPStaging\Core\Utils\Helper;
7
 
8
  /**
9
  * Class Finish
10
  * @package WPStaging\Backend\Modules\Jobs
11
  */
12
+ class Finish extends Job
13
+ {
14
 
15
  /**
16
  * Clone Key
17
+ * @var string
18
  */
19
  private $clone = '';
20
 
21
  /**
22
  * Start Module
23
  * @return object
24
+ * @throws \Exception
25
  */
26
+ public function start()
27
+ {
28
  // sanitize the clone name before saving
29
+ $this->clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
30
 
 
31
  $this->deleteCacheFiles();
32
 
33
  // Prepare clone records & save scanned directories for delete job later
37
 
38
  $return = [
39
  "directoryName" => $this->options->cloneDirectoryName,
40
+ "path" => trailingslashit($this->options->destinationDir),
41
  "url" => $this->getDestinationUrl(),
42
  "number" => $this->options->cloneNumber,
43
  "version" => WPStaging::getVersion(),
48
  "percentage" => 100
49
  ];
50
 
 
51
  do_action('wpstg_cloning_complete', $this->options);
52
 
53
+ return (object) $return;
54
  }
55
 
56
  /**
57
  * Delete Cache Files
58
+ * @throws \Exception
59
  */
60
+ protected function deleteCacheFiles()
61
+ {
62
+ $this->log("Finish: Deleting clone job's cache files...");
63
 
64
+ $this->cache->delete("clone_options");
65
+ $this->cache->delete("files_to_copy");
 
66
 
67
+ $this->log("Finish: Clone job's cache files have been deleted!");
68
  }
69
 
70
  /**
74
  *
75
  * @return bool
76
  */
77
+ protected function prepareCloneDataRecords()
78
+ {
79
  // Check if clones still exist
80
+ $this->log("Finish: Verifying existing clones...");
81
 
82
  // Clone data already exists
83
+ if (isset($this->options->existingClones[$this->options->clone])) {
84
+ if ($this->isMultisiteAndPro()) {
85
+ $this->options->existingClones[$this->options->clone]['url'] = $this->getDestinationUrl();
86
+ }
87
+
88
  $this->options->existingClones[$this->options->clone]['datetime'] = time();
89
  $this->options->existingClones[$this->options->clone]['status'] = 'finished';
90
  $this->options->existingClones[$this->options->clone]['prefix'] = $this->options->prefix;
91
  $this->options->existingClones[$this->options->clone]['emailsAllowed'] = (bool) $this->options->emailsAllowed;
92
  $this->options->existingClones[$this->options->clone]['uploadsSymlinked'] = (bool) $this->options->uploadsSymlinked;
93
+ update_option("wpstg_existing_clones_beta", $this->options->existingClones);
94
+ $this->log("Finish: The job finished!");
95
  return true;
96
  }
97
 
98
+ $this->log("Finish: {$this->options->clone}'s clone job's data is not in database, generating data");
 
 
 
 
99
 
100
  $this->options->existingClones[$this->clone] = [
101
  "directoryName" => $this->options->cloneDirectoryName,
102
+ "path" => trailingslashit($this->options->destinationDir),
103
  "url" => $this->getDestinationUrl(),
104
  "number" => $this->options->cloneNumber,
105
  "version" => WPStaging::getVersion(),
 
106
  "status" => "finished",
107
  "prefix" => $this->options->prefix,
108
  "datetime" => time(),
115
  "uploadsSymlinked" => (bool) $this->options->uploadsSymlinked
116
  ];
117
 
118
+ if (update_option("wpstg_existing_clones_beta", $this->options->existingClones) === false) {
119
+ $this->log("Finish: Failed to save {$this->options->clone}'s clone job data to database'");
120
  return false;
121
  }
122
 
124
  }
125
 
126
  /**
127
+ * Get destination Hostname depending on whether WP has been installed in sub dir or not
128
+ * @return string
129
  */
130
+ private function getDestinationUrl()
131
+ {
132
 
133
+ if (!empty($this->options->cloneHostname)) {
134
  return $this->options->cloneHostname;
135
  }
136
 
137
+ // if this is single site
138
+ if (!$this->isMultisiteAndPro()) {
139
+ return trailingslashit(get_site_url()) . $this->options->cloneDirectoryName;
140
+ }
141
+
142
+ // The relative path to the main multisite without appending a trailingslash e.g. wordpress
143
+ $multisitePath = defined('PATH_CURRENT_SITE') ? PATH_CURRENT_SITE : '/';
144
+ return rtrim((new Helper())->getBaseUrl(), '/\\') . $multisitePath . $this->options->cloneDirectoryName;
145
  }
146
  }
Backend/Modules/Jobs/Interfaces/JobInterface.php DELETED
@@ -1,22 +0,0 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs\Interfaces;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
- /**
11
- * Interface JobInterface
12
- * @package WPStaging\Backend\Modules\Jobs\Interfaces
13
- */
14
- interface JobInterface
15
- {
16
-
17
- /**
18
- * Start Module
19
- * @return object
20
- */
21
- public function start();
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/Modules/Jobs/Job.php CHANGED
@@ -7,11 +7,12 @@ if (!defined("WPINC")) {
7
  die;
8
  }
9
 
10
- use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
11
  use WPStaging\Core\Utils\Logger;
12
  use WPStaging\Core\WPStaging;
13
  use WPStaging\Core\Utils\Cache;
14
  use WPStaging\Core\thirdParty\thirdPartyCompatibility;
 
 
15
  use DateTime;
16
  use DateInterval;
17
  use Exception;
@@ -20,7 +21,7 @@ use Exception;
20
  * Class Job
21
  * @package WPStaging\Backend\Modules\Jobs
22
  */
23
- abstract class Job implements JobInterface
24
  {
25
 
26
  const EXECUTION_TIME_RATIO = 0.8;
@@ -83,18 +84,6 @@ abstract class Job implements JobInterface
83
  */
84
  protected $maxRecursionLimit;
85
 
86
- /**
87
- * Multisite Home Url without Scheme
88
- * @var string
89
- */
90
- //protected $multisiteHomeUrlWithoutScheme;
91
-
92
- /**
93
- * Multisite home domain without scheme
94
- * @var string
95
- */
96
- //protected $multisiteDomainWithoutScheme;
97
-
98
  /**
99
  * Multisite home domain without scheme
100
  * @var string
@@ -114,28 +103,26 @@ abstract class Job implements JobInterface
114
 
115
  /**
116
  * Job constructor.
 
117
  */
118
  public function __construct()
119
  {
120
- // Get max limits
121
  $this->start = $this->time();
 
122
  $this->maxMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
 
123
  $this->thirdParty = new thirdPartyCompatibility();
124
 
125
- //$multisite = new Multisite;
126
- //$this->multisiteHomeUrlWithoutScheme = $multisite->getHomeUrlWithoutScheme();
127
- //$this->baseUrl = (new Helper)->getBaseUrl();
128
- //$this->multisiteDomainWithoutScheme = $multisite->getHomeDomainWithoutScheme();
129
- $this->maxExecutionTime = ( int )10;
130
 
131
  // Services
132
- $this->cache = new Cache(-1, \WPStaging\Core\WPStaging::getContentDir());
133
  $this->logger = WPStaging::getInstance()->get("logger");
134
 
135
  // Settings and Options
136
  $this->options = $this->cache->get("clone_options");
137
 
138
- $this->settings = ( object )get_option("wpstg_settings", []);
139
 
140
  if (!$this->options) {
141
  $this->options = new \stdClass();
@@ -146,7 +133,8 @@ abstract class Job implements JobInterface
146
  }
147
 
148
  // check default options
149
- if (!isset($this->settings) ||
 
150
  !isset($this->settings->queryLimit) ||
151
  !isset($this->settings->querySRLimit) ||
152
  !isset($this->settings->batchSize) ||
@@ -161,7 +149,7 @@ abstract class Job implements JobInterface
161
  // Set limits accordingly to CPU LIMITS
162
  $this->setLimits();
163
 
164
- $this->maxRecursionLimit = ( int )ini_get("xdebug.max_nesting_level");
165
 
166
  /*
167
  * This is needed to make sure that maxRecursionLimit = -1
@@ -202,9 +190,16 @@ abstract class Job implements JobInterface
202
  {
203
  $this->settings->queryLimit = "10000";
204
  $this->settings->querySRLimit = "5000";
205
- $this->settings->fileLimit = "50";
 
 
 
 
 
 
 
 
206
  $this->settings->batchSize = "2";
207
- $this->settings->cpuLoad = 'low';
208
  $this->settings->maxFileSize = 8;
209
  $this->settings->optimizer = "1";
210
  update_option('wpstg_settings', $this->settings);
@@ -251,7 +246,7 @@ abstract class Job implements JobInterface
251
  $options = $this->options;
252
  }
253
 
254
- $now = new DateTime;
255
  $options->expiresAt = $now->add(new DateInterval('P1D'))->format('Y-m-d H:i:s');
256
 
257
  // Ensure that it is an object
@@ -274,12 +269,12 @@ abstract class Job implements JobInterface
274
  protected function getMemoryInBytes($memory)
275
  {
276
  // Handle unlimited ones
277
- if (( int )$memory < 1) {
278
  // 128 MB default value
279
- return ( int )134217728;
280
  }
281
 
282
- $bytes = ( int )$memory; // grab only the number
283
  $size = trim(str_replace($bytes, null, strtolower($memory))); // strip away number and lower-case it
284
  // Actual calculation
285
  switch ($size) {
@@ -304,17 +299,17 @@ abstract class Job implements JobInterface
304
  */
305
  protected function formatBytes($bytes)
306
  {
307
- if (( int )$bytes < 1) {
308
  return '';
309
  }
310
 
311
  $units = ['B', 'K', 'M', 'G']; // G since PHP 5.1.x so we are good!
312
 
313
- $bytes = ( int )$bytes;
314
  $base = log($bytes) / log(1000);
315
  $pow = pow(1000, $base - floor($base));
316
 
317
- return round($pow, 0) . $units[( int )floor($base)];
318
  }
319
 
320
  /**
@@ -335,7 +330,7 @@ abstract class Job implements JobInterface
335
  public function isOverThreshold()
336
  {
337
  // Check if the memory is over threshold
338
- $usedMemory = ( int )@memory_get_usage(true);
339
 
340
  $this->debugLog('Used Memory: ' . $this->formatBytes($usedMemory) . ' Max Memory Limit: ' . $this->formatBytes($this->maxMemoryLimit) . ' Max Script Memory Limit: ' . $this->formatBytes($this->memoryLimit), Logger::TYPE_DEBUG);
341
 
@@ -395,7 +390,7 @@ abstract class Job implements JobInterface
395
  $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
396
  }
397
 
398
- if ($this->hasLoggedFileNameSet === false && strlen($this->options->clone) > 0) {
399
  $this->logger->setFileName($this->options->clone);
400
  $this->hasLoggedFileNameSet = true;
401
  }
@@ -434,7 +429,7 @@ abstract class Job implements JobInterface
434
  }
435
 
436
  try {
437
- $now = new DateTime;
438
  $expiresAt = new DateTime($this->options->expiresAt);
439
  return $this->options->isRunning === true && $now < $expiresAt;
440
  } catch (Exception $e) {
@@ -442,4 +437,21 @@ abstract class Job implements JobInterface
442
 
443
  return false;
444
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  }
7
  die;
8
  }
9
 
 
10
  use WPStaging\Core\Utils\Logger;
11
  use WPStaging\Core\WPStaging;
12
  use WPStaging\Core\Utils\Cache;
13
  use WPStaging\Core\thirdParty\thirdPartyCompatibility;
14
+ use WPStaging\Framework\Utils\Strings;
15
+ use WPStaging\Framework\Utils\WpDefaultDirectories;
16
  use DateTime;
17
  use DateInterval;
18
  use Exception;
21
  * Class Job
22
  * @package WPStaging\Backend\Modules\Jobs
23
  */
24
+ abstract class Job
25
  {
26
 
27
  const EXECUTION_TIME_RATIO = 0.8;
84
  */
85
  protected $maxRecursionLimit;
86
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  /**
88
  * Multisite home domain without scheme
89
  * @var string
103
 
104
  /**
105
  * Job constructor.
106
+ * @throws Exception
107
  */
108
  public function __construct()
109
  {
 
110
  $this->start = $this->time();
111
+
112
  $this->maxMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
113
+
114
  $this->thirdParty = new thirdPartyCompatibility();
115
 
116
+ $this->maxExecutionTime = 10;
 
 
 
 
117
 
118
  // Services
119
+ $this->cache = new Cache(-1, WPStaging::getContentDir());
120
  $this->logger = WPStaging::getInstance()->get("logger");
121
 
122
  // Settings and Options
123
  $this->options = $this->cache->get("clone_options");
124
 
125
+ $this->settings = (object)get_option("wpstg_settings", []);
126
 
127
  if (!$this->options) {
128
  $this->options = new \stdClass();
133
  }
134
 
135
  // check default options
136
+ if (
137
+ !isset($this->settings) ||
138
  !isset($this->settings->queryLimit) ||
139
  !isset($this->settings->querySRLimit) ||
140
  !isset($this->settings->batchSize) ||
149
  // Set limits accordingly to CPU LIMITS
150
  $this->setLimits();
151
 
152
+ $this->maxRecursionLimit = (int)ini_get("xdebug.max_nesting_level");
153
 
154
  /*
155
  * This is needed to make sure that maxRecursionLimit = -1
190
  {
191
  $this->settings->queryLimit = "10000";
192
  $this->settings->querySRLimit = "5000";
193
+
194
+ if (defined('WPSTG_DEV') && WPSTG_DEV) {
195
+ $this->settings->fileLimit = "500";
196
+ $this->settings->cpuLoad = 'high';
197
+ } else {
198
+ $this->settings->fileLimit = "50";
199
+ $this->settings->cpuLoad = 'low';
200
+ }
201
+
202
  $this->settings->batchSize = "2";
 
203
  $this->settings->maxFileSize = 8;
204
  $this->settings->optimizer = "1";
205
  update_option('wpstg_settings', $this->settings);
246
  $options = $this->options;
247
  }
248
 
249
+ $now = new DateTime();
250
  $options->expiresAt = $now->add(new DateInterval('P1D'))->format('Y-m-d H:i:s');
251
 
252
  // Ensure that it is an object
269
  protected function getMemoryInBytes($memory)
270
  {
271
  // Handle unlimited ones
272
+ if ((int)$memory < 1) {
273
  // 128 MB default value
274
+ return (int)134217728;
275
  }
276
 
277
+ $bytes = (int)$memory; // grab only the number
278
  $size = trim(str_replace($bytes, null, strtolower($memory))); // strip away number and lower-case it
279
  // Actual calculation
280
  switch ($size) {
299
  */
300
  protected function formatBytes($bytes)
301
  {
302
+ if ((int)$bytes < 1) {
303
  return '';
304
  }
305
 
306
  $units = ['B', 'K', 'M', 'G']; // G since PHP 5.1.x so we are good!
307
 
308
+ $bytes = (int)$bytes;
309
  $base = log($bytes) / log(1000);
310
  $pow = pow(1000, $base - floor($base));
311
 
312
+ return round($pow, 0) . $units[(int)floor($base)];
313
  }
314
 
315
  /**
330
  public function isOverThreshold()
331
  {
332
  // Check if the memory is over threshold
333
+ $usedMemory = (int)@memory_get_usage(true);
334
 
335
  $this->debugLog('Used Memory: ' . $this->formatBytes($usedMemory) . ' Max Memory Limit: ' . $this->formatBytes($this->maxMemoryLimit) . ' Max Script Memory Limit: ' . $this->formatBytes($this->memoryLimit), Logger::TYPE_DEBUG);
336
 
390
  $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
391
  }
392
 
393
+ if ($this->hasLoggedFileNameSet === false && $this->options->clone != '') {
394
  $this->logger->setFileName($this->options->clone);
395
  $this->hasLoggedFileNameSet = true;
396
  }
429
  }
430
 
431
  try {
432
+ $now = new DateTime();
433
  $expiresAt = new DateTime($this->options->expiresAt);
434
  return $this->options->isRunning === true && $now < $expiresAt;
435
  } catch (Exception $e) {
437
 
438
  return false;
439
  }
440
+
441
+ /**
442
+ * @return bool
443
+ */
444
+ protected function isMultisiteAndPro()
445
+ {
446
+ return defined('WPSTGPRO_VERSION') && is_multisite();
447
+ }
448
+
449
+ /**
450
+ * Check if external database is used
451
+ * @return boolean
452
+ */
453
+ protected function isExternalDatabase()
454
+ {
455
+ return !(empty($this->options->databaseUser) && empty($this->options->databasePassword));
456
+ }
457
  }
Backend/Modules/Jobs/JobExecutable.php CHANGED
@@ -1,9 +1,11 @@
1
  <?php
 
2
  namespace WPStaging\Backend\Modules\Jobs;
3
 
 
 
4
  // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
  die;
8
  }
9
 
@@ -15,6 +17,10 @@ if (!defined("WPINC"))
15
  */
16
  abstract class JobExecutable extends Job
17
  {
 
 
 
 
18
 
19
  /**
20
  * @var array
@@ -39,6 +45,9 @@ abstract class JobExecutable extends Job
39
 
40
  // Set server settings (Do not set this globally. HS Ticket #9061)
41
  wpstg_setup_environment();
 
 
 
42
  }
43
 
44
  /**
@@ -49,13 +58,12 @@ abstract class JobExecutable extends Job
49
  */
50
  protected function prepareResponse($status = false, $incrementCurrentStep = true)
51
  {
52
- if ($incrementCurrentStep)
53
- {
54
  $this->options->currentStep++;
55
  }
56
 
57
  $percentage = 0;
58
- if (isset($this->options->currentStep) && isset($this->options->totalSteps) && $this->options->totalSteps > 0){
59
  $percentage = round(($this->options->currentStep / $this->options->totalSteps) * 100);
60
  $percentage = ($percentage > 100) ? 100 : $percentage;
61
  }
@@ -93,18 +101,16 @@ abstract class JobExecutable extends Job
93
  protected function run()
94
  {
95
  // Execute steps
96
- for ($i = 0; $i < $this->options->totalSteps; $i++)
97
- {
98
  // Job is finished or over threshold limits was hit
99
- if (!$this->execute())
100
- {
101
  break;
102
  }
103
  // Return after every step to create lower batches
104
  // This also gets a smoother progress bar and to a less consumptive php cpu load
105
  // This decrease performance tremendous but also lowers memory consumption
106
- if ($this->settings->cpuLoad === 'low'){
107
- return (object) $this->response;
108
  }
109
  }
110
  }
@@ -121,4 +127,4 @@ abstract class JobExecutable extends Job
121
  * @return bool
122
  */
123
  abstract protected function execute();
124
- }
1
  <?php
2
+
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
+ use WPStaging\Framework\Utils\Strings;
6
+
7
  // No Direct Access
8
+ if (!defined("WPINC")) {
 
9
  die;
10
  }
11
 
17
  */
18
  abstract class JobExecutable extends Job
19
  {
20
+ /**
21
+ * @var Strings
22
+ */
23
+ protected $strUtil;
24
 
25
  /**
26
  * @var array
45
 
46
  // Set server settings (Do not set this globally. HS Ticket #9061)
47
  wpstg_setup_environment();
48
+
49
+ // TODO: inject using DI
50
+ $this->strUtil = new Strings();
51
  }
52
 
53
  /**
58
  */
59
  protected function prepareResponse($status = false, $incrementCurrentStep = true)
60
  {
61
+ if ($incrementCurrentStep) {
 
62
  $this->options->currentStep++;
63
  }
64
 
65
  $percentage = 0;
66
+ if (isset($this->options->currentStep) && isset($this->options->totalSteps) && $this->options->totalSteps > 0) {
67
  $percentage = round(($this->options->currentStep / $this->options->totalSteps) * 100);
68
  $percentage = ($percentage > 100) ? 100 : $percentage;
69
  }
101
  protected function run()
102
  {
103
  // Execute steps
104
+ for ($i = 0; $i < $this->options->totalSteps; $i++) {
 
105
  // Job is finished or over threshold limits was hit
106
+ if (!$this->execute()) {
 
107
  break;
108
  }
109
  // Return after every step to create lower batches
110
  // This also gets a smoother progress bar and to a less consumptive php cpu load
111
  // This decrease performance tremendous but also lowers memory consumption
112
+ if ($this->settings->cpuLoad === 'low') {
113
+ return (object) $this->response;
114
  }
115
  }
116
  }
127
  * @return bool
128
  */
129
  abstract protected function execute();
130
+ }
Backend/Modules/Jobs/Logs.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Backend\Modules\Jobs;
3
 
4
  /**
@@ -14,8 +15,7 @@ class Logs extends Job
14
  */
15
  public function initialize()
16
  {
17
- if (isset($_POST["clone"]))
18
- {
19
  $this->clone = $_POST["clone"];
20
  }
21
  }
@@ -45,4 +45,4 @@ class Logs extends Job
45
  $logs = explode(PHP_EOL, $this->logger->read($this->getCloneFileName()));
46
  return trim(implode("<br>", array_reverse($logs)), "<br>");
47
  }
48
- }
1
  <?php
2
+
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  /**
15
  */
16
  public function initialize()
17
  {
18
+ if (isset($_POST["clone"])) {
 
19
  $this->clone = $_POST["clone"];
20
  }
21
  }
45
  $logs = explode(PHP_EOL, $this->logger->read($this->getCloneFileName()));
46
  return trim(implode("<br>", array_reverse($logs)), "<br>");
47
  }
48
+ }
Backend/Modules/Jobs/Multisite/Directories.php DELETED
@@ -1,606 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs\Multisite;
4
-
5
- // No Direct Access
6
- if (!defined("WPINC")) {
7
- die;
8
- }
9
-
10
- use Exception;
11
- use WPStaging\Core\WPStaging;
12
- use WPStaging\Framework\Utils\Strings;
13
- use WPStaging\Core\Iterators\RecursiveDirectoryIterator;
14
- use WPStaging\Core\Iterators\RecursiveFilterExclude;
15
- use WPStaging\Backend\Modules\Jobs\JobExecutable;
16
-
17
- /**
18
- * Class Files
19
- * @package WPStaging\Backend\Modules\Directories
20
- */
21
- class Directories extends JobExecutable
22
- {
23
-
24
- /**
25
- * @var array
26
- */
27
- private $files = [];
28
-
29
- /**
30
- * Total steps to do
31
- * @var int
32
- */
33
- private $total = 5;
34
-
35
- /**
36
- * path to the cache file
37
- * @var string
38
- */
39
- private $filename;
40
-
41
- /**
42
- * Initialize
43
- */
44
- public function initialize()
45
- {
46
- $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
47
- }
48
-
49
- /**
50
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
51
- * @return void
52
- */
53
- protected function calculateTotalSteps()
54
- {
55
-
56
- $this->options->totalSteps = $this->total + count($this->options->extraDirectories);
57
- }
58
-
59
- /**
60
- * Start Module
61
- * @return object
62
- */
63
- public function start()
64
- {
65
-
66
- // Execute steps
67
- $this->run();
68
-
69
- // Save option, progress
70
- $this->saveProgress();
71
-
72
- return ( object )$this->response;
73
- }
74
-
75
- /**
76
- * Step 0
77
- * Get WP Root files
78
- * Does not collect any sub folders
79
- */
80
- private function getWpRootFiles()
81
- {
82
-
83
- // open file handle
84
- $files = $this->open($this->filename, 'a');
85
-
86
- try {
87
-
88
- // Iterate over wp root directory
89
- $iterator = new \DirectoryIterator(WPStaging::getWPpath());
90
-
91
- $this->log("Scanning / for files");
92
-
93
- // Write path line
94
- foreach ($iterator as $item) {
95
- if (!$item->isDot() && $item->isFile()) {
96
- if ($this->write($files, $iterator->getFilename() . PHP_EOL)) {
97
- $this->options->totalFiles++;
98
- }
99
- }
100
- }
101
- } catch (Exception $e) {
102
- $this->returnException('Error: ' . $e->getMessage());
103
- }
104
-
105
- $this->close($files);
106
- return true;
107
- }
108
-
109
- /**
110
- * Step 2
111
- * Get WP Content Files without multisite folder wp-content/uploads/sites or wp-content/blogs.dir/
112
- */
113
- private function getWpContentFiles()
114
- {
115
-
116
- // Skip it
117
- if ($this->isDirectoryExcluded(WP_CONTENT_DIR)) {
118
- $this->log("Skip " . WPStaging::getWPpath() . WP_CONTENT_DIR);
119
- return true;
120
- }
121
- // open file handle
122
- $files = $this->open($this->filename, 'a');
123
-
124
- /**
125
- * Excluded folders relative to the folder to iterate
126
- */
127
- $excludePaths = [
128
- 'cache',
129
- 'plugins/wps-hide-login',
130
- 'uploads/sites',
131
- 'blogs.dir'
132
- ];
133
-
134
- /**
135
- * Get user excluded folders
136
- */
137
- $directory = [];
138
- foreach ($this->options->excludedDirectories as $dir) {
139
- $dir = wpstg_replace_windows_directory_separator($dir);
140
- $wpContentDir = wpstg_replace_windows_directory_separator(WP_CONTENT_DIR);
141
-
142
- if (strpos($dir, $wpContentDir) !== false) {
143
- $directory[] = ltrim(str_replace($wpContentDir, '', $dir), '/\\');
144
- }
145
- }
146
-
147
- $excludePaths = array_merge($excludePaths, $directory);
148
-
149
- try {
150
-
151
- // Iterate over content directory
152
- $iterator = new RecursiveDirectoryIterator(WP_CONTENT_DIR);
153
-
154
- // Exclude sites, uploads, plugins or themes
155
- $iterator = new RecursiveFilterExclude($iterator, apply_filters('wpstg_clone_mu_excl_folders', $excludePaths));
156
-
157
- // Recursively iterate over content directory
158
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
159
-
160
- $this->log("Scanning /wp-content folder " . WP_CONTENT_DIR);
161
-
162
- // Write path line
163
- foreach ($iterator as $item) {
164
- if ($item->isFile()) {
165
- $file = 'wp-content/' . $iterator->getSubPathName() . PHP_EOL;
166
- if ($this->write($files, $file)) {
167
- $this->options->totalFiles++;
168
- }
169
- }
170
- }
171
- } catch (Exception $e) {
172
- $this->returnException('Error: ' . $e->getMessage());
173
- }
174
-
175
- // close the file handler
176
- $this->close($files);
177
- return true;
178
- }
179
-
180
- /**
181
- * Step 2
182
- * @return boolean
183
- * @throws Exception
184
- */
185
- private function getWpIncludesFiles()
186
- {
187
-
188
- // Skip it
189
- if ($this->isDirectoryExcluded(WPStaging::getWPpath() . 'wp-includes/')) {
190
- $this->log("Skip " . WPStaging::getWPpath() . 'wp-includes/');
191
- return true;
192
- }
193
-
194
- // open file handle and attach data to end of file
195
- $files = $this->open($this->filename, 'a');
196
-
197
- try {
198
-
199
- // Iterate over wp-admin directory
200
- $iterator = new RecursiveDirectoryIterator(WPStaging::getWPpath() . 'wp-includes/');
201
-
202
- // Recursively iterate over wp-includes directory
203
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
204
-
205
- $this->log("Scanning /wp-includes for its sub-directories and files");
206
-
207
- // Write files
208
- foreach ($iterator as $item) {
209
- if ($item->isFile()) {
210
- if ($this->write($files, 'wp-includes/' . $iterator->getSubPathName() . PHP_EOL)) {
211
- $this->options->totalFiles++;
212
- }
213
- }
214
- }
215
- } catch (Exception $e) {
216
- $this->returnException('Error: ' . $e->getMessage());
217
- }
218
-
219
- // close the file handler
220
- $this->close($files);
221
- return true;
222
- }
223
-
224
- /**
225
- * Step 3
226
- * @return boolean
227
- * @throws Exception
228
- */
229
- private function getWpAdminFiles()
230
- {
231
-
232
- // Skip it
233
- if ($this->isDirectoryExcluded(WPStaging::getWPpath() . 'wp-admin/')) {
234
- $this->log("Skip " . WPStaging::getWPpath() . 'wp-admin/');
235
- return true;
236
- }
237
-
238
- // open file handle and attach data to end of file
239
- $files = $this->open($this->filename, 'a');
240
-
241
- try {
242
-
243
- // Iterate over wp-admin directory
244
- $iterator = new RecursiveDirectoryIterator(WPStaging::getWPpath() . 'wp-admin/');
245
-
246
- // Recursively iterate over content directory
247
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
248
-
249
- $this->log("Scanning /wp-admin for its sub-directories and files");
250
-
251
- // Write path line
252
- foreach ($iterator as $item) {
253
- if ($item->isFile()) {
254
- if ($this->write($files, 'wp-admin/' . $iterator->getSubPathName() . PHP_EOL)) {
255
- $this->options->totalFiles++;
256
- // Too much cpu time
257
- //$this->options->totalFileSize += $iterator->getSize();
258
- }
259
- }
260
- }
261
- } catch (Exception $e) {
262
- $this->returnException('Error: ' . $e->getMessage());
263
- }
264
-
265
- // close the file handler
266
- $this->close($files);
267
- return true;
268
- }
269
-
270
- /**
271
- * Step 4
272
- * Get WP Content Uploads Files multisite folder wp-content/uploads/sites or wp-content/blogs.dir/ID/files
273
- */
274
- private function getWpContentUploadsSites()
275
- {
276
-
277
- // Skip if main site is cloned
278
- if (is_main_site()) {
279
- return true;
280
- }
281
-
282
- // Absolute path to uploads folder
283
- $path = $this->getAbsUploadPath();
284
-
285
- // Skip it
286
- if (!is_dir($path)) {
287
- $this->log("Skipping: {$path} does not exist.");
288
- return true;
289
- }
290
-
291
- // Skip it
292
- if ($this->isDirectoryExcluded($path)) {
293
- $this->log("Skipping: {$path}");
294
- return true;
295
- }
296
-
297
-
298
- // open file handle
299
- $files = $this->open($this->filename, 'a');
300
-
301
- /**
302
- * Excluded folders relative to the folder to iterate
303
- */
304
- $excludePaths = [
305
- 'cache',
306
- 'plugins/wps-hide-login',
307
- 'uploads/sites'
308
- ];
309
-
310
- /**
311
- * Get user excluded folders
312
- */
313
- $directory = [];
314
- foreach ($this->options->excludedDirectories as $dir) {
315
- $path = wpstg_replace_windows_directory_separator($path);
316
- $dir = wpstg_replace_windows_directory_separator($dir);
317
- if (strpos($dir, $path) !== false) {
318
- $directory[] = ltrim(str_replace($path, '', $dir), '/');
319
- }
320
- }
321
-
322
- $excludePaths = array_merge($excludePaths, $directory);
323
-
324
- try {
325
-
326
- // Iterate over content directory
327
- $iterator = new RecursiveDirectoryIterator($path);
328
-
329
- // Exclude sites, uploads, plugins or themes
330
- $iterator = new RecursiveFilterExclude($iterator, $excludePaths);
331
-
332
- // Recursively iterate over content directory
333
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
334
- $uploadDir = wpstg_get_upload_dir();
335
- $this->log("Scanning {$uploadDir} for its sub-directories and files...");
336
-
337
- // Write path line
338
- foreach ($iterator as $item) {
339
- if ($item->isFile()) {
340
- $file = $this->getRelUploadPath() . $iterator->getSubPathName() . PHP_EOL;
341
- if ($this->write($files, $file)) {
342
- $this->options->totalFiles++;
343
- }
344
- }
345
- }
346
- } catch (Exception $e) {
347
- $this->returnException('Error: ' . $e->getMessage());
348
- }
349
-
350
- // close the file handler
351
- $this->close($files);
352
- return true;
353
- }
354
-
355
- /**
356
- * Get absolute path to the upload folder e.g. /srv/www/wp-content/blogs.dir/ID/files or /srv/www/wp-content/uploads/sites/ID/
357
- * @return string
358
- */
359
- private function getAbsUploadPath()
360
- {
361
- // Check first which method is used
362
- $uploads = wp_upload_dir();
363
- $basedir = $uploads['basedir'];
364
-
365
- return trailingslashit($basedir);
366
- }
367
-
368
- /**
369
- * Get relative path to the upload folder like wp-content/uploads or wp-content/blogs.dir/2/files
370
- * @return string
371
- */
372
- private function getRelUploadPath()
373
- {
374
- $uploads = wp_upload_dir();
375
- $basedir = $uploads['basedir'];
376
-
377
- return trailingslashit(str_replace(wpstg_replace_windows_directory_separator(WPStaging::getWPpath()), null, wpstg_replace_windows_directory_separator($basedir)));
378
- }
379
-
380
- /**
381
- * Step 5 - x
382
- * Get extra folders of the wp root level
383
- * Does not collect wp-includes, wp-admin and wp-content folder
384
- */
385
- private function getExtraFiles($folder)
386
- {
387
-
388
- if (!is_dir($folder)) {
389
- return true;
390
- }
391
-
392
- // open file handle and attach data to end of file
393
- $files = $this->open($this->filename, 'a');
394
-
395
- try {
396
-
397
- // Iterate over extra directory
398
- $iterator = new RecursiveDirectoryIterator($folder);
399
-
400
- $exclude = [];
401
-
402
- $iterator = new RecursiveFilterExclude($iterator, $exclude);
403
- // Recursively iterate over content directory
404
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
405
-
406
- $strings = new Strings();
407
- $this->log("Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files");
408
-
409
- // Write path line
410
- foreach ($iterator as $item) {
411
- if ($item->isFile()) {
412
- $path = str_replace(wpstg_replace_windows_directory_separator(WPStaging::getWPpath()), '', wpstg_replace_windows_directory_separator($folder)) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL;
413
- if ($this->write($files, $path)) {
414
- $this->options->totalFiles++;
415
- }
416
- }
417
- }
418
- } catch (Exception $e) {
419
- $this->returnException('Error: ' . $e->getMessage());
420
- }
421
-
422
- // close the file handler
423
- $this->close($files);
424
- return true;
425
- }
426
-
427
- /**
428
- * Closes a file handle
429
- *
430
- * @param resource $handle File handle to close
431
- * @return boolean
432
- */
433
- public function close($handle)
434
- {
435
- return @fclose($handle);
436
- }
437
-
438
- /**
439
- * Opens a file in specified mode
440
- *
441
- * @param string $file Path to the file to open
442
- * @param string $mode Mode in which to open the file
443
- * @return resource
444
- * @throws Exception
445
- */
446
- public function open($file, $mode)
447
- {
448
-
449
- $file_handle = @fopen($file, $mode);
450
- if ($file_handle === false) {
451
- $this->returnException(sprintf(__('Unable to open %s with mode %s', 'wp-staging'), $file, $mode));
452
- }
453
-
454
- return $file_handle;
455
- }
456
-
457
- /**
458
- * Write contents to a file
459
- *
460
- * @param resource $handle File handle to write to
461
- * @param string $content Contents to write to the file
462
- * @return integer
463
- * @throws Exception
464
- */
465
- public function write($handle, $content)
466
- {
467
- $write_result = @fwrite($handle, $content);
468
- if ($write_result === false) {
469
- if (($meta = \stream_get_meta_data($handle))) {
470
- throw new Exception(sprintf(__('Unable to write to: %s', 'wp-staging'), $meta['uri']));
471
- }
472
- } elseif ($write_result !== strlen($content)) {
473
- throw new Exception(__('Out of disk space.', 'wp-staging'));
474
- }
475
-
476
- return $write_result;
477
- }
478
-
479
- /**
480
- * Execute the Current Step
481
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
482
- * @return bool
483
- */
484
- protected function execute()
485
- {
486
-
487
- // No job left to execute
488
- if ($this->isFinished()) {
489
- $this->prepareResponse(true, false);
490
- return false;
491
- }
492
-
493
-
494
- if ($this->options->currentStep == 0) {
495
- $this->getWpRootFiles();
496
- $this->prepareResponse(false, true);
497
- return false;
498
- }
499
-
500
- if ($this->options->currentStep == 1) {
501
- $this->getWpContentFiles();
502
- $this->prepareResponse(false, true);
503
- return false;
504
- }
505
-
506
- if ($this->options->currentStep == 2) {
507
- $this->getWpIncludesFiles();
508
- $this->prepareResponse(false, true);
509
- return false;
510
- }
511
-
512
- if ($this->options->currentStep == 3) {
513
- $this->getWpAdminFiles();
514
- $this->prepareResponse(false, true);
515
- return false;
516
- }
517
-
518
- if ($this->options->currentStep == 4) {
519
- $this->getWpContentUploadsSites();
520
- $this->prepareResponse(false, true);
521
- return false;
522
- }
523
-
524
- if (isset($this->options->extraDirectories[$this->options->currentStep - $this->total])) {
525
- $this->getExtraFiles($this->options->extraDirectories[$this->options->currentStep - $this->total]);
526
- $this->prepareResponse(false, true);
527
- return false;
528
- }
529
-
530
-
531
- // Prepare response
532
- $this->prepareResponse(false, true);
533
- // Not finished
534
- return true;
535
- }
536
-
537
- /**
538
- * Checks Whether There is Any Job to Execute or Not
539
- * @return bool
540
- */
541
- protected function isFinished()
542
- {
543
- if ($this->options->currentStep >= $this->options->totalSteps) {
544
- return true;
545
- }
546
-
547
- return false;
548
- }
549
-
550
- /**
551
- * Save files
552
- * @return bool
553
- */
554
- protected function saveProgress()
555
- {
556
- return $this->saveOptions();
557
- }
558
-
559
- /**
560
- * Get files
561
- * @return void
562
- */
563
- protected function getFiles()
564
- {
565
- $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
566
-
567
- if (($this->files = @file_get_contents($fileName)) === false) {
568
- $this->files = [];
569
- return;
570
- }
571
-
572
- $this->files = explode(PHP_EOL, $this->files);
573
- }
574
-
575
- /**
576
- * Replace forward slash with current directory separator
577
- *
578
- * @param string $path Path
579
- *
580
- * @return string
581
- */
582
- private function sanitizeDirectorySeparator($path)
583
- {
584
- $string = str_replace("/", "\\", $path);
585
- return str_replace('\\\\', '\\', $string);
586
- }
587
-
588
- /**
589
- * Check if directory is excluded
590
- * @param string $directory
591
- * @return bool
592
- */
593
- protected function isDirectoryExcluded($directory)
594
- {
595
- $directory = $this->sanitizeDirectorySeparator($directory);
596
- foreach ($this->options->excludedDirectories as $excludedDirectory) {
597
- $excludedDirectory = $this->sanitizeDirectorySeparator($excludedDirectory);
598
- if (strpos(trailingslashit($directory), trailingslashit($excludedDirectory)) === 0) {
599
- return true;
600
- }
601
- }
602
-
603
- return false;
604
- }
605
-
606
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/Modules/Jobs/Multisite/Files.php DELETED
@@ -1,527 +0,0 @@
1
- <?php
2
-
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
15
- * @see \WPStaging\Backend\Modules\Jobs\Files Todo
16
- *
17
- * @package WPStaging\Backend\Modules\Jobs
18
- */
19
- class Files extends JobExecutable
20
- {
21
- /**
22
- * @var \SplFileObject
23
- */
24
- private $file;
25
-
26
- /**
27
- * @var int
28
- */
29
- private $maxFilesPerRun;
30
-
31
- /**
32
- * @var string
33
- */
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();
52
-
53
- if (is_file($filePath)) {
54
- $this->file = new \SplFileObject($filePath, 'r');
55
- }
56
-
57
- // Informational logs
58
- if ($this->options->currentStep === 1) {
59
- $this->log("Copying files...");
60
- }
61
-
62
- $this->settings->batchSize = $this->settings->batchSize * 1000000;
63
- $this->maxFilesPerRun = $this->settings->fileLimit;
64
- }
65
-
66
- /**
67
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
68
- * @return void
69
- */
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
- /**
78
- * Execute the Current Step
79
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
80
- * @return bool
81
- */
82
- protected function execute()
83
- {
84
- // Finished
85
- if ($this->isFinished()) {
86
- $this->symlinkUploadFolder();
87
- $this->log("Copying files finished");
88
- $this->prepareResponse(true, false);
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);
101
- return false;
102
- }
103
-
104
- // Prepare and return response
105
- $this->prepareResponse();
106
-
107
- // Not finished
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
158
- $this->prepareResponse(false, false);
159
- $this->saveOptions();
160
- return false;
161
- }
162
-
163
- // Go to last copied line and than to next one
164
- //if ($this->options->copiedFiles != 0) {
165
- if (isset($this->options->copiedFiles) && $this->options->copiedFiles != 0) {
166
- $this->file->seek($this->options->copiedFiles - 1);
167
- }
168
-
169
- $this->file->setFlags(\SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
170
-
171
- for ($i = 0; $i < $this->maxFilesPerRun; $i++) {
172
- // Increment copied files
173
- // Do this anytime to make sure not to stuck in the same step / files
174
- $this->options->copiedFiles++;
175
-
176
- // End of file
177
- if ($this->file->eof()) {
178
- break;
179
- }
180
-
181
- $file = $this->file->fgets();
182
-
183
-
184
- $this->copyFile($file);
185
- }
186
-
187
-
188
- $totalFiles = $this->options->copiedFiles;
189
- // Log this only every 50 entries to keep the log small and to not block the rendering browser
190
- if ($this->options->copiedFiles % 50 == 0) {
191
- $this->log("Total {$totalFiles} files processed");
192
- }
193
-
194
- return true;
195
- }
196
-
197
- /**
198
- * Symlink the upload folder to production site if set
199
- * @return bool
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
-
208
- if (!$this->options->uploadsSymlinked) {
209
- return true;
210
- }
211
-
212
- $symlinker = new WpUploadsFolderSymlinker($this->options->destinationDir);
213
- if ($symlinker->trySymlink()) {
214
- $this->log("Uploads Folder successfully symlinked with the production site");
215
- return true;
216
- }
217
-
218
- $this->returnException('Unable to symlink production sites uploads folder to ' . $this->options->destinationDir . '. Make sure no other link or folder exist with the same same path.');
219
- return false;
220
- }
221
-
222
- /**
223
- * Checks Whether There is Any Job to Execute or Not
224
- * @return bool
225
- */
226
- private function isFinished()
227
- {
228
- return !$this->isRunning() ||
229
- $this->options->currentStep > $this->options->totalSteps ||
230
- $this->options->copiedFiles >= $this->options->totalFiles;
231
- }
232
-
233
- /**
234
- * @param string $file
235
- * @return bool
236
- */
237
- private function copyFile($file)
238
- {
239
- $file = trim(WPStaging::getWPpath() . $file);
240
-
241
- $file = wpstg_replace_windows_directory_separator($file);
242
-
243
- $directory = dirname($file);
244
-
245
- // Directory is excluded
246
- if ($this->isDirectoryExcluded($directory)) {
247
- $this->debugLog("Skipping directory by rule: {$file}", Logger::TYPE_INFO);
248
- return false;
249
- }
250
-
251
- // File is excluded
252
- if ($this->isFileExcluded($file)) {
253
- $this->debugLog("Skipping file by rule: {$file}", Logger::TYPE_INFO);
254
- return false;
255
- }
256
- // Path + File is excluded
257
- if ($this->isFileExcludedFullPath($file)) {
258
- $this->debugLog("Skipping file by rule: {$file}", Logger::TYPE_INFO);
259
- return false;
260
- }
261
-
262
- // Invalid file, skipping it as if succeeded
263
- if (!is_file($file)) {
264
- $this->log("File doesn't exist {$file}", Logger::TYPE_WARNING);
265
- return true;
266
- }
267
- // Invalid file, skipping it as if succeeded
268
- if (!is_readable($file)) {
269
- $this->log("Can't read file {$file}", Logger::TYPE_WARNING);
270
- return true;
271
- }
272
-
273
-
274
- // Get file size
275
- $fileSize = filesize($file);
276
-
277
- // File is over maximum allowed file size (8MB)
278
- if ($fileSize >= $this->settings->maxFileSize * 1000000) {
279
- $this->log("Skipping big file: {$file}", Logger::TYPE_INFO);
280
- return false;
281
- }
282
-
283
- // Failed to get destination
284
- if (($destination = $this->getDestination($file)) === false) {
285
- $this->log(
286
- "Can't get the destination of {$file}",
287
- Logger::TYPE_WARNING
288
- );
289
- return false;
290
- }
291
-
292
- // File is over batch size
293
- if ($fileSize >= $this->settings->batchSize) {
294
- return $this->copyBig(
295
- $file,
296
- $destination,
297
- $this->settings->batchSize
298
- );
299
- }
300
-
301
- // Attempt to copy
302
- if (!@copy($file, $destination)) {
303
- $errors = error_get_last();
304
- $this->log(
305
- "Files: Failed to copy file to destination. Error: {$errors['message']} {$file} -> {$destination}",
306
- Logger::TYPE_ERROR
307
- );
308
- return false;
309
- }
310
-
311
- // Set file permissions
312
- @chmod($destination, $this->permissions->getFilesOctal());
313
-
314
- $this->setDirPermissions($destination);
315
-
316
- return true;
317
- }
318
-
319
- /**
320
- * Set directory permissions
321
- * @param string $file
322
- * @return boolean
323
- */
324
- private function setDirPermissions($file)
325
- {
326
- $dir = dirname($file);
327
- if (is_dir($dir)) {
328
- @chmod($dir, $this->permissions->getDirectoryOctal());
329
- }
330
- return false;
331
- }
332
-
333
- /**
334
- * Gets destination file and checks if the directory exists, if it does not attempts to create it.
335
- * If creating destination directory fails, it returns false, gives destination full path otherwise
336
- * @param string $file
337
- * @return bool|string
338
- */
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
351
- );
352
- return false;
353
- }
354
-
355
- return $this->sanitizeDirectorySeparator($destinationPath);
356
- }
357
-
358
- /**
359
- * Copy bigger files than $this->settings->batchSize
360
- * @param string $src
361
- * @param string $dst
362
- * @param int $buffersize
363
- * @return boolean
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;
372
- }
373
-
374
- // Try first method:
375
- while (!feof($src)) {
376
- if (fwrite($dest, fread($src, $buffersize)) === false) {
377
- $error = true;
378
- }
379
- }
380
- // Try second method if first one failed
381
- if (isset($error) && ($error === true)) {
382
- while (!feof($src)) {
383
- if (stream_copy_to_stream($src, $dest, 1024) === false) {
384
- $this->log("Can not copy file; {$src} -> {$dest}");
385
- fclose($src);
386
- fclose($dest);
387
- return false;
388
- }
389
- }
390
- }
391
- // Close any open handler
392
- fclose($src);
393
- fclose($dest);
394
- return true;
395
- }
396
-
397
- /**
398
- * Check if certain file is excluded from copying process
399
- *
400
- * @param string $file full path + filename
401
- * @return boolean
402
- */
403
- private function isFileExcluded($file)
404
- {
405
- $excludedFiles = (array)$this->options->excludedFiles;
406
-
407
- // Remove .htaccess and web.config from 'excludedFiles' if staging site is copied to a subdomain
408
- if ($this->isIdenticalHostname() === false) {
409
- $excludedFiles = \array_diff(
410
- $excludedFiles,
411
- ["web.config", ".htaccess"]
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
- }
425
-
426
- return false;
427
- }
428
-
429
- /**
430
- * Check if production and staging hostname are identical
431
- * If they are not identical we assume website is cloned to a subdomain and not into a subfolder
432
- * @return boolean
433
- */
434
- private function isIdenticalHostname()
435
- {
436
- // hostname of production site without scheme
437
- $siteurl = get_site_url();
438
- $url = parse_url($siteurl);
439
- $productionHostname = $url['host'];
440
-
441
- // hostname of staging site without scheme
442
- $cloneUrl = empty($this->options->cloneHostname) ? $url : parse_url($this->options->cloneHostname);
443
- $targetHostname = $cloneUrl['host'];
444
-
445
- // Check if target hostname beginns with the production hostname
446
- // Only compare the hostname without path
447
- if (wpstg_starts_with($productionHostname, $targetHostname)) {
448
- return true;
449
- }
450
- return false;
451
- }
452
-
453
- /**
454
- * Check if certain file is excluded from copying process
455
- *
456
- * @param string $file filename including ending + (part) path e.g wp-content/db.php
457
- * @return boolean
458
- */
459
- private function isFileExcludedFullPath($file)
460
- {
461
- // If path + file exists
462
- foreach ($this->options->excludedFilesFullPath as $excludedFile) {
463
- if (strpos($file, $excludedFile) !== false) {
464
- return true;
465
- }
466
- }
467
-
468
- return false;
469
- }
470
-
471
- /**
472
- * Replace backward slash with forward slash directory separator
473
- * Windows Compatibility Fix
474
- *
475
- * @param string $path Path
476
- * @return string
477
- */
478
- private function sanitizeDirectorySeparator($path)
479
- {
480
- return preg_replace('/[\\\\]+/', '/', $path);
481
- }
482
-
483
- /**
484
- * Check if directory is excluded from copying
485
- * @param string $directory
486
- * @return bool
487
- */
488
- private function isDirectoryExcluded($directory)
489
- {
490
- // Make sure that wp-staging-pro directory / plugin is never excluded
491
- if (strpos($directory, 'wp-staging') !== false || strpos($directory, 'wp-staging-pro') !== false) {
492
- return false;
493
- }
494
-
495
- $directory = trailingslashit($this->sanitizeDirectorySeparator($directory));
496
-
497
- foreach ($this->options->excludedDirectories as $excludedDirectory) {
498
- $excludedDirectory = trailingslashit($this->sanitizeDirectorySeparator($excludedDirectory));
499
- if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
500
- return true;
501
- }
502
- }
503
-
504
- return false;
505
- }
506
-
507
- /**
508
- * Check if directory is an extra directory and should be copied
509
- * @param string $directory
510
- * @return boolean
511
- */
512
- private function isExtraDirectory($directory)
513
- {
514
- $directory = $this->sanitizeDirectorySeparator($directory);
515
-
516
- foreach ($this->options->extraDirectories as $extraDirectory) {
517
- if (strpos(
518
- $directory,
519
- $this->sanitizeDirectorySeparator($extraDirectory)
520
- ) === 0) {
521
- return true;
522
- }
523
- }
524
-
525
- return false;
526
- }
527
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/Modules/Jobs/Multisite/Finish.php DELETED
@@ -1,135 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs\Multisite;
4
-
5
- use WPStaging\Core\WPStaging;
6
- use WPStaging\Backend\Modules\Jobs\Job;
7
- use WPStaging\Core\Utils\Helper;
8
-
9
- /**
10
- * Class Finish
11
- * @package WPStaging\Backend\Modules\Jobs
12
- */
13
- class Finish extends Job {
14
-
15
- /**
16
- * Clone Key
17
- * @var string
18
- */
19
- private $clone = '';
20
-
21
- /**
22
- * Start Module
23
- * @return object
24
- */
25
- public function start() {
26
- // sanitize the clone name before saving
27
- $this->clone = preg_replace( "#\W+#", '-', strtolower( $this->options->clone ) );
28
-
29
- // Delete Cache Files
30
- $this->deleteCacheFiles();
31
-
32
- // Prepare clone records & save scanned directories for delete job later
33
- $this->prepareCloneDataRecords();
34
-
35
- $this->options->isRunning = false;
36
-
37
- $return = [
38
- "directoryName" => $this->options->cloneDirectoryName,
39
- "path" => trailingslashit( $this->options->destinationDir ),
40
- "url" => $this->getDestinationUrl(),
41
- "number" => $this->options->cloneNumber,
42
- "version" => WPStaging::getVersion(),
43
- "status" => 'finished',
44
- "prefix" => $this->options->prefix,
45
- "last_msg" => $this->logger->getLastLogMsg(),
46
- "job" => $this->options->currentJob,
47
- "percentage" => 100
48
- ];
49
-
50
- do_action( 'wpstg_cloning_complete', $this->options );
51
-
52
-
53
- return ( object ) $return;
54
- }
55
-
56
- /**
57
- * Delete Cache Files
58
- */
59
- protected function deleteCacheFiles() {
60
- $this->log( "Finish: Deleting clone job's cache files..." );
61
-
62
- // Clean cache files
63
- $this->cache->delete( "clone_options" );
64
- $this->cache->delete( "files_to_copy" );
65
-
66
- $this->log( "Finish: Clone job's cache files have been deleted!" );
67
- }
68
-
69
- /**
70
- * Prepare clone records
71
- * @return bool
72
- */
73
- protected function prepareCloneDataRecords() {
74
- // Check if clones still exist
75
- $this->log( "Finish: Verifying existing clones..." );
76
-
77
- // Clone data already exists
78
- if( isset( $this->options->existingClones[$this->options->clone] ) ) {
79
- $this->options->existingClones[$this->options->clone]['datetime'] = time();
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;
88
- }
89
-
90
- // Save new clone data
91
- $this->log( "Finish: {$this->options->clone}'s clone job's data is not in database, generating data" );
92
-
93
- $this->options->existingClones[$this->clone] = [
94
- "directoryName" => $this->options->cloneDirectoryName,
95
- "path" => trailingslashit( $this->options->destinationDir ),
96
- "url" => $this->getDestinationUrl(),
97
- "number" => $this->options->cloneNumber,
98
- "version" => WPStaging::getVersion(),
99
- "status" => "finished",
100
- "prefix" => $this->options->prefix,
101
- "datetime" => time(),
102
- "databaseUser" => $this->options->databaseUser,
103
- "databasePassword" => $this->options->databasePassword,
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 ) {
112
- $this->log( "Finish: Failed to save {$this->options->clone}'s clone job data to database'" );
113
- return false;
114
- }
115
-
116
- return true;
117
- }
118
-
119
- /**
120
- * Get destination Hostname incl. scheme depending on whether WP has been installed in sub dir or not
121
- * @return string
122
- */
123
- private function getDestinationUrl() {
124
-
125
- if( !empty( $this->options->cloneHostname ) ) {
126
- return $this->options->cloneHostname;
127
- }
128
-
129
- // The relative path to the main multisite without appending a trailingslash e.g. wordpress
130
- $multisitePath = defined( 'PATH_CURRENT_SITE' ) ? PATH_CURRENT_SITE : '/';
131
-
132
- return rtrim( (new Helper)->getBaseUrl(), '/\\' ) . $multisitePath . $this->options->cloneDirectoryName;
133
- }
134
-
135
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/Modules/Jobs/PreserveDataFirstStep.php CHANGED
@@ -41,7 +41,7 @@ class PreserveDataFirstStep extends JobExecutable
41
 
42
  $this->stagingPrefix = $this->options->prefix;
43
 
44
- if($db->isExternalDatabase()){
45
  $this->stagingPrefix = $this->options->databasePrefix;
46
  }
47
 
@@ -49,7 +49,7 @@ class PreserveDataFirstStep extends JobExecutable
49
 
50
  $this->saveOptions();
51
 
52
- return ( object )$this->response;
53
  }
54
 
55
  /**
@@ -72,30 +72,33 @@ class PreserveDataFirstStep extends JobExecutable
72
  {
73
  // Delete wpstg_tmp_data and reset it
74
  $delete = $this->productionDb->query(
75
- $this->productionDb->prepare("DELETE FROM " . $this->productionDb->prefix . "options WHERE `option_name` = %s", "wpstg_tmp_data"
76
- )
77
  );
78
 
79
- if(!$this->tableExists($this->stagingPrefix . "options")){
80
  return true;
81
  }
82
 
83
  // Get wpstg_existing_clones_beta from staging database
84
  $result = $this->stagingDb->get_var(
85
  $this->stagingDb->prepare(
86
- "SELECT `option_value` FROM " . $this->stagingPrefix . "options WHERE `option_name` = %s", "wpstg_existing_clones_beta"
 
87
  )
88
  );
89
 
90
  // Nothing to do
91
- if (!$result){
92
  return true;
93
  }
94
 
95
  // Insert wpstg_existing_clones_beta into wpstg_tmp_data in production database
96
  $insert = $this->productionDb->query(
97
  $this->productionDb->prepare(
98
- "INSERT INTO `" . $this->productionDb->prefix . "options` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s )", "wpstg_tmp_data", $result, "no"
 
 
 
99
  )
100
  );
101
 
@@ -120,6 +123,4 @@ class PreserveDataFirstStep extends JobExecutable
120
  {
121
  return !($table != $this->stagingDb->get_var("SHOW TABLES LIKE '{$table}'"));
122
  }
123
-
124
-
125
  }
41
 
42
  $this->stagingPrefix = $this->options->prefix;
43
 
44
+ if ($db->isExternalDatabase()) {
45
  $this->stagingPrefix = $this->options->databasePrefix;
46
  }
47
 
49
 
50
  $this->saveOptions();
51
 
52
+ return (object)$this->response;
53
  }
54
 
55
  /**
72
  {
73
  // Delete wpstg_tmp_data and reset it
74
  $delete = $this->productionDb->query(
75
+ $this->productionDb->prepare("DELETE FROM " . $this->productionDb->prefix . "options WHERE `option_name` = %s", "wpstg_tmp_data")
 
76
  );
77
 
78
+ if (!$this->tableExists($this->stagingPrefix . "options")) {
79
  return true;
80
  }
81
 
82
  // Get wpstg_existing_clones_beta from staging database
83
  $result = $this->stagingDb->get_var(
84
  $this->stagingDb->prepare(
85
+ "SELECT `option_value` FROM " . $this->stagingPrefix . "options WHERE `option_name` = %s",
86
+ "wpstg_existing_clones_beta"
87
  )
88
  );
89
 
90
  // Nothing to do
91
+ if (!$result) {
92
  return true;
93
  }
94
 
95
  // Insert wpstg_existing_clones_beta into wpstg_tmp_data in production database
96
  $insert = $this->productionDb->query(
97
  $this->productionDb->prepare(
98
+ "INSERT INTO `" . $this->productionDb->prefix . "options` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s )",
99
+ "wpstg_tmp_data",
100
+ $result,
101
+ "no"
102
  )
103
  );
104
 
123
  {
124
  return !($table != $this->stagingDb->get_var("SHOW TABLES LIKE '{$table}'"));
125
  }
 
 
126
  }
Backend/Modules/Jobs/PreserveDataSecondStep.php CHANGED
@@ -35,7 +35,7 @@ class PreserveDataSecondStep extends JobExecutable
35
 
36
  $this->saveOptions();
37
 
38
- return ( object )$this->response;
39
  }
40
 
41
  /**
@@ -51,7 +51,7 @@ class PreserveDataSecondStep extends JobExecutable
51
 
52
  $this->stagingPrefix = $this->options->prefix;
53
 
54
- if($db->isExternalDatabase()){
55
  $this->stagingPrefix = $this->options->databasePrefix;
56
  }
57
 
@@ -70,25 +70,28 @@ class PreserveDataSecondStep extends JobExecutable
70
  // Get wpstg_tmp_data from production database
71
  $result = $this->productionDb->get_var(
72
  $this->productionDb->prepare(
73
- "SELECT `option_value` FROM " . $this->productionDb->prefix . "options WHERE `option_name` = %s", "wpstg_tmp_data"
 
74
  )
75
  );
76
 
77
  // Nothing to do
78
- if (!$result){
79
  return true;
80
  }
81
 
82
  // Delete wpstg_tmp_data
83
  $delete = $this->stagingDb->query(
84
- $this->stagingDb->prepare("DELETE FROM " . $this->stagingPrefix . "options WHERE `option_name` = %s", "wpstg_existing_clones_beta"
85
- )
86
  );
87
 
88
  // Insert wpstg_existing_clones_beta in staging database
89
  $insert = $this->stagingDb->query(
90
  $this->stagingDb->prepare(
91
- "INSERT INTO `" . $this->stagingPrefix . "options` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s )", "wpstg_existing_clones_beta", $result, "no"
 
 
 
92
  )
93
  );
94
 
@@ -103,6 +106,4 @@ class PreserveDataSecondStep extends JobExecutable
103
  }
104
  return true;
105
  }
106
-
107
-
108
  }
35
 
36
  $this->saveOptions();
37
 
38
+ return (object)$this->response;
39
  }
40
 
41
  /**
51
 
52
  $this->stagingPrefix = $this->options->prefix;
53
 
54
+ if ($db->isExternalDatabase()) {
55
  $this->stagingPrefix = $this->options->databasePrefix;
56
  }
57
 
70
  // Get wpstg_tmp_data from production database
71
  $result = $this->productionDb->get_var(
72
  $this->productionDb->prepare(
73
+ "SELECT `option_value` FROM " . $this->productionDb->prefix . "options WHERE `option_name` = %s",
74
+ "wpstg_tmp_data"
75
  )
76
  );
77
 
78
  // Nothing to do
79
+ if (!$result) {
80
  return true;
81
  }
82
 
83
  // Delete wpstg_tmp_data
84
  $delete = $this->stagingDb->query(
85
+ $this->stagingDb->prepare("DELETE FROM " . $this->stagingPrefix . "options WHERE `option_name` = %s", "wpstg_existing_clones_beta")
 
86
  );
87
 
88
  // Insert wpstg_existing_clones_beta in staging database
89
  $insert = $this->stagingDb->query(
90
  $this->stagingDb->prepare(
91
+ "INSERT INTO `" . $this->stagingPrefix . "options` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s )",
92
+ "wpstg_existing_clones_beta",
93
+ $result,
94
+ "no"
95
  )
96
  );
97
 
106
  }
107
  return true;
108
  }
 
 
109
  }
Backend/Modules/Jobs/ProcessLock.php CHANGED
@@ -10,19 +10,20 @@ use WPStaging\Core\WPStaging;
10
  * Class Cloning
11
  * @package WPStaging\Backend\Modules\Jobs
12
  */
13
- class ProcessLock extends JobExecutable {
 
14
 
15
  /**
16
  * Check if any process is already running
17
  * @return boolean
18
  */
19
- public function isRunning() {
 
20
  // Another process is running
21
  if (parent::isRunning()) {
 
22
 
23
- $this->log( "Another process is running" );
24
-
25
- $message = __( 'Hold on, another WP Staging process is already running...', 'wp-staging' );
26
 
27
  require_once WPSTG_PLUGIN_DIR . "Backend/views/clone/ajax/process-lock.php";
28
 
@@ -36,26 +37,26 @@ class ProcessLock extends JobExecutable {
36
  /**
37
  * remove process lock value
38
  */
39
- public function restart() {
40
- unset( $this->options->isRunning );
41
- $this->cache->delete( "clone_options" );
42
- $this->cache->delete( "files_to_copy" );
 
43
  }
44
 
45
  /**
46
  * abstract
47
  * @return void
48
  */
49
- protected function calculateTotalSteps() {
50
-
51
  }
52
 
53
  /**
54
  * abstract
55
  * @return bool
56
  */
57
- protected function execute() {
58
-
59
  }
60
-
61
  }
10
  * Class Cloning
11
  * @package WPStaging\Backend\Modules\Jobs
12
  */
13
+ class ProcessLock extends JobExecutable
14
+ {
15
 
16
  /**
17
  * Check if any process is already running
18
  * @return boolean
19
  */
20
+ public function isRunning()
21
+ {
22
  // Another process is running
23
  if (parent::isRunning()) {
24
+ $this->log("Another process is running");
25
 
26
+ $message = __('Hold on, another WP Staging process is already running...', 'wp-staging');
 
 
27
 
28
  require_once WPSTG_PLUGIN_DIR . "Backend/views/clone/ajax/process-lock.php";
29
 
37
  /**
38
  * remove process lock value
39
  */
40
+ public function restart()
41
+ {
42
+ unset($this->options->isRunning);
43
+ $this->cache->delete("clone_options");
44
+ $this->cache->delete("files_to_copy");
45
  }
46
 
47
  /**
48
  * abstract
49
  * @return void
50
  */
51
+ protected function calculateTotalSteps()
52
+ {
53
  }
54
 
55
  /**
56
  * abstract
57
  * @return bool
58
  */
59
+ protected function execute()
60
+ {
61
  }
 
62
  }
Backend/Modules/Jobs/Scan.php CHANGED
@@ -3,33 +3,65 @@
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  // No Direct Access
6
- if( !defined( "WPINC" ) ) {
7
  die;
8
  }
9
 
10
- use WPStaging\Command\Database\Snapshot\SnapshotHandler;
11
- use WPStaging\Core\WPStaging;
12
- use WPStaging\Core\Utils\Directories;
13
  use WPStaging\Backend\Optimizer\Optimizer;
14
  use WPStaging\Core\Iterators;
 
 
 
 
 
15
 
16
  /**
17
  * Class Scan
18
  * @package WPStaging\Backend\Modules\Jobs
 
 
19
  */
20
- class Scan extends Job {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  /** @var array */
23
  private $directories = [];
24
 
25
- /** @var Directories */
26
  private $objDirectories;
27
 
28
  /**
29
  * Upon class initialization
30
  */
31
- protected function initialize() {
32
- $this->objDirectories = new Directories();
 
33
 
34
  $this->getTables();
35
 
@@ -41,15 +73,16 @@ class Scan extends Job {
41
  /**
42
  * Start Module
43
  * @return $this|object
44
- * @throws \Exception
45
  */
46
- public function start() {
 
47
  // Basic Options
48
- $this->options->root = str_replace( ["\\", '/'], DIRECTORY_SEPARATOR, WPStaging::getWPpath() );
49
- $this->options->existingClones = get_option( "wpstg_existing_clones_beta", [] );
50
  $this->options->current = null;
51
 
52
- if( isset( $_POST["clone"] ) && array_key_exists( $_POST["clone"], $this->options->existingClones ) ) {
53
  $this->options->current = $_POST["clone"];
54
  }
55
 
@@ -67,7 +100,6 @@ class Scan extends Job {
67
  $this->options->includedExtraDirectories = [];
68
  $this->options->excludedDirectories = [];
69
  $this->options->extraDirectories = [];
70
- $this->options->directoriesToCopy = [];
71
  $this->options->scannedDirectories = [];
72
 
73
  // Job
@@ -78,9 +110,13 @@ class Scan extends Job {
78
  // Define mainJob to differentiate between cloning, updating and pushing
79
  $this->options->mainJob = 'cloning';
80
 
 
 
 
 
81
  // Delete previous cached files
82
- $this->cache->delete( "files_to_copy" );
83
- $this->cache->delete( "clone_options" );
84
 
85
  $this->saveOptions();
86
 
@@ -90,7 +126,8 @@ class Scan extends Job {
90
  /**
91
  * Make sure the Optimizer mu plugin is installed before cloning or pushing
92
  */
93
- private function installOptimizer() {
 
94
  $optimizer = new Optimizer();
95
  $optimizer->installOptimizer();
96
  }
@@ -101,18 +138,19 @@ class Scan extends Job {
101
  * @param int $precision
102
  * @return string
103
  */
104
- public function formatSize( $bytes, $precision = 2 ) {
105
- if( ( double ) $bytes < 1 ) {
 
106
  return '';
107
  }
108
 
109
  $units = ['B', "KB", "MB", "GB", "TB"];
110
 
111
- $bytes = ( double ) $bytes;
112
- $base = log( $bytes ) / log( 1000 ); // 1024 would be for MiB KiB etc
113
- $pow = pow( 1000, $base - floor( $base ) ); // Same rule for 1000
114
 
115
- return round( $pow, $precision ) . ' ' . $units[( int ) floor( $base )];
116
  }
117
 
118
  /**
@@ -120,34 +158,37 @@ class Scan extends Job {
120
  * @param bool $forceDisabled
121
  * @return string
122
  */
123
- public function directoryListing( $directories = null, $forceDisabled = false ) {
124
- if( $directories == null ) {
 
125
  $directories = $this->directories;
126
  }
127
 
128
  // Sort results
129
- uksort( $directories, 'strcasecmp' );
130
 
131
  $output = '';
132
- foreach ( $directories as $name => $directory ) {
133
  // Not a directory, possibly a symlink, therefore we will skip it
134
- if( !is_array( $directory ) ) {
135
  continue;
136
  }
137
 
138
  // Need to preserve keys so no array_shift()
139
- $data = reset( $directory );
140
- unset( $directory[key( $directory )] );
141
 
142
 
143
  $isChecked = (
144
- empty( $this->options->includedDirectories ) ||
145
- in_array( $data["path"], $this->options->includedDirectories )
146
  );
147
 
148
- $dataPath = isset( $data["path"] ) ? $data["path"] : '';
149
- $dataSize = isset( $data["size"] ) ? $data["size"] : '';
150
-
 
 
151
 
152
  // Select all wp core folders and their sub dirs.
153
  // Unselect all other folders (default setting)
@@ -155,9 +196,9 @@ class Scan extends Job {
155
  $name !== 'wp-includes' &&
156
  $name !== 'wp-content' &&
157
  $name !== 'sites') &&
158
- strpos( strrev( wpstg_replace_windows_directory_separator( $dataPath ) ), strrev( wpstg_replace_windows_directory_separator( ABSPATH . "wp-admin" ) ) ) === false &&
159
- strpos( strrev( wpstg_replace_windows_directory_separator( $dataPath ) ), strrev( wpstg_replace_windows_directory_separator( ABSPATH . "wp-includes" ) ) ) === false &&
160
- strpos( strrev( wpstg_replace_windows_directory_separator( $dataPath ) ), strrev( wpstg_replace_windows_directory_separator( ABSPATH . "wp-content" ) ) ) === false ? true : false;
161
 
162
  // Extra class to differentiate between wp core and non core folders
163
  $class = !$isDisabled ? 'wpstg-root' : 'wpstg-extra';
@@ -165,23 +206,32 @@ class Scan extends Job {
165
  $output .= "<div class='wpstg-dir'>";
166
  $output .= "<input type='checkbox' class='wpstg-check-dir " . $class . "'";
167
 
168
- if( $isChecked && !$isDisabled && !$forceDisabled )
169
  $output .= " checked";
 
 
 
 
 
 
 
 
170
 
171
- $output .= " name='selectedDirectories[]' value='{$dataPath}'>";
172
 
173
  $output .= "<a href='#' class='wpstg-expand-dirs ";
174
- if( !$isChecked || $isDisabled ) {
175
  $output .= " disabled";
176
  }
 
177
  $output .= "'>{$name}";
178
  $output .= "</a>";
179
  $output .= "<span class='wpstg-size-info'>{$this->formatSize( $dataSize )}</span>";
180
- $output .= isset( $this->settings->debugMode ) ? "<span class='wpstg-size-info'> {$dataPath}</span>" : "";
181
 
182
- if( !empty( $directory ) ) {
183
  $output .= "<div class='wpstg-dir wpstg-subdir'>";
184
- $output .= $this->directoryListing( $directory, $isDisabled );
185
  $output .= "</div>";
186
  }
187
 
@@ -196,47 +246,48 @@ class Scan extends Job {
196
  * Returns null when can't run disk_free_space function one way or another
197
  * @return bool|null
198
  */
199
- public function hasFreeDiskSpace() {
200
- if( !function_exists( "disk_free_space" ) ) {
 
201
  return null;
202
  }
203
 
204
 
205
  $data = [
206
- 'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( WPStaging::getWPpath() ) )
207
  ];
208
 
209
- echo json_encode( $data );
210
  die();
211
  }
212
 
213
  /**
214
  * Get Database Tables
215
  */
216
- protected function getTables() {
217
- $db = WPStaging::getInstance()->get( "wpdb" );
 
218
 
219
  $sql = "SHOW TABLE STATUS";
220
 
221
- $tables = $db->get_results( $sql );
222
 
223
  $currentTables = [];
224
 
225
  // Reset excluded Tables than loop through all tables
226
  $this->options->excludedTables = [];
227
- foreach ( $tables as $table ) {
228
-
229
  // Create array of unchecked tables
230
  // On the main website of a multisite installation, do not select network site tables beginning with wp_1_, wp_2_ etc.
231
  // (On network sites, the correct tables are selected anyway)
232
- if (( ! empty($db->prefix) && strpos($table->Name, $db->prefix) !== 0)
233
- || (is_multisite() && is_main_site() && preg_match('/^'.$db->prefix.'\d+_/', $table->Name))) {
 
 
234
  $this->options->excludedTables[] = $table->Name;
235
  }
236
 
237
- if ((strpos($table->Name, SnapshotHandler::PREFIX_MANUAL) !== 0)
238
- && (strpos($table->Name, SnapshotHandler::PREFIX_AUTOMATIC) !== 0)
239
- && ($table->Comment !== "VIEW")) {
240
  $currentTables[] = [
241
  "name" => $table->Name,
242
  "size" => ($table->Data_length + $table->Index_length)
@@ -244,68 +295,69 @@ class Scan extends Job {
244
  }
245
  }
246
 
247
- $this->options->tables = json_decode( json_encode( $currentTables ) );
248
  }
249
 
250
-
251
  /**
252
  * Get directories and main meta data about'em recursively
253
  */
254
- protected function getDirectories() {
 
255
 
256
- $directories = new Iterators\RecursiveDirectoryIterator( WPStaging::getWPpath() );
257
 
258
- foreach ( $directories as $directory ) {
259
  // Not a valid directory
260
- if( ($path = $this->getPath( $directory )) === false ) {
261
  continue;
262
  }
263
 
264
- $this->handleDirectory( $path );
265
 
266
  // Get Sub-directories
267
- $this->getSubDirectories( $directory->getRealPath() );
268
  }
269
 
270
  // Gather Plugins
271
- $this->getSubDirectories( WP_PLUGIN_DIR );
272
 
273
  // Gather Themes
274
- $this->getSubDirectories( WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "themes" );
275
 
276
  // Gather Custom Uploads Folder if there is one
277
- $this->getSubDirectories( $this->getUploadDir() );
278
  }
279
 
280
  /**
281
  * @param string $path
282
  * @return bool
283
  */
284
- protected function getSubDirectories( $path ) {
 
285
 
286
- if( !is_readable( $path ) ) {
287
  return false;
288
  }
289
 
290
- if( !is_dir( $path ) ) {
291
  return false;
292
  }
293
 
294
- // IMPORTANT: If this is not used and a folder belongs to another user
295
  // DirectoryIterator() will throw a fatal error which can not be catched with is_readable()
296
- if( !opendir( $path ) ) {
297
  return false;
298
  }
299
 
300
- $directories = new \DirectoryIterator( $path );
301
 
302
- foreach ( $directories as $directory ) {
303
  // Not a valid directory
304
- if( ($path = $this->getPath( $directory )) === false ) {
305
  continue;
306
  }
307
 
308
- $this->handleDirectory( $path );
309
  }
310
  return false;
311
  }
@@ -315,7 +367,8 @@ class Scan extends Job {
315
  * @param string
316
  * @return bool|string
317
  */
318
- protected function getPath( $directory ) {
 
319
 
320
  /*
321
  * Do not follow root path like src/web/..
@@ -323,13 +376,12 @@ class Scan extends Job {
323
  * Prevents open base dir restriction fatal errors
324
  */
325
 
326
- if( strpos( $directory->getRealPath(), WPStaging::getWPpath() ) !== 0 ) {
327
  return false;
328
  }
329
- $path = str_replace( WPStaging::getWPpath(), null, $directory->getRealPath() );
330
-
331
  // Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
332
- if( !$directory->isDir() || strlen( $path ) < 1 ) {
333
  return false;
334
  }
335
 
@@ -340,31 +392,32 @@ class Scan extends Job {
340
  * Organizes $this->directories
341
  * @param string $path
342
  */
343
- protected function handleDirectory( $path ) {
344
- $directoryArray = explode( DIRECTORY_SEPARATOR, $path );
345
- $total = is_array( $directoryArray ) || $directoryArray instanceof \Countable ? count( $directoryArray ) : 0;
 
346
 
347
- if( $total < 1 ) {
348
  return;
349
  }
350
 
351
  $total = $total - 1;
352
  $currentArray = &$this->directories;
353
 
354
- for ( $i = 0; $i <= $total; $i++ ) {
355
- if( !isset( $currentArray[$directoryArray[$i]] ) ) {
356
  $currentArray[$directoryArray[$i]] = [];
357
  }
358
 
359
  $currentArray = &$currentArray[$directoryArray[$i]];
360
 
361
  // Attach meta data to the end
362
- if( $i < $total ) {
363
  continue;
364
  }
365
 
366
  $fullPath = WPStaging::getWPpath() . $path;
367
- $size = $this->getDirectorySize( $fullPath );
368
 
369
  $currentArray["metaData"] = [
370
  "size" => $size,
@@ -378,12 +431,13 @@ class Scan extends Job {
378
  * @param string $path
379
  * @return int|null
380
  */
381
- protected function getDirectorySize( $path ) {
382
- if( !isset( $this->settings->checkDirectorySize ) || $this->settings->checkDirectorySize !== '1' ) {
 
383
  return null;
384
  }
385
 
386
- return $this->objDirectories->size( $path );
387
  }
388
 
389
  /**
@@ -391,42 +445,38 @@ class Scan extends Job {
391
  * @param string $dir
392
  * @return int
393
  */
394
- protected function getDirectorySizeInclSubdirs( $dir ) {
 
395
  $size = 0;
396
- foreach ( glob( rtrim( $dir, '/' ) . '/*', GLOB_NOSORT ) as $each ) {
397
- $size += is_file( $each ) ? filesize( $each ) : $this->getDirectorySizeInclSubdirs( $each );
398
  }
399
  return $size;
400
  }
401
 
402
-
403
  /**
404
- * Get absolute WP uploads path e.g.
405
- * Multisites: /var/www/htdocs/example.com/wp-content/uploads/sites/1 or /var/www/htdocs/example.com/wp-content/blogs.dir/1/files
406
- * Single sites: /var/www/htdocs/example.com/wp-content/uploads
 
 
 
 
 
 
407
  * @return string
408
  */
409
- protected function getUploadDir() {
410
- $uploads = wp_upload_dir( null, false );
411
- $baseDir = wpstg_replace_windows_directory_separator( $uploads['basedir'] );
412
-
413
- // If multisite (and if not the main site in a post-MU network)
414
- if( is_multisite() && !( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
415
- // blogs.dir is used on WP 3.5 and lower
416
- if( strpos( $baseDir, 'blogs.dir' ) !== false ) {
417
- // remove this piece from the basedir: /blogs.dir/2/files
418
- $uploadDir = wpstg_replace_first_match( '/blogs.dir/' . get_current_blog_id() . '/files', null, $baseDir );
419
- $dir = wpstg_replace_windows_directory_separator( $uploadDir . '/blogs.dir' );
420
- } else {
421
- // remove this piece from the basedir: /sites/2
422
- $uploadDir = wpstg_replace_first_match( '/sites/' . get_current_blog_id(), null, $baseDir );
423
- $dir = wpstg_replace_windows_directory_separator( $uploadDir . '/sites' );
424
- }
425
-
426
-
427
- return $dir;
428
  }
429
- return $baseDir;
430
- }
431
 
 
 
 
432
  }
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  // No Direct Access
6
+ if (!defined("WPINC")) {
7
  die;
8
  }
9
 
10
+ use Countable;
11
+ use DirectoryIterator;
12
+ use Exception;
13
  use WPStaging\Backend\Optimizer\Optimizer;
14
  use WPStaging\Core\Iterators;
15
+ use WPStaging\Core\Utils\Directories as DirectoriesUtil;
16
+ use WPStaging\Core\WPStaging;
17
+ use WPStaging\Framework\Database\LegacyDatabaseInfo;
18
+ use WPStaging\Framework\Utils\WpDefaultDirectories;
19
+ use WPStaging\Framework\Utils\Strings;
20
 
21
  /**
22
  * Class Scan
23
  * @package WPStaging\Backend\Modules\Jobs
24
+ *
25
+ * @todo replace WPStaging::getWPpath() with ABSPATH - separate PR
26
  */
27
+ class Scan extends Job
28
+ {
29
+ /**
30
+ * separator to separate directory path and its is scanned flag
31
+ * @var string
32
+ */
33
+ const DIRECTORY_PATH_FLAG_SEPARATOR = '::';
34
+
35
+ /**
36
+ * separator to separate directories
37
+ * @var string
38
+ */
39
+ const DIRECTORIES_SEPARATOR = ',';
40
+
41
+ /**
42
+ * const to use when directory is scanned
43
+ * @var int
44
+ */
45
+ const IS_RECURSIVE = '1';
46
+
47
+ /**
48
+ * const to use when directory is not scanned
49
+ * @var int
50
+ */
51
+ const IS_NON_RECURSIVE = '0';
52
 
53
  /** @var array */
54
  private $directories = [];
55
 
56
+ /** @var DirectoriesUtil */
57
  private $objDirectories;
58
 
59
  /**
60
  * Upon class initialization
61
  */
62
+ protected function initialize()
63
+ {
64
+ $this->objDirectories = new DirectoriesUtil();
65
 
66
  $this->getTables();
67
 
73
  /**
74
  * Start Module
75
  * @return $this|object
76
+ * @throws Exception
77
  */
78
+ public function start()
79
+ {
80
  // Basic Options
81
+ $this->options->root = str_replace(["\\", '/'], DIRECTORY_SEPARATOR, WPStaging::getWPpath());
82
+ $this->options->existingClones = get_option("wpstg_existing_clones_beta", []);
83
  $this->options->current = null;
84
 
85
+ if (isset($_POST["clone"]) && array_key_exists($_POST["clone"], $this->options->existingClones)) {
86
  $this->options->current = $_POST["clone"];
87
  }
88
 
100
  $this->options->includedExtraDirectories = [];
101
  $this->options->excludedDirectories = [];
102
  $this->options->extraDirectories = [];
 
103
  $this->options->scannedDirectories = [];
104
 
105
  // Job
110
  // Define mainJob to differentiate between cloning, updating and pushing
111
  $this->options->mainJob = 'cloning';
112
 
113
+ if ($this->options->current !== null) {
114
+ $this->options->mainJob = 'updating';
115
+ }
116
+
117
  // Delete previous cached files
118
+ $this->cache->delete("files_to_copy");
119
+ $this->cache->delete("clone_options");
120
 
121
  $this->saveOptions();
122
 
126
  /**
127
  * Make sure the Optimizer mu plugin is installed before cloning or pushing
128
  */
129
+ private function installOptimizer()
130
+ {
131
  $optimizer = new Optimizer();
132
  $optimizer->installOptimizer();
133
  }
138
  * @param int $precision
139
  * @return string
140
  */
141
+ public function formatSize($bytes, $precision = 2)
142
+ {
143
+ if ((double) $bytes < 1) {
144
  return '';
145
  }
146
 
147
  $units = ['B', "KB", "MB", "GB", "TB"];
148
 
149
+ $bytes = (double) $bytes;
150
+ $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
151
+ $pow = pow(1000, $base - floor($base)); // Same rule for 1000
152
 
153
+ return round($pow, $precision) . ' ' . $units[(int) floor($base)];
154
  }
155
 
156
  /**
158
  * @param bool $forceDisabled
159
  * @return string
160
  */
161
+ public function directoryListing($directories = null, $forceDisabled = false)
162
+ {
163
+ if ($directories == null) {
164
  $directories = $this->directories;
165
  }
166
 
167
  // Sort results
168
+ uksort($directories, 'strcasecmp');
169
 
170
  $output = '';
171
+ foreach ($directories as $name => $directory) {
172
  // Not a directory, possibly a symlink, therefore we will skip it
173
+ if (!is_array($directory)) {
174
  continue;
175
  }
176
 
177
  // Need to preserve keys so no array_shift()
178
+ $data = reset($directory);
179
+ unset($directory[key($directory)]);
180
 
181
 
182
  $isChecked = (
183
+ empty($this->options->includedDirectories) ||
184
+ in_array($data["path"], $this->options->includedDirectories)
185
  );
186
 
187
+ $dataPath = isset($data["path"]) ? $data["path"] : '';
188
+ $dataSize = isset($data["size"]) ? $data["size"] : '';
189
+ $strUtils = new Strings();
190
+ $path = $strUtils->sanitizeDirectorySeparator($dataPath);
191
+ $wpRoot = $strUtils->sanitizeDirectorySeparator(ABSPATH);
192
 
193
  // Select all wp core folders and their sub dirs.
194
  // Unselect all other folders (default setting)
196
  $name !== 'wp-includes' &&
197
  $name !== 'wp-content' &&
198
  $name !== 'sites') &&
199
+ strpos(strrev($path), strrev($wpRoot . "wp-admin")) === false &&
200
+ strpos(strrev($path), strrev($wpRoot . "wp-includes")) === false &&
201
+ strpos(strrev($path), strrev($wpRoot . "wp-content")) === false;
202
 
203
  // Extra class to differentiate between wp core and non core folders
204
  $class = !$isDisabled ? 'wpstg-root' : 'wpstg-extra';
206
  $output .= "<div class='wpstg-dir'>";
207
  $output .= "<input type='checkbox' class='wpstg-check-dir " . $class . "'";
208
 
209
+ if ($isChecked && !$isDisabled && !$forceDisabled) {
210
  $output .= " checked";
211
+ }
212
+
213
+ // append recursive flag to dataPath value for only wp root directories
214
+ $isScanned = !empty($directory);
215
+ $dirPath = $dataPath;
216
+ if ($class === 'wpstg-root') {
217
+ $dirPath = $this->appendRecursiveFlag($dirPath, $isScanned);
218
+ }
219
 
220
+ $output .= " name='selectedDirectories[]' value='{$dirPath}'>";
221
 
222
  $output .= "<a href='#' class='wpstg-expand-dirs ";
223
+ if (!$isChecked || $isDisabled) {
224
  $output .= " disabled";
225
  }
226
+
227
  $output .= "'>{$name}";
228
  $output .= "</a>";
229
  $output .= "<span class='wpstg-size-info'>{$this->formatSize( $dataSize )}</span>";
230
+ $output .= isset($this->settings->debugMode) ? "<span class='wpstg-size-info'> {$dataPath}</span>" : "";
231
 
232
+ if ($isScanned) {
233
  $output .= "<div class='wpstg-dir wpstg-subdir'>";
234
+ $output .= $this->directoryListing($directory, $isDisabled);
235
  $output .= "</div>";
236
  }
237
 
246
  * Returns null when can't run disk_free_space function one way or another
247
  * @return bool|null
248
  */
249
+ public function hasFreeDiskSpace()
250
+ {
251
+ if (!function_exists("disk_free_space")) {
252
  return null;
253
  }
254
 
255
 
256
  $data = [
257
+ 'usedspace' => $this->formatSize($this->getDirectorySizeInclSubdirs(WPStaging::getWPpath()))
258
  ];
259
 
260
+ echo json_encode($data);
261
  die();
262
  }
263
 
264
  /**
265
  * Get Database Tables
266
  */
267
+ protected function getTables()
268
+ {
269
+ $db = WPStaging::getInstance()->get("wpdb");
270
 
271
  $sql = "SHOW TABLE STATUS";
272
 
273
+ $tables = $db->get_results($sql);
274
 
275
  $currentTables = [];
276
 
277
  // Reset excluded Tables than loop through all tables
278
  $this->options->excludedTables = [];
279
+ foreach ($tables as $table) {
 
280
  // Create array of unchecked tables
281
  // On the main website of a multisite installation, do not select network site tables beginning with wp_1_, wp_2_ etc.
282
  // (On network sites, the correct tables are selected anyway)
283
+ if (
284
+ ( ! empty($db->prefix) && strpos($table->Name, $db->prefix) !== 0)
285
+ || (is_multisite() && is_main_site() && preg_match('/^' . $db->prefix . '\d+_/', $table->Name))
286
+ ) {
287
  $this->options->excludedTables[] = $table->Name;
288
  }
289
 
290
+ if ($table->Comment !== "VIEW" && !LegacyDatabaseInfo::isBackupTable($table->Name)) {
 
 
291
  $currentTables[] = [
292
  "name" => $table->Name,
293
  "size" => ($table->Data_length + $table->Index_length)
295
  }
296
  }
297
 
298
+ $this->options->tables = json_decode(json_encode($currentTables));
299
  }
300
 
 
301
  /**
302
  * Get directories and main meta data about'em recursively
303
  */
304
+ protected function getDirectories()
305
+ {
306
 
307
+ $directories = new Iterators\RecursiveDirectoryIterator(WPStaging::getWPpath());
308
 
309
+ foreach ($directories as $directory) {
310
  // Not a valid directory
311
+ if (($path = $this->getPath($directory)) === false) {
312
  continue;
313
  }
314
 
315
+ $this->handleDirectory($path);
316
 
317
  // Get Sub-directories
318
+ $this->getSubDirectories($directory->getRealPath());
319
  }
320
 
321
  // Gather Plugins
322
+ $this->getSubDirectories(WP_PLUGIN_DIR);
323
 
324
  // Gather Themes
325
+ $this->getSubDirectories(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "themes");
326
 
327
  // Gather Custom Uploads Folder if there is one
328
+ $this->getSubDirectories((new WpDefaultDirectories())->getSiteUploadsPath());
329
  }
330
 
331
  /**
332
  * @param string $path
333
  * @return bool
334
  */
335
+ protected function getSubDirectories($path)
336
+ {
337
 
338
+ if (!is_readable($path)) {
339
  return false;
340
  }
341
 
342
+ if (!is_dir($path)) {
343
  return false;
344
  }
345
 
346
+ // IMPORTANT: This is necessary if directory does not belongs to current php user
347
  // DirectoryIterator() will throw a fatal error which can not be catched with is_readable()
348
+ if (!opendir($path)) {
349
  return false;
350
  }
351
 
352
+ $directories = new DirectoryIterator($path);
353
 
354
+ foreach ($directories as $directory) {
355
  // Not a valid directory
356
+ if (($path = $this->getPath($directory)) === false) {
357
  continue;
358
  }
359
 
360
+ $this->handleDirectory($path);
361
  }
362
  return false;
363
  }
367
  * @param string
368
  * @return bool|string
369
  */
370
+ protected function getPath($directory)
371
+ {
372
 
373
  /*
374
  * Do not follow root path like src/web/..
376
  * Prevents open base dir restriction fatal errors
377
  */
378
 
379
+ if (strpos($directory->getRealPath(), WPStaging::getWPpath()) !== 0) {
380
  return false;
381
  }
382
+ $path = str_replace(WPStaging::getWPpath(), null, $directory->getRealPath());
 
383
  // Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
384
+ if (!$directory->isDir() || strlen($path) < 1) {
385
  return false;
386
  }
387
 
392
  * Organizes $this->directories
393
  * @param string $path
394
  */
395
+ protected function handleDirectory($path)
396
+ {
397
+ $directoryArray = explode(DIRECTORY_SEPARATOR, $path);
398
+ $total = is_array($directoryArray) || $directoryArray instanceof Countable ? count($directoryArray) : 0;
399
 
400
+ if ($total < 1) {
401
  return;
402
  }
403
 
404
  $total = $total - 1;
405
  $currentArray = &$this->directories;
406
 
407
+ for ($i = 0; $i <= $total; $i++) {
408
+ if (!isset($currentArray[$directoryArray[$i]])) {
409
  $currentArray[$directoryArray[$i]] = [];
410
  }
411
 
412
  $currentArray = &$currentArray[$directoryArray[$i]];
413
 
414
  // Attach meta data to the end
415
+ if ($i < $total) {
416
  continue;
417
  }
418
 
419
  $fullPath = WPStaging::getWPpath() . $path;
420
+ $size = $this->getDirectorySize($fullPath);
421
 
422
  $currentArray["metaData"] = [
423
  "size" => $size,
431
  * @param string $path
432
  * @return int|null
433
  */
434
+ protected function getDirectorySize($path)
435
+ {
436
+ if (!isset($this->settings->checkDirectorySize) || $this->settings->checkDirectorySize !== '1') {
437
  return null;
438
  }
439
 
440
+ return $this->objDirectories->size($path);
441
  }
442
 
443
  /**
445
  * @param string $dir
446
  * @return int
447
  */
448
+ protected function getDirectorySizeInclSubdirs($dir)
449
+ {
450
  $size = 0;
451
+ foreach (glob(rtrim($dir, '/') . '/*', GLOB_NOSORT) as $each) {
452
+ $size += is_file($each) ? filesize($each) : $this->getDirectorySizeInclSubdirs($each);
453
  }
454
  return $size;
455
  }
456
 
 
457
  /**
458
+ * Append recursive flag to directoryPath
459
+ * If directory is scanned then there is no need to recursively scan it,
460
+ * since all its direct child directories will be in the list already,
461
+ * so append IS_NON_RECURSIVE flag i.e. 0 to it.
462
+ * And we only need IS_RECURSIVE flag i.e. 1 for non scanned directories
463
+ * to custom recursive iterator over all it sub directories.
464
+ * Also remove wp root path from the directory path.
465
+ * @param string $directoryPath
466
+ * @param bool $isScanned
467
  * @return string
468
  */
469
+ protected function appendRecursiveFlag($directoryPath, $isScanned)
470
+ {
471
+ // use relative path for core directories
472
+ $filteredPath["directoryPath"] = str_replace(ABSPATH, '', $directoryPath);
473
+ $filteredPath["isRecursive"] = self::IS_RECURSIVE;
474
+ // no need to recursively iterate in directory job if already scanned
475
+ if ($isScanned) {
476
+ $filteredPath["isRecursive"] = self::IS_NON_RECURSIVE;
 
 
 
 
 
 
 
 
 
 
 
477
  }
 
 
478
 
479
+ // Don't use json_encode as it will increase the size of post request
480
+ return implode(self::DIRECTORY_PATH_FLAG_SEPARATOR, $filteredPath);
481
+ }
482
  }
Backend/Modules/Jobs/SearchReplace.php CHANGED
@@ -7,11 +7,12 @@ if (!defined("WPINC")) {
7
  die;
8
  }
9
 
10
- use WPStaging\Framework\CloningProcess\SearchReplace\SearchReplaceService;
11
- use WPStaging\Framework\Utils\Strings;
12
  use WPStaging\Core\Utils\Helper;
13
  use WPStaging\Core\Utils\Logger;
14
  use WPStaging\Core\Utils\Multisite;
 
 
 
15
 
16
  /**
17
  * Class Database
@@ -20,6 +21,21 @@ use WPStaging\Core\Utils\Multisite;
20
  class SearchReplace extends CloningProcess
21
  {
22
  use TotalStepsAreNumberOfTables;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  /**
25
  * @var int
@@ -87,7 +103,7 @@ class SearchReplace extends CloningProcess
87
  // Save option, progress
88
  $this->saveOptions();
89
 
90
- return ( object )$this->response;
91
  }
92
 
93
  /**
@@ -225,22 +241,26 @@ class SearchReplace extends CloningProcess
225
 
226
  // Search & Replace
227
  $this->searchReplace($table, []);
228
-
229
- // Set new offset
230
- $this->options->job->start += $this->settings->querySRLimit;
231
  }
232
 
233
  /**
234
  * Gets the columns in a table.
235
  * @access public
236
  * @param string $table The table to check.
237
- * @return array
 
238
  */
239
- private function get_columns($table)
240
  {
241
  $primary_key = null;
242
  $columns = [];
243
  $fields = $this->stagingDb->get_results('DESCRIBE ' . $table);
 
 
 
 
 
 
244
  if (is_array($fields)) {
245
  foreach ($fields as $column) {
246
  $columns[] = $column->Field;
@@ -256,7 +276,7 @@ class SearchReplace extends CloningProcess
256
  *
257
  * @param string $table The table to run the replacement on.
258
  * @param array $args An associative array containing arguments for this run.
259
- * @return bool
260
  */
261
  private function searchReplace($table, $args)
262
  {
@@ -285,21 +305,32 @@ class SearchReplace extends CloningProcess
285
  $args = apply_filters('wpstg_clone_searchreplace_params', $args);
286
 
287
  // Get columns and primary keys
288
- list($primary_key, $columns) = $this->get_columns($table);
 
 
 
 
 
 
 
 
289
 
290
  $currentRow = 0;
291
- $start = $this->options->job->start;
292
- $end = $this->settings->querySRLimit;
293
 
294
- $data = $this->stagingDb->get_results("SELECT * FROM $table LIMIT $start, $end", ARRAY_A);
295
 
296
  // Filter certain rows (of other plugins)
297
  $filter = $this->searchReplaceService->excludedStrings();
298
 
299
  $filter = apply_filters('wpstg_clone_searchreplace_excl_rows', $filter);
300
 
 
 
301
  // Go through the table rows
302
  foreach ($data as $row) {
 
303
  $currentRow++;
304
  $updateSql = [];
305
  $whereSql = [];
@@ -311,8 +342,10 @@ class SearchReplace extends CloningProcess
311
  }
312
 
313
  // Skip transients (There can be thousands of them. Save memory and increase performance)
314
- if (isset($row['option_name']) && $args['skip_transients'] === 'on' && strpos($row['option_name'], '_transient')
315
- !== false) {
 
 
316
  continue;
317
  }
318
  // Skip rows with more than 5MB to save memory. These rows contain log data or something similiar but never site relevant data
@@ -368,11 +401,9 @@ class SearchReplace extends CloningProcess
368
  }
369
  }
370
  } // end row loop
371
- unset($row);
372
- unset($updateSql);
373
- unset($whereSql);
374
- unset($sql);
375
- unset($currentRow);
376
 
377
  // DB Flush
378
  $this->stagingDb->flush();
@@ -411,11 +442,17 @@ class SearchReplace extends CloningProcess
411
  return false;
412
  }
413
 
 
 
 
 
414
  if ($this->options->job->start != 0) {
415
- return true;
 
416
  }
417
 
418
- $this->options->job->total = ( int )$this->productionDb->get_var("SELECT COUNT(1) FROM {$old}");
 
419
 
420
  if ($this->options->job->total == 0) {
421
  $this->finishStep();
@@ -451,7 +488,7 @@ class SearchReplace extends CloningProcess
451
  /**
452
  * Finish the step
453
  */
454
- private function finishStep()
455
  {
456
  // This job is not finished yet
457
  if ($this->options->job->total > $this->options->job->start) {
@@ -496,4 +533,30 @@ class SearchReplace extends CloningProcess
496
 
497
  return str_replace([$home, '/'], '', $siteurl);
498
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  }
7
  die;
8
  }
9
 
 
 
10
  use WPStaging\Core\Utils\Helper;
11
  use WPStaging\Core\Utils\Logger;
12
  use WPStaging\Core\Utils\Multisite;
13
+ use WPStaging\Framework\CloningProcess\SearchReplace\SearchReplaceService;
14
+ use WPStaging\Framework\Traits\DbRowsGeneratorTrait;
15
+ use WPStaging\Framework\Utils\Strings;
16
 
17
  /**
18
  * Class Database
21
  class SearchReplace extends CloningProcess
22
  {
23
  use TotalStepsAreNumberOfTables;
24
+ use DbRowsGeneratorTrait;
25
+
26
+ /**
27
+ * The maximum number of failed attempts after which the Job should just move on.
28
+ *
29
+ * @var int
30
+ */
31
+ protected $maxFailedAttempts = 10;
32
+
33
+ /**
34
+ * The number of processed items, or `null` if the job did not run yet.
35
+ *
36
+ * @var int|null
37
+ */
38
+ protected $processed;
39
 
40
  /**
41
  * @var int
103
  // Save option, progress
104
  $this->saveOptions();
105
 
106
+ return (object)$this->response;
107
  }
108
 
109
  /**
241
 
242
  // Search & Replace
243
  $this->searchReplace($table, []);
 
 
 
244
  }
245
 
246
  /**
247
  * Gets the columns in a table.
248
  * @access public
249
  * @param string $table The table to check.
250
+ * @return array|false Either the primary key and columns structures, or `false` to indicate the query
251
+ * failed or the table is not describe-able.
252
  */
253
+ protected function get_columns($table)
254
  {
255
  $primary_key = null;
256
  $columns = [];
257
  $fields = $this->stagingDb->get_results('DESCRIBE ' . $table);
258
+
259
+ if (empty($fields)) {
260
+ // Either there was an error or the table has no columns.
261
+ return false;
262
+ }
263
+
264
  if (is_array($fields)) {
265
  foreach ($fields as $column) {
266
  $columns[] = $column->Field;
276
  *
277
  * @param string $table The table to run the replacement on.
278
  * @param array $args An associative array containing arguments for this run.
279
+ * @return bool Whether the search-replace operation was successful or not.
280
  */
281
  private function searchReplace($table, $args)
282
  {
305
  $args = apply_filters('wpstg_clone_searchreplace_params', $args);
306
 
307
  // Get columns and primary keys
308
+ $primaryKeyAndColumns = $this->get_columns($table);
309
+
310
+ if (false === $primaryKeyAndColumns) {
311
+ // Stop here: for some reason the table cannot be described or there was an error.
312
+ ++$this->options->job->failedAttempts;
313
+ return false;
314
+ }
315
+
316
+ list($primary_key, $columns) = $primaryKeyAndColumns;
317
 
318
  $currentRow = 0;
319
+ $offset = $this->options->job->start;
320
+ $limit = $this->settings->querySRLimit;
321
 
322
+ $data = $this->rowsGenerator($table, $offset, $limit, $this->stagingDb);
323
 
324
  // Filter certain rows (of other plugins)
325
  $filter = $this->searchReplaceService->excludedStrings();
326
 
327
  $filter = apply_filters('wpstg_clone_searchreplace_excl_rows', $filter);
328
 
329
+ $processed = 0;
330
+
331
  // Go through the table rows
332
  foreach ($data as $row) {
333
+ $processed++;
334
  $currentRow++;
335
  $updateSql = [];
336
  $whereSql = [];
342
  }
343
 
344
  // Skip transients (There can be thousands of them. Save memory and increase performance)
345
+ if (
346
+ isset($row['option_name']) && $args['skip_transients'] === 'on' && strpos($row['option_name'], '_transient')
347
+ !== false
348
+ ) {
349
  continue;
350
  }
351
  // Skip rows with more than 5MB to save memory. These rows contain log data or something similiar but never site relevant data
401
  }
402
  }
403
  } // end row loop
404
+ unset($row,$updateSql,$whereSql,$sql,$currentRow);
405
+
406
+ $this->updateJobStart($processed);
 
 
407
 
408
  // DB Flush
409
  $this->stagingDb->flush();
442
  return false;
443
  }
444
 
445
+ if (!isset($this->options->job->failedAttempts)) {
446
+ $this->options->job->failedAttempts = 0;
447
+ }
448
+
449
  if ($this->options->job->start != 0) {
450
+ // The job was attempted too many times and should be skipped now.
451
+ return !($this->options->job->failedAttempts > $this->maxFailedAttempts);
452
  }
453
 
454
+ $this->options->job->total = (int)$this->productionDb->get_var("SELECT COUNT(1) FROM {$old}");
455
+ $this->options->job->failedAttempts = 0;
456
 
457
  if ($this->options->job->total == 0) {
458
  $this->finishStep();
488
  /**
489
  * Finish the step
490
  */
491
+ protected function finishStep()
492
  {
493
  // This job is not finished yet
494
  if ($this->options->job->total > $this->options->job->start) {
533
 
534
  return str_replace([$home, '/'], '', $siteurl);
535
  }
536
+
537
+ /**
538
+ * Updates the (next) job start to reflect the number of actually processed rows.
539
+ *
540
+ * If nothing was processed, then the job start will be ticked by 1.
541
+ *
542
+ * @param int $processed The number of actually processed rows in this run.
543
+ t
544
+ * @return void The method does not return any value.
545
+ */
546
+ protected function updateJobStart($processed)
547
+ {
548
+ $this->processed = absint($processed);
549
+ $this->options->job->start += max($processed, 1);
550
+ }
551
+
552
+ /**
553
+ * Returns the number of rows processed by the job.
554
+ *
555
+ * @return int|null Either the number of rows processed by the Job, or `null` if the Job did
556
+ * not run yet.
557
+ */
558
+ public function getProcessed()
559
+ {
560
+ return $this->processed;
561
+ }
562
  }
Backend/Modules/Jobs/TotalStepsAreNumberOfTables.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Backend\Modules\Jobs;
5
 
6
-
7
  trait TotalStepsAreNumberOfTables
8
  {
9
  /**
@@ -14,4 +12,4 @@ trait TotalStepsAreNumberOfTables
14
  {
15
  $this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
16
  }
17
- }
1
  <?php
2
 
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
 
5
  trait TotalStepsAreNumberOfTables
6
  {
7
  /**
12
  {
13
  $this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
14
  }
15
+ }
Backend/Modules/Jobs/Updating.php CHANGED
@@ -2,9 +2,10 @@
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
- use WPStaging\Core\Utils\Logger;
6
  use WPStaging\Core\WPStaging;
7
  use WPStaging\Core\Utils\Helper;
 
 
8
  use WPStaging\Framework\Utils\WpDefaultDirectories;
9
 
10
  /**
@@ -13,24 +14,55 @@ use WPStaging\Framework\Utils\WpDefaultDirectories;
13
  */
14
  class Updating extends Job
15
  {
 
 
 
 
 
 
 
 
 
16
 
17
  /**
18
  * External Database Used
19
  * @var bool
20
  */
21
- public $isExternal;
22
 
23
  /**
24
  * @var mixed|null
25
  */
26
  private $db;
27
 
 
 
 
 
 
28
  /**
29
  * Initialize is called in \Job
30
  */
31
  public function initialize()
32
  {
33
  $this->db = WPStaging::getInstance()->get("wpdb");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  }
35
 
36
  /**
@@ -48,8 +80,6 @@ class Updating extends Job
48
  $this->cache->delete("files_to_copy");
49
 
50
  // Generate Options
51
- // Clone
52
- //$this->options->clone = $_POST["cloneID"];
53
  $this->options->clone = preg_replace("#\W+#", '-', strtolower($_POST["cloneID"]));
54
  $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
55
  $this->options->cloneNumber = 1;
@@ -66,8 +96,8 @@ class Updating extends Job
66
  '.gitignore',
67
  '*.log',
68
  'object-cache.php',
69
- 'web.config' // Important: Windows IIS configuration file. Do not copy this to the staging site is staging site is placed into subfolder
70
-
71
  ];
72
 
73
  $this->options->excludedFilesFullPath = [
@@ -77,11 +107,16 @@ class Updating extends Job
77
  ];
78
 
79
  // Define mainJob to differentiate between cloning, updating and pushing
80
- $this->options->mainJob = 'updating';
81
 
82
  // Job
83
  $this->options->job = new \stdClass();
84
 
 
 
 
 
 
85
  // Check if clone data already exists and use that one
86
  if (isset($this->options->existingClones[$this->options->clone])) {
87
  $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]['number'];
@@ -91,68 +126,59 @@ class Updating extends Job
91
  $this->options->databaseServer = $this->options->existingClones[$this->options->clone]['databaseServer'];
92
  $this->options->databasePrefix = $this->options->existingClones[$this->options->clone]['databasePrefix'];
93
  $this->options->destinationHostname = $this->options->existingClones[$this->options->clone]['url'];
94
- $this->options->uploadsSymlinked = isset($this->options->existingClones[strtolower($this->options->current)]['uploadsSymlinked']) ? $this->options->existingClones[strtolower($this->options->current)]['uploadsSymlinked'] : false;
95
- $this->options->prefix = $this->getStagingPrefix();
 
 
96
  $helper = new Helper();
97
  $this->options->homeHostname = $helper->getHomeUrlWithoutScheme();
98
  } else {
99
- wp_die('Fatal Error: Can not update clone because there is no clone data.');
100
- }
101
-
102
- $this->isExternal = !(empty($this->options->databaseUser) && empty($this->options->databasePassword));
103
-
104
- // Included Tables
105
- if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"])) {
106
- $this->options->tables = $_POST["includedTables"];
107
- } else {
108
- $this->options->tables = [];
109
- }
110
-
111
- /* $uploadsSymlinked = isset($_POST['uploadsSymlinked']) && $_POST['uploadsSymlinked'] === 'true';
112
- if ($uploadsSymlinked !== $this->options->uploadsSymlinked) {
113
- $this->returnException('Symlink Option cannot be changed at the moment. Updating Stopped!');
114
- }*/
115
 
116
- // Add upload folder to list of excluded directories for push if symlink option is enabled
117
- if ($this->options->uploadsSymlinked) {
118
- $wpUploadsFolder = (new WpDefaultDirectories())->getUploadPath();
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"]);
135
- }
136
 
137
  // Excluded Directories TOTAL
138
  // Do not copy these folders and plugins
139
  $excludedDirectories = [
140
- \WPStaging\Core\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
141
- \WPStaging\Core\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
142
- \WPStaging\Core\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wp-super-cache',
143
- \WPStaging\Core\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'peters-login-redirect',
144
  ];
145
 
146
- $this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
147
-
148
- // Included Directories
149
- if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"])) {
150
- $this->options->includedDirectories = wpstg_urldecode($_POST["includedDirectories"]);
151
  }
152
 
153
- // Extra Directories
154
- if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"])) {
155
- $this->options->extraDirectories = wpstg_urldecode($_POST["extraDirectories"]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  }
157
 
158
  $this->options->cloneDir = '';
@@ -164,19 +190,6 @@ class Updating extends Job
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(
175
- $this->options->includedDirectories, $this->options->extraDirectories
176
- );
177
-
178
- array_unshift($this->options->directoriesToCopy, ABSPATH);
179
-
180
  // Process lock state
181
  $this->options->isRunning = true;
182
 
@@ -190,47 +203,58 @@ class Updating extends Job
190
  private function getDestinationDir()
191
  {
192
  if (empty($this->options->cloneDir)) {
193
- return trailingslashit(\WPStaging\Core\WPStaging::getWPpath() . $this->options->cloneDirectoryName);
194
  }
195
  return trailingslashit($this->options->cloneDir);
196
  }
197
 
198
- /**
199
- * Check and return prefix of the staging site
200
- */
201
- public function getStagingPrefix()
202
  {
203
- // prefix not defined! Happens if staging site has ben generated with older version of wpstg
204
- // Try to get staging prefix from wp-config.php of staging site
205
- $this->options->prefix = $this->options->existingClones[$this->options->clone]['prefix'];
206
- if (empty($this->options->prefix)) {
207
- // Throw error if wp-config.php is not readable
208
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
209
- if (($content = @file_get_contents($path)) === false) {
210
- $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
211
- $this->returnException("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
212
- wp_die("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
213
- } else {
214
- // Get prefix from wp-config.php
215
- preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
216
-
217
- if (!empty($matches[1])) {
218
- $this->options->prefix = $matches[1];
219
- } else {
220
- $this->returnException("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
221
- wp_die("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
222
- }
223
- }
224
  }
 
225
 
226
- // Die() if staging prefix is the same as the live prefix
227
- if ($this->isExternal === false && $this->db->prefix === $this->options->prefix) {
228
- $this->log("Fatal Error: Can not update staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
229
- wp_die("Fatal Error: Can not update staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
 
 
 
230
  }
231
 
232
- // Else
233
- return $this->options->prefix;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  }
235
 
236
  /**
@@ -240,5 +264,4 @@ class Updating extends Job
240
  public function start()
241
  {
242
  }
243
-
244
  }
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
 
5
  use WPStaging\Core\WPStaging;
6
  use WPStaging\Core\Utils\Helper;
7
+ use WPStaging\Framework\Adapter\Database as DatabaseAdapter;
8
+ use WPStaging\Framework\Database\TableService;
9
  use WPStaging\Framework\Utils\WpDefaultDirectories;
10
 
11
  /**
14
  */
15
  class Updating extends Job
16
  {
17
+ /**
18
+ * @var string
19
+ */
20
+ const NORMAL_UPDATE = 'updating';
21
+
22
+ /**
23
+ * @var string
24
+ */
25
+ const RESET_UPDATE = 'resetting';
26
 
27
  /**
28
  * External Database Used
29
  * @var bool
30
  */
31
+ public $isExternalDb;
32
 
33
  /**
34
  * @var mixed|null
35
  */
36
  private $db;
37
 
38
+ /**
39
+ * @var string
40
+ */
41
+ private $mainJob;
42
+
43
  /**
44
  * Initialize is called in \Job
45
  */
46
  public function initialize()
47
  {
48
  $this->db = WPStaging::getInstance()->get("wpdb");
49
+ $this->mainJob = self::NORMAL_UPDATE;
50
+ }
51
+
52
+ /**
53
+ * @param $mainJob
54
+ */
55
+ public function setMainJob($mainJob)
56
+ {
57
+ $this->mainJob = $mainJob;
58
+ }
59
+
60
+ /**
61
+ * @return string
62
+ */
63
+ public function getMainJob()
64
+ {
65
+ return $this->mainJob;
66
  }
67
 
68
  /**
80
  $this->cache->delete("files_to_copy");
81
 
82
  // Generate Options
 
 
83
  $this->options->clone = preg_replace("#\W+#", '-', strtolower($_POST["cloneID"]));
84
  $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
85
  $this->options->cloneNumber = 1;
96
  '.gitignore',
97
  '*.log',
98
  'object-cache.php',
99
+ 'web.config', // Important: Windows IIS configuration file. Do not copy this to the staging site is staging site is placed into subfolder
100
+ '.wp-staging-cloneable', // File which make staging site to be cloneable
101
  ];
102
 
103
  $this->options->excludedFilesFullPath = [
107
  ];
108
 
109
  // Define mainJob to differentiate between cloning, updating and pushing
110
+ $this->options->mainJob = $this->mainJob;
111
 
112
  // Job
113
  $this->options->job = new \stdClass();
114
 
115
+ // This is required for reset job because Jobs/Scan was not run for reset
116
+ if ($this->mainJob === self::RESET_UPDATE) {
117
+ $this->options->existingClones = get_option("wpstg_existing_clones_beta", []);
118
+ }
119
+
120
  // Check if clone data already exists and use that one
121
  if (isset($this->options->existingClones[$this->options->clone])) {
122
  $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]['number'];
126
  $this->options->databaseServer = $this->options->existingClones[$this->options->clone]['databaseServer'];
127
  $this->options->databasePrefix = $this->options->existingClones[$this->options->clone]['databasePrefix'];
128
  $this->options->destinationHostname = $this->options->existingClones[$this->options->clone]['url'];
129
+ $this->options->uploadsSymlinked = isset($this->options->existingClones[strtolower($this->options->clone)]['uploadsSymlinked']) ? $this->options->existingClones[strtolower($this->options->clone)]['uploadsSymlinked'] : false;
130
+ $this->options->prefix = $this->options->existingClones[$this->options->clone]['prefix'];
131
+ $this->options->emailsAllowed = $this->options->existingClones[$this->options->clone]['emailsAllowed'];
132
+ //$this->options->prefix = $this->getStagingPrefix();
133
  $helper = new Helper();
134
  $this->options->homeHostname = $helper->getHomeUrlWithoutScheme();
135
  } else {
136
+ $job = 'update';
137
+ if ($this->mainJob === self::RESET_UPDATE) {
138
+ $job = 'reset';
139
+ }
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
+ wp_die("Fatal Error: Can not {$job} clone because there is no clone data.");
 
 
 
142
  }
143
 
144
+ $this->isExternalDb = !(empty($this->options->databaseUser) && empty($this->options->databasePassword));
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
  // Excluded Directories TOTAL
147
  // Do not copy these folders and plugins
148
  $excludedDirectories = [
149
+ WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
150
+ WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
151
+ WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wp-super-cache',
152
+ WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'peters-login-redirect',
153
  ];
154
 
155
+ // Add upload folder to list of excluded directories for push if symlink option is enabled
156
+ if ($this->options->uploadsSymlinked) {
157
+ $wpUploadsFolder = (new WpDefaultDirectories())->getUploadsPath();
158
+ $excludedDirectories[] = rtrim($wpUploadsFolder, '/\\');
 
159
  }
160
 
161
+ $this->options->excludedDirectories = $excludedDirectories;
162
+
163
+ if ($this->mainJob === self::RESET_UPDATE) {
164
+ $this->setTablesForResetJob();
165
+ $this->options->includedDirectories = (new WpDefaultDirectories())->getWpCoreDirectories();
166
+ // Files
167
+ $this->options->totalFiles = 0;
168
+ $this->options->totalFileSize = 0;
169
+ $this->options->copiedFiles = 0;
170
+ // Job
171
+ $this->options->currentJob = "PreserveDataFirstStep";
172
+ $this->options->currentStep = 0;
173
+ $this->options->totalSteps = 0;
174
+ } else {
175
+ $this->setTablesForUpdateJob();
176
+ $this->setDirectoriesForUpdateJob();
177
+ // Make sure it is always enabled for free version
178
+ $this->options->emailsAllowed = true;
179
+ if (defined('WPSTGPRO_VERSION')) {
180
+ $this->options->emailsAllowed = isset($_POST['emailsAllowed']) && $_POST['emailsAllowed'] !== "false";
181
+ }
182
  }
183
 
184
  $this->options->cloneDir = '';
190
 
191
  $this->options->cloneHostname = $this->options->destinationHostname;
192
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  // Process lock state
194
  $this->options->isRunning = true;
195
 
203
  private function getDestinationDir()
204
  {
205
  if (empty($this->options->cloneDir)) {
206
+ return trailingslashit(WPStaging::getWPpath() . $this->options->cloneDirectoryName);
207
  }
208
  return trailingslashit($this->options->cloneDir);
209
  }
210
 
211
+ private function setDirectoriesForUpdateJob()
 
 
 
212
  {
213
+ $this->options->areDirectoriesIncluded = isset($_POST['areDirectoriesIncluded']) && $_POST['areDirectoriesIncluded'] === 'true';
214
+
215
+ $directories = '';
216
+ // Included Directories
217
+ if ($this->options->areDirectoriesIncluded) {
218
+ $directories = isset($_POST["includedDirectories"]) ? $_POST["includedDirectories"] : '';
219
+ } else { // Get Included Directories from Excluded Directories
220
+ $directories = isset($_POST["excludedDirectories"]) ? $_POST["excludedDirectories"] : '';
221
+ }
222
+
223
+ $this->options->includedDirectories = (new WpDefaultDirectories())->getSelectedDirectories($directories, $this->options->areDirectoriesIncluded);
224
+
225
+ // Extra Directories
226
+ if (isset($_POST["extraDirectories"])) {
227
+ $this->options->extraDirectories = wpstg_urldecode(explode(Scan::DIRECTORIES_SEPARATOR, $_POST["extraDirectories"]));
 
 
 
 
 
 
228
  }
229
+ }
230
 
231
+ private function setTablesForUpdateJob()
232
+ {
233
+ // Included Tables
234
+ if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"])) {
235
+ $this->options->tables = $_POST["includedTables"];
236
+ } else {
237
+ $this->options->tables = [];
238
  }
239
 
240
+ // delete uploads folder before copying if uploads is not symlinked
241
+ $this->options->deleteUploadsFolder = !$this->options->uploadsSymlinked && isset($_POST['cleanUploadsDir']) && $_POST['cleanUploadsDir'] === 'true';
242
+ // should not backup uploads during update process
243
+ $this->options->backupUploadsFolder = false;
244
+ // clean plugins and themes dir before updating
245
+ $this->options->deletePluginsAndThemes = isset($_POST['cleanPluginsThemes']) && $_POST['cleanPluginsThemes'] === 'true';
246
+ // set default statuses for backup of uploads dir and cleaning of uploads, themes and plugins dirs
247
+ $this->options->statusBackupUploadsDir = 'skipped';
248
+ $this->options->statusContentCleaner = 'pending';
249
+ }
250
+
251
+ private function setTablesForResetJob()
252
+ {
253
+ $tableService = new TableService(new DatabaseAdapter());
254
+ $tables = $tableService->findTableStatusStartsWith();
255
+ $tables = $tableService->getTablesName($tables->toArray());
256
+ $this->options->tables = $tables;
257
+ $this->options->excludedTables = [];
258
  }
259
 
260
  /**
264
  public function start()
265
  {
266
  }
 
267
  }
Backend/Modules/Jobs/Verify.php CHANGED
@@ -13,7 +13,8 @@ if (!defined("WPINC")) {
13
  * Class Files
14
  * @package WPStaging\Backend\Modules\Jobs
15
  */
16
- class Verify extends JobExecutable {
 
17
 
18
  /**
19
  * @var \SplFileObject
@@ -38,7 +39,8 @@ class Verify extends JobExecutable {
38
  /**
39
  * Initialization
40
  */
41
- public function initialize() {
 
42
  $this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
43
 
44
  $this->getCopyFiles();
@@ -58,7 +60,8 @@ class Verify extends JobExecutable {
58
  * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
59
  * @return void
60
  */
61
- protected function calculateTotalSteps() {
 
62
  $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
63
  }
64
 
@@ -67,7 +70,8 @@ class Verify extends JobExecutable {
67
  * Returns false when over threshold limits are hit or when the job is done, true otherwise
68
  * @return bool
69
  */
70
- protected function execute() {
 
71
  // Finished
72
  if ($this->isFinished()) {
73
  $this->log("Verifying files finished");
@@ -93,7 +97,8 @@ class Verify extends JobExecutable {
93
  * Get files and copy
94
  * @return bool
95
  */
96
- private function getFilesAndVerify() {
 
97
  // Over limits threshold
98
  if ($this->isOverThreshold()) {
99
  // Prepare response and save current progress
@@ -112,7 +117,8 @@ class Verify extends JobExecutable {
112
  * Get files
113
  * @return void
114
  */
115
- protected function getVerifyFiles() {
 
116
  $file = $this->cache->getCacheDir() . "files_to_verify." . $this->cache->getCacheExtension();
117
 
118
  if (($this->verifyFiles = @file_get_contents($file)) === false) {
@@ -127,7 +133,8 @@ class Verify extends JobExecutable {
127
  * Get files
128
  * @return void
129
  */
130
- protected function getCopyFiles() {
 
131
  $file = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
132
 
133
  if (($this->verifyFiles = @file_get_contents($file)) === false) {
@@ -142,11 +149,12 @@ class Verify extends JobExecutable {
142
  * Save Result of File Verification
143
  * @return bool
144
  */
145
- protected function saveVerifyFiles() {
 
146
 
147
  // Get file copy differences
148
  $filesVerified = array_diff($this->files, $this->verifyFiles);
149
-
150
  $fileName = $this->cache->getCacheDir() . "files_verified" . $this->cache->getCacheExtension();
151
  $files = implode(PHP_EOL, $filesVerified);
152
 
@@ -157,11 +165,11 @@ class Verify extends JobExecutable {
157
  * Checks Whether There is Any Job to Execute or Not
158
  * @return bool
159
  */
160
- private function isFinished() {
 
161
  return (
162
  $this->options->currentStep > $this->options->totalSteps ||
163
  $this->options->verifiedFiles >= $this->options->totalFiles
164
  );
165
  }
166
-
167
  }
13
  * Class Files
14
  * @package WPStaging\Backend\Modules\Jobs
15
  */
16
+ class Verify extends JobExecutable
17
+ {
18
 
19
  /**
20
  * @var \SplFileObject
39
  /**
40
  * Initialization
41
  */
42
+ public function initialize()
43
+ {
44
  $this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
45
 
46
  $this->getCopyFiles();
60
  * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
61
  * @return void
62
  */
63
+ protected function calculateTotalSteps()
64
+ {
65
  $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
66
  }
67
 
70
  * Returns false when over threshold limits are hit or when the job is done, true otherwise
71
  * @return bool
72
  */
73
+ protected function execute()
74
+ {
75
  // Finished
76
  if ($this->isFinished()) {
77
  $this->log("Verifying files finished");
97
  * Get files and copy
98
  * @return bool
99
  */
100
+ private function getFilesAndVerify()
101
+ {
102
  // Over limits threshold
103
  if ($this->isOverThreshold()) {
104
  // Prepare response and save current progress
117
  * Get files
118
  * @return void
119
  */
120
+ protected function getVerifyFiles()
121
+ {
122
  $file = $this->cache->getCacheDir() . "files_to_verify." . $this->cache->getCacheExtension();
123
 
124
  if (($this->verifyFiles = @file_get_contents($file)) === false) {
133
  * Get files
134
  * @return void
135
  */
136
+ protected function getCopyFiles()
137
+ {
138
  $file = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
139
 
140
  if (($this->verifyFiles = @file_get_contents($file)) === false) {
149
  * Save Result of File Verification
150
  * @return bool
151
  */
152
+ protected function saveVerifyFiles()
153
+ {
154
 
155
  // Get file copy differences
156
  $filesVerified = array_diff($this->files, $this->verifyFiles);
157
+
158
  $fileName = $this->cache->getCacheDir() . "files_verified" . $this->cache->getCacheExtension();
159
  $files = implode(PHP_EOL, $filesVerified);
160
 
165
  * Checks Whether There is Any Job to Execute or Not
166
  * @return bool
167
  */
168
+ private function isFinished()
169
+ {
170
  return (
171
  $this->options->currentStep > $this->options->totalSteps ||
172
  $this->options->verifiedFiles >= $this->options->totalFiles
173
  );
174
  }
 
175
  }
Backend/Modules/SystemInfo.php CHANGED
@@ -160,10 +160,10 @@ class SystemInfo
160
  public function wpstaging()
161
  {
162
  // Get wpstg settings
163
- $settings = ( object )get_option('wpstg_settings', []);
164
 
165
  // Clones data < 1.1.6.x
166
- $clones = ( object )get_option('wpstg_existing_clones', []);
167
  // Clones data version > 2.x
168
  $clonesBeta = get_option('wpstg_existing_clones_beta', []);
169
 
@@ -184,7 +184,6 @@ class SystemInfo
184
  $output .= PHP_EOL . PHP_EOL . "-- Available Sites Version > 2.0.x" . PHP_EOL . PHP_EOL;
185
 
186
  foreach ($clonesBeta as $key => $clone) {
187
-
188
  $path = !empty($clone['path']) ? $clone['path'] : 'undefined';
189
 
190
  $output .= $this->info("Number:", isset($clone['number']) ? $clone['number'] : 'undefined');
@@ -206,8 +205,8 @@ class SystemInfo
206
  //$output .= PHP_EOL . PHP_EOL;
207
 
208
  $output .= $this->info("Plugin Pro Version:", get_option('wpstgpro_version', 'undefined'));
209
- $output .= $this->info("Plugin Pro License Key:", get_option('wpstg_license_key'));
210
- $output .= $this->info("Plugin Free Version:", get_option('wpstg_version', 'undefined'));
211
  $output .= $this->info("Install Date:", get_option('wpstg_installDate', 'undefined'));
212
  $output .= $this->info("Upgraded from Pro:", get_option('wpstgpro_version_upgraded_from', 'undefined'));
213
  $output .= $this->info("Upgraded from Free:", get_option('wpstg_version_upgraded_from', 'undefined'));
@@ -224,7 +223,7 @@ class SystemInfo
224
  public function browser()
225
  {
226
  $output = $this->header("User Browser");
227
- $output .= (new Browser);
228
 
229
  return apply_filters("wpstg_sysinfo_after_user_browser", $output);
230
  }
@@ -264,7 +263,8 @@ class SystemInfo
264
 
265
  // Send request
266
  $response = wp_remote_post(
267
- "https://www.paypal.com/cgi-bin/webscr", [
 
268
  "sslverify" => false,
269
  "timeout" => 60,
270
  "user-agent" => "WPSTG/" . WPStaging::getVersion(),
@@ -290,7 +290,8 @@ class SystemInfo
290
  $output .= $this->info("Version:", get_bloginfo("version"));
291
  $output .= $this->info("Language:", (defined("WPLANG") && WPLANG) ? WPLANG : "en_US");
292
 
293
- $permalinkStructure = get_option("permalink_structure");;
 
294
  $output .= $this->info("Permalink Structure:", ($permalinkStructure) ? $permalinkStructure : "Default");
295
 
296
  $output .= $this->info("Active Theme:", $this->theme());
@@ -313,12 +314,14 @@ class SystemInfo
313
  // Constants
314
  $output .= $this->info("WP Content Path:", WP_CONTENT_DIR);
315
  $output .= $this->info("WP Plugin Dir:", WP_PLUGIN_DIR);
316
- if (defined('UPLOADS'))
317
  $output .= $this->info("WP UPLOADS CONST:", UPLOADS);
 
318
  $uploads = wp_upload_dir();
319
  $output .= $this->info("WP Uploads Dir:", $uploads['basedir']);
320
- if (defined('WP_TEMP_DIR'))
321
  $output .= $this->info("WP Temp Dir:", WP_TEMP_DIR);
 
322
 
323
  // WP Debug
324
  $output .= $this->info("WP_DEBUG:", (defined("WP_DEBUG")) ? WP_DEBUG ? "Enabled" : "Disabled" : "Not set");
@@ -589,7 +592,6 @@ class SystemInfo
589
  if (($content = @file_get_contents($path)) === false) {
590
  return 'Can\'t find staging wp-config.php';
591
  } else {
592
-
593
  // Get prefix from wp-config.php
594
  //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
595
  preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
@@ -628,5 +630,4 @@ class SystemInfo
628
  }
629
  return $matches[1];
630
  }
631
-
632
  }
160
  public function wpstaging()
161
  {
162
  // Get wpstg settings
163
+ $settings = (object)get_option('wpstg_settings', []);
164
 
165
  // Clones data < 1.1.6.x
166
+ $clones = (object)get_option('wpstg_existing_clones', []);
167
  // Clones data version > 2.x
168
  $clonesBeta = get_option('wpstg_existing_clones_beta', []);
169
 
184
  $output .= PHP_EOL . PHP_EOL . "-- Available Sites Version > 2.0.x" . PHP_EOL . PHP_EOL;
185
 
186
  foreach ($clonesBeta as $key => $clone) {
 
187
  $path = !empty($clone['path']) ? $clone['path'] : 'undefined';
188
 
189
  $output .= $this->info("Number:", isset($clone['number']) ? $clone['number'] : 'undefined');
205
  //$output .= PHP_EOL . PHP_EOL;
206
 
207
  $output .= $this->info("Plugin Pro Version:", get_option('wpstgpro_version', 'undefined'));
208
+ $output .= $this->info("Plugin Pro License Key:", get_option('wpstg_license_key'));
209
+ $output .= $this->info("Plugin Free Version:", get_option('wpstg_version', 'undefined'));
210
  $output .= $this->info("Install Date:", get_option('wpstg_installDate', 'undefined'));
211
  $output .= $this->info("Upgraded from Pro:", get_option('wpstgpro_version_upgraded_from', 'undefined'));
212
  $output .= $this->info("Upgraded from Free:", get_option('wpstg_version_upgraded_from', 'undefined'));
223
  public function browser()
224
  {
225
  $output = $this->header("User Browser");
226
+ $output .= (new Browser());
227
 
228
  return apply_filters("wpstg_sysinfo_after_user_browser", $output);
229
  }
263
 
264
  // Send request
265
  $response = wp_remote_post(
266
+ "https://www.paypal.com/cgi-bin/webscr",
267
+ [
268
  "sslverify" => false,
269
  "timeout" => 60,
270
  "user-agent" => "WPSTG/" . WPStaging::getVersion(),
290
  $output .= $this->info("Version:", get_bloginfo("version"));
291
  $output .= $this->info("Language:", (defined("WPLANG") && WPLANG) ? WPLANG : "en_US");
292
 
293
+ $permalinkStructure = get_option("permalink_structure");
294
+ ;
295
  $output .= $this->info("Permalink Structure:", ($permalinkStructure) ? $permalinkStructure : "Default");
296
 
297
  $output .= $this->info("Active Theme:", $this->theme());
314
  // Constants
315
  $output .= $this->info("WP Content Path:", WP_CONTENT_DIR);
316
  $output .= $this->info("WP Plugin Dir:", WP_PLUGIN_DIR);
317
+ if (defined('UPLOADS')) {
318
  $output .= $this->info("WP UPLOADS CONST:", UPLOADS);
319
+ }
320
  $uploads = wp_upload_dir();
321
  $output .= $this->info("WP Uploads Dir:", $uploads['basedir']);
322
+ if (defined('WP_TEMP_DIR')) {
323
  $output .= $this->info("WP Temp Dir:", WP_TEMP_DIR);
324
+ }
325
 
326
  // WP Debug
327
  $output .= $this->info("WP_DEBUG:", (defined("WP_DEBUG")) ? WP_DEBUG ? "Enabled" : "Disabled" : "Not set");
592
  if (($content = @file_get_contents($path)) === false) {
593
  return 'Can\'t find staging wp-config.php';
594
  } else {
 
595
  // Get prefix from wp-config.php
596
  //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
597
  preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
630
  }
631
  return $matches[1];
632
  }
 
633
  }
Backend/Modules/Views/Forms/Settings.php CHANGED
@@ -14,243 +14,265 @@ use WPStaging\Backend\Modules\Views\Tabs\Tabs;
14
  * Class Settings
15
  * @package WPStaging\Backend\Modules\Views\Forms
16
  */
17
- class Settings {
 
18
 
19
  /**
20
  * @var array
21
  */
22
- private $form = [];
23
 
24
  /**
25
  * @var Tabs
26
  */
27
- private $tabs;
28
 
29
  /**
30
  * Settings constructor.
31
  * @param Tabs $tabs
32
  */
33
- public function __construct( $tabs ) {
34
- $this->tabs = $tabs;
35
-
36
- foreach ( $this->tabs->get() as $id => $name ) {
37
- if( !method_exists( $this, $id ) ) {
38
- continue;
39
- }
40
-
41
- $this->{$id}();
42
- }
43
- }
44
-
45
- private function general() {
46
- $this->form["general"] = new Form();
47
-
48
- $settings = json_decode( json_encode( get_option( "wpstg_settings", [] ) ) );
49
-
50
- // DB Copy Query Limit
51
- $element = new Numerical(
52
- "wpstg_settings[queryLimit]", [
53
- "class" => "medium-text",
54
- "step" => 1,
55
- "max" => 999999,
56
- "min" => 0
 
 
 
57
  ]
58
- );
59
-
60
- $this->form["general"]->add(
61
- $element->setLabel(__("DB Copy Query Limit", "wp-staging"))
62
- ->setDefault( isset( $settings->queryLimit ) ? $settings->queryLimit : 10000 )
63
- );
64
- // DB Search & Replace Query Limit
65
- $element = new Numerical(
66
- "wpstg_settings[querySRLimit]", [
67
- "class" => "medium-text",
68
- "step" => 1,
69
- "max" => 999999,
70
- "min" => 0
 
71
  ]
72
- );
73
-
74
- $this->form["general"]->add(
75
- $element->setLabel(__("DB Search & Replace Limit", "wp-staging"))
76
- ->setDefault( isset( $settings->querySRLimit ) ? $settings->querySRLimit : 5000 )
77
- );
78
-
79
- $options = ['1' => '1', '10' => '10', '50' => '50', '250' => '250', '500' => '500', '1000' => '1000'];
80
- // DB Copy Query Limit
81
- $element = new Select(
82
- "wpstg_settings[fileLimit]", $options, [
83
- "class" => "medium-text",
84
- "step" => 1,
85
- "max" => 999999,
86
- "min" => 0
 
 
87
  ]
88
- );
 
 
89
 
90
- $this->form["general"]->add(
91
- $element->setLabel(__("File Copy Limit" , "wp-staging"))->setDefault( isset( $settings->fileLimit ) ? $settings->fileLimit : '50' )
92
- );
93
 
94
 
95
- // File Copy Batch Size
96
- $element = new Numerical(
97
- "wpstg_settings[maxFileSize]", [
98
- "class" => "medium-text",
99
- "step" => 1,
100
- "max" => 999999,
101
- "min" => 0
 
102
  ]
103
- );
104
-
105
- $this->form["general"]->add(
106
- $element->setLabel(__("Maximum File Size (MB)", "wp-staging"))
107
- ->setDefault( isset( $settings->maxFileSize ) ? $settings->maxFileSize : 8 )
108
- );
109
-
110
- // File Copy Batch Size
111
- $element = new Numerical(
112
- "wpstg_settings[batchSize]", [
113
- "class" => "medium-text",
114
- "step" => 1,
115
- "max" => 999999,
116
- "min" => 0
 
117
  ]
118
- );
119
-
120
- $this->form["general"]->add(
121
- $element->setLabel(__("File Copy Batch Size", "wp-staging"))
122
- ->setDefault( isset( $settings->batchSize ) ? $settings->batchSize : 2 )
123
- );
124
-
125
- // CPU load priority
126
- $element = new Select(
127
- "wpstg_settings[cpuLoad]", [
128
- "high" => __("High (fast)", "wp-staging"),
129
- "medium" => __("Medium (average)", "wp-staging"),
130
- "low" => __("Low (slow)", "wp-staging")
 
131
  ]
132
- );
133
-
134
- $this->form["general"]->add(
135
- $element->setLabel(__("CPU Load Priority", "wp-staging"))
136
- ->setDefault( isset( $settings->cpuLoad ) ? $settings->cpuLoad : "low" )
137
- );
138
-
139
- // Delay Between Requests
140
- $element = new Numerical(
141
- "wpstg_settings[delayRequests]", [
142
- "class" => "medium-text",
143
- "step" => 1,
144
- "max" => 5,
145
- "min" => 0
 
 
 
146
  ]
147
- );
148
 
149
- $this->form["general"]->add(
150
- $element->setLabel(__("Delay Between Requests", "wp-staging"))
151
- ->setDefault( (isset( $settings->delayRequests )) ? $settings->delayRequests : 0 )
152
- );
153
 
154
 
155
- // Optimizer
156
- $element = new Check(
157
- "wpstg_settings[optimizer]", ['1' => ""]
158
- );
 
159
 
160
- $this->form["general"]->add(
161
- $element->setLabel(__("Optimizer", "wp-staging"))
162
- ->setDefault( (isset( $settings->optimizer )) ? $settings->optimizer : null )
163
- );
164
 
165
 
166
- // Disable admin authorization
167
- if (!defined('WPSTGPRO_VERSION')) {
168
- $element = new Check(
169
- "wpstg_settings[disableAdminLogin]", ['1' => '']
170
- );
 
171
 
172
- $this->form["general"]->add(
173
- $element->setLabel(__("Disable admin authorization", "wp-staging"))
174
  ->setDefault((isset($settings->disableAdminLogin)) ? $settings->disableAdminLogin : null)
175
- );
176
- }
177
- // Keep permalinks
178
- if (defined('WPSTGPRO_VERSION')) {
179
- $element = new Check(
180
- "wpstg_settings[keepPermalinks]", ['1' => '']
181
- );
182
-
183
- $this->form["general"]->add(
184
- $element->setLabel(__("Keep Permalinks", "wp-staging"))
 
185
  ->setDefault((isset($settings->keepPermalinks)) ? $settings->keepPermalinks : null)
186
- );
187
- }
188
-
189
-
190
- // Debug Mode
191
- $element = new Check(
192
- "wpstg_settings[debugMode]", ['1' => '']
193
- );
194
-
195
- $this->form["general"]->add(
196
- $element->setLabel(__("Debug Mode", "wp-staging"))
197
- ->setDefault( (isset( $settings->debugMode )) ? $settings->debugMode : null )
198
- );
199
-
200
- // Remove Data on Uninstall?
201
- $element = new Check(
202
- "wpstg_settings[unInstallOnDelete]", ['1' => '']
203
- );
204
-
205
- $this->form["general"]->add(
206
- $element->setLabel(__("Remove Data on Uninstall?", "wp-staging"))
207
- ->setDefault( (isset( $settings->unInstallOnDelete )) ? $settings->unInstallOnDelete : null )
208
- );
209
-
210
- // Check Directory Sizes
211
- $element = new Check(
212
- "wpstg_settings[checkDirectorySize]", ['1' => '']
213
- );
214
-
215
- $this->form["general"]->add(
216
- $element->setLabel(__("Check Directory Size", "wp-staging"))
217
- ->setDefault( (isset( $settings->checkDirectorySize )) ? $settings->checkDirectorySize : null )
218
- );
219
-
220
- // Get user roles
221
- if (defined('WPSTGPRO_VERSION')) {
222
- $element = new SelectMultiple('wpstg_settings[userRoles][]', $this->getUserRoles());
223
- $this->form["general"]->add(
224
- $element->setLabel(__("Access Permissions", "wp-staging"))
 
 
 
225
  ->setDefault((isset($settings->userRoles)) ? $settings->userRoles : 'administrator')
226
- );
227
 
228
- $usersWithStagingAccess = new Text('wpstg_settings[usersWithStagingAccess]', []);
229
- $this->form["general"]->add(
230
- $usersWithStagingAccess->setLabel(__("Users With Staging Access", "wp-staging"))
231
  ->setDefault(isset($settings->usersWithStagingAccess) ? $settings->usersWithStagingAccess : '')
232
- );
233
- }
234
  }
235
 
236
  /**
237
  * Get available user Roles
238
  * @return array
239
  */
240
- private function getUserRoles() {
241
- $userRoles = [];
242
- foreach ( get_editable_roles() as $key => $value ) {
243
- $userRoles[$key] = $key;
244
- }
245
- return array_merge( ['all' => __('Allow access from all visitors', 'wp-staging')], $userRoles );
246
- }
 
247
 
248
  /**
249
  * @param string $name
250
  * @return array|Form
251
  */
252
- public function get( $name = null ) {
253
- return ($name === null) ? $this->form : $this->form[$name];
254
- }
255
-
256
  }
14
  * Class Settings
15
  * @package WPStaging\Backend\Modules\Views\Forms
16
  */
17
+ class Settings
18
+ {
19
 
20
  /**
21
  * @var array
22
  */
23
+ private $form = [];
24
 
25
  /**
26
  * @var Tabs
27
  */
28
+ private $tabs;
29
 
30
  /**
31
  * Settings constructor.
32
  * @param Tabs $tabs
33
  */
34
+ public function __construct($tabs)
35
+ {
36
+ $this->tabs = $tabs;
37
+
38
+ foreach ($this->tabs->get() as $id => $name) {
39
+ if (!method_exists($this, $id)) {
40
+ continue;
41
+ }
42
+
43
+ $this->{$id}();
44
+ }
45
+ }
46
+
47
+ private function general()
48
+ {
49
+ $this->form["general"] = new Form();
50
+
51
+ $settings = json_decode(json_encode(get_option("wpstg_settings", [])));
52
+
53
+ // DB Copy Query Limit
54
+ $element = new Numerical(
55
+ "wpstg_settings[queryLimit]",
56
+ [
57
+ "class" => "medium-text",
58
+ "step" => 1,
59
+ "max" => 999999,
60
+ "min" => 0
61
  ]
62
+ );
63
+
64
+ $this->form["general"]->add(
65
+ $element->setLabel(__("DB Copy Query Limit", "wp-staging"))
66
+ ->setDefault(isset($settings->queryLimit) ? $settings->queryLimit : 10000)
67
+ );
68
+ // DB Search & Replace Query Limit
69
+ $element = new Numerical(
70
+ "wpstg_settings[querySRLimit]",
71
+ [
72
+ "class" => "medium-text",
73
+ "step" => 1,
74
+ "max" => 999999,
75
+ "min" => 0
76
  ]
77
+ );
78
+
79
+ $this->form["general"]->add(
80
+ $element->setLabel(__("DB Search & Replace Limit", "wp-staging"))
81
+ ->setDefault(isset($settings->querySRLimit) ? $settings->querySRLimit : 5000)
82
+ );
83
+
84
+ $options = ['1' => '1', '10' => '10', '50' => '50', '250' => '250', '500' => '500', '1000' => '1000'];
85
+ // DB Copy Query Limit
86
+ $element = new Select(
87
+ "wpstg_settings[fileLimit]",
88
+ $options,
89
+ [
90
+ "class" => "medium-text",
91
+ "step" => 1,
92
+ "max" => 999999,
93
+ "min" => 0
94
  ]
95
+ );
96
+
97
+ $defaultFileLimit = defined('WPSTG_DEV') && WPSTG_DEV ? 500 : 50;
98
 
99
+ $this->form["general"]->add(
100
+ $element->setLabel(__("File Copy Limit", "wp-staging"))->setDefault(isset($settings->fileLimit) ? $settings->fileLimit : $defaultFileLimit)
101
+ );
102
 
103
 
104
+ // File Copy Batch Size
105
+ $element = new Numerical(
106
+ "wpstg_settings[maxFileSize]",
107
+ [
108
+ "class" => "medium-text",
109
+ "step" => 1,
110
+ "max" => 999999,
111
+ "min" => 0
112
  ]
113
+ );
114
+
115
+ $this->form["general"]->add(
116
+ $element->setLabel(__("Maximum File Size (MB)", "wp-staging"))
117
+ ->setDefault(isset($settings->maxFileSize) ? $settings->maxFileSize : 8)
118
+ );
119
+
120
+ // File Copy Batch Size
121
+ $element = new Numerical(
122
+ "wpstg_settings[batchSize]",
123
+ [
124
+ "class" => "medium-text",
125
+ "step" => 1,
126
+ "max" => 999999,
127
+ "min" => 0
128
  ]
129
+ );
130
+
131
+ $this->form["general"]->add(
132
+ $element->setLabel(__("File Copy Batch Size", "wp-staging"))
133
+ ->setDefault(isset($settings->batchSize) ? $settings->batchSize : 2)
134
+ );
135
+
136
+ // CPU load priority
137
+ $element = new Select(
138
+ "wpstg_settings[cpuLoad]",
139
+ [
140
+ "high" => __("High (fast)", "wp-staging"),
141
+ "medium" => __("Medium (average)", "wp-staging"),
142
+ "low" => __("Low (slow)", "wp-staging")
143
  ]
144
+ );
145
+
146
+ $defaultCpuPriority = defined('WPSTG_DEV') && WPSTG_DEV ? 'high' : 'low';
147
+
148
+ $this->form["general"]->add(
149
+ $element->setLabel(__("CPU Load Priority", "wp-staging"))
150
+ ->setDefault(isset($settings->cpuLoad) ? $settings->cpuLoad : $defaultCpuPriority)
151
+ );
152
+
153
+ // Delay Between Requests
154
+ $element = new Numerical(
155
+ "wpstg_settings[delayRequests]",
156
+ [
157
+ "class" => "medium-text",
158
+ "step" => 1,
159
+ "max" => 5,
160
+ "min" => 0
161
  ]
162
+ );
163
 
164
+ $this->form["general"]->add(
165
+ $element->setLabel(__("Delay Between Requests", "wp-staging"))
166
+ ->setDefault((isset($settings->delayRequests)) ? $settings->delayRequests : 0)
167
+ );
168
 
169
 
170
+ // Optimizer
171
+ $element = new Check(
172
+ "wpstg_settings[optimizer]",
173
+ ['1' => ""]
174
+ );
175
 
176
+ $this->form["general"]->add(
177
+ $element->setLabel(__("Optimizer", "wp-staging"))
178
+ ->setDefault((isset($settings->optimizer)) ? $settings->optimizer : null)
179
+ );
180
 
181
 
182
+ // Disable admin authorization
183
+ if (!defined('WPSTGPRO_VERSION')) {
184
+ $element = new Check(
185
+ "wpstg_settings[disableAdminLogin]",
186
+ ['1' => '']
187
+ );
188
 
189
+ $this->form["general"]->add(
190
+ $element->setLabel(__("Disable admin authorization", "wp-staging"))
191
  ->setDefault((isset($settings->disableAdminLogin)) ? $settings->disableAdminLogin : null)
192
+ );
193
+ }
194
+ // Keep permalinks
195
+ if (defined('WPSTGPRO_VERSION')) {
196
+ $element = new Check(
197
+ "wpstg_settings[keepPermalinks]",
198
+ ['1' => '']
199
+ );
200
+
201
+ $this->form["general"]->add(
202
+ $element->setLabel(__("Keep Permalinks", "wp-staging"))
203
  ->setDefault((isset($settings->keepPermalinks)) ? $settings->keepPermalinks : null)
204
+ );
205
+ }
206
+
207
+
208
+ // Debug Mode
209
+ $element = new Check(
210
+ "wpstg_settings[debugMode]",
211
+ ['1' => '']
212
+ );
213
+
214
+ $this->form["general"]->add(
215
+ $element->setLabel(__("Debug Mode", "wp-staging"))
216
+ ->setDefault((isset($settings->debugMode)) ? $settings->debugMode : null)
217
+ );
218
+
219
+ // Remove Data on Uninstall?
220
+ $element = new Check(
221
+ "wpstg_settings[unInstallOnDelete]",
222
+ ['1' => '']
223
+ );
224
+
225
+ $this->form["general"]->add(
226
+ $element->setLabel(__("Remove Data on Uninstall?", "wp-staging"))
227
+ ->setDefault((isset($settings->unInstallOnDelete)) ? $settings->unInstallOnDelete : null)
228
+ );
229
+
230
+ // Check Directory Sizes
231
+ $element = new Check(
232
+ "wpstg_settings[checkDirectorySize]",
233
+ ['1' => '']
234
+ );
235
+
236
+ $this->form["general"]->add(
237
+ $element->setLabel(__("Check Directory Size", "wp-staging"))
238
+ ->setDefault((isset($settings->checkDirectorySize)) ? $settings->checkDirectorySize : null)
239
+ );
240
+
241
+ // Get user roles
242
+ if (defined('WPSTGPRO_VERSION')) {
243
+ $element = new SelectMultiple('wpstg_settings[userRoles][]', $this->getUserRoles());
244
+ $this->form["general"]->add(
245
+ $element->setLabel(__("Access Permissions", "wp-staging"))
246
  ->setDefault((isset($settings->userRoles)) ? $settings->userRoles : 'administrator')
247
+ );
248
 
249
+ $usersWithStagingAccess = new Text('wpstg_settings[usersWithStagingAccess]', []);
250
+ $this->form["general"]->add(
251
+ $usersWithStagingAccess->setLabel(__("Users With Staging Access", "wp-staging"))
252
  ->setDefault(isset($settings->usersWithStagingAccess) ? $settings->usersWithStagingAccess : '')
253
+ );
254
+ }
255
  }
256
 
257
  /**
258
  * Get available user Roles
259
  * @return array
260
  */
261
+ private function getUserRoles()
262
+ {
263
+ $userRoles = [];
264
+ foreach (get_editable_roles() as $key => $value) {
265
+ $userRoles[$key] = $key;
266
+ }
267
+ return array_merge(['all' => __('Allow access from all visitors', 'wp-staging')], $userRoles);
268
+ }
269
 
270
  /**
271
  * @param string $name
272
  * @return array|Form
273
  */
274
+ public function get($name = null)
275
+ {
276
+ return ($name === null) ? $this->form : $this->form[$name];
277
+ }
278
  }
Backend/Modules/Views/Tabs/Tabs.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Backend\Modules\Views\Tabs;
3
 
4
  /**
@@ -19,8 +20,7 @@ class Tabs
19
  */
20
  public function __construct($tabs)
21
  {
22
- if (is_array($tabs))
23
- {
24
  self::$tabs = $tabs;
25
  }
26
  }
@@ -43,4 +43,4 @@ class Tabs
43
  {
44
  return self::$tabs;
45
  }
46
- }
1
  <?php
2
+
3
  namespace WPStaging\Backend\Modules\Views\Tabs;
4
 
5
  /**
20
  */
21
  public function __construct($tabs)
22
  {
23
+ if (is_array($tabs)) {
 
24
  self::$tabs = $tabs;
25
  }
26
  }
43
  {
44
  return self::$tabs;
45
  }
46
+ }
Backend/Notices/BooleanNotice.php CHANGED
@@ -13,7 +13,7 @@ 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();
@@ -23,13 +23,13 @@ abstract class BooleanNotice
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
  {
@@ -38,11 +38,11 @@ abstract class BooleanNotice
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
- }
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();
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
  {
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 DELETED
@@ -1,23 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Notices;
4
-
5
- /**
6
- * Class DisabledCacheNotice
7
- *
8
- * This class is used to show notice about why cache is disabled on the staging site
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/DisabledItemsNotice.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Notices;
4
+
5
+ use WPStaging\Framework\SiteInfo;
6
+
7
+ /**
8
+ * Class DisabledItemsNotice
9
+ *
10
+ * This class is used to show notice about what WP Staging has disabled on the staging site
11
+ *
12
+ * @package WPStaging\Backend\Notices;
13
+ */
14
+ class DisabledItemsNotice extends BooleanNotice
15
+ {
16
+ /**
17
+ * The option name to store the visibility of disabled cache notice
18
+ */
19
+ const OPTION_NAME = 'wpstg_disabled_notice';
20
+
21
+ public function getOptionName()
22
+ {
23
+ return self::OPTION_NAME;
24
+ }
25
+
26
+ /**
27
+ * Show this notice only on staging site
28
+ *
29
+ * @return bool
30
+ */
31
+ public function isEnabled()
32
+ {
33
+ // TODO: inject using DI
34
+ // Early bail if not staging site
35
+ if (!(new SiteInfo())->isStaging()) {
36
+ return false;
37
+ }
38
+
39
+ return parent::isEnabled();
40
+ }
41
+ }
Backend/Notices/Notices.php CHANGED
@@ -6,19 +6,20 @@ namespace WPStaging\Backend\Notices;
6
  * Admin Notices | Warnings | Messages
7
  */
8
 
9
- // No Direct Access
10
- if (!defined("WPINC")) {
11
- die;
12
- }
13
-
14
  use WPStaging\Backend\Pro\Notices\Notices as ProNotices;
15
  use WPStaging\Core\Utils\Cache;
16
  use WPStaging\Core\Utils\Logger;
17
  use WPStaging\Core\WPStaging;
 
 
 
 
18
 
19
  /**
20
  * Class Notices
21
  * @package WPStaging\Backend\Notices
 
 
22
  */
23
  class Notices
24
  {
@@ -28,19 +29,19 @@ class Notices
28
  private $path;
29
 
30
  /**
31
- * @var string
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;
43
- $this->url = $url;
44
  }
45
 
46
  /**
@@ -138,15 +139,25 @@ class Notices
138
  }
139
  }
140
 
 
 
 
141
  // Show all pro version notices
142
  if ($this->isPro()) {
143
  $proNotices = new ProNotices($this);
 
 
 
144
  $proNotices->getNotices();
145
  }
146
 
147
- // Show notice about cache being disabled in the staging site. (Show only on staging site)
148
- if ((new DisabledCacheNotice())->isEnabled()) {
149
- require_once "{$viewsNoticesPath}disabled-cache.php";
 
 
 
 
150
  }
151
 
152
  // Display notices below in wp staging admin pages only
@@ -176,7 +187,7 @@ class Notices
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
  }
182
 
@@ -185,6 +196,11 @@ class Notices
185
  require_once "{$viewsNoticesPath}wrong-scheme.php";
186
  }
187
 
 
 
 
 
 
188
  $this->showDirectoryListingWarningNotice($viewsNoticesPath);
189
  }
190
 
@@ -221,7 +237,7 @@ class Notices
221
  * Check if the url scheme of siteurl and home is identical
222
  * @return boolean
223
  */
224
- private function isDifferentScheme()
225
  {
226
  $siteurlScheme = parse_url(get_option('siteurl'), PHP_URL_SCHEME);
227
  $homeScheme = parse_url(get_option('home'), PHP_URL_SCHEME);
@@ -229,6 +245,38 @@ class Notices
229
  return !($siteurlScheme === $homeScheme);
230
  }
231
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  /**
233
  * Get the path of plugin
234
  * @return string
@@ -239,12 +287,11 @@ class Notices
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
  }
6
  * Admin Notices | Warnings | Messages
7
  */
8
 
 
 
 
 
 
9
  use WPStaging\Backend\Pro\Notices\Notices as ProNotices;
10
  use WPStaging\Core\Utils\Cache;
11
  use WPStaging\Core\Utils\Logger;
12
  use WPStaging\Core\WPStaging;
13
+ use WPStaging\Framework\Assets\Assets;
14
+ use WPStaging\Framework\CloningProcess\ExcludedPlugins;
15
+ use WPStaging\Framework\Staging\CloneOptions;
16
+ use WPStaging\Framework\Staging\FirstRun;
17
 
18
  /**
19
  * Class Notices
20
  * @package WPStaging\Backend\Notices
21
+ *
22
+ * @todo Have NoticeServiceProvider?
23
  */
24
  class Notices
25
  {
29
  private $path;
30
 
31
  /**
32
+ * @var Assets
33
  */
34
+ private $assets;
35
 
36
  /**
37
  * @var string The key that holds directory listing errors in the container.
38
  */
39
  public static $directoryListingErrors = 'directoryListingErrors';
40
 
41
+ public function __construct($path, $assets)
42
  {
43
  $this->path = $path;
44
+ $this->assets = $assets;
45
  }
46
 
47
  /**
139
  }
140
  }
141
 
142
+ // Never show disable mail message if free version
143
+ $outgoingMailsDisabled = false;
144
+
145
  // Show all pro version notices
146
  if ($this->isPro()) {
147
  $proNotices = new ProNotices($this);
148
+ // TODO: inject CloneOptions using DI
149
+ // Check mails disabled against both the old and new way of emails disabled option
150
+ $outgoingMailsDisabled = (bool)(new CloneOptions())->get(FirstRun::MAILS_DISABLED_KEY) || ((bool)get_option(FirstRun::MAILS_DISABLED_KEY, false));
151
  $proNotices->getNotices();
152
  }
153
 
154
+ // Show notice about what disabled in the staging site. (Show only on staging site)
155
+ if ((new DisabledItemsNotice())->isEnabled()) {
156
+ // TODO: inject ExcludedPlugins using DI
157
+ $excludedPlugins = (array)(new ExcludedPlugins())->getExcludedPlugins();
158
+ // use require here instead of require_once otherwise unit tests will always fail,
159
+ // as this notice is tested multiple times.
160
+ require "{$viewsNoticesPath}disabled-items-notice.php";
161
  }
162
 
163
  // Display notices below in wp staging admin pages only
187
  }
188
 
189
  // WPSTAGING is not tested with current WordPress version
190
+ if (!$this->isPro() && version_compare(WPStaging::getInstance()->get('WPSTG_COMPATIBLE'), get_bloginfo("version"), "<")) {
191
  require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
192
  }
193
 
196
  require_once "{$viewsNoticesPath}wrong-scheme.php";
197
  }
198
 
199
+ // Outdated version of WP Staging Hooks
200
+ if ($this->isUsingOutdatedWpstgHooksPlugin()) {
201
+ require_once "{$viewsNoticesPath}outdated-wp-staging-hooks.php";
202
+ }
203
+
204
  $this->showDirectoryListingWarningNotice($viewsNoticesPath);
205
  }
206
 
237
  * Check if the url scheme of siteurl and home is identical
238
  * @return boolean
239
  */
240
+ private function isDifferentScheme()
241
  {
242
  $siteurlScheme = parse_url(get_option('siteurl'), PHP_URL_SCHEME);
243
  $homeScheme = parse_url(get_option('home'), PHP_URL_SCHEME);
245
  return !($siteurlScheme === $homeScheme);
246
  }
247
 
248
+ /**
249
+ * Check if the user is using an outdated version of WP Staging Hooks plugin
250
+ * @return boolean
251
+ */
252
+ private function isUsingOutdatedWpstgHooksPlugin()
253
+ {
254
+ // Minimum version to check
255
+ $versionToCheck = '0.0.2';
256
+
257
+ // Path to WP Staging Hooks plugins in a directory
258
+ $wpstgHooksPath = 'wp-staging-hooks/wp-staging-hooks.php';
259
+
260
+ // Plugin doesn't exist, so no need to show notice
261
+ if (file_exists(WP_PLUGIN_DIR . '/' . $wpstgHooksPath)) {
262
+ $wpstgHooksData = get_plugin_data(WP_PLUGIN_DIR . '/' . $wpstgHooksPath);
263
+ // Only show notice if current version is below required min version.
264
+ return version_compare($wpstgHooksData['Version'], $versionToCheck, '>=') ? false : true;
265
+ }
266
+
267
+ // Path to WP Staging Hooks plugins directly in plugins dir
268
+ $wpstgHooksPath = 'wp-staging-hooks.php';
269
+
270
+ // Plugin doesn't exist, so no need to show notice
271
+ if (file_exists(WP_PLUGIN_DIR . '/' . $wpstgHooksPath)) {
272
+ $wpstgHooksData = get_plugin_data(WP_PLUGIN_DIR . '/' . $wpstgHooksPath);
273
+ // Only show notice if current version is below required min version.
274
+ return version_compare($wpstgHooksData['Version'], $versionToCheck, '>=') ? false : true;
275
+ }
276
+
277
+ return false;
278
+ }
279
+
280
  /**
281
  * Get the path of plugin
282
  * @return string
287
  }
288
 
289
  /**
290
+ * Get the assets helper
291
+ * @return Assets
292
  */
293
+ public function getAssets()
294
  {
295
+ return $this->assets;
296
  }
 
297
  }
Backend/Optimizer/Optimizer.php CHANGED
@@ -1,72 +1,77 @@
1
  <?php
 
2
  namespace WPStaging\Backend\Optimizer;
3
 
4
  // No Direct Access
5
  use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
- if( !defined( "WPINC" ) ) {
8
- die;
9
  }
10
 
11
  /**
12
  * Optimizer
13
  */
14
 
15
- class Optimizer {
 
16
 
17
- private $mudir;
18
- private $source;
19
- private $dest;
20
 
21
  /**
22
  * Optimizer constructor.
23
  *
24
  * If changes are made to this, also check uninstall.php!
25
  */
26
- public function __construct() {
27
- $this->mudir = ( defined( 'WPMU_PLUGIN_DIR' ) && defined( 'WPMU_PLUGIN_URL' ) ) ? WPMU_PLUGIN_DIR : trailingslashit( WP_CONTENT_DIR ) . 'mu-plugins';
28
-
29
- $this->source = trailingslashit( WPSTG_PLUGIN_DIR ) . 'Backend/Optimizer/wp-staging-optimizer.php';
30
- $this->dest = trailingslashit( $this->mudir ) . 'wp-staging-optimizer.php';
31
- }
32
-
33
- public function installOptimizer() {
34
- if (file_exists( $this->dest ) && $this->mustUpdateOptimizer() === false){
35
- return false;
36
- }
37
-
38
- if( (new Filesystem)->mkdir( $this->mudir ) ) {
39
- $this->copy();
40
- }
41
- return false;
42
- }
43
-
44
- private function copy() {
45
- if( !copy( $this->source, $this->dest ) ) {
46
- return false;
47
- }
48
- }
 
 
 
49
 
50
  /**
51
  * Check if the Optimizer must use plugin must be updated
52
  * @return boolean
53
  */
54
- private function mustUpdateOptimizer(){
55
- $isVersionNumber = defined('WPSTG_OPTIMIZER_VERSION') ? WPSTG_OPTIMIZER_VERSION : false;
56
-
57
- $update = false;
58
-
59
- if ($isVersionNumber === false){
60
- return true;
61
- }
62
-
63
- $mustVersionNumber = defined('WPSTG_OPTIMIZER_MUVERSION') ? WPSTG_OPTIMIZER_MUVERSION : false;
64
-
65
- if ($mustVersionNumber){
66
- $update = version_compare($isVersionNumber, $mustVersionNumber, '!=');
67
- }
68
-
69
- return $update;
70
- }
71
 
 
 
72
  }
1
  <?php
2
+
3
  namespace WPStaging\Backend\Optimizer;
4
 
5
  // No Direct Access
6
  use WPStaging\Framework\Filesystem\Filesystem;
7
 
8
+ if (!defined("WPINC")) {
9
+ die;
10
  }
11
 
12
  /**
13
  * Optimizer
14
  */
15
 
16
+ class Optimizer
17
+ {
18
 
19
+ private $mudir;
20
+ private $source;
21
+ private $dest;
22
 
23
  /**
24
  * Optimizer constructor.
25
  *
26
  * If changes are made to this, also check uninstall.php!
27
  */
28
+ public function __construct()
29
+ {
30
+ $this->mudir = ( defined('WPMU_PLUGIN_DIR') && defined('WPMU_PLUGIN_URL') ) ? WPMU_PLUGIN_DIR : trailingslashit(WP_CONTENT_DIR) . 'mu-plugins';
31
+
32
+ $this->source = trailingslashit(WPSTG_PLUGIN_DIR) . 'Backend/Optimizer/wp-staging-optimizer.php';
33
+ $this->dest = trailingslashit($this->mudir) . 'wp-staging-optimizer.php';
34
+ }
35
+
36
+ public function installOptimizer()
37
+ {
38
+ if (file_exists($this->dest) && $this->mustUpdateOptimizer() === false) {
39
+ return false;
40
+ }
41
+
42
+ if ((new Filesystem())->mkdir($this->mudir)) {
43
+ $this->copy();
44
+ }
45
+ return false;
46
+ }
47
+
48
+ private function copy()
49
+ {
50
+ if (!copy($this->source, $this->dest)) {
51
+ return false;
52
+ }
53
+ }
54
 
55
  /**
56
  * Check if the Optimizer must use plugin must be updated
57
  * @return boolean
58
  */
59
+ private function mustUpdateOptimizer()
60
+ {
61
+ $isVersionNumber = defined('WPSTG_OPTIMIZER_VERSION') ? WPSTG_OPTIMIZER_VERSION : false;
62
+
63
+ $update = false;
64
+
65
+ if ($isVersionNumber === false) {
66
+ return true;
67
+ }
68
+
69
+ $mustVersionNumber = defined('WPSTG_OPTIMIZER_MUVERSION') ? WPSTG_OPTIMIZER_MUVERSION : false;
70
+
71
+ if ($mustVersionNumber) {
72
+ $update = version_compare($isVersionNumber, $mustVersionNumber, '!=');
73
+ }
 
 
74
 
75
+ return $update;
76
+ }
77
  }
Backend/Optimizer/blank-theme/functions.php CHANGED
@@ -1,7 +1,6 @@
1
  <?php
2
 
3
- /*
4
  * We need an empty functions.php to simulate a valid theme
5
- *
6
  */
7
-
1
  <?php
2
 
3
+ /*
4
  * We need an empty functions.php to simulate a valid theme
5
+ *
6
  */
 
Backend/Optimizer/wp-staging-optimizer.php CHANGED
@@ -9,13 +9,13 @@
9
  * Do not use any of these methods in WP STAGING code base as this mu-plugin can be missing!
10
  *
11
  * Author: René Hermenau
12
- * Version: 1.4
13
  * Author URI: https://wp-staging.com
14
  */
15
 
16
  // Version number of this mu-plugin. Important for automatic updates
17
  if (!defined('WPSTG_OPTIMIZER_VERSION')) {
18
- define('WPSTG_OPTIMIZER_VERSION', 1.4);
19
  }
20
 
21
  /** @return string */
@@ -25,7 +25,7 @@ function wpstgGetPluginsDir()
25
  $pluginsDir = '';
26
  if (defined('WP_PLUGIN_DIR')) {
27
  $pluginsDir = trailingslashit(WP_PLUGIN_DIR);
28
- } else if (defined('WP_CONTENT_DIR')) {
29
  $pluginsDir = trailingslashit(WP_CONTENT_DIR) . 'plugins/';
30
  }
31
  return $pluginsDir;
@@ -40,7 +40,7 @@ function wpstgGetPluginsDir()
40
  function wpstgIsEnabledOptimizer()
41
  {
42
 
43
- $status = ( object )get_option('wpstg_settings');
44
 
45
  if ($status && isset($status->optimizer) && $status->optimizer == 1) {
46
  return true;
@@ -91,7 +91,6 @@ function wpstgExcludePlugins($plugins)
91
  }
92
 
93
  foreach ($plugins as $key => $plugin) {
94
-
95
  // Default filter. Must be at the beginning or wp staging plugin will be filtered and killed
96
  if (strpos($plugin, 'wp-staging') !== false || wpstgIsExcludedPlugin($plugin)) {
97
  continue;
@@ -123,7 +122,6 @@ function wpstgExcludeSitePlugins($plugins)
123
  }
124
 
125
  foreach ($plugins as $key => $plugin) {
126
-
127
  // Default filter. Must be at the beginning or wp staging plugin will be filtered and killed
128
  if (strpos($plugin, 'wp-staging') !== false || wpstgIsExcludedPlugin($plugin)) {
129
  continue;
@@ -163,7 +161,6 @@ function wpstgDisableTheme($dir)
163
  } else {
164
  return '';
165
  }
166
- return $themeDir;
167
  }
168
 
169
  return $dir;
@@ -184,13 +181,13 @@ function wpstgIsOptimizerRequest()
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
 
@@ -253,10 +250,40 @@ function wpstgIsStaging()
253
  return false;
254
  }
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  /**
257
  * Disable all outgoing e-mails on Staging site
 
258
  */
259
- if (((bool)get_option("wpstg_emails_disabled") === true) && wpstgIsStaging()) {
260
  if (!function_exists('wp_mail')) {
261
  function wp_mail($to, $subject, $message, $headers = '', $attachments = [])
262
  {
@@ -293,4 +320,3 @@ LOG_ENTRY;
293
  }
294
  }
295
  }
296
-
9
  * Do not use any of these methods in WP STAGING code base as this mu-plugin can be missing!
10
  *
11
  * Author: René Hermenau
12
+ * Version: 1.4.1
13
  * Author URI: https://wp-staging.com
14
  */
15
 
16
  // Version number of this mu-plugin. Important for automatic updates
17
  if (!defined('WPSTG_OPTIMIZER_VERSION')) {
18
+ define('WPSTG_OPTIMIZER_VERSION', '1.4.1');
19
  }
20
 
21
  /** @return string */
25
  $pluginsDir = '';
26
  if (defined('WP_PLUGIN_DIR')) {
27
  $pluginsDir = trailingslashit(WP_PLUGIN_DIR);
28
+ } elseif (defined('WP_CONTENT_DIR')) {
29
  $pluginsDir = trailingslashit(WP_CONTENT_DIR) . 'plugins/';
30
  }
31
  return $pluginsDir;
40
  function wpstgIsEnabledOptimizer()
41
  {
42
 
43
+ $status = (object)get_option('wpstg_settings');
44
 
45
  if ($status && isset($status->optimizer) && $status->optimizer == 1) {
46
  return true;
91
  }
92
 
93
  foreach ($plugins as $key => $plugin) {
 
94
  // Default filter. Must be at the beginning or wp staging plugin will be filtered and killed
95
  if (strpos($plugin, 'wp-staging') !== false || wpstgIsExcludedPlugin($plugin)) {
96
  continue;
122
  }
123
 
124
  foreach ($plugins as $key => $plugin) {
 
125
  // Default filter. Must be at the beginning or wp staging plugin will be filtered and killed
126
  if (strpos($plugin, 'wp-staging') !== false || wpstgIsExcludedPlugin($plugin)) {
127
  continue;
161
  } else {
162
  return '';
163
  }
 
164
  }
165
 
166
  return $dir;
181
  return false;
182
  }
183
 
184
+ if (
185
+ defined('DOING_AJAX') &&
186
  DOING_AJAX &&
187
  isset($_POST['action']) &&
188
  strpos($_POST['action'], 'wpstg_send_report') === false &&
189
  strpos($_POST['action'], 'wpstg') === 0
190
  ) {
 
191
  return true;
192
  }
193
 
250
  return false;
251
  }
252
 
253
+ /**
254
+ * Get the value of the given option in clone settings,
255
+ * If no option given return all clone settings
256
+ *
257
+ * @param string $option
258
+ * @return mixed
259
+ */
260
+ function wpstgGetCloneSettings($option = null)
261
+ {
262
+ $settings = get_option('wpstg_clone_settings', null);
263
+
264
+ // Return settings if no options given
265
+ if ($option === null) {
266
+ return $settings;
267
+ }
268
+
269
+ // Early Bail: if settings is null or if settings isn't object
270
+ if ($settings === null || !is_object($settings)) {
271
+ return null;
272
+ }
273
+
274
+ // Early bail if given option not exists
275
+ if (!property_exists($settings, $option)) {
276
+ return null;
277
+ }
278
+
279
+ return $settings->{$option};
280
+ }
281
+
282
  /**
283
  * Disable all outgoing e-mails on Staging site
284
+ * Will check against both the old and new logic of storing emails disabled option
285
  */
286
+ if (wpstgIsStaging() && (((bool)get_option("wpstg_emails_disabled") === true) || ((bool)wpstgGetCloneSettings('wpstg_emails_disabled')))) {
287
  if (!function_exists('wp_mail')) {
288
  function wp_mail($to, $subject, $message, $headers = '', $attachments = [])
289
  {
320
  }
321
  }
322
  }
 
Backend/Pluginmeta/Meta.php CHANGED
@@ -7,36 +7,38 @@ namespace WPStaging\Backend\Pluginmeta;
7
  */
8
 
9
  // No Direct Access
10
- if( !defined( "WPINC" ) ) {
11
  die;
12
  }
13
 
14
  use WPStaging\Core\WPStaging;
15
 
16
 
17
- class Meta {
18
-
 
19
  public $get = '';
20
-
21
- public function __construct() {
 
22
  $this->get = WPSTG_PLUGIN_DIR . wpstg_base('YXBwcy9CYWNrZW5kL1Byby9MaWNlbnNpbmcvTGljZW5zaW5nLnBocA==');
23
  $this->save();
24
  }
25
-
26
- public function get(){
 
27
  $hash = new \WPStaging\Core\Utils\Hash($this->get, true);
28
  $get = $hash->getHash();
29
  return $get;
30
  }
31
-
32
- public function save(){
 
33
  $var = '';
34
- if(($var = $this->get()) !== '97226140ae745eb0ef4f780c2d40448f'){
35
  update_option($var, true);
36
  }
37
-
38
  return;
39
-
40
  }
41
-
42
  }
7
  */
8
 
9
  // No Direct Access
10
+ if (!defined("WPINC")) {
11
  die;
12
  }
13
 
14
  use WPStaging\Core\WPStaging;
15
 
16
 
17
+ class Meta
18
+ {
19
+
20
  public $get = '';
21
+
22
+ public function __construct()
23
+ {
24
  $this->get = WPSTG_PLUGIN_DIR . wpstg_base('YXBwcy9CYWNrZW5kL1Byby9MaWNlbnNpbmcvTGljZW5zaW5nLnBocA==');
25
  $this->save();
26
  }
27
+
28
+ public function get()
29
+ {
30
  $hash = new \WPStaging\Core\Utils\Hash($this->get, true);
31
  $get = $hash->getHash();
32
  return $get;
33
  }
34
+
35
+ public function save()
36
+ {
37
  $var = '';
38
+ if (($var = $this->get()) !== '97226140ae745eb0ef4f780c2d40448f') {
39
  update_option($var, true);
40
  }
41
+
42
  return;
 
43
  }
 
44
  }
Backend/Pluginmeta/Pluginmeta.php CHANGED
@@ -7,7 +7,7 @@ namespace WPStaging\Backend\Pluginmeta;
7
  */
8
 
9
  // No Direct Access
10
- if( !defined( "WPINC" ) ) {
11
  die;
12
  }
13
 
@@ -18,18 +18,19 @@ class Pluginmeta
18
  {
19
  // Link to Upgrade WP Staging
20
  const UPGRADE_LINK = "https://wp-staging.com/premium-upgrade";
21
-
22
- public function __construct() {
 
23
  $this->defineHooks();
24
  }
25
 
26
  /**
27
  * Define Hooks
28
  */
29
- public function defineHooks() {
30
- add_filter( 'plugin_row_meta', [$this, 'rowMeta'], 10, 2 );
31
- add_filter( 'plugin_action_links', [$this,'actionLinks'], 10, 2 );
32
-
33
  }
34
 
35
  /**
@@ -41,7 +42,7 @@ class Pluginmeta
41
  * @param string $file plugin file path and name being processed
42
  * @return array $links
43
  */
44
- public function actionLinks($links, $file)
45
  {
46
  $upgrade_link = '<a style="color: #27ae60;" target="_blank" href="' . self::UPGRADE_LINK . '">' . esc_html__('Premium Upgrade', 'wp-staging') . '</a>';
47
  $freePlugins = [
@@ -53,8 +54,8 @@ class Pluginmeta
53
  if (in_array($file, $freePlugins)) {
54
  array_unshift($links, $upgrade_link);
55
  }
56
-
57
- $settings_link = '<a href="' . admin_url( 'admin.php?page=wpstg-settings' ) . '">' . esc_html__( 'Settings', 'wp-staging' ) . '</a>';
58
  // show on both free and pro version
59
  // as WPSTG_PLUGIN_FILE is common for both free and pro version
60
  // defined during requirement bootstrapping
@@ -76,16 +77,16 @@ class Pluginmeta
76
  * @param string $file plugin file path and name being processed
77
  * @return array $input
78
  */
79
- public function rowMeta( $input, $file ) {
80
- if( $file != 'wp-staging/wp-staging.php' && $file != 'wp-staging-pro/wp-staging-pro.php'){
 
81
  return $input;
82
  }
83
 
84
  $links = [
85
- '<a href="' . admin_url( 'admin.php?page=wpstg_clone' ) . '">' . esc_html__( 'Start Now', 'wp-staging' ) . '</a>',
86
  ];
87
- $input = array_merge( $input, $links );
88
  return $input;
89
  }
90
-
91
  }
7
  */
8
 
9
  // No Direct Access
10
+ if (!defined("WPINC")) {
11
  die;
12
  }
13
 
18
  {
19
  // Link to Upgrade WP Staging
20
  const UPGRADE_LINK = "https://wp-staging.com/premium-upgrade";
21
+
22
+ public function __construct()
23
+ {
24
  $this->defineHooks();
25
  }
26
 
27
  /**
28
  * Define Hooks
29
  */
30
+ public function defineHooks()
31
+ {
32
+ add_filter('plugin_row_meta', [$this, 'rowMeta'], 10, 2);
33
+ add_filter('plugin_action_links', [$this,'actionLinks'], 10, 2);
34
  }
35
 
36
  /**
42
  * @param string $file plugin file path and name being processed
43
  * @return array $links
44
  */
45
+ public function actionLinks($links, $file)
46
  {
47
  $upgrade_link = '<a style="color: #27ae60;" target="_blank" href="' . self::UPGRADE_LINK . '">' . esc_html__('Premium Upgrade', 'wp-staging') . '</a>';
48
  $freePlugins = [
54
  if (in_array($file, $freePlugins)) {
55
  array_unshift($links, $upgrade_link);
56
  }
57
+
58
+ $settings_link = '<a href="' . admin_url('admin.php?page=wpstg-settings') . '">' . esc_html__('Settings', 'wp-staging') . '</a>';
59
  // show on both free and pro version
60
  // as WPSTG_PLUGIN_FILE is common for both free and pro version
61
  // defined during requirement bootstrapping
77
  * @param string $file plugin file path and name being processed
78
  * @return array $input
79
  */
80
+ public function rowMeta($input, $file)
81
+ {
82
+ if ($file != 'wp-staging/wp-staging.php' && $file != 'wp-staging-pro/wp-staging-pro.php') {
83
  return $input;
84
  }
85
 
86
  $links = [
87
+ '<a href="' . admin_url('admin.php?page=wpstg_clone') . '">' . esc_html__('Start Now', 'wp-staging') . '</a>',
88
  ];
89
+ $input = array_merge($input, $links);
90
  return $input;
91
  }
 
92
  }
Backend/helpers/wp-config.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * The base configuration for WordPress
4
  *
@@ -29,15 +30,15 @@
29
  *
30
  * @since 2.6.0
31
  */
32
- define( 'AUTH_KEY', 'cwC1]Y4Qz<-jS?V W0U*CbW~$yY9U@=-t5X{][-S0GF~LqY yt[ChoYm@?`}iJzm' );
33
- define( 'SECURE_AUTH_KEY', 'dmZ5$.d:gCbUxX)FZ+[h-t81O>Yd5W!x<:D[q;6{A?;Q0F>fvKcjaM V0V?-XD(t' );
34
- define( 'LOGGED_IN_KEY', '{B%(DDM,))zFtDD8gLk;N^EK`iiG<V`XNZ/k~d]ne^t/MLN85o5ILrMWC!.:cq.X' );
35
- define( 'NONCE_KEY', 'tvt!~WL>)x{ ``SK6ZO^/R1lwZPerR?&(W>]h/da(Z^M$2?)ZDVsICxQV3?/h6)U' );
36
- define( 'AUTH_SALT', ';%e$?CJm$s-N!a;(B;NM/>_~gDuPa(VM1t:nUvQ+LZw;e]1)_`-qwCZe,-@^{Xd%' );
37
- define( 'SECURE_AUTH_SALT', '? nh*~x!7Jm^4E4w(y(qmaPK:$l5aB6!n6L}$IN+cXZSsE?<2~FfK|qN=s:=P+(c' );
38
- define( 'LOGGED_IN_SALT', '=Ty=Q):}E/[pW3y4IDN@Bas/&-MInTxFXziE)9H9^rnl g7TUj-7OP*UX2Oyz=Y$' );
39
- define( 'NONCE_SALT', 'HATY?A^EQ#F;oN8!W-oe5P%)aFaeU,E;rLFmRm&u-<g6tL9k(pyh77_,Kc _q2BY' );
40
- define( 'WP_CACHE_KEY_SALT', '5Y@>B@S8O{a w%ASH BX!;wu/RBk.HT[~R{csF.r)5f0q/YTy%$8lwV4o0eygz};' );
41
 
42
  /**
43
  * WordPress Database Table prefix.
@@ -50,8 +51,9 @@ $table_prefix = 'wp_';
50
  /* That's all, stop editing! Happy blogging. */
51
 
52
  /** Absolute path to the WordPress directory. */
53
- if ( ! defined( 'ABSPATH' ) )
54
- define( 'ABSPATH', dirname( __FILE__ ) . '/' );
 
55
 
56
  /** Sets up WordPress vars and included files. */
57
- require_once ABSPATH . 'wp-settings.php';
1
  <?php
2
+
3
  /**
4
  * The base configuration for WordPress
5
  *
30
  *
31
  * @since 2.6.0
32
  */
33
+ define('AUTH_KEY', 'cwC1]Y4Qz<-jS?V W0U*CbW~$yY9U@=-t5X{][-S0GF~LqY yt[ChoYm@?`}iJzm');
34
+ define('SECURE_AUTH_KEY', 'dmZ5$.d:gCbUxX)FZ+[h-t81O>Yd5W!x<:D[q;6{A?;Q0F>fvKcjaM V0V?-XD(t');
35
+ define('LOGGED_IN_KEY', '{B%(DDM,))zFtDD8gLk;N^EK`iiG<V`XNZ/k~d]ne^t/MLN85o5ILrMWC!.:cq.X');
36
+ define('NONCE_KEY', 'tvt!~WL>)x{ ``SK6ZO^/R1lwZPerR?&(W>]h/da(Z^M$2?)ZDVsICxQV3?/h6)U');
37
+ define('AUTH_SALT', ';%e$?CJm$s-N!a;(B;NM/>_~gDuPa(VM1t:nUvQ+LZw;e]1)_`-qwCZe,-@^{Xd%');
38
+ define('SECURE_AUTH_SALT', '? nh*~x!7Jm^4E4w(y(qmaPK:$l5aB6!n6L}$IN+cXZSsE?<2~FfK|qN=s:=P+(c');
39
+ define('LOGGED_IN_SALT', '=Ty=Q):}E/[pW3y4IDN@Bas/&-MInTxFXziE)9H9^rnl g7TUj-7OP*UX2Oyz=Y$');
40
+ define('NONCE_SALT', 'HATY?A^EQ#F;oN8!W-oe5P%)aFaeU,E;rLFmRm&u-<g6tL9k(pyh77_,Kc _q2BY');
41
+ define('WP_CACHE_KEY_SALT', '5Y@>B@S8O{a w%ASH BX!;wu/RBk.HT[~R{csF.r)5f0q/YTy%$8lwV4o0eygz};');
42
 
43
  /**
44
  * WordPress Database Table prefix.
51
  /* That's all, stop editing! Happy blogging. */
52
 
53
  /** Absolute path to the WordPress directory. */
54
+ if (! defined('ABSPATH')) {
55
+ define('ABSPATH', dirname(__FILE__) . '/');
56
+ }
57
 
58
  /** Sets up WordPress vars and included files. */
59
+ require_once ABSPATH . 'wp-settings.php';
Backend/public/js/wpstg-admin-beta.js DELETED
@@ -1,22 +0,0 @@
1
- jQuery(document).ready(function($){
2
- $(".wpstg_hide_beta").click(function(e) {
3
- e.preventDefault();
4
-
5
- window.WPStaging.ajax(
6
- {action: "wpstg_hide_beta"},
7
- function(response)
8
- {
9
- if (true === response)
10
- {
11
- $(".wpstg_beta_notice").slideUp("slow");
12
- return true;
13
- }
14
-
15
- alert(
16
- "Unexpected message received. This might mean the data was not saved " +
17
- "and you might see this message again"
18
- );
19
- }
20
- );
21
- })
22
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/public/js/wpstg-admin-cache-notice.js DELETED
@@ -1,32 +0,0 @@
1
- jQuery(document).ready(function ($) {
2
- jQuery(document).on('click', ".wpstg_hide_cache_notice", function (e) {
3
- e.preventDefault();
4
-
5
- jQuery.ajax({
6
- url: ajaxurl,
7
- type: "POST",
8
- data: { action: "wpstg_hide_cache_notice" },
9
- error: function(xhr, textStatus, errorThrown) {
10
- console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
11
- console.log(textStatus);
12
-
13
- alert(
14
- "Unknown error. Please get in contact with us to solve it support@wp-staging.com"
15
- );
16
- },
17
- success: function(data) {
18
- jQuery(".wpstg-cache-notice").slideUp("fast");
19
- return true;
20
- },
21
- statusCode: {
22
- 404: function() {
23
- alert("Something went wrong; can't find ajax request URL! Please get in contact with us to solve it support@wp-staging.com");
24
- },
25
- 500: function() {
26
- alert("Something went wrong; internal server error while processing the request! Please get in contact with us to solve it support@wp-staging.com");
27
- }
28
- }
29
- });
30
-
31
- })
32
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/public/js/wpstg-admin-plugins.js DELETED
@@ -1,80 +0,0 @@
1
- var strict;
2
-
3
- jQuery(document).ready(function ($) {
4
-
5
- /**
6
- * DEACTIVATION FEEDBACK FORM
7
- */
8
- // show overlay when clicked on "deactivate"
9
- wpstg_deactivate_link = $('.wp-admin.plugins-php tr[data-slug="wp-staging"] .row-actions .deactivate a');
10
- wpstg_deactivate_link_url = wpstg_deactivate_link.attr('href');
11
-
12
- wpstg_deactivate_link.click(function (e) {
13
- e.preventDefault();
14
-
15
- // only show feedback form once per 30 days
16
- var c_value = wpstg_admin_get_cookie("wpstg_hide_feedback");
17
-
18
- if (c_value === undefined) {
19
- $('#wpstg-feedback-overlay').show();
20
- } else {
21
- // click on the link
22
- window.location.href = wpstg_deactivate_link_url;
23
- }
24
- });
25
- // show text fields
26
- $('#wpstg-feedback-content input[type="radio"]').click(function () {
27
- // show text field if there is one
28
- $(this).parents('li').next('li').children('input[type="text"], textarea').show();
29
- });
30
- // send form or close it
31
- $('#wpstg-feedback-content .button').click(function (e) {
32
- e.preventDefault();
33
- // set cookie for 30 days
34
- var exdate = new Date();
35
- exdate.setSeconds(exdate.getSeconds() + 2592000);
36
- document.cookie = "wpstg_hide_feedback=1; expires=" + exdate.toUTCString() + "; path=/";
37
-
38
- $('#wpstg-feedback-overlay').hide();
39
- if ('wpstg-feedback-submit' === this.id) {
40
- // Send form data
41
- $.ajax({
42
- type: 'POST',
43
- url: ajaxurl,
44
- dataType: 'json',
45
- data: {
46
- action: 'wpstg_send_feedback',
47
- data: $('#wpstg-feedback-content form').serialize()
48
- },
49
- complete: function (MLHttpRequest, textStatus, errorThrown) {
50
- // deactivate the plugin and close the popup
51
- $('#wpstg-feedback-overlay').remove();
52
- window.location.href = wpstg_deactivate_link_url;
53
-
54
- }
55
- });
56
- } else {
57
- $('#wpstg-feedback-overlay').remove();
58
- window.location.href = wpstg_deactivate_link_url;
59
- }
60
- });
61
- // close form without doing anything
62
- $('.wpstg-feedback-not-deactivate').click(function (e) {
63
- $('#wpstg-feedback-overlay').hide();
64
- });
65
-
66
- function wpstg_admin_get_cookie (name) {
67
- var i, x, y, wpstg_cookies = document.cookie.split( ";" );
68
- for (i = 0; i < wpstg_cookies.length; i++)
69
- {
70
- x = wpstg_cookies[i].substr( 0, wpstg_cookies[i].indexOf( "=" ) );
71
- y = wpstg_cookies[i].substr( wpstg_cookies[i].indexOf( "=" ) + 1 );
72
- x = x.replace( /^\s+|\s+$/g, "" );
73
- if (x === name)
74
- {
75
- return unescape( y );
76
- }
77
- }
78
- }
79
-
80
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/public/js/wpstg-admin-poll.js DELETED
@@ -1,23 +0,0 @@
1
- jQuery(document).ready(function ($) {
2
- $(".wpstg_hide_poll").click(function (e) {
3
- e.preventDefault();
4
-
5
- window.WPStaging.ajax(
6
- {action: "wpstg_hide_poll"},
7
- function (response)
8
- {
9
- if (true === response)
10
- {
11
- $(".wpstg_poll").slideUp("fast");
12
- return true;
13
- } else {
14
-
15
- alert(
16
- "Unexpected message received. This might mean the data was not saved " +
17
- "and you might see this message again."
18
- );
19
- }
20
- }
21
- );
22
- })
23
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/public/js/wpstg-admin-rating.js DELETED
@@ -1,163 +0,0 @@
1
- var wpstgTimesWaited = 0;
2
-
3
- /*
4
- Let's wait for jQuery to be available to show the rating.
5
- We need it to dispatch AJAX requests.
6
- */
7
- var wpstgWaitForJQuery = setInterval(function () {
8
- if (wpstgTimesWaited > 100) {
9
- // Give up waiting.
10
- clearInterval(wpstgWaitForJQuery);
11
- }
12
- if (typeof jQuery != 'undefined') {
13
-
14
- wpstgRegisterRatingEvents();
15
-
16
- clearInterval(wpstgWaitForJQuery);
17
- }
18
- wpstgTimesWaited = wpstgTimesWaited + 1;
19
- }, 100);
20
-
21
- function wpstgRegisterRatingEvents() {
22
-
23
- // Show the rating once jQuery is loaded.
24
- jQuery('.wpstg_fivestar').show();
25
-
26
- /**
27
- * Dispatch the request to hide the video after user clicks to rate the plugin.
28
- */
29
- jQuery(document).on('click', '#wpstg_clicked_deserved_it', function(e) {
30
- jQuery.ajax({
31
- url: ajaxurl,
32
- type: "POST",
33
- data: { action: "wpstg_hide_rating" },
34
- error: function(xhr, textStatus, errorThrown) {
35
- console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
36
- console.log(textStatus);
37
-
38
- alert(
39
- "Unknown error. Please get in contact with us to solve it support@wp-staging.com"
40
- );
41
- },
42
- success: function(data) {
43
- jQuery(".wpstg_fivestar").slideUp("fast");
44
- return true;
45
- },
46
- statusCode: {
47
- 404: function() {
48
- alert("Something went wrong; can't find ajax request URL! Please get in contact with us to solve it support@wp-staging.com");
49
- },
50
- 500: function() {
51
- alert("Something went wrong; internal server error while processing the request! Please get in contact with us to solve it support@wp-staging.com");
52
- }
53
- }
54
- });
55
- });
56
-
57
- jQuery(".wpstg_hide_rating").click(function(e) {
58
- e.preventDefault();
59
-
60
- jQuery.ajax({
61
- url: ajaxurl,
62
- type: "POST",
63
- data: { action: "wpstg_hide_rating" },
64
- error: function(xhr, textStatus, errorThrown) {
65
- console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
66
- console.log(textStatus);
67
-
68
- alert(
69
- "Unknown error. Please get in contact with us to solve it support@wp-staging.com"
70
- );
71
- },
72
- success: function(data) {
73
- jQuery(".wpstg_fivestar").slideUp("fast");
74
- return true;
75
- },
76
- statusCode: {
77
- 404: function() {
78
- alert("Something went wrong; can't find ajax request URL! Please get in contact with us to solve it support@wp-staging.com");
79
- },
80
- 500: function() {
81
- alert("Something went wrong; internal server error while processing the request! Please get in contact with us to solve it support@wp-staging.com");
82
- }
83
- }
84
- });
85
- });
86
-
87
- jQuery(".wpstg_rate_later").click(function(e) {
88
- e.preventDefault();
89
-
90
- jQuery.ajax({
91
- url: ajaxurl,
92
- type: "POST",
93
- data: { action: "wpstg_hide_later" },
94
- error: function(xhr, textStatus, errorThrown) {
95
- console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
96
- console.log(textStatus);
97
-
98
- alert(
99
- "Unknown error. Please get in contact with us to solve it support@wp-staging.com"
100
- );
101
- },
102
- success: function(data) {
103
- jQuery(".wpstg_fivestar").slideUp("fast");
104
- return true;
105
- },
106
- statusCode: {
107
- 404: function() {
108
- alert("Something went wrong; can't find ajax request URL! Please get in contact with us to solve it support@wp-staging.com");
109
- },
110
- 500: function() {
111
- alert("Something went wrong; internal server error while processing the request! Please get in contact with us to solve it support@wp-staging.com");
112
- }
113
- }
114
- });
115
- });
116
- }
117
-
118
- document.styleSheets[0].insertRule("@media only screen and (max-width:600px){.wpstg-welcome-box{display:block !important}.wpstg-welcome-video-container{width:100% !important;height:auto !important}.wpstg-welcome-text{padding-left:8px !important}}", "");
119
-
120
- document.addEventListener("DOMContentLoaded", function() {
121
- var player;
122
- var accepted = wpstgYouTubeConfig.accepted;
123
- var playerPlaceholder = document.getElementById("welcomeNoticeFree");
124
- playerPlaceholder.addEventListener("click", function() {
125
- if (!accepted) {
126
- var message = wpstgYouTubeConfig.message + "\n \n" + wpstgYouTubeConfig.regards;
127
- var conf = confirm(message);
128
- if (conf) {
129
- accepted = true;
130
- wpstgFetchVideo();
131
- }
132
- }
133
- });
134
- });
135
-
136
- function wpstgFetchVideo() {
137
- var tag = document.createElement('script');
138
-
139
- tag.src = "https://www.youtube.com/iframe_api";
140
- var firstScriptTag = document.getElementsByTagName('script')[0];
141
- firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
142
-
143
- }
144
-
145
- function onYouTubeIframeAPIReady() {
146
- player = new YT.Player('welcomeNoticeFree', {
147
- height: '225',
148
- width: '400',
149
- videoId: 'fsC9ZvbRQ5Y',
150
- playerVars: {
151
- rel: 0,
152
- showinfo: 0,
153
- ecver: 2
154
- },
155
- events: {
156
- 'onReady': onPlayerReady
157
- }
158
- });
159
- }
160
-
161
- function onPlayerReady(event) {
162
- event.target.playVideo();
163
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/public/js/wpstg-admin.js DELETED
@@ -1,3078 +0,0 @@
1
- "use strict";
2
-
3
- /**
4
- * Show warning during cloning or push process when closing tab or browser, or changing page
5
- * @param {beforeunload} event
6
- * @return {null}
7
- */
8
- var wpstgWarnIfClose = function (event) {
9
- // Only some browsers shows the message below, most say something like "Changes you made may not be saved" (Chrome) or "You have unsaved changes. Exit?"
10
- event.returnValue = 'You MUST leave this window open while cloning/pushing. Please wait...';
11
- return null;
12
- };
13
-
14
- var WPStaging = (function ($) {
15
- var that = {
16
- isCancelled: false,
17
- isFinished: false,
18
- getLogs: false,
19
- time: 1,
20
- executionTime: false,
21
- progressBar: 0
22
- },
23
- cache = {elements: []},
24
- timeout, ajaxSpinner;
25
-
26
- /**
27
- * Get / Set Cache for Selector
28
- * @param {String} selector
29
- * @returns {*}
30
- */
31
- cache.get = function (selector) {
32
- // It is already cached!
33
- if ($.inArray(selector, cache.elements) !== -1) {
34
- return cache.elements[selector];
35
- }
36
-
37
- // Create cache and return
38
- cache.elements[selector] = jQuery(selector);
39
-
40
- return cache.elements[selector];
41
- };
42
-
43
- /**
44
- * Refreshes given cache
45
- * @param {String} selector
46
- */
47
- cache.refresh = function (selector) {
48
- selector.elements[selector] = jQuery(selector);
49
- };
50
-
51
- /**
52
- * Show and Log Error Message
53
- * @param {String} message
54
- */
55
- var showError = function (message) {
56
- cache.get("#wpstg-try-again").css("display", "inline-block");
57
- cache.get("#wpstg-cancel-cloning").text("Reset");
58
- cache.get("#wpstg-resume-cloning").show();
59
- cache.get("#wpstg-error-wrapper").show();
60
- cache.get("#wpstg-error-details").show().html(message);
61
- cache.get("#wpstg-removing-clone").removeClass("loading");
62
- cache.get(".wpstg-loader").hide();
63
- $('.wpstg--modal--process--generic-problem').show().html(message);
64
- };
65
-
66
-
67
- /**
68
- *
69
- * @param obj
70
- * @returns {boolean}
71
- */
72
- function isEmpty(obj) {
73
- for(var prop in obj) {
74
- if(obj.hasOwnProperty(prop))
75
- return false;
76
- }
77
-
78
- return true;
79
- }
80
-
81
- /**
82
- *
83
- * @param response the error object
84
- * @param prependMessage Overwrite default error message at beginning
85
- * @param appendMessage Overwrite default error message at end
86
- * @returns void
87
- */
88
-
89
- var showAjaxFatalError = function (response, prependMessage, appendMessage) {
90
- prependMessage = prependMessage ? prependMessage + '<br/><br/>' : 'Something went wrong! <br/><br/>';
91
- appendMessage = appendMessage ? appendMessage + '<br/><br/>' : '<br/><br/>Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.';
92
-
93
- if (response === false) {
94
- showError(prependMessage + ' Error: No response.' + appendMessage);
95
- window.removeEventListener('beforeunload', wpstgWarnIfClose);
96
- return;
97
- }
98
-
99
- if (typeof response.error !== 'undefined' && response.error) {
100
- console.error(response.message);
101
- showError(prependMessage + ' Error: ' + response.message + appendMessage);
102
- window.removeEventListener('beforeunload', wpstgWarnIfClose);
103
- return;
104
- }
105
- }
106
-
107
- /**
108
- *
109
- * @param response
110
- * @returns {{ok}|*}
111
- */
112
- var handleFetchErrors = function (response) {
113
- if (!response.ok) {
114
- showError('Error: ' + response.status + ' - ' + response.statusText + '. Please try again or contact support.');
115
- }
116
- return response;
117
- }
118
-
119
- /** Hide and reset previous thrown visible errors */
120
- var resetErrors = function () {
121
- cache.get("#wpstg-error-details").hide().html('');
122
- }
123
-
124
- var slugify = function (url) {
125
- return url.toString()
126
- .toLowerCase()
127
- .normalize('NFD')
128
- .replace(/[\u0300-\u036f]/g, '')
129
- .replace(/\s+/g, '-')
130
- .replace(/&/g, '-and-')
131
- .replace(/[^a-z0-9\-]/g, '')
132
- .replace(/-+/g, '-')
133
- .replace(/^-*/, '')
134
- .replace(/-*$/, '')
135
- ;
136
- };
137
-
138
-
139
- /**
140
- * Common Elements
141
- */
142
- var elements = function () {
143
- var $workFlow = cache.get("#wpstg-workflow"),
144
- isAllChecked = true,
145
- urlSpinner = ajaxurl.replace("/admin-ajax.php", '') + "/images/spinner",
146
- timer;
147
-
148
- if (2 < window.devicePixelRatio) {
149
- urlSpinner += "-2x";
150
- }
151
-
152
- urlSpinner += ".gif";
153
-
154
- ajaxSpinner = "<img src=''" + urlSpinner + "' alt='' class='ajax-spinner general-spinner' />";
155
-
156
- var getBaseValues = function () {
157
- var path = $('#wpstg-use-target-dir').data('base-path');
158
- var uri = $('#wpstg-use-target-hostname').data('base-uri');
159
- return {
160
- path
161
- };
162
- };
163
-
164
- $workFlow
165
- // Check / Un-check All Database Tables New
166
- .on("click", ".wpstg-button-unselect", function (e) {
167
- e.preventDefault();
168
-
169
- if (false === isAllChecked) {
170
- console.log('true');
171
- cache.get("#wpstg_select_tables_cloning .wpstg-db-table").prop("selected", "selected");
172
- cache.get(".wpstg-button-unselect").text("Unselect All");
173
- cache.get(".wpstg-db-table-checkboxes").prop("checked", true);
174
- isAllChecked = true;
175
- } else {
176
- console.log('false');
177
- cache.get("#wpstg_select_tables_cloning .wpstg-db-table").prop("selected", false);
178
- cache.get(".wpstg-button-unselect").text("Select All");
179
- cache.get(".wpstg-db-table-checkboxes").prop("checked", false);
180
- isAllChecked = false;
181
- }
182
- })
183
-
184
- /**
185
- * Select tables with certain tbl prefix | NEW
186
- * @param obj e
187
- * @returns {undefined}
188
- */
189
- .on("click", ".wpstg-button-select", function (e) {
190
- e.preventDefault();
191
- $("#wpstg_select_tables_cloning .wpstg-db-table").each(function () {
192
- if (wpstg.isMultisite == 1) {
193
- if ($(this).attr('name').match("^" + wpstg.tblprefix + "([^0-9])_*")) {
194
- $(this).prop("selected", "selected");
195
- } else {
196
- $(this).prop("selected", false);
197
- }
198
- }
199
-
200
- if (wpstg.isMultisite == 0) {
201
- if ($(this).attr('name').match("^" + wpstg.tblprefix)) {
202
- $(this).prop("selected", "selected");
203
- } else {
204
- $(this).prop("selected", false);
205
- }
206
- }
207
- })
208
- })
209
- // Expand Directories
210
- .on("click", ".wpstg-expand-dirs", function (e) {
211
- e.preventDefault();
212
-
213
- var $this = $(this);
214
-
215
- if (!$this.hasClass("disabled")) {
216
- $this.siblings(".wpstg-subdir").slideToggle();
217
- }
218
- })
219
- // When a directory checkbox is Selected
220
- .on("change", "input.wpstg-check-dir", function () {
221
- var $directory = $(this).parent(".wpstg-dir");
222
-
223
- if (this.checked) {
224
- $directory.parents(".wpstg-dir").children(".wpstg-check-dir").prop("checked", true);
225
- $directory.find(".wpstg-expand-dirs").removeClass("disabled");
226
- $directory.find(".wpstg-subdir .wpstg-check-dir").prop("checked", true);
227
- } else {
228
- $directory.find(".wpstg-dir .wpstg-check-dir").prop("checked", false);
229
- $directory.find(".wpstg-expand-dirs, .wpstg-check-subdirs").addClass("disabled");
230
- $directory.find(".wpstg-check-subdirs").data("action", "check").text("check");
231
- }
232
- })
233
- // When a directory name is Selected
234
- .on("change", "href.wpstg-check-dir", function () {
235
- var $directory = $(this).parent(".wpstg-dir");
236
-
237
- if (this.checked) {
238
- $directory.parents(".wpstg-dir").children(".wpstg-check-dir").prop("checked", true);
239
- $directory.find(".wpstg-expand-dirs").removeClass("disabled");
240
- $directory.find(".wpstg-subdir .wpstg-check-dir").prop("checked", true);
241
- } else {
242
- $directory.find(".wpstg-dir .wpstg-check-dir").prop("checked", false);
243
- $directory.find(".wpstg-expand-dirs, .wpstg-check-subdirs").addClass("disabled");
244
- $directory.find(".wpstg-check-subdirs").data("action", "check").text("check");
245
- }
246
- })
247
- // Check the max length of the clone name and if the clone name already exists
248
- .on("keyup", "#wpstg-new-clone-id", function () {
249
-
250
- // Hide previous errors
251
- document.getElementById('wpstg-error-details').style.display = "none";
252
-
253
- // This request was already sent, clear it up!
254
- if ("number" === typeof (timer)) {
255
- clearInterval(timer);
256
- }
257
-
258
- var cloneID = this.value;
259
-
260
- timer = setTimeout(
261
- function () {
262
- ajax(
263
- {
264
- action: "wpstg_check_clone",
265
- accessToken: wpstg.accessToken,
266
- nonce: wpstg.nonce,
267
- cloneID: cloneID
268
- },
269
- function (response) {
270
- if (response.status === "success") {
271
- cache.get("#wpstg-new-clone-id").removeClass("wpstg-error-input");
272
- cache.get("#wpstg-start-cloning").removeAttr("disabled");
273
- cache.get("#wpstg-clone-id-error").text('').hide();
274
- } else {
275
- cache.get("#wpstg-new-clone-id").addClass("wpstg-error-input");
276
- cache.get("#wpstg-start-cloning").prop("disabled", true);
277
- cache.get("#wpstg-clone-id-error").text(response.message).show();
278
- }
279
- }
280
- );
281
- },
282
- 500
283
- );
284
- })
285
- // Restart cloning process
286
- .on("click", "#wpstg-start-cloning", function () {
287
- resetErrors();
288
- that.isCancelled = false;
289
- that.getLogs = false;
290
- that.progressBar = 0;
291
- })
292
- .on('input', '#wpstg-new-clone-id', function () {
293
- if ($('#wpstg-clone-directory').length < 1) {
294
- return;
295
- }
296
-
297
- var slug = slugify(this.value);
298
- var $targetDir = $('#wpstg-use-target-dir');
299
- var $targetUri = $('#wpstg-use-target-hostname');
300
- var path = $targetDir.data('base-path');
301
- var uri = $targetUri.data('base-uri');
302
-
303
- if (path) {
304
- path = path.replace(/\/+$/g, '') + '/' + slug + '/';
305
- }
306
-
307
- if (uri) {
308
- uri = uri.replace(/\/+$/g, '') + '/' + slug;
309
- }
310
-
311
-
312
- $('.wpstg-use-target-dir--value').text(path);
313
- $('.wpstg-use-target-hostname--value').text(uri);
314
-
315
- $targetDir.attr('data-path', path);
316
- $targetUri.attr('data-uri', uri);
317
- $('#wpstg_clone_dir').attr('placeholder', path);
318
- $('#wpstg_clone_hostname').attr('placeholder', uri);
319
- })
320
- .on('input', '#wpstg_clone_hostname', function () {
321
- if ($(this).val() === "" || validateTargetHost()) {
322
- $('#wpstg_clone_hostname_error').remove();
323
- return;
324
- }
325
- if (!validateTargetHost() && !$('#wpstg_clone_hostname_error').length) {
326
- $('#wpstg-clone-directory tr:last-of-type').after('<tr><td>&nbsp;</td><td><p id="wpstg_clone_hostname_error" style="color: red;">&nbsp;Invalid host name. Please provide it in a format like http://example.com</p></td></tr>');
327
- }
328
- })
329
- ;
330
-
331
- cloneActions();
332
- };
333
-
334
- /* @returns {boolean} */
335
- var validateTargetHost = function () {
336
- var the_domain = $('#wpstg_clone_hostname').val();
337
-
338
- if (the_domain === "") {
339
- return true;
340
- }
341
-
342
- var reg = /^http(s)?:\/\/.*$/;
343
- if (reg.test(the_domain) === false) {
344
- return false;
345
- }
346
- return true;
347
- };
348
-
349
- /**
350
- * Clone actions
351
- */
352
- var cloneActions = function () {
353
- var $workFlow = cache.get("#wpstg-workflow");
354
-
355
- $workFlow
356
- // Cancel cloning
357
- .on("click", "#wpstg-cancel-cloning", function () {
358
- if (!confirm("Are you sure you want to cancel cloning process?")) {
359
- return false;
360
- }
361
-
362
- var $this = $(this);
363
-
364
- $("#wpstg-try-again, #wpstg-home-link").hide();
365
- $this.prop("disabled", true);
366
-
367
- that.isCancelled = true;
368
- that.progressBar = 0;
369
-
370
- $("#wpstg-processing-status").text("Please wait...this can take up a while.");
371
- $(".wpstg-loader, #wpstg-show-log-button").hide();
372
-
373
- $this.parent().append(ajaxSpinner);
374
-
375
- cancelCloning();
376
- })
377
- // Resume cloning
378
- .on("click", "#wpstg-resume-cloning", function () {
379
- resetErrors();
380
- var $this = $(this);
381
-
382
- $("#wpstg-try-again, #wpstg-home-link").hide();
383
-
384
- that.isCancelled = false;
385
-
386
- $("#wpstg-processing-status").text("Try to resume cloning process...");
387
- $("#wpstg-error-details").hide();
388
- $(".wpstg-loader").show();
389
-
390
- $this.parent().append(ajaxSpinner);
391
-
392
- that.startCloning();
393
- })
394
- // Cancel update cloning
395
- .on("click", "#wpstg-cancel-cloning-update", function () {
396
- resetErrors();
397
-
398
- var $this = $(this);
399
-
400
- $("#wpstg-try-again, #wpstg-home-link").hide();
401
- $this.prop("disabled", true);
402
-
403
- that.isCancelled = true;
404
-
405
- $("#wpstg-cloning-result").text("Please wait...this can take up a while.");
406
- $(".wpstg-loader, #wpstg-show-log-button").hide();
407
-
408
- $this.parent().append(ajaxSpinner);
409
-
410
- cancelCloningUpdate();
411
- })
412
- // Restart cloning
413
- .on("click", "#wpstg-restart-cloning", function () {
414
- resetErrors();
415
-
416
- var $this = $(this);
417
-
418
- $("#wpstg-try-again, #wpstg-home-link").hide();
419
- $this.prop("disabled", true);
420
-
421
- that.isCancelled = true;
422
-
423
- $("#wpstg-cloning-result").text("Please wait...this can take up a while.");
424
- $(".wpstg-loader, #wpstg-show-log-button").hide();
425
-
426
- $this.parent().append(ajaxSpinner);
427
-
428
- restart();
429
- })
430
- // Delete clone - confirmation
431
- .on("click", ".wpstg-remove-clone[data-clone]", function (e) {
432
- resetErrors();
433
- e.preventDefault();
434
-
435
- var $existingClones = cache.get("#wpstg-existing-clones");
436
-
437
- $workFlow.removeClass('active');
438
-
439
- cache.get(".wpstg-loader").show();
440
-
441
- ajax(
442
- {
443
- action: "wpstg_confirm_delete_clone",
444
- accessToken: wpstg.accessToken,
445
- nonce: wpstg.nonce,
446
- clone: $(this).data("clone")
447
- },
448
- function (response) {
449
- cache.get("#wpstg-removing-clone").html(response);
450
-
451
- $existingClones.children("img").remove();
452
-
453
- cache.get(".wpstg-loader").hide();
454
-
455
- $('html, body').animate({
456
- //This logic is meant to be a "scrollBottom"
457
- scrollTop: $("#wpstg-remove-clone").offset().top - $(window).height() +
458
- $("#wpstg-remove-clone").height() + 50
459
- }, 100);
460
- },
461
- "HTML"
462
- );
463
- })
464
- // Delete clone - confirmed
465
- .on("click", "#wpstg-remove-clone", function (e) {
466
- resetErrors();
467
- e.preventDefault();
468
-
469
- cache.get("#wpstg-removing-clone").addClass("loading");
470
-
471
- cache.get(".wpstg-loader").show();
472
-
473
- deleteClone($(this).data("clone"));
474
- })
475
- // Cancel deleting clone
476
- .on("click", "#wpstg-cancel-removing", function (e) {
477
- e.preventDefault();
478
- $(".wpstg-clone").removeClass("active");
479
- cache.get("#wpstg-removing-clone").html('');
480
- })
481
- // Update
482
- .on("click", ".wpstg-execute-clone", function (e) {
483
- e.preventDefault();
484
-
485
- var clone = $(this).data("clone");
486
-
487
- $workFlow.addClass("loading");
488
-
489
- ajax(
490
- {
491
- action: "wpstg_scanning",
492
- clone: clone,
493
- accessToken: wpstg.accessToken,
494
- nonce: wpstg.nonce
495
- },
496
- function (response) {
497
- if (response.length < 1) {
498
- showError(
499
- "Something went wrong! Error: No response. Please try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report and contact us."
500
- );
501
- }
502
-
503
- $workFlow.removeClass("loading").html(response);
504
- // register check disk space function for clone update process.
505
- checkDiskSpace();
506
- that.switchStep(2);
507
- },
508
- "HTML"
509
- );
510
- });
511
- };
512
-
513
- /**
514
- * Ajax Requests
515
- * @param Object data
516
- * @param Function callback
517
- * @param string dataType
518
- * @param bool showErrors
519
- * @param int tryCount
520
- * @param float incrementRatio
521
- */
522
- var ajax = function (data, callback, dataType, showErrors, tryCount, incrementRatio = null) {
523
- if ("undefined" === typeof (dataType)) {
524
- dataType = "json";
525
- }
526
-
527
- if (false !== showErrors) {
528
- showErrors = true;
529
- }
530
-
531
- tryCount = "undefined" === typeof (tryCount) ? 0 : tryCount;
532
-
533
- var retryLimit = 10;
534
-
535
- var retryTimeout = 10000 * tryCount;
536
-
537
- incrementRatio = parseInt(incrementRatio);
538
- if (!isNaN(incrementRatio)) {
539
- retryTimeout *= incrementRatio;
540
- }
541
-
542
- $.ajax({
543
- url: ajaxurl + '?action=wpstg_processing&_=' + (Date.now() / 1000),
544
- type: "POST",
545
- dataType: dataType,
546
- cache: false,
547
- data: data,
548
- error: function (xhr, textStatus, errorThrown) {
549
- console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
550
-
551
- //try again after 10 seconds
552
- tryCount++;
553
- if (tryCount <= retryLimit) {
554
- setTimeout(function () {
555
- ajax(data, callback, dataType, showErrors, tryCount, incrementRatio);
556
- return;
557
- }, retryTimeout);
558
-
559
- } else {
560
- var errorCode = "undefined" === typeof (xhr.status) ? "Unknown" : xhr.status;
561
- showError(
562
- "Fatal Error: " + errorCode + " Please try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report and contact us."
563
- );
564
- }
565
-
566
-
567
- },
568
- success: function (data) {
569
- if ("function" === typeof (callback)) {
570
- callback(data);
571
- }
572
- },
573
- statusCode: {
574
- 404: function () {
575
- if (tryCount >= retryLimit) {
576
- showError("Error 404 - Can't find ajax request URL! Please try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report and contact us.");
577
- }
578
- },
579
- 500: function () {
580
- if (tryCount >= retryLimit) {
581
- showError("Fatal Error 500 - Internal server error while processing the request! Please try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report and contact us.");
582
- }
583
- },
584
- 504: function () {
585
- if (tryCount > retryLimit) {
586
- showError("Error 504 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ");
587
- }
588
-
589
- },
590
- 502: function () {
591
- if (tryCount >= retryLimit) {
592
- showError("Error 502 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ");
593
- }
594
- },
595
- 503: function () {
596
- if (tryCount >= retryLimit) {
597
- showError("Error 503 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ");
598
- }
599
- },
600
- 429: function () {
601
- if (tryCount >= retryLimit) {
602
- showError("Error 429 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ");
603
- }
604
- },
605
- 403: function () {
606
- if (tryCount >= retryLimit) {
607
- showError("Refresh page or login again! The process should be finished successfully. \n\ ");
608
- }
609
- }
610
- }
611
- });
612
- };
613
-
614
- /**
615
- * Next / Previous Step Clicks to Navigate Through Staging Job
616
- */
617
- var stepButtons = function () {
618
-
619
- var $workFlow = cache.get("#wpstg-workflow");
620
-
621
- $workFlow
622
- // Next Button
623
- .on("click", ".wpstg-next-step-link", function (e) {
624
- e.preventDefault();
625
-
626
- var $this = $(this);
627
- var isScan = false;
628
-
629
- if($('#wpstg_clone_hostname').length && !validateTargetHost()) {
630
- $('#wpstg_clone_hostname').focus();
631
- return false;
632
- }
633
-
634
- if ($this.data("action") === "wpstg_update") {
635
- // Update Clone - confirmed
636
- if (!confirm("STOP! This will overwrite your staging site with all selected data from the live site! This should be used only if you want to clone again your production site. Are you sure you want to do this? \n\nMake sure to exclude all tables and folders which you do not want to overwrite, first! \n\nDo not necessarily cancel the updating process! This can break your staging site. \n\n\Make sure you have a backop of your staging website before you proceed.")) {
637
- return false;
638
- }
639
-
640
- }
641
-
642
- // Button is disabled
643
- if ($this.attr("disabled")) {
644
- return false;
645
- }
646
-
647
- if ($this.data("action") === "wpstg_cloning") {
648
- // Verify External Database If Checked and Not Skipped
649
- if($("#wpstg-ext-db").is(':checked')) {
650
- verifyExternalDatabase($this, $workFlow);
651
- return;
652
- }
653
-
654
- }
655
-
656
- proceedCloning($this, $workFlow);
657
- })
658
- // Previous Button
659
- .on("click", ".wpstg-prev-step-link", function (e) {
660
- e.preventDefault();
661
- cache.get(".wpstg-loader").removeClass('wpstg-finished');
662
- cache.get(".wpstg-loader").hide();
663
- loadOverview();
664
- });
665
- };
666
-
667
- /**
668
- * Get Included (Checked) Database Tables
669
- * @returns {Array}
670
- */
671
- var getIncludedTables = function () {
672
- var includedTables = [];
673
-
674
- $("#wpstg_select_tables_cloning option:selected").each(function () {
675
- includedTables.push(this.value);
676
- });
677
-
678
- return includedTables;
679
- };
680
-
681
- /**
682
- * Get Excluded (Unchecked) Database Tables
683
- * Not used anymore!
684
- * @returns {Array}
685
- */
686
- var getExcludedTables = function () {
687
- var excludedTables = [];
688
-
689
- $(".wpstg-db-table input:not(:checked)").each(function () {
690
- excludedTables.push(this.name);
691
- });
692
-
693
- return excludedTables;
694
- };
695
-
696
- /**
697
- * Get Included Directories
698
- * @returns {Array}
699
- */
700
- var getIncludedDirectories = function () {
701
- var includedDirectories = [];
702
-
703
- $(".wpstg-dir input:checked.wpstg-root").each(function () {
704
- var $this = $(this);
705
- includedDirectories.push(encodeURIComponent($this.val()));
706
- });
707
-
708
- return includedDirectories;
709
- };
710
-
711
- /**
712
- * Get Excluded Directories
713
- * @returns {Array}
714
- */
715
- var getExcludedDirectories = function () {
716
- var excludedDirectories = [];
717
-
718
- $(".wpstg-dir input:not(:checked).wpstg-root").each(function () {
719
- var $this = $(this);
720
- excludedDirectories.push(encodeURIComponent($this.val()));
721
- });
722
-
723
- return excludedDirectories;
724
- };
725
-
726
- /**
727
- * Get included extra directories of the root level
728
- * All directories except wp-content, wp-admin, wp-includes
729
- * @returns {Array}
730
- */
731
- var getIncludedExtraDirectories = function () {
732
- // Add directories from the root level
733
- var extraDirectories = [];
734
- $(".wpstg-dir input:checked.wpstg-extra").each(function () {
735
- var $this = $(this);
736
- extraDirectories.push(encodeURIComponent($this.val()));
737
- });
738
-
739
- // Add any other custom selected extra directories
740
- if (!$("#wpstg_extraDirectories").val()) {
741
- return extraDirectories;
742
- }
743
-
744
- var extraCustomDirectories = encodeURIComponent($("#wpstg_extraDirectories").val().split(/\r?\n/));
745
-
746
- return extraDirectories.concat(extraCustomDirectories);
747
- };
748
-
749
- /**
750
- * Verify External Database for Cloning
751
- */
752
- var verifyExternalDatabase = function($this, workflow) {
753
- cache.get(".wpstg-loader").show();
754
- ajax(
755
- {
756
- action: "wpstg_database_verification",
757
- accessToken: wpstg.accessToken,
758
- nonce: wpstg.nonce,
759
- databaseUser: cache.get('#wpstg_db_username').val(),
760
- databasePassword: cache.get('#wpstg_db_password').val(),
761
- databaseServer: cache.get('#wpstg_db_server').val(),
762
- databaseDatabase: cache.get('#wpstg_db_database').val(),
763
- },
764
- function (response)
765
- {
766
- // Undefined Error
767
- if (false === response)
768
- {
769
- showError(
770
- "Something went wrong! Error: No response." +
771
- "Please try again. If that does not help, " +
772
- "<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
773
- );
774
- cache.get(".wpstg-loader").hide();
775
- return;
776
- }
777
-
778
- // Throw Error
779
- if ("undefined" === typeof (response.success)) {
780
- showError(
781
- "Something went wrong! Error: Invalid response." +
782
- "Please try again. If that does not help, " +
783
- "<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
784
- );
785
- cache.get(".wpstg-loader").hide();
786
- return;
787
- }
788
-
789
- if (response.success) {
790
- cache.get(".wpstg-loader").hide();
791
- proceedCloning($this, workflow);
792
- return;
793
- }
794
-
795
- if (response.error_type === 'comparison') {
796
- cache.get(".wpstg-loader").hide();
797
- var render = '<table style="width: 100%;"><thead><tr><th>Property</th><th>Production DB</th><th>Staging DB</th><th>Status</th></tr></thead><tbody>';
798
- response.checks.forEach(x=>{
799
- var icon = '<i style="color: #00ff00">✔</i>';
800
- if (x.production !== x.staging) {
801
- icon = '<i style="color: #ff0000">❌</i>'
802
- }
803
- render += "<tr><td>"+x.name+"</td><td>"+x.production+"</td><td>"+x.staging+"</td><td>"+icon+"</td></tr>";
804
- });
805
- render += '</tbody></table><p>Note: Some mySQL properties do not match. You may proceed but the staging site may not work as expected.</p>';
806
- Swal.fire({
807
- title: 'Different Database Properties',
808
- icon: 'warning',
809
- html: render,
810
- width: '650px',
811
- focusConfirm: false,
812
- confirmButtonText: 'Proceed Anyway',
813
- showCancelButton: true,
814
- }).then(function(result) {
815
- if (result.value) {
816
- proceedCloning($this, workflow);
817
- }
818
- });
819
- return;
820
- }
821
-
822
- Swal.fire({
823
- title: 'Different Database Properties',
824
- icon: 'error',
825
- html: response.message,
826
- focusConfirm: true,
827
- confirmButtonText: 'Ok',
828
- showCancelButton: false,
829
- });
830
- cache.get(".wpstg-loader").hide();
831
- },
832
- "json",
833
- false
834
- );
835
- };
836
-
837
- /**
838
- * Get Cloning Step Data
839
- */
840
- var getCloningData = function () {
841
- if ("wpstg_cloning" !== that.data.action && "wpstg_update" !== that.data.action) {
842
- return;
843
- }
844
-
845
- that.data.cloneID = $("#wpstg-new-clone-id").val() || new Date().getTime().toString();
846
- // Remove this to keep &_POST[] small otherwise mod_security will throw error 404
847
- //that.data.excludedTables = getExcludedTables();
848
- that.data.includedTables = getIncludedTables();
849
- that.data.includedDirectories = getIncludedDirectories();
850
- that.data.excludedDirectories = getExcludedDirectories();
851
- that.data.extraDirectories = getIncludedExtraDirectories();
852
- that.data.databaseServer = $("#wpstg_db_server").val();
853
- that.data.databaseUser = $("#wpstg_db_username").val();
854
- that.data.databasePassword = $("#wpstg_db_password").val();
855
- that.data.databaseDatabase = $("#wpstg_db_database").val();
856
- that.data.databasePrefix = $("#wpstg_db_prefix").val();
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) {
867
- // Add loading overlay
868
- workflow.addClass("loading");
869
-
870
- // Prepare data
871
- that.data = {
872
- action: $this.data("action"),
873
- accessToken: wpstg.accessToken,
874
- nonce: wpstg.nonce
875
- };
876
-
877
- // Cloning data
878
- getCloningData();
879
-
880
- console.log(that.data);
881
-
882
- // Send ajax request
883
- ajax(
884
- that.data,
885
- function (response) {
886
-
887
- // Undefined Error
888
- if (false === response) {
889
- showError(
890
- "Something went wrong!<br/><br/> Go to WP Staging > Settings and lower 'File Copy Limit' and 'DB Query Limit'. Also set 'CPU Load Priority to low '" +
891
- "and try again. If that does not help, " +
892
- "<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
893
- );
894
- }
895
-
896
-
897
- if (response.length < 1) {
898
- showError(
899
- "Something went wrong! No response. Go to WP Staging > Settings and lower 'File Copy Limit' and 'DB Query Limit'. Also set 'CPU Load Priority to low '" +
900
- "and try again. If that does not help, " +
901
- "<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
902
- );
903
- }
904
-
905
- // Styling of elements
906
- workflow.removeClass("loading").html(response);
907
-
908
- if ($this.data("action") === "wpstg_scanning") {
909
- that.switchStep(2);
910
- } else if ($this.data("action") === "wpstg_cloning" || $this.data("action") === "wpstg_update") {
911
- that.switchStep(3);
912
- }
913
-
914
- // Start cloning
915
- that.startCloning();
916
-
917
- },
918
- "HTML"
919
- );
920
- };
921
-
922
- /**
923
- * Loads Overview (first step) of Staging Job
924
- */
925
- var loadOverview = function () {
926
- var $workFlow = cache.get("#wpstg-workflow");
927
-
928
- $workFlow.addClass("loading");
929
-
930
- ajax(
931
- {
932
- action: "wpstg_overview",
933
- accessToken: wpstg.accessToken,
934
- nonce: wpstg.nonce
935
- },
936
- function (response) {
937
-
938
- if (response.length < 1) {
939
- showError(
940
- "Something went wrong! No response. Please try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report."
941
- );
942
- }
943
-
944
- var $currentStep = cache.get(".wpstg-current-step");
945
-
946
- // Styling of elements
947
- $workFlow.removeClass("loading").html(response);
948
-
949
- },
950
- "HTML"
951
- );
952
-
953
- that.switchStep(1);
954
- cache.get(".wpstg-step3-cloning").show();
955
- cache.get(".wpstg-step3-pushing").hide();
956
- };
957
-
958
- /**
959
- * Load Tabs
960
- */
961
- var tabs = function () {
962
-
963
- cache.get("#wpstg-workflow").on("click", ".wpstg-tab-header", function (e) {
964
- e.preventDefault();
965
-
966
- var $this = $(this);
967
- var $section = cache.get($this.data("id"));
968
-
969
- $this.toggleClass("expand");
970
-
971
- $section.slideToggle();
972
-
973
- if ($this.hasClass("expand")) {
974
- $this.find(".wpstg-tab-triangle").html("&#9660;");
975
- } else {
976
- $this.find(".wpstg-tab-triangle").html("&#9658;");
977
- }
978
-
979
-
980
- });
981
- };
982
-
983
- /**
984
- * Delete Clone
985
- * @param {String} clone
986
- */
987
- var deleteClone = function (clone) {
988
-
989
- var deleteDir = $("#deleteDirectory:checked").data("deletepath");
990
-
991
- ajax(
992
- {
993
- action: "wpstg_delete_clone",
994
- clone: clone,
995
- accessToken: wpstg.accessToken,
996
- nonce: wpstg.nonce,
997
- excludedTables: getExcludedTables(),
998
- deleteDir: deleteDir
999
- },
1000
- function (response) {
1001
- if (response) {
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('');
1015
- }
1016
-
1017
- cache.get(".wpstg-loader").hide();
1018
- return;
1019
- }
1020
- }
1021
- // continue
1022
- if (true !== response) {
1023
- deleteClone(clone);
1024
- return;
1025
- }
1026
-
1027
- }
1028
- );
1029
- };
1030
-
1031
- /**
1032
- * Cancel Cloning Process
1033
- */
1034
- var cancelCloning = function () {
1035
-
1036
- that.timer('stop');
1037
-
1038
-
1039
- if (true === that.isFinished) {
1040
- return true;
1041
- }
1042
-
1043
- ajax(
1044
- {
1045
- action: "wpstg_cancel_clone",
1046
- clone: that.data.cloneID,
1047
- accessToken: wpstg.accessToken,
1048
- nonce: wpstg.nonce
1049
- },
1050
- function (response) {
1051
-
1052
-
1053
- if (response && "undefined" !== typeof (response.delete) && response.delete === "finished") {
1054
- cache.get(".wpstg-loader").hide();
1055
- // Load overview
1056
- loadOverview();
1057
- return;
1058
- }
1059
-
1060
- if (true !== response) {
1061
- // continue
1062
- cancelCloning();
1063
- return;
1064
- }
1065
-
1066
- // Load overview
1067
- loadOverview();
1068
- }
1069
- );
1070
- };
1071
-
1072
- /**
1073
- * Cancel Cloning Process
1074
- */
1075
- var cancelCloningUpdate = function () {
1076
- if (true === that.isFinished) {
1077
- return true;
1078
- }
1079
-
1080
- ajax(
1081
- {
1082
- action: "wpstg_cancel_update",
1083
- clone: that.data.cloneID,
1084
- accessToken: wpstg.accessToken,
1085
- nonce: wpstg.nonce
1086
- },
1087
- function (response) {
1088
-
1089
-
1090
- if (response && "undefined" !== typeof (response.delete) && response.delete === "finished") {
1091
- // Load overview
1092
- loadOverview();
1093
- return;
1094
- }
1095
-
1096
- if (true !== response) {
1097
- // continue
1098
- cancelCloningUpdate();
1099
- return;
1100
- }
1101
-
1102
- // Load overview
1103
- loadOverview();
1104
- }
1105
- );
1106
- };
1107
-
1108
- /**
1109
- * Cancel Cloning Process
1110
- */
1111
- var restart = function () {
1112
- if (true === that.isFinished) {
1113
- return true;
1114
- }
1115
-
1116
- ajax(
1117
- {
1118
- action: "wpstg_restart",
1119
- //clone: that.data.cloneID,
1120
- accessToken: wpstg.accessToken,
1121
- nonce: wpstg.nonce
1122
- },
1123
- function (response) {
1124
-
1125
-
1126
- if (response && "undefined" !== typeof (response.delete) && response.delete === "finished") {
1127
- // Load overview
1128
- loadOverview();
1129
- return;
1130
- }
1131
-
1132
- if (true !== response) {
1133
- // continue
1134
- cancelCloningUpdate();
1135
- return;
1136
- }
1137
-
1138
- // Load overview
1139
- loadOverview();
1140
- }
1141
- );
1142
- };
1143
-
1144
- /**
1145
- * Scroll the window log to bottom
1146
- * @returns void
1147
- */
1148
- var logscroll = function () {
1149
- var $div = cache.get(".wpstg-log-details");
1150
- if ("undefined" !== typeof ($div[0])) {
1151
- $div.scrollTop($div[0].scrollHeight);
1152
- }
1153
- }
1154
-
1155
- /**
1156
- * Append the log to the logging window
1157
- * @param string log
1158
- * @returns void
1159
- */
1160
- var getLogs = function (log) {
1161
- if (log != null && "undefined" !== typeof (log)) {
1162
- if (log.constructor === Array) {
1163
- $.each(log, function (index, value) {
1164
- if (value === null) {
1165
- return;
1166
- }
1167
- if (value.type === 'ERROR') {
1168
- cache.get(".wpstg-log-details").append('<span style="color:red;">[' + value.type + ']</span>-' + '[' + value.date + '] ' + value.message + '</br>');
1169
- } else {
1170
- cache.get(".wpstg-log-details").append('[' + value.type + ']-' + '[' + value.date + '] ' + value.message + '</br>');
1171
- }
1172
- })
1173
- } else {
1174
- cache.get(".wpstg-log-details").append('[' + log.type + ']-' + '[' + log.date + '] ' + log.message + '</br>');
1175
- }
1176
- }
1177
- logscroll();
1178
-
1179
- };
1180
-
1181
- /**
1182
- * Check diskspace
1183
- * @returns string json
1184
- */
1185
- var checkDiskSpace = function () {
1186
- cache.get("#wpstg-check-space").on("click", function (e) {
1187
- cache.get(".wpstg-loader").show();
1188
- console.log("check disk space");
1189
- ajax(
1190
- {
1191
- action: "wpstg_check_disk_space",
1192
- accessToken: wpstg.accessToken,
1193
- nonce: wpstg.nonce
1194
- },
1195
- function (response) {
1196
- if (false === response) {
1197
- cache.get("#wpstg-clone-id-error").text('Can not detect required disk space').show();
1198
- cache.get(".wpstg-loader").hide();
1199
- return;
1200
- }
1201
-
1202
- // Show required disk space
1203
- cache.get("#wpstg-clone-id-error").html('Estimated necessary disk space: ' + response.usedspace + '<br> <span style="color:#444;">Before you proceed ensure your account has enough free disk space to hold the entire instance of the production site. You can check the available space from your hosting account (cPanel or similar).</span>').show();
1204
- cache.get(".wpstg-loader").hide();
1205
- },
1206
- "json",
1207
- false
1208
- );
1209
- });
1210
- }
1211
-
1212
- var mainTabs = function () {
1213
- $('.wpstg--tab--header a[data-target]').on('click', function () {
1214
- var $this = $(this);
1215
- var target = $this.attr('data-target');
1216
- var $wrapper = $this.parents('.wpstg--tab--wrapper');
1217
- var $menuItems = $wrapper.find('.wpstg--tab--header a[data-target]');
1218
- var $contents = $wrapper.find('.wpstg--tab--contents > .wpstg--tab--content');
1219
-
1220
- $contents.filter('.wpstg--tab--active:not(.wpstg--tab--active' + target + ')').removeClass('wpstg--tab--active');
1221
- $menuItems.not($this).removeClass('wpstg--tab--active');
1222
- $this.addClass('wpstg--tab--active');
1223
- $(target).addClass('wpstg--tab--active');
1224
-
1225
- if ('#wpstg--tab--snapshot' === target) {
1226
- that.snapshots.init();
1227
- }
1228
- });
1229
- };
1230
-
1231
- /**
1232
- * Show or hide animated loading icon
1233
- * @param isLoading bool
1234
- */
1235
- var isLoading = function (isLoading) {
1236
- if (!isLoading || isLoading === false) {
1237
- cache.get(".wpstg-loader").hide();
1238
- } else {
1239
- cache.get(".wpstg-loader").show();
1240
- }
1241
- };
1242
-
1243
- /**
1244
- * Count up processing execution time
1245
- * @param string status
1246
- * @returns html
1247
- */
1248
- that.timer = function (status) {
1249
-
1250
- if (status === 'stop') {
1251
- var time = that.time;
1252
- that.time = 1;
1253
- clearInterval(that.executionTime);
1254
- return that.convertSeconds(time);
1255
- }
1256
-
1257
-
1258
- that.executionTime = setInterval(function () {
1259
- if (null !== document.getElementById('wpstg-processing-timer')) {
1260
- document.getElementById('wpstg-processing-timer').innerHTML = 'Elapsed Time: ' + that.convertSeconds(that.time);
1261
- }
1262
- that.time++;
1263
- if (status === 'stop') {
1264
- that.time = 1;
1265
- clearInterval(that.executionTime);
1266
- }
1267
- }, 1000);
1268
- };
1269
-
1270
- /**
1271
- * Convert seconds to hourly format
1272
- * @param int seconds
1273
- * @returns string
1274
- */
1275
- that.convertSeconds = function (seconds) {
1276
- var date = new Date(null);
1277
- date.setSeconds(seconds); // specify value for SECONDS here
1278
- return date.toISOString().substr(11, 8);
1279
- };
1280
-
1281
- /**
1282
- * Start Cloning Process
1283
- * @type {Function}
1284
- */
1285
- that.startCloning = (function () {
1286
-
1287
- resetErrors();
1288
-
1289
- // Register function for checking disk space
1290
- checkDiskSpace();
1291
-
1292
- if ("wpstg_cloning" !== that.data.action && "wpstg_update" !== that.data.action) {
1293
- return;
1294
- }
1295
-
1296
- that.isCancelled = false;
1297
-
1298
- // Start the process
1299
- start();
1300
-
1301
- // Functions
1302
- // Start
1303
- function start() {
1304
-
1305
- console.log("Starting cloning process...");
1306
-
1307
- cache.get(".wpstg-loader").show();
1308
- cache.get("#wpstg-cancel-cloning").text('Cancel');
1309
- cache.get("#wpstg-resume-cloning").hide();
1310
- cache.get("#wpstg-error-details").hide();
1311
-
1312
-
1313
- // Clone Database
1314
- setTimeout(function () {
1315
- //cloneDatabase();
1316
- window.addEventListener('beforeunload', wpstgWarnIfClose);
1317
- processing();
1318
- }, wpstg.delayReq);
1319
-
1320
- that.timer('start');
1321
-
1322
- }
1323
-
1324
-
1325
- /**
1326
- * Start ajax processing
1327
- * @returns string
1328
- */
1329
- var processing = function () {
1330
-
1331
- if (true === that.isCancelled) {
1332
- window.removeEventListener('beforeunload', wpstgWarnIfClose);
1333
- return false;
1334
- }
1335
-
1336
- isLoading(true);
1337
-
1338
- // Show logging window
1339
- cache.get('.wpstg-log-details').show();
1340
-
1341
- WPStaging.ajax(
1342
- {
1343
- action: "wpstg_processing",
1344
- accessToken: wpstg.accessToken,
1345
- nonce: wpstg.nonce,
1346
- excludedTables: getExcludedTables(),
1347
- includedDirectories: getIncludedDirectories(),
1348
- excludedDirectories: getExcludedDirectories(),
1349
- extraDirectories: getIncludedExtraDirectories()
1350
- },
1351
- function (response) {
1352
-
1353
- showAjaxFatalError(response);
1354
-
1355
- // Add Log messages
1356
- if ("undefined" !== typeof (response.last_msg) && response.last_msg) {
1357
- getLogs(response.last_msg);
1358
- }
1359
- // Continue processing
1360
- if (false === response.status) {
1361
- progressBar(response);
1362
-
1363
- setTimeout(function () {
1364
- cache.get(".wpstg-loader").show();
1365
- processing();
1366
- }, wpstg.delayReq);
1367
-
1368
- } else if (true === response.status && 'finished' !== response.status) {
1369
- cache.get("#wpstg-error-details").hide();
1370
- cache.get("#wpstg-error-wrapper").hide();
1371
- progressBar(response, true);
1372
- processing();
1373
- } else if ('finished' === response.status || ("undefined" !== typeof (response.job_done) && response.job_done)) {
1374
- window.removeEventListener('beforeunload', wpstgWarnIfClose);
1375
- finish(response);
1376
- }
1377
- ;
1378
- },
1379
- "json",
1380
- false
1381
- );
1382
- };
1383
-
1384
- // Finish
1385
- function finish(response) {
1386
-
1387
- if (true === that.getLogs) {
1388
- getLogs();
1389
- }
1390
-
1391
- progressBar(response);
1392
-
1393
- // Add Log
1394
- if ("undefined" !== typeof (response.last_msg)) {
1395
- getLogs(response.last_msg);
1396
- }
1397
-
1398
- console.log("Cloning process finished");
1399
-
1400
- cache.get(".wpstg-loader").hide();
1401
- cache.get("#wpstg-processing-header").html('Processing Complete');
1402
- $("#wpstg-processing-status").text("Succesfully finished");
1403
-
1404
- cache.get("#wpstg_staging_name").html(that.data.cloneID);
1405
- cache.get("#wpstg-finished-result").show();
1406
- cache.get("#wpstg-cancel-cloning").hide();
1407
- cache.get("#wpstg-resume-cloning").hide();
1408
- cache.get("#wpstg-cancel-cloning-update").prop("disabled", true);
1409
-
1410
- var $link1 = cache.get("#wpstg-clone-url-1");
1411
- var $link = cache.get("#wpstg-clone-url");
1412
- $link1.attr("href", response.url);
1413
- $link1.html(response.url);
1414
- $link.attr("href", response.url);
1415
-
1416
- cache.get("#wpstg-remove-clone").data("clone", that.data.cloneID);
1417
-
1418
- // Finished
1419
- that.isFinished = true;
1420
- that.timer('stop');
1421
-
1422
-
1423
- cache.get(".wpstg-loader").hide();
1424
- cache.get("#wpstg-processing-header").html('Processing Complete');
1425
-
1426
- return false;
1427
-
1428
- }
1429
-
1430
- /**
1431
- * Add percentage progress bar
1432
- * @param object response
1433
- * @returns {Boolean}
1434
- */
1435
- var progressBar = function (response, restart) {
1436
- if ("undefined" === typeof (response.percentage))
1437
- return false;
1438
-
1439
- if (response.job === 'database') {
1440
- cache.get("#wpstg-progress-db").width(response.percentage * 0.2 + '%').html(response.percentage + '%');
1441
- cache.get("#wpstg-processing-status").html(response.percentage.toFixed(0) + '%' + ' - Step 1 of 4 Cloning Database Tables...');
1442
- }
1443
-
1444
- if (response.job === 'SearchReplace') {
1445
- cache.get("#wpstg-progress-db").css('background-color', '#3bc36b');
1446
- cache.get("#wpstg-progress-db").html('1. Database');
1447
- //Assumption: All previous steps are done.
1448
- //This avoids bugs where some steps are skipped and the progress bar is incomplete as a result
1449
- cache.get("#wpstg-progress-db").width('20%');
1450
-
1451
- cache.get("#wpstg-progress-sr").width(response.percentage * 0.1 + '%').html(response.percentage + '%');
1452
- cache.get("#wpstg-processing-status").html(response.percentage.toFixed(0) + '%' + ' - Step 2 of 4 Preparing Database Data...');
1453
- }
1454
-
1455
- if (response.job === 'directories') {
1456
- cache.get("#wpstg-progress-sr").css('background-color', '#3bc36b');
1457
- cache.get("#wpstg-progress-sr").html('2. Data');
1458
- cache.get("#wpstg-progress-sr").width('10%');
1459
-
1460
- cache.get("#wpstg-progress-dirs").width(response.percentage * 0.1 + '%').html(response.percentage + '%');
1461
- cache.get("#wpstg-processing-status").html(response.percentage.toFixed(0) + '%' + ' - Step 3 of 4 Getting files...');
1462
- }
1463
- if (response.job === 'files') {
1464
- cache.get("#wpstg-progress-dirs").css('background-color', '#3bc36b');
1465
- cache.get("#wpstg-progress-dirs").html('3. Files');
1466
- cache.get("#wpstg-progress-dirs").width('10%');
1467
-
1468
- cache.get("#wpstg-progress-files").width(response.percentage * 0.6 + '%').html(response.percentage + '%');
1469
- cache.get("#wpstg-processing-status").html(response.percentage.toFixed(0) + '%' + ' - Step 4 of 4 Copy files...');
1470
- }
1471
- if (response.job === 'finish') {
1472
- cache.get("#wpstg-progress-files").css('background-color', '#3bc36b');
1473
- cache.get("#wpstg-progress-files").html('4. Copy Files');
1474
- cache.get("#wpstg-progress-files").width('60%');
1475
-
1476
- cache.get("#wpstg-processing-status").html(response.percentage.toFixed(0) + '%' + ' - Cloning Process Finished');
1477
- }
1478
- }
1479
- });
1480
-
1481
- that.switchStep = function (step) {
1482
- cache.get(".wpstg-current-step")
1483
- .removeClass("wpstg-current-step");
1484
- cache.get(".wpstg-step" + step)
1485
- .addClass("wpstg-current-step");
1486
- }
1487
-
1488
- /**
1489
- * Initiation
1490
- * @type {Function}
1491
- */
1492
- that.init = (function () {
1493
- loadOverview();
1494
- elements();
1495
- stepButtons();
1496
- tabs();
1497
- mainTabs();
1498
- });
1499
-
1500
- /**
1501
- * Ajax call
1502
- * @type {ajax}
1503
- */
1504
- that.ajax = ajax;
1505
- that.showError = showError;
1506
- that.getLogs = getLogs;
1507
- that.loadOverview = loadOverview;
1508
-
1509
- // TODO RPoC (too big, scattered and unorganized)
1510
- that.snapshots = {
1511
- type: null,
1512
- isCancelled: false,
1513
- processInfo: {
1514
- title: null,
1515
- interval: null,
1516
- },
1517
- modal: {
1518
- create: {
1519
- html: null,
1520
- confirmBtnTxt: null,
1521
- },
1522
- process: {
1523
- html: null,
1524
- cancelBtnTxt: null,
1525
- modal: null,
1526
- },
1527
- download: {
1528
- html: null,
1529
- },
1530
- import: {
1531
- html: null,
1532
- btnTxtNext: null,
1533
- btnTxtConfirm: null,
1534
- btnTxtCancel: null,
1535
- searchReplaceForm: null,
1536
- file: null,
1537
- containerUpload: null,
1538
- containerFilesystem: null,
1539
- setFile: (file, upload = true) => {
1540
- const toUnit = (bytes) => {
1541
- const i = Math.floor( Math.log(bytes) / Math.log(1024) );
1542
- return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
1543
- }
1544
-
1545
- if (!file) {
1546
- return;
1547
- }
1548
-
1549
- that.snapshots.modal.import.file = file;
1550
- that.snapshots.modal.import.data.file = file.name;
1551
- console.log(`File ${file.name}`);
1552
- $('.wpstg--snapshot--import--selected-file').html(`${file.name} <br /> (${toUnit(file.size)})`).show();
1553
- $('.wpstg--drag').hide();
1554
- $('.wpstg--drag-or-upload').show();
1555
-
1556
- if (upload) {
1557
- $('.wpstg--modal--actions .swal2-confirm').prop('disabled', true);
1558
- that.snapshots.upload.start();
1559
- }
1560
- },
1561
- baseDirectory: null,
1562
- data: {
1563
- file: null,
1564
- search: [],
1565
- replace: [],
1566
- },
1567
- },
1568
- },
1569
- messages: {
1570
- WARNING: 'warning',
1571
- ERROR: 'error',
1572
- INFO: 'info',
1573
- DEBUG: 'debug',
1574
- CRITICAL: 'critical',
1575
- data: {
1576
- all: [], // TODO RPoC
1577
- info: [],
1578
- error: [],
1579
- critical: [],
1580
- warning: [],
1581
- debug: [],
1582
- },
1583
- shouldWarn() {
1584
- return that.snapshots.messages.data.error.length > 0
1585
- || that.snapshots.messages.data.critical.length > 0
1586
- ;
1587
- },
1588
- countByType(type = that.snapshots.messages.ERROR) {
1589
- return that.snapshots.messages.data[type].length;
1590
- },
1591
- addMessage(message) {
1592
- if (Array.isArray(message)) {
1593
- message.forEach(item => {
1594
- that.snapshots.messages.addMessage(item);
1595
- });
1596
- return;
1597
- }
1598
- const type = message.type.toLowerCase() || 'info';
1599
- if (!that.snapshots.messages.data[type]) {
1600
- that.snapshots.messages.data[type] = [];
1601
- }
1602
- that.snapshots.messages.data.all.push(message); // TODO RPoC
1603
- that.snapshots.messages.data[type].push(message);
1604
- },
1605
- reset() {
1606
- that.snapshots.messages.data = {
1607
- all: [],
1608
- info: [],
1609
- error: [],
1610
- critical: [],
1611
- warning: [],
1612
- debug: [],
1613
- };
1614
- },
1615
- },
1616
- timer: {
1617
- totalSeconds: 0,
1618
- interval: null,
1619
- start() {
1620
- if (null !== that.snapshots.timer.interval) {
1621
- return;
1622
- }
1623
-
1624
- const prettify = (seconds) => {
1625
- console.log(`Process running for ${seconds} seconds`);
1626
- // If potentially anything can exceed 24h execution time than that;
1627
- // const _seconds = parseInt(seconds, 10)
1628
- // const hours = Math.floor(_seconds / 3600)
1629
- // const minutes = Math.floor(_seconds / 60) % 60
1630
- // seconds = _seconds % 60
1631
- //
1632
- // return [hours, minutes, seconds]
1633
- // .map(v => v < 10 ? '0' + v : v)
1634
- // .filter((v,i) => v !== '00' || i > 0)
1635
- // .join(':')
1636
- // ;
1637
- // Are we sure we won't create anything that exceeds 24h execution time? If not then this;
1638
- return `${(new Date(seconds * 1000)).toISOString().substr(11, 8)}`;
1639
- };
1640
-
1641
- that.snapshots.timer.interval = setInterval(() => {
1642
- $('.wpstg--modal--process--elapsed-time').text(prettify(that.snapshots.timer.totalSeconds));
1643
- that.snapshots.timer.totalSeconds++;
1644
- }, 1000);
1645
- },
1646
- stop() {
1647
- that.snapshots.timer.totalSeconds = 0;
1648
- if (that.snapshots.timer.interval) {
1649
- clearInterval(that.snapshots.timer.interval);
1650
- that.snapshots.timer.interval = null;
1651
- }
1652
- },
1653
- },
1654
- upload: {
1655
- reader: null,
1656
- file: null,
1657
- iop: 1000 * 1024,
1658
- uploadInfo(isShow) {
1659
- const $containerUpload = $('.wpstg--modal--import--upload--process');
1660
- const $containerUploader = $('.wpstg--uploader');
1661
- if (isShow) {
1662
- $containerUpload.css('display', 'flex');
1663
- $containerUploader.hide();
1664
- return;
1665
- }
1666
-
1667
- $containerUploader.css('display', 'flex');
1668
- $containerUpload.hide();
1669
- },
1670
- start() {
1671
- console.log(`file ${that.snapshots.modal.import.data.file}`);
1672
- that.snapshots.upload.reader = new FileReader();
1673
- that.snapshots.upload.file = that.snapshots.modal.import.file;
1674
- that.snapshots.upload.uploadInfo(true);
1675
- that.snapshots.upload.sendChunk();
1676
- },
1677
- sendChunk(startsAt = 0) {
1678
- if (!that.snapshots.upload.file) {
1679
- return;
1680
- }
1681
- const isReset = startsAt < 1;
1682
- const endsAt = startsAt + that.snapshots.upload.iop + 1;
1683
- const blob = that.snapshots.upload.file.slice(startsAt, endsAt);
1684
- that.snapshots.upload.reader.onloadend = function(event) {
1685
- if (event.target.readyState !== FileReader.DONE) {
1686
- return;
1687
- }
1688
-
1689
- const body = new FormData();
1690
- body.append('accessToken', wpstg.accessToken);
1691
- body.append('nonce', wpstg.nonce);
1692
- body.append('data', event.target.result);
1693
- body.append('filename', that.snapshots.upload.file.name);
1694
- body.append('reset', isReset ? '1' : '0');
1695
-
1696
-
1697
- fetch(`${ajaxurl}?action=wpstg--snapshots--import--file-upload`, {
1698
- method: 'POST',
1699
- body,
1700
- }).then(handleFetchErrors)
1701
- .then(res => res.json())
1702
- .then(res => {
1703
- showAjaxFatalError(res, '', 'Submit an error report.');
1704
- const writtenBytes = startsAt + that.snapshots.upload.iop;
1705
- const percent = Math.floor((writtenBytes / that.snapshots.upload.file.size) * 100);
1706
- if (endsAt >= that.snapshots.upload.file.size) {
1707
- that.snapshots.upload.uploadInfo(false);
1708
- isLoading(false);
1709
- $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
1710
- return;
1711
- }
1712
- $('.wpstg--modal--import--upload--progress--title > span').text(percent);
1713
- $('.wpstg--modal--import--upload--progress').css('width', `${percent}%`)
1714
- that.snapshots.upload.sendChunk(endsAt);
1715
- })
1716
- .catch(e => showAjaxFatalError(e, '', 'Submit an error report.'))
1717
- ;
1718
- };
1719
- that.snapshots.upload.reader.readAsDataURL(blob);
1720
- },
1721
- },
1722
- status: {
1723
- hasResponse: null,
1724
- reTryAfter: 5000,
1725
- },
1726
- init() {
1727
- this.create();
1728
- this.delete();
1729
- this.restore();
1730
- this.edit();
1731
-
1732
- // noinspection JSIgnoredPromiseFromCall
1733
- that.snapshots.fetchListing();
1734
-
1735
- $('body')
1736
- .off('change', '#wpstg--snapshots--filter')
1737
- .on('change', '#wpstg--snapshots--filter', function() {
1738
- const $records = $('#wpstg-existing-snapshots').find('> div[id][data-type].wpstg-snapshot');
1739
- if (this.value === '') {
1740
- $records.show();
1741
- } else if (this.value === 'database') {
1742
- $records.filter('[data-type="site"]').hide();
1743
- $records.filter('[data-type="database"]').show();
1744
- } else if (this.value === 'site') {
1745
- $records.filter('[data-type="database"]').hide();
1746
- $records.filter('[data-type="site"]').show();
1747
- }
1748
- })
1749
- .on('click', '.wpstg--snapshot--download', function() {
1750
- const url = this.getAttribute('data-url');
1751
- if (url.length > 0) {
1752
- window.location.href = url;
1753
- return;
1754
- }
1755
- that.snapshots.downloadModal({
1756
- titleExport: this.getAttribute('data-title-export'),
1757
- title: this.getAttribute('data-title'),
1758
- id: this.getAttribute('data-id'),
1759
- btnTxtCancel: this.getAttribute('data-btn-cancel-txt'),
1760
- btnTxtConfirm: this.getAttribute('data-btn-download-txt'),
1761
- });
1762
- })
1763
- .off('click', '#wpstg-import-snapshot')
1764
- .on('click', '#wpstg-import-snapshot', function() {
1765
- that.snapshots.importModal();
1766
- })
1767
- // Import
1768
- .off('click', '.wpstg--snapshot--import--choose-option')
1769
- .on('click', '.wpstg--snapshot--import--choose-option', function() {
1770
- const $this = $(this);
1771
- const $parent = $this.parent();
1772
- if (!$parent.hasClass('wpstg--show-options')) {
1773
- $parent.addClass('wpstg--show-options');
1774
- $this.text($this.attr('data-txtChoose'));
1775
- } else {
1776
- $parent.removeClass('wpstg--show-options');
1777
- $this.text($this.attr('data-txtOther'));
1778
- }
1779
- })
1780
- .off('click', '.wpstg--modal--snapshot--import--search-replace--new')
1781
- .on('click', '.wpstg--modal--snapshot--import--search-replace--new', function(e) {
1782
- e.preventDefault();
1783
- const $container = $(Swal.getContainer()).find('.wpstg--modal--snapshot--import--search-replace--input--container');
1784
- const total = $container.find('.wpstg--modal--snapshot--import--search-replace--input-group').length;
1785
- $container.append(that.snapshots.modal.import.searchReplaceForm.replace(/{i}/g, total));
1786
- })
1787
- .off('input', '.wpstg--snapshot--import--search')
1788
- .on('input', '.wpstg--snapshot--import--search', function() {
1789
- const index = parseInt(this.getAttribute('data-index'));
1790
- if (!isNaN(index)) {
1791
- that.snapshots.modal.import.data.search[index] = this.value;
1792
- }
1793
- })
1794
- .off('input', '.wpstg--snapshot--import--replace')
1795
- .on('input', '.wpstg--snapshot--import--replace', function() {
1796
- const index = parseInt(this.getAttribute('data-index'));
1797
- if (!isNaN(index)) {
1798
- that.snapshots.modal.import.data.replace[index] = this.value;
1799
- }
1800
- })
1801
- // Other Options
1802
- .off('click', '.wpstg--snapshot--import--option[data-option]')
1803
- .on('click', '.wpstg--snapshot--import--option[data-option]', function() {
1804
- const option = this.getAttribute('data-option');
1805
-
1806
- if (option === 'file') {
1807
- $('input[type="file"][name="wpstg--snapshot--import--upload--file"]').click();
1808
- return;
1809
- }
1810
-
1811
- if (option === 'upload') {
1812
- that.snapshots.modal.import.containerFilesystem.hide();
1813
- that.snapshots.modal.import.containerUpload.show();
1814
- $('.wpstg--snapshot--import--choose-option').click();
1815
- $('.wpstg--modal--snapshot--import--search-replace--wrapper').show();
1816
-
1817
- }
1818
-
1819
- if (option !== 'filesystem') {
1820
- return;
1821
- }
1822
-
1823
- that.snapshots.modal.import.containerUpload.hide();
1824
- const $containerFilesystem = that.snapshots.modal.import.containerFilesystem;
1825
- $containerFilesystem.show();
1826
-
1827
- fetch(`${ajaxurl}?action=wpstg--snapshots--import--file-list&_=${Math.random()}&accessToken=${wpstg.accessToken}&nonce=${wpstg.nonce}`)
1828
- .then(handleFetchErrors)
1829
- .then(res => res.json())
1830
- .then(res => {
1831
- const $ul = $('.wpstg--modal--snapshot--import--filesystem ul');
1832
- $ul.empty();
1833
-
1834
- if (!res || isEmpty(res)) {
1835
- $ul.append(`<span id="wpstg--snapshots--import--file-list-empty">No import file found! Upload an import file to the folder above.</span><br />`);
1836
- $('.wpstg--modal--snapshot--import--search-replace--wrapper').hide();
1837
- return;
1838
- }
1839
-
1840
- $ul.append(`<span id="wpstg--snapshots--import--file-list">Select file to import:</span><br />`);
1841
- res.forEach(function(file, index){
1842
- //var checked = (index === 0) ? 'checked' : '';
1843
- $ul.append(`<li><label><input name="snapshot_import_file" type="radio" value="${file.fullPath}">${file.name} <br /> ${file.size}</label></li>`);
1844
- });
1845
- //$('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
1846
- return res;
1847
- })
1848
- .catch(e => showAjaxFatalError(e, '', 'Submit an error report.'))
1849
- ;
1850
- })
1851
- .off('change', 'input[type="file"][name="wpstg--snapshot--import--upload--file"]')
1852
- .on('change', 'input[type="file"][name="wpstg--snapshot--import--upload--file"]', function() {
1853
- that.snapshots.modal.import.setFile(this.files[0] || null);
1854
- $('.wpstg--snapshot--import--choose-option').click();
1855
- })
1856
- .off('change', 'input[type="radio"][name="snapshot_import_file"]')
1857
- .on('change', 'input[type="radio"][name="snapshot_import_file"]', function() {
1858
- $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
1859
- that.snapshots.modal.import.data.file = this.value;
1860
- })
1861
- // Drag & Drop
1862
- .on('drag dragstart dragend dragover dragenter dragleave drop', '.wpstg--modal--snapshot--import--upload--container', function(e) {
1863
- e.preventDefault();
1864
- e.stopPropagation();
1865
- })
1866
- .on('dragover dragenter', '.wpstg--modal--snapshot--import--upload--container', function() {
1867
- $(this).addClass('wpstg--has-dragover');
1868
- })
1869
- .on('dragleave dragend drop', '.wpstg--modal--snapshot--import--upload--container', function() {
1870
- $(this).removeClass('wpstg--has-dragover');
1871
- })
1872
- .on('drop', '.wpstg--modal--snapshot--import--upload--container', function(e) {
1873
- that.snapshots.modal.import.setFile(e.originalEvent.dataTransfer.files[0] || null);
1874
- })
1875
- ;
1876
- },
1877
- fetchListing(isResetErrors = true) {
1878
- isLoading(true);
1879
-
1880
- if (isResetErrors) {
1881
- resetErrors();
1882
- }
1883
-
1884
- return fetch(`${ajaxurl}?action=wpstg--snapshots--listing&_=${Math.random()}&accessToken=${wpstg.accessToken}&nonce=${wpstg.nonce}`)
1885
- .then(handleFetchErrors)
1886
- .then(res => res.json())
1887
- .then(res => {
1888
- showAjaxFatalError(res, '', 'Submit an error report.');
1889
- cache.get('#wpstg--tab--snapshot').html(res);
1890
- isLoading(false);
1891
- return res;
1892
- })
1893
- .catch(e => showAjaxFatalError(e, '', 'Submit an error report.'))
1894
- ;
1895
- },
1896
- delete() {
1897
- $('#wpstg--tab--snapshot')
1898
- .off('click', '.wpstg-delete-snapshot[data-id]')
1899
- .on('click', '.wpstg-delete-snapshot[data-id]', function (e) {
1900
- e.preventDefault();
1901
- resetErrors();
1902
- isLoading(true);
1903
- cache.get('#wpstg-existing-snapshots').hide();
1904
- var id = this.getAttribute('data-id');
1905
- that.ajax(
1906
- {
1907
- action: 'wpstg--snapshots--delete--confirm',
1908
- id: id,
1909
- accessToken: wpstg.accessToken,
1910
- nonce: wpstg.nonce
1911
- },
1912
- function (response) {
1913
- showAjaxFatalError(response, '', ' Please submit an error report by using the REPORT ISSUE button.');
1914
- isLoading(false);
1915
- cache.get('#wpstg-delete-confirmation').html(response);
1916
- },
1917
- );
1918
- })
1919
- // Delete final confirmation page
1920
- .off('click', '#wpstg-delete-snapshot')
1921
- .on('click', '#wpstg-delete-snapshot', function (e) {
1922
- e.preventDefault();
1923
- resetErrors();
1924
- isLoading(true);
1925
- var id = this.getAttribute('data-id');
1926
- that.ajax(
1927
- {
1928
- action: 'wpstg--snapshots--delete',
1929
- id: id,
1930
- accessToken: wpstg.accessToken,
1931
- nonce: wpstg.nonce
1932
- },
1933
- function (response) {
1934
- showAjaxFatalError(response, '', ' Please submit an error report by using the REPORT ISSUE button.');
1935
- // noinspection JSIgnoredPromiseFromCall
1936
- that.snapshots.fetchListing();
1937
- isLoading(false);
1938
- },
1939
- );
1940
- })
1941
- .off('click', '#wpstg-cancel-snapshot-delete')
1942
- .on('click', '#wpstg-cancel-snapshot-delete', function (e) {
1943
- e.preventDefault();
1944
- isLoading(false);
1945
- // noinspection JSIgnoredPromiseFromCall
1946
- that.snapshots.fetchListing();
1947
- })
1948
- ;
1949
-
1950
- // Force delete if snapshot tables do not exist
1951
- // TODO This is bloated, no need extra ID, use existing one?
1952
- $('#wpstg-error-wrapper')
1953
- .off('click', '#wpstg-snapshot-force-delete')
1954
- .on('click', '#wpstg-snapshot-force-delete', function (e) {
1955
- e.preventDefault();
1956
- resetErrors();
1957
- isLoading(true);
1958
- var id = this.getAttribute('data-id');
1959
-
1960
- if (!confirm("Do you want to delete this snapshot " + id + " from the listed snapshots?")) {
1961
- isLoading(false);
1962
- return false;
1963
- }
1964
-
1965
- that.ajax(
1966
- {
1967
- action: 'wpstg--snapshots--delete',
1968
- id: id,
1969
- accessToken: wpstg.accessToken,
1970
- nonce: wpstg.nonce
1971
- },
1972
- function (response) {
1973
- showAjaxFatalError(response, '', ' Please submit an error report by using the REPORT ISSUE button.');
1974
- // noinspection JSIgnoredPromiseFromCall
1975
- that.snapshots.fetchListing();
1976
- isLoading(false);
1977
- },
1978
- );
1979
- })
1980
- ;
1981
- },
1982
- create() {
1983
- var createSnapshot = function (data) {
1984
- resetErrors();
1985
-
1986
- if (that.snapshots.isCancelled) {
1987
- // Swal.close();
1988
- return;
1989
- }
1990
-
1991
- const reset = data['reset'];
1992
- delete data['reset'];
1993
- let requestData = Object.assign({}, data);
1994
- let useResponseTitle = true;
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
2022
- );
2023
- useResponseTitle = false;
2024
- requestData.jobs.snapshot.site.create.directories = [
2025
- data.wpContentDir,
2026
- ];
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;
2042
- delete requestData.jobs.snapshot.site.create.wpStagingDir;
2043
- delete requestData.jobs.snapshot.site.create.availableDirectories;
2044
-
2045
- } else {
2046
- that.snapshots.type = null;
2047
- Swal.close();
2048
- showError('Invalid Snapshot Type');
2049
- return;
2050
- }
2051
-
2052
- that.snapshots.timer.start();
2053
-
2054
- const statusStop = () => {
2055
- console.log('Status: Stop');
2056
- clearInterval(that.snapshots.processInfo.interval);
2057
- that.snapshots.processInfo.interval = null;
2058
- };
2059
- const status = () => {
2060
- if (that.snapshots.processInfo.interval !== null) {
2061
- return;
2062
- }
2063
- console.log('Status: Start');
2064
- that.snapshots.processInfo.interval = setInterval(() => {
2065
- if (true === that.snapshots.isCancelled) {
2066
- statusStop();
2067
- return;
2068
- }
2069
-
2070
- if (that.snapshots.status.hasResponse === false) {
2071
- return;
2072
- }
2073
-
2074
- that.snapshots.status.hasResponse = false;
2075
- fetch(`${ajaxurl}?action=wpstg--snapshots--status&accessToken=${wpstg.accessToken}&nonce=${wpstg.nonce}`)
2076
- .then(res => res.json())
2077
- .then(res => {
2078
- that.snapshots.status.hasResponse = true;
2079
- if (typeof res === 'undefined') {
2080
- statusStop();
2081
- }
2082
-
2083
- if (that.snapshots.processInfo.title === res.currentStatusTitle) {
2084
- return;
2085
- }
2086
-
2087
- that.snapshots.processInfo.title = res.currentStatusTitle;
2088
- const $container = $(Swal.getContainer());
2089
- $container.find('.wpstg--modal--process--title').text(res.currentStatusTitle);
2090
- $container.find('.wpstg--modal--process--percent').text('0');
2091
- })
2092
- .catch(e => {
2093
- that.snapshots.status.hasResponse = true;
2094
- showAjaxFatalError(e, '', 'Submit an error report.');
2095
- })
2096
- ;
2097
- }, 5000);
2098
- };
2099
-
2100
- WPStaging.ajax(
2101
- {
2102
- action: 'wpstg--snapshots--create',
2103
- accessToken: wpstg.accessToken,
2104
- nonce: wpstg.nonce,
2105
- reset,
2106
- wpstg: requestData,
2107
- },
2108
- function (response) {
2109
- if (typeof response === 'undefined') {
2110
- setTimeout(function () {
2111
- createSnapshot(data);
2112
- }, wpstg.delayReq);
2113
- return;
2114
- }
2115
-
2116
- that.snapshots.processResponse(response, useResponseTitle);
2117
- if (!useResponseTitle && !that.snapshots.processInfo.interval) {
2118
- status();
2119
- }
2120
-
2121
- if (response.status === false){
2122
- createSnapshot(data);
2123
- } else if (response.status === true) {
2124
- $('#wpstg--progress--status').text('Snapshot successfully created!');
2125
- that.snapshots.type = null;
2126
- if (that.snapshots.messages.shouldWarn()) {
2127
- // noinspection JSIgnoredPromiseFromCall
2128
- that.snapshots.fetchListing();
2129
- that.snapshots.logsModal();
2130
- return;
2131
- }
2132
- statusStop();
2133
- Swal.close();
2134
- that.snapshots.fetchListing()
2135
- .then(() => {
2136
- if (!response.snapshotId) {
2137
- showError('Failed to get snapshot ID from response');
2138
- return;
2139
- }
2140
-
2141
- // TODO RPoC
2142
- const $el = $(`.wpstg--snapshot--download[data-id="${response.snapshotId}"]`);
2143
- that.snapshots.downloadModal({
2144
- id: $el.data('id'),
2145
- url: $el.data('url'),
2146
- title: $el.data('title'),
2147
- titleExport: $el.data('title-export'),
2148
- btnTxtCancel: $el.data('btn-cancel-txt'),
2149
- btnTxtConfirm: $el.data('btn-download-txt'),
2150
- })
2151
- $('.wpstg--modal--download--logs--wrapper').show();
2152
- const $logsContainer = $('.wpstg--modal--process--logs');
2153
- that.snapshots.messages.data.all.forEach(message => {
2154
- const msgClass = `wpstg--modal--process--msg--${message.type.toLowerCase()}`;
2155
- $logsContainer
2156
- .append(`<p class="${msgClass}">[${message.type}] - [${message.date}] - ${message.message}</p>`)
2157
- ;
2158
- });
2159
- })
2160
- ;
2161
- } else {
2162
- setTimeout(function () {
2163
- createSnapshot(data);
2164
- }, wpstg.delayReq);
2165
- }
2166
- },
2167
- 'json',
2168
- false,
2169
- 0, // Don't retry upon failure
2170
- 1.25
2171
- );
2172
- };
2173
-
2174
- const $body = $('body');
2175
-
2176
- $body
2177
- .off('click', 'input[name="snapshot_type"]')
2178
- .on('click', 'input[name="snapshot_type"]', function() {
2179
- const $advancedOptions = $('.wpstg-advanced-options');
2180
- if (this.value === 'database') {
2181
- $advancedOptions.hide();
2182
- return;
2183
- }
2184
- $advancedOptions.show();
2185
- })
2186
- .off('click', '.wpstg--tab--toggle')
2187
- .on('click', '.wpstg--tab--toggle', function() {
2188
- const $this = $(this);
2189
- const $target = $($this.attr('data-target'));
2190
- $target.toggle();
2191
- if ($target.is(':visible')) {
2192
- $this.find('span').text('▼');
2193
- } else {
2194
- $this.find('span').text('►');
2195
- }
2196
- })
2197
- .off('change', '[name="includedDirectories\[\]"], [type="checkbox"][name="export_database"]')
2198
- .on('change', '[type="checkbox"][name="includedDirectories\[\]"], [type="checkbox"][name="export_database"]', function() {
2199
- const totalDirs = $('[type="checkbox"][name="includedDirectories\[\]"]:checked').length;
2200
- const isExportDatabase = $('[type="checkbox"][name="export_database"]:checked').length === 1;
2201
- if (totalDirs < 1 && !isExportDatabase) {
2202
- $('.swal2-confirm').prop('disabled', true);
2203
- } else {
2204
- $('.swal2-confirm').prop('disabled', false);
2205
- }
2206
- })
2207
- ;
2208
-
2209
- // Add backup name and notes
2210
- $('#wpstg--tab--snapshot')
2211
- .off('click', '#wpstg-new-snapshot')
2212
- .on('click', '#wpstg-new-snapshot', async function(e) {
2213
- resetErrors();
2214
- e.preventDefault();
2215
- that.snapshots.isCancelled = false;
2216
-
2217
- if (!that.snapshots.modal.create.html || !that.snapshots.modal.create.confirmBtnTxt) {
2218
- const $newSnapshotModal = $('#wpstg--modal--snapshot--new');
2219
- const html = $newSnapshotModal.html();
2220
- const btnTxt = $newSnapshotModal.attr('data-confirmButtonText');
2221
- that.snapshots.modal.create.html = html || null;
2222
- that.snapshots.modal.create.confirmBtnTxt = btnTxt || null;
2223
- $newSnapshotModal.remove();
2224
- }
2225
-
2226
- const { value: formValues } = await Swal.fire({
2227
- title: '',
2228
- html: that.snapshots.modal.create.html,
2229
- focusConfirm: false,
2230
- confirmButtonText: that.snapshots.modal.create.confirmBtnTxt,
2231
- showCancelButton: true,
2232
- preConfirm: () => {
2233
- const container = Swal.getContainer();
2234
-
2235
- if(document.getElementById('snapshot_type_database').offsetParent == '') {
2236
- var snapshotType = 'database';
2237
- } else {
2238
- var snapshotType = container.querySelector('input[name="snapshot_type"]:checked').value;
2239
- }
2240
-
2241
- return {
2242
- type: snapshotType || null,
2243
- name: container.querySelector('input[name="snapshot_name"]').value || null,
2244
- notes: container.querySelector('textarea[name="snapshot_note"]').value || null,
2245
- includedDirectories: Array.from((container.querySelectorAll('input[name="includedDirectories\\[\\]"]:checked') || [])).map(i => i.value),
2246
- wpContentDir: container.querySelector('input[name="wpContentDir"]').value || null,
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
- });
2254
-
2255
- if (!formValues) {
2256
- return;
2257
- }
2258
-
2259
- formValues.reset = true;
2260
-
2261
- that.snapshots.process({
2262
- execute: () => {
2263
- that.snapshots.messages.reset();
2264
- createSnapshot(formValues);
2265
- },
2266
- });
2267
- })
2268
- ;
2269
- },
2270
- restore() {
2271
- var restoreSnapshot = function (prefix, reset) {
2272
- isLoading(true);
2273
- resetErrors();
2274
-
2275
- if (typeof reset === 'undefined') {
2276
- reset = false;
2277
- }
2278
-
2279
- WPStaging.ajax(
2280
- {
2281
- action: 'wpstg--snapshots--restore',
2282
- accessToken: wpstg.accessToken,
2283
- nonce: wpstg.nonce,
2284
- wpstg: {
2285
- tasks: {
2286
- snapshot: {
2287
- database: {
2288
- create: {
2289
- source: prefix,
2290
- reset,
2291
- },
2292
- },
2293
- },
2294
- },
2295
- },
2296
- },
2297
- function (response) {
2298
- if (typeof response === 'undefined') {
2299
- setTimeout(function () {
2300
- restoreSnapshot(prefix);
2301
- }, wpstg.delayReq);
2302
- return;
2303
- }
2304
-
2305
- that.snapshots.processResponse(response);
2306
-
2307
- if (response.status === false || response.job_done === false) {
2308
- restoreSnapshot(prefix);
2309
- } else if (response.status === true && response.job_done === true) {
2310
- isLoading(false);
2311
- $('.wpstg--modal--process--title').text('Snapshot successfully restored');
2312
- setTimeout(() => {
2313
- Swal.close();
2314
- // noinspection JSIgnoredPromiseFromCall
2315
- that.snapshots.fetchListing();
2316
- }, 1000);
2317
- } else {
2318
- setTimeout(function () {
2319
- restoreSnapshot(prefix);
2320
- }, wpstg.delayReq);
2321
- }
2322
- },
2323
- 'json',
2324
- false,
2325
- 0,
2326
- 1.25
2327
- );
2328
- };
2329
-
2330
- $('#wpstg--tab--snapshot')
2331
- .off('click', '.wpstg--snapshot--restore[data-id]')
2332
- .on('click', '.wpstg--snapshot--restore[data-id]', function (e) {
2333
- e.preventDefault();
2334
- resetErrors();
2335
- that.ajax(
2336
- {
2337
- action: 'wpstg--snapshots--restore--confirm',
2338
- accessToken: wpstg.accessToken,
2339
- nonce: wpstg.nonce,
2340
- id: $(this).data('id'),
2341
- },
2342
- function (data) {
2343
- cache.get('#wpstg--tab--snapshot').html(data);
2344
- },
2345
- );
2346
- })
2347
- .off('click', '#wpstg--snapshot--restore--cancel')
2348
- .on('click', '#wpstg--snapshot--restore--cancel', function (e) {
2349
- resetErrors();
2350
- e.preventDefault();
2351
- // noinspection JSIgnoredPromiseFromCall
2352
- that.snapshots.fetchListing();
2353
- })
2354
- .off('click', '#wpstg--snapshot--restore[data-id]')
2355
- .on('click', '#wpstg--snapshot--restore[data-id]', function (e) {
2356
- e.preventDefault();
2357
- resetErrors();
2358
- const id = this.getAttribute('data-id');
2359
- that.snapshots.process({
2360
- execute: () => {
2361
- that.snapshots.messages.reset();
2362
- restoreSnapshot(id, true);
2363
- },
2364
- isShowCancelButton: false,
2365
- });
2366
- })
2367
- ;
2368
- },
2369
- // Edit snapshots name and notes
2370
- edit() {
2371
- $('#wpstg--tab--snapshot')
2372
- .off('click', '.wpstg--snapshot--edit[data-id]')
2373
- .on('click', '.wpstg--snapshot--edit[data-id]', async function(e) {
2374
- e.preventDefault();
2375
-
2376
- const $this = $(this);
2377
- const name = $this.data('name');
2378
- const notes = $this.data('notes');
2379
-
2380
- const { value: formValues } = await Swal.fire({
2381
- title: '',
2382
- html: `
2383
- <label id="wpstg-snapshot-edit-name">Backup Name</label>
2384
- <input id="wpstg-snapshot-edit-name-input" class="swal2-input" value="${name}">
2385
- <label>Additional Notes</label>
2386
- <textarea id="wpstg-snapshot-edit-notes-textarea" class="swal2-textarea">${notes}</textarea>
2387
- `,
2388
- focusConfirm: false,
2389
- confirmButtonText: 'Update Backup',
2390
- showCancelButton: true,
2391
- preConfirm: () => ({
2392
- name: document.getElementById('wpstg-snapshot-edit-name-input').value || null,
2393
- notes: document.getElementById('wpstg-snapshot-edit-notes-textarea').value || null,
2394
- }),
2395
- });
2396
-
2397
- if (!formValues) {
2398
- return;
2399
- }
2400
-
2401
- that.ajax(
2402
- {
2403
- action: 'wpstg--snapshots--edit',
2404
- accessToken: wpstg.accessToken,
2405
- nonce: wpstg.nonce,
2406
- id: $this.data('id'),
2407
- name: formValues.name,
2408
- notes: formValues.notes,
2409
- },
2410
- function(response) {
2411
- showAjaxFatalError(response, '', 'Submit an error report.');
2412
- // noinspection JSIgnoredPromiseFromCall
2413
- that.snapshots.fetchListing();
2414
- },
2415
- );
2416
- })
2417
- ;
2418
- },
2419
- cancel() {
2420
- that.snapshots.timer.stop();
2421
- that.snapshots.isCancelled = true;
2422
- Swal.close();
2423
- setTimeout(() => that.ajax(
2424
- {
2425
- action: 'wpstg--snapshots--cancel',
2426
- accessToken: wpstg.accessToken,
2427
- nonce: wpstg.nonce,
2428
- type: that.snapshots.type,
2429
- },
2430
- function(response) {
2431
- showAjaxFatalError(response, '', 'Submit an error report.');
2432
- },
2433
- ), 500);
2434
- },
2435
- /**
2436
- * If process.execute exists, process.data and process.onResponse is not used
2437
- * process = { data: {}, onResponse: (resp) => {}, onAfterClose: () => {}, execute: () => {}, isShowCancelButton: bool }
2438
- * @param {object} process
2439
- */
2440
- process(process) {
2441
- if (typeof process.execute !== 'function' && (!process.data || !process.onResponse)) {
2442
- Swal.close();
2443
- showError('process.data and / or process.onResponse is not set');
2444
- return;
2445
- }
2446
-
2447
- // TODO move to backend and get the contents as xhr response?
2448
- if (!that.snapshots.modal.process.html || !that.snapshots.modal.process.cancelBtnTxt) {
2449
- const $modal = $('#wpstg--modal--snapshot--process');
2450
- const html = $modal.html();
2451
- const btnTxt = $modal.attr('data-cancelButtonText');
2452
- that.snapshots.modal.process.html = html || null;
2453
- that.snapshots.modal.process.cancelBtnTxt = btnTxt || null;
2454
- $modal.remove();
2455
- }
2456
-
2457
- $('body')
2458
- .off('click', '.wpstg--modal--process--logs--tail')
2459
- .on('click', '.wpstg--modal--process--logs--tail', function(e) {
2460
- e.preventDefault();
2461
- const container = Swal.getContainer();
2462
- const $logs = $(container).find('.wpstg--modal--process--logs');
2463
- $logs.toggle();
2464
- if ($logs.is(':visible')) {
2465
- container.childNodes[0].style.width = '100%';
2466
- container.style['z-index'] = 9999;
2467
- } else {
2468
- container.childNodes[0].style.width = '600px';
2469
- }
2470
- })
2471
- ;
2472
-
2473
- process.isShowCancelButton = false !== process.isShowCancelButton;
2474
-
2475
- that.snapshots.modal.process.modal = Swal.mixin({
2476
- customClass: {
2477
- cancelButton: 'wpstg--btn--cancel wpstg-blue-primary wpstg-link-btn',
2478
- content: 'wpstg--process--content',
2479
- },
2480
- buttonsStyling: false,
2481
- }).fire({
2482
- html: that.snapshots.modal.process.html,
2483
- cancelButtonText: that.snapshots.modal.process.cancelBtnTxt,
2484
- showCancelButton: process.isShowCancelButton,
2485
- showConfirmButton: false,
2486
- allowOutsideClick: false,
2487
- allowEscapeKey: false,
2488
- width: 600,
2489
- onRender: () => {
2490
- const _btnCancel = Swal.getContainer().getElementsByClassName('swal2-cancel wpstg--btn--cancel')[0];
2491
- const btnCancel = _btnCancel.cloneNode(true);
2492
- _btnCancel.parentNode.replaceChild(btnCancel, _btnCancel);
2493
-
2494
- btnCancel.addEventListener('click', function(e) {
2495
- if (confirm('Are You Sure? This will cancel the process!')) {
2496
- Swal.close();
2497
- }
2498
- });
2499
-
2500
- if (typeof process.execute === 'function') {
2501
- process.execute();
2502
- return;
2503
- }
2504
-
2505
- if (!process.data || !process.onResponse) {
2506
- Swal.close();
2507
- showError('process.data and / or process.onResponse is not set');
2508
- return;
2509
- }
2510
-
2511
- that.ajax(process.data, process.onResponse);
2512
- },
2513
- onAfterClose: () => typeof process.onAfterClose === 'function' && process.onAfterClose(),
2514
- onClose: () => {
2515
- console.log('cancelled');
2516
- that.snapshots.cancel();
2517
- }
2518
- });
2519
- },
2520
- processResponse(response, useTitle) {
2521
- if (response === null) {
2522
- Swal.close();
2523
- showError('Invalid Response; null');
2524
- throw new Error(`Invalid Response; ${response}`);
2525
- }
2526
-
2527
- const $container = $(Swal.getContainer());
2528
- const title = () => {
2529
- if ((response.title || response.statusTitle) && useTitle === true) {
2530
- $container.find('.wpstg--modal--process--title').text(response.title || response.statusTitle);
2531
- }
2532
- };
2533
- const percentage = () => {
2534
- if (response.percentage) {
2535
- $container.find('.wpstg--modal--process--percent').text(response.percentage);
2536
- }
2537
- };
2538
- const logs = () => {
2539
- if (!response.messages) {
2540
- return;
2541
- }
2542
- const $logsContainer = $container.find('.wpstg--modal--process--logs');
2543
- const stoppingTypes = [
2544
- that.snapshots.messages.ERROR,
2545
- that.snapshots.messages.CRITICAL,
2546
- ];
2547
- const appendMessage = (message) => {
2548
- if (Array.isArray(message)) {
2549
- for (const item of message) {
2550
- appendMessage(item);
2551
- }
2552
- return;
2553
- }
2554
- const msgClass = `wpstg--modal--process--msg--${message.type.toLowerCase()}`;
2555
- $logsContainer.append(`<p class="${msgClass}">[${message.type}] - [${message.date}] - ${message.message}</p>`);
2556
-
2557
- if (stoppingTypes.includes(message.type.toLowerCase())) {
2558
- that.snapshots.cancel();
2559
- setTimeout(that.snapshots.logsModal, 500);
2560
- }
2561
- };
2562
- for (const message of response.messages) {
2563
- if (!message) {
2564
- continue;
2565
- }
2566
- that.snapshots.messages.addMessage(message);
2567
- appendMessage(message);
2568
- }
2569
-
2570
- if ($logsContainer.is(':visible')) {
2571
- $logsContainer.scrollTop($logsContainer[0].scrollHeight);
2572
- }
2573
-
2574
- if (!that.snapshots.messages.shouldWarn()) {
2575
- return;
2576
- }
2577
-
2578
- const $btnShowLogs = $container.find('.wpstg--modal--process--logs--tail');
2579
- $btnShowLogs.html($btnShowLogs.attr('data-txt-bad'));
2580
-
2581
- $btnShowLogs
2582
- .find('.wpstg--modal--logs--critical-count')
2583
- .text(that.snapshots.messages.countByType(that.snapshots.messages.CRITICAL))
2584
- ;
2585
-
2586
- $btnShowLogs
2587
- .find('.wpstg--modal--logs--error-count')
2588
- .text(that.snapshots.messages.countByType(that.snapshots.messages.ERROR))
2589
- ;
2590
-
2591
- $btnShowLogs
2592
- .find('.wpstg--modal--logs--warning-count')
2593
- .text(that.snapshots.messages.countByType(that.snapshots.messages.WARNING))
2594
- ;
2595
- };
2596
-
2597
- title();
2598
- percentage();
2599
- logs();
2600
-
2601
- if (response.status === true && response.job_done === true) {
2602
- that.snapshots.timer.stop();
2603
- that.snapshots.isCancelled = true;
2604
- }
2605
- },
2606
- requestData(notation, data) {
2607
- const obj = {};
2608
- const keys = notation.split('.');
2609
- const lastIndex = keys.length - 1;
2610
- keys.reduce((accumulated, current, index) => {
2611
- return accumulated[current] = index >= lastIndex? data : {};
2612
- }, obj);
2613
- return obj;
2614
- },
2615
- logsModal() {
2616
- Swal.fire({
2617
- html: `<div class="wpstg--modal--error--logs" style="display:block"></div><div class="wpstg--modal--process--logs" style="display:block"></div>`,
2618
- width: '95%',
2619
- onRender: () => {
2620
- const $container = $(Swal.getContainer());
2621
- $container[0].style['z-index'] = 9999;
2622
-
2623
- const $logsContainer = $container.find('.wpstg--modal--process--logs');
2624
- const $errorContainer = $container.find('.wpstg--modal--error--logs');
2625
- const $translations = $('#wpstg--js--translations');
2626
- const messages = that.snapshots.messages;
2627
- const title = $translations.attr('data-modal-logs-title')
2628
- .replace('{critical}', messages.countByType(messages.CRITICAL))
2629
- .replace('{errors}', messages.countByType(messages.ERROR))
2630
- .replace('{warnings}', messages.countByType(messages.WARNING))
2631
- ;
2632
-
2633
- $errorContainer.before(`<h3>${title}</h3>`);
2634
- const warnings = [
2635
- that.snapshots.messages.CRITICAL,
2636
- that.snapshots.messages.ERROR,
2637
- that.snapshots.messages.WARNING,
2638
- ];
2639
-
2640
- if (!that.snapshots.messages.shouldWarn()) {
2641
- $errorContainer.hide();
2642
- }
2643
-
2644
- for (const message of messages.data.all) {
2645
- const msgClass = `wpstg--modal--process--msg--${message.type.toLowerCase()}`;
2646
- // TODO RPoC
2647
- if (warnings.includes(message.type)) {
2648
- $errorContainer.append(
2649
- `<p class="${msgClass}">[${message.type}] - [${message.date}] - ${message.message}</p>`
2650
- );
2651
- }
2652
- $logsContainer.append(
2653
- `<p class="${msgClass}">[${message.type}] - [${message.date}] - ${message.message}</p>`
2654
- );
2655
- }
2656
- },
2657
- onOpen: (container) => {
2658
- const $logsContainer = $(container).find('.wpstg--modal--process--logs');
2659
- $logsContainer.scrollTop($logsContainer[0].scrollHeight);
2660
- },
2661
- });
2662
- },
2663
- downloadModal({ title = null, titleExport = null, id = null, url = null, btnTxtCancel = 'Cancel', btnTxtConfirm = 'Download' }) {
2664
-
2665
- if (null === that.snapshots.modal.download.html) {
2666
- const $el = $('#wpstg--modal--snapshot--download');
2667
- that.snapshots.modal.download.html = $el.html();
2668
- $el.remove();
2669
- }
2670
-
2671
- const exportModal = () => Swal.fire({
2672
- html: `<h2>${titleExport}</h2><span class="wpstg-loader"></span>`,
2673
- showCancelButton: false,
2674
- showConfirmButton: false,
2675
- onRender: () => {
2676
- that.ajax(
2677
- {
2678
- action: 'wpstg--snapshots--export',
2679
- accessToken: wpstg.accessToken,
2680
- nonce: wpstg.nonce,
2681
- id,
2682
- },
2683
- function (response) {
2684
- if (!response || !response.success || !response.data || response.data.length < 1) {
2685
- return;
2686
- }
2687
-
2688
- const a = document.createElement('a');
2689
- a.style.display = 'none';
2690
- a.href = response.data;
2691
- document.body.appendChild(a);
2692
- a.click();
2693
- document.body.removeChild(a);
2694
-
2695
- Swal.close();
2696
- },
2697
- );
2698
- },
2699
- });
2700
-
2701
- Swal.mixin({
2702
- customClass: {
2703
- cancelButton: 'wpstg--btn--cancel wpstg-blue-primary wpstg-link-btn',
2704
- confirmButton: 'wpstg--btn--confirm wpstg-blue-primary wpstg-button wpstg-link-btn',
2705
- actions: 'wpstg--modal--actions',
2706
- },
2707
- buttonsStyling: false,
2708
- })
2709
- .fire({
2710
- icon: 'success',
2711
- html: that.snapshots.modal.download.html.replace('{title}', title).replace('{btnTxtLog}', 'Show Logs'),
2712
- cancelButtonText: btnTxtCancel,
2713
- confirmButtonText: btnTxtConfirm,
2714
- showCancelButton: true,
2715
- showConfirmButton: true,
2716
- })
2717
- .then(isConfirm => {
2718
- if (!isConfirm || !isConfirm.value) {
2719
- return;
2720
- }
2721
-
2722
- if (url && url.length > 0) {
2723
- window.location.href = url;
2724
- return;
2725
- }
2726
-
2727
- exportModal();
2728
- })
2729
- ;
2730
- },
2731
- importModal() {
2732
-
2733
- const restoreSiteSnapshot = (data) => {
2734
- resetErrors();
2735
-
2736
- if (that.snapshots.isCancelled) {
2737
- console.log('cancelled');
2738
- // Swal.close();
2739
- return;
2740
- }
2741
-
2742
- const reset = data['reset'];
2743
- delete data['reset'];
2744
- data['mergeMediaFiles'] = 1; // always merge for uploads / media
2745
- let requestData = Object.assign({}, data);
2746
-
2747
- requestData = that.snapshots.requestData(
2748
- 'jobs.snapshot.site.restore',
2749
- { ...that.snapshots.modal.import.data }
2750
- );
2751
-
2752
- that.snapshots.timer.start();
2753
-
2754
- const statusStop = () => {
2755
- console.log('Status: Stop');
2756
- clearInterval(that.snapshots.processInfo.interval);
2757
- that.snapshots.processInfo.interval = null;
2758
- };
2759
- const status = () => {
2760
- if (that.snapshots.processInfo.interval !== null) {
2761
- return;
2762
- }
2763
- console.log('Status: Start');
2764
- that.snapshots.processInfo.interval = setInterval(() => {
2765
- if (true === that.snapshots.isCancelled) {
2766
- statusStop();
2767
- return;
2768
- }
2769
-
2770
- if (that.snapshots.status.hasResponse === false) {
2771
- return;
2772
- }
2773
-
2774
- that.snapshots.status.hasResponse = false;
2775
- fetch(`${ajaxurl}?action=wpstg--snapshots--status&process=restore&accessToken=${wpstg.accessToken}&nonce=${wpstg.nonce}`)
2776
- .then(res => res.json())
2777
- .then(res => {
2778
- that.snapshots.status.hasResponse = true;
2779
- if (typeof res === 'undefined') {
2780
- statusStop();
2781
- }
2782
-
2783
- if (that.snapshots.processInfo.title === res.currentStatusTitle) {
2784
- return;
2785
- }
2786
-
2787
- that.snapshots.processInfo.title = res.currentStatusTitle;
2788
- const $container = $(Swal.getContainer());
2789
- $container.find('.wpstg--modal--process--title').text(res.currentStatusTitle);
2790
- $container.find('.wpstg--modal--process--percent').text('0');
2791
- })
2792
- .catch(e => {
2793
- that.snapshots.status.hasResponse = true;
2794
- showAjaxFatalError(e, '', 'Submit an error report.');
2795
- })
2796
- ;
2797
- }, 5000);
2798
- };
2799
-
2800
- WPStaging.ajax(
2801
- {
2802
- action: 'wpstg--snapshots--site--restore',
2803
- accessToken: wpstg.accessToken,
2804
- nonce: wpstg.nonce,
2805
- reset,
2806
- wpstg: requestData,
2807
- },
2808
- function (response) {
2809
- if (typeof response === 'undefined') {
2810
- setTimeout(function () {
2811
- restoreSiteSnapshot(data);
2812
- }, wpstg.delayReq);
2813
- return;
2814
- }
2815
-
2816
- that.snapshots.processResponse(response, true);
2817
- if (!that.snapshots.processInfo.interval) {
2818
- status();
2819
- }
2820
-
2821
- if (response.status === false){
2822
- restoreSiteSnapshot(data);
2823
- } else if (response.status === true) {
2824
- $('#wpstg--progress--status').text('Snapshot successfully restored!');
2825
- that.snapshots.type = null;
2826
- if (that.snapshots.messages.shouldWarn()) {
2827
- // noinspection JSIgnoredPromiseFromCall
2828
- that.snapshots.fetchListing();
2829
- that.snapshots.logsModal();
2830
- return;
2831
- }
2832
- statusStop();
2833
- var logEntries = $(".wpstg--modal--process--logs").get(1).innerHTML;
2834
- var html = '<div class="wpstg--modal--process--logs">' + logEntries + '</div>';
2835
- var issueFound = html.includes('wpstg--modal--process--msg--warning') || html.includes('wpstg--modal--process--msg--error') ? 'Issues(s) found! ' : '';
2836
- console.log('errors found: ' + issueFound);
2837
- //var errorMessage = html.includes('wpstg--modal--process--msg--error') ? 'Errors(s) found! ' : '';
2838
- //var Message = warningMessage + errorMessage;
2839
-
2840
- //Swal.close();
2841
- Swal.fire({
2842
- icon: 'success',
2843
- title: 'Finished',
2844
- html: 'System restored from snapshot. <br/><span class="wpstg--modal--process--msg-found">'+issueFound+'</span><button class="wpstg--modal--process--logs--tail" data-txt-bad="">Show Logs</button><br/>' + html,
2845
- }
2846
- );
2847
-
2848
- // noinspection JSIgnoredPromiseFromCall
2849
- that.snapshots.fetchListing();
2850
- } else {
2851
- setTimeout(function () {
2852
- restoreSiteSnapshot(data);
2853
- }, wpstg.delayReq);
2854
- }
2855
- },
2856
- 'json',
2857
- false,
2858
- 0, // Don't retry upon failure
2859
- 1.25
2860
- );
2861
- }
2862
-
2863
- if (!that.snapshots.modal.import.html) {
2864
- const $modal = $('#wpstg--modal--snapshot--import');
2865
-
2866
- // Search & Replace Form
2867
- const $form = $modal.find('.wpstg--modal--snapshot--import--search-replace--input--container');
2868
- that.snapshots.modal.import.searchReplaceForm = $form.html();
2869
- $form.find('.wpstg--modal--snapshot--import--search-replace--input-group').remove();
2870
- $form.html(that.snapshots.modal.import.searchReplaceForm.replace(/{i}/g, 0));
2871
-
2872
- that.snapshots.modal.import.html = $modal.html();
2873
- that.snapshots.modal.import.baseDirectory = $modal.attr('data-baseDirectory');
2874
- that.snapshots.modal.import.btnTxtNext = $modal.attr('data-nextButtonText');
2875
- that.snapshots.modal.import.btnTxtConfirm = $modal.attr('data-confirmButtonText');
2876
- that.snapshots.modal.import.btnTxtCancel = $modal.attr('data-cancelButtonText');
2877
- $modal.remove();
2878
- }
2879
-
2880
- that.snapshots.modal.import.data.search = [];
2881
- that.snapshots.modal.import.data.replace = [];
2882
-
2883
- let $btnConfirm = null;
2884
- Swal
2885
- .mixin({
2886
- customClass: {
2887
- confirmButton: 'wpstg--btn--confirm wpstg-blue-primary wpstg-button wpstg-link-btn',
2888
- cancelButton: 'wpstg--btn--cancel wpstg-blue-primary wpstg-link-btn',
2889
- actions: 'wpstg--modal--actions',
2890
- },
2891
- buttonsStyling: false,
2892
- //progressSteps: ['1', '2']
2893
- })
2894
- .queue([{
2895
- html: that.snapshots.modal.import.html,
2896
- confirmButtonText: that.snapshots.modal.import.btnTxtNext,
2897
- showCancelButton: false,
2898
- showConfirmButton: true,
2899
- showLoaderOnConfirm: true,
2900
- width: 650,
2901
- onRender() {
2902
- $btnConfirm = $('.wpstg--modal--actions .swal2-confirm');
2903
- $btnConfirm.prop('disabled', true);
2904
-
2905
- that.snapshots.modal.import.containerUpload = $('.wpstg--modal--snapshot--import--upload');
2906
- that.snapshots.modal.import.containerFilesystem = $('.wpstg--modal--snapshot--import--filesystem');
2907
- },
2908
- preConfirm() {
2909
- const body = new FormData;
2910
- body.append('accessToken', wpstg.accessToken);
2911
- body.append('nonce', wpstg.nonce);
2912
- body.append('filePath', that.snapshots.modal.import.data.file);
2913
-
2914
- that.snapshots.modal.import.data.search.forEach((item, index) => {
2915
- body.append(`search[${index}]`, item);
2916
- });
2917
- that.snapshots.modal.import.data.replace.forEach((item, index) => {
2918
- body.append(`replace[${index}]`, item);
2919
- });
2920
-
2921
- return fetch(`${ajaxurl}?action=wpstg--snapshots--import--file-info`, {
2922
- method: 'POST',
2923
- body,
2924
- }).then(handleFetchErrors)
2925
- .then(res => res.json())
2926
- .then(html => {
2927
- return Swal.insertQueueStep({
2928
- html: html,
2929
- confirmButtonText: that.snapshots.modal.import.btnTxtConfirm,
2930
- cancelButtonText: that.snapshots.modal.import.btnTxtCancel,
2931
- showCancelButton: true,
2932
- });
2933
- })
2934
- .catch(e => showAjaxFatalError(e, '', 'Submit an error report.'))
2935
- ;
2936
- },
2937
- }])
2938
- .then(res => {
2939
- if (!res || !res.value || !res.value[1] || res.value[1] !== true) {
2940
- return;
2941
- }
2942
-
2943
- that.snapshots.isCancelled = false;
2944
- const data = that.snapshots.modal.import.data;
2945
- data['file'] = that.snapshots.modal.import.baseDirectory + data['file'];
2946
- data['reset'] = true;
2947
-
2948
- that.snapshots.process({
2949
- execute: () => {
2950
- that.snapshots.messages.reset();
2951
- restoreSiteSnapshot(data);
2952
- },
2953
- });
2954
- })
2955
- ;
2956
- },
2957
- };
2958
-
2959
- return that;
2960
- })(jQuery);
2961
-
2962
- jQuery(document).ready(function () {
2963
- WPStaging.init();
2964
- });
2965
-
2966
- /**
2967
- * Report Issue modal
2968
- */
2969
- jQuery(document).ready(function ($) {
2970
-
2971
- $('#wpstg-report-issue-button').click(function (e) {
2972
- $('.wpstg-report-issue-form').toggleClass('wpstg-report-show');
2973
- e.preventDefault();
2974
- });
2975
-
2976
- $('body').on('click', '#wpstg-snapshots-report-issue-button', function (e) {
2977
- $('.wpstg-report-issue-form').toggleClass('wpstg-report-show');
2978
- console.log('test');
2979
- e.preventDefault();
2980
- });
2981
-
2982
- $('#wpstg-report-cancel').click(function (e) {
2983
- $('.wpstg-report-issue-form').removeClass('wpstg-report-show');
2984
- e.preventDefault();
2985
- });
2986
-
2987
- /*
2988
- * Close Success Modal
2989
- */
2990
-
2991
- $('body').on('click', '#wpstg-success-button', function (e) {
2992
- e.preventDefault();
2993
- $('.wpstg-report-issue-form').removeClass('wpstg-report-show');
2994
- });
2995
-
2996
- function sendIssueReport(button, forceSend = 'false') {
2997
- var spinner = button.next();
2998
- var email = $('.wpstg-report-email').val();
2999
- var hosting_provider = $('.wpstg-report-hosting-provider').val();
3000
- var message = $('.wpstg-report-description').val();
3001
- var syslog = $('.wpstg-report-syslog').is(':checked');
3002
- var terms = $('.wpstg-report-terms').is(':checked');
3003
-
3004
- button.attr('disabled', true);
3005
- spinner.css('visibility', 'visible');
3006
-
3007
- $.ajax({
3008
- url: ajaxurl,
3009
- type: 'POST',
3010
- dataType: 'json',
3011
- async: true,
3012
- data: {
3013
- 'action': 'wpstg_send_report',
3014
- 'accessToken': wpstg.accessToken,
3015
- 'nonce': wpstg.nonce,
3016
- 'wpstg_email': email,
3017
- 'wpstg_provider': hosting_provider,
3018
- 'wpstg_message': message,
3019
- 'wpstg_syslog': +syslog,
3020
- 'wpstg_terms': +terms,
3021
- 'wpstg_force_send': forceSend
3022
- },
3023
- }).done(function (data) {
3024
- button.attr('disabled', false);
3025
- spinner.css('visibility', 'hidden');
3026
-
3027
- if (data.errors.length > 0) {
3028
- $('.wpstg-report-issue-form .wpstg-message').remove();
3029
-
3030
- var errorMessage = $('<div />').addClass('wpstg-message wpstg-error-message');
3031
- $.each(data.errors, function (key, value) {
3032
- if (value.status === 'already_submitted') {
3033
- errorMessage = '';
3034
- Swal.fire({
3035
- title: '',
3036
- customClass: {
3037
- container: 'wpstg-issue-resubmit-confirmation'
3038
- },
3039
- icon: 'warning',
3040
- html: value.message,
3041
- showCloseButton: true,
3042
- showCancelButton: true,
3043
- focusConfirm: false,
3044
- confirmButtonText: 'Yes',
3045
- cancelButtonText: 'No'
3046
- }).then((result) => {
3047
- if (result.isConfirmed) {
3048
- sendIssueReport(button, 'true');
3049
- }
3050
- })
3051
- } else {
3052
- errorMessage.append('<p>' + value + '</p>');
3053
- }
3054
- });
3055
-
3056
- $('.wpstg-report-issue-form').prepend(errorMessage);
3057
- } else {
3058
- var successMessage = $('<div />').addClass('wpstg-message wpstg-success-message');
3059
- successMessage.append('<p>Thanks for submitting your request! You should receive an auto reply mail with your ticket ID immediately for confirmation!<br><br>If you do not get that mail please contact us directly at <strong>support@wp-staging.com</strong></p>');
3060
-
3061
- $('.wpstg-report-issue-form').html(successMessage);
3062
- $('.wpstg-success-message').append('<div style="float:right;margin-top:10px;"><a id="wpstg-success-button" href="#">Close</a></div>');
3063
-
3064
- // Hide message
3065
- setTimeout(function () {
3066
- $('.wpstg-report-issue-form').removeClass('wpstg-report-active');
3067
- }, 2000);
3068
- }
3069
- });
3070
- }
3071
-
3072
- $('#wpstg-report-submit').click(function (e) {
3073
- var self = $(this);
3074
- sendIssueReport(self, 'false');
3075
- e.preventDefault();
3076
- });
3077
-
3078
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/public/vendor/sweetalert2/LICENSE DELETED
@@ -1,22 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2014 Tristan Edwards & Limon Monte
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/views/_main/footer.php CHANGED
@@ -12,7 +12,7 @@
12
  - <a href="https://wp-staging.com/docs/can-not-update-wp-staging-staging-site/" target="_blank" rel="external"><?php _e("Can not update WP Staging plugin", "wp-staging")?></a> <br/>
13
  - <a href="https://wp-staging.com/docs/page-not-found-error-404-after-pushing/" target="_blank" rel="external"><?php _e("Page not found – Error 404 after Pushing", "wp-staging")?></a> <br/>
14
  - <a href="https://wp-staging.com/docs/troubleshooting-try-this-first/" target="_blank" rel="external"><?php _e("More", "wp-staging")?></a><br/><br/>
15
- <?php echo __('Did not find a solution?','wp-staging');?>
16
  <br>
17
- <?php echo sprintf (__('Open a <a href="%s" target="_blank" rel="external nofollow">support ticket</a> and we will resolve it quickly.', 'wp-staging'), 'https://wp-staging.com/support');?>
18
  </div>
12
  - <a href="https://wp-staging.com/docs/can-not-update-wp-staging-staging-site/" target="_blank" rel="external"><?php _e("Can not update WP Staging plugin", "wp-staging")?></a> <br/>
13
  - <a href="https://wp-staging.com/docs/page-not-found-error-404-after-pushing/" target="_blank" rel="external"><?php _e("Page not found – Error 404 after Pushing", "wp-staging")?></a> <br/>
14
  - <a href="https://wp-staging.com/docs/troubleshooting-try-this-first/" target="_blank" rel="external"><?php _e("More", "wp-staging")?></a><br/><br/>
15
+ <?php echo __('Did not find a solution?', 'wp-staging');?>
16
  <br>
17
+ <?php echo sprintf(__('Open a <a href="%s" target="_blank" rel="external nofollow">support ticket</a> and we will resolve it quickly.', 'wp-staging'), 'https://wp-staging.com/support');?>
18
  </div>
Backend/views/_main/header.php CHANGED
@@ -1,9 +1,11 @@
1
  <span class="wpstg-logo">
2
- <img src="<?php echo $this->url . "img/logo_clean_small_212_25.png" ?>">
3
  </span>
4
 
5
  <span class="wpstg-version">
6
- <?php if (defined('WPSTGPRO_VERSION')) echo "Pro" ?> Version <?php echo WPStaging\Core\WPStaging::getVersion() ?>
 
 
7
  </span>
8
 
9
  <div class="wpstg-header">
@@ -32,7 +34,6 @@
32
  </div>
33
 
34
  <?php if ($_GET['page'] === 'wpstg_clone') { ?>
35
-
36
  <div class="wpstg-fs-14">
37
  <?php _e("Tutorial:", "wp-staging") ?> <a href="https://wp-staging.com/docs/copy-staging-site-to-live-site/" target="_blank"><?php _e("Push staging site to production website", "wp-staging") ?></a>
38
  </div>
1
  <span class="wpstg-logo">
2
+ <img src="<?php echo $this->assets->getAssetsUrl("img/logo_clean_small_212_25.png") ?>">
3
  </span>
4
 
5
  <span class="wpstg-version">
6
+ <?php if (defined('WPSTGPRO_VERSION')) {
7
+ echo "Pro";
8
+ } ?> Version <?php echo WPStaging\Core\WPStaging::getVersion() ?>
9
  </span>
10
 
11
  <div class="wpstg-header">
34
  </div>
35
 
36
  <?php if ($_GET['page'] === 'wpstg_clone') { ?>
 
37
  <div class="wpstg-fs-14">
38
  <?php _e("Tutorial:", "wp-staging") ?> <a href="https://wp-staging.com/docs/copy-staging-site-to-live-site/" target="_blank"><?php _e("Push staging site to production website", "wp-staging") ?></a>
39
  </div>
Backend/views/_main/report-issue.php CHANGED
@@ -12,9 +12,9 @@
12
  <label for="wpstg-report-syslog">
13
  <input type="checkbox" class="wpstg-report-syslog" id="wpstg-report-syslog">
14
  <?php echo sprintf(
15
- __('Optional: Submit the <a href="%s" target="_blank">System Log</a> and your WordPress debug log. This helps us to resolve your technical issues.','wp-staging'),
16
- admin_url().'admin.php?page=wpstg-tools&tab=system_info'
17
- ); ?>
18
  </label>
19
  </div>
20
  <div class="wpstg-field wpstg-report-privacy-policy">
@@ -26,7 +26,7 @@
26
  <div class="wpstg-field">
27
  <div class="wpstg-buttons">
28
  <button type="submit" id="wpstg-report-submit" class="wpstg-form-submit button-primary wpstg-button">
29
- <?php _e( 'Submit', 'wp-staging' ); ?>
30
  </button>
31
  <span class="spinner"></span>
32
  <a href="#" id="wpstg-report-cancel" class="wpstg-report-cancel">Close</a>
12
  <label for="wpstg-report-syslog">
13
  <input type="checkbox" class="wpstg-report-syslog" id="wpstg-report-syslog">
14
  <?php echo sprintf(
15
+ __('Optional: Submit the <a href="%s" target="_blank">System Log</a> and your WordPress debug log. This helps us to resolve your technical issues.', 'wp-staging'),
16
+ admin_url() . 'admin.php?page=wpstg-tools&tab=system_info'
17
+ ); ?>
18
  </label>
19
  </div>
20
  <div class="wpstg-field wpstg-report-privacy-policy">
26
  <div class="wpstg-field">
27
  <div class="wpstg-buttons">
28
  <button type="submit" id="wpstg-report-submit" class="wpstg-form-submit button-primary wpstg-button">
29
+ <?php _e('Submit', 'wp-staging'); ?>
30
  </button>
31
  <span class="spinner"></span>
32
  <a href="#" id="wpstg-report-cancel" class="wpstg-report-cancel">Close</a>
Backend/views/backup/listing-single-backup.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @var \WPStaging\Framework\TemplateEngine\TemplateEngine $this
5
+ * @var \WPStaging\Pro\Backup\Entity\ListableBackup $backup
6
+ */
7
+ $name = $backup->backupName;
8
+ $downloadUrl = $backup->downloadUrl;
9
+ $notes = $backup->notes;
10
+ $createdAt = $backup->dateCreatedFormatted;
11
+ $size = $backup->size;
12
+ $id = $backup->id;
13
+ $automatedBackup = $backup->automatedBackup;
14
+ $legacy = $backup->legacy;
15
+ ?>
16
+ <li id="<?php echo esc_attr($id) ?>" class="wpstg-clone wpstg-backup">
17
+
18
+ <div class="wpstg-clone-header">
19
+ <span class="wpstg-clone-title">
20
+ <?php echo esc_html($name); ?>
21
+ </span>
22
+ <div class="wpstg-clone-actions">
23
+ <div class="wpstg-dropdown wpstg-action-dropdown">
24
+ <a href="#" class="wpstg-dropdown-toggler transparent">
25
+ <?php _e("Actions", "wp-staging"); ?>
26
+ </a>
27
+ <div class="wpstg-dropdown-menu">
28
+ <a href="#" class="wpstg-clone-action wpstg--backup--import"
29
+ data-filePath="<?php echo esc_attr($backup->fullPath) ?>"
30
+ data-title="<?php esc_attr_e('Import Backup', 'wp-staging') ?>"
31
+ title="<?php esc_attr_e('Import Backup', 'wp-staging') ?>">
32
+ <?php esc_html_e('Import', 'wp-staging') ?>
33
+ </a>
34
+ <a href="#" class="wpstg--backup--download wpstg-merge-clone wpstg-clone-action"
35
+ data-md5="<?php echo esc_attr($backup->md5BaseName) ?>"
36
+ data-url="<?php echo esc_url($downloadUrl ?: '') ?>"
37
+ data-title="<?php esc_attr_e('Download Backup', 'wp-staging') ?>"
38
+ data-title-export="<?php esc_attr_e('Exporting Database Tables...', 'wp-staging') ?>"
39
+ data-btn-cancel-txt="<?php esc_attr_e('CANCEL', 'wp-staging') ?>"
40
+ data-btn-download-txt="<?php esc_attr_e($downloadUrl ? 'Download' : 'Export & Download', 'wp-staging') ?>"
41
+ title="<?php esc_attr_e('Download backup file on local system', 'wp-staging') ?>">
42
+ <?php esc_html_e('Download', 'wp-staging') ?>
43
+ </a>
44
+ <a href="#" class="wpstg--backup--edit wpstg-clone-action"
45
+ data-md5="<?php echo esc_attr($backup->md5BaseName); ?>"
46
+ data-name="<?php echo esc_attr($name); ?>"
47
+ data-notes="<?php echo esc_attr($notes); ?>"
48
+ title="<?php esc_attr_e('Edit backup name and / or notes', 'wp-staging') ?>">
49
+ <?php esc_html_e('Edit', 'wp-staging') ?>
50
+ </a>
51
+ <a href="#" class="wpstg-remove-clone wpstg-clone-action wpstg-delete-backup"
52
+ data-md5="<?php echo esc_attr($backup->md5BaseName) ?>"
53
+ title="<?php esc_attr_e('Delete this backup. This action can not be undone!', 'wp-staging') ?>">
54
+ <?php esc_html_e('Delete', 'wp-staging') ?>
55
+ </a>
56
+ <?php
57
+ do_action('wpstg.views.backup.listing.single.after_actions', $backup);
58
+ ?>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+
64
+ <div class="wpstg-staging-info">
65
+ <ul>
66
+ <li><strong>Id:</strong> <?php esc_html_e($id); ?></li>
67
+ <li>
68
+ <strong><?php esc_html_e('Created on:', 'wp-staging') ?></strong>
69
+ <?php echo esc_html($this->transformToWpFormat(new DateTime($createdAt))); ?>
70
+ </li>
71
+ <?php if ($notes) : ?>
72
+ <li>
73
+ <strong><?php esc_html_e('Notes:', 'wp-staging') ?></strong><br/>
74
+ <?php echo esc_html(nl2br($notes)); ?>
75
+ </li>
76
+ <?php endif ?>
77
+ <li>
78
+ <strong><?php esc_html_e('Size: ', 'wp-staging') ?></strong>
79
+ <?php echo esc_html($size); ?>
80
+ </li>
81
+ <li class="single-backup-includes">
82
+ <strong><?php esc_html_e('Contains: ', 'wp-staging') ?></strong>
83
+ <ul class="wpstg-import-backup-contains wpstg-listing-single-backup">
84
+ <?php if ($backup->isExportingDatabase) : ?>
85
+ <li><span class="dashicons dashicons-database wpstg--tooltip"><div class='wpstg--tooltiptext'>Database</div></span></li>
86
+ <?php endif; ?>
87
+ <?php if ($backup->isExportingPlugins) : ?>
88
+ <li><span class="dashicons dashicons-admin-plugins wpstg--tooltip"><div class='wpstg--tooltiptext'>Plugins</div></span></li>
89
+ <?php endif; ?>
90
+ <?php if ($backup->isExportingMuPlugins) : ?>
91
+ <li><span class="dashicons dashicons-plugins-checked wpstg--tooltip"><div class='wpstg--tooltiptext'>Mu-plugins</div></span></li>
92
+ <?php endif; ?>
93
+ <?php if ($backup->isExportingThemes) : ?>
94
+ <li><span class="dashicons dashicons-layout wpstg--tooltip"><div class='wpstg--tooltiptext'>Themes</div></span></li>
95
+ <?php endif; ?>
96
+ <?php if ($backup->isExportingUploads) : ?>
97
+ <li><span class="dashicons dashicons-images-alt wpstg--tooltip"><div class='wpstg--tooltiptext'>Uploads</div></span></li>
98
+ <?php endif; ?>
99
+ <?php if ($backup->isExportingOtherWpContentFiles) : ?>
100
+ <li><span class="dashicons dashicons-admin-generic wpstg--tooltip"><div class='wpstg--tooltiptext'>Other files in wp-content</div></span></li>
101
+ <?php endif; ?>
102
+ </ul>
103
+ </li>
104
+ <?php if ($automatedBackup) : ?>
105
+ <li style="font-style: italic">
106
+ <span class="dashicons dashicons-database"></span> <?php esc_html_e('This database backup was automatically created before pushing a staging site to production.', 'wp-staging') ?>
107
+ </li>
108
+ <?php endif ?>
109
+ <?php if ($legacy) : ?>
110
+ <li style="font-style: italic">
111
+ <span class="dashicons dashicons-hourglass"></span> <?php esc_html_e('This database backup was automatically converted from an existing legacy WPSTAGING Database export in the .SQL format.', 'wp-staging') ?>
112
+ </li>
113
+ <?php endif ?>
114
+ </ul>
115
+ </div>
116
+ </li>
Backend/views/backup/listing.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use WPStaging\Framework\TemplateEngine\TemplateEngine;
4
+ use WPStaging\Framework\Adapter\Directory;
5
+
6
+ /**
7
+ * @see \WPStaging\Pro\Backup\Ajax\Listing::render
8
+ *
9
+ * @var TemplateEngine $this
10
+ * @var array $directories
11
+ * @var string $urlPublic
12
+ * @var Directory $directory
13
+ */
14
+ ?>
15
+
16
+ <div id="wpstg-step-1">
17
+ <button id="wpstg-new-backup" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button"
18
+ data-action="wpstg--backups--export">
19
+ <?php esc_html_e('Backup & Export', 'wp-staging') ?>
20
+ </button>
21
+ <button id="wpstg-import-backup" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button"
22
+ data-action="wpstg--backups--import">
23
+ <?php esc_html_e('Import', 'wp-staging') ?>
24
+ </button>
25
+ </div>
26
+
27
+ <div id="wpstg-existing-backups">
28
+ <div style="display: flex; flex-direction: row; justify-content: space-between">
29
+ <h3><?php _e('Your Backups:', 'wp-staging') ?></h3>
30
+ </div>
31
+ <div class="wpstg-backup-list">
32
+ <ul>
33
+ <li><?php _e('Searching for existing backups...', 'wp-staging') ?></li>
34
+ </ul>
35
+ </div>
36
+ </div>
37
+
38
+ <?php include(__DIR__ . '/modal/export.php'); ?>
39
+ <?php include(__DIR__ . '/modal/progress.php'); ?>
40
+ <?php include(__DIR__ . '/modal/download.php'); ?>
41
+ <?php include(__DIR__ . '/modal/import.php'); ?>
42
+
43
+ <div
44
+ id="wpstg--js--translations"
45
+ style="display:none;"
46
+ data-modal-txt-critical="<?php esc_attr_e('Critical', 'wp-staging') ?>"
47
+ data-modal-txt-errors="<?php esc_attr_e('Error(s)', 'wp-staging') ?>"
48
+ data-modal-txt-warnings="<?php esc_attr_e('Warning(s)', 'wp-staging') ?>"
49
+ data-modal-txt-and="<?php esc_attr_e('and', 'wp-staging') ?>"
50
+ data-modal-txt-found="<?php esc_attr_e('Found', 'wp-staging') ?>"
51
+ data-modal-txt-show-logs="<?php esc_attr_e('Show Logs', 'wp-staging') ?>"
52
+ data-modal-logs-title="<?php esc_attr_e(
53
+ '{critical} Critical, {errors} Error(s) and {warnings} Warning(s) Found',
54
+ 'wp-staging'
55
+ ) ?>"
56
+ ></div>
57
+
58
+ <div id="wpstg-delete-confirmation"></div>
Backend/views/backup/modal/download.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <div id="wpstg--modal--backup--download" style="display: none">
2
+ <h2>{title}</h2>
3
+ <div class="wpstg--modal--download--logs--wrapper" style="display:none">
4
+ <button class="wpstg--modal--process--logs--tail">{btnTxtLog}</button>
5
+ <div class="wpstg--modal--process--logs"></div>
6
+ </div>
7
+ </div>
Backend/views/backup/modal/export.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var \WPStaging\Framework\Adapter\Directory $directories
4
+ */
5
+ ?>
6
+ <div id="wpstg--modal--backup--new" data-confirmButtonText="<?php esc_attr_e('Start Backup', 'wp-staging') ?>" style="display: none">
7
+ <label for="wpstg-backup-name-input"><?php esc_html_e('Backup & Export', 'wp-staging') ?></label>
8
+ <input id="wpstg-backup-name-input" name="backup_name" class="swal2-input" placeholder="<?php esc_attr_e('Name your backup for better distinction', 'wp-staging') ?>">
9
+
10
+ <div class="wpstg-advanced-options" style="text-align: left;">
11
+
12
+ <!-- EXPORT CHECKBOXES -->
13
+ <div class="wpstg-advanced-options-site" style="padding-left: .75em;">
14
+ <label style="display: block;margin: .5em 0;">
15
+ <input type="checkbox" name="includedDirectories[]" id="includeMediaLibraryInBackup" value="<?php echo esc_attr($directories['uploads']); ?>" checked/>
16
+ <?php esc_html_e('Export Media Library', 'wp-staging') ?>
17
+ </label>
18
+ <label style="display: block;margin: .5em 0;">
19
+ <input type="checkbox" name="includedDirectories[]" id="includeThemesInBackup" value="<?php echo esc_attr($directories['themes']); ?>" checked/>
20
+ <?php esc_html_e('Export Themes', 'wp-staging') ?>
21
+ </label>
22
+ <label style="display: block;margin: .5em 0;">
23
+ <input type="checkbox" name="includedDirectories[]" id="includeMuPluginsInBackup" value="<?php echo esc_attr($directories['muPlugins']); ?>" checked/>
24
+ <?php esc_html_e('Export Must-Use Plugins', 'wp-staging') ?>
25
+ </label>
26
+ <label style="display: block;margin: .5em 0;">
27
+ <input type="checkbox" name="includedDirectories[]" id="includePluginsInBackup" value="<?php echo esc_attr($directories['plugins']); ?>" checked/>
28
+ <?php esc_html_e('Export Plugins', 'wp-staging') ?>
29
+ </label>
30
+ <label style="display: block;margin: .5em 0;">
31
+ <input type="checkbox" name="includeOtherFilesInWpContent" id="includeOtherFilesInWpContent" value="true" checked/>
32
+ <?php esc_html_e('Export Other Files In wp-content', 'wp-staging') ?>
33
+ <div class="wpstg--tooltip">
34
+ <span class="dashicons dashicons-info-outline"></span>
35
+ <span class="wpstg--tooltiptext wpstg--tooltiptext-backups">
36
+ <p>
37
+ <?php esc_html_e('Export files at wp-content that are not plugins, themes, mu-plugins or uploads. Eg: Cache and database drop-ins, etc. Recommended for full-site backups.', 'wp-staging') ?>
38
+ </p>
39
+ </span>
40
+ </div>
41
+ </label>
42
+ <label style="display: block;margin: .5em 0;">
43
+ <input type="checkbox" name="export_database" id="includeDatabaseInBackup" value="true" checked/>
44
+ <?php esc_html_e('Export Database', 'wp-staging') ?>
45
+ <div id="exportUploadsWithoutDatabaseWarning" style="display:none;">
46
+ <?php esc_html_e('When exporting the Media Library without the Database, the attachments will be migrated but won\'t show up in the media library after import.', 'wp-staging'); ?>
47
+ </div>
48
+ </label>
49
+ <input type="hidden" name="wpContentDir" value="<?php echo esc_attr($directories['wpContent']); ?>"/>
50
+ <input type="hidden" name="wpStagingDir" value="<?php echo esc_attr($directories['wpStaging']); ?>"/>
51
+ <?php unset($directories['wpContent'], $directories['wpStaging']) ?>
52
+ <input type="hidden" name="availableDirectories" value="<?php echo esc_attr(implode('|', $directories)); ?>"/>
53
+ </div>
54
+
55
+ <!-- ADVANCED OPTIONS DROPDOWN -->
56
+ <div class="wpstg-advanced-options-dropdown-wrapper">
57
+ <a href="#" class="wpstg--tab--toggle" data-target=".wpstg-advanced-options-dropdown" style="text-decoration: none;">
58
+ <span style="margin-right: .25em">►</span>
59
+ <?php esc_html_e('Advanced Options', 'wp-staging') ?>
60
+ </a>
61
+
62
+ <div class="wpstg-advanced-options-dropdown" style="display:none; padding-left: .75em;">
63
+ Advanced options
64
+ </div>
65
+ </div>
66
+
67
+ </div>
68
+ </div>
Backend/views/backup/modal/import.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var \WPStaging\Framework\Adapter\Directory $directory
4
+ */
5
+ ?>
6
+ <div
7
+ id="wpstg--modal--backup--import"
8
+ data-confirmButtonText="<?php esc_attr_e('IMPORT', 'wp-staging'); ?>"
9
+ data-nextButtonText="<?php esc_attr_e('NEXT', 'wp-staging'); ?>"
10
+ data-cancelButtonText="<?php esc_attr_e('CANCEL', 'wp-staging'); ?>"
11
+ data-baseDirectory="<?php echo esc_attr($directory->getPluginUploadsDirectory()); ?>"
12
+ style="display: none"
13
+ >
14
+ <h2 class="wpstg--modal--backup--import--upload--title"><?php esc_html_e('Import Backup', 'wp-staging') ?></h2>
15
+ <div style="padding: .75em; margin: 1em auto;">
16
+ <?php include(__DIR__ . '/partials/import-upload.php'); ?>
17
+ <?php include(__DIR__ . '/partials/import-filesystem.php'); ?>
18
+ <?php include(__DIR__ . '/partials/import-configure.php'); ?>
19
+ </div>
20
+ </div>
Backend/views/backup/modal/partials/import-configure.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var string $urlPublic
4
+ */
5
+ ?>
6
+ <div class="wpstg--modal--backup--import--configure">
7
+ <div class="wpstg--modal--backup--import--configure-review-backup">
8
+ Reviewing backup.
9
+ </div>
10
+ <!-- DATABASE SEARCH AND REPLACE -->
11
+ <div class="wpstg--modal--backup--import--search-replace--wrapper">
12
+ <a href="#" class="wpstg--tab--toggle" data-target=".wpstg--import--database-search-and-replace" style="text-decoration: none;">
13
+ <span style="margin-right: .25em">►</span>
14
+ <?php esc_html_e('Database Search and Replace', 'wp-staging') ?>
15
+ </a>
16
+
17
+ <div class="wpstg--import--database-search-and-replace" style="display:none; padding-left: .75em;">
18
+ <div class="wpstg--modal--backup--import--search-replace--info">
19
+ <p><?php esc_html_e('You can do a search-and-replace of the values of the database being imported. This is optional, as WPSTAGING already takes care of replacing the Site URL and ABSPATH for you. You should avoid doing replacements of generic strings, as this could replace unwanted values and cause issues in the database.', 'wp-staging') ?></p>
20
+ </div>
21
+ <div class="wpstg--modal--backup--import--search-replace--input--container">
22
+ <div class="wpstg--modal--backup--import--search-replace--input-group">
23
+ <input name="wpstg__backup__import__search[{i}]" data-index="{i}" class="wpstg--backup--import--search" placeholder="Search"/>
24
+ <input name="wpstg__backup__import__replace[{i}]" data-index="{i}" class="wpstg--backup--import--replace" placeholder="Replace"/>
25
+ <button class="wpstg--import--advanced-options--button wpstg--modal--backup--import--search-replace--remove"><?php esc_html_e('-', 'wp-staging') ?></button>
26
+ </div>
27
+ </div>
28
+ <div class="wpstg--modal--backup--import--search-replace--new--wrapper">
29
+ <button class="wpstg--import--advanced-options--button wpstg--modal--backup--import--search-replace--new"><?php esc_html_e('+', 'wp-staging') ?></button>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </div>
Backend/views/backup/modal/partials/import-filesystem.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var \WPStaging\Framework\Adapter\Directory $directory
4
+ */
5
+ ?>
6
+ <div class="wpstg--modal--backup--import--filesystem">
7
+ <button class="wpstg--backup--import--option wpstg-blue-primary" data-option="upload">
8
+ <?php esc_html_e('GO BACK', 'wp-staging') ?>
9
+ </button>
10
+ <div style="margin-top: .25em;font-size:14px;">
11
+ <?php
12
+ echo __('Upload import file to server directory:', 'wp-staging') . '<br>';
13
+ echo esc_html($directory->getPluginUploadsDirectory());
14
+ ?>
15
+ </div>
16
+ <ul></ul>
17
+ </div>
Backend/views/backup/modal/partials/import-upload.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var string $urlAssets
4
+ */
5
+ ?>
6
+ <div class="wpstg--modal--backup--import--upload">
7
+ <div class="wpstg--modal--backup--import--upload--container">
8
+ <div class="wpstg--uploader">
9
+ <input type="file" name="wpstg--backup--import--upload--file" accept=".wpstg"/>
10
+ <img src="<?php echo esc_url($urlAssets . 'img/upload.svg'); ?>" alt="Upload Image"/>
11
+ <span class="wpstg--backup--import--selected-file"></span>
12
+ <span class="wpstg--drag-or-upload">
13
+ <?php esc_html_e('Drag a new export file here or choose another option', 'wp-staging') ?>
14
+ </span>
15
+ <span class="wpstg--drag">
16
+ <?php esc_html_e('Drag and Drop a WPSTAGING Backup file to start import', 'wp-staging') ?>
17
+ <br>
18
+ <small><?php esc_html_e('You can upload a file of any size here', 'wp-staging') ?></small>
19
+ </span>
20
+ <span class="wpstg--drop">
21
+ <?php esc_html_e('Drop export file here', 'wp-staging') ?>
22
+ </span>
23
+ <div class="wpstg--backup--import--options">
24
+ <button
25
+ class="wpstg-blue-primary wpstg-button wpstg-link-btn wpstg--backup--import--choose-option"
26
+ data-txtOther="<?php esc_attr_e('Import from', 'wp-staging') ?>"
27
+ data-txtChoose="<?php esc_attr_e('Choose an Option', 'wp-staging') ?>"
28
+ >
29
+ <?php esc_html_e('Import from', 'wp-staging') ?>
30
+ </button>
31
+ <ul>
32
+ <li>
33
+ <button class="wpstg--backup--import--option wpstg-blue-primary" data-option="file">
34
+ <?php esc_html_e('Local Computer', 'wp-staging') ?>
35
+ </button>
36
+ </li>
37
+ <li>
38
+ <button class="wpstg--backup--import--option wpstg-blue-primary" data-option="filesystem">
39
+ <?php esc_html_e('Existing Backups', 'wp-staging') ?>
40
+ </button>
41
+ </li>
42
+ </ul>
43
+ </div>
44
+ </div>
45
+ <div class="wpstg--modal--import--upload--process">
46
+ <div class="wpstg--modal--import--upload--progress"></div>
47
+ <h4 class="wpstg--modal--import--upload--progress--title">
48
+ <?php echo sprintf(esc_html__('Uploading %s%%...', 'wp-staging'), '<span></span>') ?>
49
+ </h4>
50
+ </div>
51
+ </div>
52
+ <div
53
+ class="wpstg--modal--backup--import--upload--status"
54
+ data-txt-uploading="<?php esc_html_e('Uploading...', 'wp-staging') ?>"
55
+ data-txt-done="<?php esc_html_e('Uploaded Successfully', 'wp-staging') ?>"
56
+ data-txt-error="<?php esc_html_e('Error! {message}', 'wp-staging') ?>"
57
+ >
58
+ </div>
59
+ </div>
Backend/views/backup/modal/progress.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="wpstg--modal--backup--process" data-cancelButtonText="<?php esc_attr_e('CANCEL', 'wp-staging') ?>" style="display: none">
2
+ <span class="wpstg-loader"></span>
3
+ <h3 class="wpstg--modal--process--title" style="color: #a8a8a8;margin: .25em 0;">
4
+ <?php esc_html_e('Processing...', 'wp-staging') ?>
5
+ </h3>
6
+ <div style="margin: .5em 0; color: #a8a8a8;">
7
+ <?php
8
+ echo sprintf(
9
+ esc_html__('Progress %s - Elapsed time %s', 'wp-staging'),
10
+ '<span class="wpstg--modal--process--percent">0</span>%',
11
+ '<span class="wpstg--modal--process--elapsed-time">0:00</span>'
12
+ )
13
+ ?>
14
+ </div>
15
+ <div class="wpstg--modal--process--generic-problem"></div>
16
+ <button
17
+ class="wpstg--modal--process--logs--tail"
18
+ data-txt-bad="<?php echo sprintf(
19
+ esc_attr__('(%s) Critical, (%s) Errors, (%s) Warnings. Show Logs', 'wp-staging'),
20
+ '<span class=\'wpstg--modal--logs--critical-count\'>0</span>',
21
+ '<span class=\'wpstg--modal--logs--error-count\'>0</span>',
22
+ '<span class=\'wpstg--modal--logs--warning-count\'>0</span>'
23
+ ) ?>"
24
+ >
25
+ <?php _e('Show Logs', 'wp-staging') ?>
26
+ </button>
27
+ <div class="wpstg--modal--process--logs"></div>
28
+ </div>
Backend/views/backup/site/info.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use WPStaging\Pro\Backup\Entity\BackupMetadata;
4
+
5
+ /**
6
+ * @var BackupMetadata $info
7
+ */
8
+
9
+ ?>
10
+ <div id="wpstg-confirm-backup-restore-wrapper">
11
+ <div class="wpstg-confirm-backup-restore-header">
12
+ <h3 style="margin:0;"><?php _e('This will restore your website! </br> Are you sure?', 'wp-staging'); ?></h3>
13
+ </div>
14
+ <div id="wpstg-confirm-backup-restore-data">
15
+ <ul>
16
+ <?php if ($info->getIsExportingDatabase()) : ?>
17
+ <li style="list-style-type: square;"><?php _e('Database will be replaced.', 'wp-staging'); ?></li>
18
+ <?php endif; ?>
19
+ <?php if ($info->getIsExportingPlugins()) : ?>
20
+ <li style="list-style-type: square;"><?php _e('Plugins will be added.', 'wp-staging') ?></li>
21
+ <?php endif; ?>
22
+ <?php if ($info->getIsExportingThemes()) : ?>
23
+ <li style="list-style-type: square;"><?php _e('Themes will be added.', 'wp-staging') ?></li>
24
+ <?php endif; ?>
25
+ <?php if ($info->getIsExportingMuPlugins()) : ?>
26
+ <li style="list-style-type: square;"><?php _e('Mu-plugins will be added.', 'wp-staging') ?></li>
27
+ <?php endif; ?>
28
+ <?php if ($info->getIsExportingUploads()) : ?>
29
+ <li style="list-style-type: square;"><?php _e('Media files and images will be added. ', 'wp-staging') ?></li>
30
+ <?php endif; ?>
31
+ <?php if ($info->getIsExportingOtherWpContentFiles()) : ?>
32
+ <li style="list-style-type: square;"><?php _e('Other files in wp-content folder will be added. ', 'wp-staging') ?></li>
33
+ <?php endif; ?>
34
+ </ul>
35
+ <?php if (!empty($info->getTotalFiles())) : ?>
36
+ <div class="wpstg-db-table" style="margin-top:5px;">
37
+ <strong><?php _e('Total Files:', 'wp-staging') ?></strong>
38
+ <span class=""><?php echo $info->getTotalFiles() ?></span>
39
+ </div>
40
+ <?php endif; ?>
41
+ <?php if (!empty($_POST['search'])) : ?>
42
+ <div class="wpstg-db-table" style="margin-top:20px;">
43
+ <?php foreach ($_POST['search'] as $index => $search) : ?>
44
+ <span class=""><?php echo sprintf(__('Search: %s', 'wp-staging'), $search) ?></span> <br/>
45
+ <span class=""><?php echo sprintf(__('Replace: %s', 'wp-staging'), $_POST['replace'][$index]) ?></span>
46
+ <hr>
47
+ <?php endforeach ?>
48
+ </div>
49
+ <?php endif ?>
50
+ <div class="wpstg-db-table" style="margin-top:5px;display:none;">
51
+ <?php
52
+ $backupGeneratedInVersion = $info->getVersion();
53
+ $thisVersion = \WPStaging\Core\WPStaging::getVersion();
54
+ // Use this in the future if we need to warn the user about compatibility issues between export version and current version.
55
+ ?>
56
+ <small><?php _e(sprintf('This backup was generated on WP STAGING %s. You are running WP STAGING %s.', $info->getVersion(), \WPStaging\Core\WPStaging::getVersion()), 'wp-staging') ?></small>
57
+ </div>
58
+ </div>
59
+ </div>
Backend/views/clone/ajax/custom-directory.php CHANGED
@@ -1,16 +1,18 @@
1
  <?php
 
2
  /**
3
  * This file is currently being called only for the Free version:
4
  * src/Backend/views/clone/ajax/scan.php:113
5
  *
6
  * @file src/Backend/Pro/views/clone/ajax/custom-directory.php For the Pro counterpart.
7
  */
 
8
  ?>
9
  <fieldset disabled style="opacity:0.8;border-top: 1px solid white;margin-top: 20px;">
10
  <p>
11
- <strong style="font-size: 14px;"> <?php _e( 'Copy Staging Site to Custom Directory', 'wp-staging' ); ?></strong>
12
  <br>
13
- <?php _e( 'Path must be writeable by PHP and an absolute path like <code>/www/public_html/dev</code>.', 'wp-staging' ); ?>
14
  </p>
15
  <div class="wpstg-form-group wpstg-text-field">
16
  <label><?php _e('Target Directory: ', 'wp-staging') ?> </label>
@@ -29,14 +31,14 @@
29
  </fieldset>
30
  <fieldset disabled class="wpstg-fieldset">
31
  <p>
32
- <strong class="wpstg-fs-14"> <?php _e( 'Symlink Upload Folder', 'wp-staging' ); ?></strong>
33
  <br/>
34
- <p><?php _e( 'Activating will symlink the upload folder with the production site. All images and content on the production site uploads folder will be linked to the staging site folder. This will speed up the cloning and pushing process tremendously as no images and other data is copied between both sites.', 'wp-staging' ); ?></p>
35
- <p><?php _e( 'Note: This feature will only work if the staging site is on the same hosting as the production site.', 'wp-staging' ); ?></p>
36
  </p>
37
  <div class="wpstg-form-group">
38
  <label class="wpstg-checkbox" for="wpstg_symlink_upload">
39
- <?php _e( 'Symlink Upload Folder:', 'wp-staging' ); ?>
40
  <input disabled type="checkbox" name="wpstg_symlink_upload" id="wpstg_symlink_upload" value="true" title="wpstg_symlink_upload">
41
  </label>
42
  </div>
1
  <?php
2
+
3
  /**
4
  * This file is currently being called only for the Free version:
5
  * src/Backend/views/clone/ajax/scan.php:113
6
  *
7
  * @file src/Backend/Pro/views/clone/ajax/custom-directory.php For the Pro counterpart.
8
  */
9
+
10
  ?>
11
  <fieldset disabled style="opacity:0.8;border-top: 1px solid white;margin-top: 20px;">
12
  <p>
13
+ <strong style="font-size: 14px;"> <?php _e('Copy Staging Site to Custom Directory', 'wp-staging'); ?></strong>
14
  <br>
15
+ <?php _e('Path must be writeable by PHP and an absolute path like <code>/www/public_html/dev</code>.', 'wp-staging'); ?>
16
  </p>
17
  <div class="wpstg-form-group wpstg-text-field">
18
  <label><?php _e('Target Directory: ', 'wp-staging') ?> </label>
31
  </fieldset>
32
  <fieldset disabled class="wpstg-fieldset">
33
  <p>
34
+ <strong class="wpstg-fs-14"> <?php _e('Symlink Upload Folder', 'wp-staging'); ?></strong>
35
  <br/>
36
+ <p><?php _e('Activating will symlink the upload folder with the production site. All images and content on the production site uploads folder will be linked to the staging site folder. This will speed up the cloning and pushing process tremendously as no images and other data is copied between both sites.', 'wp-staging'); ?></p>
37
+ <p><?php _e('Note: This feature will only work if the staging site is on the same hosting as the production site.', 'wp-staging'); ?></p>
38
  </p>
39
  <div class="wpstg-form-group">
40
  <label class="wpstg-checkbox" for="wpstg_symlink_upload">
41
+ <?php _e('Symlink Upload Folder:', 'wp-staging'); ?>
42
  <input disabled type="checkbox" name="wpstg_symlink_upload" id="wpstg_symlink_upload" value="true" title="wpstg_symlink_upload">
43
  </label>
44
  </div>
Backend/views/clone/ajax/delete-confirmation.php CHANGED
@@ -2,24 +2,24 @@
2
  <div class="wpstg-notice-alert">
3
  <h3 class="wpstg-m-0 wpstg-pb-5px">
4
  <?php
5
- _e("This staging site will be deleted:", "wp-staging")
6
  ?>
7
  </h3>
8
 
9
  <p>
10
- <?php _e('Clone Name:', 'wp-staging'); ?>
11
  <span class="wpstg-confirmation-label">
12
- <?php
13
- echo $clone->directoryName;
14
  ?>
15
  </span>
16
  </p>
17
  <p>
18
- <?php _e('Database Name:', 'wp-staging'); ?>
19
  <span class="wpstg-confirmation-label">
20
- <?php
21
  $database = empty($clone->databaseDatabase) ? "{$dbname} / Main Database)" : $clone->databaseDatabase;
22
- echo $database;
23
  ?>
24
  </span>
25
  </p>
@@ -28,7 +28,8 @@
28
 
29
  <?php if (!$isDatabaseConnected) { ?>
30
  <div class="wpstg-notice-alert wpstg-failed">
31
- <h4 class="wpstg-mb-0"><?php _e('Error: Can not connect to external database: ', 'wp-staging'); echo $clone->databaseDatabase; ?></h4>
 
32
  <ul class="wpstg-mb-0">
33
  <li><?php _e('This can happen if the password of the external database has been changed or if the database was deleted', 'wp-staging') ?></li>
34
  <li><?php _e('You can still delete this staging site but deleting this site will not delete any table or database. You will have to delete them manually if they exist.', 'wp-staging') ?></li>
@@ -55,15 +56,15 @@
55
  </a>
56
  </div>
57
 
58
- <?php foreach ($delete->getTables() as $table):?>
59
  <div class="wpstg-db-table">
60
  <label>
61
  <input class="wpstg-db-table-checkboxes" type="checkbox" name="<?php echo $table->name?>" checked>
62
  <?php echo $table->name?>
63
  </label>
64
  <span class="wpstg-size-info">
65
- <?php echo isset($table->size) ? $table->size : '';?>
66
- </span>
67
  </div>
68
  <?php endforeach ?>
69
  <div class="wpstg-my-6px">
2
  <div class="wpstg-notice-alert">
3
  <h3 class="wpstg-m-0 wpstg-pb-5px">
4
  <?php
5
+ _e("Do you really want to delete the staging site? This can not be undone:", "wp-staging")
6
  ?>
7
  </h3>
8
 
9
  <p>
10
+ <?php _e('Staging Site:', 'wp-staging'); ?>
11
  <span class="wpstg-confirmation-label">
12
+ <?php
13
+ echo $clone->directoryName;
14
  ?>
15
  </span>
16
  </p>
17
  <p>
18
+ <?php _e('Database:', 'wp-staging'); ?>
19
  <span class="wpstg-confirmation-label">
20
+ <?php
21
  $database = empty($clone->databaseDatabase) ? "{$dbname} / Main Database)" : $clone->databaseDatabase;
22
+ echo $database;
23
  ?>
24
  </span>
25
  </p>
28
 
29
  <?php if (!$isDatabaseConnected) { ?>
30
  <div class="wpstg-notice-alert wpstg-failed">
31
+ <h4 class="wpstg-mb-0"><?php _e('Error: Can not connect to external database: ', 'wp-staging');
32
+ echo $clone->databaseDatabase; ?></h4>
33
  <ul class="wpstg-mb-0">
34
  <li><?php _e('This can happen if the password of the external database has been changed or if the database was deleted', 'wp-staging') ?></li>
35
  <li><?php _e('You can still delete this staging site but deleting this site will not delete any table or database. You will have to delete them manually if they exist.', 'wp-staging') ?></li>
56
  </a>
57
  </div>
58
 
59
+ <?php foreach ($delete->getTables() as $table) :?>
60
  <div class="wpstg-db-table">
61
  <label>
62
  <input class="wpstg-db-table-checkboxes" type="checkbox" name="<?php echo $table->name?>" checked>
63
  <?php echo $table->name?>
64
  </label>
65
  <span class="wpstg-size-info">
66
+ <?php echo isset($table->size) ? $table->size : '';?>
67
+ </span>
68
  </div>
69
  <?php endforeach ?>
70
  <div class="wpstg-my-6px">
Backend/views/clone/ajax/external-database.php CHANGED
@@ -1,10 +1,12 @@
1
  <?php
 
2
  /**
3
  * This file is currently being called only for the Free version:
4
  * src/Backend/views/clone/ajax/scan.php:113
5
  *
6
  * @file src/Backend/Pro/views/clone/ajax/external-database.php For the Pro counterpart.
7
  */
 
8
  ?>
9
  <fieldset disabled class="wpstg-opacity-80">
10
  <p><strong class="wpstg-fs-14">
1
  <?php
2
+
3
  /**
4
  * This file is currently being called only for the Free version:
5
  * src/Backend/views/clone/ajax/scan.php:113
6
  *
7
  * @file src/Backend/Pro/views/clone/ajax/external-database.php For the Pro counterpart.
8
  */
9
+
10
  ?>
11
  <fieldset disabled class="wpstg-opacity-80">
12
  <p><strong class="wpstg-fs-14">
Backend/views/clone/ajax/process-lock.php CHANGED
@@ -3,7 +3,7 @@
3
 
4
 
5
  <button type="button" class="wpstg-prev-step-link wpstg-link-btn button-primary wpstg-button">
6
- <?php _e( "Back", "wp-staging" ) ?>
7
  </button>
8
 
9
  <button type="button" id="wpstg-restart-cloning" class="wpstg-link-btn button-primary wpstg-button">
3
 
4
 
5
  <button type="button" class="wpstg-prev-step-link wpstg-link-btn button-primary wpstg-button">
6
+ <?php _e("Back", "wp-staging") ?>
7
  </button>
8
 
9
  <button type="button" id="wpstg-restart-cloning" class="wpstg-link-btn button-primary wpstg-button">
Backend/views/clone/ajax/scan.php CHANGED
@@ -10,15 +10,18 @@
10
  */
11
  ?>
12
  <label id="wpstg-clone-label" for="wpstg-new-clone">
13
- <?php echo __( 'Staging Site Name:', 'wp-staging' ) ?>
14
- <input type="text" id="wpstg-new-clone-id" value="<?php echo $options->current; ?>"<?php if( $options->current !== null ) echo " disabled='disabled'" ?>>
 
 
15
  </label>
16
 
17
  <span class="wpstg-error-msg" id="wpstg-clone-id-error" style="display:none;">
18
  <?php
19
  echo __(
20
- "<br>Probably not enough free disk space to create a staging site. " .
21
- "<br> You can continue but its likely that the copying process will fail.", "wp-staging"
 
22
  )
23
  ?>
24
  </span>
@@ -26,17 +29,18 @@
26
  <div class="wpstg-tabs-wrapper">
27
  <a href="#" class="wpstg-tab-header active" data-id="#wpstg-scanning-db">
28
  <span class="wpstg-tab-triangle">&#9658;</span>
29
- <?php echo __( "Database Tables", "wp-staging" ) ?>
30
  </a>
31
 
32
- <div class="wpstg-tab-section" id="wpstg-scanning-db">
33
  <?php do_action("wpstg_scanning_db") ?>
34
  <h4 style="margin:0">
35
- <p><?php printf( __( "Select the tables to copy. Tables beginning with the prefix '%s' have already been selected.", "wp-staging" ), $db->prefix ); ?></p>
36
  <p></p>
37
  <?php
38
  echo __(
39
- "Select multiple tables by pressing left mouse button and moving or by pressing STRG+Left Mouse button. (Mac ⌘+Left Mouse Button)", "wp-staging"
 
40
  );
41
  ?>
42
  </h4>
@@ -46,7 +50,7 @@
46
  </div>
47
  <select multiple="multiple" id="wpstg_select_tables_cloning">
48
  <?php
49
- foreach ($options->tables as $table):
50
  $attributes = !in_array($table->name, $options->excludedTables) && (strpos($table->name, $db->prefix) === 0) ? "selected='selected'" : "";
51
  $attributes .= in_array($table->name, $options->clonedTables) ? "disabled" : '';
52
  ?>
@@ -60,22 +64,22 @@
60
  <a href="#" class="wpstg-button-unselect button"> <?php _e('Unselect All', 'wp-staging'); ?> </a>
61
  <a href="#" class="wpstg-button-select button"> <?php _e(WPStaging\Core\WPStaging::getTablePrefix(), 'wp-staging'); ?> </a>
62
  </div>
63
- </div>
64
 
65
  <a href="#" class="wpstg-tab-header" data-id="#wpstg-scanning-files">
66
  <span class="wpstg-tab-triangle">&#9658;</span>
67
- <?php echo __( "Files", "wp-staging" ) ?>
68
  </a>
69
 
70
- <div class="wpstg-tab-section" id="wpstg-scanning-files">
71
  <h4 style="margin:0">
72
- <?php echo __( "Select folders to copy. Click on folder name to list subfolders!", "wp-staging" ) ?>
73
  </h4>
74
 
75
  <?php echo $scan->directoryListing() ?>
76
 
77
  <h4 style="margin:10px 0 10px 0">
78
- <?php echo __( "Extra directories to copy", "wp-staging" ) ?>
79
  </h4>
80
 
81
  <textarea id="wpstg_extraDirectories" name="wpstg_extraDirectories" style="width:100%;height:100px;"></textarea>
@@ -83,8 +87,9 @@
83
  <span>
84
  <?php
85
  echo __(
86
- "Enter one folder path per line.<br>" .
87
- "Folders must start with absolute path: " . $options->root, "wp-staging"
 
88
  )
89
  ?>
90
  </span>
@@ -93,19 +98,19 @@
93
  <p>
94
  <span>
95
  <?php
96
- if( isset( $options->clone ) ) {
97
- echo __( "All files will be copied to: ", "wp-staging" ) . $options->root . $options->clone;
98
  }
99
  ?>
100
  </span>
101
  </p>
102
- </div>
103
 
104
  <a href="#" class="wpstg-tab-header" data-id="#wpstg-advanced-settings">
105
  <span class="wpstg-tab-triangle"><input type="checkbox" name="wpstg-advanced" value="true"></span>
106
  <?php
107
  $pro = defined('WPSTGPRO_VERSION') ? ' ' : ' / Pro';
108
- echo __( "Advanced Settings " . $pro, "wp-staging" ); ?>
109
  </a>
110
 
111
  <div class="wpstg-tab-section" id="wpstg-advanced-settings">
@@ -123,39 +128,41 @@
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
 
147
  <button type="button" class="wpstg-prev-step-link wpstg-link-btn wpstg-blue-primary wpstg-button">
148
- <?php _e( "Back", "wp-staging" ) ?>
149
  </button>
150
 
151
  <?php
152
- if( $options->current !== null ) {
153
- $label = __( "Update Clone", "wp-staging" );
154
  $action = 'wpstg_update';
155
 
156
  echo '<button type="button" id="wpstg-start-updating" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="' . $action . '">' . $label . '</button>';
157
  } else {
158
- $label = __( "Start Cloning", "wp-staging" );
159
  $action = 'wpstg_cloning';
160
 
161
  echo '<button type="button" id="wpstg-start-cloning" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="' . $action . '">' . $label . '</button>';
10
  */
11
  ?>
12
  <label id="wpstg-clone-label" for="wpstg-new-clone">
13
+ <?php echo __('Staging Site Name:', 'wp-staging') ?>
14
+ <input type="text" id="wpstg-new-clone-id" value="<?php echo $options->current; ?>"<?php if ($options->current !== null) {
15
+ echo " disabled='disabled'";
16
+ } ?>>
17
  </label>
18
 
19
  <span class="wpstg-error-msg" id="wpstg-clone-id-error" style="display:none;">
20
  <?php
21
  echo __(
22
+ "<br>Probably not enough free disk space to create a staging site. " .
23
+ "<br> You can continue but its likely that the copying process will fail.",
24
+ "wp-staging"
25
  )
26
  ?>
27
  </span>
29
  <div class="wpstg-tabs-wrapper">
30
  <a href="#" class="wpstg-tab-header active" data-id="#wpstg-scanning-db">
31
  <span class="wpstg-tab-triangle">&#9658;</span>
32
+ <?php echo __("Database Tables", "wp-staging") ?>
33
  </a>
34
 
35
+ <fieldset class="wpstg-tab-section" id="wpstg-scanning-db">
36
  <?php do_action("wpstg_scanning_db") ?>
37
  <h4 style="margin:0">
38
+ <p><?php printf(__("Select the tables to copy. Tables beginning with the prefix '%s' have already been selected.", "wp-staging"), $db->prefix); ?></p>
39
  <p></p>
40
  <?php
41
  echo __(
42
+ "Select multiple tables by pressing left mouse button and moving or by pressing STRG+Left Mouse button. (Mac ⌘+Left Mouse Button)",
43
+ "wp-staging"
44
  );
45
  ?>
46
  </h4>
50
  </div>
51
  <select multiple="multiple" id="wpstg_select_tables_cloning">
52
  <?php
53
+ foreach ($options->tables as $table) :
54
  $attributes = !in_array($table->name, $options->excludedTables) && (strpos($table->name, $db->prefix) === 0) ? "selected='selected'" : "";
55
  $attributes .= in_array($table->name, $options->clonedTables) ? "disabled" : '';
56
  ?>
64
  <a href="#" class="wpstg-button-unselect button"> <?php _e('Unselect All', 'wp-staging'); ?> </a>
65
  <a href="#" class="wpstg-button-select button"> <?php _e(WPStaging\Core\WPStaging::getTablePrefix(), 'wp-staging'); ?> </a>
66
  </div>
67
+ </fieldset>
68
 
69
  <a href="#" class="wpstg-tab-header" data-id="#wpstg-scanning-files">
70
  <span class="wpstg-tab-triangle">&#9658;</span>
71
+ <?php echo __("Files", "wp-staging") ?>
72
  </a>
73
 
74
+ <fieldset class="wpstg-tab-section" id="wpstg-scanning-files">
75
  <h4 style="margin:0">
76
+ <?php echo __("Select folders to copy. Click on folder name to list subfolders!", "wp-staging") ?>
77
  </h4>
78
 
79
  <?php echo $scan->directoryListing() ?>
80
 
81
  <h4 style="margin:10px 0 10px 0">
82
+ <?php echo __("Extra directories to copy", "wp-staging") ?>
83
  </h4>
84
 
85
  <textarea id="wpstg_extraDirectories" name="wpstg_extraDirectories" style="width:100%;height:100px;"></textarea>
87
  <span>
88
  <?php
89
  echo __(
90
+ "Enter one folder path per line.<br>" .
91
+ "Folders must start with absolute path: " . $options->root,
92
+ "wp-staging"
93
  )
94
  ?>
95
  </span>
98
  <p>
99
  <span>
100
  <?php
101
+ if (isset($options->clone)) {
102
+ echo __("All files will be copied to: ", "wp-staging") . $options->root . $options->clone;
103
  }
104
  ?>
105
  </span>
106
  </p>
107
+ </fieldset>
108
 
109
  <a href="#" class="wpstg-tab-header" data-id="#wpstg-advanced-settings">
110
  <span class="wpstg-tab-triangle"><input type="checkbox" name="wpstg-advanced" value="true"></span>
111
  <?php
112
  $pro = defined('WPSTGPRO_VERSION') ? ' ' : ' / Pro';
113
+ echo __("Advanced Settings " . $pro, "wp-staging"); ?>
114
  </a>
115
 
116
  <div class="wpstg-tab-section" id="wpstg-advanced-settings">
128
  </div>
129
 
130
  <?php
131
+
132
  if (defined('WPSTGPRO_VERSION')) {
133
  require_once(WPSTG_PLUGIN_DIR . 'Backend/Pro/views/clone/ajax/mail-setting.php');
134
  }
135
 
136
+ if ($options->current !== null && $options->mainJob === 'updating') {
137
  $uploadsSymlinked = isset($options->existingClones[$options->current]['uploadsSymlinked']) ? (bool)$options->existingClones[$options->current]['uploadsSymlinked'] : false;
138
+
139
+ ?>
140
  <p><label>
141
  <input type="checkbox" id="wpstg-clean-plugins-themes" name="wpstg-clean-plugins-themes">
142
  <?php echo __("Delete all plugins & themes on staging site before starting copy process.", "wp-staging"); ?>
143
  </label></p>
144
+ <p><label> <?php echo ($uploadsSymlinked ? "<b>" . __("Note: This option is disabled as uploads directory is symlinked", "wp-staging") . "</b><br/>" : '') ?>
145
  <input type="checkbox" id="wpstg-clean-uploads" name="wpstg-clean-uploads" <?php echo ($uploadsSymlinked ? 'disabled' : '') ?>>
146
  <?php echo __("Delete entire folder wp-content/uploads on staging site including all images before starting copy process.", "wp-staging"); ?>
147
  </label></p>
148
+ <?php
149
  }
150
  ?>
151
+ <strong>Important:</strong><a href="#" id="wpstg-check-space"><?php _e('Check required disk space', 'wp-staging'); ?></a>
152
  <p></p>
153
 
154
  <button type="button" class="wpstg-prev-step-link wpstg-link-btn wpstg-blue-primary wpstg-button">
155
+ <?php _e("Back", "wp-staging") ?>
156
  </button>
157
 
158
  <?php
159
+ if ($options->current !== null && $options->mainJob === 'updating') {
160
+ $label = __("Update Clone", "wp-staging");
161
  $action = 'wpstg_update';
162
 
163
  echo '<button type="button" id="wpstg-start-updating" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="' . $action . '">' . $label . '</button>';
164
  } else {
165
+ $label = __("Start Cloning", "wp-staging");
166
  $action = 'wpstg_cloning';
167
 
168
  echo '<button type="button" id="wpstg-start-cloning" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="' . $action . '">' . $label . '</button>';
Backend/views/clone/ajax/single-overview.php CHANGED
@@ -7,56 +7,71 @@
7
  ?>
8
  <div id="wpstg-step-1">
9
  <button id="wpstg-new-clone" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="wpstg_scanning">
10
- <?php echo __( "Create new staging site", "wp-staging" ) ?>
11
  </button>
12
  </div>
13
 
14
- <?php if( isset( $availableClones ) && !empty( $availableClones ) ): ?>
15
  <!-- Existing Clones -->
16
  <div id="wpstg-existing-clones">
17
  <h3>
18
- <?php _e( "Your Staging Sites:", "wp-staging" ) ?>
19
  </h3>
20
- <?php foreach ( $availableClones as $name => $data ): ?>
21
  <div id="<?php echo $data["directoryName"]; ?>" class="wpstg-clone">
22
-
23
  <?php $urlLogin = $data["url"]; ?>
24
-
25
- <a href="<?php echo $urlLogin ?>" class="wpstg-clone-title" target="_blank">
26
- <?php echo $data["directoryName"]; ?>
27
- </a>
28
-
29
- <?php
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" ) ?>">
40
- <?php _e( "Open", "wp-staging" ); ?>
41
- </a>
42
-
43
- <a href="#" class="wpstg-execute-clone wpstg-clone-action" data-clone="<?php echo $name ?>" title="<?php echo __( "Update and overwrite this clone. Select folders and database tables in the next step.", "wp-staging" ) ?>">
44
- <?php _e( "Update", "wp-staging" ); ?>
45
- </a>
46
-
47
- <a href="#" class="wpstg-remove-clone wpstg-clone-action" data-clone="<?php echo $name ?>" title="<?php echo __( "Delete this clone. Select specific folders and database tables in the next step.", "wp-staging" ) ?>">
48
- <?php _e( "Delete", "wp-staging" ); ?>
49
- </a>
50
-
51
- <?php
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">
62
  <?php
@@ -67,18 +82,18 @@
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".
73
- "If you have an unstable internet connection and cloning breaks due to that, clone again only the folders wp-admin, wp-includes, and all database tables.\n\n".
74
- "That will not take much time. Then, you can proceed with the wp-content folder that usually needs the most disk space. ".
75
  "If it interrupts again, at least it will not break the existing staging site again, and you can repeat and resume the last operation.";
76
 
77
  if (!empty($data['status']) && $data['status'] !== 'finished') {
78
  $status = sprintf(
79
- __('Status: <span class="wpstg-bold" style="color:#ffc2c2;" title="%s">%s</span>', 'wp-staging'),
80
- $statusTooltip,
81
- $data['status']
82
  );
83
  } else {
84
  $status = '&nbsp;&nbsp;&nbsp;';
@@ -99,7 +114,11 @@
99
  echo '</br>';
100
  echo sprintf(__('Updated: <span>%s</span>', 'wp-staging'), $datetime);
101
 
102
- do_action('wpstg.views.single_overview.after_existing_clones_details', $name, $data, $license);
 
 
 
 
103
  ?>
104
  </div>
105
  </div>
7
  ?>
8
  <div id="wpstg-step-1">
9
  <button id="wpstg-new-clone" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="wpstg_scanning">
10
+ <?php echo __("Create new staging site", "wp-staging") ?>
11
  </button>
12
  </div>
13
 
14
+ <?php if (isset($availableClones) && !empty($availableClones)) : ?>
15
  <!-- Existing Clones -->
16
  <div id="wpstg-existing-clones">
17
  <h3>
18
+ <?php _e("Your Staging Sites:", "wp-staging") ?>
19
  </h3>
20
+ <?php foreach ($availableClones as $name => $data) : ?>
21
  <div id="<?php echo $data["directoryName"]; ?>" class="wpstg-clone">
 
22
  <?php $urlLogin = $data["url"]; ?>
23
+ <div class="wpstg-clone-header">
24
+ <a href="<?php echo $urlLogin ?>" class="wpstg-clone-title" target="_blank">
25
+ <?php echo $data["directoryName"]; ?>
26
+ </a>
27
+ <div class="wpstg-clone-actions">
28
+ <div class="wpstg-dropdown wpstg-action-dropdown">
29
+ <a href="#" class="wpstg-dropdown-toggler transparent">
30
+ <?php _e("Actions", "wp-staging"); ?>
31
+ </a>
32
+ <div class="wpstg-dropdown-menu">
33
+ <?php
34
+ do_action('wpstg.views.single_overview.before_existing_clones_actions', $name, $data, $license);
35
+ // Todo: Remove in future versions
36
+ if (function_exists('do_action_deprecated')) {
37
+ // do_action_deprecated exists since WP 4.6
38
+ echo do_action_deprecated("wpstg.views.single_overview.before_existing_clones_buttons", [$name, $data, $license], '2.7.6', 'wpstg.views.single_overview.before_existing_clones_actions', 'The replacement action uses better name according to ui');
39
+ }
40
+ // Todo: Remove in future versions
41
+ if (function_exists('apply_filters_deprecated')) {
42
+ // apply_filters_deprecated exists since WP 4.6
43
+ echo apply_filters_deprecated("wpstg_before_stage_buttons", [$html = '', $name, $data], '2.7.6', 'wpstg.views.single_overview.before_existing_clones_actions', 'The replacement filter uses do_action()');
44
+ }
45
+ ?>
46
+ <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") ?>">
47
+ <?php _e("Open", "wp-staging"); ?>
48
+ </a>
49
+ <a href="#" class="wpstg-execute-clone wpstg-clone-action" data-clone="<?php echo $name ?>" title="<?php echo __("Update and overwrite this clone. Select folders and database tables in the next step.", "wp-staging") ?>">
50
+ <?php _e("Update", "wp-staging"); ?>
51
+ </a>
52
+ <a href="#" class="wpstg-reset-clone wpstg-clone-action" data-clone="<?php echo $name ?>" title="<?php echo __("Reset this clone with existing production site. Confirm to proceed.", "wp-staging") ?>">
53
+ <?php _e("Reset", "wp-staging"); ?>
54
+ </a>
55
+ <a href="#" class="wpstg-remove-clone wpstg-clone-action" data-clone="<?php echo $name ?>" title="<?php echo __("Delete this clone. Select specific folders and database tables in the next step.", "wp-staging") ?>">
56
+ <?php _e("Delete", "wp-staging"); ?>
57
+ </a>
58
+ <?php
59
+ do_action('wpstg.views.single_overview.after_existing_clones_actions', $name, $data, $license);
60
+ // Todo: Remove in future versions
61
+ if (function_exists('do_action_deprecated')) {
62
+ // do_action_deprecated exists since WP 4.6
63
+ echo do_action_deprecated("wpstg.views.single_overview.after_existing_clones_buttons", [$name, $data, $license], '2.7.6', 'wpstg.views.single_overview.after_existing_clones_actions', 'The replacement action uses better name according to ui');
64
+ }
65
+ // Todo: Remove in future versions
66
+ if (function_exists('apply_filters_deprecated')) {
67
+ // apply_filters_deprecated exists since WP 4.6
68
+ echo apply_filters_deprecated("wpstg_after_stage_buttons", [$html = '', $name, $data], '2.7.6', 'wpstg.views.single_overview.after_existing_clones_actions', 'The replacement filter uses do_action()');
69
+ }
70
+ ?>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
 
76
  <div class="wpstg-staging-info">
77
  <?php
82
  $datetime = ! empty($data['datetime']) ? date("D, d M Y H:i:s T", $data['datetime']) : '&nbsp;&nbsp;&nbsp;';
83
  $owner = ! empty($data['ownerId']) ? get_userdata($data['ownerId']) : null;
84
  $ownerName = ($owner !== null) ? $owner->user_login : 'N/A';
85
+ $statusTooltip = "This clone is incomplete and does not work. Clone or update it again! \n\n" .
86
+ "Important: Keep the browser open until the cloning is finished. \n" .
87
+ "It will not proceed if your browser is not open.\n\n" .
88
+ "If you have an unstable internet connection and cloning breaks due to that, clone again only the folders wp-admin, wp-includes, and all database tables.\n\n" .
89
+ "That will not take much time. Then, you can proceed with the wp-content folder that usually needs the most disk space. " .
90
  "If it interrupts again, at least it will not break the existing staging site again, and you can repeat and resume the last operation.";
91
 
92
  if (!empty($data['status']) && $data['status'] !== 'finished') {
93
  $status = sprintf(
94
+ __('Status: <span class="wpstg-bold" style="color:#ffc2c2;" title="%s">%s</span>', 'wp-staging'),
95
+ $statusTooltip,
96
+ $data['status']
97
  );
98
  } else {
99
  $status = '&nbsp;&nbsp;&nbsp;';
114
  echo '</br>';
115
  echo sprintf(__('Updated: <span>%s</span>', 'wp-staging'), $datetime);
116
 
117
+ // Todo: Remove in future versions
118
+ if (function_exists('do_action_deprecated')) {
119
+ // do_action_deprecated exists since WP 4.6
120
+ echo do_action_deprecated("wpstg.views.single_overview.after_existing_clones_details", [$name, $data, $license], '2.7.6', '', 'This will be removed from the future update');
121
+ }
122
  ?>
123
  </div>
124
  </div>
Backend/views/clone/ajax/start.php CHANGED
@@ -39,10 +39,10 @@
39
  <h3>Congratulations
40
  </h3>
41
  <?php
42
- $subDirectory = str_replace( get_home_path(), '', ABSPATH );
43
  $helper = new \WPStaging\Core\Utils\Helper();
44
  $url = $helper->getHomeUrl() . str_replace('/', '', $subDirectory);
45
- echo sprintf( __( 'WP Staging successfully created a staging site in a sub-directory of your main site accessable from:<br><strong><a href="%1$s" target="_blank" id="wpstg-clone-url-1">%1$s</a></strong>', 'wp-staging' ), $url );
46
  ?>
47
  <br>
48
  <br>
@@ -82,7 +82,7 @@
82
  <p>
83
  To make it more clear when you work on the staging site WP Staging changed the color of the admin bar:
84
  <br><br>
85
- <img src="<?php echo $this->url . "/img/admin_dashboard.png" ?>">
86
  <br>
87
  On the fronpage the site name also changed to <br>
88
  <strong class="wpstg-font-italic">
39
  <h3>Congratulations
40
  </h3>
41
  <?php
42
+ $subDirectory = str_replace(get_home_path(), '', ABSPATH);
43
  $helper = new \WPStaging\Core\Utils\Helper();
44
  $url = $helper->getHomeUrl() . str_replace('/', '', $subDirectory);
45
+ echo sprintf(__('WP Staging successfully created a staging site in a sub-directory of your main site accessable from:<br><strong><a href="%1$s" target="_blank" id="wpstg-clone-url-1">%1$s</a></strong>', 'wp-staging'), $url);
46
  ?>
47
  <br>
48
  <br>
82
  <p>
83
  To make it more clear when you work on the staging site WP Staging changed the color of the admin bar:
84
  <br><br>
85
+ <img src="<?php echo $this->assets->getAssetsUrl("img/admin_dashboard.png") ?>">
86
  <br>
87
  On the fronpage the site name also changed to <br>
88
  <strong class="wpstg-font-italic">
Backend/views/clone/ajax/update.php CHANGED
@@ -1,6 +1,7 @@
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
  ?>
@@ -19,8 +20,14 @@
19
  <div class="wpstg-clear-both"></div>
20
  </div>
21
 
22
- <button type="button" id="wpstg-cancel-cloning-update" class="wpstg-link-btn button-primary">
23
- <?php echo __("Cancel Update", "wp-staging")?>
 
 
 
 
 
 
24
  </button>
25
 
26
  <button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
1
  <?php
2
  /**
3
  * @see \WPStaging\Backend\Administrator::ajaxUpdateProcess A place where this view is being called.
4
+ * @see \WPStaging\Backend\Administrator::ajaxResetProcess A place where this view is being called.
5
  * @var \WPStaging\Backend\Modules\Jobs\Cloning $cloning
6
  */
7
  ?>
20
  <div class="wpstg-clear-both"></div>
21
  </div>
22
 
23
+ <button type="button" id="wpstg-cancel-cloning-update" data-job="<?php echo $cloning->getOptions()->mainJob; ?>" class="wpstg-link-btn button-primary">
24
+ <?php
25
+ if ($cloning->getOptions()->mainJob === 'resetting') {
26
+ _e("Cancel Reset", "wp-staging");
27
+ } else {
28
+ _e("Cancel Update", "wp-staging");
29
+ }
30
+ ?>
31
  </button>
32
 
33
  <button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
Backend/views/clone/index.php CHANGED
@@ -5,7 +5,7 @@
5
  do_action('wpstg_notifications');
6
 
7
  $display = '';
8
- if (!defined('WPSTGPRO_VERSION')){
9
  $display = 'display:none;';
10
  }
11
  ?>
@@ -14,14 +14,22 @@
14
  <ul>
15
  <li style="<?php echo $display ?>">
16
  <a class="wpstg--tab--content wpstg--tab--active wpstg-button" data-target="#wpstg--tab--staging">
17
- <?php _e('Staging Sites', 'wp-staging') ?>
18
  </a>
19
  </li>
 
20
  <li style="<?php echo $display ?>">
21
- <a class="wpstg-button" data-target="#wpstg--tab--snapshot">
 
 
 
 
 
 
22
  <?php _e('Backups', 'wp-staging') ?>
23
  </a>
24
  </li>
 
25
  <li>
26
  <span class="wpstg-loader"></span>
27
  </li>
@@ -30,8 +38,8 @@
30
  <div class="wpstg--tab--contents">
31
  <div id="wpstg--tab--staging" class="wpstg--tab--content wpstg--tab--active">
32
  <?php
33
- if (wpstg_is_stagingsite()) {
34
- // Staging site
35
  require_once($this->path . "views/clone/staging-site/index.php");
36
  } elseif (!defined('WPSTGPRO_VERSION') && is_multisite()) {
37
  require_once($this->path . "views/clone/multi-site/index.php");
@@ -41,8 +49,11 @@
41
  }
42
  ?>
43
  </div>
44
- <div id="wpstg--tab--snapshot" class="wpstg--tab--content">
45
- Snapshots
 
 
 
46
  </div>
47
  </div>
48
  </div>
5
  do_action('wpstg_notifications');
6
 
7
  $display = '';
8
+ if (!defined('WPSTGPRO_VERSION')) {
9
  $display = 'display:none;';
10
  }
11
  ?>
14
  <ul>
15
  <li style="<?php echo $display ?>">
16
  <a class="wpstg--tab--content wpstg--tab--active wpstg-button" data-target="#wpstg--tab--staging">
17
+ <?php _e('Staging', 'wp-staging') ?>
18
  </a>
19
  </li>
20
+ <?php if(class_exists('\WPStaging\Pro\Backup\BackupServiceProvider') && \WPStaging\Pro\Backup\BackupServiceProvider::isEnabled()): ?>
21
  <li style="<?php echo $display ?>">
22
+ <a class="wpstg-button" data-target="#wpstg--tab--backup">
23
+ <?php _e('Backup & Migrate', 'wp-staging') ?>
24
+ </a>
25
+ </li>
26
+ <?php else: ?>
27
+ <li style="<?php echo $display ?>">
28
+ <a class="wpstg-button" data-target="#wpstg--tab--database-backups">
29
  <?php _e('Backups', 'wp-staging') ?>
30
  </a>
31
  </li>
32
+ <?php endif; ?>
33
  <li>
34
  <span class="wpstg-loader"></span>
35
  </li>
38
  <div class="wpstg--tab--contents">
39
  <div id="wpstg--tab--staging" class="wpstg--tab--content wpstg--tab--active">
40
  <?php
41
+ if (!$this->siteInfo->isCloneable()) {
42
+ // Staging site but not cloneable
43
  require_once($this->path . "views/clone/staging-site/index.php");
44
  } elseif (!defined('WPSTGPRO_VERSION') && is_multisite()) {
45
  require_once($this->path . "views/clone/multi-site/index.php");
49
  }
50
  ?>
51
  </div>
52
+ <div id="wpstg--tab--backup" class="wpstg--tab--content">
53
+ <?php _e('Loading...', 'wp-staging') ?>
54
+ </div>
55
+ <div id="wpstg--tab--database-backups" class="wpstg--tab--content">
56
+ <?php _e('Loading...', 'wp-staging') ?>
57
  </div>
58
  </div>
59
  </div>
Backend/views/clone/single-site/index.php CHANGED
@@ -1,23 +1,23 @@
1
  <ul id="wpstg-steps">
2
  <li class="wpstg-current-step wpstg-step1">
3
  <span class="wpstg-step-num">1</span>
4
- <?php echo __( "Overview", "wp-staging" ) ?>
5
  </li>
6
  <li class="wpstg-step2">
7
  <span class="wpstg-step-num">2</span>
8
- <?php echo __( "Scanning", "wp-staging" ) ?>
9
  </li>
10
  <li class="wpstg-step3 wpstg-step3-cloning">
11
  <span class="wpstg-step-num">3</span>
12
- <?php echo __( "Cloning", "wp-staging" ) ?>
13
  </li>
14
  <li class="wpstg-step3 wpstg-step3-pushing" style="display: none;">
15
  <span class="wpstg-step-num">3</span>
16
- <?php echo __( "Pushing", "wp-staging" ) ?>
17
  </li>
18
  <li>
19
  <button type="button" id="wpstg-report-issue-button" class="wpstg-button">
20
- <i class="wpstg-icon-issue"></i><?php echo __( "Report Issue", "wp-staging" ); ?>
21
  </button>
22
  </li>
23
  </ul>
@@ -32,7 +32,7 @@
32
  </span>
33
  </div>
34
  <a href="https://wp-staging.com/?utm_source=tryout&utm_medium=plugin&utm_campaign=tryout&utm_term=tryout" target="_new">
35
- <img src="<?php echo WPSTG_PLUGIN_URL . 'Backend/public/img/wpstaging-banner200x400-tryout.gif'; ?>">
36
  </a>
37
  </div>
38
  <?php } ?>
1
  <ul id="wpstg-steps">
2
  <li class="wpstg-current-step wpstg-step1">
3
  <span class="wpstg-step-num">1</span>
4
+ <?php echo __("Overview", "wp-staging") ?>
5
  </li>
6
  <li class="wpstg-step2">
7
  <span class="wpstg-step-num">2</span>
8
+ <?php echo __("Scanning", "wp-staging") ?>
9
  </li>
10
  <li class="wpstg-step3 wpstg-step3-cloning">
11
  <span class="wpstg-step-num">3</span>
12
+ <?php echo __("Cloning", "wp-staging") ?>
13
  </li>
14
  <li class="wpstg-step3 wpstg-step3-pushing" style="display: none;">
15
  <span class="wpstg-step-num">3</span>
16
+ <?php echo __("Pushing", "wp-staging") ?>
17
  </li>
18
  <li>
19
  <button type="button" id="wpstg-report-issue-button" class="wpstg-button">
20
+ <i class="wpstg-icon-issue"></i><?php echo __("Report Issue", "wp-staging"); ?>
21
  </button>
22
  </li>
23
  </ul>
32
  </span>
33
  </div>
34
  <a href="https://wp-staging.com/?utm_source=tryout&utm_medium=plugin&utm_campaign=tryout&utm_term=tryout" target="_new">
35
+ <img src="<?php echo $this->assets->getAssetsUrl('img/wpstaging-banner200x400-tryout.gif'); ?>">
36
  </a>
37
  </div>
38
  <?php } ?>
Backend/views/clone/staging-site/index.php CHANGED
@@ -1,8 +1,12 @@
1
  <span class="wpstg-notice-alert wpstg-mt-20px">
2
  <?php echo __("This staging site can be pushed and modified with WP STAGING Pro plugin installed on your production site! Open WP Staging Pro on your production site and start the pushing process from there!", "wp-staging")?>
3
  <br/>
4
- <?php echo sprintf(__("<a href='%s' target='_new'>Open WP STAGING Pro on Live Site</a>"), wpstg_get_production_hostname() . '/wp-admin/admin.php?page=wpstg_clone'); ?>
5
  <br/>
6
  <br/>
7
- <?php echo sprintf(__("If you like to clone this staging site check out <a href='%s' target='_new'>this article</a>."), 'https://wp-staging.com/docs/cloning-a-staging-site-testing-push-method/'); ?>
 
 
 
 
8
  </span>
1
  <span class="wpstg-notice-alert wpstg-mt-20px">
2
  <?php echo __("This staging site can be pushed and modified with WP STAGING Pro plugin installed on your production site! Open WP Staging Pro on your production site and start the pushing process from there!", "wp-staging")?>
3
  <br/>
4
+ <?php echo sprintf(__("<a href='%s' target='_new'>Open WP STAGING Pro on Live Site</a>", 'wp-staging'), wpstg_get_production_hostname() . '/wp-admin/admin.php?page=wpstg_clone'); ?>
5
  <br/>
6
  <br/>
7
+ <br/>
8
+ <button class='wpstg-button wpstg-button-light' id="wpstg-enable-staging-cloning"><?php _e('Click here', 'wp-staging') ?></button> <?php _e(' to make this staging site cloneable.', 'wp-staging') ?>
9
+ <br/>
10
+ <br/>
11
+ <?php echo sprintf(__("If you would like to know more about cloning staging sites check out <a href='%s' target='_new'>this article</a>.", 'wp-staging'), 'https://wp-staging.com/docs/cloning-a-staging-site-testing-push-method/'); ?>
12
  </span>
Backend/views/database/legacy/confirm-delete.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use WPStaging\Framework\TemplateEngine\TemplateEngine;
4
+ use WPStaging\Pro\Database\Legacy\Entity\Backup;
5
+
6
+ /** @var TemplateEngine $this */
7
+ /** @var Backup $backup */
8
+ ?>
9
+ <div class="wpstg-notice-alert wpstg-failed">
10
+ <h4 style="margin:0;">
11
+ <?php _e('This backup will be deleted.', 'wp-staging') ?>
12
+ </h4>
13
+ <?php _e('Are you sure that you want to delete the site backup? This action can not be undone!', 'wp-staging') ?>
14
+ </div>
15
+
16
+ <div class="wpstg-box">
17
+ <div class="wpstg-db-table">
18
+ <?php if (!empty($size = $backup->getFileSize())) : ?>
19
+ <label><?php _e('File Size', 'wp-staging')?></label>
20
+ <span class="wpstg-size-info"><?php echo esc_html($size); ?></span>
21
+ <?php endif; ?>
22
+ </div>
23
+ </div>
24
+
25
+ <a href="#" class="wpstg-link-btn button-primary" id="wpstg-cancel-backup-delete">
26
+ <?php _e('Cancel', 'wp-staging')?>
27
+ </a>
28
+
29
+ <a href="#" class="wpstg-link-btn button-primary" id="wpstg-delete-backup" data-id="<?php echo $backup->getId()?>">
30
+ <?php _e('Delete', 'wp-staging')?>
31
+ </a>
Backend/views/database/legacy/confirm-restore.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use WPStaging\Framework\Database\TableDto;
4
+ use WPStaging\Framework\Collection\Collection;
5
+ use WPStaging\Framework\TemplateEngine\TemplateEngine;
6
+ use WPStaging\Pro\Database\Legacy\Entity\Backup;
7
+
8
+ /** @var TemplateEngine $this */
9
+ /** @var Backup $backup */
10
+ /** @var TableDto[]|Collection $backupTables */
11
+ /** @var TableDto[]|Collection $prodTables */
12
+ /** @var Closure $isTableChanged */
13
+ ?>
14
+ <div class="wpstg-beta-notice" style="display:none;">
15
+ <?php _e('WP STAGING backup restoring works 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-backups-report-issue-button">please give us some feedback.</a>', 'wp-staging') ?>
16
+ </div>
17
+ <div class="wpstg-notice-alert wpstg-failed">
18
+ <h4 style="margin:0;">
19
+ <?php _e('Take care: This will overwrite your database!', 'wp-staging') ?>
20
+ </h4>
21
+
22
+ <p>
23
+ <?php _e('Are you sure that you want to restore the WordPress database with this backup created at ' . $this->transformToWpFormat($backup->getCreatedAt()) . '?', 'wp-staging') ?>
24
+ </p>
25
+ <?php _e('The production tables will be overwritten with the backup and the WordPress database will be restored to another point in time.', 'wp-staging') ?>
26
+ <br><br>
27
+ <?php _e('This will not delete any tables that are existing on the production site but not in the backup. If you want to remove them you will need to delete them manually.', 'wp-staging') ?>
28
+ <br><br>
29
+ <?php _e('Do not interrupt the process. Restoring can not be undone!', 'wp-staging') ?>
30
+ </div>
31
+
32
+ <div class="wpstg-box">
33
+ <div class="wpstg-float-left">
34
+
35
+ <h3><?php _e('Tables below will be overwritten:', 'wp-staging') ?></h3>
36
+ <table class="wpstg--snaphot-restore-table">
37
+ <?php foreach ($prodTables as $prodTable) : ?>
38
+ <tr>
39
+ <?php
40
+ $lastUpdate = $this->transformToWpFormat($prodTable->getUpdatedAt() ?: $prodTable->getCreatedAt());
41
+ $textBoldDanger = $isTableChanged($prodTable, $backupTables) ? ' wpstg--text--strong wpstg--text--danger' : '';
42
+ $title = empty($textBoldDanger) ? '' : __('This table is different compared to its backup equivalent.', 'wp-staging');
43
+ ?>
44
+ <td>
45
+ <span class="wpstg-db-table<?php echo $textBoldDanger ?>" title="<?php echo $title ?>"><?php echo $prodTable->getName() ?></span>
46
+ </td>
47
+ <td>
48
+ <span class="wpstg-size-info <?php echo $textBoldDanger ?>"><?php echo $prodTable->getHumanReadableSize() ?></span>
49
+ <span class="wpstg-size-info <?php echo $textBoldDanger ?>" title="Last updated: <?php echo $lastUpdate ?>"> - <?php echo $lastUpdate; ?></span>
50
+ </td>
51
+ </tr>
52
+ <?php endforeach ?>
53
+ </table>
54
+ </div>
55
+ <div class="wpstg-float-left" style="margin-left:10px;">
56
+ <h3><?php _e('Tables below will be restored:', 'wp-staging') ?></h3>
57
+ <table class="wpstg--snaphot-restore-table">
58
+ <?php foreach ($backupTables as $backupTable) : ?>
59
+ <tr>
60
+ <?php
61
+ $lastUpdate = $this->transformToWpFormat($backupTable->getUpdatedAt() ?: $backupTable->getCreatedAt());
62
+ $textBold = $isTableChanged($backupTable, $prodTables) ? ' wpstg--text--strong' : '';
63
+ $title = empty($textBold) ? '' : __('This table is different compared to its production equivalent.', 'wp-staging');
64
+ ?>
65
+ <td>
66
+ <span class="wpstg-db-table<?php echo $textBold ?>" title="<?php echo $title ?>"><?php echo $backupTable->getName() ?></span>
67
+ </td>
68
+ <td>
69
+ <span class="wpstg-size-info <?php echo $textBold ?>"><?php echo $backupTable->getHumanReadableSize() ?></span>
70
+ <span class="wpstg-size-info <?php echo $textBold ?>" title="Last updated: <?php echo $lastUpdate ?>"> - <?php echo $lastUpdate ?></span>
71
+ </td>
72
+ </tr>
73
+ <?php endforeach ?>
74
+ </table>
75
+ </div>
76
+ </div>
77
+
78
+ <a href="#" class="wpstg-link-btn wpstg-blue-primary" id="wpstg--backup--restore--cancel">
79
+ <?php _e('Cancel', 'wp-staging') ?>
80
+ </a>
81
+
82
+ <a href="#" class="wpstg-link-btn wpstg-blue-primary" id="wpstg--backup--restore"
83
+ data-id="<?php echo $backup->getId() ?>">
84
+ <?php _e('Restore Backup', 'wp-staging') ?>
85
+ </a>
86
+
87
+ <!-- TODO RPoC -->
88
+ <div id="wpstg--modal--backup--process" data-cancelButtonText="<?php _e('CANCEL', 'wp-staging') ?>" style="display: none">
89
+ <span class="wpstg-loader"></span>
90
+ <h3 class="wpstg--modal--process--title" style="color: #a8a8a8;margin: .25em 0;">
91
+ <?php _e('Processing...', 'wp-staging') ?>
92
+ </h3>
93
+ <div style="margin: .5em 0; color: #a8a8a8;">
94
+ <?php
95
+ echo sprintf(
96
+ __('Progress %s - Elapsed time %s', 'wp-staging'),
97
+ '<span class="wpstg--modal--process--percent">0</span>%',
98
+ '<span class="wpstg--modal--process--elapsed-time">0:00</span>'
99
+ )
100
+ ?>
101
+ </div>
102
+ <button
103
+ class="wpstg--modal--process--logs--tail"
104
+ data-txt-bad="<?php echo sprintf(
105
+ __('(%s) Critical, (%s) Errors, (%s) Warnings. Show Logs', 'wp-staging'),
106
+ '<span class=\'wpstg--modal--logs--critical-count\'>0</span>',
107
+ '<span class=\'wpstg--modal--logs--error-count\'>0</span>',
108
+ '<span class=\'wpstg--modal--logs--warning-count\'>0</span>'
109
+ ) ?>"
110
+ >
111
+ <?php _e('Show Logs', 'wp-staging') ?>
112
+ </button>
113
+ <div class="wpstg--modal--process--logs"></div>
114
+ </div>
Backend/views/database/legacy/listing.php ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @todo See if we can unify this file with src/template/Component/Backend/Backup/listing.php:8
5
+ */
6
+
7
+ use WPStaging\Pro\Database\Legacy\Entity\Backup;
8
+ use WPStaging\Pro\Database\Legacy\Collection\OptionCollection;
9
+ use WPStaging\Framework\TemplateEngine\TemplateEngine;
10
+ use WPStaging\Framework\Adapter\Directory;
11
+
12
+ /** @var TemplateEngine $this */
13
+ /** @var Backup[]|OptionCollection $backups */
14
+ /** @var array $directories */
15
+ /** @var string $urlAssets */
16
+ /** @var Directory $directory */
17
+
18
+ ?>
19
+
20
+ <div id="wpstg-step-1">
21
+ <button id="wpstg-new-database-backup" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button"
22
+ data-action="wpstg--backups--create">
23
+ <?php _e('Backup & Export Database', 'wp-staging') ?>
24
+ </button>
25
+ <div class="wpstg--tooltip"> <?php _e('What is this?', 'wp-staging'); ?>
26
+
27
+ <span class="wpstg--tooltiptext wpstg--tooltiptext-backups">
28
+ <?php _e('This can create a backup of the WordPress database tables at a particular point in time.
29
+ You can restore WordPress and roll back the database to another state.<br><br>
30
+ 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
31
+ like activating a new theme or updating its settings.<br><br>
32
+ This backup include all WordPress core tables and custom ones created by other plugins.
33
+ Restoring a backup will not affect other staging sites or existing backups. <br><br>
34
+ No files are included in backups! This is a quick way to roll back your site in time. For a full site backup it is recommended to use a dedicated backup plugin!
35
+ ', 'wp-staging') ?>
36
+ <p></p>
37
+ <?php if (is_multisite()) {
38
+ echo '<strong>' . __('Multisite Users Only: ', 'wp-staging') . '</strong>';
39
+ echo '<p></p>';
40
+ echo __("- If you run the backup function on a multisite network site the backup will contain only the tables belonging to the particular network site. <p></p>It will not save all database tables of all network sites. So you are able to restore all network sites independently. <p></p>- If you create a backup on a multisite main site it will create a backup of <strong>all database tables</strong>.</p></p><strong>Take care:</strong> Restoring a multisite main backup will <strong>restore all children sites including the mainsite.</strong>", 'wp-staging');
41
+ } ?>
42
+ </span>
43
+ </div>
44
+ </div>
45
+
46
+ <div id="wpstg-existing-database-backups">
47
+
48
+ &nbsp;
49
+ <?php
50
+
51
+ if (!empty($backups)) {
52
+ echo '<h3>' . __('Available Backups', 'wp-staging') . '</h3>';
53
+ }
54
+
55
+
56
+ foreach ($backups as $backup) : ?>
57
+ <div id="<?php echo $backup->getId() ?>" class="wpstg-clone wpstg-backup" data-type="<?php echo $backup->getType() ?>">
58
+ <div class="wpstg-clone-header">
59
+ <span class="wpstg-clone-title" data-title="<?php echo esc_attr($backup->getName()) ?>"><?php echo $backup->getName() ?></span>
60
+ <div class="wpstg-clone-actions">
61
+ <div class="wpstg-dropdown wpstg-action-dropdown">
62
+ <a href="#" class="wpstg-dropdown-toggler transparent">Actions</a>
63
+ <div class="wpstg-dropdown-menu">
64
+
65
+ <a href="#" class="wpstg--backup--download wpstg-merge-clone wpstg-clone-action"
66
+ data-id="<?php echo $backup->getId() ?>"
67
+ data-url="<?php echo $backup->getUrlDownload() ?: null ?>"
68
+ data-title="<?php _e('Download Backup', 'wp-staging') ?>"
69
+ data-title-export="<?php _e('Exporting Database Tables...', 'wp-staging') ?>"
70
+ data-btn-cancel-txt="<?php _e('CANCEL', 'wp-staging') ?>"
71
+ data-btn-download-txt="<?php _e($backup->getUrlDownload() ? 'Download' : 'Export & Download', 'wp-staging') ?>"
72
+ title="<?php _e('Download backup file on local system', 'wp-staging') ?>">
73
+ <?php _e('Download', 'wp-staging') ?>
74
+ </a>
75
+
76
+ <a href="#" class="wpstg--backup--restore wpstg-merge-clone wpstg-clone-action"
77
+ data-id="<?php echo $backup->getId() ?>"
78
+ title="<?php _e('Restore this backup to your live website!', 'wp-staging') ?>">
79
+ <?php _e('Restore', 'wp-staging') ?>
80
+ </a>
81
+
82
+ <a href="#" class="wpstg-remove-clone wpstg-clone-action wpstg-delete-backup"
83
+ data-id="<?php echo $backup->getId() ?>"
84
+ title="<?php _e('Delete this backup. This action can not be undone!', 'wp-staging') ?>">
85
+ <?php _e('Delete', 'wp-staging') ?>
86
+ </a>
87
+
88
+ <a href="#" class="wpstg--backup--edit wpstg-clone-action"
89
+ data-id="<?php echo $backup->getId() ?>"
90
+ data-name="<?php echo $backup->getName() ?>"
91
+ data-notes="<?php echo $backup->getNotes() ?>"
92
+ title="<?php _e('Edit backup name and / or notes', 'wp-staging') ?>">
93
+ <?php _e('Edit', 'wp-staging') ?>
94
+ </a>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+
100
+ <div class="wpstg-staging-info">
101
+ <ul>
102
+ <li>
103
+ <strong>
104
+ <?php
105
+ _e(
106
+ $backup->getType() === Backup::TYPE_DATABASE ? 'Table Prefix:' : 'Id:',
107
+ 'wp-staging'
108
+ )
109
+ ?>
110
+ </strong>
111
+ <?php echo $backup->getId() ?>
112
+ </li>
113
+ <li>
114
+ <strong><?php _e('Created on:', 'wp-staging') ?></strong>
115
+ <?php echo $this->transformToWpFormat($backup->getCreatedAt()) ?>
116
+ <?php if ($backup->getUpdatedAt()) : ?>
117
+ &nbsp; | &nbsp;<strong><?php _e('Updated on:', 'wp-staging') ?></strong>
118
+ <?php echo $this->transformToWpFormat($backup->getUpdatedAt()) ?>
119
+ <?php endif ?>
120
+ </li>
121
+ <?php if ($backup->getNotes()) : ?>
122
+ <li>
123
+ <strong><?php _e('Notes:', 'wp-staging') ?></strong><br/>
124
+ <?php echo nl2br($backup->getNotes()) ?>
125
+ </li>
126
+ <?php endif ?>
127
+ <?php if (!empty($size = $backup->getFileSize())) : ?>
128
+ <li>
129
+ <strong><?php _e('Size:', 'wp-staging') ?></strong><br/>
130
+ <?php echo esc_html($size); ?>
131
+ </li>
132
+ <?php endif ?>
133
+ </ul>
134
+ </div>
135
+ </div>
136
+ <?php endforeach ?>
137
+ </div>
138
+
139
+ <div id="wpstg--modal--database--new" data-confirmButtonText="<?php _e('Take New Database Backup', 'wp-staging') ?>" style="display: none">
140
+ <label for="backup_type_database" style="display:none;"><?php _e('Backup Type', 'wp-staging') ?></label>
141
+ <div style="padding: .75em; margin: 1em auto;display:none;">
142
+ <label style="margin-right: .5em;">
143
+ <input type="radio" name="backup_type" id="backup_type_database" value="database" checked/>
144
+ <?php _e('Database Only', 'wp-staging') ?>
145
+ </label>
146
+ <label>
147
+ <input type="radio" name="backup_type" id="backup_type_site" value="site"/>
148
+ <?php _e('Files and Database', 'wp-staging') ?>
149
+ </label>
150
+ </div>
151
+ <label for="wpstg-backup-name-input"><?php _e('Backup Name', 'wp-staging') ?></label>
152
+ <input id="wpstg-backup-name-input" name="backup_name" class="swal2-input" placeholder="<?php _e('Name your backup for better distinction', 'wp-staging') ?>">
153
+ <label for="wpstg-backup-notes-textarea"><?php _e('Additional Notes', 'wp-staging') ?></label>
154
+ <textarea id="wpstg-backup-notes-textarea" name="backup_note" class="swal2-textarea" placeholder="<?php _e("Add an optional description e.g.: 'before push of staging site', 'before updating plugin XY'", 'wp-staging') ?>"></textarea>
155
+
156
+ <div class="wpstg-advanced-options" style="text-align: left; display: none">
157
+ <a href="#" class="wpstg--tab--toggle" data-target=".wpstg-advanced-options-site" style="text-decoration: none;">
158
+ <span style="margin-right: .25em">►</span>
159
+ <?php _e('Advanced Options', 'wp-staging') ?>
160
+ </a>
161
+ <?php _e('(click to expand)', 'wp-staging') ?>
162
+
163
+ <div class="wpstg-advanced-options-site" style="display: none; padding-left: .75em;">
164
+ <label style="display: block;margin: .5em 0;">
165
+ <input type="checkbox" name="includedDirectories[]" value="<?php echo $directories['uploads'] ?>" checked/>
166
+ <?php _e('Export Media Library', 'wp-staging') ?>
167
+ </label>
168
+ <label style="display: block;margin: .5em 0;">
169
+ <input type="checkbox" name="includedDirectories[]" value="<?php echo $directories['themes'] ?>" checked/>
170
+ <?php _e('Export Themes', 'wp-staging') ?>
171
+ </label>
172
+ <label style="display: block;margin: .5em 0;">
173
+ <input type="checkbox" name="includedDirectories[]" value="<?php echo $directories['muPlugins'] ?>" checked/>
174
+ <?php _e('Export Must-Use Plugins', 'wp-staging') ?>
175
+ </label>
176
+ <label style="display: block;margin: .5em 0;">
177
+ <input type="checkbox" name="includedDirectories[]" value="<?php echo $directories['plugins'] ?>" checked/>
178
+ <?php _e('Export Plugins', 'wp-staging') ?>
179
+ </label>
180
+ <label style="display: block;margin: .5em 0;">
181
+ <input type="checkbox" name="export_database" value="true" checked/>
182
+ <?php _e('Exporting Database', 'wp-staging') ?>
183
+ </label>
184
+ <input type="hidden" name="wpContentDir" value="<?php echo $directories['wpContent'] ?>"/>
185
+ <input type="hidden" name="wpStagingDir" value="<?php echo $directories['wpStaging'] ?>"/>
186
+ <?php unset($directories['wpContent'], $directories['wpStaging']) ?>
187
+ <input type="hidden" name="availableDirectories" value="<?php echo implode('|', $directories) ?>"/>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ <div id="wpstg--modal--backup--process" data-cancelButtonText="<?php _e('CANCEL', 'wp-staging') ?>" style="display: none">
192
+ <span class="wpstg-loader"></span>
193
+ <h3 class="wpstg--modal--process--title" style="color: #a8a8a8;margin: .25em 0;">
194
+ <?php _e('Processing...', 'wp-staging') ?>
195
+ </h3>
196
+ <div style="margin: .5em 0; color: #a8a8a8;">
197
+ <?php
198
+ echo sprintf(
199
+ __('Progress %s - Elapsed time %s', 'wp-staging'),
200
+ '<span class="wpstg--modal--process--percent">0</span>%',
201
+ '<span class="wpstg--modal--process--elapsed-time">0:00</span>'
202
+ )
203
+ ?>
204
+ </div>
205
+ <div class="wpstg--modal--process--generic-problem"></div>
206
+ <button
207
+ class="wpstg--modal--process--logs--tail"
208
+ data-txt-bad="<?php echo sprintf(
209
+ __('(%s) Critical, (%s) Errors, (%s) Warnings. Show Logs', 'wp-staging'),
210
+ '<span class=\'wpstg--modal--logs--critical-count\'>0</span>',
211
+ '<span class=\'wpstg--modal--logs--error-count\'>0</span>',
212
+ '<span class=\'wpstg--modal--logs--warning-count\'>0</span>'
213
+ ) ?>"
214
+ >
215
+ <?php _e('Show Logs', 'wp-staging') ?>
216
+ </button>
217
+ <div class="wpstg--modal--process--logs"></div>
218
+ </div>
219
+ <div id="wpstg--modal--backup--download" style="display: none">
220
+ <h2>{title}</h2>
221
+ <div class="wpstg--modal--download--logs--wrapper" style="display:none">
222
+ <button class="wpstg--modal--process--logs--tail">{btnTxtLog}</button>
223
+ <div class="wpstg--modal--process--logs"></div>
224
+ </div>
225
+ </div>
226
+ <div
227
+ id="wpstg--modal--backup--import"
228
+ data-confirmButtonText="<?php _e('IMPORT', 'wp-staging') ?>"
229
+ data-nextButtonText="<?php _e('NEXT', 'wp-staging') ?>"
230
+ data-cancelButtonText="<?php _e('CANCEL', 'wp-staging') ?>"
231
+ data-baseDirectory="<?php echo $directory->getPluginUploadsDirectory() ?>"
232
+ style="display: none"
233
+ >
234
+ <h2 class="wpstg--modal--backup--import--upload--title"><?php _e('Import Backup', 'wp-staging') ?></h2>
235
+ <div style="padding: .75em; margin: 1em auto;">
236
+ <div class="wpstg--modal--backup--import--upload">
237
+ <div class="wpstg--modal--backup--import--upload--container">
238
+ <div class="wpstg--uploader">
239
+ <input type="file" name="wpstg--backup--import--upload--file"/>
240
+ <img src="<?php echo $urlAssets . 'img/upload.svg' ?>" alt="Upload Image"/>
241
+ <span class="wpstg--backup--import--selected-file"></span>
242
+ <span class="wpstg--drag-or-upload">
243
+ <?php _e('Drag a new export file here or choose another option', 'wp-staging') ?>
244
+ </span>
245
+ <span class="wpstg--drag">
246
+ <?php _e('Drag and Drop a backup file to start import', 'wp-staging') ?>
247
+ </span>
248
+ <span class="wpstg--drop">
249
+ <?php _e('Drop export file here', 'wp-staging') ?>
250
+ </span>
251
+ <div class="wpstg--backup--import--options">
252
+ <button
253
+ class="wpstg-blue-primary wpstg-button wpstg-link-btn wpstg--backup--import--choose-option"
254
+ data-txtOther="<?php _e('Import from', 'wp-staging') ?>"
255
+ data-txtChoose="<?php _e('Choose an Option', 'wp-staging') ?>"
256
+ >
257
+ <?php _e('Import from', 'wp-staging') ?>
258
+ </button>
259
+ <ul>
260
+ <li>
261
+ <button class="wpstg--backup--import--option wpstg-blue-primary" data-option="file">
262
+ <?php _e('Local Computer', 'wp-staging') ?>
263
+ </button>
264
+ </li>
265
+ <li>
266
+ <button class="wpstg--backup--import--option wpstg-blue-primary" data-option="filesystem">
267
+ <?php _e('Upload Directory', 'wp-staging') ?>
268
+ </button>
269
+ </li>
270
+ </ul>
271
+ </div>
272
+ </div>
273
+ <div class="wpstg--modal--import--upload--process">
274
+ <div class="wpstg--modal--import--upload--progress"></div>
275
+ <h4 class="wpstg--modal--import--upload--progress--title">
276
+ <?php echo sprintf(__('Uploading %s%%...', 'wp-staging'), '<span></span>') ?>
277
+ </h4>
278
+ </div>
279
+ </div>
280
+ <div
281
+ class="wpstg--modal--backup--import--upload--status"
282
+ data-txt-uploading="<?php _e('Uploading...', 'wp-staging') ?>"
283
+ data-txt-done="<?php _e('Uploaded Successfully', 'wp-staging') ?>"
284
+ data-txt-error="<?php _e('Error! {message}', 'wp-staging') ?>"
285
+ >
286
+ </div>
287
+ </div>
288
+ <div class="wpstg--modal--backup--import--filesystem">
289
+ <button class="wpstg--backup--import--option wpstg-blue-primary" data-option="upload">
290
+ <?php _e('GO BACK', 'wp-staging') ?>
291
+ </button>
292
+ <div style="margin-top: .25em;font-size:14px;">
293
+ <?php
294
+ echo __('Upload import file to server directory:', 'wp-staging') . '<br>';
295
+ echo $directory->getPluginUploadsDirectory();
296
+ ?>
297
+ </div>
298
+ <ul></ul>
299
+ </div>
300
+ <div class="wpstg--modal--backup--import--search-replace--wrapper">
301
+ <div class="wpstg--modal--backup--import--search-replace--info">
302
+ <p><?php _e('Search & Replace strings in the database. (Fully optional).', 'wp-staging') ?></p>
303
+ <p><?php _e('Leave empty and WP Staging handles this automatically.', 'wp-staging') ?></p>
304
+ </div>
305
+ <div class="wpstg--modal--backup--import--search-replace--input--container">
306
+ <div class="wpstg--modal--backup--import--search-replace--input-group">
307
+ <input name="wpstg__backup__import__search[{i}]" data-index="{i}" class="wpstg--backup--import--search" placeholder="Search"/>
308
+ <input name="wpstg__backup__import__replace[{i}]" data-index="{i}" class="wpstg--backup--import--replace" placeholder="Replace"/>
309
+ </div>
310
+ </div>
311
+ <button class="wpstg--modal--backup--import--search-replace--new"><?php _e('+', 'wp-staging') ?></button>
312
+ </div>
313
+ </div>
314
+ </div>
315
+
316
+ <div
317
+ id="wpstg--js--translations"
318
+ style="display:none;"
319
+ data-modal-txt-critical="<?php _e('Critical', 'wp-staging') ?>"
320
+ data-modal-txt-errors="<?php _e('Error(s)', 'wp-staging') ?>"
321
+ data-modal-txt-warnings="<?php _e('Warning(s)', 'wp-staging') ?>"
322
+ data-modal-txt-and="<?php _e('and', 'wp-staging') ?>"
323
+ data-modal-txt-found="<?php _e('Found', 'wp-staging') ?>"
324
+ data-modal-txt-show-logs="<?php _e('Show Logs', 'wp-staging') ?>"
325
+ data-modal-logs-title="<?php _e(
326
+ '{critical} Critical, {errors} Error(s) and {warnings} Warning(s) Found',
327
+ 'wp-staging'
328
+ ) ?>"
329
+ ></div>
330
+
331
+ <div id="wpstg-delete-confirmation"></div>
Backend/views/feedback/deactivate-feedback.php CHANGED
@@ -1,14 +1,14 @@
1
- <?php
2
  $reasons = [
3
- 1 => '<li><label><input type="radio" name="wpstg_disable_reason" value="temporary"/>' . __('Only temporary', 'wp-staging') . '</label></li>',
4
- //2 => '<li><label><input type="radio" name="wpstg_disable_reason" value="stopped showing social buttons"/>' . __('I do not use it any longer ', 'wp-staging') . '</label></li>',
5
- 3 => '<li><label><input type="radio" name="wpstg_disable_reason" value="missing feature"/>' . __('Miss a feature', 'wp-staging') . '</label></li>
6
  <li><input type="text" name="wpstg_disable_text[]" value="" placeholder="Please describe the feature"/></li>',
7
- 4 => '<li><label><input type="radio" name="wpstg_disable_reason" value="technical issue"/>' . __('Technical Issue', 'wp-staging') . '</label></li>
8
  <li><textarea name="wpstg_disable_text[]" placeholder="' . __('Can we help? Please describe your problem', 'wp-staging') . '"></textarea></li>',
9
- 5 => '<li><label><input type="radio" name="wpstg_disable_reason" value="other plugin"/>' . __('Switched to another plugin/staging solution', 'wp-staging') . '</label></li>
10
  <li><input type="text" name="wpstg_disable_text[]" value="" placeholder="Name of the plugin"/></li>',
11
- 6 => '<li><label><input type="radio" name="wpstg_disable_reason" value="other"/>' . __('Other reason', 'wp-staging') . '</label></li>
12
  <li><textarea name="wpstg_disable_text[]" placeholder="' . __('Please specify, if possible', 'wp-staging') . '"></textarea></li>',
13
  ];
14
  shuffle($reasons);
@@ -17,21 +17,21 @@ shuffle($reasons);
17
 
18
  <div id="wpstg-feedback-overlay" style="display: none;">
19
  <div id="wpstg-feedback-content">
20
- <form action="" method="post">
21
- <h3><strong><?php _e('Please let us know why you are deactivating:', 'wp-staging'); ?></strong></h3>
22
- <ul>
23
- <?php
24
- foreach ($reasons as $reason){
25
  echo $reason;
26
  }
27
  ?>
28
- </ul>
29
- <?php if ($email) : ?>
30
- <input type="hidden" name="wpstg_disable_from" value="<?php echo $email; ?>"/>
31
- <?php endif; ?>
32
- <input id="wpstg-feedback-submit" class="button button-primary" type="submit" name="wpstg_disable_submit" value="<?php _e('Submit & Deactivate', 'wp-staging'); ?>"/>
33
- <a class="button"><?php _e('Only Deactivate', 'wp-staging'); ?></a>
34
- <a class="wpstg-feedback-not-deactivate" href="#"><?php _e('Don\'t deactivate', 'wp-staging'); ?></a>
35
- </form>
36
  </div>
37
  </div>
1
+ <?php
2
  $reasons = [
3
+ 1 => '<li><label><input type="radio" name="wpstg_disable_reason" value="temporary"/>' . __('Only temporary', 'wp-staging') . '</label></li>',
4
+ //2 => '<li><label><input type="radio" name="wpstg_disable_reason" value="stopped showing social buttons"/>' . __('I do not use it any longer ', 'wp-staging') . '</label></li>',
5
+ 3 => '<li><label><input type="radio" name="wpstg_disable_reason" value="missing feature"/>' . __('Miss a feature', 'wp-staging') . '</label></li>
6
  <li><input type="text" name="wpstg_disable_text[]" value="" placeholder="Please describe the feature"/></li>',
7
+ 4 => '<li><label><input type="radio" name="wpstg_disable_reason" value="technical issue"/>' . __('Technical Issue', 'wp-staging') . '</label></li>
8
  <li><textarea name="wpstg_disable_text[]" placeholder="' . __('Can we help? Please describe your problem', 'wp-staging') . '"></textarea></li>',
9
+ 5 => '<li><label><input type="radio" name="wpstg_disable_reason" value="other plugin"/>' . __('Switched to another plugin/staging solution', 'wp-staging') . '</label></li>
10
  <li><input type="text" name="wpstg_disable_text[]" value="" placeholder="Name of the plugin"/></li>',
11
+ 6 => '<li><label><input type="radio" name="wpstg_disable_reason" value="other"/>' . __('Other reason', 'wp-staging') . '</label></li>
12
  <li><textarea name="wpstg_disable_text[]" placeholder="' . __('Please specify, if possible', 'wp-staging') . '"></textarea></li>',
13
  ];
14
  shuffle($reasons);
17
 
18
  <div id="wpstg-feedback-overlay" style="display: none;">
19
  <div id="wpstg-feedback-content">
20
+ <form action="" method="post">
21
+ <h3><strong><?php _e('Please let us know why you are deactivating:', 'wp-staging'); ?></strong></h3>
22
+ <ul>
23
+ <?php
24
+ foreach ($reasons as $reason) {
25
  echo $reason;
26
  }
27
  ?>
28
+ </ul>
29
+ <?php if ($email) : ?>
30
+ <input type="hidden" name="wpstg_disable_from" value="<?php echo $email; ?>"/>
31
+ <?php endif; ?>
32
+ <input id="wpstg-feedback-submit" class="button button-primary" type="submit" name="wpstg_disable_submit" value="<?php _e('Submit & Deactivate', 'wp-staging'); ?>"/>
33
+ <a class="button"><?php _e('Only Deactivate', 'wp-staging'); ?></a>
34
+ <a class="wpstg-feedback-not-deactivate" href="#"><?php _e('Don\'t deactivate', 'wp-staging'); ?></a>
35
+ </form>
36
  </div>
37
  </div>
Backend/views/notices/beta.php CHANGED
@@ -18,4 +18,4 @@
18
  </li>
19
  </ul>
20
  </div>
21
- <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-beta.js"?>"></script>
18
  </li>
19
  </ul>
20
  </div>
21
+ <script type="text/javascript" src="<?php echo $this->assets->getAssetUrl("js/dist/wpstg-admin-beta.js") ?>"></script>
Backend/views/notices/disabled-cache.php DELETED
@@ -1,32 +0,0 @@
1
- <?php
2
- /**
3
- * @var $this \WPStaging\Backend\Notices\Notices
4
- * @see \WPStaging\Backend\Notices\Notices::showNotices
5
- */
6
- ?>
7
- <div class="notice wpstg-cache-notice" style="border-left: 4px solid #ffba00;">
8
- <p>
9
- <strong style="margin-bottom: 10px;"><?php _e( 'Cache Disabled', 'wp-staging' ); ?></strong> <br/>
10
- <?php _e( 'WP STAGING disabled the cache on this staging site by setting the constant WP_CACHE to false in the wp-config.php.', 'wp-staging' ); ?>
11
- </p>
12
- <p>
13
- <a href="javascript:void(0);" class="wpstg_hide_cache_notice" title="Close this message"
14
- style="font-weight:bold;">
15
- <?php _e('Close this message', 'wp-staging') ?>
16
- </a>
17
- </p>
18
- </div>
19
- <?php
20
- /*
21
- * Cache-burst mechanism to ensure the browser cache will not get in the way
22
- * of the script working properly when there's updates.
23
- */
24
- $file = trailingslashit($this->path) . "public/js/wpstg-admin-cache-notice.js";
25
-
26
- if (file_exists($file)) {
27
- $version = (string)@filemtime($file);
28
- } else {
29
- $version = '{{version}}';
30
- }
31
- ?>
32
- <script src="<?php echo esc_url(trailingslashit($this->url) . "js/wpstg-admin-cache-notice.js?v=$version") ?>"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/views/notices/disabled-items-notice.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $this \WPStaging\Backend\Notices\Notices
4
+ * @see \WPStaging\Backend\Notices\Notices::showNotices
5
+ */
6
+ ?>
7
+ <div class="notice wpstg-disabled-items-notice" style="border-left: 4px solid #ffba00; padding: 8px; padding-left: 16px; padding-top: 12px;">
8
+ <strong style="margin-bottom: 10px;"><?php _e('WP STAGING Notes:', 'wp-staging'); ?></strong> <br/>
9
+ <ol style="margin-left: 12px;">
10
+ <li> <?php echo sprintf(__('Disabled the cache by setting the constant WP_CACHE to FALSE in the wp-config.php and excluding wp-content/cache. <a href="%s" target="_blank">How to activate caching</a>', 'wp-staging'), 'https://wp-staging.com/docs/how-to-activate-caching-on-staging-site/') ?></li>
11
+ <?php if ($outgoingMailsDisabled) : ?>
12
+ <li> <?php echo sprintf(__('Disabled outgoing emails. <a href="%s" target="_blank">How to activate mails</a>', 'wp-staging'), 'https://wp-staging.com/docs/how-to-activate-email-sending-on-the-staging-site/')?></li>
13
+ <?php endif; ?>
14
+ <?php if (count($excludedPlugins) > 0) : ?>
15
+ <li>
16
+ <?php _e('Excluded the following plugins:', 'wp-staging') ?>
17
+ <ul style="margin-left: 0px; margin-top: 4px;">
18
+ <?php foreach ($excludedPlugins as $excludedPlugin) : ?>
19
+ <li> <span style="font-size: 13px;">➜</span> <?php echo $excludedPlugin; ?></li>
20
+ <?php endforeach; ?>
21
+ </ul>
22
+ </li>
23
+ <?php endif; ?>
24
+ </ol>
25
+ <p>
26
+ <a href="javascript:void(0);" class="wpstg_hide_disabled_items_notice" title="Close this message"
27
+ style="font-weight:bold;">
28
+ <?php _e('Close this message', 'wp-staging') ?>
29
+ </a>
30
+ </p>
31
+ </div>
32
+ <script>
33
+ jQuery(document).ready(function ($) {
34
+ jQuery(document).on('click', '.wpstg_hide_disabled_items_notice', function (e) {
35
+ e.preventDefault();
36
+ jQuery.ajax({
37
+ url: ajaxurl,
38
+ type: 'POST',
39
+ data: {
40
+ action: 'wpstg_hide_disabled_items_notice'
41
+ },
42
+ error: function error(xhr, textStatus, errorThrown) {
43
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
44
+ console.log(textStatus);
45
+ alert('Unknown error. Please get in contact with us to solve it support@wp-staging.com');
46
+ },
47
+ success: function success(data) {
48
+ jQuery('.wpstg-disabled-items-notice').slideUp('fast');
49
+ return true;
50
+ },
51
+ statusCode: {
52
+ 404: function _() {
53
+ alert('Something went wrong; can\'t find ajax request URL! Please get in contact with us to solve it support@wp-staging.com');
54
+ },
55
+ 500: function _() {
56
+ alert('Something went wrong; internal server error while processing the request! Please get in contact with us to solve it support@wp-staging.com');
57
+ }
58
+ }
59
+ });
60
+ });
61
+ });
62
+ </script>
Backend/views/notices/low-memory-limit.php CHANGED
@@ -1,3 +1,3 @@
1
  <div class="wpstg-warning">
2
- <?php echo sprintf(__( 'It\'s recommended to increase the memory limit to 256M or more! If cloning/pushing <strong>fails</strong> <a href="%s" target="_blank">increase the memory limit</a>.', 'wp-staging' ), 'https://wp-staging.com/docs/php-fatal-error-allowed-memory-size-of-134217728-bytes-exhausted/'); ?>
3
  </div>
1
  <div class="wpstg-warning">
2
+ <?php echo sprintf(__('It\'s recommended to increase the memory limit to 256M or more! If cloning/pushing <strong>fails</strong> <a href="%s" target="_blank">increase the memory limit</a>.', 'wp-staging'), 'https://wp-staging.com/docs/php-fatal-error-allowed-memory-size-of-134217728-bytes-exhausted/'); ?>
3
  </div>
Backend/views/notices/no-license-key.php CHANGED
@@ -1,10 +1,11 @@
1
  <div class="error">
2
  <p>
3
  <?php
4
- echo sprintf( __(
5
- 'You have no license key entered. WP STAGING Pro needs a valid license key to be activated. You can purchase the license from here:' .
6
- '<br/> <a href="%1$s" target="_blank">%1$s</a>', 'wp-staging'), 'https://wp-staging.com'
7
- );
 
8
  ?>
9
  </p>
10
  </div>
1
  <div class="error">
2
  <p>
3
  <?php
4
+ echo sprintf(__(
5
+ 'You have no license key entered. WP STAGING Pro needs a valid license key to be activated. You can purchase the license from here:' .
6
+ '<br/> <a href="%1$s" target="_blank">%1$s</a>',
7
+ 'wp-staging'
8
+ ), 'https://wp-staging.com');
9
  ?>
10
  </p>
11
  </div>
Backend/views/notices/outdated-wp-staging-hooks.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $this \WPStaging\Backend\Notices\Notices
4
+ * @see \WPStaging\Backend\Notices\Notices::showNotices
5
+ */
6
+ ?>
7
+ <div class="notice wpstg-hooks-outdated-notice notice notice-error wpstg-error">
8
+ <p>
9
+ <strong style="margin-bottom: 10px;"><?php _e('WP Staging Hooks Outdated!', 'wp-staging'); ?></strong> <br/>
10
+ <?php echo sprintf(__('You are using an outdated version of the WP STAGING hooks plugin. The filters for <code>wpstg_clone_excl_folders</code> and <code>wpstg_clone_mu_excl_folders</code> have been changed. Download the latest version from <a href="%s" target="_blank">here</a> and adjust your filters.', 'wp-staging'), 'https://github.com/wp-staging/wp-staging-hooks'); ?>
11
+ </p>
12
+ </div>
Backend/views/notices/poll.php CHANGED
@@ -37,4 +37,4 @@
37
  </ul>
38
  </div>
39
 
40
- <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-poll.js"?>"></script>
37
  </ul>
38
  </div>
39
 
40
+ <script type="text/javascript" src="<?php echo $this->assets->getAssetUrl("js/wpstg-admin-poll.js") ?>"></script>
Backend/views/notices/rating.php CHANGED
@@ -9,13 +9,13 @@
9
  <div class="wpstg-welcome-video-container" style="width: 375px; height: 210px;">
10
  <div style="width: 100%; height: 100%; display: flex; background: #000; position: relative; align-items: center; justify-content: center; z-index: 0;">
11
  <div class="wpstg-yt-thumbnail-container" style="display: flex; width: 100%; height: 100%; z-index: 100; cursor: pointer; justify-content: center; align-items: center;" id="welcomeNoticeFree">
12
- <img style="width: 100%; object-fit: fill; z-index: 0; cursor: pointer;" alt="WP Staging Welcome Video Thumbnail" src="<?php echo esc_url(plugins_url('../public/img/thumbnail.jpg', dirname(__FILE__))) ?>" />
13
  <button style="cursor: pointer; position: absolute; width: 68px; height: 48px; background: transparent; border: 0px solid transparent; -moz-transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); -webkit-transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); z-index: 63;">
14
  <svg height="100%" version="1.1" viewBox="0 0 68 48" width="100%"><path class="wpstg-yt-button-svg" style="-moz-transition: fill .1s cubic-bezier(0.4,0.0,1,1),fill-opacity .1s cubic-bezier(0.4,0.0,1,1); -webkit-transition: fill .1s cubic-bezier(0.4,0.0,1,1),fill-opacity .1s cubic-bezier(0.4,0.0,1,1); transition: fill .1s cubic-bezier(0.4,0.0,1,1),fill-opacity .1s cubic-bezier(0.4,0.0,1,1); fill: #212121; fill-opacity: .8; height: 100%; left: 0; position: absolute; top: 0; width: 100%;" d="M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z" fill="#f00"></path><path d="M 45,24 27,14 27,34" fill="#fff"></path></svg>
15
  </button>
16
  </div>
17
  <div style="position: absolute; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;">
18
- <img style="width: 90px; height: 90px; z-index: 10;" width="90px" src="<?php echo esc_url(plugins_url('../public/img/tail-spin.svg', dirname(__FILE__))) ?>">
19
  </div>
20
  </div>
21
  </div>
@@ -72,17 +72,4 @@
72
  'regards': "<?php _e("Your WP STAGING Team") ?>"
73
  };
74
  </script>
75
- <?php
76
- /*
77
- * Cache-burst mechanism to ensure the browser cache will not get in the way
78
- * of the script working properly when there's updates.
79
- */
80
- $file = trailingslashit($this->path) . "public/js/wpstg-admin-rating.js";
81
-
82
- if (file_exists($file)) {
83
- $version = (string)@filemtime($file);
84
- } else {
85
- $version = '2.7.6';
86
- }
87
- ?>
88
- <script src="<?php echo esc_url(trailingslashit($this->url) . "js/wpstg-admin-rating.js?v=$version") ?>"></script>
9
  <div class="wpstg-welcome-video-container" style="width: 375px; height: 210px;">
10
  <div style="width: 100%; height: 100%; display: flex; background: #000; position: relative; align-items: center; justify-content: center; z-index: 0;">
11
  <div class="wpstg-yt-thumbnail-container" style="display: flex; width: 100%; height: 100%; z-index: 100; cursor: pointer; justify-content: center; align-items: center;" id="welcomeNoticeFree">
12
+ <img style="width: 100%; object-fit: fill; z-index: 0; cursor: pointer;" alt="WP Staging Welcome Video Thumbnail" src="<?php echo esc_url(plugins_url('/assets/img/thumbnail.jpg', dirname(dirname(__DIR__)))) ?>" />
13
  <button style="cursor: pointer; position: absolute; width: 68px; height: 48px; background: transparent; border: 0px solid transparent; -moz-transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); -webkit-transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); z-index: 63;">
14
  <svg height="100%" version="1.1" viewBox="0 0 68 48" width="100%"><path class="wpstg-yt-button-svg" style="-moz-transition: fill .1s cubic-bezier(0.4,0.0,1,1),fill-opacity .1s cubic-bezier(0.4,0.0,1,1); -webkit-transition: fill .1s cubic-bezier(0.4,0.0,1,1),fill-opacity .1s cubic-bezier(0.4,0.0,1,1); transition: fill .1s cubic-bezier(0.4,0.0,1,1),fill-opacity .1s cubic-bezier(0.4,0.0,1,1); fill: #212121; fill-opacity: .8; height: 100%; left: 0; position: absolute; top: 0; width: 100%;" d="M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z" fill="#f00"></path><path d="M 45,24 27,14 27,34" fill="#fff"></path></svg>
15
  </button>
16
  </div>
17
  <div style="position: absolute; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;">
18
+ <img style="width: 90px; height: 90px; z-index: 10;" width="90px" src="<?php echo esc_url(plugins_url('/assets/img/tail-spin.svg', dirname(dirname(__DIR__)))) ?>">
19
  </div>
20
  </div>
21
  </div>
72
  'regards': "<?php _e("Your WP STAGING Team") ?>"
73
  };
74
  </script>
75
+ <script src="<?php echo esc_url($this->assets->getAssetsUrlWithVersion("js/dist/wpstg-admin-rating.js", '2.7.6')) ?>"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
Backend/views/notices/staging-directory-permission-problem.php CHANGED
@@ -5,7 +5,7 @@
5
  %1$s is not write and/or readable.
6
  <br>
7
  Check if the folder <strong>%1$s</strong> is writeable by php user %2$s or www-data .
8
- File permissions should be chmod 755 or 777.','wp-staging'), ABSPATH, getenv('USERNAME') ?: getenv('USER') );
9
- ?>
10
  </p>
11
  </div>
5
  %1$s is not write and/or readable.
6
  <br>
7
  Check if the folder <strong>%1$s</strong> is writeable by php user %2$s or www-data .
8
+ File permissions should be chmod 755 or 777.', 'wp-staging'), ABSPATH, getenv('USERNAME') ?: getenv('USER'));
9
+ ?>
10
  </p>
11
  </div>
Backend/views/notices/transient.php CHANGED
@@ -3,7 +3,7 @@
3
  <?php
4
  echo esc_html(
5
  ($deactivatedNoticeID === '1') ?
6
- __("WP Staging and WP STAGING Pro cannot both be active. We've automatically deactivated WP STAGING.", "wp-staging"):
7
  __("WP Staging and WP STAGING Pro cannot both be active. We've automatically deactivated WP STAGING Pro.", "wp-staging")
8
  )
9
  ?>
3
  <?php
4
  echo esc_html(
5
  ($deactivatedNoticeID === '1') ?
6
+ __("WP Staging and WP STAGING Pro cannot both be active. We've automatically deactivated WP STAGING.", "wp-staging") :
7
  __("WP Staging and WP STAGING Pro cannot both be active. We've automatically deactivated WP STAGING Pro.", "wp-staging")
8
  )
9
  ?>
Backend/views/notices/wp-version-compatible-message.php CHANGED
@@ -1,12 +1,13 @@
1
  <div class="wpstg-error">
2
  <p>
3
  <?php
4
- echo sprintf( __(
5
  'Your version of WP STAGING has not been tested with WordPress %2$s.' .
6
  '<br/><br/>WP STAGING has an enterprise-level quality control that performs a compatibility audit on every new WordPress release.' .
7
  '<br/>We prioritize testing the Pro version of the plugin first, which receives the compatibility audit earlier than the Free version. If you are in a rush, upgrade to Pro today to get the latest version of WPSTAGING.' .
8
- '<p><a href="%1$s" target="_blank"><strong>Get the latest version Now</strong></a>.', 'wp-staging'), 'https://wp-staging.com', get_bloginfo( 'version' )
9
- );
 
10
  ?>
11
  </p>
12
  </div>
1
  <div class="wpstg-error">
2
  <p>
3
  <?php
4
+ echo sprintf(__(
5
  'Your version of WP STAGING has not been tested with WordPress %2$s.' .
6
  '<br/><br/>WP STAGING has an enterprise-level quality control that performs a compatibility audit on every new WordPress release.' .
7
  '<br/>We prioritize testing the Pro version of the plugin first, which receives the compatibility audit earlier than the Free version. If you are in a rush, upgrade to Pro today to get the latest version of WPSTAGING.' .
8
+ '<p><a href="%1$s" target="_blank"><strong>Get the latest version Now</strong></a>.',
9
+ 'wp-staging'
10
+ ), 'https://wp-staging.com', get_bloginfo('version'));
11
  ?>
12
  </p>
13
  </div>
Backend/views/notices/wrong-scheme.php CHANGED
@@ -1,7 +1,7 @@
1
  <div class="wpstg-error">
2
  <p>
3
- <strong><?php _e( 'WP Staging HTTP/HTTPS Scheme Error:', 'wp-staging' ); ?></strong>
4
- <?php echo sprintf(__( 'Go to <a href="%s" target="_blank">Settings > General</a> and make sure that WordPress Address (URL) and Site Address (URL) do both contain the same http / https scheme.', 'wp-staging' ), admin_url() . 'options-general.php'); ?>
5
  <br>
6
- <?php _e( 'Otherwise your staging site will not be reachable after creation.', 'wp-staging' ); ?></p>
7
  </div>
1
  <div class="wpstg-error">
2
  <p>
3
+ <strong><?php _e('WP Staging HTTP/HTTPS Scheme Error:', 'wp-staging'); ?></strong>
4
+ <?php echo sprintf(__('Go to <a href="%s" target="_blank">Settings > General</a> and make sure that WordPress Address (URL) and Site Address (URL) do both contain the same http / https scheme.', 'wp-staging'), admin_url() . 'options-general.php'); ?>
5
  <br>
6
+ <?php _e('Otherwise your staging site will not be reachable after creation.', 'wp-staging'); ?></p>
7
  </div>
Backend/views/settings/main-settings.php CHANGED
@@ -1,3 +1,4 @@
 
1
  <div class="wpstg_admin">
2
  <?php require_once(WPSTG_PLUGIN_DIR . 'Backend/views/_main/header.php'); ?>
3
 
@@ -8,7 +9,7 @@
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
  [
1
+ <?php settings_errors(); ?>
2
  <div class="wpstg_admin">
3
  <?php require_once(WPSTG_PLUGIN_DIR . 'Backend/views/_main/header.php'); ?>
4
 
9
  $activeTab = (isset($_GET["tab"]) && array_key_exists($_GET["tab"], $tabs)) ? $_GET["tab"] : "general";
10
 
11
  # Loop through tabs
12
+ foreach ($tabs as $id => $name) :
13
  $url = esc_url(
14
  add_query_arg(
15
  [
Backend/views/settings/tabs/general.php CHANGED
@@ -4,7 +4,7 @@
4
  <?php
5
  settings_fields("wpstg_settings");
6
 
7
- foreach ($tabs as $id => $name):
8
  if ($id === 'mail-settings') {
9
  continue;
10
  }
@@ -389,8 +389,23 @@
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();
4
  <?php
5
  settings_fields("wpstg_settings");
6
 
7
+ foreach ($tabs as $id => $name) :
8
  if ($id === 'mail-settings') {
9
  continue;
10
  }
389
  </tbody>
390
  </table>
391
  </div>
392
+ <?php
393
  endforeach;
394
+ // show this option only on the staging site
395
+ if ($this->siteInfo->isStaging()) :
396
+ ?>
397
+ <div class="wpstg-settings-row">
398
+ <b class="wpstg-settings-title"><?php _e('Allow Cloning (Staging Site Only)', 'wp-staging') ?></b>
399
+ <div class="wpstg-settings-form-group">
400
+ <p class="wpstg-settings-message">
401
+ <?php _e('Check this box to make this staging site cloneable.', 'wp-staging') ?>
402
+ <?php echo sprintf(__("If you would like to know more about cloning staging sites check out <a href='%s' target='_new'>this article</a>.", 'wp-staging'), 'https://wp-staging.com/docs/cloning-a-staging-site-testing-push-method/'); ?>
403
+ </p>
404
+ <input type="checkbox" id="wpstg-is-staging-cloneable" name="wpstg_settings[isStagingSiteCloneable]" class="wpstg-checkbox wpstg-settings-field" value="true" <?php echo $this->siteInfo->isCloneable() ? 'checked' : '' ?> />
405
+ </div>
406
+ </div>
407
+ <?php
408
+ endif;
409
  // Show submit button any tab but add-ons
410
  if ($activeTab !== "add-ons") {
411
  submit_button();
Backend/views/settings/tabs/mail-settings.php CHANGED
@@ -1,5 +1,5 @@
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/>
1
  <form class="wpstg-mail-settings-form" method="post">
2
+ <?php $emailsAllowed = !((bool)wpstgGetCloneSettings(\WPStaging\Framework\Staging\FirstRun::MAILS_DISABLED_KEY)); ?>
3
  <p>
4
  <strong class="wpstg-fs-14"> <?php _e('Mail Delivery Setting', 'wp-staging'); ?></strong>
5
  <br/>
Backend/views/tools/index.php CHANGED
@@ -8,7 +8,7 @@
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
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
Backend/views/tools/tabs/import_export.php CHANGED
@@ -9,7 +9,7 @@
9
  <div class="inside">
10
  <p>
11
  <?php _e(
12
- "Export the WP-Staging settings for this site as a .json file. ".
13
  "This allows you to easily import the configuration into another site.",
14
  "wp-staging"
15
  )?>
@@ -37,7 +37,7 @@
37
  <div class="inside">
38
  <p>
39
  <?php _e(
40
- "Import the WP-Staging settings from a .json file. This file can be obtained ".
41
  "by exporting the settings on another site using the form above.",
42
  "wp-staging"
43
  )?>
9
  <div class="inside">
10
  <p>
11
  <?php _e(
12
+ "Export the WP-Staging settings for this site as a .json file. " .
13
  "This allows you to easily import the configuration into another site.",
14
  "wp-staging"
15
  )?>
37
  <div class="inside">
38
  <p>
39
  <?php _e(
40
+ "Import the WP-Staging settings from a .json file. This file can be obtained " .
41
  "by exporting the settings on another site using the form above.",
42
  "wp-staging"
43
  )?>
Backend/views/tools/tabs/system_info.php CHANGED
@@ -1,6 +1,6 @@
1
  <form action="<?php echo esc_url(admin_url("admin-post.php?action=wpstg_download_sysinfo"))?>" method="post" dir="ltr">
2
  <textarea class="wpstg-sysinfo" readonly="readonly" id="system-info-textarea" name="wpstg-sysinfo" title="To copy the system info, click below then press Ctrl + C (PC) or Cmd + C (Mac)."><?php echo \WPStaging\Core\WPStaging::getInstance()->get("systemInfo")?></textarea>
3
  <p class="submit">
4
- <?php submit_button("Download System Info File", "primary", "wpstg-download-sysinfo", false )?>
5
  </p>
6
  </form>
1
  <form action="<?php echo esc_url(admin_url("admin-post.php?action=wpstg_download_sysinfo"))?>" method="post" dir="ltr">
2
  <textarea class="wpstg-sysinfo" readonly="readonly" id="system-info-textarea" name="wpstg-sysinfo" title="To copy the system info, click below then press Ctrl + C (PC) or Cmd + C (Mac)."><?php echo \WPStaging\Core\WPStaging::getInstance()->get("systemInfo")?></textarea>
3
  <p class="submit">
4
+ <?php submit_button("Download System Info File", "primary", "wpstg-download-sysinfo", false)?>
5
  </p>
6
  </form>
Backend/views/welcome/welcome.php CHANGED
@@ -1,16 +1,16 @@
1
  <div class="" id="wpstg-welcome">
2
  <div class="wpstg-welcome-container">
3
  <h2 class="wpstg-h2">
4
- <span class="wpstg-heading-pro"><?php _e( 'WP STAGING Pro', 'wp-staging' ); ?></span><?php _e( ' - Copy Themes & Plugins from Staging to Live Site', 'wp-staging' ); ?>
5
  </h2>
6
- <li><strong>Cloning</strong> - <?php _e( 'Create a clone of your website with a simple click', 'wp-staging' ); ?></li>
7
- <li><strong>Push Changes</strong> - <?php _e( 'Copy plugin and theme files from staging to live site', 'wp-staging' ); ?></li>
8
- <li><strong>Authentication</strong> - <?php _e( 'Staging Site is available to authenticated users only', 'wp-staging' ); ?></li>
9
- <li><strong>High Performance</strong> - <?php _e( 'Cloning process is fast and does not slow down website loading', 'wp-staging' ); ?></li>
10
- <li><strong>Secure</strong> - <?php _e( 'WP Staging is coded well for protection of your data', 'wp-staging' ); ?></li>
11
- <li><strong>Multisites</strong> - <?php _e( 'WP STAGING Pro allows to clone and push Multisites, (main site & sub sites)', 'wp-staging' ); ?></li>
12
  <a href="http://wp-staging.com/?utm_source=wpstg&utm_medium=addon_page&utm_term=click-wpstaging-pro&utm_campaign=wpstaging" target="_blank" class="wpstg-button green">Buy WP STAGING Pro</a>
13
  <a href="<?php echo admin_url(); ?>admin.php?page=wpstg_clone" target="_self" class="wpstg-ml-30px">Skip - Start Cloning</a>
14
- <div class="wpstg-footer"> <?php _e( 'Comes with our 30-day money back guarantee * You need to give us chance to resolve your issue first.', 'wp-staging' ); ?></div>
15
  </div>
16
  </div>
1
  <div class="" id="wpstg-welcome">
2
  <div class="wpstg-welcome-container">
3
  <h2 class="wpstg-h2">
4
+ <span class="wpstg-heading-pro"><?php _e('WP STAGING Pro', 'wp-staging'); ?></span><?php _e(' - Copy Themes & Plugins from Staging to Live Site', 'wp-staging'); ?>
5
  </h2>
6
+ <li><strong>Cloning</strong> - <?php _e('Create a clone of your website with a simple click', 'wp-staging'); ?></li>
7
+ <li><strong>Push Changes</strong> - <?php _e('Copy plugin and theme files from staging to live site', 'wp-staging'); ?></li>
8
+ <li><strong>Authentication</strong> - <?php _e('Staging Site is available to authenticated users only', 'wp-staging'); ?></li>
9
+ <li><strong>High Performance</strong> - <?php _e('Cloning process is fast and does not slow down website loading', 'wp-staging'); ?></li>
10
+ <li><strong>Secure</strong> - <?php _e('WP Staging is coded well for protection of your data', 'wp-staging'); ?></li>
11
+ <li><strong>Multisites</strong> - <?php _e('WP STAGING Pro allows to clone and push Multisites, (main site & sub sites)', 'wp-staging'); ?></li>
12
  <a href="http://wp-staging.com/?utm_source=wpstg&utm_medium=addon_page&utm_term=click-wpstaging-pro&utm_campaign=wpstaging" target="_blank" class="wpstg-button green">Buy WP STAGING Pro</a>
13
  <a href="<?php echo admin_url(); ?>admin.php?page=wpstg_clone" target="_self" class="wpstg-ml-30px">Skip - Start Cloning</a>
14
+ <div class="wpstg-footer"> <?php _e('Comes with our 30-day money back guarantee * You need to give us chance to resolve your issue first.', 'wp-staging'); ?></div>
15
  </div>
16
  </div>
Command/.gitkeep DELETED
File without changes
Command/Database/Export/ExportCommand.php DELETED
@@ -1,86 +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\Command\Database\Export;
7
-
8
- use Exception;
9
- use WPStaging\Pro\Library\Mysqldump\Mysqldump;
10
- use WPStaging\Vendor\Psr\Log\LoggerInterface;
11
- use WPStaging\Framework\Database\TableService;
12
- use WPStaging\Framework\Command\CommandInterface;
13
-
14
- class ExportCommand implements CommandInterface
15
- {
16
- const FORMAT_GZIP = Mysqldump::GZIP;
17
- const FORMAT_BZIP2 = Mysqldump::BZIP2;
18
- const FORMAT_SQL = Mysqldump::NONE;
19
-
20
- /** @var ExportDto */
21
- private $dto;
22
-
23
- /** @var TableManager */
24
- private $tableService;
25
-
26
- /** @var LoggerInterface */
27
- private $logger;
28
-
29
- public function __construct(ExportDto $dto, TableService $tableService = null, LoggerInterface $logger = null)
30
- {
31
- $this->dto = $dto;
32
- $this->logger = $logger;
33
- $this->tableService = $tableService;
34
- }
35
-
36
- /**
37
- * @inheritDoc
38
- * @throws Exception
39
- */
40
- public function execute()
41
- {
42
- $this->generateSqlFile();
43
- }
44
-
45
- /**
46
- * @throws Exception
47
- */
48
- protected function generateSqlFile()
49
- {
50
- $dumper = new Mysqldump(
51
- sprintf('mysql:host=%s;port=%d;dbname=%s', $this->dto->getHost(), $this->dto->getPort(), $this->dto->getName()),
52
- $this->dto->getUsername(),
53
- $this->dto->getPassword(),
54
- [
55
- 'compress' => $this->dto->getFormat(),
56
- 'include-tables' => $this->getIncludeTables(),
57
- 'version' => $this->dto->getVersion(),
58
- ]
59
- );
60
-
61
- try {
62
- $dumper->start($this->dto->getFullPath());
63
- return $this->dto->getFullPath();
64
- }
65
- catch (Exception $e) {
66
- $this->logger->alert($e->getMessage());
67
- throw new ExportException(sprintf(
68
- 'Failed to export database. Database Name: %s | Prefix: %s | File Path: %s | %s',
69
- $this->dto->getUsername() .':' . $this->dto->getPassword() . '@' . $this->dto->getHost() . ':' . $this->dto->getPort() . ' - ' . $this->dto->getName(),
70
- $this->dto->getPrefix(),
71
- $this->dto->getFullPath(),
72
- $e->getMessage()
73
- ));
74
- }
75
- }
76
-
77
- protected function getIncludeTables()
78
- {
79
- $tables = $this->tableService->findTableNamesStartWith($this->dto->getPrefix());
80
- $data = [];
81
- foreach ($tables as $table) {
82
- $data[] = $table->getName();
83
- }
84
- return $data;
85
- }
86
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Command/Database/Export/ExportDto.php DELETED
@@ -1,261 +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\Command\Database\Export;
7
-
8
- use DateTime;
9
- use Exception;
10
- use WPStaging\Framework\Adapter\Database;
11
- use WPStaging\Framework\Traits\HydrateTrait;
12
-
13
- class ExportDto
14
- {
15
- use HydrateTrait;
16
-
17
- const DEFAULT_FORMAT = ExportCommand::FORMAT_GZIP;
18
- const DEFAULT_PORT = 3306;
19
-
20
- /** @var string */
21
- private $host;
22
-
23
- /** @var int */
24
- private $port;
25
-
26
- /** @var string */
27
- private $name;
28
-
29
- /** @var string */
30
- private $username;
31
-
32
- /** @var string */
33
- private $password;
34
-
35
- /** @var string */
36
- private $prefix;
37
-
38
- /** @var string */
39
- private $format;
40
-
41
- /** @var string|null */
42
- private $directory;
43
-
44
- /** @var string|null */
45
- private $fullPath;
46
-
47
- /** @var string */
48
- private $version;
49
-
50
- /**
51
- * @return string
52
- */
53
- public function getHost()
54
- {
55
- if (!$this->host) {
56
- return DB_HOST;
57
- }
58
- return $this->host;
59
- }
60
-
61
- /**
62
- * @param string $host
63
- */
64
- public function setHost($host)
65
- {
66
- $this->host = $host;
67
- }
68
-
69
- /**
70
- * @return int
71
- */
72
- public function getPort()
73
- {
74
- if ($this->port) {
75
- return $this->port;
76
- }
77
-
78
- $parts = explode(':', DB_HOST);
79
- if (isset($parts[1]) && (int) $parts[1] > 0) {
80
- return (int) $parts[1];
81
- }
82
-
83
- return self::DEFAULT_PORT;
84
- }
85
-
86
- /**
87
- * @param int $port
88
- */
89
- public function setPort($port)
90
- {
91
- $this->port = (int) $port;
92
- }
93
-
94
- /**
95
- * @return string
96
- */
97
- public function getName()
98
- {
99
- if (!$this->name) {
100
- return DB_NAME;
101
- }
102
- return $this->name;
103
- }
104
-
105
- /**
106
- * @param string $name
107
- */
108
- public function setName($name)
109
- {
110
- $this->name = $name;
111
- }
112
-
113
- /**
114
- * @return string
115
- */
116
- public function getUsername()
117
- {
118
- if (!$this->username) {
119
- return DB_USER;
120
- }
121
- return $this->username;
122
- }
123
-
124
- /**
125
- * @param string $username
126
- */
127
- public function setUsername($username)
128
- {
129
- $this->username = $username;
130
- }
131
-
132
- /**
133
- * @return string
134
- */
135
- public function getPassword()
136
- {
137
- if (!$this->password) {
138
- return DB_PASSWORD;
139
- }
140
- return $this->password;
141
- }
142
-
143
- /**
144
- * @param string $password
145
- */
146
- public function setPassword($password)
147
- {
148
- $this->password = $password;
149
- }
150
-
151
- /**
152
- * @return string
153
- */
154
- public function getPrefix()
155
- {
156
- if (!$this->prefix) {
157
- return (new Database)->getPrefix();
158
- }
159
- return $this->prefix;
160
- }
161
-
162
- /**
163
- * @param string $prefix
164
- */
165
- public function setPrefix($prefix)
166
- {
167
- $this->prefix = $prefix;
168
- }
169
-
170
- /**
171
- * @return string
172
- */
173
- public function getFormat()
174
- {
175
- return $this->format ?: self::DEFAULT_FORMAT;
176
- }
177
-
178
- /**
179
- * @param string $format
180
- */
181
- public function setFormat($format)
182
- {
183
- $this->format = $format;
184
- }
185
-
186
- public function provideFileFormat()
187
- {
188
- switch($this->getFormat()) {
189
- case ExportCommand::FORMAT_GZIP:
190
- return 'gz';
191
- case ExportCommand::FORMAT_SQL:
192
- return 'sql';
193
- case ExportCommand::FORMAT_BZIP2:
194
- return 'bz2';
195
- }
196
-
197
- return $this->format;
198
- }
199
-
200
- /**
201
- * @return string|null
202
- */
203
- public function getDirectory()
204
- {
205
- return $this->directory;
206
- }
207
-
208
- /**
209
- * @param string|null $directory
210
- */
211
- public function setDirectory($directory)
212
- {
213
- $this->directory = $directory;
214
- }
215
-
216
- /**
217
- * @return string
218
- * @throws Exception
219
- */
220
- public function getFullPath()
221
- {
222
- if ($this->fullPath) {
223
- return $this->fullPath;
224
- }
225
-
226
- $fileName = sprintf(
227
- '%s_%s_%s.%s',
228
- rtrim($this->prefix, '_-'),
229
- (new DateTime)->format('Y-m-d'),
230
- md5(mt_rand()),
231
- $this->provideFileFormat()
232
- );
233
-
234
- $this->setFullPath($this->getDirectory() . $fileName);
235
- return $this->fullPath;
236
- }
237
-
238
- /**
239
- * @param string|null $fullPath
240
- */
241
- public function setFullPath($fullPath)
242
- {
243
- $this->fullPath = $fullPath;
244
- }
245
-
246
- /**
247
- * @return string
248
- */
249
- public function getVersion()
250
- {
251
- return $this->version;
252
- }
253
-
254
- /**
255
- * @param string $version
256
- */
257
- public function setVersion($version)
258
- {
259
- $this->version = $version;
260
- }
261
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Command/Database/Export/ExportException.php DELETED
@@ -1,13 +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\Command\Database\Export;
7
-
8
- use RuntimeException;
9
-
10
- class ExportException extends RuntimeException
11
- {
12
-
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
Command/Database/Snapshot/AbstractSnapshotCommand.php DELETED
@@ -1,71 +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\Command\Database\Snapshot;
8
-
9
- use WPStaging\Pro\Snapshot\Entity\Snapshot;
10
- use WPStaging\Pro\Snapshot\Repository\SnapshotRepository;
11
- use WPStaging\Framework\Adapter\Database;
12
- use WPStaging\Framework\Collection\Collection;
13
- use WPStaging\Framework\Collection\OptionCollection;
14
- use WPStaging\Framework\Command\CommandInterface;
15
-
16
- abstract class AbstractSnapshotCommand implements CommandInterface
17
- {
18
-
19
- /** @var Database */
20
- protected $database;
21
-
22
- /** @var SnapshotRepository */
23
- protected $repository;
24
-
25
- /** @var SnapshotDto */
26
- protected $dto;
27
-
28
- /** @var Snapshot[]|OptionCollection */
29
- protected $snapshots;
30
-
31
- abstract protected function saveSnapshots();
32
-
33
- public function __construct(Database $database, SnapshotRepository $repository, SnapshotDto $dto = null)
34
- {
35
- $this->database = $database;
36
- $this->repository = $repository;
37
- $this->setDto($dto);
38
-
39
- $this->snapshots = $this->repository->findAll()? : new OptionCollection(Snapshot::class);
40
- }
41
-
42
- /**
43
- * @param SnapshotDto $dto
44
- */
45
- public function setDto(SnapshotDto $dto = null)
46
- {
47
- if (!$dto) {
48
- return;
49
- }
50
-
51
- $this->dto = $dto;
52
-
53
- if (!$this->dto->getSourcePrefix()) {
54
- $this->dto->setSourcePrefix($this->database->getPrefix());
55
- }
56
- }
57
-
58
- /**
59
- * @throws SnapshotCommandException
60
- */
61
- protected function validateSnapshot()
62
- {
63
- if ($this->database->getPrefix() === $this->dto->getTargetPrefix()) {
64
- throw new SnapshotCommandException('You are trying to process production tables!');
65
- }
66
-
67
- if ($this->dto->getSourcePrefix() === $this->dto->getTargetPrefix()) {
68
- throw new SnapshotCommandException('You are trying to process same tables!');
69
- }
70
- }
71
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Command/Database/Snapshot/CreateSnapshotCommand.php DELETED
@@ -1,101 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Command\Database\Snapshot;
4
-
5
- use DateTime;
6
- use WPStaging\Pro\Snapshot\Entity\Snapshot;
7
- use WPStaging\Framework\Database\TableDto;
8
- use WPStaging\Framework\Database\TableService;
9
-
10
- class CreateSnapshotCommand extends AbstractSnapshotCommand
11
- {
12
-
13
- /** @noinspection PhpUnhandledExceptionInspection */
14
- public function execute()
15
- {
16
- $this->validateSnapshot();
17
-
18
- if ($this->dto->getStep() === null) {
19
- $this->executeAll();
20
- return;
21
- }
22
-
23
- $this->executeStep();
24
- }
25
-
26
- protected function executeAll()
27
- {
28
- foreach($this->findTables() as $table) {
29
- $this->backupTable($table);
30
- }
31
-
32
- $this->saveSnapshots();
33
- }
34
-
35
- /**
36
- * @noinspection PhpUnhandledExceptionInspection
37
- */
38
- protected function executeStep()
39
- {
40
- /** @var array $tables */
41
- $tables = $this->findTables();
42
-
43
- if (!isset($tables[$this->dto->getStep()])) {
44
- throw new SnapshotCommandException('failed to get tables with prefix: ' . $this->dto->getSourcePrefix());
45
- }
46
-
47
- $this->backupTable($tables[$this->dto->getStep()]);
48
-
49
- // This was the last step, save the snapshot
50
- if (count($tables) === $this->dto->getStep() + 1) {
51
- $this->saveSnapshots();
52
- }
53
- }
54
-
55
- /**
56
- * @return TableDto[]|null
57
- */
58
- protected function findTables()
59
- {
60
- $tables = (new TableService)->findTableStatusStartsWith($this->dto->getSourcePrefix());
61
- if (!$tables) {
62
- return null;
63
- }
64
- return $tables->toArray();
65
- }
66
-
67
- protected function backupTable(TableDto $tableDto)
68
- {
69
- $newTableName = $this->dto->getTargetPrefix() . str_replace($this->dto->getSourcePrefix(), null, $tableDto->getName());
70
- $this->database->exec('OPTIMIZE TABLE '. $tableDto->getName());
71
- $this->database->exec('DROP TABLE IF EXISTS '. $newTableName);
72
- $this->database->exec('CREATE TABLE ' . $newTableName . ' LIKE ' . $tableDto->getName());
73
- $this->database->exec('INSERT INTO ' . $newTableName . ' SELECT * FROM ' . $tableDto->getName());
74
- $this->database->exec('OPTIMIZE TABLE ' . $newTableName);
75
- }
76
-
77
- protected function saveSnapshots()
78
- {
79
- if (!$this->dto->isSaveRecords()) {
80
- return;
81
- }
82
-
83
- /** @var Snapshot $snapshot */
84
- $snapshot = $this->snapshots->findById($this->dto->getTargetPrefix());
85
-
86
- if ($snapshot) {
87
- $snapshot->setUpdatedAt(new DateTime);
88
- $this->repository->save($this->snapshots);
89
- return;
90
- }
91
-
92
- $snapshot = new Snapshot;
93
- $snapshot->setId($this->dto->getTargetPrefix());
94
- $snapshot->setName($this->dto->getName());
95
- $snapshot->setNotes($this->dto->getNotes());
96
- $snapshot->setCreatedAt(new DateTime);
97
-
98
- $this->snapshots->attach($snapshot);
99
- $this->repository->save($this->snapshots);
100
- }
101
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Command/Database/Snapshot/SnapshotCommandException.php DELETED
@@ -1,8 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Command\Database\Snapshot;
4
-
5
- use Exception;
6
-
7
- class SnapshotCommandException extends Exception
8
- {}
 
 
 
 
 
 
 
 
Command/Database/Snapshot/SnapshotDto.php DELETED
@@ -1,126 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_type=1);
4
- // TODO PHP7.x; type hints & return types
5
-
6
- namespace WPStaging\Command\Database\Snapshot;
7
-
8
- class SnapshotDto
9
- {
10
- const SNAPSHOT_DEFAULT_NAME = 'snapshot';
11
-
12
- /** @var string */
13
- private $name;
14
-
15
- /** @var string|null */
16
- private $notes;
17
-
18
- /** @var string */
19
- private $targetPrefix;
20
-
21
- /** @var string|null */
22
- private $sourcePrefix;
23
-
24
- /** @var int|null */
25
- private $step;
26
-
27
- /** @var bool */
28
- private $isSaveRecords = true;
29
-
30
- /**
31
- * @return string
32
- */
33
- public function getName()
34
- {
35
- return $this->name ?: self::SNAPSHOT_DEFAULT_NAME;
36
- }
37
-
38
- /**
39
- * @param string|null $name
40
- */
41
- public function setName($name)
42
- {
43
- $this->name = $name;
44
- }
45
-
46
- /**
47
- * @return string|null
48
- */
49
- public function getNotes()
50
- {
51
- return $this->notes ?: null;
52
- }
53
-
54
- /**
55
- * @param string|null $notes
56
- */
57
- public function setNotes($notes)
58
- {
59
- $this->notes = $notes;
60
- }
61
-
62
- /**
63
- * @return string
64
- */
65
- public function getTargetPrefix()
66
- {
67
- return $this->targetPrefix;
68
- }
69
-
70
- /**
71
- * @param string $targetPrefix
72
- */
73
- public function setTargetPrefix($targetPrefix)
74
- {
75
- $this->targetPrefix = $targetPrefix;
76
- }
77
-
78
- /**
79
- * @return string|null
80
- */
81
- public function getSourcePrefix()
82
- {
83
- return $this->sourcePrefix;
84
- }
85
-
86
- /**
87
- * @param string|null $sourcePrefix
88
- */
89
- public function setSourcePrefix($sourcePrefix)
90
- {
91
- $this->sourcePrefix = $sourcePrefix;
92
- }
93
-
94
- /**
95
- * @return int|null
96
- */
97
- public function getStep()
98
- {
99
- return $this->step;
100
- }
101
-
102
- /**
103
- * @param int|null $step
104
- */
105
- public function setStep($step)
106
- {
107
- $this->step = $step;
108
- }
109
-
110
- /**
111
- * @return bool
112
- */
113
- public function isSaveRecords()
114
- {
115
- return $this->isSaveRecords;
116
- }
117
-
118
- /**
119
- * @param bool $isSaveRecords
120
- */
121
- public function setIsSaveRecords($isSaveRecords)
122
- {
123
- $this->isSaveRecords = (bool) $isSaveRecords;
124
- }
125
- }
126
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Command/Database/Snapshot/SnapshotHandler.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Command\Database\Snapshot;
4
-
5
- use WPStaging\Framework\Command\CommandInterface;
6
- use WPStaging\Framework\Command\HandlerInterface;
7
- use SplObjectStorage;
8
-
9
- class SnapshotHandler implements HandlerInterface
10
- {
11
- const PREFIX_AUTOMATIC = 'wpsa';
12
- const PREFIX_MANUAL = 'wpsm';
13
-
14
- /** @var SplObjectStorage */
15
- private $commands;
16
-
17
- /** @var SnapshotDto */
18
- private $dto;
19
-
20
- public function __construct(SnapshotDto $dto)
21
- {
22
- $this->dto = $dto;
23
- $this->commands = new SplObjectStorage;
24
- }
25
-
26
- public function addCommand(CommandInterface $command)
27
- {
28
- /** @var AbstractSnapshotCommand $command */
29
- if ($this->commands->contains($command)) {
30
- return;
31
- }
32
-
33
- $command->setDto($this->dto);
34
-
35
- $this->commands->attach($command);
36
- }
37
-
38
- /**
39
- * @param string|null $action
40
- */
41
- public function handle($action = null)
42
- {
43
- /** @var CommandInterface $command */
44
- foreach($this->commands as $command) {
45
- $command->execute();
46
- }
47
- }
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Command/Database/Snapshot/UpdateSnapshotCommand.php DELETED
@@ -1,55 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Command\Database\Snapshot;
4
-
5
- use DateTime;
6
- use WPStaging\Pro\Snapshot\Entity\Snapshot;
7
- use WPStaging\Framework\Database\TableService;
8
-
9
- class UpdateSnapshotCommand extends AbstractSnapshotCommand
10
- {
11
- /**
12
- * @noinspection PhpUnhandledExceptionInspection
13
- * @noinspection DuplicatedCode
14
- */
15
- public function execute()
16
- {
17
- $this->validateSnapshot();
18
-
19
- $tables = (new TableService)->findTableNamesStartWith();
20
- if (!$tables) {
21
- throw new SnapshotCommandException('UpdateSnapshot failed to get tables');
22
- }
23
-
24
- $this->database->exec('SET FOREIGN_KEY_CHECKS = 0');
25
- foreach($tables as $table) {
26
- $newTableName = $this->dto->getTargetPrefix() . $table->getName();
27
- // Why this way? Because of schema changes, do not go shortcut with UPDATE query!
28
- $this->database->exec('DROP TABLE IF EXISTS ' . $newTableName);
29
- $this->database->exec('OPTIMIZE TABLE '. $table->getName());
30
- $this->database->exec('CREATE TABLE ' . $newTableName . ' LIKE ' . $table->getName());
31
- $this->database->exec('INSERT INTO ' . $newTableName . ' SELECT * FROM ' . $table->getName());
32
- }
33
- $this->database->exec('SET FOREIGN_KEY_CHECKS = 1');
34
-
35
- $this->saveSnapshots();
36
- }
37
-
38
- protected function saveSnapshots()
39
- {
40
- /** @var Snapshot $snapshot */
41
- $snapshot = $this->snapshots->findById($this->dto->getTargetPrefix());
42
- $snapshot->setUpdatedAt(new DateTime);
43
- $this->repository->save($this->snapshots);
44
- }
45
-
46
- protected function validateSnapshot()
47
- {
48
- parent::validateSnapshot();
49
- if (!$this->snapshots->doesIncludeId($this->dto->getTargetPrefix())) {
50
- throw new SnapshotCommandException(
51
- 'UpdateSnapshot prefix does not exist: ' . $this->dto->getTargetPrefix()
52
- );
53
- }
54
- }
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Command/Database/SnapshotFactory.php DELETED
@@ -1,55 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Command\Database;
4
-
5
- use WPStaging\Command\Database\Snapshot\CreateSnapshotCommand;
6
- use WPStaging\Command\Database\Snapshot\SnapshotDto;
7
- use WPStaging\Command\Database\Snapshot\SnapshotHandler;
8
- use WPStaging\Command\Database\Snapshot\UpdateSnapshotCommand;
9
- use WPStaging\Framework\Command\HandlerInterface;
10
- use WPStaging\Core\WPStaging;
11
-
12
- class SnapshotFactory
13
- {
14
- // TODO PHP7.1; visibility
15
- const CREATE_SNAPSHOT = 'create';
16
- const UPDATE_SNAPSHOT = 'update';
17
- const DELETE_SNAPSHOT = 'delete';
18
-
19
- /**
20
- * @param SnapshotDto $dto
21
- * @param string|null $action
22
- *
23
- * @return SnapshotHandler
24
- */
25
- public static function make(SnapshotDto $dto, $action = null)
26
- {
27
- $handler = new SnapshotHandler($dto);
28
-
29
- if ($action) {
30
- self::makeAction($handler, $action);
31
- return $handler;
32
- }
33
-
34
- foreach([self::CREATE_SNAPSHOT, self::UPDATE_SNAPSHOT, self::DELETE_SNAPSHOT] as $item) {
35
- self::makeAction($handler, $item);
36
- }
37
-
38
- return $handler;
39
- }
40
-
41
- private static function makeAction(HandlerInterface $handler, $action)
42
- {
43
- switch($action) {
44
- case self::CREATE_SNAPSHOT:
45
- $handler->addCommand(WPStaging::getInstance()->get(CreateSnapshotCommand::class));
46
- return;
47
- case self::UPDATE_SNAPSHOT:
48
- $handler->addCommand(WPStaging::getInstance()->get(UpdateSnapshotCommand::class));
49
- return;
50
- case self::DELETE_SNAPSHOT:
51
- // todo Implement Delete Snapshot Command
52
- return;
53
- }
54
- }
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Dto/AbstractDto.php DELETED
@@ -1,41 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x declare(strict_types=1);
4
- // TODO PHP7.x type-hints & return types
5
-
6
- namespace WPStaging\Component\Dto;
7
-
8
- use JsonSerializable;
9
- use Serializable;
10
- use WPStaging\Framework\Traits\ArrayableTrait;
11
- use WPStaging\Framework\Traits\HydrateTrait;
12
-
13
- abstract class AbstractDto implements JsonSerializable, Serializable
14
- {
15
- use ArrayableTrait;
16
- use HydrateTrait;
17
-
18
- /**
19
- * @inheritDoc
20
- */
21
- public function serialize()
22
- {
23
- return serialize($this->toArray());
24
- }
25
-
26
- /**
27
- * @inheritDoc
28
- */
29
- public function unserialize($serialized)
30
- {
31
- $this->hydrate(unserialize($serialized));
32
- }
33
-
34
- /**
35
- * @inheritDoc
36
- */
37
- public function jsonSerialize()
38
- {
39
- return $this->toArray();
40
- }
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Dto/AbstractRequestDto.php DELETED
@@ -1,33 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x declare(strict_types=1);
4
- // TODO PHP7.x type-hints & return types
5
-
6
- namespace WPStaging\Component\Dto;
7
-
8
- abstract class AbstractRequestDto extends AbstractDto
9
- {
10
-
11
- /** @var StepsDto */
12
- protected $steps;
13
-
14
- /**
15
- * @return StepsDto
16
- */
17
- public function getSteps()
18
- {
19
- if (!$this->steps) {
20
- $this->steps = (new StepsDto)->hydrate([
21
- 'current' => 0,
22
- 'total' => 0,
23
- ]);
24
- }
25
- return $this->steps;
26
- }
27
-
28
- /** @noinspection PhpUnused */
29
- public function setSteps(StepsDto $steps)
30
- {
31
- $this->steps = $steps;
32
- }
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Dto/InitiableDtoInterface.php DELETED
@@ -1,20 +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\Component\Dto;
7
-
8
- interface InitiableDtoInterface
9
- {
10
- /**
11
- * @return bool
12
- */
13
- public function isInit();
14
-
15
- /**
16
- * @param bool $isInit
17
- * @return void
18
- */
19
- public function setInit($isInit);
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Dto/StepsDto.php DELETED
@@ -1,77 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x declare(strict_types=1);
4
- // TODO PHP7.x type-hints & return types
5
-
6
- namespace WPStaging\Component\Dto;
7
-
8
- class StepsDto extends AbstractDto
9
- {
10
- /** @var int */
11
- private $total;
12
-
13
- /** @var int */
14
- private $current;
15
-
16
- /**
17
- * @return int
18
- */
19
- public function getTotal()
20
- {
21
- return $this->total;
22
- }
23
-
24
- /**
25
- * @param int $total
26
- */
27
- public function setTotal($total)
28
- {
29
- $this->total = (int) $total;
30
- }
31
-
32
- /**
33
- * @return int
34
- */
35
- public function getCurrent()
36
- {
37
- return $this->current;
38
- }
39
-
40
- /**
41
- * @param int $current
42
- */
43
- public function setCurrent($current)
44
- {
45
- $this->current = (int) $current;
46
- }
47
-
48
- /**
49
- * @return int
50
- */
51
- public function getPercentage()
52
- {
53
- if ($this->total < 1) {
54
- return 100;
55
- }
56
-
57
- $percentage = (int) round(($this->current / $this->total) * 100);
58
- return ($percentage > 100) ? 100 : $percentage;
59
- }
60
-
61
- public function incrementCurrentStep()
62
- {
63
- if ($this->current < $this->total) {
64
- $this->current++;
65
- }
66
- }
67
-
68
- public function isFinished()
69
- {
70
- return $this->total <= $this->current;
71
- }
72
-
73
- public function finish()
74
- {
75
- $this->current = $this->total;
76
- }
77
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Job/AbstractJob.php DELETED
@@ -1,83 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Component\Job;
4
-
5
- use WPStaging\Component\Task\TaskResponseDto;
6
- use WPStaging\Framework\Adapter\Directory;
7
- use WPStaging\Framework\Filesystem\Filesystem;
8
- use WPStaging\Core\WPStaging;
9
-
10
- abstract class AbstractJob implements JobInterface
11
- {
12
- /**
13
- * @return object
14
- */
15
- abstract public function execute();
16
-
17
- /**
18
- * @return string
19
- */
20
- abstract public function getJobName();
21
-
22
- /**
23
- * @param TaskResponseDto $response
24
- * @return TaskResponseDto
25
- */
26
- protected function getResponse(TaskResponseDto $response)
27
- {
28
- $response->setJob(substr($this->findCurrentJob(), 3));
29
- return $response;
30
- }
31
-
32
- protected function findCurrentJob()
33
- {
34
- $class = explode('\\', static::class);
35
- return end($class);
36
- }
37
-
38
- /**
39
- * Clean anything that is left over from executing the job
40
- * @return void
41
- */
42
- protected function clean()
43
- {
44
- /** @var Directory $directory */
45
- $directory = WPStaging::getInstance()->get(Directory::class);
46
- (new Filesystem)->delete($directory->getCacheDirectory() . $this->getJobName());
47
- }
48
-
49
- /**
50
- * @param string $notation
51
- * @param mixed $value
52
- */
53
- protected function injectTaskRequest($notation, $value)
54
- {
55
- if (!isset($_POST['wpstg']) || !is_array($_POST['wpstg'])) {
56
- $_POST['wpstg'] = [];
57
- }
58
-
59
- if (!isset($_POST['wpstg']['tasks']) || !is_array($_POST['wpstg']['tasks'])) {
60
- $_POST['wpstg']['tasks'] = [];
61
- }
62
-
63
- $data = &$_POST['wpstg']['tasks'];
64
- $keys = explode('.', $notation);
65
- foreach ($keys as $key) {
66
- if (!isset($data[$key])) {
67
- $data[$key] = [];
68
- }
69
- $data = &$data[$key];
70
- }
71
-
72
- if (!is_array($value)) {
73
- $data = $value;
74
- return;
75
- }
76
-
77
- if (!is_array($data)) {
78
- $data = [];
79
- }
80
-
81
- $data = array_merge($data, $value);
82
- }
83
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Job/AbstractQueueJob.php DELETED
@@ -1,207 +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\Component\Job;
7
-
8
- use WPStaging\Component\Task\AbstractTask;
9
- 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\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;
23
-
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
- }
44
-
45
- public function __destruct()
46
- {
47
- if ($this->dto->isStatusCheck()) {
48
- return;
49
- }
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();
61
-
62
- /**
63
- * @return QueueJobDto
64
- */
65
- public function getDto()
66
- {
67
- return $this->dto;
68
- }
69
-
70
- protected function clean()
71
- {
72
- parent::clean();
73
- $this->queue->reset();
74
- }
75
-
76
- protected function prepare()
77
- {
78
- if (method_exists($this, 'provideRequestDto')) {
79
- $this->provideRequestDto();
80
- }
81
-
82
- if (method_exists($this, 'injectRequests')) {
83
- $this->injectRequests();
84
- }
85
-
86
- $this->saveCurrentStatusTitle();
87
- }
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
- }
109
-
110
- // TODO RPoC Hack
111
- $this->dto->setStatusCheck(!empty($_GET['action']) && $_GET['action'] === 'wpstg--snapshots--status');
112
-
113
- if ($this->dto->isStatusCheck()) {
114
- return;
115
- }
116
-
117
- if ($this->dto->getId() === null) {
118
- $this->dto->setId(time());
119
- }
120
-
121
- if ($this->dto->isInit() && method_exists($this, 'init')) {
122
- $this->init();
123
- }
124
-
125
- if ($this->dto->isInit()) {
126
- $this->queue->reset();
127
- $this->initiateTasks();
128
- }
129
-
130
- $this->dto->setInit(false);
131
- /** @var AbstractTask currentTask */
132
- $this->currentTask = WPStaging::getInstance()->get($this->queue->pop());
133
- if (!$this->currentTask) {
134
- return;
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 = [])
153
- {
154
- foreach ($tasks as $task) {
155
- $this->queue->push($task);
156
- }
157
- }
158
-
159
- /**
160
- * @inheritDoc
161
- */
162
- protected function getResponse(TaskResponseDto $response)
163
- {
164
- $response = parent::getResponse($response);
165
-
166
- // TODO RPoC below
167
- // Task is not done yet, add it to beginning of the queue again
168
- if (!$response->isStatus()) {
169
- // TODO PHP7.x; $this->currentTask::class;
170
- $className = get_class($this->currentTask);
171
- $this->queue->prepend($className);
172
- }
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
-
185
- /**
186
- * Provides arguments to be used within saveCurrentStatusTitle()
187
- * In most use cases it will be overwritten but in case it is forgotten, it will not throw error
188
- * @return array
189
- */
190
- protected function findCurrentStatusTitleArgs()
191
- {
192
- return [];
193
- }
194
-
195
- protected function saveCurrentStatusTitle()
196
- {
197
- // Might happen when xdebug enabled so saving us some extra reading unnecessary log output
198
- if (!$this->currentTask) {
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/JobInterface.php DELETED
@@ -1,13 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Component\Job;
4
-
5
- use WPStaging\Component\Task\TaskResponseDto;
6
-
7
- interface JobInterface
8
- {
9
- /**
10
- * @return TaskResponseDto
11
- */
12
- public function execute();
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Job/ProcessLock.php DELETED
@@ -1,52 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_type=1);
4
-
5
- namespace WPStaging\Component\Job;
6
-
7
- use DateTime;
8
- use Exception;
9
- use WPStaging\Core\Utils\Cache;
10
- use WPStaging\Core\WPStaging;
11
-
12
- class ProcessLock
13
- {
14
- /** @var Cache */
15
- private $cache;
16
-
17
- /** @var array */
18
- private $options;
19
-
20
- public function __construct()
21
- {
22
- /** @noinspection PhpUnhandledExceptionInspection */
23
- $this->cache = new Cache(-1, WPStaging::getContentDir());
24
- // TODO RPoC; DTO
25
- $this->options = $this->cache->get('clone_options');
26
- }
27
-
28
- public function isRunning()
29
- {
30
- if (!$this->options || !isset($this->options->isRunning, $this->options->expiresAt)) {
31
- return false;
32
- }
33
-
34
- try {
35
- $now = new DateTime;
36
- $expiresAt = new DateTime($this->options->expiresAt);
37
- return $this->options->isRunning === true && $now < $expiresAt;
38
- }
39
- catch (Exception $e) {
40
- return false;
41
- }
42
- }
43
-
44
- /** @noinspection PhpUnhandledExceptionInspection */
45
- public function restart()
46
- {
47
- // TODO RPoC
48
- unset($this->options->isRunning);
49
- $this->cache->delete('clone_options');
50
- $this->cache->delete('files_to_copy');
51
- }
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Job/QueueJobDto.php DELETED
@@ -1,103 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Component\Job;
4
-
5
- use WPStaging\Component\Dto\AbstractDto;
6
-
7
- abstract class QueueJobDto extends AbstractDto
8
- {
9
- /** @var string|int|null */
10
- protected $id;
11
-
12
- /** @var bool */
13
- protected $init;
14
-
15
- /** @var bool */
16
- protected $finished;
17
-
18
- /** @var bool */
19
- protected $statusCheck;
20
-
21
- /** @var string|null */
22
- protected $currentStatusTitle;
23
-
24
- /**
25
- * @return string|int|null
26
- */
27
- public function getId()
28
- {
29
- return $this->id;
30
- }
31
-
32
- /**
33
- * @param string|int|null $id
34
- */
35
- public function setId($id)
36
- {
37
- $this->id = $id;
38
- }
39
-
40
- /**
41
- * @return bool
42
- */
43
- public function isInit()
44
- {
45
- return $this->init;
46
- }
47
-
48
- /**
49
- * @param bool $init
50
- */
51
- public function setInit($init)
52
- {
53
- $this->init = $init;
54
- }
55
-
56
- /**
57
- * @return bool
58
- */
59
- public function isFinished()
60
- {
61
- return $this->finished;
62
- }
63
-
64
- /**
65
- * @param bool $finished
66
- */
67
- public function setFinished($finished)
68
- {
69
- $this->finished = $finished;
70
- }
71
-
72
- /**
73
- * @return bool
74
- */
75
- public function isStatusCheck()
76
- {
77
- return $this->statusCheck;
78
- }
79
-
80
- /**
81
- * @param bool $statusCheck
82
- */
83
- public function setStatusCheck($statusCheck)
84
- {
85
- $this->statusCheck = $statusCheck;
86
- }
87
-
88
- /**
89
- * @return string|null
90
- */
91
- public function getCurrentStatusTitle()
92
- {
93
- return $this->currentStatusTitle;
94
- }
95
-
96
- /**
97
- * @param string|null $currentStatusTitle
98
- */
99
- public function setCurrentStatusTitle($currentStatusTitle)
100
- {
101
- $this->currentStatusTitle = $currentStatusTitle;
102
- }
103
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Task/AbstractTask.php DELETED
@@ -1,232 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Component\Task;
4
-
5
- use WPStaging\Vendor\Psr\Log\LoggerInterface;
6
- use WPStaging\Component\Dto\AbstractRequestDto;
7
- use WPStaging\Framework\Traits\RequestNotationTrait;
8
- use WPStaging\Framework\Traits\TimerTrait;
9
- use WPStaging\Framework\Utils\Cache\AbstractCache;
10
- use WPStaging\Framework\Utils\Cache\Cache;
11
- use WPStaging\Core\Utils\Logger;
12
-
13
- abstract class AbstractTask implements TaskInterface
14
- {
15
- use TimerTrait;
16
- use RequestNotationTrait;
17
-
18
- /** @var LoggerInterface */
19
- protected $logger;
20
-
21
- /** @var Cache */
22
- protected $cache;
23
-
24
- /** @var AbstractRequestDto */
25
- protected $requestDto;
26
-
27
- /** @var bool */
28
- protected $prepared;
29
-
30
- // TODO RPoC
31
- /** @var string|null */
32
- protected $jobName;
33
-
34
- /** @var int|null */
35
- protected $jobId;
36
-
37
- /** @var bool */
38
- protected $debug;
39
-
40
- public function __construct(LoggerInterface $logger, Cache $cache)
41
- {
42
- $this->initiateStartTime();
43
-
44
- /** @var Logger logger */
45
- $this->logger = $logger;
46
- $this->cache = clone $cache;
47
-
48
- $this->cache->setLifetime(HOUR_IN_SECONDS);
49
- $this->cache->setFilename('task_' . $this->getTaskName());
50
-
51
- if (method_exists($this, 'init')) {
52
- $this->init();
53
- }
54
- }
55
-
56
- public function __destruct()
57
- {
58
- if (!$this->requestDto) {
59
- return;
60
- }
61
-
62
- if ($this->requestDto->getSteps()->isFinished()) {
63
- $this->cache->delete();
64
- return;
65
- }
66
-
67
- $this->cache->save($this->requestDto->toArray());
68
- }
69
-
70
- /**
71
- * @return object
72
- */
73
- abstract public function execute();
74
-
75
- /**
76
- * @return string
77
- */
78
- abstract public function getTaskName();
79
-
80
- /**
81
- * @inheritDoc
82
- */
83
- abstract public function getStatusTitle(array $args = []);
84
-
85
- /**
86
- * @return string
87
- */
88
- abstract public function getRequestNotation();
89
-
90
- /**
91
- * @return string
92
- */
93
- abstract public function getRequestDtoClass();
94
-
95
- public function prepare()
96
- {
97
- if ($this->prepared) {
98
- return;
99
- }
100
-
101
- $this->findRequestDto();
102
- $this->prepared = true;
103
- }
104
-
105
- public function setRelativeCacheDirectory($path)
106
- {
107
- /** @var AbstractCache $cache */
108
- foreach ($this->getCaches() as $cache) {
109
- $fullPath = trailingslashit($cache->getPath() . $path);
110
- $cache->setPath($fullPath);
111
- }
112
- }
113
-
114
- public function setRequestDto(AbstractRequestDto $dto)
115
- {
116
- $this->requestDto = $dto;
117
- }
118
-
119
- /**
120
- * @return TaskResponseDto
121
- */
122
- public function generateResponse()
123
- {
124
- $steps = $this->requestDto->getSteps();
125
- $steps->incrementCurrentStep();
126
-
127
- // TODO Hydrate
128
- $response = $this->getResponseDto();
129
- $response->setStatus($steps->isFinished());
130
- $response->setPercentage($steps->getPercentage());
131
- $response->setTotal($steps->getTotal());
132
- $response->setStep($steps->getCurrent());
133
- $response->setTask($this->getTaskName());
134
- $response->setRunTime($this->getRunningTime());
135
- $response->setStatusTitle($this->getStatusTitle());
136
- $response->addMessage($this->logger->getLastLogMsg()); // TODO grab current task's logs, not last log message!
137
-
138
- /** @noinspection PhpPossiblePolymorphicInvocationInspection */
139
- /** @noinspection PhpParamsInspection */
140
- $this->logger->setFileName(sprintf('%s__%s__%s',
141
- $this->getJobId(),
142
- $this->getJobName(),
143
- date('Y_m_d__H')
144
- ));
145
-
146
- return $response;
147
- }
148
-
149
- /**
150
- * @return string|null
151
- */
152
- public function getJobName()
153
- {
154
- return $this->jobName;
155
- }
156
-
157
- /**
158
- * @param string|null $jobName
159
- */
160
- public function setJobName($jobName)
161
- {
162
- $this->jobName = $jobName;
163
- // TODO RPoC?
164
- $this->setRelativeCacheDirectory($jobName);
165
- }
166
-
167
- /**
168
- * @return string|int|null
169
- */
170
- public function getJobId()
171
- {
172
- return $this->jobId;
173
- }
174
-
175
- /**
176
- * @param string|int|null $jobId
177
- */
178
- public function setJobId($jobId)
179
- {
180
- $this->jobId = $jobId;
181
- }
182
-
183
- /**
184
- * @param bool $debug
185
- */
186
- public function setDebug($debug)
187
- {
188
- $this->debug = (bool) $debug;
189
- }
190
-
191
- /**
192
- * Finds via cache or $_POST the request data.
193
- * If nothing is provided, sets and empty RequestDto for the task
194
- * @return void
195
- */
196
- protected function findRequestDto()
197
- {
198
- if ($this->requestDto) {
199
- return;
200
- }
201
-
202
- $data = $this->cache->get([]);
203
- $postData = $this->resolvePostRequestData('tasks.' . $this->getRequestNotation());
204
- if ($postData) {
205
- $data = array_replace_recursive($data, $postData);
206
- }
207
-
208
- $dtoClass = $this->getRequestDtoClass();
209
- /** @var AbstractRequestDto $dto */
210
- $this->requestDto = (new $dtoClass);
211
- $this->requestDto->hydrate($data);
212
-
213
- if ($this->debug) {
214
- $this->logger->debug($this->getTaskName() . ' Request: ' . json_encode($this->requestDto));
215
- }
216
- }
217
-
218
- protected function getResponseDto()
219
- {
220
- return new TaskResponseDto;
221
- }
222
-
223
- /**
224
- * @return Cache[]|AbstractCache[]|array
225
- */
226
- protected function getCaches()
227
- {
228
- return [
229
- $this->cache,
230
- ];
231
- }
232
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Task/Database/RenameTablesRequestDto.php DELETED
@@ -1,50 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_type=1);
4
- // TODO PHP7.x; type hints & return types
5
-
6
- namespace WPStaging\Component\Task\Database;
7
-
8
- use WPStaging\Component\Dto\AbstractRequestDto;
9
-
10
- class RenameTablesRequestDto extends AbstractRequestDto
11
- {
12
-
13
- /** @var string */
14
- private $source;
15
-
16
- /** @var string */
17
- private $target;
18
-
19
- /**
20
- * @return string
21
- */
22
- public function getSource()
23
- {
24
- return $this->source;
25
- }
26
-
27
- /**
28
- * @param string $source
29
- */
30
- public function setSource($source)
31
- {
32
- $this->source = $source;
33
- }
34
-
35
- /**
36
- * @return string
37
- */
38
- public function getTarget()
39
- {
40
- return $this->target;
41
- }
42
-
43
- /**
44
- * @param string $target
45
- */
46
- public function setTarget($target)
47
- {
48
- $this->target = $target;
49
- }
50
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Task/Database/RenameTablesTask.php DELETED
@@ -1,120 +0,0 @@
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\Component\Task\Database;
8
-
9
- use WPStaging\Vendor\Psr\Log\LoggerInterface;
10
- use RuntimeException;
11
- use WPStaging\Component\Task\AbstractTask;
12
- use WPStaging\Framework\Adapter\Database;
13
- use WPStaging\Framework\Utils\Cache\Cache;
14
- use WPStaging\Framework\Database\TableService;
15
- use WPStaging\Core\Utils\Logger;
16
-
17
- class RenameTablesTask extends AbstractTask
18
- {
19
-
20
- const REQUEST_NOTATION = 'database.tables.rename';
21
- const REQUEST_DTO_CLASS = RenameTablesRequestDto::class;
22
- const TASK_NAME = 'database_tables_rename';
23
- const TASK_TITLE = 'Renaming Tables';
24
-
25
- /** @var TableService */
26
- private $service;
27
-
28
- /** @var RenameTablesRequestDto */
29
- protected $requestDto;
30
-
31
- public function __construct(TableService $service, LoggerInterface $logger, Cache $cache)
32
- {
33
- parent::__construct($logger, $cache);
34
- $this->service = $service;
35
- }
36
-
37
- /**
38
- * @inheritDoc
39
- */
40
- public function execute()
41
- {
42
- $this->prepare();
43
-
44
- // TODO shall we check for locks and force unlock? such as;
45
- // `SHOW OPEN TABLES WHERE in_use > 0;`
46
- // `SHOW PROCESSLIST;`
47
- // `KILL {PROCESS_ID};`
48
- $tables = $this->service->findTableStatusStartsWith($this->requestDto->getSource());
49
-
50
- if (!$tables) {
51
- throw new RuntimeException('Failed to find tables with prefix: ' . $this->requestDto->getSource());
52
- }
53
-
54
- // Renaming table is rather instant thing to do thus all in one action!
55
- $sqlRename = 'RENAME TABLE ';
56
- $sqlDropTarget = 'DROP TABLE IF EXISTS ';
57
- $sqlDropSource = 'DROP TABLE IF EXISTS ';
58
- foreach ($tables as $table) {
59
- $newName = $this->requestDto->getTarget();
60
- $newName .= str_replace($this->requestDto->getSource(), null, $table->getName());
61
-
62
- $sqlRename .= $table->getName() . ' TO ' . $newName . ',';
63
- $sqlDropTarget .= $newName . ',';
64
- $sqlDropSource .= $table->getName() . ',';
65
- }
66
-
67
- /** @var Database $database */
68
- $database = $this->service->getDatabase();
69
- $database->exec('SET FOREIGN_KEY_CHECKS = 0');
70
- $database->exec(trim($sqlDropTarget, ','));
71
- $database->exec(trim($sqlRename, ','));
72
- $database->exec(trim($sqlDropSource, ','));
73
- $database->exec('SET FOREIGN_KEY_CHECKS = 1');
74
-
75
- wp_cache_flush();
76
-
77
- $this->logger->log(
78
- Logger::TYPE_INFO,
79
- sprintf('Replaced %s to %s', $this->requestDto->getSource(), $this->requestDto->getTarget())
80
- );
81
-
82
- return $this->generateResponse();
83
- }
84
-
85
- /**
86
- * @inheritDoc
87
- */
88
- public function getTaskName()
89
- {
90
- return self::TASK_NAME;
91
- }
92
-
93
- /**
94
- * @inheritDoc
95
- */
96
- public function getRequestNotation()
97
- {
98
- return self::REQUEST_NOTATION;
99
- }
100
-
101
- /**
102
- * @inheritDoc
103
- */
104
- public function getRequestDtoClass()
105
- {
106
- return self::REQUEST_DTO_CLASS;
107
- }
108
-
109
- public function getStatusTitle(array $args = [])
110
- {
111
- return __(self::TASK_TITLE, 'wp-staging');
112
- }
113
-
114
- public function getCacheFiles()
115
- {
116
- return [
117
- $this->cache->getFilePath(),
118
- ];
119
- }
120
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Task/Filesystem/DirectoryScannerRequestDto.php DELETED
@@ -1,46 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_type=1);
4
- // TODO PHP7.x; type hints & return types
5
-
6
- namespace WPStaging\Component\Task\Filesystem;
7
-
8
- use WPStaging\Component\Dto\AbstractRequestDto;
9
-
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
- }
30
-
31
- /**
32
- * @return array
33
- */
34
- public function getExcluded()
35
- {
36
- return (array)$this->excluded;
37
- }
38
-
39
- public function setExcluded(array $excluded = null)
40
- {
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 DELETED
@@ -1,221 +0,0 @@
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\Component\Task\Filesystem;
8
-
9
- use Exception;
10
- use InvalidArgumentException;
11
- use WPStaging\Vendor\Psr\Log\LoggerInterface;
12
- use WPStaging\Component\Task\AbstractTask;
13
- 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\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;
29
-
30
- const REQUEST_NOTATION = 'filesystem.directory.scanner';
31
- const REQUEST_DTO_CLASS = DirectoryScannerRequestDto::class;
32
- const TASK_NAME = 'filesystem_directory_scanner';
33
- const TASK_TITLE = 'Scanning Directories';
34
-
35
- /** @var DirectoryScannerRequestDto */
36
- public $requestDto;
37
-
38
- /** @var DirectoryScannerControl */
39
- private $scannerControl;
40
-
41
- /** @var BufferedCache */
42
- private $directoryCache;
43
-
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()
61
- {
62
- parent::__destruct();
63
-
64
- if ($this->directories) {
65
- $this->directoryCache->append($this->directories);
66
- }
67
- }
68
-
69
- /**
70
- * @inheritDoc
71
- */
72
- public function execute()
73
- {
74
- $this->prepare();
75
- while ($this->shouldContinue()) {
76
- $this->scanCurrentDirectory();
77
- }
78
- $this->updateSteps();
79
- $this->logger->info(sprintf('Scanned %d directories', $this->requestDto->getSteps()->getTotal()));
80
- return $this->generateResponse();
81
- }
82
-
83
- /**
84
- * @inheritDoc
85
- */
86
- public function getTaskName()
87
- {
88
- return self::TASK_NAME;
89
- }
90
-
91
- /**
92
- * @inheritDoc
93
- */
94
- public function getRequestNotation()
95
- {
96
- return self::REQUEST_NOTATION;
97
- }
98
-
99
- /**
100
- * @inheritDoc
101
- */
102
- public function getRequestDtoClass()
103
- {
104
- return self::REQUEST_DTO_CLASS;
105
- }
106
-
107
- /**
108
- * @inheritDoc
109
- */
110
- public function getStatusTitle(array $args = [])
111
- {
112
- return __(self::TASK_TITLE, 'wp-staging');
113
- }
114
-
115
- protected function scanCurrentDirectory()
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();
123
- return;
124
- } catch (InvalidArgumentException $e) {
125
- // This happens when a symlink is there and we don't follow them.
126
- } catch (Exception $e) {
127
- $this->logger->warning($e->getMessage());
128
- }
129
-
130
- // No directories found here, skip it
131
- if (empty($directories)) {
132
- return;
133
- }
134
-
135
- foreach ($directories as $directory) {
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()
147
- {
148
- parent::findRequestDto();
149
-
150
- if (!$this->requestDto->getIncluded()) {
151
- $this->requestDto->setIncluded([ ABSPATH ]);
152
- }
153
-
154
- if ($this->requestDto->getSteps()->getTotal() > 0) {
155
- return;
156
- }
157
-
158
- $this->directories = array_map(static function($dir) {
159
- return str_replace(ABSPATH, null, $dir);
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());
168
- if ($totalSteps < 0) {
169
- $totalSteps = 0;
170
- }
171
- $this->requestDto->getSteps()->setTotal($totalSteps);
172
-
173
- // Exclude WP Staging related directories and Cache dir
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';
183
- $this->requestDto->setExcluded($excludedDirs);
184
- }
185
-
186
- protected function getCaches()
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
-
196
- /**
197
- * @return bool
198
- */
199
- private function shouldContinue()
200
- {
201
- return !$this->isThreshold() && !$this->requestDto->getSteps()->isFinished();
202
- }
203
-
204
- private function updateSteps()
205
- {
206
- // Update steps
207
- $steps = $this->requestDto->getSteps();
208
- $steps->setTotal($steps->getTotal() + count($this->directories));
209
-
210
- /** @noinspection NullPointerExceptionInspection */
211
- if ($this->scannerControl->getQueue()->count() > 0) {
212
- return;
213
- }
214
-
215
- $total = $steps->getTotal() - count($this->requestDto->getIncluded());
216
- $total = $total >= 0 ? $total : 0;
217
-
218
- $steps->setTotal($total);
219
- $steps->setCurrent($total);
220
- }
221
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Task/Filesystem/FileScannerRequestDto.php DELETED
@@ -1,59 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_type=1);
4
- // TODO PHP7.x; type hints & return types
5
-
6
- namespace WPStaging\Component\Task\Filesystem;
7
-
8
- use WPStaging\Component\Dto\AbstractRequestDto;
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
- }
33
-
34
- /**
35
- * @return array
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 DELETED
@@ -1,222 +0,0 @@
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\Component\Task\Filesystem;
8
-
9
- use Exception;
10
- use WPStaging\Vendor\Psr\Log\LoggerInterface;
11
- use RuntimeException;
12
- use WPStaging\Component\Task\AbstractTask;
13
- use WPStaging\Framework\Queue\FinishedQueueException;
14
- use WPStaging\Framework\Queue\Storage\BufferedCacheStorage;
15
- 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\DirectoryScannerControl;
20
- use WPStaging\Framework\Filesystem\FileScannerControl;
21
- use WPStaging\Framework\Filesystem\Filesystem;
22
-
23
- class FileScannerTask extends AbstractTask
24
- {
25
- use ResourceTrait;
26
-
27
- const REQUEST_NOTATION = 'filesystem.file.scanner';
28
- const REQUEST_DTO_CLASS = FileScannerRequestDto::class;
29
- const TASK_NAME = 'filesystem_file_scanner';
30
- const TASK_TITLE = 'Scanning Files in %d Directories';
31
-
32
- /** @var FileScannerRequestDto */
33
- public $requestDto;
34
-
35
- /** @var FileScannerControl */
36
- private $scannerControl;
37
-
38
- /** @var BufferedCache */
39
- private $fileCache;
40
-
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()
58
- {
59
- parent::__destruct();
60
-
61
- if ($this->files) {
62
- $this->fileCache->append($this->files);
63
- }
64
- }
65
-
66
- /**
67
- * @inheritDoc
68
- */
69
- public function execute()
70
- {
71
- $this->prepare();
72
- while ($this->shouldContinue()) {
73
- $this->scanCurrentDirectory();
74
- }
75
-
76
- $this->updateSteps();
77
- $this->logger->info(sprintf('Scanned %d files', $this->requestDto->getSteps()->getTotal()));
78
- return $this->generateResponse();
79
- }
80
-
81
- /**
82
- * @inheritDoc
83
- */
84
- public function getTaskName()
85
- {
86
- return self::TASK_NAME;
87
- }
88
-
89
- /**
90
- * @inheritDoc
91
- */
92
- public function getRequestNotation()
93
- {
94
- return self::REQUEST_NOTATION;
95
- }
96
-
97
- /**
98
- * @inheritDoc
99
- */
100
- public function getRequestDtoClass()
101
- {
102
- return self::REQUEST_DTO_CLASS;
103
- }
104
-
105
- /**
106
- * @inheritDoc
107
- */
108
- public function getStatusTitle(array $args = [])
109
- {
110
- $total = isset($args[0]) ? $args[0] : 0;
111
- if ($this->requestDto && $this->requestDto->getSteps()) {
112
- $total = $this->requestDto->getSteps()->getTotal();
113
- }
114
- return sprintf(__(self::TASK_TITLE, 'wp-staging'), $total);
115
- }
116
-
117
- protected function scanCurrentDirectory()
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();
125
- return;
126
- } catch (Exception $e) {
127
- $this->logger->warning($e->getMessage());
128
- }
129
-
130
- // No files found here, skip it
131
- if (empty($files)) {
132
- return;
133
- }
134
-
135
- foreach ($files as $file) {
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()
146
- {
147
- parent::findRequestDto();
148
-
149
- if ($this->requestDto->getSteps()->getTotal() > 0) {
150
- return;
151
- }
152
-
153
- $this->requestDto->setIncluded(array_map(static function($dir) {
154
- return str_replace(ABSPATH, null, $dir);
155
- }, $this->requestDto->getIncluded()));
156
-
157
- /** @noinspection NullPointerExceptionInspection */
158
- if ($this->scannerControl->getQueue()->count() < 1) {
159
- $this->initiateQueue();
160
- }
161
-
162
- $this->requestDto->getSteps()->setTotal(1);
163
- }
164
-
165
- protected function getCaches()
166
- {
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',
184
- $directoryData
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)) {
192
- throw new RuntimeException('Failed to copy %s as file scanner queue %s', $directoryData, $queueFile);
193
- }
194
- }
195
-
196
- /**
197
- * @return bool
198
- */
199
- private function shouldContinue()
200
- {
201
- return !$this->isThreshold() && !$this->requestDto->getSteps()->isFinished();
202
- }
203
-
204
- private function updateSteps()
205
- {
206
- // Update steps
207
- $steps = $this->requestDto->getSteps();
208
- $steps->setTotal($steps->getTotal() + count($this->files));
209
-
210
- /** @noinspection NullPointerExceptionInspection */
211
- if ($this->scannerControl->getQueue()->count() > 0) {
212
- return;
213
- }
214
-
215
- $steps = $this->requestDto->getSteps();
216
- $total = $steps->getTotal() - 1;
217
- $total = $total >= 0 ? $total : 0;
218
-
219
- $steps->setTotal($total);
220
- $steps->setCurrent($total);
221
- }
222
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Task/TaskInterface.php DELETED
@@ -1,54 +0,0 @@
1
- <?php
2
-
3
-
4
- // TODO PHP7.x; declare(strict_types=1);
5
- // TODO PHP7.x; return types && type-hints
6
-
7
- namespace WPStaging\Component\Task;
8
-
9
- interface TaskInterface
10
- {
11
-
12
- public function execute();
13
-
14
- /**
15
- * Set relative cache directory for a task. This is only optional
16
- * @param string $path
17
- * @return void
18
- */
19
- public function setRelativeCacheDirectory($path);
20
-
21
- /**
22
- * Set task title, arguments used to feed information to the title such as;
23
- * sprintf('There are %d directories', $args[0]);
24
- * @param array $args
25
- * @return string
26
- */
27
- public function getStatusTitle(array $args = []);
28
-
29
- /**
30
- * Prepares the task for the execution.
31
- * @return void
32
- */
33
- public function prepare();
34
-
35
- /**
36
- * @return string|null
37
- */
38
- public function getJobName();
39
-
40
- /**
41
- * @param string|null $jobName
42
- */
43
- public function setJobName($jobName);
44
-
45
- /**
46
- * @return string|int|null
47
- */
48
- public function getJobId();
49
-
50
- /**
51
- * @param string|int|null $jobId
52
- */
53
- public function setJobId($jobId);
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Component/Task/TaskResponseDto.php DELETED
@@ -1,210 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_type=1);
4
- // TODO PHP7.x; type hints & return types
5
-
6
- namespace WPStaging\Component\Task;
7
-
8
- use WPStaging\Component\Dto\AbstractDto;
9
- use WPStaging\Framework\Traits\ArrayableTrait;
10
-
11
- class TaskResponseDto extends AbstractDto
12
- {
13
- use ArrayableTrait {
14
- toArray as traitToArray;
15
- }
16
-
17
- /** @var boolean */
18
- protected $status;
19
-
20
- /** @var int */
21
- protected $percentage;
22
-
23
- /** @var int */
24
- protected $total;
25
-
26
- /** @var int */
27
- protected $step;
28
-
29
- /** @var string */
30
- protected $task;
31
-
32
- /** @var string */
33
- protected $job;
34
-
35
- /** @var string */
36
- protected $statusTitle;
37
-
38
- /** @var array */
39
- protected $messages;
40
-
41
- /** @var float */
42
- protected $runTime;
43
-
44
- public function toArray()
45
- {
46
- $data = $this->traitToArray();
47
-
48
- $lastMsg = null;
49
- if ($data['messages']) {
50
- $lastMsg = end($data['messages']);
51
- }
52
-
53
- // TODO REF: Remove
54
- $data['last_msg'] = $lastMsg;
55
- $data['running_time'] = $data['runTime']?: 0;
56
- $data['isForceSave'] = true;
57
- $data['job_done'] = $data['status'];
58
-
59
- return $data;
60
- }
61
-
62
- public function addMessage($message)
63
- {
64
- if (!is_array($this->messages)) {
65
- $this->messages = [];
66
- }
67
- $this->messages[] = $message;
68
- }
69
-
70
- /**
71
- * @return bool
72
- */
73
- public function isStatus()
74
- {
75
- return $this->status;
76
- }
77
-
78
- /**
79
- * @param bool $status
80
- */
81
- public function setStatus($status)
82
- {
83
- $this->status = $status;
84
- }
85
-
86
- /**
87
- * @return int
88
- */
89
- public function getPercentage()
90
- {
91
- return $this->percentage;
92
- }
93
-
94
- /**
95
- * @param int $percentage
96
- */
97
- public function setPercentage($percentage)
98
- {
99
- $this->percentage = $percentage;
100
- }
101
-
102
- /**
103
- * @return int
104
- */
105
- public function getTotal()
106
- {
107
- return $this->total;
108
- }
109
-
110
- /**
111
- * @param int $total
112
- */
113
- public function setTotal($total)
114
- {
115
- $this->total = $total;
116
- }
117
-
118
- /**
119
- * @return int
120
- */
121
- public function getStep()
122
- {
123
- return $this->step;
124
- }
125
-
126
- /**
127
- * @param int $step
128
- */
129
- public function setStep($step)
130
- {
131
- $this->step = $step;
132
- }
133
-
134
- /**
135
- * @return string
136
- */
137
- public function getTask()
138
- {
139
- return $this->task;
140
- }
141
-
142
- /**
143
- * @param string $task
144
- */
145
- public function setTask($task)
146
- {
147
- $this->task = $task;
148
- }
149
-
150
- /**
151
- * @return string
152
- */
153
- public function getJob()
154
- {
155
- return $this->job;
156
- }
157
-
158
- /**
159
- * @param string $job
160
- */
161
- public function setJob($job)
162
- {
163
- $this->job = $job;
164
- }
165
-
166
- /**
167
- * @return string
168
- */
169
- public function getStatusTitle()
170
- {
171
- return $this->statusTitle;
172
- }
173
-
174
- /**
175
- * @param string $statusTitle
176
- */
177
- public function setStatusTitle($statusTitle)
178
- {
179
- $this->statusTitle = $statusTitle;
180
- }
181
-
182
- /**
183
- * @return array
184
- */
185
- public function getMessages()
186
- {
187
- return $this->messages;
188
- }
189
-
190
- public function setMessages(array $messages)
191
- {
192
- $this->messages = $messages;
193
- }
194
-
195
- /**
196
- * @return float
197
- */
198
- public function getRunTime()
199
- {
200
- return $this->runTime;
201
- }
202
-
203
- /**
204
- * @param float $runTime
205
- */
206
- public function setRunTime($runTime)
207
- {
208
- $this->runTime = $runTime;
209
- }
210
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Core/Cron/Cron.php CHANGED
@@ -54,5 +54,4 @@ class Cron
54
 
55
  return true;
56
  }
57
-
58
  }
54
 
55
  return true;
56
  }
 
57
  }
Core/DTO/Settings.php CHANGED
@@ -126,7 +126,7 @@ class Settings
126
  */
127
  public function getQueryLimit()
128
  {
129
- return ( int )$this->queryLimit;
130
  }
131
 
132
  /**
@@ -142,7 +142,7 @@ class Settings
142
  */
143
  public function getFileLimit()
144
  {
145
- return ( int )$this->fileLimit;
146
  }
147
 
148
  /**
@@ -158,7 +158,7 @@ class Settings
158
  */
159
  public function getBatchSize()
160
  {
161
- return ( int )$this->batchSize;
162
  }
163
 
164
  /**
@@ -282,5 +282,4 @@ class Settings
282
  {
283
  $this->usersWithStagingAccess = $usersWithStagingAccess;
284
  }
285
-
286
  }
126
  */
127
  public function getQueryLimit()
128
  {
129
+ return (int)$this->queryLimit;
130
  }
131
 
132
  /**
142
  */
143
  public function getFileLimit()
144
  {
145
+ return (int)$this->fileLimit;
146
  }
147
 
148
  /**
158
  */
159
  public function getBatchSize()
160
  {
161
+ return (int)$this->batchSize;
162
  }
163
 
164
  /**
282
  {
283
  $this->usersWithStagingAccess = $usersWithStagingAccess;
284
  }
 
285
  }
Core/Forms/Elements.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms;
3
 
4
  use WPStaging\Core\Forms\Elements\Interfaces\InterfaceElement;
@@ -72,7 +73,7 @@ abstract class Elements implements InterfaceElement
72
  */
73
  public function getName()
74
  {
75
- return $this->name;
76
  }
77
 
78
  /**
@@ -93,8 +94,7 @@ abstract class Elements implements InterfaceElement
93
  */
94
  public function setAttributes($attributes)
95
  {
96
- foreach ($attributes as $name => $value)
97
- {
98
  $this->setAttribute($name, $value);
99
  }
100
 
@@ -107,8 +107,7 @@ abstract class Elements implements InterfaceElement
107
  public function prepareAttributes()
108
  {
109
  $attributes = '';
110
- foreach ($this->attributes as $name => $value)
111
- {
112
  $attributes .= "{$name}='{$value}' ";
113
  }
114
 
@@ -156,12 +155,9 @@ abstract class Elements implements InterfaceElement
156
  */
157
  public function setFilters($filters)
158
  {
159
- if (is_string($filters))
160
- {
161
  $this->filters[] = $filters;
162
- }
163
- else
164
- {
165
  array_merge($this->filters, $filters);
166
  }
167
 
@@ -220,8 +216,7 @@ abstract class Elements implements InterfaceElement
220
  */
221
  public function setRenderFile($file)
222
  {
223
- if (file_exists($file) && is_readable($file))
224
- {
225
  $this->renderFile = $file;
226
  }
227
 
@@ -250,13 +245,11 @@ abstract class Elements implements InterfaceElement
250
  */
251
  public function getId($name = null)
252
  {
253
- if ($name === null)
254
- {
255
  $name = $this->name;
256
  }
257
 
258
- if (!$name)
259
- {
260
  return '';
261
  }
262
 
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms;
4
 
5
  use WPStaging\Core\Forms\Elements\Interfaces\InterfaceElement;
73
  */
74
  public function getName()
75
  {
76
+ return $this->name;
77
  }
78
 
79
  /**
94
  */
95
  public function setAttributes($attributes)
96
  {
97
+ foreach ($attributes as $name => $value) {
 
98
  $this->setAttribute($name, $value);
99
  }
100
 
107
  public function prepareAttributes()
108
  {
109
  $attributes = '';
110
+ foreach ($this->attributes as $name => $value) {
 
111
  $attributes .= "{$name}='{$value}' ";
112
  }
113
 
155
  */
156
  public function setFilters($filters)
157
  {
158
+ if (is_string($filters)) {
 
159
  $this->filters[] = $filters;
160
+ } else {
 
 
161
  array_merge($this->filters, $filters);
162
  }
163
 
216
  */
217
  public function setRenderFile($file)
218
  {
219
+ if (file_exists($file) && is_readable($file)) {
 
220
  $this->renderFile = $file;
221
  }
222
 
245
  */
246
  public function getId($name = null)
247
  {
248
+ if ($name === null) {
 
249
  $name = $this->name;
250
  }
251
 
252
+ if (!$name) {
 
253
  return '';
254
  }
255
 
Core/Forms/Elements/Check.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
- namespace WPStaging\Core\Forms\Elements;
3
 
 
4
 
5
  use WPStaging\Core\Forms\ElementsWithOptions;
6
 
@@ -18,16 +18,14 @@ class Check extends ElementsWithOptions
18
  {
19
  $output = '';
20
 
21
- foreach ($this->options as $id => $value)
22
- {
23
  $checked = ($this->isChecked($id)) ? " checked=''" : '';
24
 
25
  $attributeId = $this->getId() . '_' . $this->getId($id);
26
 
27
  $output .= "<input type='checkbox' name='{$this->getId()}' id='{$attributeId}' value='{$id}' {$checked}/>";
28
 
29
- if ($value)
30
- {
31
  $output .= "<label for='{$attributeId}'>{$value}</label>";
32
  }
33
  }
@@ -48,8 +46,7 @@ class Check extends ElementsWithOptions
48
  (is_int($value) && $value == (int) $this->default) ||
49
  (is_array($this->default) && in_array($value, $this->default))
50
  )
51
- )
52
- {
53
  return true;
54
  }
55
 
1
  <?php
 
2
 
3
+ namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\ElementsWithOptions;
6
 
18
  {
19
  $output = '';
20
 
21
+ foreach ($this->options as $id => $value) {
 
22
  $checked = ($this->isChecked($id)) ? " checked=''" : '';
23
 
24
  $attributeId = $this->getId() . '_' . $this->getId($id);
25
 
26
  $output .= "<input type='checkbox' name='{$this->getId()}' id='{$attributeId}' value='{$id}' {$checked}/>";
27
 
28
+ if ($value) {
 
29
  $output .= "<label for='{$attributeId}'>{$value}</label>";
30
  }
31
  }
46
  (is_int($value) && $value == (int) $this->default) ||
47
  (is_array($this->default) && in_array($value, $this->default))
48
  )
49
+ ) {
 
50
  return true;
51
  }
52
 
Core/Forms/Elements/Date.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\Elements;
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
Core/Forms/Elements/DateTime.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\Elements;
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
Core/Forms/Elements/Email.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\Elements;
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
Core/Forms/Elements/File.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\Elements;
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
Core/Forms/Elements/Hidden.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
- namespace WPStaging\Core\Forms\Elements;
3
 
 
4
 
5
  use WPStaging\Core\Forms\Elements;
6
 
1
  <?php
 
2
 
3
+ namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
6
 
Core/Forms/Elements/Interfaces/InterfaceElement.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements\Interfaces;
3
 
4
  /**
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements\Interfaces;
4
 
5
  /**
Core/Forms/Elements/Interfaces/InterfaceElementWithOptions.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements\Interfaces;
3
 
4
  /**
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements\Interfaces;
4
 
5
  /**
Core/Forms/Elements/Numerical.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\Elements;
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
Core/Forms/Elements/Password.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\Elements;
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
Core/Forms/Elements/Radio.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
- namespace WPStaging\Core\Forms\Elements;
3
 
 
4
 
5
  use WPStaging\Core\Forms\ElementsWithOptions;
6
 
@@ -18,8 +18,7 @@ class Radio extends ElementsWithOptions
18
  {
19
  $output = '';
20
 
21
- foreach ($this->options as $id => $value)
22
- {
23
  $checked = ($this->default && $this->default === $value) ? " checked=''" : '';
24
 
25
  $attributeId = $this->getId($id);
1
  <?php
 
2
 
3
+ namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\ElementsWithOptions;
6
 
18
  {
19
  $output = '';
20
 
21
+ foreach ($this->options as $id => $value) {
 
22
  $checked = ($this->default && $this->default === $value) ? " checked=''" : '';
23
 
24
  $attributeId = $this->getId($id);
Core/Forms/Elements/Select.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\ElementsWithOptions;
@@ -17,14 +18,13 @@ class Select extends ElementsWithOptions
17
  {
18
  $output = "<select id='{$this->getId()}' name='{$this->name}' {$this->prepareAttributes()}>";
19
 
20
- foreach ($this->options as $id => $value)
21
- {
22
- $selected = ($this->isSelected($id)) ? " selected=''" : '';
23
 
24
- $output .= "<option value='{$id}'{$selected}>{$value}</option>";
25
- }
26
 
27
- $output.= "</select>";
28
 
29
  return $output;
30
  }
@@ -41,8 +41,7 @@ class Select extends ElementsWithOptions
41
  (is_string($this->default) && trim($this->default) === trim($value)) ||
42
  (is_array($this->default) && in_array($value, $this->default))
43
  )
44
- )
45
- {
46
  return true;
47
  }
48
 
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\ElementsWithOptions;
18
  {
19
  $output = "<select id='{$this->getId()}' name='{$this->name}' {$this->prepareAttributes()}>";
20
 
21
+ foreach ($this->options as $id => $value) {
22
+ $selected = ($this->isSelected($id)) ? " selected=''" : '';
 
23
 
24
+ $output .= "<option value='{$id}'{$selected}>{$value}</option>";
25
+ }
26
 
27
+ $output .= "</select>";
28
 
29
  return $output;
30
  }
41
  (is_string($this->default) && trim($this->default) === trim($value)) ||
42
  (is_array($this->default) && in_array($value, $this->default))
43
  )
44
+ ) {
 
45
  return true;
46
  }
47
 
Core/Forms/Elements/SelectMultiple.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\ElementsWithOptions;
@@ -17,15 +18,14 @@ class SelectMultiple extends ElementsWithOptions
17
  {
18
  $output = "<select multiple id='{$this->getId()}' name='{$this->name}' {$this->prepareAttributes()}>";
19
 
20
- foreach ($this->options as $id => $value)
21
- {
22
- $selected = ($this->isSelected($id)) ? " selected=''" : '';
23
 
24
- //$output .= "<option value='{$id}'{$selected}>{$value}</option>";
25
- $output .= "<option value='{$id}'{$selected}>{$value}</option>";
26
- }
27
 
28
- $output.= "</select>";
29
 
30
  return $output;
31
  }
@@ -42,8 +42,7 @@ class SelectMultiple extends ElementsWithOptions
42
  (is_string($this->default) && $this->default === $value) ||
43
  (is_array($this->default) && in_array($value, $this->default))
44
  )
45
- )
46
- {
47
  return true;
48
  }
49
 
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\ElementsWithOptions;
18
  {
19
  $output = "<select multiple id='{$this->getId()}' name='{$this->name}' {$this->prepareAttributes()}>";
20
 
21
+ foreach ($this->options as $id => $value) {
22
+ $selected = ($this->isSelected($id)) ? " selected=''" : '';
 
23
 
24
+ //$output .= "<option value='{$id}'{$selected}>{$value}</option>";
25
+ $output .= "<option value='{$id}'{$selected}>{$value}</option>";
26
+ }
27
 
28
+ $output .= "</select>";
29
 
30
  return $output;
31
  }
42
  (is_string($this->default) && $this->default === $value) ||
43
  (is_array($this->default) && in_array($value, $this->default))
44
  )
45
+ ) {
 
46
  return true;
47
  }
48
 
Core/Forms/Elements/Text.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\Elements;
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
Core/Forms/Elements/TextArea.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms\Elements;
3
 
4
  use WPStaging\Core\Forms\Elements;
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms\Elements;
4
 
5
  use WPStaging\Core\Forms\Elements;
Core/Forms/ElementsWithOptions.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms;
3
 
4
  use WPStaging\Core\Forms\Elements\Interfaces\InterfaceElementWithOptions;
@@ -45,8 +46,7 @@ abstract class ElementsWithOptions extends Elements implements InterfaceElementW
45
  */
46
  public function removeOption($id)
47
  {
48
- if (isset($this->options[$id]))
49
- {
50
  unset($this->options[$id]);
51
  }
52
 
@@ -59,8 +59,7 @@ abstract class ElementsWithOptions extends Elements implements InterfaceElementW
59
  */
60
  public function addOptions($options)
61
  {
62
- foreach ($options as $id => $name)
63
- {
64
  $this->addOption($id, $name);
65
  }
66
 
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms;
4
 
5
  use WPStaging\Core\Forms\Elements\Interfaces\InterfaceElementWithOptions;
46
  */
47
  public function removeOption($id)
48
  {
49
+ if (isset($this->options[$id])) {
 
50
  unset($this->options[$id]);
51
  }
52
 
59
  */
60
  public function addOptions($options)
61
  {
62
+ foreach ($options as $id => $name) {
 
63
  $this->addOption($id, $name);
64
  }
65
 
Core/Forms/Form.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Forms;
3
 
4
  use WPStaging\Core\Forms\Elements\Interfaces\InterfaceElement;
@@ -15,13 +16,11 @@ class Form
15
 
16
  public function __construct()
17
  {
18
-
19
  }
20
 
21
  public function add($element)
22
  {
23
- if (!($element instanceof InterfaceElement) && !($element instanceof InterfaceElementWithOptions))
24
- {
25
  return;
26
  }
27
 
@@ -30,8 +29,7 @@ class Form
30
 
31
  public function render($name)
32
  {
33
- if (!isset($this->elements[$name]))
34
- {
35
  return false;
36
  }
37
 
@@ -40,8 +38,7 @@ class Form
40
 
41
  public function label($name)
42
  {
43
- if (!isset($this->elements[$name]))
44
- {
45
  return false;
46
  }
47
 
1
  <?php
2
+
3
  namespace WPStaging\Core\Forms;
4
 
5
  use WPStaging\Core\Forms\Elements\Interfaces\InterfaceElement;
16
 
17
  public function __construct()
18
  {
 
19
  }
20
 
21
  public function add($element)
22
  {
23
+ if (!($element instanceof InterfaceElement) && !($element instanceof InterfaceElementWithOptions)) {
 
24
  return;
25
  }
26
 
29
 
30
  public function render($name)
31
  {
32
+ if (!isset($this->elements[$name])) {
 
33
  return false;
34
  }
35
 
38
 
39
  public function label($name)
40
  {
41
+ if (!isset($this->elements[$name])) {
 
42
  return false;
43
  }
44
 
Core/Iterators/RecursiveDirectoryIterator.php CHANGED
@@ -6,49 +6,57 @@ namespace WPStaging\Core\Iterators;
6
  if (!defined("WPINC")) {
7
  die;
8
  }
9
- // todo remove this class, use \RecursiveDirectoryIterator instead
10
- class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator {
11
 
12
- protected $excludeFolders = [];
13
-
14
-
15
- public function __construct( $path ) {
16
- parent::__construct( $path );
17
-
18
- // Skip current and parent directory
19
- $this->skipdots();
20
-
21
- }
22
-
23
- public function rewind() {
24
- parent::rewind();
25
-
26
- // Skip current and parent directory
27
- $this->skipdots();
28
- }
29
-
30
- public function next() {
31
- parent::next();
32
-
33
- // Skip current and parent directory
34
- $this->skipdots();
35
- }
36
-
37
- /**
38
- * Returns whether current entry is a directory and not '.' or '..'
39
- *
40
- * Explicitly set allow links flag, because RecursiveDirectoryIterator::FOLLOW_SYMLINKS
41
- * is not supported by <= PHP 5.3.0
42
- *
43
- * @return bool
44
- */
45
- public function hasChildren( $allow_links = true ) {
46
- return parent::hasChildren( $allow_links );
47
- }
48
-
49
- protected function skipdots() {
50
- while ( $this->isDot() ) {
51
- parent::next();
52
- }
53
- }
 
 
 
 
 
 
 
 
 
 
54
  }
6
  if (!defined("WPINC")) {
7
  die;
8
  }
 
 
9
 
10
+ /**
11
+ * @deprecated use \RecursiveDirectoryIterator instead
12
+ */
13
+ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
14
+ {
15
+
16
+ protected $excludeFolders = [];
17
+
18
+
19
+ public function __construct($path)
20
+ {
21
+ parent::__construct($path);
22
+
23
+ // Skip current and parent directory
24
+ $this->skipdots();
25
+ }
26
+
27
+ public function rewind()
28
+ {
29
+ parent::rewind();
30
+
31
+ // Skip current and parent directory
32
+ $this->skipdots();
33
+ }
34
+
35
+ public function next()
36
+ {
37
+ parent::next();
38
+
39
+ // Skip current and parent directory
40
+ $this->skipdots();
41
+ }
42
+
43
+ /**
44
+ * Returns whether current entry is a directory and not '.' or '..'
45
+ *
46
+ * Explicitly set allow links flag, because RecursiveDirectoryIterator::FOLLOW_SYMLINKS
47
+ * is not supported by <= PHP 5.3.0
48
+ *
49
+ * @return bool
50
+ */
51
+ public function hasChildren($allow_links = true)
52
+ {
53
+ return parent::hasChildren($allow_links);
54
+ }
55
+
56
+ protected function skipdots()
57
+ {
58
+ while ($this->isDot()) {
59
+ parent::next();
60
+ }
61
+ }
62
  }
Core/Iterators/RecursiveFilterExclude.php CHANGED
@@ -5,14 +5,16 @@ namespace WPStaging\Core\Iterators;
5
  use RecursiveFilterIterator;
6
  use RecursiveIterator;
7
 
8
- // todo refactor this class and its tests to RecursivePathExcludeFilter, move this class and its tests to WPStaging\Framework\Filesystem\Filters
 
 
9
  class RecursiveFilterExclude extends RecursiveFilterIterator
10
  {
11
  protected $exclude = [];
12
 
13
  public function __construct(RecursiveIterator $iterator, $exclude = [])
14
  {
15
- parent::__construct( $iterator );
16
  $this->exclude = $exclude;
17
  }
18
 
@@ -40,5 +42,4 @@ class RecursiveFilterExclude extends RecursiveFilterIterator
40
  {
41
  return new self($this->getInnerIterator()->getChildren(), $this->exclude);
42
  }
43
-
44
  }
5
  use RecursiveFilterIterator;
6
  use RecursiveIterator;
7
 
8
+ /**
9
+ * @deprecated Use WPStaging\Framework\Filesystem\Filters\RecursivePathExcludeFilter, which have more features and well tested
10
+ */
11
  class RecursiveFilterExclude extends RecursiveFilterIterator
12
  {
13
  protected $exclude = [];
14
 
15
  public function __construct(RecursiveIterator $iterator, $exclude = [])
16
  {
17
+ parent::__construct($iterator);
18
  $this->exclude = $exclude;
19
  }
20
 
42
  {
43
  return new self($this->getInnerIterator()->getChildren(), $this->exclude);
44
  }
 
45
  }
Core/Utils/Cache.php CHANGED
@@ -3,7 +3,7 @@
3
  namespace WPStaging\Core\Utils;
4
 
5
  // No Direct Access
6
- if( !defined( "WPINC" ) ) {
7
  die;
8
  }
9
 
@@ -14,7 +14,8 @@ use WPStaging\Framework\Filesystem\Filesystem;
14
  * Class Cache
15
  * @package WPStaging\Core\Utils
16
  */
17
- class Cache {
 
18
 
19
  /**
20
  * Cache directory (full path)
@@ -48,25 +49,25 @@ class Cache {
48
  * @throws \Exception
49
  */
50
 
51
- public function __construct( $lifetime = null, $cacheDir = null, $cacheExtension = null ) {
 
52
  // Set lifetime
53
- $lifetime = ( int ) $lifetime;
54
- if( $lifetime > 0 ) {
55
  $this->lifetime = $lifetime;
56
  }
57
 
58
  // Set cache directory
59
- if( !empty( $cacheDir ) && is_dir( $cacheDir ) ) {
60
  $this->cacheDir = $cacheDir;
61
  }
62
  // Set default
63
  else {
64
-
65
  $this->cacheDir = \WPStaging\Core\WPStaging::getContentDir();
66
  }
67
 
68
  // Set cache extension
69
- if( !empty( $cacheExtension ) ) {
70
  $this->cacheExtension = $cacheExtension;
71
  }
72
 
@@ -78,7 +79,7 @@ class Cache {
78
  *
79
  * @see \WPStaging\Backend\Notices\Notices::messages
80
  */
81
- (new Filesystem)->mkdir($this->cacheDir);
82
  }
83
 
84
  /**
@@ -88,13 +89,14 @@ class Cache {
88
  * @param null|int $lifetime
89
  * @return mixed|null
90
  */
91
- public function get( $cacheFileName, $defaultValue = null, $lifetime = null ) {
 
92
  // Check if file is valid
93
- if( ($cacheFile = $this->isValid( $cacheFileName, true, $lifetime )) === false ) {
94
  return $defaultValue;
95
  }
96
 
97
- return @unserialize( file_get_contents( $cacheFile ) );
98
  }
99
 
100
  /**
@@ -104,24 +106,24 @@ class Cache {
104
  * @return bool
105
  * @throws \Exception
106
  */
107
- public function save( $cacheFileName, $value ) {
 
108
  $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
109
 
110
  // Attempt to delete cache file if it exists
111
- if( is_file( $cacheFile ) && !@unlink( $cacheFile ) ) {
112
- $this->returnException( "Can't delete existing cache file" );
113
- throw new \Exception( "Can't delete existing cache file" );
114
  }
115
 
116
  try {
117
-
118
  // Save it to file
119
- if( !wpstg_put_contents( $cacheFile, @serialize( $value ) ) ) {
120
- $this->returnException( " Can't save data to: " . $cacheFile . " Disk quota exceeded or not enough free disk space left" );
121
  return false;
122
  }
123
- } catch ( Exception $e ) {
124
- $this->returnException( " Can't save data to: " . $cacheFile . " Error: " . $e );
125
  return false;
126
  }
127
  return true;
@@ -135,10 +137,11 @@ class Cache {
135
  * @return string|bool
136
  * @throws \Exception
137
  */
138
- public function isValid( $cacheFileName, $deleteFileIfInvalid = false, $lifetime = null ) {
 
139
  // Lifetime
140
- $lifetime = ( int ) $lifetime;
141
- if( $lifetime < -1 || $lifetime == 0 ) {
142
  $lifetime = $this->lifetime;
143
  }
144
 
@@ -146,21 +149,20 @@ class Cache {
146
  $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
147
 
148
  // File doesn't exist
149
- if( !is_file( $cacheFile ) ) {
150
  return false;
151
  }
152
 
153
  // As long as file exists, don't check lifetime
154
- if( $lifetime == -1 ) {
155
  return $cacheFile;
156
  }
157
 
158
  // Time is up, file is invalid
159
- if( $lifetime <= time() - filemtime( $cacheFile ) ) {
160
-
161
  // Attempt to delete the file
162
- if( $deleteFileIfInvalid === true && !@unlink( $cacheFile ) ) {
163
- throw new \Exception( "Attempting to delete invalid cache file has failed!" );
164
  }
165
 
166
  // No need to delete the file, return
@@ -176,9 +178,10 @@ class Cache {
176
  * @return bool
177
  * @throws \Exception
178
  */
179
- public function delete( $cacheFileName ) {
180
- if( ($cacheFile = $this->isValid( $cacheFileName, true )) !== false && @unlink( $cacheFile ) === false ) {
181
- throw new \Exception( "Couldn't delete cache: {$cacheFileName}. Full Path: {$cacheFile}" );
 
182
  }
183
 
184
  return true;
@@ -187,14 +190,16 @@ class Cache {
187
  /**
188
  * @return string
189
  */
190
- public function getCacheDir() {
 
191
  return $this->cacheDir;
192
  }
193
 
194
  /**
195
  * @return string
196
  */
197
- public function getCacheExtension() {
 
198
  return $this->cacheExtension;
199
  }
200
 
@@ -202,13 +207,13 @@ class Cache {
202
  * Throw a errror message via json and stop further execution
203
  * @param string $message
204
  */
205
- protected function returnException( $message = '' ) {
206
- wp_die( json_encode( [
207
- 'job' => isset( $this->options->currentJob ) ? $this->options->currentJob : '',
 
208
  'status' => false,
209
  'message' => $message,
210
  'error' => true
211
- ] ) );
212
  }
213
-
214
  }
3
  namespace WPStaging\Core\Utils;
4
 
5
  // No Direct Access
6
+ if (!defined("WPINC")) {
7
  die;
8
  }
9
 
14
  * Class Cache
15
  * @package WPStaging\Core\Utils
16
  */
17
+ class Cache
18
+ {
19
 
20
  /**
21
  * Cache directory (full path)
49
  * @throws \Exception
50
  */
51
 
52
+ public function __construct($lifetime = null, $cacheDir = null, $cacheExtension = null)
53
+ {
54
  // Set lifetime
55
+ $lifetime = (int) $lifetime;
56
+ if ($lifetime > 0) {
57
  $this->lifetime = $lifetime;
58
  }
59
 
60
  // Set cache directory
61
+ if (!empty($cacheDir) && is_dir($cacheDir)) {
62
  $this->cacheDir = $cacheDir;
63
  }
64
  // Set default
65
  else {
 
66
  $this->cacheDir = \WPStaging\Core\WPStaging::getContentDir();
67
  }
68
 
69
  // Set cache extension
70
+ if (!empty($cacheExtension)) {
71
  $this->cacheExtension = $cacheExtension;
72
  }
73
 
79
  *
80
  * @see \WPStaging\Backend\Notices\Notices::messages
81
  */
82
+ (new Filesystem())->mkdir($this->cacheDir);
83
  }
84
 
85
  /**
89
  * @param null|int $lifetime
90
  * @return mixed|null
91
  */
92
+ public function get($cacheFileName, $defaultValue = null, $lifetime = null)
93
+ {
94
  // Check if file is valid
95
+ if (($cacheFile = $this->isValid($cacheFileName, true, $lifetime)) === false) {
96
  return $defaultValue;
97
  }
98
 
99
+ return @unserialize(file_get_contents($cacheFile));
100
  }
101
 
102
  /**
106
  * @return bool
107
  * @throws \Exception
108
  */
109
+ public function save($cacheFileName, $value)
110
+ {
111
  $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
112
 
113
  // Attempt to delete cache file if it exists
114
+ if (is_file($cacheFile) && !@unlink($cacheFile)) {
115
+ $this->returnException("Can't delete existing cache file");
116
+ throw new \Exception("Can't delete existing cache file");
117
  }
118
 
119
  try {
 
120
  // Save it to file
121
+ if (!wpstg_put_contents($cacheFile, @serialize($value))) {
122
+ $this->returnException(" Can't save data to: " . $cacheFile . " Disk quota exceeded or not enough free disk space left");
123
  return false;
124
  }
125
+ } catch (Exception $e) {
126
+ $this->returnException(" Can't save data to: " . $cacheFile . " Error: " . $e);
127
  return false;
128
  }
129
  return true;
137
  * @return string|bool
138
  * @throws \Exception
139
  */
140
+ public function isValid($cacheFileName, $deleteFileIfInvalid = false, $lifetime = null)
141
+ {
142
  // Lifetime
143
+ $lifetime = (int) $lifetime;
144
+ if ($lifetime < -1 || $lifetime == 0) {
145
  $lifetime = $this->lifetime;
146
  }
147
 
149
  $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
150
 
151
  // File doesn't exist
152
+ if (!is_file($cacheFile)) {
153
  return false;
154
  }
155
 
156
  // As long as file exists, don't check lifetime
157
+ if ($lifetime == -1) {
158
  return $cacheFile;
159
  }
160
 
161
  // Time is up, file is invalid
162
+ if ($lifetime <= time() - filemtime($cacheFile)) {
 
163
  // Attempt to delete the file
164
+ if ($deleteFileIfInvalid === true && !@unlink($cacheFile)) {
165
+ throw new \Exception("Attempting to delete invalid cache file has failed!");
166
  }
167
 
168
  // No need to delete the file, return
178
  * @return bool
179
  * @throws \Exception
180
  */
181
+ public function delete($cacheFileName)
182
+ {
183
+ if (($cacheFile = $this->isValid($cacheFileName, true)) !== false && @unlink($cacheFile) === false) {
184
+ throw new \Exception("Couldn't delete cache: {$cacheFileName}. Full Path: {$cacheFile}");
185
  }
186
 
187
  return true;
190
  /**
191
  * @return string
192
  */
193
+ public function getCacheDir()
194
+ {
195
  return $this->cacheDir;
196
  }
197
 
198
  /**
199
  * @return string
200
  */
201
+ public function getCacheExtension()
202
+ {
203
  return $this->cacheExtension;
204
  }
205
 
207
  * Throw a errror message via json and stop further execution
208
  * @param string $message
209
  */
210
+ protected function returnException($message = '')
211
+ {
212
+ wp_die(json_encode([
213
+ 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
214
  'status' => false,
215
  'message' => $message,
216
  'error' => true
217
+ ]));
218
  }
 
219
  }
Core/Utils/Directories.php CHANGED
@@ -1,9 +1,9 @@
1
  <?php
 
2
  namespace WPStaging\Core\Utils;
3
 
4
  // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
  die;
8
  }
9
 
@@ -49,8 +49,7 @@ class Directories
49
  $path = realpath($path);
50
 
51
  // Invalid path
52
- if ($path === false)
53
- {
54
  return null;
55
  }
56
 
@@ -74,15 +73,12 @@ class Directories
74
  $totalBytes = 0;
75
 
76
  // Loop & add file size
77
- foreach ($iterator as $file)
78
- {
79
- try
80
- {
81
  $totalBytes += $file->getSize();
82
  }
83
  // Some invalid symbolik links can cause issues in *nix systems
84
- catch(\Exception $e)
85
- {
86
  $this->log->add("{$file} is a symbolic link or for some reason its size is invalid");
87
  }
88
  }
1
  <?php
2
+
3
  namespace WPStaging\Core\Utils;
4
 
5
  // No Direct Access
6
+ if (!defined("WPINC")) {
 
7
  die;
8
  }
9
 
49
  $path = realpath($path);
50
 
51
  // Invalid path
52
+ if ($path === false) {
 
53
  return null;
54
  }
55
 
73
  $totalBytes = 0;
74
 
75
  // Loop & add file size
76
+ foreach ($iterator as $file) {
77
+ try {
 
 
78
  $totalBytes += $file->getSize();
79
  }
80
  // Some invalid symbolik links can cause issues in *nix systems
81
+ catch (\Exception $e) {
 
82
  $this->log->add("{$file} is a symbolic link or for some reason its size is invalid");
83
  }
84
  }
Core/Utils/Helper.php CHANGED
@@ -3,12 +3,12 @@
3
  namespace WPStaging\Core\Utils;
4
 
5
  // No Direct Access
6
- if (!defined("WPINC"))
7
- {
8
  die;
9
  }
10
 
11
- class Helper {
 
12
 
13
 
14
  /**
@@ -19,44 +19,48 @@ class Helper {
19
  * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
20
 
21
  */
22
- public function getHomeUrl($blog_id = null, $scheme = null ) {
23
-
24
- if( empty( $blog_id ) || !is_multisite() ) {
25
- $url = get_option( 'home' );
26
- } else {
27
- switch_to_blog( $blog_id );
28
- $url = get_option( 'home' );
29
- restore_current_blog();
30
- }
31
-
32
- if( !in_array( $scheme, ['http', 'https', 'relative'] ) ) {
33
- if( is_ssl())
34
- $scheme = 'https';
35
- else
36
- $scheme = parse_url( $url, PHP_URL_SCHEME );
37
- }
38
-
39
- $url = set_url_scheme( $url, $scheme );
40
-
41
- return $url;
42
- }
 
 
43
 
44
  /**
45
  * Return WordPress home url without scheme e.h. host.com or www.host.com
46
  * @param string $str
47
  * @return string
48
  */
49
- public function getHomeUrlWithoutScheme() {
50
- return preg_replace( '#^https?://#', '', rtrim( $this->getHomeUrl(), '/' ) );
51
- }
 
52
 
53
 
54
  /**
55
  * Get raw base URL e.g. https://blog.domain.com or https://domain.com without any subfolder
56
  * @return string
57
  */
58
- public function getBaseUrl() {
59
- $result = parse_url( $this->getHomeUrl() );
 
60
  return $result['scheme'] . "://" . $result['host'];
61
  }
62
 
@@ -66,10 +70,8 @@ class Helper {
66
  * @param string $str
67
  * @return string
68
  */
69
- public function getBaseUrlWithoutScheme() {
70
- return preg_replace( '#^https?://#', '', rtrim( $this->getBaseUrl(), '/' ) );
 
71
  }
72
-
73
-
74
-
75
  }
3
  namespace WPStaging\Core\Utils;
4
 
5
  // No Direct Access
6
+ if (!defined("WPINC")) {
 
7
  die;
8
  }
9
 
10
+ class Helper
11
+ {
12
 
13
 
14
  /**
19
  * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
20
 
21
  */
22
+ public function getHomeUrl($blog_id = null, $scheme = null)
23
+ {
24
+
25
+ if (empty($blog_id) || !is_multisite()) {
26
+ $url = get_option('home');
27
+ } else {
28
+ switch_to_blog($blog_id);
29
+ $url = get_option('home');
30
+ restore_current_blog();
31
+ }
32
+
33
+ if (!in_array($scheme, ['http', 'https', 'relative'])) {
34
+ if (is_ssl()) {
35
+ $scheme = 'https';
36
+ } else {
37
+ $scheme = parse_url($url, PHP_URL_SCHEME);
38
+ }
39
+ }
40
+
41
+ $url = set_url_scheme($url, $scheme);
42
+
43
+ return $url;
44
+ }
45
 
46
  /**
47
  * Return WordPress home url without scheme e.h. host.com or www.host.com
48
  * @param string $str
49
  * @return string
50
  */
51
+ public function getHomeUrlWithoutScheme()
52
+ {
53
+ return preg_replace('#^https?://#', '', rtrim($this->getHomeUrl(), '/'));
54
+ }
55
 
56
 
57
  /**
58
  * Get raw base URL e.g. https://blog.domain.com or https://domain.com without any subfolder
59
  * @return string
60
  */
61
+ public function getBaseUrl()
62
+ {
63
+ $result = parse_url($this->getHomeUrl());
64
  return $result['scheme'] . "://" . $result['host'];
65
  }
66
 
70
  * @param string $str
71
  * @return string
72
  */
73
+ public function getBaseUrlWithoutScheme()
74
+ {
75
+ return preg_replace('#^https?://#', '', rtrim($this->getBaseUrl(), '/'));
76
  }
 
 
 
77
  }
Core/Utils/Htaccess.php CHANGED
@@ -5,8 +5,7 @@ namespace WPStaging\Core\Utils;
5
  use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
- if (!defined("WPINC"))
9
- {
10
  die;
11
  }
12
 
@@ -15,15 +14,17 @@ if (!defined("WPINC"))
15
  *
16
  * @author IronMan
17
  */
18
- class Htaccess {
19
-
 
20
  /**
21
- *
22
  * @var obj
23
  */
24
  public $filesystem;
25
-
26
- public function __construct() {
 
27
  $this->filesystem = new Filesystem();
28
  }
29
 
@@ -33,8 +34,9 @@ class Htaccess {
33
  * @param string $path Path to file
34
  * @return boolean
35
  */
36
- public function create( $path ) {
37
- return $this->filesystem->create( $path, implode( PHP_EOL, [
 
38
  '<IfModule mod_mime.c>',
39
  'AddType application/octet-stream .log',
40
  '</IfModule>',
@@ -44,7 +46,7 @@ class Htaccess {
44
  '<IfModule mod_autoindex.c>',
45
  'Options -Indexes',
46
  '</IfModule>',
47
- ] ) );
48
  }
49
 
50
  /**
@@ -55,12 +57,12 @@ class Htaccess {
55
  * @param string $path Path to file
56
  * @return boolean
57
  */
58
- public function createLitespeed( $path ) {
59
- return $this->filesystem->createWithMarkers( $path, 'LiteSpeed', [
 
60
  '<IfModule Litespeed>',
61
  'SetEnv noabort 1',
62
  '</IfModule>',
63
- ] );
64
  }
65
-
66
  }
5
  use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
+ if (!defined("WPINC")) {
 
9
  die;
10
  }
11
 
14
  *
15
  * @author IronMan
16
  */
17
+ class Htaccess
18
+ {
19
+
20
  /**
21
+ *
22
  * @var obj
23
  */
24
  public $filesystem;
25
+
26
+ public function __construct()
27
+ {
28
  $this->filesystem = new Filesystem();
29
  }
30
 
34
  * @param string $path Path to file
35
  * @return boolean
36
  */
37
+ public function create($path)
38
+ {
39
+ return $this->filesystem->create($path, implode(PHP_EOL, [
40
  '<IfModule mod_mime.c>',
41
  'AddType application/octet-stream .log',
42
  '</IfModule>',
46
  '<IfModule mod_autoindex.c>',
47
  'Options -Indexes',
48
  '</IfModule>',
49
+ ]));
50
  }
51
 
52
  /**
57
  * @param string $path Path to file
58
  * @return boolean
59
  */
60
+ public function createLitespeed($path)
61
+ {
62
+ return $this->filesystem->createWithMarkers($path, 'LiteSpeed', [
63
  '<IfModule Litespeed>',
64
  'SetEnv noabort 1',
65
  '</IfModule>',
66
+ ]);
67
  }
 
68
  }
Core/Utils/IISWebConfig.php CHANGED
@@ -5,7 +5,7 @@ namespace WPStaging\Core\Utils;
5
  use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
- if( !defined( "WPINC" ) ) {
9
  die;
10
  }
11
 
@@ -14,15 +14,17 @@ if( !defined( "WPINC" ) ) {
14
  *
15
  * @author IronMan
16
  */
17
- class IISWebConfig {
 
18
 
19
  /**
20
- *
21
  * @var obj
22
  */
23
  private $filesystem;
24
 
25
- public function __construct() {
 
26
  $this->filesystem = new Filesystem();
27
  }
28
 
@@ -32,8 +34,9 @@ class IISWebConfig {
32
  * @param string $path Path to file
33
  * @return boolean
34
  */
35
- public function create( $path ) {
36
- return $this->filesystem->create( $path, implode( PHP_EOL, [
 
37
  '<configuration>',
38
  '<system.webServer>',
39
  '<staticContent>',
@@ -47,7 +50,6 @@ class IISWebConfig {
47
  '<directoryBrowse enabled="false" />',
48
  '</system.webServer>',
49
  '</configuration>',
50
- ] ) );
51
  }
52
-
53
  }
5
  use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
+ if (!defined("WPINC")) {
9
  die;
10
  }
11
 
14
  *
15
  * @author IronMan
16
  */
17
+ class IISWebConfig
18
+ {
19
 
20
  /**
21
+ *
22
  * @var obj
23
  */
24
  private $filesystem;
25
 
26
+ public function __construct()
27
+ {
28
  $this->filesystem = new Filesystem();
29
  }
30
 
34
  * @param string $path Path to file
35
  * @return boolean
36
  */
37
+ public function create($path)
38
+ {
39
+ return $this->filesystem->create($path, implode(PHP_EOL, [
40
  '<configuration>',
41
  '<system.webServer>',
42
  '<staticContent>',
50
  '<directoryBrowse enabled="false" />',
51
  '</system.webServer>',
52
  '</configuration>',
53
+ ]));
54
  }
 
55
  }
Core/Utils/Info.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Core\Utils;
3
 
4
  class Info
@@ -26,8 +27,7 @@ class Info
26
  */
27
  public function getOS()
28
  {
29
- if (self::$OS === null)
30
- {
31
  self::$OS = strtoupper(substr(PHP_OS, 0, 3)); // WIN, LIN..
32
  }
33
 
@@ -41,14 +41,12 @@ class Info
41
  public function canUse($functionName)
42
  {
43
  // Set
44
- if (isset(self::$canUse[$functionName]))
45
- {
46
  return self::$canUse[$functionName];
47
  }
48
 
49
  // Function doesn't exist
50
- if (!function_exists($functionName))
51
- {
52
  return self::$canUse[$functionName] = false;
53
  }
54
 
1
  <?php
2
+
3
  namespace WPStaging\Core\Utils;
4
 
5
  class Info
27
  */
28
  public function getOS()
29
  {
30
+ if (self::$OS === null) {
 
31
  self::$OS = strtoupper(substr(PHP_OS, 0, 3)); // WIN, LIN..
32
  }
33
 
41
  public function canUse($functionName)
42
  {
43
  // Set
44
+ if (isset(self::$canUse[$functionName])) {
 
45
  return self::$canUse[$functionName];
46
  }
47
 
48
  // Function doesn't exist
49
+ if (!function_exists($functionName)) {
 
50
  return self::$canUse[$functionName] = false;
51
  }
52
 
Core/Utils/Logger.php CHANGED
@@ -9,6 +9,7 @@
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;
@@ -66,21 +67,16 @@ class Logger implements LoggerInterface
66
  public function __construct($logDir = null, $logExtension = null)
67
  {
68
  // Set log directory
69
- if (!empty($logDir) && is_dir($logDir))
70
- {
71
  $this->logDir = rtrim($logDir, "/\\") . DIRECTORY_SEPARATOR;
72
  }
73
  // Set default
74
- else
75
- {
76
-
77
- $this->logDir = \WPStaging\Core\WPStaging::getContentDir() . "logs" . DIRECTORY_SEPARATOR;
78
-
79
  }
80
 
81
  // Set log extension
82
- if (!empty($logExtension))
83
- {
84
  $this->logExtension = $logExtension;
85
  }
86
 
@@ -88,7 +84,7 @@ class Logger implements LoggerInterface
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()
@@ -109,8 +105,8 @@ class Logger implements LoggerInterface
109
  }
110
 
111
  /**
112
- * @param Strings $message
113
- * @param Strings $type
114
  */
115
  public function add($message, $type = self::TYPE_ERROR)
116
  {
@@ -142,21 +138,18 @@ class Logger implements LoggerInterface
142
  */
143
  public function commit()
144
  {
145
- if (empty($this->messages))
146
- {
147
  return true;
148
  }
149
 
150
  $messageString = '';
151
- foreach ($this->messages as $message)
152
- {
153
  if (is_array($message)) {
154
  $messageString .= "[{$message["type"]}]-[{$message["date"]}] {$message["message"]}" . PHP_EOL;
155
  }
156
  }
157
 
158
- if (strlen($messageString) < 1)
159
- {
160
  return true;
161
  }
162
 
@@ -182,8 +175,7 @@ class Logger implements LoggerInterface
182
  public function getLogFile($fileName = null)
183
  {
184
  // Default
185
- if ($fileName === null)
186
- {
187
  $fileName = ($this->fileName !== null) ? $this->fileName : date("Y_m_d");
188
  }
189
 
@@ -202,8 +194,7 @@ class Logger implements LoggerInterface
202
  {
203
  $logFile = $this->logDir . $logFileName . '.' . $this->logExtension;
204
 
205
- if (@unlink($logFile) === false)
206
- {
207
  throw new \Exception("Couldn't delete cache: {$logFileName}. Full Path: {$logFile}");
208
  }
209
 
@@ -233,11 +224,11 @@ class Logger implements LoggerInterface
233
  public function getLastLogMsg()
234
  {
235
  // return all messages
236
- if (count ($this->messages) > 1){
237
  return $this->messages;
238
- }else{
239
  // Return last message
240
- return $this->messages[]=array_pop($this->messages);
241
  }
242
  }
243
 
9
 
10
  namespace WPStaging\Core\Utils;
11
 
12
+ use WPStaging\Core\WPStaging;
13
  use WPStaging\Framework\Filesystem\Filesystem;
14
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
15
  use WPStaging\Vendor\Psr\Log\LogLevel;
67
  public function __construct($logDir = null, $logExtension = null)
68
  {
69
  // Set log directory
70
+ if (!empty($logDir) && is_dir($logDir)) {
 
71
  $this->logDir = rtrim($logDir, "/\\") . DIRECTORY_SEPARATOR;
72
  }
73
  // Set default
74
+ else {
75
+ $this->logDir = WPStaging::getContentDir() . "logs" . DIRECTORY_SEPARATOR;
 
 
 
76
  }
77
 
78
  // Set log extension
79
+ if (!empty($logExtension)) {
 
80
  $this->logExtension = $logExtension;
81
  }
82
 
84
  * If log directory doesn't exists, create it.
85
  * @see \WPStaging\Backend\Notices\Notices::messages Notice that shows if log directory couldn't be created.
86
  */
87
+ (new Filesystem())->mkdir($this->logDir);
88
  }
89
 
90
  public function __destruct()
105
  }
106
 
107
  /**
108
+ * @param string $message
109
+ * @param string $type
110
  */
111
  public function add($message, $type = self::TYPE_ERROR)
112
  {
138
  */
139
  public function commit()
140
  {
141
+ if (empty($this->messages)) {
 
142
  return true;
143
  }
144
 
145
  $messageString = '';
146
+ foreach ($this->messages as $message) {
 
147
  if (is_array($message)) {
148
  $messageString .= "[{$message["type"]}]-[{$message["date"]}] {$message["message"]}" . PHP_EOL;
149
  }
150
  }
151
 
152
+ if (strlen($messageString) < 1) {
 
153
  return true;
154
  }
155
 
175
  public function getLogFile($fileName = null)
176
  {
177
  // Default
178
+ if ($fileName === null) {
 
179
  $fileName = ($this->fileName !== null) ? $this->fileName : date("Y_m_d");
180
  }
181
 
194
  {
195
  $logFile = $this->logDir . $logFileName . '.' . $this->logExtension;
196
 
197
+ if (@unlink($logFile) === false) {
 
198
  throw new \Exception("Couldn't delete cache: {$logFileName}. Full Path: {$logFile}");
199
  }
200
 
224
  public function getLastLogMsg()
225
  {
226
  // return all messages
227
+ if (count($this->messages) > 1) {
228
  return $this->messages;
229
+ } else {
230
  // Return last message
231
+ return $this->messages[] = array_pop($this->messages);
232
  }
233
  }
234
 
Core/Utils/Multisite.php CHANGED
@@ -5,54 +5,59 @@ namespace WPStaging\Core\Utils;
5
  use WPStaging\Core\Utils\Helper;
6
 
7
  // No Direct Access
8
- if( !defined( "WPINC" ) ) {
9
- die;
10
  }
11
 
12
- class Multisite {
 
13
 
14
  /**
15
  * @var string
16
  */
17
- private $url;
18
 
19
- public function __construct() {
20
- $helper = new Helper();
21
- $this->url = $helper->getHomeUrl();
22
- }
 
23
 
24
  /**
25
  * Get raw site domain e.g. https://blog.domain.com or https://domain.com without any subfolder
26
  * @return string
27
  */
28
- public function getHomeDomain() {
29
- $result = parse_url( $this->url );
30
- return $result['scheme'] . "://" . $result['host'];
31
- }
 
32
 
33
  /**
34
  * Return domain without scheme e.g. blog.domain.com or domain.com
35
  * @param string $str
36
  * @return string
37
  */
38
- public function getHomeDomainWithoutScheme() {
39
- return preg_replace( '#^https?://#', '', rtrim( $this->getHomeDomain(), '/' ) );
40
- }
 
41
  /**
42
  * Get home url e.g. blog.domain.com
43
  * @return string
44
  */
45
- public function getHomeUrl() {
46
- return $this->url;
47
- }
 
48
 
49
  /**
50
  * Return url without scheme e.g. blog.domain.com/site1 or domain.com/site1
51
  * @param string $str
52
  * @return string
53
  */
54
- public function getHomeUrlWithoutScheme() {
55
- return preg_replace( '#^https?://#', '', rtrim( $this->url, '/' ) );
56
- }
57
-
58
  }
5
  use WPStaging\Core\Utils\Helper;
6
 
7
  // No Direct Access
8
+ if (!defined("WPINC")) {
9
+ die;
10
  }
11
 
12
+ class Multisite
13
+ {
14
 
15
  /**
16
  * @var string
17
  */
18
+ private $url;
19
 
20
+ public function __construct()
21
+ {
22
+ $helper = new Helper();
23
+ $this->url = $helper->getHomeUrl();
24
+ }
25
 
26
  /**
27
  * Get raw site domain e.g. https://blog.domain.com or https://domain.com without any subfolder
28
  * @return string
29
  */
30
+ public function getHomeDomain()
31
+ {
32
+ $result = parse_url($this->url);
33
+ return $result['scheme'] . "://" . $result['host'];
34
+ }
35
 
36
  /**
37
  * Return domain without scheme e.g. blog.domain.com or domain.com
38
  * @param string $str
39
  * @return string
40
  */
41
+ public function getHomeDomainWithoutScheme()
42
+ {
43
+ return preg_replace('#^https?://#', '', rtrim($this->getHomeDomain(), '/'));
44
+ }
45
  /**
46
  * Get home url e.g. blog.domain.com
47
  * @return string
48
  */
49
+ public function getHomeUrl()
50
+ {
51
+ return $this->url;
52
+ }
53
 
54
  /**
55
  * Return url without scheme e.g. blog.domain.com/site1 or domain.com/site1
56
  * @param string $str
57
  * @return string
58
  */
59
+ public function getHomeUrlWithoutScheme()
60
+ {
61
+ return preg_replace('#^https?://#', '', rtrim($this->url, '/'));
62
+ }
63
  }
Core/Utils/MySQL.php CHANGED
@@ -8,11 +8,13 @@ namespace WPStaging\Core\Utils;
8
  * @todo Confirm if it's deprecated and remove.
9
  * @see \WPStaging\Framework\Adapter\Database\MysqlAdapter Similar class
10
  */
11
- class MySQL {
12
-
 
13
  private $wpdb;
14
-
15
- public function __construct($wpdb){
 
16
  $this->wpdb = $wpdb;
17
  }
18
 
@@ -22,9 +24,10 @@ class MySQL {
22
  * @param string $input SQL query
23
  * @return resource
24
  */
25
- public function query( $input ) {
 
26
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
27
- return mysql_query( $input, $this->wpdb->dbh );
28
  }
29
 
30
  /**
@@ -33,9 +36,10 @@ class MySQL {
33
  * @param string $input String to escape
34
  * @return string
35
  */
36
- public function escape( $input ) {
 
37
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
38
- return mysql_real_escape_string( $input, $this->wpdb->dbh );
39
  }
40
 
41
  /**
@@ -43,9 +47,10 @@ class MySQL {
43
  *
44
  * @return integer
45
  */
46
- public function errno() {
 
47
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
48
- return mysql_errno( $this->wpdb->dbh );
49
  }
50
 
51
  /**
@@ -53,9 +58,10 @@ class MySQL {
53
  *
54
  * @return string
55
  */
56
- public function error() {
 
57
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
58
- return mysql_error( $this->wpdb->dbh );
59
  }
60
 
61
  /**
@@ -63,9 +69,10 @@ class MySQL {
63
  *
64
  * @return string
65
  */
66
- public function version() {
 
67
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
68
- return mysql_get_server_info( $this->wpdb->dbh );
69
  }
70
 
71
  /**
@@ -74,9 +81,10 @@ class MySQL {
74
  * @param resource $result MySQL resource
75
  * @return array
76
  */
77
- public function fetchAssoc( $result ) {
 
78
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
79
- return mysql_fetch_assoc( $result );
80
  }
81
 
82
  /**
@@ -85,9 +93,10 @@ class MySQL {
85
  * @param resource $result MySQL resource
86
  * @return array
87
  */
88
- public function fetchRow( $result ) {
 
89
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
90
- return mysql_fetch_row( $result );
91
  }
92
 
93
  /**
@@ -96,9 +105,10 @@ class MySQL {
96
  * @param resource $result MySQL resource
97
  * @return integer
98
  */
99
- public function numRows( $result ) {
 
100
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
101
- return mysql_num_rows( $result );
102
  }
103
 
104
  /**
@@ -107,9 +117,9 @@ class MySQL {
107
  * @param resource $result MySQL resource
108
  * @return void
109
  */
110
- public function freeResult( $result ) {
 
111
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
112
- mysql_free_result( $result );
113
  }
114
-
115
  }
8
  * @todo Confirm if it's deprecated and remove.
9
  * @see \WPStaging\Framework\Adapter\Database\MysqlAdapter Similar class
10
  */
11
+ class MySQL
12
+ {
13
+
14
  private $wpdb;
15
+
16
+ public function __construct($wpdb)
17
+ {
18
  $this->wpdb = $wpdb;
19
  }
20
 
24
  * @param string $input SQL query
25
  * @return resource
26
  */
27
+ public function query($input)
28
+ {
29
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
30
+ return mysql_query($input, $this->wpdb->dbh);
31
  }
32
 
33
  /**
36
  * @param string $input String to escape
37
  * @return string
38
  */
39
+ public function escape($input)
40
+ {
41
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
42
+ return mysql_real_escape_string($input, $this->wpdb->dbh);
43
  }
44
 
45
  /**
47
  *
48
  * @return integer
49
  */
50
+ public function errno()
51
+ {
52
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
53
+ return mysql_errno($this->wpdb->dbh);
54
  }
55
 
56
  /**
58
  *
59
  * @return string
60
  */
61
+ public function error()
62
+ {
63
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
64
+ return mysql_error($this->wpdb->dbh);
65
  }
66
 
67
  /**
69
  *
70
  * @return string
71
  */
72
+ public function version()
73
+ {
74
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
75
+ return mysql_get_server_info($this->wpdb->dbh);
76
  }
77
 
78
  /**
81
  * @param resource $result MySQL resource
82
  * @return array
83
  */
84
+ public function fetchAssoc($result)
85
+ {
86
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
87
+ return mysql_fetch_assoc($result);
88
  }
89
 
90
  /**
93
  * @param resource $result MySQL resource
94
  * @return array
95
  */
96
+ public function fetchRow($result)
97
+ {
98
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
99
+ return mysql_fetch_row($result);
100
  }
101
 
102
  /**
105
  * @param resource $result MySQL resource
106
  * @return integer
107
  */
108
+ public function numRows($result)
109
+ {
110
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
111
+ return mysql_num_rows($result);
112
  }
113
 
114
  /**
117
  * @param resource $result MySQL resource
118
  * @return void
119
  */
120
+ public function freeResult($result)
121
+ {
122
  // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
123
+ mysql_free_result($result);
124
  }
 
125
  }
Core/Utils/MySQLi.php CHANGED
@@ -7,11 +7,13 @@ namespace WPStaging\Core\Utils;
7
  *
8
  * @todo Confirm if it's deprecated and remove.
9
  */
10
- class MySQLi {
 
11
 
12
  private $wpdb;
13
 
14
- public function __construct( $wpdb ) {
 
15
  $this->wpdb = $wpdb;
16
  }
17
 
@@ -21,8 +23,9 @@ class MySQLi {
21
  * @param string $input SQL query
22
  * @return resource
23
  */
24
- public function query( $input ) {
25
- return mysqli_query( $this->wpdb->dbh, $input, MYSQLI_STORE_RESULT );
 
26
  }
27
 
28
  /**
@@ -31,8 +34,9 @@ class MySQLi {
31
  * @param string $input String to escape
32
  * @return string
33
  */
34
- public function escape( $input ) {
35
- return mysqli_real_escape_string( $this->wpdb->dbh, $input );
 
36
  }
37
 
38
  /**
@@ -40,8 +44,9 @@ class MySQLi {
40
  *
41
  * @return integer
42
  */
43
- public function errno() {
44
- return mysqli_errno( $this->wpdb->dbh );
 
45
  }
46
 
47
  /**
@@ -49,8 +54,9 @@ class MySQLi {
49
  *
50
  * @return string
51
  */
52
- public function error() {
53
- return mysqli_error( $this->wpdb->dbh );
 
54
  }
55
 
56
  /**
@@ -58,8 +64,9 @@ class MySQLi {
58
  *
59
  * @return string
60
  */
61
- public function version() {
62
- return mysqli_get_server_info( $this->wpdb->dbh );
 
63
  }
64
 
65
  /**
@@ -68,8 +75,9 @@ class MySQLi {
68
  * @param resource $result MySQL resource
69
  * @return array
70
  */
71
- public function fetchAssoc( $result ) {
72
- return mysqli_fetch_assoc( $result );
 
73
  }
74
 
75
  /**
@@ -78,8 +86,9 @@ class MySQLi {
78
  * @param resource $result MySQL resource
79
  * @return array
80
  */
81
- public function fetchRow( $result ) {
82
- return mysqli_fetch_row( $result );
 
83
  }
84
 
85
  /**
@@ -88,8 +97,9 @@ class MySQLi {
88
  * @param resource $result MySQL resource
89
  * @return integer
90
  */
91
- public function numRows( $result ) {
92
- return mysqli_num_rows( $result );
 
93
  }
94
 
95
  /**
@@ -98,8 +108,8 @@ class MySQLi {
98
  * @param resource $result MySQL resource
99
  * @return void
100
  */
101
- public function freeResult( $result ) {
102
- mysqli_free_result( $result );
 
103
  }
104
-
105
  }
7
  *
8
  * @todo Confirm if it's deprecated and remove.
9
  */
10
+ class MySQLi
11
+ {
12
 
13
  private $wpdb;
14
 
15
+ public function __construct($wpdb)
16
+ {
17
  $this->wpdb = $wpdb;
18
  }
19
 
23
  * @param string $input SQL query
24
  * @return resource
25
  */
26
+ public function query($input)
27
+ {
28
+ return mysqli_query($this->wpdb->dbh, $input, MYSQLI_STORE_RESULT);
29
  }
30
 
31
  /**
34
  * @param string $input String to escape
35
  * @return string
36
  */
37
+ public function escape($input)
38
+ {
39
+ return mysqli_real_escape_string($this->wpdb->dbh, $input);
40
  }
41
 
42
  /**
44
  *
45
  * @return integer
46
  */
47
+ public function errno()
48
+ {
49
+ return mysqli_errno($this->wpdb->dbh);
50
  }
51
 
52
  /**
54
  *
55
  * @return string
56
  */
57
+ public function error()
58
+ {
59
+ return mysqli_error($this->wpdb->dbh);
60
  }
61
 
62
  /**
64
  *
65
  * @return string
66
  */
67
+ public function version()
68
+ {
69
+ return mysqli_get_server_info($this->wpdb->dbh);
70
  }
71
 
72
  /**
75
  * @param resource $result MySQL resource
76
  * @return array
77
  */
78
+ public function fetchAssoc($result)
79
+ {
80
+ return mysqli_fetch_assoc($result);
81
  }
82
 
83
  /**
86
  * @param resource $result MySQL resource
87
  * @return array
88
  */
89
+ public function fetchRow($result)
90
+ {
91
+ return mysqli_fetch_row($result);
92
  }
93
 
94
  /**
97
  * @param resource $result MySQL resource
98
  * @return integer
99
  */
100
+ public function numRows($result)
101
+ {
102
+ return mysqli_num_rows($result);
103
  }
104
 
105
  /**
108
  * @param resource $result MySQL resource
109
  * @return void
110
  */
111
+ public function freeResult($result)
112
+ {
113
+ mysqli_free_result($result);
114
  }
 
115
  }
Core/Utils/RobotsTxt.php CHANGED
@@ -5,8 +5,7 @@ namespace WPStaging\Core\Utils;
5
  use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
- if (!defined("WPINC"))
9
- {
10
  die;
11
  }
12
 
@@ -14,15 +13,17 @@ if (!defined("WPINC"))
14
  * Class for robots.txt
15
  *
16
  */
17
- class RobotsTxt {
18
-
 
19
  /**
20
- *
21
  * @var obj
22
  */
23
  public $filesystem;
24
-
25
- public function __construct() {
 
26
  $this->filesystem = new Filesystem();
27
  }
28
 
@@ -32,11 +33,11 @@ class RobotsTxt {
32
  * @param string $path Path to file
33
  * @return boolean
34
  */
35
- public function create( $path ) {
36
- return $this->filesystem->create( $path, implode( PHP_EOL, [
 
37
  'User-agent: *',
38
  'Disallow: /',
39
- ] ) );
40
  }
41
-
42
  }
5
  use WPStaging\Framework\Filesystem\Filesystem;
6
 
7
  // No Direct Access
8
+ if (!defined("WPINC")) {
 
9
  die;
10
  }
11
 
13
  * Class for robots.txt
14
  *
15
  */
16
+ class RobotsTxt
17
+ {
18
+
19
  /**
20
+ *
21
  * @var obj
22
  */
23
  public $filesystem;
24
+
25
+ public function __construct()
26
+ {
27
  $this->filesystem = new Filesystem();
28
  }
29
 
33
  * @param string $path Path to file
34
  * @return boolean
35
  */
36
+ public function create($path)
37
+ {
38
+ return $this->filesystem->create($path, implode(PHP_EOL, [
39
  'User-agent: *',
40
  'Disallow: /',
41
+ ]));
42
  }
 
43
  }
Core/Utils/Strings.php CHANGED
@@ -3,11 +3,12 @@
3
  namespace WPStaging\Core\Utils;
4
 
5
  // No Direct Access
6
- if( !defined( "WPINC" ) ) {
7
  die;
8
  }
9
 
10
- class Strings {
 
11
 
12
  /**
13
  * Replace first occurrence of certain string
@@ -16,14 +17,16 @@ class Strings {
16
  * @param string $subject
17
  * @return string
18
  */
19
- public function str_replace_first( $search, $replace, $subject ) {
 
20
 
21
- if( empty( $search ) )
22
  return $subject;
 
23
 
24
- $pos = strpos( $subject, $search );
25
- if( $pos !== false ) {
26
- return substr_replace( $subject, $replace, $pos, strlen( $search ) );
27
  }
28
  return $subject;
29
  }
@@ -35,9 +38,10 @@ class Strings {
35
  * @param string $haystack
36
  * @return string
37
  */
38
- public function getLastElemAfterString( $needle, $haystack ) {
39
- $pos = strrpos( $haystack, $needle );
40
- return $pos === false ? $haystack : substr( $haystack, $pos + 1 );
 
41
  }
42
 
43
  /**
@@ -45,8 +49,9 @@ class Strings {
45
  * @param string $str
46
  * @return string
47
  */
48
- public function getUrlWithoutScheme( $str ) {
49
- return preg_replace( '#^https?://#', '', rtrim( $str, '/' ) );
 
50
  }
51
 
52
  /**
@@ -56,8 +61,8 @@ class Strings {
56
  *
57
  * @return string
58
  */
59
- public function sanitizeDirectorySeparator( $path ) {
60
- return preg_replace( '/[\\\\]+/', '/', $path );
 
61
  }
62
-
63
  }
3
  namespace WPStaging\Core\Utils;
4
 
5
  // No Direct Access
6
+ if (!defined("WPINC")) {
7
  die;
8
  }
9
 
10
+ class Strings
11
+ {
12
 
13
  /**
14
  * Replace first occurrence of certain string
17
  * @param string $subject
18
  * @return string
19
  */
20
+ public function str_replace_first($search, $replace, $subject)
21
+ {
22
 
23
+ if (empty($search)) {
24
  return $subject;
25
+ }
26
 
27
+ $pos = strpos($subject, $search);
28
+ if ($pos !== false) {
29
+ return substr_replace($subject, $replace, $pos, strlen($search));
30
  }
31
  return $subject;
32
  }
38
  * @param string $haystack
39
  * @return string
40
  */
41
+ public function getLastElemAfterString($needle, $haystack)
42
+ {
43
+ $pos = strrpos($haystack, $needle);
44
+ return $pos === false ? $haystack : substr($haystack, $pos + 1);
45
  }
46
 
47
  /**
49
  * @param string $str
50
  * @return string
51
  */
52
+ public function getUrlWithoutScheme($str)
53
+ {
54
+ return preg_replace('#^https?://#', '', rtrim($str, '/'));
55
  }
56
 
57
  /**
61
  *
62
  * @return string
63
  */
64
+ public function sanitizeDirectorySeparator($path)
65
+ {
66
+ return preg_replace('/[\\\\]+/', '/', $path);
67
  }
 
68
  }
Core/Utils/functions.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Globally applicable very tiny functions that have only one specific use case but that are needed more than one time.
4
  * We use snake case prefix 'wpstg_' to differentiate them with the rest of our code base
@@ -27,7 +28,6 @@ function wpstg_setup_environment()
27
 
28
  // Set maximum backtracking steps
29
  @ini_set('pcre.backtrack_limit', PHP_INT_MAX);
30
-
31
  }
32
 
33
  /**
@@ -145,7 +145,7 @@ function wpstg_urldecode($data)
145
  */
146
  function wpstg_is_stagingsite()
147
  {
148
- return (new \WPStaging\Framework\SiteInfo)->isStaging();
149
  }
150
 
151
  /**
@@ -186,7 +186,7 @@ function wpstg_get_memory_in_bytes($memory)
186
  */
187
  function wpstg_unique_constraint($query)
188
  {
189
- // Change name to random in all constraints, if there, to prevent trouble with existing
190
  $query = preg_replace_callback("/CONSTRAINT\s`(\w+)`/", function () {
191
  return "CONSTRAINT `" . uniqid() . "`";
192
  }, $query);
@@ -218,15 +218,15 @@ function wpstg_unique_constraint($query)
218
  /**
219
  * Get relative path to the uploads folder, can be a custom folder e.g assets or default folder wp-content/uploads
220
  *
221
- * @deprecated
222
- * @see \WPStaging\Framework\Utils\WpDefaultDirectories::getUploadPath Removed in favor of this.
223
  * @todo Remove this in future versions.
224
  *
225
- * @return string
226
  */
227
  function wpstg_get_abs_upload_dir()
228
  {
229
- return (new \WPStaging\Framework\Utils\WpDefaultDirectories())->getUploadPath();
230
  }
231
 
232
  /**
1
  <?php
2
+
3
  /**
4
  * Globally applicable very tiny functions that have only one specific use case but that are needed more than one time.
5
  * We use snake case prefix 'wpstg_' to differentiate them with the rest of our code base
28
 
29
  // Set maximum backtracking steps
30
  @ini_set('pcre.backtrack_limit', PHP_INT_MAX);
 
31
  }
32
 
33
  /**
145
  */
146
  function wpstg_is_stagingsite()
147
  {
148
+ return (new \WPStaging\Framework\SiteInfo())->isStaging();
149
  }
150
 
151
  /**
186
  */
187
  function wpstg_unique_constraint($query)
188
  {
189
+ // Change name to random in all constraints, if there, to prevent trouble with existing
190
  $query = preg_replace_callback("/CONSTRAINT\s`(\w+)`/", function () {
191
  return "CONSTRAINT `" . uniqid() . "`";
192
  }, $query);
218
  /**
219
  * Get relative path to the uploads folder, can be a custom folder e.g assets or default folder wp-content/uploads
220
  *
221
+ * @return string
222
+ *@see \WPStaging\Framework\Utils\WpDefaultDirectories::getUploadsPath Removed in favor of this.
223
  * @todo Remove this in future versions.
224
  *
225
+ * @deprecated
226
  */
227
  function wpstg_get_abs_upload_dir()
228
  {
229
+ return (new \WPStaging\Framework\Utils\WpDefaultDirectories())->getUploadsPath();
230
  }
231
 
232
  /**
Core/WPStaging.php CHANGED
@@ -2,76 +2,31 @@
2
 
3
  namespace WPStaging\Core;
4
 
5
- // No Direct Access
6
- if( !defined( "WPINC" ) ) {
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;
15
- use WPStaging\Core\DTO\Settings;
16
- use WPStaging\Framework\Security\AccessToken;
17
- use WPStaging\Framework\Security\Nonce;
18
  use WPStaging\Frontend\Frontend;
19
  use WPStaging\Core\Utils\Cache;
20
  use WPStaging\Core\Utils\Logger;
21
  use WPStaging\Framework\Permalinks\PermalinksPurge;
22
  use WPStaging\Core\Cron\Cron;
23
- use WPStaging\Framework\SiteInfo;
24
  use WPStaging\Framework\Staging\FirstRun;
25
 
26
  /**
27
  * Class WPStaging
28
  * @package WPStaging
29
  */
30
- final class WPStaging {
31
-
32
- /**
33
- * Slug: Either wp-staging or wp-staging-pro
34
- * @var string
35
- */
36
- public $slug;
37
-
38
- /**
39
- * Absolute plugin path
40
- * @var string
41
- */
42
- private $pluginPath;
43
-
44
  /**
45
  * Singleton instance
46
  * @var WPStaging
47
  */
48
  private static $instance;
49
 
50
- /**
51
- * @var \WPStaging\Framework\SiteInfo
52
- */
53
- private $siteInfo;
54
-
55
- /*
56
- * @var string
57
- */
58
- private $backend_url;
59
-
60
- /**
61
- * @var string
62
- */
63
- private $frontend_url;
64
-
65
- /**
66
- * @var string
67
- */
68
- private $url;
69
-
70
- /**
71
- * @var AccessToken
72
- */
73
- private $accessToken;
74
-
75
  /**
76
  * @var Container
77
  */
@@ -85,29 +40,27 @@ final class WPStaging {
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.
100
- * Therefore, we don't inject them using DI here.
101
- */
102
- $this->accessToken = new AccessToken;
103
- $this->siteInfo = new SiteInfo;
104
-
105
- $this->registerMain();
106
  $this->loadDependencies();
107
- $this->defineHooks();
108
  $this->initCron();
109
  $this->cloneSiteFirstRun();
110
- $this->maybeLoadPro();
 
 
 
 
111
  $this->handleCacheIssues();
112
  $this->preventDirectoryListing();
113
  }
@@ -115,179 +68,29 @@ final class WPStaging {
115
  /**
116
  * Initialize cron jobs
117
  */
118
- private function initCron() {
 
119
  // Register cron job and add new interval 'weekly'
120
- new Cron;
121
  }
122
 
123
  /**
124
  * Get root WP root path -
125
  * Changed ABSPATH trailingslash for windows compatibility
126
-
127
  * @return string
128
  */
129
- public static function getWPpath() {
130
- return str_replace( '/', DIRECTORY_SEPARATOR, ABSPATH );
131
- }
132
-
133
- /**
134
- * Method to be executed upon activation of the plugin
135
- */
136
- public function registerMain() {
137
- // Slug of the plugin
138
- $this->slug = plugin_basename( dirname(__DIR__) );
139
-
140
- // absolute path to the main plugin dir
141
- $this->pluginPath = plugin_dir_path(__DIR__);
142
-
143
- // URL to main plugin folder
144
- $this->url = plugin_dir_url(__DIR__);
145
-
146
- // URL to backend public folder folder
147
- $this->backend_url = plugin_dir_url(__DIR__) . "Backend/public/";
148
-
149
- // URL to frontend public folder folder
150
- $this->frontend_url = plugin_dir_url(__DIR__) . "Frontend/public/";
151
- }
152
-
153
- /**
154
- * Define Hooks
155
- */
156
- public function defineHooks() {
157
- add_action('admin_enqueue_scripts', [$this, 'enqueueElements'], 100);
158
- add_action('admin_enqueue_scripts', [$this, 'removeWPCoreJs'], 5);
159
- add_action('wp_enqueue_scripts', [$this, 'enqueueElements'], 100);
160
- }
161
-
162
- /**
163
- * Remove heartbeat api and user login check
164
- * @param bool $hook
165
- */
166
- public function removeWPCoreJs( $hook ) {
167
-
168
- if( $this->isDisabledAssets($hook)) {
169
- return;
170
- }
171
-
172
- // Disable user login status check
173
- // Todo: Can we remove this now that we have AccessToken?
174
- remove_action( 'admin_enqueue_scripts', 'wp_auth_check_load' );
175
-
176
- // Disable heartbeat check for cloning and pushing
177
- wp_deregister_script( 'heartbeat' );
178
- }
179
-
180
- /**
181
- * Check if current page is plugins.php
182
- * @global array $pagenow
183
- * @return bool
184
- */
185
- private function isPluginsPage() {
186
- global $pagenow;
187
- return ( $pagenow === 'plugins.php' );
188
- }
189
-
190
- /**
191
- * Scripts and Styles
192
- * @param string $hook
193
- */
194
- public function enqueueElements( $hook ) {
195
-
196
- // Load this css file on frontend and backend on all pages if current site is a staging site
197
- if( wpstg_is_stagingsite() ) {
198
- wp_enqueue_style( "wpstg-admin-bar", $this->backend_url . "css/wpstg-admin-bar.css", [], self::getVersion() );
199
- }
200
-
201
- // Load js file on page plugins.php in free version only
202
- if( !defined('WPSTGPRO_VERSION') && $this->isPluginsPage() ) {
203
- wp_enqueue_script(
204
- "wpstg-admin-script", $this->backend_url . "js/wpstg-admin-plugins.js", ["jquery"], self::getVersion(), false
205
- );
206
- wp_enqueue_style(
207
- "wpstg-admin-feedback", $this->backend_url . "css/wpstg-admin-feedback.css", [], self::getVersion()
208
- );
209
- }
210
-
211
- if( $this->isDisabledAssets($hook)) {
212
- return;
213
- }
214
-
215
-
216
- // Load admin js files
217
- wp_enqueue_script(
218
- "wpstg-admin-script", $this->backend_url . "js/wpstg-admin.js", ["jquery"], self::getVersion(), false
219
- );
220
-
221
- // Sweet Alert
222
- wp_enqueue_script(
223
- 'wpstg-admin-sweetalerts',
224
- $this->url . 'Backend/public/vendor/sweetalert2/sweetalert2.all.min.js',
225
- [],
226
- self::getVersion(),
227
- true
228
- );
229
-
230
- wp_enqueue_style(
231
- 'wpstg-admin-sweetalerts',
232
- $this->url . 'Backend/public/vendor/sweetalert2/wordpress-admin.min.css',
233
- [],
234
- self::getVersion()
235
- );
236
-
237
- // Load admin js pro files
238
- if(defined('WPSTGPRO_VERSION')) {
239
- wp_enqueue_script(
240
- "wpstg-admin-pro-script", $this->url . "Backend/Pro/public/js/wpstg-admin-pro.js", ["jquery"], self::getVersion(), false
241
- );
242
- }
243
-
244
- // Load admin css files
245
- wp_enqueue_style(
246
- "wpstg-admin", $this->backend_url . "css/wpstg-admin.css", [], self::getVersion()
247
- );
248
-
249
- wp_localize_script( "wpstg-admin-script", "wpstg", [
250
- "delayReq" => $this->getDelay(),
251
- "settings" => ( object )[], // TODO add settings?
252
- "tblprefix" => self::getTablePrefix(),
253
- "isMultisite" => is_multisite(),
254
- AccessToken::REQUEST_KEY => (string)$this->accessToken->getToken() ?: (string)$this->accessToken->generateNewToken(),
255
- 'nonce' => wp_create_nonce(Nonce::WPSTG_NONCE),
256
- ] );
257
- }
258
-
259
- /**
260
- * Load css and js files only on wp staging admin pages
261
- * @param $page string slug of the current page
262
- * @return bool
263
- */
264
- private function isDisabledAssets($page)
265
  {
266
- if (defined('WPSTGPRO_VERSION')) {
267
- $availablePages = [
268
- "toplevel_page_wpstg_clone",
269
- "wp-staging-pro_page_wpstg-settings",
270
- "wp-staging-pro_page_wpstg-tools",
271
- "wp-staging-pro_page_wpstg-license"
272
- ];
273
- } else {
274
- $availablePages = [
275
- "toplevel_page_wpstg_clone",
276
- "wp-staging_page_wpstg-settings",
277
- "wp-staging_page_wpstg-tools",
278
- "wp-staging_page_wpstg-welcome",
279
- ];
280
- }
281
-
282
- return !in_array($page, $availablePages) || !is_admin();
283
  }
284
 
285
  /**
286
  * Get table prefix of the current site
287
  * @return string
288
  */
289
- public static function getTablePrefix() {
290
- return WPStaging::getInstance()->get( "wpdb" )->prefix;
 
291
  }
292
 
293
  /**
@@ -295,11 +98,13 @@ final class WPStaging {
295
  *
296
  * @return string
297
  */
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
  /**
@@ -310,8 +115,8 @@ final class WPStaging {
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) {
@@ -321,96 +126,111 @@ final class WPStaging {
321
  return static::$instance;
322
  }
323
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  /**
325
  * Load Dependencies
326
  */
327
- private function loadDependencies() {
 
328
  // Load globally available functions
329
- require_once ($this->pluginPath . "Core/Utils/functions.php");
330
-
331
- $this->set( "cache", new Cache() );
332
 
333
- $this->set( "logger", new Logger() );
334
 
335
- $this->set( "settings", new Settings() );
336
 
337
  $this->loadLanguages();
338
 
339
  // Set Administrator
340
- if( is_admin() ) {
341
  new Administrator();
342
  } else {
343
  new Frontend();
344
  }
345
  }
346
 
347
- private function loadLanguages()
348
- {
349
- /** @noinspection NullPointerExceptionInspection */
350
- $languagesDirectory = WPSTG_PLUGIN_DIR . 'languages/';
351
-
352
- if (function_exists('get_user_locale')){
353
- $locale = get_user_locale();
354
- } else {
355
- $locale = get_locale();
356
- }
357
-
358
- // Traditional WP plugin locale filter
359
- $locale = apply_filters('plugin_locale', $locale, 'wp-staging');
360
- $moFile = sprintf('%1$s-%2$s.mo', 'wp-staging', $locale);
361
-
362
- // Setup paths to current locale file
363
- $moFileLocal = $languagesDirectory . $moFile;
364
- $moFileGlobal = sprintf('%s/wp-staging/%s', WP_LANG_DIR, $moFile);
365
-
366
- if (file_exists($moFileGlobal)) {
367
- load_textdomain('wp-staging', $moFileGlobal);
368
- }
369
- elseif (file_exists($moFileLocal)) {
370
- load_textdomain('wp-staging', $moFileLocal);
371
- }
372
- else {
373
- load_plugin_textdomain('wp-staging', false, $languagesDirectory);
374
- }
375
- }
376
 
377
  /**
378
  * Set a variable to DI with given name
 
379
  * @param string $name
380
- * @param mixed $variable
381
  *
382
  * @deprecated Refactor implementations of this method to use the Container instead.
383
  *
384
  * @return $this
385
  */
386
- public function set( $name, $variable ) {
387
- $this->container->setVar( $name, $variable );
 
388
 
389
- return $this;
390
  }
391
 
392
  /**
393
  * Get given name index from DI
 
394
  * @param string $name
395
  *
396
  * @deprecated Refactor implementations of this method to use the Container instead.
397
  *
398
  * @return mixed|null
399
  */
400
- public function get( $name ) {
401
- return $this->container->_get( $name );
 
402
  }
403
 
404
  /**
405
  * Get given name index from DI
 
406
  * @param string $name
407
  *
408
  * @deprecated Refactor implementations of this method to use Dependency Injection instead.
409
  *
410
  * @return mixed|null
411
  */
412
- public function _make( $name ) {
413
- return $this->container->make( $name );
 
414
  }
415
 
416
  /**
@@ -443,57 +263,13 @@ final class WPStaging {
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
- */
456
- public function getDelay() {
457
- $options = $this->get( "settings" );
458
- $setting = $options->getDelayRequests();
459
-
460
- switch ( $setting ) {
461
- case "0":
462
- $delay = 0;
463
- break;
464
-
465
- case "1":
466
- $delay = 1000;
467
- break;
468
-
469
- case "2":
470
- $delay = 2000;
471
- break;
472
-
473
- case "3":
474
- $delay = 3000;
475
- break;
476
-
477
- case "4":
478
- $delay = 4000;
479
- break;
480
-
481
- default:
482
- $delay = 0;
483
- }
484
-
485
- return $delay;
486
- }
487
-
488
- /**
489
- * Load Pro actions if they exist.
490
- */
491
- private function maybeLoadPro()
492
- {
493
- if (class_exists('\WPStaging\Pro\ProServiceProvider')) {
494
- $this->container->register(\WPStaging\Pro\ProServiceProvider::class);
495
- }
496
- }
497
 
498
  /**
499
  * Executes the first time a clone site runs.
@@ -506,10 +282,11 @@ final class WPStaging {
506
  /**
507
  * Takes care of cache issues in certain situations
508
  */
509
- private function handleCacheIssues() {
 
510
  $permalinksPurge = new PermalinksPurge();
511
- add_action( 'wpstg_pushing_complete', [ $permalinksPurge, 'executeAfterPushing' ]);
512
- add_action( 'wp_loaded', [ $permalinksPurge, 'purgePermalinks' ], $permalinksPurge::PLUGINS_LOADED_PRIORITY);
513
  }
514
 
515
  /**
@@ -523,5 +300,4 @@ final class WPStaging {
523
  $directoryListing->protectPluginUploadDirectory();
524
  }
525
  }
526
-
527
  }
2
 
3
  namespace WPStaging\Core;
4
 
5
+ use WPStaging\Framework\AssetServiceProvider;
 
 
 
 
6
  use WPStaging\Framework\Filesystem\DirectoryListing;
7
  use WPStaging\Framework\Filesystem\Filesystem;
8
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
9
  use WPStaging\Framework\DI\Container;
10
  use WPStaging\Backend\Administrator;
 
 
 
11
  use WPStaging\Frontend\Frontend;
12
  use WPStaging\Core\Utils\Cache;
13
  use WPStaging\Core\Utils\Logger;
14
  use WPStaging\Framework\Permalinks\PermalinksPurge;
15
  use WPStaging\Core\Cron\Cron;
 
16
  use WPStaging\Framework\Staging\FirstRun;
17
 
18
  /**
19
  * Class WPStaging
20
  * @package WPStaging
21
  */
22
+ final class WPStaging
23
+ {
 
 
 
 
 
 
 
 
 
 
 
 
24
  /**
25
  * Singleton instance
26
  * @var WPStaging
27
  */
28
  private static $instance;
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  /**
31
  * @var Container
32
  */
40
  /**
41
  * WPStaging constructor.
42
  */
43
+ private function __construct(Container $container)
44
+ {
45
  $this->container = $container;
46
  }
47
 
48
+ public function bootstrap()
49
+ {
50
  $this->isBootstrapped = true;
51
 
52
  // Todo: Move this to a common service Provider for both Free and Pro. Do not register anything else here.
53
  $this->container->bind(LoggerInterface::class, Logger::class);
54
 
 
 
 
 
 
 
 
 
55
  $this->loadDependencies();
56
+ $this->container->register(AssetServiceProvider::class);
57
  $this->initCron();
58
  $this->cloneSiteFirstRun();
59
+
60
+ if (class_exists('\WPStaging\Pro\ProServiceProvider')) {
61
+ $this->container->register(\WPStaging\Pro\ProServiceProvider::class);
62
+ }
63
+
64
  $this->handleCacheIssues();
65
  $this->preventDirectoryListing();
66
  }
68
  /**
69
  * Initialize cron jobs
70
  */
71
+ private function initCron()
72
+ {
73
  // Register cron job and add new interval 'weekly'
74
+ new Cron();
75
  }
76
 
77
  /**
78
  * Get root WP root path -
79
  * Changed ABSPATH trailingslash for windows compatibility
 
80
  * @return string
81
  */
82
+ public static function getWPpath()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  {
84
+ return str_replace('/', DIRECTORY_SEPARATOR, ABSPATH);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  }
86
 
87
  /**
88
  * Get table prefix of the current site
89
  * @return string
90
  */
91
+ public static function getTablePrefix()
92
+ {
93
+ return WPStaging::getInstance()->get("wpdb")->prefix;
94
  }
95
 
96
  /**
98
  *
99
  * @return string
100
  */
101
+ public static function getContentDir()
102
+ {
103
  $wp_upload_dir = wp_upload_dir();
104
  $path = $wp_upload_dir['basedir'] . '/wp-staging';
105
+ (new Filesystem())->mkdir($path);
106
+
107
+ return apply_filters('wpstg_get_upload_dir', $path . '/');
108
  }
109
 
110
  /**
115
  public static function getInstance()
116
  {
117
  if (static::$instance === null) {
118
+ static::$instance = new WPStaging(new Container());
119
+ static::getInstance();
120
  }
121
 
122
  if (!static::$instance->isBootstrapped) {
126
  return static::$instance;
127
  }
128
 
129
+ /**
130
+ * Resets the Dependency Injection Container
131
+ * Only to be used in automated tests.
132
+ */
133
+ public function resetContainer()
134
+ {
135
+ if (php_sapi_name() == "cli") {
136
+ $this->isBootstrapped = false;
137
+ $this->container = new Container();
138
+ }
139
+ }
140
+
141
  /**
142
  * Load Dependencies
143
  */
144
+ private function loadDependencies()
145
+ {
146
  // Load globally available functions
147
+ require_once(__DIR__ . "/Utils/functions.php");
 
 
148
 
149
+ $this->set("cache", new Cache());
150
 
151
+ $this->set("logger", new Logger());
152
 
153
  $this->loadLanguages();
154
 
155
  // Set Administrator
156
+ if (is_admin()) {
157
  new Administrator();
158
  } else {
159
  new Frontend();
160
  }
161
  }
162
 
163
+ private function loadLanguages()
164
+ {
165
+ /** @noinspection NullPointerExceptionInspection */
166
+ $languagesDirectory = WPSTG_PLUGIN_DIR . 'languages/';
167
+
168
+ if (function_exists('get_user_locale')) {
169
+ $locale = get_user_locale();
170
+ } else {
171
+ $locale = get_locale();
172
+ }
173
+
174
+ // Traditional WP plugin locale filter
175
+ $locale = apply_filters('plugin_locale', $locale, 'wp-staging');
176
+ $moFile = sprintf('%1$s-%2$s.mo', 'wp-staging', $locale);
177
+
178
+ // Setup paths to current locale file
179
+ $moFileLocal = $languagesDirectory . $moFile;
180
+ $moFileGlobal = sprintf('%s/wp-staging/%s', WP_LANG_DIR, $moFile);
181
+
182
+ if (file_exists($moFileGlobal)) {
183
+ load_textdomain('wp-staging', $moFileGlobal);
184
+ } elseif (file_exists($moFileLocal)) {
185
+ load_textdomain('wp-staging', $moFileLocal);
186
+ } else {
187
+ load_plugin_textdomain('wp-staging', false, $languagesDirectory);
188
+ }
189
+ }
 
 
190
 
191
  /**
192
  * Set a variable to DI with given name
193
+ *
194
  * @param string $name
195
+ * @param mixed $variable
196
  *
197
  * @deprecated Refactor implementations of this method to use the Container instead.
198
  *
199
  * @return $this
200
  */
201
+ public function set($name, $variable)
202
+ {
203
+ $this->container->setVar($name, $variable);
204
 
205
+ return $this;
206
  }
207
 
208
  /**
209
  * Get given name index from DI
210
+ *
211
  * @param string $name
212
  *
213
  * @deprecated Refactor implementations of this method to use the Container instead.
214
  *
215
  * @return mixed|null
216
  */
217
+ public function get($name)
218
+ {
219
+ return $this->container->_get($name);
220
  }
221
 
222
  /**
223
  * Get given name index from DI
224
+ *
225
  * @param string $name
226
  *
227
  * @deprecated Refactor implementations of this method to use Dependency Injection instead.
228
  *
229
  * @return mixed|null
230
  */
231
+ public function _make($name)
232
+ {
233
+ return $this->container->make($name);
234
  }
235
 
236
  /**
263
 
264
  /**
265
  * @return bool
266
+ *
267
  * @todo find a better place to make it mockable or add filter for mocking
268
  */
269
  public static function isPro()
270
  {
271
  return defined('WPSTGPRO_VERSION');
272
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
  /**
275
  * Executes the first time a clone site runs.
282
  /**
283
  * Takes care of cache issues in certain situations
284
  */
285
+ private function handleCacheIssues()
286
+ {
287
  $permalinksPurge = new PermalinksPurge();
288
+ add_action('wpstg_pushing_complete', [$permalinksPurge, 'executeAfterPushing']);
289
+ add_action('wp_loaded', [$permalinksPurge, 'purgePermalinks'], $permalinksPurge::PLUGINS_LOADED_PRIORITY);
290
  }
291
 
292
  /**
300
  $directoryListing->protectPluginUploadDirectory();
301
  }
302
  }
 
303
  }
Framework/Adapter/Database.php CHANGED
@@ -1,7 +1,9 @@
1
  <?php
 
2
  /**
3
  * IMPORTANT use only named queries or WP queries
4
  */
 
5
  /** @noinspection PhpUndefinedClassInspection */
6
 
7
  namespace WPStaging\Framework\Adapter;
@@ -26,10 +28,21 @@ class Database
26
  /** @var wpdb */
27
  private $wpdb;
28
 
29
- public function __construct()
 
 
 
 
 
 
30
  {
31
  global $wpdb;
32
  $this->wpdb = $wpdb;
 
 
 
 
 
33
  $this->wpdba = new WpDbAdapter($this->wpdb);
34
  $this->client = $this->findClient();
35
  }
@@ -59,6 +72,22 @@ class Database
59
  return $this->wpdb->prefix;
60
  }
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  /**
63
  * Return escaped prefix to use it in sql queries like 'wp\_'
64
  * @param string|null $prefix
1
  <?php
2
+
3
  /**
4
  * IMPORTANT use only named queries or WP queries
5
  */
6
+
7
  /** @noinspection PhpUndefinedClassInspection */
8
 
9
  namespace WPStaging\Framework\Adapter;
28
  /** @var wpdb */
29
  private $wpdb;
30
 
31
+ /** @var string */
32
+ private $productionPrefix;
33
+
34
+ /**
35
+ * @param wpdb $wpDatabase
36
+ */
37
+ public function __construct($wpDatabase = null)
38
  {
39
  global $wpdb;
40
  $this->wpdb = $wpdb;
41
+ if ($wpDatabase !== null && $wpDatabase !== $wpdb) {
42
+ $this->wpdb = $wpDatabase;
43
+ }
44
+
45
+ $this->productionPrefix = $wpdb->prefix;
46
  $this->wpdba = new WpDbAdapter($this->wpdb);
47
  $this->client = $this->findClient();
48
  }
72
  return $this->wpdb->prefix;
73
  }
74
 
75
+ /**
76
+ * @return string
77
+ */
78
+ public function getProductionPrefix()
79
+ {
80
+ return $this->productionPrefix;
81
+ }
82
+
83
+ /**
84
+ * @return bool
85
+ */
86
+ public function isExternal()
87
+ {
88
+ return !($this->wpdb->__get('dbhost') === DB_HOST && $this->wpdb->__get('dbname') === DB_NAME);
89
+ }
90
+
91
  /**
92
  * Return escaped prefix to use it in sql queries like 'wp\_'
93
  * @param string|null $prefix
Framework/Adapter/Database/DatabaseQueryDto.php CHANGED
@@ -2,7 +2,6 @@
2
 
3
  namespace WPStaging\Framework\Adapter\Database;
4
 
5
-
6
  class DatabaseQueryDto
7
  {
8
  /** @var string */
2
 
3
  namespace WPStaging\Framework\Adapter\Database;
4
 
 
5
  class DatabaseQueryDto
6
  {
7
  /** @var string */
Framework/Adapter/Database/InterfaceDatabase.php CHANGED
@@ -55,5 +55,4 @@ interface InterfaceDatabase
55
  * @return bool|int
56
  */
57
  public function exec($sql);
58
-
59
  }
55
  * @return bool|int
56
  */
57
  public function exec($sql);
 
58
  }
Framework/Adapter/Database/InterfaceDatabaseClient.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /** @noinspection PhpComposerExtensionStubsInspection */
3
 
4
  namespace WPStaging\Framework\Adapter\Database;
1
  <?php
2
+
3
  /** @noinspection PhpComposerExtensionStubsInspection */
4
 
5
  namespace WPStaging\Framework\Adapter\Database;
Framework/Adapter/Database/MysqlAdapter.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /** @noinspection PhpComposerExtensionStubsInspection */
3
 
4
  namespace WPStaging\Framework\Adapter\Database;
@@ -111,5 +112,4 @@ class MysqlAdapter implements InterfaceDatabaseClient
111
 
112
  return null;
113
  }
114
-
115
  }
1
  <?php
2
+
3
  /** @noinspection PhpComposerExtensionStubsInspection */
4
 
5
  namespace WPStaging\Framework\Adapter\Database;
112
 
113
  return null;
114
  }
 
115
  }
Framework/Adapter/Database/MysqliAdapter.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /** @noinspection PhpComposerExtensionStubsInspection */
3
 
4
  namespace WPStaging\Framework\Adapter\Database;
@@ -109,5 +110,4 @@ class MysqliAdapter implements InterfaceDatabaseClient
109
 
110
  return null;
111
  }
112
-
113
  }
1
  <?php
2
+
3
  /** @noinspection PhpComposerExtensionStubsInspection */
4
 
5
  namespace WPStaging\Framework\Adapter\Database;
110
 
111
  return null;
112
  }
 
113
  }
Framework/Adapter/Database/WpDbAdapter.php CHANGED
@@ -34,8 +34,8 @@ class WpDbAdapter extends AbstractDatabase
34
  return null;
35
  }
36
 
37
- $collection = new SplObjectStorage;
38
- foreach($records as $record) {
39
  $collection->attach($record);
40
  }
41
 
@@ -92,8 +92,7 @@ class WpDbAdapter extends AbstractDatabase
92
  {
93
  if (!$conditions) {
94
  $response = $this->client->get_results($sql);
95
- }
96
- else {
97
  $response = $this->client->get_results($this->client->prepare($sql, $conditions));
98
  }
99
 
34
  return null;
35
  }
36
 
37
+ $collection = new SplObjectStorage();
38
+ foreach ($records as $record) {
39
  $collection->attach($record);
40
  }
41
 
92
  {
93
  if (!$conditions) {
94
  $response = $this->client->get_results($sql);
95
+ } else {
 
96
  $response = $this->client->get_results($this->client->prepare($sql, $conditions));
97
  }
98
 
Framework/Adapter/DateTimeAdapter.php CHANGED
@@ -79,7 +79,7 @@ class DateTimeAdapter
79
  }
80
  }
81
 
82
- return $date?: null;
83
  }
84
 
85
  // TODO
79
  }
80
  }
81
 
82
+ return $date ?: null;
83
  }
84
 
85
  // TODO
Framework/Adapter/Directory.php CHANGED
@@ -57,6 +57,6 @@ class Directory
57
 
58
  public function getSlug()
59
  {
60
- return WPSTG_PLUGIN_SLUG;
61
  }
62
  }
57
 
58
  public function getSlug()
59
  {
60
+ return WPSTG_PLUGIN_SLUG;
61
  }
62
  }
Framework/Adapter/Maintenance.php CHANGED
@@ -11,7 +11,7 @@ class Maintenance
11
 
12
  public function isMaintenance()
13
  {
14
- return (new Filesystem)->exists($this->findMaintenanceFilePath());
15
  }
16
 
17
  public function enableMaintenance($isMaintenance)
@@ -25,7 +25,7 @@ class Maintenance
25
  }
26
 
27
  if (!$isMaintenance && $fileExists) {
28
- (new Filesystem)->delete($maintenanceFile);
29
  }
30
  }
31
 
11
 
12
  public function isMaintenance()
13
  {
14
+ return (new Filesystem())->exists($this->findMaintenanceFilePath());
15
  }
16
 
17
  public function enableMaintenance($isMaintenance)
25
  }
26
 
27
  if (!$isMaintenance && $fileExists) {
28
+ (new Filesystem())->delete($maintenanceFile);
29
  }
30
  }
31
 
Framework/Adapter/SourceDatabase.php CHANGED
@@ -1,6 +1,5 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\Adapter;
5
 
6
  use stdClass;
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\Adapter;
4
 
5
  use stdClass;
Framework/AssetServiceProvider.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework;
4
+
5
+ use WPStaging\Framework\Assets\Assets;
6
+ use WPStaging\Framework\DI\ServiceProvider;
7
+
8
+ class AssetServiceProvider extends ServiceProvider
9
+ {
10
+ protected function registerClasses()
11
+ {
12
+ $this->container->singleton(Assets::class);
13
+ }
14
+
15
+ protected function addHooks()
16
+ {
17
+ add_action('admin_enqueue_scripts', $this->container->callback(Assets::class, 'enqueueElements'), 100, 1);
18
+ add_action('admin_enqueue_scripts', $this->container->callback(Assets::class, 'removeWPCoreJs'), 5, 1);
19
+ add_action('wp_enqueue_scripts', $this->container->callback(Assets::class, 'enqueueElements'), 100, 1);
20
+ }
21
+ }
Framework/Assets/Assets.php ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Assets;
4
+
5
+ use WPStaging\Backend\Modules\Jobs\Scan;
6
+ use WPStaging\Core\DTO\Settings;
7
+ use WPStaging\Core\WPStaging;
8
+ use WPStaging\Framework\Security\AccessToken;
9
+ use WPStaging\Framework\Security\Nonce;
10
+
11
+ class Assets
12
+ {
13
+ private $accessToken;
14
+
15
+ private $settings;
16
+
17
+ public function __construct(AccessToken $accessToken, Settings $settings)
18
+ {
19
+ $this->accessToken = $accessToken;
20
+ $this->settings = $settings;
21
+ }
22
+
23
+ /**
24
+ * Prepand the URL to the assets to the given file
25
+ *
26
+ * @param string $assetsFile optional
27
+ * @return string
28
+ */
29
+ public function getAssetsUrl($assetsFile = '')
30
+ {
31
+ return WPSTG_PLUGIN_URL . "assets/$assetsFile";
32
+ }
33
+
34
+ /**
35
+ * Get the version the given file. Use for caching
36
+ *
37
+ * @param string $assetsFile
38
+ * @param string $assetsVersion, use WPStaging::getVersion() instead if not given
39
+ * @return string
40
+ */
41
+ public function getAssetsUrlWithVersion($assetsFile, $assetsVersion = '')
42
+ {
43
+ $url = $this->getAssetsUrl($assetsFile);
44
+ $ver = $this->getAssetsVersion($assetsFile, $assetsVersion);
45
+ return $url . '?v=' . $ver;
46
+ }
47
+
48
+ /**
49
+ * Prepand the Path to the assets to the given file
50
+ *
51
+ * @param string $assetsFile optional
52
+ * @return string
53
+ */
54
+ public function getAssetsPath($assetsFile = '')
55
+ {
56
+ return WPSTG_PLUGIN_DIR . "assets/$assetsFile";
57
+ }
58
+
59
+ /**
60
+ * Get the version the given file. Use for caching
61
+ *
62
+ * @param string $assetsFile
63
+ * @param string $assetsVersion Optional, use WPStaging::getVersion() instead if not given
64
+ * @return string
65
+ */
66
+ public function getAssetsVersion($assetsFile, $assetsVersion = '')
67
+ {
68
+ $filemtime = @filemtime($this->getAssetsPath($assetsFile));
69
+
70
+ if ($filemtime !== false) {
71
+ return $filemtime;
72
+ } else {
73
+ return $assetsVersion !== '' ? $assetsVersion : WPStaging::getVersion();
74
+ }
75
+ }
76
+
77
+ /**
78
+ * @action admin_enqueue_scripts 100 1
79
+ * @action wp_enqueue_scripts 100 1
80
+ */
81
+ public function enqueueElements($hook)
82
+ {
83
+ // Load this css file on frontend and backend on all pages if current site is a staging site
84
+ if (wpstg_is_stagingsite()) {
85
+ $asset = 'css/src/frontend/wpstg-admin-bar.css';
86
+ wp_enqueue_style(
87
+ "wpstg-admin-bar",
88
+ $this->getAssetsUrl($asset),
89
+ [],
90
+ $this->getAssetsVersion($asset)
91
+ );
92
+ }
93
+
94
+ // Load js file on page plugins.php in free version only
95
+ if (!defined('WPSTGPRO_VERSION') && $this->isPluginsPage()) {
96
+ $asset = 'js/dist/wpstg-admin-plugins.js';
97
+ wp_enqueue_script(
98
+ "wpstg-admin-script",
99
+ $this->getAssetsUrl($asset),
100
+ ["jquery"],
101
+ $this->getAssetsVersion($asset),
102
+ false
103
+ );
104
+
105
+ $asset = 'css/src/wpstg-admin-feedback.css';
106
+ wp_enqueue_style(
107
+ "wpstg-admin-feedback",
108
+ $this->getAssetsUrl($asset),
109
+ [],
110
+ $this->getAssetsVersion($asset)
111
+ );
112
+ }
113
+
114
+ if ($this->isDisabledAssets($hook)) {
115
+ return;
116
+ }
117
+
118
+ // Load admin js files
119
+ $asset = 'js/dist/wpstg-admin.js';
120
+ wp_enqueue_script(
121
+ "wpstg-admin-script",
122
+ $this->getAssetsUrl($asset),
123
+ ["jquery"],
124
+ $this->getAssetsVersion($asset),
125
+ false
126
+ );
127
+
128
+ // Load admin js files
129
+ $asset = 'js/dist/wpstg-legacy-database.js';
130
+ wp_enqueue_script(
131
+ "wpstg-legacy-database",
132
+ $this->getAssetsUrl($asset),
133
+ ["wpstg-admin-script"],
134
+ $this->getAssetsVersion($asset),
135
+ false
136
+ );
137
+ // Sweet Alert
138
+ $asset = 'js/vendor/sweetalert2.all.min.js';
139
+ wp_enqueue_script(
140
+ 'wpstg-admin-sweetalerts',
141
+ $this->getAssetsUrl($asset),
142
+ [],
143
+ $this->getAssetsVersion($asset),
144
+ true
145
+ );
146
+
147
+ $asset = 'css/vendor/sweetalert2.min.css';
148
+ wp_enqueue_style(
149
+ 'wpstg-admin-sweetalerts',
150
+ $this->getAssetsUrl($asset),
151
+ [],
152
+ $this->getAssetsVersion($asset)
153
+ );
154
+
155
+ // Notyf Toast Notification
156
+ $asset = 'js/vendor/notyf.min.js';
157
+ wp_enqueue_script(
158
+ 'wpstg-admin-notyf',
159
+ $this->getAssetsUrl($asset),
160
+ [],
161
+ $this->getAssetsVersion($asset),
162
+ true
163
+ );
164
+
165
+ $asset = 'css/vendor/notyf.min.css';
166
+ wp_enqueue_style(
167
+ 'wpstg-admin-notyf',
168
+ $this->getAssetsUrl($asset),
169
+ [],
170
+ $this->getAssetsVersion($asset)
171
+ );
172
+
173
+ // Load admin js pro files
174
+ if (defined('WPSTGPRO_VERSION')) {
175
+ $asset = 'js/dist/pro/wpstg-admin-pro.js';
176
+ wp_enqueue_script(
177
+ "wpstg-admin-pro-script",
178
+ $this->getAssetsUrl($asset),
179
+ ["jquery"],
180
+ $this->getAssetsVersion($asset),
181
+ false
182
+ );
183
+ }
184
+
185
+ // Load admin css files
186
+ $asset = 'css/src/wpstg-admin.css';
187
+ wp_enqueue_style(
188
+ "wpstg-admin",
189
+ $this->getAssetsUrl($asset),
190
+ [],
191
+ $this->getAssetsVersion($asset)
192
+ );
193
+
194
+ // Add ability to import/export modules to wpstg-admin.js and wpstg-admin-pro.js
195
+ add_filter('script_loader_tag', [$this, 'makeScriptsES6'], 10, 3);
196
+
197
+ wp_localize_script("wpstg-admin-script", "wpstg", [
198
+ "delayReq" => $this->getDelay(),
199
+ "settings" => (object)[
200
+ "directorySeparator" => Scan::DIRECTORIES_SEPARATOR,
201
+ ],
202
+ "tblprefix" => WPStaging::getTablePrefix(),
203
+ "isMultisite" => is_multisite(),
204
+ AccessToken::REQUEST_KEY => (string)$this->accessToken->getToken() ?: (string)$this->accessToken->generateNewToken(),
205
+ 'nonce' => wp_create_nonce(Nonce::WPSTG_NONCE),
206
+ 'ajaxUrl' => admin_url('admin-ajax.php'),
207
+ // TODO: handle i18n translations through Class/Service Provider?
208
+ 'i18n' => [
209
+ 'dbConnectionSuccess' => esc_html__('Database Connection - Success', 'wp-staging'),
210
+ 'dbConnectionFailed' => esc_html__('Database Connection - Failed', 'wp-staging'),
211
+ 'somethingWentWrong' => esc_html__('Something went wrong.', 'wp-staging'),
212
+ 'noImportFileFound' => esc_html__('No import file found.', 'wp-staging'),
213
+ 'selectFileToImport' => esc_html__('Select file to import.', 'wp-staging'),
214
+ ],
215
+ ]);
216
+ }
217
+
218
+ /**
219
+ * Load css and js files only on wp staging admin pages
220
+ *
221
+ * @param $page string slug of the current page
222
+ *
223
+ * @return bool
224
+ */
225
+ private function isDisabledAssets($page)
226
+ {
227
+ if (defined('WPSTGPRO_VERSION')) {
228
+ $availablePages = [
229
+ "toplevel_page_wpstg_clone",
230
+ "wp-staging-pro_page_wpstg-settings",
231
+ "wp-staging-pro_page_wpstg-tools",
232
+ "wp-staging-pro_page_wpstg-license",
233
+ ];
234
+ } else {
235
+ $availablePages = [
236
+ "toplevel_page_wpstg_clone",
237
+ "wp-staging_page_wpstg-settings",
238
+ "wp-staging_page_wpstg-tools",
239
+ "wp-staging_page_wpstg-welcome",
240
+ ];
241
+ }
242
+
243
+ return !in_array($page, $availablePages) || !is_admin();
244
+ }
245
+
246
+ /**
247
+ * Check if current page is plugins.php
248
+ * @global array $pagenow
249
+ * @return bool
250
+ */
251
+ private function isPluginsPage()
252
+ {
253
+ global $pagenow;
254
+
255
+ return ($pagenow === 'plugins.php');
256
+ }
257
+
258
+
259
+ /**
260
+ * Remove heartbeat api and user login check
261
+ *
262
+ * @action admin_enqueue_scripts 100 1
263
+ *
264
+ * @param bool $hook
265
+ */
266
+ public function removeWPCoreJs($hook)
267
+ {
268
+ if ($this->isDisabledAssets($hook)) {
269
+ return;
270
+ }
271
+
272
+ // Disable user login status check
273
+ // Todo: Can we remove this now that we have AccessToken?
274
+ remove_action('admin_enqueue_scripts', 'wp_auth_check_load');
275
+
276
+ // Disable heartbeat check for cloning and pushing
277
+ wp_deregister_script('heartbeat');
278
+ }
279
+
280
+ /**
281
+ * @return array|mixed|object
282
+ */
283
+ public function getDelay()
284
+ {
285
+ switch ($this->settings->getDelayRequests()) {
286
+ case "0":
287
+ $delay = 0;
288
+ break;
289
+
290
+ case "1":
291
+ $delay = 1000;
292
+ break;
293
+
294
+ case "2":
295
+ $delay = 2000;
296
+ break;
297
+
298
+ case "3":
299
+ $delay = 3000;
300
+ break;
301
+
302
+ case "4":
303
+ $delay = 4000;
304
+ break;
305
+
306
+ default:
307
+ $delay = 0;
308
+ }
309
+
310
+ return $delay;
311
+ }
312
+
313
+ public function makeScriptsES6($tag, $handle, $source)
314
+ {
315
+ if ($handle === 'wpstg-admin-pro-script' || $handle === 'wpstg-admin-script') {
316
+ $tag = '<script src="' . $source . '" type="module"></script>';
317
+ }
318
+
319
+ return $tag;
320
+ }
321
+ }
Framework/CloningProcess/CloningDto.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Job;
8
 
9
  class CloningDto
@@ -149,5 +147,4 @@ class CloningDto
149
  {
150
  return $this->externalDatabaseName;
151
  }
152
-
153
  }
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Job;
6
 
7
  class CloningDto
147
  {
148
  return $this->externalDatabaseName;
149
  }
 
150
  }
Framework/CloningProcess/Data/CloningService.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
  use WPStaging\Core\Utils\Logger;
9
 
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
  use WPStaging\Core\Utils\Logger;
7
 
Framework/CloningProcess/Data/CopyWpConfig.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
  use WPStaging\Core\Utils\Logger;
9
 
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
  use WPStaging\Core\Utils\Logger;
7
 
Framework/CloningProcess/Data/DBCloningService.php CHANGED
@@ -1,6 +1,5 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
  abstract class DBCloningService extends CloningService
@@ -49,7 +48,7 @@ abstract class DBCloningService extends CloningService
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;
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
5
  abstract class DBCloningService extends CloningService
48
  }
49
  // during update process option table was not skipped even though it was not selected
50
  // that was causing problem if staging site prefix is basically something string appended to,
51
+ // production site prefix i.e. staging prefix: wp_stagging_ and production prefix: wp_
52
  if (!in_array($prefix . $table, $this->dto->getTables())) {
53
  $this->log("Table " . $prefix . $table . ' not selected/updated. Skipping');
54
  return true;
Framework/CloningProcess/Data/DataCloningDto.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Job;
8
  use WPStaging\Framework\CloningProcess\CloningDto;
9
 
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Job;
6
  use WPStaging\Framework\CloningProcess\CloningDto;
7
 
Framework/CloningProcess/Data/FileCloningService.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
 
9
  //TODO: Class may not be needed in the future due to DTO introduction. Remove if unnecessary
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
 
7
  //TODO: Class may not be needed in the future due to DTO introduction. Remove if unnecessary
Framework/CloningProcess/Data/MultisiteAddNetworkAdministrators.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Core\Utils\Logger;
8
 
9
  class MultisiteAddNetworkAdministrators extends DBCloningService
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Core\Utils\Logger;
6
 
7
  class MultisiteAddNetworkAdministrators extends DBCloningService
Framework/CloningProcess/Data/MultisiteUpdateActivePlugins.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
  use WPStaging\Core\Utils\Logger;
9
 
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
  use WPStaging\Core\Utils\Logger;
7
 
Framework/CloningProcess/Data/MultisiteUpdateTablePrefix.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  class MultisiteUpdateTablePrefix extends UpdateTablePrefix
8
  {
9
  /**
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  class MultisiteUpdateTablePrefix extends UpdateTablePrefix
6
  {
7
  /**
Framework/CloningProcess/Data/ResetIndexPhp.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
 
9
  class ResetIndexPhp extends FileCloningService
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
 
7
  class ResetIndexPhp extends FileCloningService
Framework/CloningProcess/Data/UpdateSiteUrlAndHome.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
 
9
  class UpdateSiteUrlAndHome extends DBCloningService
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
 
7
  class UpdateSiteUrlAndHome extends DBCloningService
Framework/CloningProcess/Data/UpdateStagingOptionsTable.php CHANGED
@@ -1,8 +1,9 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
 
 
6
  use WPStaging\Framework\Staging\FirstRun;
7
  use WPStaging\Core\Utils\Logger;
8
 
@@ -23,9 +24,12 @@ class UpdateStagingOptionsTable extends DBCloningService
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;
30
  $updateOrInsert['permalink_structure'] = ' ';
31
  }
@@ -40,12 +44,12 @@ class UpdateStagingOptionsTable extends DBCloningService
40
  }
41
  $this->updateOptions($update);
42
 
43
- //$this->log("Done");
44
  return true;
45
  }
46
 
47
- protected function updateOrInsertOptions($options) {
48
- foreach($options as $name => $value) {
 
49
  $this->debugLog("Updating/inserting $name to $value");
50
  if (!$this->insertDbOption($name, $value)) {
51
  $this->log("Failed to update/insert $name {$this->dto->getStagingDb()->last_error}", Logger::TYPE_WARNING);
@@ -53,8 +57,9 @@ class UpdateStagingOptionsTable extends DBCloningService
53
  }
54
  }
55
 
56
- protected function updateOptions($options) {
57
- foreach($options as $name => $value) {
 
58
  $this->debugLog("Updating $name to $value");
59
  if ($this->updateDbOption($name, $value) === false) {
60
  $this->log("Failed to update $name {$this->dto->getStagingDb()->last_error}", Logger::TYPE_WARNING);
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
5
+ use WPStaging\Framework\CloningProcess\ExcludedPlugins;
6
+ use WPStaging\Framework\Staging\CloneOptions;
7
  use WPStaging\Framework\Staging\FirstRun;
8
  use WPStaging\Core\Utils\Logger;
9
 
24
  'wpstg_rmpermalinks_executed' => ' ',
25
  'blog_public' => 0,
26
  FirstRun::FIRST_RUN_KEY => 'true',
27
+ CloneOptions::WPSTG_CLONE_SETTINGS_KEY => serialize((object) [
28
+ FirstRun::MAILS_DISABLED_KEY => !((bool) $this->dto->getJob()->getOptions()->emailsAllowed),
29
+ ExcludedPlugins::EXCLUDED_PLUGINS_KEY => (new ExcludedPlugins())->getFilteredPluginsToExclude(),
30
+ ]),
31
  ];
32
+ if (!$this->keepPermalinks()) {
33
  $updateOrInsert['rewrite_rules'] = null;
34
  $updateOrInsert['permalink_structure'] = ' ';
35
  }
44
  }
45
  $this->updateOptions($update);
46
 
 
47
  return true;
48
  }
49
 
50
+ protected function updateOrInsertOptions($options)
51
+ {
52
+ foreach ($options as $name => $value) {
53
  $this->debugLog("Updating/inserting $name to $value");
54
  if (!$this->insertDbOption($name, $value)) {
55
  $this->log("Failed to update/insert $name {$this->dto->getStagingDb()->last_error}", Logger::TYPE_WARNING);
57
  }
58
  }
59
 
60
+ protected function updateOptions($options)
61
+ {
62
+ foreach ($options as $name => $value) {
63
  $this->debugLog("Updating $name to $value");
64
  if ($this->updateDbOption($name, $value) === false) {
65
  $this->log("Failed to update $name {$this->dto->getStagingDb()->last_error}", Logger::TYPE_WARNING);
Framework/CloningProcess/Data/UpdateTablePrefix.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
 
9
  class UpdateTablePrefix extends DBCloningService
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
 
7
  class UpdateTablePrefix extends DBCloningService
Framework/CloningProcess/Data/UpdateWpConfigConstants.php CHANGED
@@ -1,6 +1,5 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
  class UpdateWpConfigConstants extends FileCloningService
@@ -120,17 +119,15 @@ class UpdateWpConfigConstants extends FileCloningService
120
  {
121
  preg_match($this->abspathRegex, $content, $matches);
122
  if (!empty($matches[0])) {
123
-
124
  $replace = "define('" . $constant . "', " . $newDefinition . "); \n" .
125
  "if ( ! defined( 'ABSPATH' ) )";
126
 
127
  // escaping dollar sign in the value
128
- $replaceEscaped = addcslashes($replace, '\\$');
129
 
130
  if (($content = preg_replace([$this->abspathRegex], $replaceEscaped, $content)) === null) {
131
  throw new \RuntimeException("Failed to change " . $constant);
132
  }
133
-
134
  } else {
135
  throw new \RuntimeException("Can not add " . $constant . " constant to wp-config.php. Can not find free position to add it.");
136
  }
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
5
  class UpdateWpConfigConstants extends FileCloningService
119
  {
120
  preg_match($this->abspathRegex, $content, $matches);
121
  if (!empty($matches[0])) {
 
122
  $replace = "define('" . $constant . "', " . $newDefinition . "); \n" .
123
  "if ( ! defined( 'ABSPATH' ) )";
124
 
125
  // escaping dollar sign in the value
126
+ $replaceEscaped = addcslashes($replace, '\\$');
127
 
128
  if (($content = preg_replace([$this->abspathRegex], $replaceEscaped, $content)) === null) {
129
  throw new \RuntimeException("Failed to change " . $constant);
130
  }
 
131
  } else {
132
  throw new \RuntimeException("Can not add " . $constant . " constant to wp-config.php. Can not find free position to add it.");
133
  }
Framework/CloningProcess/Data/UpdateWpConfigTablePrefix.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
  use WPStaging\Core\Utils\Logger;
9
 
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
  use WPStaging\Core\Utils\Logger;
7
 
Framework/CloningProcess/Data/UpdateWpOptionsTablePrefix.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Data;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
  use WPStaging\Core\Utils\Logger;
9
 
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Data;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
  use WPStaging\Core\Utils\Logger;
7
 
Framework/CloningProcess/Database/DatabaseCloningService.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\Database;
5
 
6
-
7
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
8
  use WPStaging\Framework\CloningProcess\CloningDto;
9
  use WPStaging\Core\Utils\Logger;
@@ -35,7 +33,7 @@ class DatabaseCloningService
35
  {
36
  $rows = $offset + $limit;
37
  $limitation = '';
38
- if (( int )$limit > 0) {
39
  $limitation = " LIMIT {$limit} OFFSET {$offset}";
40
  }
41
  if ($this->dto->isExternal()) {
@@ -135,7 +133,7 @@ class DatabaseCloningService
135
  $this->log("Creating table {$new}");
136
  $stagingDb->query("CREATE TABLE {$new} LIKE {$old}");
137
  }
138
- $rowsInTable = ( int )$productionDb->get_var("SELECT COUNT(1) FROM `{$productionDb->dbname}`.`{$old}`");
139
  $this->log("Table {$old} contains {$rowsInTable} rows ");
140
  return $rowsInTable;
141
  }
@@ -236,5 +234,4 @@ class DatabaseCloningService
236
  }
237
  return [];
238
  }
239
-
240
  }
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\Database;
4
 
 
5
  use WPStaging\Backend\Modules\Jobs\Exceptions\FatalException;
6
  use WPStaging\Framework\CloningProcess\CloningDto;
7
  use WPStaging\Core\Utils\Logger;
33
  {
34
  $rows = $offset + $limit;
35
  $limitation = '';
36
+ if ((int)$limit > 0) {
37
  $limitation = " LIMIT {$limit} OFFSET {$offset}";
38
  }
39
  if ($this->dto->isExternal()) {
133
  $this->log("Creating table {$new}");
134
  $stagingDb->query("CREATE TABLE {$new} LIKE {$old}");
135
  }
136
+ $rowsInTable = (int)$productionDb->get_var("SELECT COUNT(1) FROM `{$productionDb->dbname}`.`{$old}`");
137
  $this->log("Table {$old} contains {$rowsInTable} rows ");
138
  return $rowsInTable;
139
  }
234
  }
235
  return [];
236
  }
 
237
  }
Framework/CloningProcess/ExcludedPlugins.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\CloningProcess;
4
+
5
+ use WPStaging\Framework\Staging\CloneOptions;
6
+
7
+ /**
8
+ * Add here the list of excluded plugins to make sure code remain DRY
9
+ */
10
+ class ExcludedPlugins
11
+ {
12
+ /**
13
+ * @var string
14
+ */
15
+ const EXCLUDED_PLUGINS_KEY = 'excluded_plugins';
16
+
17
+ /**
18
+ * @var array
19
+ */
20
+ private $excludedPlugins;
21
+
22
+ public function __construct()
23
+ {
24
+ // list of excluded plugins defined by WP Staging
25
+ $this->excludedPlugins = [
26
+ 'wps-hide-login'
27
+ ];
28
+ }
29
+
30
+ /**
31
+ * Get List of excluded plugins
32
+ * The array can contain:
33
+ * - A parent dir of a plugin like `woocommerce`
34
+ * - A single file plugin like `hello-dolly.php`
35
+ *
36
+ * Here single file plugin means the plugins which consist of only a single file i.e. Hello Dolly plugin,
37
+ * These single file plugins can be placed directly in the plugin path without the need of subfolder.
38
+ * @see https://developer.wordpress.org/plugins/intro/ see 2nd paragraph under "Why We Make Plugins" to understand single file plugin better.
39
+ *
40
+ * @return array
41
+ */
42
+ public function getPluginsToExclude()
43
+ {
44
+ return $this->excludedPlugins;
45
+ }
46
+
47
+ /**
48
+ * Get list of excluded plugins with absolute path to them
49
+ *
50
+ * @return array
51
+ */
52
+ public function getPluginsToExcludeWithAbsolutePaths()
53
+ {
54
+ return array_map(function ($plugin) {
55
+ return trailingslashit(WP_PLUGIN_DIR) . $plugin;
56
+ }, $this->excludedPlugins);
57
+ }
58
+
59
+ /**
60
+ * Get List of excluded plugins defined by WP Staging and by excluded path hooks
61
+ *
62
+ * @param array $installedPlugins - Used for unit testing
63
+ *
64
+ * @return array
65
+ */
66
+ public function getFilteredPluginsToExclude($installedPlugins = [])
67
+ {
68
+ // Apply filter
69
+ if (is_multisite()) {
70
+ $filteredExcludedPlugins = apply_filters('wpstg_clone_mu_excl_folders', $this->getPluginsToExcludeWithAbsolutePaths());
71
+ } else {
72
+ $filteredExcludedPlugins = apply_filters('wpstg_clone_excl_folders', $this->getPluginsToExcludeWithAbsolutePaths());
73
+ }
74
+
75
+ if ($installedPlugins === []) {
76
+ $installedPlugins = get_plugins();
77
+ $installedPlugins = array_keys($installedPlugins);
78
+ }
79
+
80
+ // Remove all paths other than plugins not in installed plugins
81
+ $filteredExcludedPlugins = array_filter($filteredExcludedPlugins, function ($path) use ($installedPlugins) {
82
+ foreach ($installedPlugins as $plugin) {
83
+ $plugin = trailingslashit(WP_PLUGIN_DIR) . explode('/', $plugin)[0];
84
+ if (strpos($path, $plugin) !== false) {
85
+ return true;
86
+ }
87
+ }
88
+
89
+ return false;
90
+ });
91
+
92
+ // Reindex the array
93
+ $filteredExcludedPlugins = array_values($filteredExcludedPlugins);
94
+ /*
95
+ * Remove plugins dir from the paths and
96
+ * only return plugin dir if inside directory otherwise return file
97
+ * path/to/site/wp-content/plugins/some-plugin/some-plugin.php will return some-plugin
98
+ * path/to/site/wp-content/plugins/single-file-plugin.php will return single-file-plugin.php
99
+ * path/to/site/wp-content/plugins/plugin-dir will return plugin-dir
100
+ */
101
+ return array_map(function ($path) {
102
+ $plugin = str_replace(trailingslashit(WP_PLUGIN_DIR), '', $path);
103
+ return explode('/', $plugin)[0];
104
+ }, $filteredExcludedPlugins);
105
+ }
106
+
107
+ /**
108
+ * This returns the actual excluded plugins during cloning/updating/resetting
109
+ *
110
+ * @return array
111
+ */
112
+ public function getExcludedPlugins()
113
+ {
114
+ return (new CloneOptions())->get(self::EXCLUDED_PLUGINS_KEY);
115
+ }
116
+ }
Framework/CloningProcess/SearchReplace/SearchReplaceService.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\CloningProcess\SearchReplace;
5
 
6
-
7
  class SearchReplaceService
8
  {
9
  private $excludedStrings = [
@@ -44,4 +42,4 @@ class SearchReplaceService
44
  '//' . $string // //example.com
45
  ];
46
  }
47
- }
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\CloningProcess\SearchReplace;
4
 
 
5
  class SearchReplaceService
6
  {
7
  private $excludedStrings = [
42
  '//' . $string // //example.com
43
  ];
44
  }
45
+ }
Framework/Collection/Collection.php CHANGED
@@ -46,7 +46,7 @@ class Collection extends SplObjectStorage implements JsonSerializable
46
  }
47
 
48
  /** @var HydrateableInterface $object */
49
- $object = new $this->storedClass;
50
  $object->hydrate((array) $item);
51
  $this->attach($object);
52
  }
46
  }
47
 
48
  /** @var HydrateableInterface $object */
49
+ $object = new $this->storedClass();
50
  $object->hydrate((array) $item);
51
  $this->attach($object);
52
  }
Framework/Collection/OptionCollection.php DELETED
@@ -1,72 +0,0 @@
1
- <?php /** @noinspection PhpUndefinedClassInspection */
2
-
3
- //TODO PHP7.x; declare(strict_types=1);
4
- //TODO PHP7.x type-hints & return types
5
-
6
- namespace WPStaging\Framework\Collection;
7
-
8
- use JsonSerializable;
9
- use WPStaging\Framework\Entity\AbstractEntity;
10
- use WPStaging\Framework\Entity\IdentifyableEntityInterface;
11
-
12
- class OptionCollection extends Collection implements JsonSerializable
13
- {
14
-
15
- /**
16
- * @param string $id
17
- *
18
- * @return bool
19
- */
20
- public function doesIncludeId($id)
21
- {
22
- // We could use following for simplicity but not that performable
23
- // return array_key_exists($id, $this->toArray());
24
- /** @var IdentifyableEntityInterface $item */
25
- foreach ($this as $item) {
26
- if ($id === $item->getId()) {
27
- return true;
28
- }
29
- }
30
-
31
- return false;
32
- }
33
-
34
- /**
35
- * @param string $id
36
- *
37
- * @return AbstractEntity|null
38
- * @noinspection PhpUnused
39
- */
40
- public function findById($id)
41
- {
42
- /** @var AbstractEntity $item */
43
- foreach ($this as $item) {
44
- if (method_exists($item, 'getId') && (string) $id === (string) $item->getId()) {
45
- return $item;
46
- }
47
- }
48
-
49
- return null;
50
- }
51
-
52
- /**
53
- * @param string $id
54
- */
55
- public function removeById($id)
56
- {
57
- $item = $this->findById($id);
58
- if (!$item) {
59
- return;
60
- }
61
- $this->detach($item);
62
- }
63
-
64
- public function sortBy($key, $sort = SORT_DESC)
65
- {
66
- $array = $this->toArray();
67
- $columns = array_column($array, $key);
68
- array_multisort($columns, $sort, $array);
69
- $this->removeAll($this);
70
- $this->attachAllByArray($array);
71
- }
72
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Component/AbstractTemplateComponent.php CHANGED
@@ -23,8 +23,8 @@ abstract class AbstractTemplateComponent
23
  $this->templateEngine = $templateEngine;
24
 
25
  // Todo: Inject using DI
26
- $this->accessToken = new AccessToken;
27
- $this->nonce = new Nonce;
28
  }
29
 
30
  /**
23
  $this->templateEngine = $templateEngine;
24
 
25
  // Todo: Inject using DI
26
+ $this->accessToken = new AccessToken();
27
+ $this->nonce = new Nonce();
28
  }
29
 
30
  /**
Framework/DI/Container.php CHANGED
@@ -31,6 +31,12 @@ class Container extends \WPStaging\Vendor\tad_DI52_Container
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.
31
  }
32
  }
33
 
34
+ public function offsetUnset($offset)
35
+ {
36
+ parent::offsetUnset($offset);
37
+ unset($this->reflections[$offset]);
38
+ }
39
+
40
  /**
41
  * You can use this to store an array of data in the container, without having to worry
42
  * if the array was already initialized or not.
Framework/DI/FeatureProviderInterface.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The API provided by a Service Provider that completely provides a feature.
5
+ *
6
+ * @package WPStaging\Framework\DI
7
+ */
8
+
9
+ namespace WPStaging\Framework\DI;
10
+
11
+ /**
12
+ * Interface FeatureProviderInterface
13
+ *
14
+ * @package WPStaging\Framework\DI
15
+ */
16
+ interface FeatureProviderInterface
17
+ {
18
+ /**
19
+ * Returns whether the feature provided by the provider is enabled or not.
20
+ *
21
+ * The check will happen on the feature provider trigger by checking if
22
+ * the feature is available (the trigger constant is defined and true) and, if so,
23
+ * if the environment var by the same name is not set to falsy value.
24
+ *
25
+ * @return bool Whether the feature provided is enabled or not.
26
+ */
27
+ public static function isEnabled();
28
+
29
+ /**
30
+ * Returns the constant, or environment variables, that will trigger the feature provider
31
+ * registration when set to truthy values.
32
+ *
33
+ * A Feature Provider MUST use the same name for both the constant that will enable it if
34
+ * defined AND true and for the environment variable that will disable it if set and falsy.
35
+ *
36
+ * @return string The name of the constant, or environment variable, that will trigger the
37
+ * feature provider registration when set to truthy values.
38
+ */
39
+ public static function getFeatureTrigger();
40
+
41
+ /**
42
+ * A Feature Provider MUST clearly indicate whether it did register or not.
43
+ *
44
+ * A Feature Provider might not register as it's not enabled or because its
45
+ * requirements are not satisfied.
46
+ *
47
+ * @return bool Whether the Feature Provider did register, as enabled, or not.
48
+ */
49
+ public function register();
50
+ }
Framework/DI/FeatureServiceProvider.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The base class that models a Service Provider that will completely provide a feature or not.
5
+ *
6
+ * Feature Service Providers will differ from normal providers in that they completely manage
7
+ * a feature and all the required bindings and hooks. Feature Providers follow the concept of a
8
+ * truthy constant to enable them, and a falsy value environment variable to disable them if activated.
9
+ * This allows Feature Providers to launch a feature "darkly" by simply not setting their trigger constant,
10
+ * once launched, users can disable the feature either by setting en environment variable with the same
11
+ * name as the constant to a falsy value or by setting the constant to `false` in their `wp-config.php` file.
12
+ *
13
+ * @package WPStaging\Framework\DI
14
+ */
15
+
16
+ namespace WPStaging\Framework\DI;
17
+
18
+ /**
19
+ * Class FeatureServiceProvider
20
+ *
21
+ * @package WPStaging\Framework\DI
22
+ */
23
+ abstract class FeatureServiceProvider extends ServiceProvider implements FeatureProviderInterface
24
+ {
25
+ /**
26
+ * Returns the constant, or environment variables, that will trigger the feature provider
27
+ * registration when set to truthy values.
28
+ *
29
+ * Note: if set, the constant MUST override the value of the environment variable. The provider
30
+ * registration should still honor its requirements.
31
+ * If the constant is defined AND truthy, then the feature MUST still be disabled setting an environment variable
32
+ * by the same name as the constant to a falsy value.
33
+ *
34
+ * @return string The name of the constant, or environment variable, that will trigger the
35
+ * feature provider registration when set to truthy values.
36
+ */
37
+ abstract public static function getFeatureTrigger();
38
+
39
+ /**
40
+ * Returns whether the feature provided by the provider is enabled or not.
41
+ *
42
+ * The check will happen on the feature provider trigger by checking if
43
+ * the feature is available (the trigger constant is defined and true) and, if so,
44
+ * if the environment var by the same name is not set to falsy value.
45
+ *
46
+ * @return bool Whether the feature provided is enabled or not.
47
+ */
48
+ public static function isEnabled()
49
+ {
50
+ $trigger = static::getFeatureTrigger();
51
+
52
+ if (!(defined($trigger) && constant($trigger) === true)) {
53
+ // The feature will only be enabled if the constant is set AND `true`.
54
+ return false;
55
+ }
56
+
57
+ if (getenv($trigger) !== false && (bool)getenv($trigger) === false) {
58
+ // The feature can be disabled by setting an environment variable by the trigger name to a falsy value.
59
+ return false;
60
+ }
61
+
62
+ return true;
63
+ }
64
+ }
Framework/DI/ServiceProvider.php CHANGED
@@ -4,7 +4,7 @@ namespace WPStaging\Framework\DI;
4
 
5
  abstract class ServiceProvider extends \WPStaging\Vendor\tad_DI52_ServiceProvider
6
  {
7
- final public function register()
8
  {
9
  $this->registerClasses();
10
  $this->addHooks();
@@ -15,12 +15,18 @@ abstract class ServiceProvider extends \WPStaging\Vendor\tad_DI52_ServiceProvide
15
  *
16
  * @return void
17
  */
18
- abstract public function registerClasses();
 
 
 
19
 
20
  /**
21
  * Enqueue hooks.
22
  *
23
  * @return void
24
  */
25
- abstract public function addHooks();
 
 
 
26
  }
4
 
5
  abstract class ServiceProvider extends \WPStaging\Vendor\tad_DI52_ServiceProvider
6
  {
7
+ public function register()
8
  {
9
  $this->registerClasses();
10
  $this->addHooks();
15
  *
16
  * @return void
17
  */
18
+ protected function registerClasses()
19
+ {
20
+ // No-op by default.
21
+ }
22
 
23
  /**
24
  * Enqueue hooks.
25
  *
26
  * @return void
27
  */
28
+ protected function addHooks()
29
+ {
30
+ // No-op by default.
31
+ }
32
  }
Framework/Database/DatabaseDumper.php DELETED
@@ -1,635 +0,0 @@
1
- <?php /** @noinspection PhpComposerExtensionStubsInspection */
2
-
3
- namespace WPStaging\Framework\Database;
4
-
5
- use Exception;
6
- use mysqli_result;
7
- use WPStaging\Framework\Adapter\Database;
8
- use WPStaging\Framework\Adapter\Database\InterfaceDatabaseClient;
9
- use WPStaging\Framework\Filesystem\File;
10
-
11
- class DatabaseDumper
12
- {
13
-
14
- const MAX_SELECT_ROWS = 1000;
15
- const MAX_TRANSACTION_QUERIES = 1000;
16
- const MAX_EXECUTION_TIME_SECONDS = 10;
17
-
18
- /** @var InterfaceDatabaseClient */
19
- private $client;
20
-
21
- /** @var Database */
22
- private $database;
23
-
24
- /** @var File */
25
- private $file;
26
-
27
- /** @var string */
28
- private $filename;
29
-
30
- /**
31
- * Current table
32
- * @var string
33
- */
34
- private $tableIndex;
35
-
36
- /**
37
- * Start copy from table row
38
- * @var string
39
- */
40
- private $tableRowsOffset;
41
-
42
- /**
43
- * Executed table rows
44
- * @var string
45
- */
46
- private $tableRowsExported;
47
-
48
- /** @var array */
49
- private $tables = [];
50
-
51
- /** @var array */
52
- protected $tableWhereClauses = [];
53
-
54
- public function __construct(Database $database)
55
- {
56
- $this->database = $database;
57
- $this->client = $database->getClient();
58
- }
59
-
60
- public function __destruct()
61
- {
62
- $this->file = null;
63
- }
64
-
65
- /**
66
- * @return Database
67
- */
68
- public function getDatabase()
69
- {
70
- return $this->database;
71
- }
72
-
73
- /**
74
- * @return int|string
75
- */
76
- public function getTableRowsOffset()
77
- {
78
- return (int) $this->tableRowsOffset;
79
- }
80
-
81
- /**
82
- * @param string
83
- */
84
- public function setTableRowsOffset($tableRowsOffset)
85
- {
86
- $this->tableRowsOffset = $tableRowsOffset;
87
- }
88
-
89
- /**
90
- * @return int
91
- */
92
- public function getTableIndex()
93
- {
94
- return (int) $this->tableIndex;
95
- }
96
-
97
- /**
98
- * @param string
99
- */
100
- public function setTableIndex($tableIndex)
101
- {
102
- $this->tableIndex = $tableIndex;
103
- }
104
-
105
- /**
106
- * @param string
107
- */
108
- public function setTableRowsExported($tableRowsExported)
109
- {
110
- $this->tableRowsExported = $tableRowsExported;
111
- }
112
-
113
- /**
114
- * @return int
115
- */
116
- public function getTableRowsExported()
117
- {
118
- return (int) $this->tableRowsExported;
119
- }
120
-
121
- /**
122
- * @param string
123
- */
124
- public function setFileName($filename)
125
- {
126
- $this->filename = $filename;
127
- $this->file = new File($filename, File::MODE_APPEND);
128
- }
129
-
130
- /**
131
- * @param callable|null $shouldStop
132
- * @return string|null
133
- * @throws Exception
134
- */
135
- public function export(callable $shouldStop = null)
136
- {
137
- if ($this->tableIndex === 0) {
138
- $this->file->fwrite($this->getHeader());
139
- }
140
-
141
- $this->client->query("SET SESSION sql_mode = ''");
142
-
143
- $views = $this->getViews();
144
-
145
- $tables = $this->getTables();
146
-
147
- $loopsMax = count($tables);
148
- for (; $this->tableIndex < $loopsMax;) {
149
-
150
- $tableName = $tables[$this->tableIndex];
151
-
152
- // Export views
153
- if ($this->isView($tableName, $views)) {
154
-
155
- $this->writeQueryCreateViews($tableName);
156
-
157
- $this->tableIndex++;
158
- $this->tableRowsOffset = 0;
159
- } else {
160
- $this->writeQueryCreateTable($tableName);
161
-
162
- $primaryKeys = $this->getPrimaryKeys($tableName);
163
- $tableColumns = $this->getColumnTypes($tableName);
164
-
165
- do {
166
- $query = $this->getQuery($primaryKeys, $tableName);
167
- $result = $this->client->query($query);
168
-
169
- if ($this->isTableCorrupt()) {
170
- $this->repairTable($tableName);
171
- $result = $this->client->query($query);
172
- }
173
-
174
- $numRows = $this->writeQueryInsert($result, $tableName, $tableColumns);
175
-
176
- $this->client->freeResult($result);
177
-
178
- // Stop execution
179
- if ($shouldStop && $shouldStop()) {
180
- return null;
181
- }
182
-
183
- } while ($numRows > 0);
184
- }
185
- }
186
- return $this->filename;
187
- }
188
-
189
- /**
190
- * @param $tableName
191
- * @throws Exception
192
- */
193
- private function writeQueryCreateViews($tableName)
194
- {
195
- if ($this->tableRowsOffset === 0) {
196
-
197
- $dropView = "\nDROP VIEW IF EXISTS `{$tableName}`;\n";
198
- $this->file->fwrite($dropView);
199
-
200
- $create_view = $this->getCreateView($tableName);
201
-
202
- $create_view = $this->replaceViewOptions($create_view);
203
-
204
- $this->file->fwrite($create_view);
205
-
206
- $this->file->fwrite(";\n\n");
207
- }
208
- }
209
-
210
- /**
211
- * Get header for dump file
212
- *
213
- * @return string
214
- */
215
- private function getHeader()
216
- {
217
- return sprintf(
218
- "-- WP Staging SQL Export Dump\n" .
219
- "-- https://wp-staging.com/\n" .
220
- "--\n" .
221
- "-- Host: %s\n" .
222
- "-- Database: %s\n" .
223
- "-- Class: %s\n" .
224
- "--\n",
225
- $this->getWpDb()->dbhost,
226
- $this->getWpDb()->dbname,
227
- get_class($this)
228
- );
229
- }
230
-
231
- /**
232
- *
233
- * @return array
234
- */
235
- private function getTables()
236
- {
237
- return $this->tables;
238
- }
239
-
240
- /** @param array $tables */
241
- public function setTables(array $tables = [])
242
- {
243
- $this->tables = $tables;
244
- }
245
-
246
- /** @return array */
247
- private function getViews()
248
- {
249
- static $views = null;
250
-
251
- if ($views === null) {
252
- $views = [];
253
-
254
- // Loop over views
255
- $result = $this->client->query("SHOW FULL TABLES FROM `{$this->getWpDb()->dbname}` WHERE `Table_type` = 'VIEW'");
256
- while ($row = $this->client->fetchRow($result)) {
257
- if (isset($row[0])) {
258
- $views[] = $row[0];
259
- }
260
- }
261
-
262
- $this->client->freeResult($result);
263
- }
264
-
265
- return $views;
266
- }
267
-
268
- /**
269
- * @param string $view_name View name
270
- * @return string
271
- */
272
- private function getCreateView($view_name)
273
- {
274
- $result = $this->client->query("SHOW CREATE VIEW `{$view_name}`");
275
- $row = $this->client->fetchAssoc($result);
276
-
277
- $this->client->freeResult($result);
278
-
279
- if (isset($row['Create View'])) {
280
- return $row['Create View'];
281
- }
282
- return '';
283
- }
284
-
285
- /**
286
- *
287
- * @param string $input Table value
288
- * @return string
289
- */
290
- private function replaceViewOptions($input)
291
- {
292
- return preg_replace('/CREATE(.+?)VIEW/i', 'CREATE VIEW', $input);
293
- }
294
-
295
- /**
296
- * @param string $input SQL statement
297
- * @return string
298
- */
299
- private function replaceTableConstraints($input)
300
- {
301
- $pattern = [
302
- '/\s+CONSTRAINT(.+)REFERENCES(.+),/i',
303
- '/,\s+CONSTRAINT(.+)REFERENCES(.+)/i',
304
- ];
305
-
306
- return preg_replace($pattern, '', $input);
307
- }
308
-
309
- /**
310
- * @param string $input SQL statement
311
- * @return string
312
- */
313
- private function replaceTableOptions($input)
314
- {
315
- $search = [
316
- 'TYPE=InnoDB',
317
- 'TYPE=MyISAM',
318
- 'ENGINE=Aria',
319
- 'TRANSACTIONAL=0',
320
- 'TRANSACTIONAL=1',
321
- 'PAGE_CHECKSUM=0',
322
- 'PAGE_CHECKSUM=1',
323
- 'TABLE_CHECKSUM=0',
324
- 'TABLE_CHECKSUM=1',
325
- 'ROW_FORMAT=PAGE',
326
- 'ROW_FORMAT=FIXED',
327
- 'ROW_FORMAT=DYNAMIC',
328
- ];
329
- $replace = [
330
- 'ENGINE=InnoDB',
331
- 'ENGINE=MyISAM',
332
- 'ENGINE=MyISAM',
333
- '',
334
- '',
335
- '',
336
- '',
337
- '',
338
- '',
339
- '',
340
- '',
341
- '',
342
- ];
343
-
344
- return str_ireplace($search, $replace, $input);
345
- }
346
-
347
- /**
348
- * @param string $table_name Table name
349
- * @return array
350
- */
351
- private function getPrimaryKeys($table_name)
352
- {
353
- $primary_keys = [];
354
-
355
- $result = $this->client->query("SHOW KEYS FROM `{$table_name}` WHERE `Key_name` = 'PRIMARY'");
356
- while ($row = $this->client->fetchAssoc($result)) {
357
- if (isset($row['Column_name'])) {
358
- $primary_keys[] = $row['Column_name'];
359
- }
360
- }
361
-
362
- $this->client->freeResult($result);
363
-
364
- return $primary_keys;
365
- }
366
-
367
- /**
368
- * @param string $table_name Table name
369
- * @return array
370
- */
371
- private function getColumnTypes($table_name)
372
- {
373
- $column_types = [];
374
-
375
- $result = $this->client->query("SHOW COLUMNS FROM `{$table_name}`");
376
- while ($row = $this->client->fetchAssoc($result)) {
377
- if (isset($row['Field'])) {
378
- $column_types[strtolower($row['Field'])] = $row['Type'];
379
- }
380
- }
381
-
382
- $this->client->freeResult($result);
383
-
384
- return $column_types;
385
- }
386
-
387
- /**
388
- * @param string $table_name Table name
389
- * @return array
390
- */
391
- private function getTableWhereClauses($table_name)
392
- {
393
- if (isset($this->tableWhereClauses[strtolower($table_name)])) {
394
- return $this->tableWhereClauses[strtolower($table_name)];
395
- }
396
-
397
- return [];
398
- }
399
-
400
- /**
401
- * @param string $table_name Table name
402
- * @return void
403
- */
404
- protected function repairTable($table_name)
405
- {
406
- $this->client->query("REPAIR TABLE `{$table_name}`");
407
- }
408
-
409
- /**
410
- * Get MySQL create table query
411
- *
412
- * @param string $table_name Table name
413
- * @return string
414
- */
415
- protected function getCreateTable($table_name)
416
- {
417
- $result = $this->client->query("SHOW CREATE TABLE `{$table_name}`");
418
- $row = $this->client->fetchAssoc($result);
419
-
420
- $this->client->freeResult($result);
421
-
422
- if (isset($row['Create Table'])) {
423
- return $row['Create Table'];
424
- }
425
- return '';
426
- }
427
-
428
- /**
429
- * @param string $input
430
- * @param string $column_type
431
- * @return string
432
- */
433
- protected function prepareTableValues($input, $column_type)
434
- {
435
- if ($input === null) {
436
- return 'NULL';
437
- } elseif (stripos($column_type, 'tinyint') === 0) {
438
- return $input;
439
- } elseif (stripos($column_type, 'smallint') === 0) {
440
- return $input;
441
- } elseif (stripos($column_type, 'mediumint') === 0) {
442
- return $input;
443
- } elseif (stripos($column_type, 'int') === 0) {
444
- return $input;
445
- } elseif (stripos($column_type, 'bigint') === 0) {
446
- return $input;
447
- } elseif (stripos($column_type, 'float') === 0) {
448
- return $input;
449
- } elseif (stripos($column_type, 'double') === 0) {
450
- return $input;
451
- } elseif (stripos($column_type, 'decimal') === 0) {
452
- return $input;
453
- } elseif (stripos($column_type, 'bit') === 0) {
454
- return $input;
455
- }
456
-
457
- return "'" . $this->client->escape($input) . "'";
458
- }
459
-
460
- /**
461
- * @param $tableName
462
- * @throws Exception
463
- */
464
- private function writeQueryCreateTable($tableName)
465
- {
466
- if ($this->tableRowsOffset === 0) {
467
-
468
- $dropTable = "\nDROP TABLE IF EXISTS `{$tableName}`;\n";
469
- $this->file->fwrite($dropTable);
470
-
471
- $createTableQuery = $this->getCreateTable($tableName);
472
- $createTableQuery = $this->replaceTableConstraints($createTableQuery);
473
- $createTableQuery = $this->replaceTableOptions($createTableQuery);
474
- $this->file->fwrite(preg_replace('#\s+#', ' ', $createTableQuery));
475
-
476
- $this->file->fwrite(";\n\n");
477
- }
478
- }
479
-
480
- /**
481
- * @param array $primaryKeys
482
- * @param $tableName
483
- * @return string
484
- */
485
- private function getQuery(array $primaryKeys, $tableName)
486
- {
487
- if ($primaryKeys) {
488
- return $this->getSelectQueryPrimaryKey($primaryKeys, $tableName);
489
- }
490
- return $this->getSelectQuery($tableName);
491
- }
492
-
493
- /**
494
- * @param array $primaryKeys
495
- * @param $tableName
496
- * @return string
497
- */
498
- private function getSelectQueryPrimaryKey(array $primaryKeys, $tableName)
499
- {
500
- // Set table keys
501
- $tableKeys = [];
502
- foreach ($primaryKeys as $key) {
503
- $tableKeys[] = sprintf('`%s`', $key);
504
- }
505
-
506
- $tableKeys = implode(', ', $tableKeys);
507
-
508
- // Set table where clauses
509
- $tableWhere = [1];
510
- foreach ($this->getTableWhereClauses($tableName) as $clause) {
511
- $tableWhere[] = $clause;
512
- }
513
-
514
- $tableWhere = implode(' AND ', $tableWhere);
515
-
516
- // Return query with offset and rows count
517
- return sprintf(
518
- 'SELECT t1.* FROM `%s` AS t1 JOIN (SELECT %s FROM `%s` WHERE %s ORDER BY %s LIMIT %d, %d) AS t2 USING (%s)',
519
- $tableName,
520
- $tableKeys,
521
- $tableName,
522
- $tableWhere,
523
- $tableKeys,
524
- $this->tableRowsOffset,
525
- self::MAX_SELECT_ROWS,
526
- $tableKeys
527
- );
528
- }
529
-
530
- /**
531
- * @param $tableName
532
- * @return string
533
- */
534
- private function getSelectQuery($tableName)
535
- {
536
- // Set table keys
537
- $tableKeys = 1;
538
-
539
- // Set table where clauses
540
- $tableWhere = [1];
541
- foreach ($this->getTableWhereClauses($tableName) as $clause) {
542
- $tableWhere[] = $clause;
543
- }
544
-
545
- $tableWhere = implode(' AND ', $tableWhere);
546
-
547
- // Return query with offset and rows count
548
- return sprintf(
549
- 'SELECT * FROM `%s` WHERE %s ORDER BY %s LIMIT %d, %d',
550
- $tableName,
551
- $tableWhere,
552
- $tableKeys,
553
- $this->tableRowsOffset,
554
- self::MAX_SELECT_ROWS
555
- );
556
- }
557
-
558
- /**
559
- * @return bool
560
- */
561
- private function isTableCorrupt()
562
- {
563
- return $this->client->errno() === 1194;
564
- }
565
-
566
- /**
567
- * @param $tableName
568
- * @param array $views
569
- * @return bool
570
- */
571
- private function isView($tableName, array $views)
572
- {
573
- return in_array($tableName, $views, true);
574
- }
575
-
576
- /**
577
- * @param mysqli_result|resource $result
578
- * @param string $tableName
579
- * @param array $tableColumns
580
- * @return mixed
581
- * @throws Exception
582
- */
583
- private function writeQueryInsert($result, $tableName, $tableColumns)
584
- {
585
- if ($numRows = $this->client->numRows($result)) {
586
-
587
- // Loop over table rows
588
- while ($row = $this->client->fetchAssoc($result)) {
589
-
590
- // Start transaction
591
- if ($this->tableRowsOffset % self::MAX_TRANSACTION_QUERIES === 0) {
592
- $this->file->fwrite("START TRANSACTION;\n");
593
- }
594
-
595
- $items = [];
596
- foreach ($row as $key => $value) {
597
- $items[] = $this->prepareTableValues($value, $tableColumns[strtolower($key)]);
598
- }
599
-
600
- // Set table values
601
- $tableValues = implode(',', $items);
602
-
603
- $insertQuery = "INSERT INTO `{$tableName}` VALUES ({$tableValues});\n";
604
-
605
- // Write INSERTS
606
- $this->file->fwrite($insertQuery);
607
-
608
- $this->tableRowsOffset++;
609
- $this->tableRowsExported++;
610
-
611
- // End of transaction
612
- if ($this->tableRowsOffset % self::MAX_TRANSACTION_QUERIES === 0) {
613
- $this->file->fwrite("COMMIT;\n");
614
- }
615
-
616
- }
617
- } else {
618
-
619
- // End of transaction
620
- if ($this->tableRowsOffset % self::MAX_TRANSACTION_QUERIES !== 0) {
621
- $this->file->fwrite("COMMIT;\n");
622
- }
623
-
624
- $this->tableIndex++;
625
- $this->tableRowsOffset = 0;
626
- }
627
- return $numRows;
628
- }
629
-
630
- private function getWpDb()
631
- {
632
- return $this->database->getWpdba()->getClient();
633
- }
634
-
635
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Database/DatabaseRestore.php DELETED
@@ -1,438 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Framework\Database;
4
-
5
- use WPStaging\Vendor\Psr\Log\LoggerInterface;
6
- use RuntimeException;
7
- use WPStaging\Framework\Adapter\Database;
8
- use WPStaging\Framework\Adapter\Database\InterfaceDatabaseClient;
9
- use WPStaging\Framework\Filesystem\File;
10
- use WPStaging\Core\Utils\Logger;
11
-
12
- class DatabaseRestore
13
- {
14
- /** @var File */
15
- private $file;
16
-
17
- /** @var callable */
18
- private $shouldStop;
19
-
20
- /** @var int */
21
- private $totalLines;
22
-
23
- /** @var int */
24
- private $currentLine;
25
-
26
- /** @var bool */
27
- private $isTransactionStarted;
28
-
29
- /** @var bool */
30
- private $isCommitted;
31
-
32
- /** @var InterfaceDatabaseClient */
33
- private $client;
34
-
35
- /** @var LoggerInterface */
36
- private $logger;
37
-
38
- /** @var SearchReplace|null */
39
- private $searchReplace;
40
-
41
- /** @var Database\WpDbAdapter */
42
- private $wpdb;
43
-
44
- public function __construct(Database $database)
45
- {
46
- $this->client = $database->getClient();
47
- $this->wpdb = $database->getWpdba();
48
- }
49
-
50
- /**
51
- * @param string $filePath Full file path
52
- * @return $this
53
- */
54
- public function setFile($filePath)
55
- {
56
- $this->file = new File($filePath);
57
- $this->totalLines = $this->file->totalLines();
58
- return $this;
59
- }
60
-
61
- public function seekLine($line)
62
- {
63
- if (!$this->file) {
64
- throw new RuntimeException('Restore file is not set');
65
- }
66
- $this->file->seek($line);
67
- $this->currentLine = $line;
68
- return $this;
69
- }
70
-
71
- public function restore()
72
- {
73
- if (!$this->file) {
74
- throw new RuntimeException('Restore file is not set');
75
- }
76
-
77
- $result = null;
78
- while(!$this->stopExecution()) {
79
- $result = $this->execute();
80
- }
81
-
82
- return $result;
83
- }
84
-
85
- public function stopExecution()
86
- {
87
- return $this->isShouldStop() || $this->file->eof();
88
- }
89
-
90
- public function setShouldStop(callable $shouldStop = null)
91
- {
92
- $this->shouldStop = $shouldStop;
93
- return $this;
94
- }
95
-
96
- public function getShouldStop()
97
- {
98
- return $this->shouldStop;
99
- }
100
-
101
- public function setLogger(LoggerInterface $logger)
102
- {
103
- $this->logger = $logger;
104
- return $this;
105
- }
106
-
107
- /**
108
- * @param SearchReplace|null $searchReplace
109
- * @return $this
110
- */
111
- public function setSearchReplace(SearchReplace $searchReplace = null)
112
- {
113
- $this->searchReplace = $searchReplace;
114
- return $this;
115
- }
116
-
117
- /**
118
- * @return int
119
- */
120
- public function getCurrentLine()
121
- {
122
- return $this->currentLine;
123
- }
124
-
125
- public function getTotalLines()
126
- {
127
- return $this->totalLines;
128
- }
129
-
130
- private function execute()
131
- {
132
- $query = $this->findExecutableQuery();
133
- if (!$query || $this->file->eof()) {
134
- return true;
135
- }
136
-
137
- $query = $this->searchReplace($query);
138
-
139
- $isTransactionQuery = stripos($query, 'start transaction;') !== false;
140
- $isCommitQuery = stripos($query, 'commit;') !== false;
141
-
142
- $this->exec( "SET SESSION sql_mode = ''" );
143
-
144
- $this->maybeStartTransaction($query);
145
-
146
- $query = $this->replaceTableCollations( $query );
147
-
148
- $result = $this->exec($query);
149
-
150
- // Replace table engines (Azure)
151
- if ( $this->client->errno() === 1030 ) {
152
- $query = $this->replaceTableEngines( $query );
153
- $result = $this->exec( $query );
154
- }
155
-
156
- // Replace table row format (MyISAM and InnoDB)
157
- if ( $this->client->errno() === 1071 || $this->client->errno() === 1709 ) {
158
- $query = $this->replaceTableRowFormat( $query );
159
- $result = $this->exec( $query );
160
- }
161
-
162
- // Several possible further errors
163
- if ( $this->client->errno() === 1226 ) {
164
- if ( stripos( $this->client->error(), 'max_queries_per_hour' ) !== false ) {
165
- throw new \Exception(
166
- 'Your server has reached the maximum allowed queries per hour set by your admin or hosting provider. ' .
167
- 'Please increase MySQL max_queries_per_hour limit. ' .
168
- '<a href="https://wp-staging.com/docs/mysql-database-error-codes/" target="_blank">Technical details</a>',
169
- 503
170
- );
171
- } elseif ( stripos( $this->client->error(), 'max_updates_per_hour' ) !== false ) {
172
- throw new \Exception(
173
- 'Your server has reached the maximum allowed updates per hour set by your admin or hosting provider. ' .
174
- 'Please increase MySQL max_updates_per_hour limit. ' .
175
- '<a href="https://wp-staging.com/docs/mysql-database-error-codes/" target="_blank">Technical details</a>',
176
- 503
177
- );
178
- } elseif ( stripos( $this->client->error(), 'max_connections_per_hour' ) !== false ) {
179
- throw new \Exception(
180
- 'Your server has reached the maximum allowed connections per hour set by your admin or hosting provider. ' .
181
- 'Please increase MySQL max_connections_per_hour limit. ' .
182
- '<a href="https://wp-staging.com/docs/mysql-database-error-codes/" target="_blank">Technical details</a>',
183
- 503
184
- );
185
- } elseif ( stripos( $this->client->error(), 'max_user_connections' ) !== false ) {
186
- throw new \Exception(
187
- 'Your server has reached the maximum allowed user connections set by your admin or hosting provider. ' .
188
- 'Please increase MySQL max_user_connections limit. ' .
189
- '<a href="https://wp-staging.com/docs/mysql-database-error-codes/" target="_blank">Technical details</a>',
190
- 503
191
- );
192
- }
193
- }
194
-
195
- if (!$result) {
196
- $this->log(sprintf(
197
- 'Failed to execute query on line: %d. Reason: %d - %s',
198
- $this->currentLine,
199
- $this->client->errno(),
200
- $this->client->error()
201
- ));
202
- }
203
- if ($isTransactionQuery && $result !== false) {
204
- $this->isTransactionStarted = true;
205
- }
206
-
207
- if ($isCommitQuery && $result !== false) {
208
- $this->isCommitted = true;
209
- $this->isTransactionStarted = false;
210
- }
211
-
212
- $this->maybeCommit();
213
-
214
- return $this->file->eof();
215
- }
216
-
217
- private function searchReplace($query)
218
- {
219
- if (!$this->searchReplace) {
220
- return $query;
221
- }
222
-
223
- // Replace only values area, not anything else
224
- // Matches (combination of either following);
225
- // insert into "wp_options" values(1,2,3,'something', "another")
226
- // INSERT INTO `other_table` VALUES (1,2,3, "Some Other stuff")
227
- // INSERT INTO `other_table`VALUES (1,2,3, "Some Other stuff")
228
- $patternValues = '#INSERT[ ]{0,}INTO[ ]{0,}[\`\"][a-zA-Z_\-0-9]+[\`\"][ ]{0,}VALUES[ ]{0,}\((.*?)\)#iU';
229
- if (!preg_match_all($patternValues, $query, $matches)) {
230
- return $query;
231
- }
232
-
233
- foreach ($matches[1] as $values) {
234
- $query = $this->searchReplaceValues($query, $values);
235
- }
236
-
237
- return $query;
238
- }
239
-
240
- private function searchReplaceValues($query, $values)
241
- {
242
- $replacedValues = $values;
243
- // No serialized strings, replace all values
244
- if (!preg_match_all('#\'([a0Os]:.*})\'#iu', $values, $serialized)) {
245
- $replacedValues = $this->searchReplace->replace($values);
246
- return str_replace($values, $replacedValues, $query);
247
- }
248
-
249
- // Replaced serialized string first
250
- foreach ($serialized[1] as $item) {
251
- $replacedItem = $this->searchReplace->replace($item);
252
- $replacedValues = str_replace($item, $replacedItem, $values);
253
- }
254
-
255
- // Replace other values
256
- $replacedValues = $this->searchReplace->replace($replacedValues);
257
- return str_replace($values, $replacedValues, $query);
258
- }
259
-
260
- private function findExecutableQuery()
261
- {
262
- while (!$this->file->eof() && !$this->isShouldStop()) {
263
- $line = $this->getLine();
264
- if ($this->isExecutableQuery($line)) {
265
- return $line;
266
- }
267
- $this->file->next();
268
- }
269
- return null;
270
- }
271
-
272
- private function getLine()
273
- {
274
- if ($this->file->eof()) {
275
- return null;
276
- }
277
-
278
- $line = trim($this->file->fgets());
279
- $this->currentLine++;
280
-
281
- return $line;
282
- }
283
-
284
- /**
285
- * Checks if given query / line is a valid, executable query
286
- * Valid SQL query for the moment means; only when it is not empty line or just a comment
287
- * @param string|null $query
288
- * @return string|null
289
- */
290
- private function isExecutableQuery($query = null)
291
- {
292
- if (!$query) {
293
- return false;
294
- }
295
-
296
- // Line starts with -- or # (to the end of the line) comments
297
- $first2Chars = substr($query, 0, 2);
298
- if ($first2Chars === '--' || strpos($query, '#') === 0) {
299
- return false;
300
- }
301
-
302
- // Line is not inline comments
303
- return !preg_match_all('#/\*(.*?)\*/#', $query, $matches)
304
- || (strlen(implode('', $matches[0])) < strlen($query))
305
- ;
306
- }
307
-
308
- private function isShouldStop()
309
- {
310
- return $this->shouldStop;
311
- }
312
-
313
- private function exec($query)
314
- {
315
- $result = $this->client->query($query, true);
316
- return $result !== false;
317
- }
318
-
319
- private function log($msg, $level = Logger::TYPE_WARNING)
320
- {
321
- if ($this->logger) {
322
- $this->logger->log($level, $msg);
323
- }
324
- }
325
-
326
- /**
327
- * Starts transaction if necessary
328
- * @param string $query
329
- */
330
- private function maybeStartTransaction($query)
331
- {
332
- if ($this->isTransactionStarted || strpos($query, 'INSERT INTO') !== 0) {
333
- return;
334
- }
335
-
336
- if ($this->exec('START TRANSACTION;')) {
337
- $this->isTransactionStarted = true;
338
- return;
339
- }
340
-
341
- $this->log(sprintf(
342
- 'Failed to start transaction for the query line; %s. Reason: %d - %s',
343
- $this->currentLine,
344
- $this->client->errno(),
345
- $this->client->error()
346
- ));
347
- }
348
-
349
- /**
350
- * Commits the transaction if necessary
351
- */
352
- private function maybeCommit()
353
- {
354
- if (!$this->isTransactionStarted || $this->isCommitted || !$this->isShouldStop()) {
355
- return;
356
- }
357
-
358
- if ($this->exec('COMMIT;')) {
359
- return;
360
- }
361
-
362
- $this->log(sprintf(
363
- 'Failed to commit for safe stop; %s. Reason: %d - %s',
364
- $this->currentLine,
365
- $this->client->errno(),
366
- $this->client->error()
367
- ));
368
- }
369
-
370
- /**
371
- * Replace table collations
372
- *
373
- * @param string $input SQL statement
374
- * @return string
375
- */
376
- private function replaceTableCollations($input ) {
377
- static $search = [];
378
- static $replace = [];
379
-
380
- // Replace table collations
381
- if ( empty( $search ) || empty( $replace ) ) {
382
- if ( ! $this->wpdb->getClient()->has_cap( 'utf8mb4_520' ) ) {
383
- if ( ! $this->wpdb->getClient()->has_cap( 'utf8mb4' ) ) {
384
- $search = [ 'utf8mb4_0900_ai_ci', 'utf8mb4_unicode_520_ci', 'utf8mb4' ];
385
- $replace = [ 'utf8_unicode_ci', 'utf8_unicode_ci', 'utf8' ];
386
- } else {
387
- $search = [ 'utf8mb4_0900_ai_ci', 'utf8mb4_unicode_520_ci' ];
388
- $replace = [ 'utf8mb4_unicode_ci', 'utf8mb4_unicode_ci' ];
389
- }
390
- } else {
391
- $search = [ 'utf8mb4_0900_ai_ci' ];
392
- $replace = [ 'utf8mb4_unicode_520_ci' ];
393
- }
394
- }
395
-
396
- return str_replace( $search, $replace, $input );
397
- }
398
-
399
- /**
400
- * Replace table engines
401
- *
402
- * @param string $input SQL statement
403
- * @return string
404
- */
405
- protected function replaceTableEngines( $input ) {
406
- // Set table replace engines
407
- $search = [
408
- 'ENGINE=MyISAM',
409
- 'ENGINE=Aria',
410
- ];
411
- $replace = [
412
- 'ENGINE=InnoDB',
413
- 'ENGINE=InnoDB',
414
- ];
415
-
416
- return str_ireplace( $search, $replace, $input );
417
- }
418
-
419
- /**
420
- * Replace table row format
421
- *
422
- * @param string $input SQL statement
423
- * @return string
424
- */
425
- protected function replaceTableRowFormat( $input ) {
426
- // Set table replace row format
427
- $search = [
428
- 'ENGINE=InnoDB',
429
- 'ENGINE=MyISAM',
430
- ];
431
- $replace = [
432
- 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC',
433
- 'ENGINE=MyISAM ROW_FORMAT=DYNAMIC',
434
- ];
435
-
436
- return str_ireplace( $search, $replace, $input );
437
- }
438
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Database/DbInfo.php CHANGED
@@ -75,4 +75,4 @@ class DbInfo extends WpDbInfo
75
  {
76
  return $this->error;
77
  }
78
- }
75
  {
76
  return $this->error;
77
  }
78
+ }
Framework/Database/LegacyDatabaseInfo.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Database;
4
+
5
+ class LegacyDatabaseInfo
6
+ {
7
+ const PREFIX_AUTOMATIC = 'wpsa';
8
+ const PREFIX_MANUAL = 'wpsm';
9
+ const PREFIX_TMP = 'wpstgtmp';
10
+
11
+ /**
12
+ * Returns whether a prefixed table name matches the name used for backup tables or not.
13
+ *
14
+ * @param string $prefixedTableName The prefixed table name.
15
+ *
16
+ * @return bool Whether a prefixed table name matches the name used for backup tables or not.
17
+ */
18
+ public static function isBackupTable($prefixedTableName)
19
+ {
20
+ $pattern = '#^(' . static::PREFIX_AUTOMATIC . '|' . static::PREFIX_MANUAL . ')\\d+_#';
21
+
22
+ return (bool)preg_match($pattern, $prefixedTableName);
23
+ }
24
+ }
Framework/Database/TableDto.php CHANGED
@@ -7,7 +7,6 @@ namespace WPStaging\Framework\Database;
7
 
8
  use DateTime;
9
  use WPStaging\Framework\Interfaces\HydrateableInterface;
10
- use WPStaging\Framework\Utils\Size;
11
 
12
  class TableDto implements HydrateableInterface
13
  {
@@ -33,10 +32,10 @@ class TableDto implements HydrateableInterface
33
  {
34
  $this->setName($data['Name']);
35
 
36
- $this->setRows(isset($data['Rows'])? (int) $data['Rows'] : 0);
37
- $this->setAutoIncrement(isset($data['Auto_increment'])? $data['Auto_increment'] : null);
38
  /** @noinspection PhpUnhandledExceptionInspection */
39
- $this->setCreatedAt(new DateTime(isset($data['Create_time'])? $data['Create_time'] : ''));
40
  if (isset($data['Update_time']) && $data['Update_time']) {
41
  /** @noinspection PhpUnhandledExceptionInspection */
42
  $this->setUpdatedAt(new DateTime($data['Update_time']));
@@ -151,6 +150,6 @@ class TableDto implements HydrateableInterface
151
  */
152
  public function getHumanReadableSize()
153
  {
154
- return (new Size)->toUnit($this->size);
155
  }
156
  }
7
 
8
  use DateTime;
9
  use WPStaging\Framework\Interfaces\HydrateableInterface;
 
10
 
11
  class TableDto implements HydrateableInterface
12
  {
32
  {
33
  $this->setName($data['Name']);
34
 
35
+ $this->setRows(isset($data['Rows']) ? (int) $data['Rows'] : 0);
36
+ $this->setAutoIncrement(isset($data['Auto_increment']) ? $data['Auto_increment'] : null);
37
  /** @noinspection PhpUnhandledExceptionInspection */
38
+ $this->setCreatedAt(new DateTime(isset($data['Create_time']) ? $data['Create_time'] : ''));
39
  if (isset($data['Update_time']) && $data['Update_time']) {
40
  /** @noinspection PhpUnhandledExceptionInspection */
41
  $this->setUpdatedAt(new DateTime($data['Update_time']));
150
  */
151
  public function getHumanReadableSize()
152
  {
153
+ return size_format($this->size);
154
  }
155
  }
Framework/Database/TableService.php CHANGED
@@ -7,6 +7,7 @@ namespace WPStaging\Framework\Database;
7
 
8
  use WPStaging\Framework\Adapter\Database;
9
  use WPStaging\Framework\Collection\Collection;
 
10
 
11
  class TableService
12
  {
@@ -16,12 +17,44 @@ class TableService
16
  /** @var Database\InterfaceDatabase|Database\InterfaceDatabaseClient|Database\MysqlAdapter|Database\MysqliAdapter|null */
17
  private $client;
18
 
 
 
 
 
 
 
19
  public function __construct(Database $database = null)
20
  {
21
- $this->database = $database ?: new Database;
22
  $this->client = $this->database->getClient();
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * Get all tables information starting with a specific prefix as collection
27
  * @param string|null $prefix
@@ -37,11 +70,25 @@ class TableService
37
 
38
  $collection = new Collection(TableDto::class);
39
  foreach ($tables as $table) {
40
- $collection->attach((new TableDto)->hydrate((array) $table));
41
  }
 
42
  return $collection;
43
  }
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * Get all base tables starting with a certain prefix
47
  * This does not include table views
@@ -100,6 +147,65 @@ class TableService
100
  return $this->getFilteredResult($views, $prefix);
101
  }
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  /**
104
  * Get all elements starting with a specific string from an array
105
  * @param array $data
7
 
8
  use WPStaging\Framework\Adapter\Database;
9
  use WPStaging\Framework\Collection\Collection;
10
+ use WPStaging\Framework\Utils\Strings;
11
 
12
  class TableService
13
  {
17
  /** @var Database\InterfaceDatabase|Database\InterfaceDatabaseClient|Database\MysqlAdapter|Database\MysqliAdapter|null */
18
  private $client;
19
 
20
+ /** @var callable|null */
21
+ private $shouldStop;
22
+
23
+ /** @var array */
24
+ private $errors = [];
25
+
26
  public function __construct(Database $database = null)
27
  {
28
+ $this->database = $database ?: new Database();
29
  $this->client = $this->database->getClient();
30
  }
31
 
32
+ /**
33
+ * @return array
34
+ */
35
+ public function getErrors()
36
+ {
37
+ return $this->errors;
38
+ }
39
+
40
+ /**
41
+ * @return callable|null
42
+ */
43
+ public function getShouldStop()
44
+ {
45
+ return $this->shouldStop;
46
+ }
47
+
48
+ /**
49
+ * @param callable|null $shouldStop
50
+ * @return self
51
+ */
52
+ public function setShouldStop(callable $shouldStop = null)
53
+ {
54
+ $this->shouldStop = $shouldStop;
55
+ return $this;
56
+ }
57
+
58
  /**
59
  * Get all tables information starting with a specific prefix as collection
60
  * @param string|null $prefix
70
 
71
  $collection = new Collection(TableDto::class);
72
  foreach ($tables as $table) {
73
+ $collection->attach((new TableDto())->hydrate((array) $table));
74
  }
75
+
76
  return $collection;
77
  }
78
 
79
+ /**
80
+ * Get names of all table only
81
+ * @param array $tables
82
+ *
83
+ * @return array
84
+ */
85
+ public function getTablesName($tables)
86
+ {
87
+ return (!is_array($tables)) ? [] : array_map(function ($table) {
88
+ return ($table->getName());
89
+ }, $tables);
90
+ }
91
+
92
  /**
93
  * Get all base tables starting with a certain prefix
94
  * This does not include table views
147
  return $this->getFilteredResult($views, $prefix);
148
  }
149
 
150
+ /**
151
+ * Delete all the tables or views that starts with $startsWith
152
+ * @param string $startsWith
153
+ * @param array $excludedTables
154
+ * @return bool
155
+ */
156
+ public function deleteTablesStartWith($startsWith = null, $excludedTables = [])
157
+ {
158
+ $prefix = $this->provideSqlPrefix($startsWith);
159
+ $tables = $this->findTableStatusStartsWith($prefix);
160
+ if ($tables === null) {
161
+ return true;
162
+ }
163
+
164
+ $tables = $this->getTablesName($tables->toArray());
165
+
166
+ $tablesToRemove = array_diff($tables, $excludedTables);
167
+ if ($tablesToRemove === []) {
168
+ return true;
169
+ }
170
+
171
+ if (!$this->deleteTables($tablesToRemove)) {
172
+ return false;
173
+ }
174
+
175
+ return true;
176
+ }
177
+
178
+ /**
179
+ * Delete Tables
180
+ * @param array $tables
181
+ * @param string $prefix
182
+ *
183
+ * @return bool
184
+ */
185
+ public function deleteTables($tables)
186
+ {
187
+ foreach ($tables as $table) {
188
+ // PROTECTION: Never delete any table that beginns with wp prefix of live site
189
+ // TODO: inject class Strings using DI
190
+ if (!$this->database->isExternal() && (new Strings())->startsWith($table, $this->database->getProductionPrefix())) {
191
+ $this->errors[] = sprintf(__("Fatal Error: Trying to delete table %s of main WP installation!", 'wp-staging'), $table);
192
+ return false;
193
+ }
194
+
195
+ $this->database->getWpdba()->exec("DROP TABLE {$table}");
196
+
197
+ if (!is_callable($this->shouldStop)) {
198
+ continue;
199
+ }
200
+
201
+ if (call_user_func($this->shouldStop)) {
202
+ return false;
203
+ }
204
+ }
205
+
206
+ return true;
207
+ }
208
+
209
  /**
210
  * Get all elements starting with a specific string from an array
211
  * @param array $data
Framework/Database/WpDbInfo.php CHANGED
@@ -76,4 +76,4 @@ class WpDbInfo implements iDbInfo
76
  'db_client_ver' => $this->getMySqlClientVersion()
77
  ];
78
  }
79
- }
76
  'db_client_ver' => $this->getMySqlClientVersion()
77
  ];
78
  }
79
+ }
Framework/Database/iDbInfo.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\Database;
5
 
6
-
7
  interface iDbInfo
8
  {
9
  public function getDbCollation();
@@ -11,4 +9,4 @@ interface iDbInfo
11
  public function getMySqlServerVersion();
12
  public function getMySqlClientVersion();
13
  public function toArray();
14
- }
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\Database;
4
 
 
5
  interface iDbInfo
6
  {
7
  public function getDbCollation();
9
  public function getMySqlServerVersion();
10
  public function getMySqlClientVersion();
11
  public function toArray();
12
+ }
Framework/Dto/AbstractDto.php DELETED
@@ -1,22 +0,0 @@
1
- <?php
2
-
3
- //TODO PHP7.x; declare(strict_types=1);
4
- //TODO PHP7.x; return types
5
-
6
- namespace WPStaging\Framework\Dto;
7
-
8
- use JsonSerializable;
9
- use WPStaging\Framework\Traits\ArrayableTrait;
10
-
11
- abstract class AbstractDto implements JsonSerializable
12
- {
13
- use ArrayableTrait;
14
-
15
- /**
16
- * @inheritDoc
17
- */
18
- public function jsonSerialize()
19
- {
20
- return $this->toArray();
21
- }
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Entity/AbstractEntity.php DELETED
@@ -1,42 +0,0 @@
1
- <?php
2
-
3
- //TODO PHP7.x; declare(strict_types=1);
4
-
5
- namespace WPStaging\Framework\Entity;
6
-
7
- use Serializable;
8
- use JsonSerializable;
9
- use WPStaging\Framework\Interfaces\ArrayableInterface;
10
- use WPStaging\Framework\Interfaces\HydrateableInterface;
11
- use WPStaging\Framework\Traits\ArrayableTrait;
12
- use WPStaging\Framework\Traits\HydrateTrait;
13
-
14
- abstract class AbstractEntity implements Serializable, JsonSerializable, ArrayableInterface, HydrateableInterface
15
- {
16
- use ArrayableTrait;
17
- use HydrateTrait;
18
-
19
- /**
20
- * @inheritDoc
21
- */
22
- public function serialize()
23
- {
24
- return serialize($this->toArray());
25
- }
26
-
27
- /**
28
- * @inheritDoc
29
- */
30
- public function unserialize($serialized)
31
- {
32
- $this->hydrate(unserialize($serialized));
33
- }
34
-
35
- /**
36
- * @inheritDoc
37
- */
38
- public function jsonSerialize()
39
- {
40
- return $this->toArray();
41
- }
42
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Entity/EntityException.php DELETED
@@ -1,12 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_types=1);
4
-
5
- namespace WPStaging\Framework\Entity;
6
-
7
- use RuntimeException;
8
-
9
- class EntityException extends RuntimeException
10
- {
11
-
12
- }
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Entity/IdentifyableEntityInterface.php DELETED
@@ -1,13 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_types=1);
4
-
5
- namespace WPStaging\Framework\Entity;
6
-
7
- interface IdentifyableEntityInterface
8
- {
9
- /**
10
- * @return string
11
- */
12
- public function getId();
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Filesystem/DirectoryScanner.php CHANGED
@@ -5,8 +5,17 @@
5
 
6
  namespace WPStaging\Framework\Filesystem;
7
 
 
 
8
  class DirectoryScanner
9
  {
 
 
 
 
 
 
 
10
  /**
11
  * @param string $directory
12
  * @param array $excludedDirectories
@@ -21,37 +30,25 @@ class DirectoryScanner
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
 
5
 
6
  namespace WPStaging\Framework\Filesystem;
7
 
8
+ use WPStaging\Vendor\Psr\Log\LoggerInterface;
9
+
10
  class DirectoryScanner
11
  {
12
+ private $logger;
13
+
14
+ public function __construct(LoggerInterface $logger)
15
+ {
16
+ $this->logger = $logger;
17
+ }
18
+
19
  /**
20
  * @param string $directory
21
  * @param array $excludedDirectories
30
  return [];
31
  }
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  $excludedDirectories = array_map(function ($item) {
34
+ return trailingslashit($item);
35
  }, $excludedDirectories);
36
 
 
 
 
 
 
 
37
  $dirs = [];
38
 
39
  /** @var \SplFileInfo $item */
40
  foreach ($it as $item) {
41
  if ($item->isDir() && $item->getFilename() != "." && $item->getFilename() != "..") {
42
+ if (in_array(trailingslashit($item->getRealPath()), $excludedDirectories)) {
43
+ // Early bail: Directory is ignored
44
+ $this->logger->info(sprintf(
45
+ __('%s: Ignored directory "%s" because it\'s in the ignored directories list.', 'wp-staging'),
46
+ __('Directory Scanner', 'wp-staging'),
47
+ $item->getRealPath()
48
+ ));
49
+ continue;
50
  }
51
+ $dirs[] = $item->getRealPath();
52
  }
53
  }
54
 
Framework/Filesystem/DirectoryScannerControl.php CHANGED
@@ -7,7 +7,7 @@
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;
@@ -65,7 +65,7 @@ class DirectoryScannerControl
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
  }
7
  namespace WPStaging\Framework\Filesystem;
8
 
9
  use RuntimeException;
10
+ use WPStaging\Pro\Backup\Task\Tasks\JobExport\DirectoryScannerTask;
11
  use WPStaging\Framework\Adapter\Directory;
12
  use WPStaging\Framework\Queue\FinishedQueueException;
13
  use WPStaging\Framework\Queue\Queue;
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
  }
Framework/Filesystem/File.php CHANGED
@@ -23,7 +23,7 @@ class File extends SplFileObject
23
  {
24
 
25
  if (!file_exists($fullPath)) {
26
- (new Filesystem)->mkdir(dirname($fullPath));
27
  }
28
 
29
  parent::__construct($fullPath, $openMode);
23
  {
24
 
25
  if (!file_exists($fullPath)) {
26
+ (new Filesystem())->mkdir(dirname($fullPath));
27
  }
28
 
29
  parent::__construct($fullPath, $openMode);
Framework/Filesystem/FileScanner.php CHANGED
@@ -7,25 +7,29 @@
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);
@@ -36,18 +40,25 @@ class FileScanner
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,
@@ -66,18 +77,63 @@ class FileScanner
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
  /*
7
  namespace WPStaging\Framework\Filesystem;
8
 
9
  use WPStaging\Framework\Adapter\Directory;
10
+ use WPStaging\Vendor\Psr\Log\LoggerInterface;
11
 
12
  class FileScanner
13
  {
14
  private $directory;
15
  private $filesystem;
16
+ private $logger;
17
 
18
+ public function __construct(Directory $directory, Filesystem $filesystem, LoggerInterface $logger)
19
  {
20
  $this->directory = $directory;
21
  $this->filesystem = $filesystem;
22
+ $this->logger = $logger;
23
  }
24
 
25
  /**
26
  * @param string $directory
27
  * @param bool $includeOtherFilesInWpContent
28
+ * @param array $excludedDirectories
29
  *
30
  * @return array
31
  */
32
+ public function scan($directory, $includeOtherFilesInWpContent, $excludedDirectories = [])
33
  {
34
  try {
35
  $it = new \DirectoryIterator($directory);
40
  /**
41
  * Allow user to exclude certain file extensions from being exported.
42
  */
43
+ $ignoreFileExtensions = (array)apply_filters('wpstg.export.site.ignore.file_extension', [
44
+ 'log',
45
+ ]);
46
 
47
  /**
48
  * Allow user to exclude files larger than given size from being exported.
49
  */
50
+ $ignoreFileBiggerThan = (int)apply_filters('wpstg.export.site.ignore.max_size_in_bytes', 200 * MB_IN_BYTES);
51
+
52
+ /**
53
+ * Allow user to exclude files with extension larger than given size from being exported.
54
+ */
55
+ $ignoreFileExtensionFilesBiggerThan = (array)apply_filters('wpstg.export.site.ignore.file_extension_max_size_in_bytes', [
56
+ 'zip' => 10 * MB_IN_BYTES,
57
+ ]);
58
 
59
  /*
60
  * If "Include Other Files in WP Content" is false, only the files inside
61
  * these folders will be added to the export.
 
 
62
  */
63
  $defaultWpContentFolders = [
64
  WP_PLUGIN_DIR,
77
  $item->getFilename() != "..";
78
 
79
  if ($shouldScan) {
80
+ if (in_array($item->getExtension(), $ignoreFileExtensions)) {
81
  // Early bail: File has an ignored extension
82
+ $this->logger->info(sprintf(
83
+ __('%s: Ignored file "%s" because the extension "%s" is ignored.', 'wp-staging'),
84
+ /*
85
+ * We can't use FileScanerTask::TASK_NAME due to an architectural problem.
86
+ * It's not a trully static method. It uses sprintf that relies on the state of the class,
87
+ * thus it can only be used inside the context of the task itself.
88
+ */
89
+ __('File Scanner', 'wp-staging'),
90
+ $item->getPathname(),
91
+ $item->getExtension()
92
+ ));
93
  continue;
94
  }
95
 
96
+ if ($item->getSize() > $ignoreFileBiggerThan) {
97
  // Early bail: File is larger than max allowed size.
98
+ $this->logger->info(sprintf(
99
+ __('%s: Ignored file "%s" because it exceeds the maximum file size for exporting (%s).', 'wp-staging'),
100
+ __('File Scanner', 'wp-staging'),
101
+ $item->getPathname(),
102
+ size_format($item->getSize())
103
+ ));
104
+ continue;
105
+ }
106
+
107
+ if (
108
+ array_key_exists($item->getExtension(), $ignoreFileExtensionFilesBiggerThan) &&
109
+ $item->getSize() > $ignoreFileExtensionFilesBiggerThan[$item->getExtension()]
110
+ ) {
111
+ // Early bail: File bigger than expected for given extension
112
+ $this->logger->info(sprintf(
113
+ __('%s: Ignored file "%s" because it exceeds the maximum file size for exporting (%s) for files with the "%s" extension.', 'wp-staging'),
114
+ __('File Scanner', 'wp-staging'),
115
+ $item->getPathname(),
116
+ size_format($item->getSize()),
117
+ $item->getExtension()
118
+ ));
119
  continue;
120
  }
121
 
122
  $path = $this->filesystem->safePath($item->getPathname());
123
 
124
+ foreach ($excludedDirectories as $excludedDirectory) {
125
+ if (strpos($path, trailingslashit($excludedDirectory)) === 0) {
126
+ // Early bail: File is inside an ignored folder
127
+ $this->logger->info(sprintf(
128
+ __('%s: Ignored file "%s" because it is inside an ignored directory (%s).', 'wp-staging'),
129
+ __('File Scanner', 'wp-staging'),
130
+ $item->getPathname(),
131
+ $excludedDirectory
132
+ ));
133
+ continue;
134
+ }
135
+ }
136
+
137
  if (!$includeOtherFilesInWpContent) {
138
  foreach ($defaultWpContentFolders as $defaultFolder) {
139
  /*
Framework/Filesystem/FileScannerControl.php CHANGED
@@ -11,6 +11,7 @@ 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
  {
@@ -52,17 +53,18 @@ class FileScannerControl
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) {
@@ -71,7 +73,7 @@ class FileScannerControl
71
 
72
  $path = ABSPATH . $path;
73
 
74
- return $this->scanner->scan($path, $includeOtherFilesInWpContent);
75
  }
76
 
77
  /**
11
  use WPStaging\Framework\Queue\Queue;
12
  use WPStaging\Framework\Queue\Storage\BufferedCacheStorage;
13
  use WPStaging\Framework\Utils\Cache\BufferedCache;
14
+ use WPStaging\Vendor\Psr\Log\LoggerInterface;
15
 
16
  class FileScannerControl
17
  {
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 bool $includeOtherFilesInWpContent
63
+ * @param array $excludedDirectories
64
  *
65
  * @return array
66
  */
67
+ public function scanCurrentPath($includeOtherFilesInWpContent, $excludedDirectories = [])
68
  {
69
  $path = $this->getPathFromQueue();
70
  if ($path === null) {
73
 
74
  $path = ABSPATH . $path;
75
 
76
+ return $this->scanner->scan($path, $includeOtherFilesInWpContent, $excludedDirectories);
77
  }
78
 
79
  /**
Framework/Filesystem/Filesystem.php CHANGED
@@ -3,10 +3,7 @@
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;
@@ -38,7 +35,7 @@ class Filesystem extends FilterableDirectoryIterator
38
  */
39
  public function findFiles($directory = null)
40
  {
41
- $finder = (new Finder)
42
  ->ignoreUnreadableDirs()
43
  ->files()
44
  ->in($this->findPath($directory))
@@ -88,7 +85,7 @@ class Filesystem extends FilterableDirectoryIterator
88
  */
89
  public function exists($fullPath)
90
  {
91
- return (new SymfonyFilesystem)->exists($fullPath);
92
  }
93
 
94
  /**
@@ -103,7 +100,7 @@ class Filesystem extends FilterableDirectoryIterator
103
  }
104
 
105
  // Get all files and dirs
106
- $finder = (new Finder)->ignoreUnreadableDirs()->in($this->getPath());
107
  if ($this->getNotPath()) {
108
  foreach ($this->getNotPath() as $notPath) {
109
  $finder->notPath($notPath);
@@ -154,7 +151,7 @@ class Filesystem extends FilterableDirectoryIterator
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
 
@@ -257,7 +254,7 @@ class Filesystem extends FilterableDirectoryIterator
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
  *
@@ -276,12 +273,12 @@ class Filesystem extends FilterableDirectoryIterator
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;
285
  // TODO perhaps use stream_set_chunk_size()?
286
  $fs->copy($source, $destination);
287
  return $fs->exists($destination);
@@ -297,7 +294,7 @@ class Filesystem extends FilterableDirectoryIterator
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
 
@@ -330,7 +327,7 @@ class Filesystem extends FilterableDirectoryIterator
330
  if (file_exists($destination)) {
331
  continue;
332
  }
333
-
334
  $result = false;
335
  // if empty dir
336
  if ($item->isDir()) {
@@ -402,7 +399,7 @@ class Filesystem extends FilterableDirectoryIterator
402
  return true;
403
  }
404
 
405
- $iterator = (new Finder)
406
  ->ignoreUnreadableDirs()
407
  ->ignoreDotFiles(false)
408
  ->in($this->findPath($path))
@@ -435,7 +432,7 @@ class Filesystem extends FilterableDirectoryIterator
435
  }
436
  }
437
 
438
- if (is_dir($path)){
439
  return rmdir($path);
440
  }
441
  }
@@ -450,12 +447,11 @@ class Filesystem extends FilterableDirectoryIterator
450
  * @param bool $deleteSelf making it optional to delete the parent itself, useful during file and dir exclusion
451
  * @return bool True if folder or file is deleted or file is empty ($deleteSelf = false); Return False if folder is not empty and execution should be continued
452
  */
453
- public function deleteNew($path, $deleteSelf = true)
454
  {
455
  // if $path is link or file, delete it and stop execution
456
- if(is_link($path) || is_file($path))
457
- {
458
- if (!unlink($path)){
459
  $this->log('Permission Error: Can not delete file ' . $path);
460
  return false;
461
  }
@@ -464,20 +460,20 @@ class Filesystem extends FilterableDirectoryIterator
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;
@@ -490,7 +486,12 @@ class Filesystem extends FilterableDirectoryIterator
490
  $iterator = $this->setIteratorMode(\RecursiveIteratorIterator::CHILD_FIRST)->get();
491
  } catch (FilesystemExceptions $e) {
492
  $this->log('Permission Error: Can not create recursive iterator for ' . $path);
493
- return false;
 
 
 
 
 
494
  }
495
 
496
  foreach ($iterator as $item) {
@@ -515,8 +516,8 @@ class Filesystem extends FilterableDirectoryIterator
515
  }
516
 
517
  // Delete the empty directory itself and finish execution
518
- if (is_dir($path)){
519
- if (!rmdir($path)){
520
  $this->log('Permission Error: Can not delete directory ' . $path);
521
  }
522
  }
@@ -606,7 +607,7 @@ class Filesystem extends FilterableDirectoryIterator
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
  }
@@ -629,7 +630,7 @@ class Filesystem extends FilterableDirectoryIterator
629
  */
630
  public function findPath($path)
631
  {
632
- return $path?: $this->path;
633
  }
634
 
635
  /**
@@ -673,7 +674,7 @@ class Filesystem extends FilterableDirectoryIterator
673
  */
674
  public function getNotPath()
675
  {
676
- return $this->notPath?: [];
677
  }
678
 
679
  /**
@@ -719,7 +720,7 @@ class Filesystem extends FilterableDirectoryIterator
719
  */
720
  public function getFileNames()
721
  {
722
- return $this->fileNames?: [];
723
  }
724
 
725
  /**
@@ -765,7 +766,7 @@ class Filesystem extends FilterableDirectoryIterator
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
  }
@@ -812,26 +813,27 @@ class Filesystem extends FilterableDirectoryIterator
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;
@@ -845,9 +847,8 @@ class Filesystem extends FilterableDirectoryIterator
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
  }
3
  namespace WPStaging\Framework\Filesystem;
4
 
5
  use WPStaging\Backend\Notices\Notices;
 
 
6
  use WPStaging\Core\WPStaging;
 
7
  use WPStaging\Vendor\Psr\Log\LoggerInterface;
8
  use RuntimeException;
9
  use WPStaging\Vendor\Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
35
  */
36
  public function findFiles($directory = null)
37
  {
38
+ $finder = (new Finder())
39
  ->ignoreUnreadableDirs()
40
  ->files()
41
  ->in($this->findPath($directory))
85
  */
86
  public function exists($fullPath)
87
  {
88
+ return (new SymfonyFilesystem())->exists($fullPath);
89
  }
90
 
91
  /**
100
  }
101
 
102
  // Get all files and dirs
103
+ $finder = (new Finder())->ignoreUnreadableDirs()->in($this->getPath());
104
  if ($this->getNotPath()) {
105
  foreach ($this->getNotPath() as $notPath) {
106
  $finder->notPath($notPath);
151
  public function move($source, $target)
152
  {
153
  // if $source is link or file, move it and stop execution
154
+ if (is_link($source) || is_file($source)) {
155
  return $this->renameDirect($source, $target);
156
  }
157
 
254
  $directoryListing = WPStaging::getInstance()->getContainer()->make(DirectoryListing::class);
255
  try {
256
  $directoryListing->preventDirectoryListing($path);
257
+ } catch (\Exception $e) {
258
  /**
259
  * Enqueue this error. All enqueued errors will be shown as a single notice.
260
  *
273
  * @param string $source
274
  * @param string $destination
275
  * @return boolean
276
+ *
277
  * @todo update this to allow copying big files
278
  */
279
  public function copy($source, $destination)
280
  {
281
+ $fs = new SymfonyFilesystem();
282
  // TODO perhaps use stream_set_chunk_size()?
283
  $fs->copy($source, $destination);
284
  return $fs->exists($destination);
294
  public function copyNew($source, $target)
295
  {
296
  // if $source is link or file, move it and stop execution
297
+ if (is_link($source) || is_file($source)) {
298
  return $this->copy($source, $target);
299
  }
300
 
327
  if (file_exists($destination)) {
328
  continue;
329
  }
330
+
331
  $result = false;
332
  // if empty dir
333
  if ($item->isDir()) {
399
  return true;
400
  }
401
 
402
+ $iterator = (new Finder())
403
  ->ignoreUnreadableDirs()
404
  ->ignoreDotFiles(false)
405
  ->in($this->findPath($path))
432
  }
433
  }
434
 
435
+ if (is_dir($path)) {
436
  return rmdir($path);
437
  }
438
  }
447
  * @param bool $deleteSelf making it optional to delete the parent itself, useful during file and dir exclusion
448
  * @return bool True if folder or file is deleted or file is empty ($deleteSelf = false); Return False if folder is not empty and execution should be continued
449
  */
450
+ public function deleteNew($path, $deleteSelf = true, $throw = false)
451
  {
452
  // if $path is link or file, delete it and stop execution
453
+ if (is_link($path) || is_file($path)) {
454
+ if (!unlink($path)) {
 
455
  $this->log('Permission Error: Can not delete file ' . $path);
456
  return false;
457
  }
460
  }
461
 
462
  // Assume it is already deleted
463
+ if (!is_dir($path)) {
464
  return true;
465
  }
466
 
467
  // delete the directory if it is empty and deleteSelf was true
468
  if (is_dir($path) && $this->isEmptyDir($path) && $deleteSelf) {
469
+ if (!@rmdir($path)) {
470
  $this->log('Permission Error: Can not delete directory ' . $path);
471
  return false;
472
  }
473
 
474
  return true;
475
  }
476
+
477
  // return since directory was empty and deleteSelf was false
478
  if (is_dir($path) && $this->isEmptyDir($path) && !$deleteSelf) {
479
  return true;
486
  $iterator = $this->setIteratorMode(\RecursiveIteratorIterator::CHILD_FIRST)->get();
487
  } catch (FilesystemExceptions $e) {
488
  $this->log('Permission Error: Can not create recursive iterator for ' . $path);
489
+ if ($throw) {
490
+ // This allows us to know that Filesystem FAILED and should not continue;
491
+ throw $e;
492
+ } else {
493
+ return false;
494
+ }
495
  }
496
 
497
  foreach ($iterator as $item) {
516
  }
517
 
518
  // Delete the empty directory itself and finish execution
519
+ if (is_dir($path)) {
520
+ if (!rmdir($path)) {
521
  $this->log('Permission Error: Can not delete directory ' . $path);
522
  }
523
  }
607
  // only delete the dir if empty
608
  // helpful when we exclude path(s) during delete
609
  if (is_dir($path) && $this->isEmptyDir($path)) {
610
+ if (!@rmdir($path)) {
611
  $this->log('Permission Error: Can not delete directory ' . $path);
612
  throw new RuntimeException('Permission Error: Can not delete directory ' . $path);
613
  }
630
  */
631
  public function findPath($path)
632
  {
633
+ return $path ?: $this->path;
634
  }
635
 
636
  /**
674
  */
675
  public function getNotPath()
676
  {
677
+ return $this->notPath ?: [];
678
  }
679
 
680
  /**
720
  */
721
  public function getFileNames()
722
  {
723
+ return $this->fileNames ?: [];
724
  }
725
 
726
  /**
766
  $this->log('Permission Error: Can not delete link ' . $perms);
767
 
768
  if ($item->isLink()) {
769
+ if (!unlink($path)) {
770
  $this->log('Permission Error: Can not delete link ' . $path);
771
  throw new RuntimeException('Permission Error: Can not delete link ' . $path);
772
  }
813
  * @param string $content Content of the file
814
  * @return boolean
815
  */
816
+ public function create($path, $content)
817
+ {
818
+ if (!@file_exists($path)) {
819
+ if (!@is_writable(dirname($path))) {
820
  return false;
821
  }
822
 
823
+ if (!@touch($path)) {
824
  return false;
825
  }
826
+ } elseif (!@is_writable($path)) {
827
  return false;
828
  }
829
 
830
  $written = false;
831
+ if (( $handle = @fopen($path, 'w') ) !== false) {
832
+ if (@fwrite($handle, $content) !== false) {
833
  $written = true;
834
  }
835
 
836
+ @fclose($handle);
837
  }
838
 
839
  return $written;
847
  * @param string $content Content of the file
848
  * @return boolean
849
  */
850
+ public function createWithMarkers($path, $marker, $content)
851
+ {
852
+ return @insert_with_markers($path, $marker, $content);
853
  }
 
 
854
  }
Framework/Filesystem/FilesystemExceptions.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\Filesystem;
5
 
6
-
7
  class FilesystemExceptions extends \Exception
8
  {
9
 
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\Filesystem;
4
 
 
5
  class FilesystemExceptions extends \Exception
6
  {
7
 
Framework/Filesystem/FilterableDirectoryIterator.php CHANGED
@@ -2,41 +2,38 @@
2
 
3
  namespace WPStaging\Framework\Filesystem;
4
 
5
- use WPStaging\Core\Iterators\RecursiveFilterExclude;
 
 
 
 
6
  use WPStaging\Framework\Filesystem\Filters\DirectoryDotFilter;
7
  use WPStaging\Framework\Filesystem\Filters\PathExcludeFilter;
8
- use WPStaging\Framework\Filesystem\Filters\ExtensionExcludeFilter;
9
- use WPStaging\Framework\Filesystem\Filters\RecursiveExtensionExcludeFilter;
10
 
11
  class FilterableDirectoryIterator
12
  {
13
  /**
14
  * The directory to iterate
15
- * @var string
16
  */
17
  private $directory;
18
 
19
  /**
20
  * list of files, directories or symlinks paths to be excluded
21
- * @var array
22
  */
23
  private $paths = [];
24
 
25
- /**
26
- * list of file extensions to be excluded
27
- * @var array
28
- */
29
- private $extensions = [];
30
-
31
  /**
32
  * Iterator recursively including sub folders or only items located in root of $this->$directory
33
- * @var bool
34
  */
35
  private $isRecursive = false;
36
 
37
  /**
38
  * skip dot in non recursive iterator depending on value
39
- * @var bool
40
  */
41
  private $isDotSkip = true;
42
 
@@ -53,7 +50,7 @@ class FilterableDirectoryIterator
53
  */
54
  public function __construct()
55
  {
56
- $this->iteratorMode = \RecursiveIteratorIterator::LEAVES_ONLY;
57
  }
58
 
59
  /**
@@ -138,34 +135,6 @@ class FilterableDirectoryIterator
138
  return $this;
139
  }
140
 
141
- /**
142
- * @return array
143
- */
144
- public function getExcludeExtensions()
145
- {
146
- return $this->extensions;
147
- }
148
-
149
- /**
150
- * @param array $extensions
151
- * @return self
152
- */
153
- public function setExcludeExtensions($extensions)
154
- {
155
- $this->extensions = $extensions;
156
- return $this;
157
- }
158
-
159
- /**
160
- * @param string $extension
161
- * @return self
162
- */
163
- public function addExcludeExtension($extension)
164
- {
165
- $this->extensions[] = $extension;
166
- return $this;
167
- }
168
-
169
  /**
170
  * @return int
171
  */
@@ -186,13 +155,13 @@ class FilterableDirectoryIterator
186
 
187
  /**
188
  * Get the final iterator for iterations
189
- * @return \RecursiveIteratorIterator|\IteratorIterator
190
  * @throws FilesystemExceptions
191
  */
192
- public function get()
193
  {
194
  if (!is_dir($this->directory)) {
195
- throw new FilesystemExceptions('Directory not found on the given path');
196
  }
197
 
198
  if ($this->isRecursive) {
@@ -201,56 +170,45 @@ class FilterableDirectoryIterator
201
 
202
  return $this->getIterator();
203
  }
204
-
205
- /**
206
  * Get recursive iterator for iterations
207
- * @return \RecursiveIteratorIterator
208
  */
209
  private function getRecursiveIterator()
210
- {
211
  // force Dot Skip to avoid unlimited loop iteration.
212
  $this->isDotSkip = true;
213
 
214
- $iterator = new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS);
215
-
216
  if (count($this->paths) !== 0) {
217
- $iterator = new RecursiveFilterExclude($iterator, $this->paths);
218
  }
219
 
220
- if (count($this->extensions) !== 0) {
221
- $iterator = new RecursiveExtensionExcludeFilter($iterator, $this->extensions);
222
- }
223
 
224
- $iterator = new \RecursiveIteratorIterator($iterator, $this->iteratorMode);
225
-
226
- return $iterator;
227
  }
228
 
229
  /**
230
  * Get non recursive iterator for iterations
231
- * @return \IteratorIterator
232
  */
233
- private function getIterator()
234
- {
235
- $iterator = new \DirectoryIterator($this->directory);
236
 
237
  if ($this->isDotSkip) {
238
  $iterator = new DirectoryDotFilter($iterator);
239
  }
240
-
241
  if (count($this->paths) !== 0) {
242
  $iterator = new PathExcludeFilter($iterator, $this->paths);
243
  }
244
 
245
- if (count($this->extensions) !== 0) {
246
- $iterator = new ExtensionExcludeFilter($iterator, $this->extensions);
247
- }
248
 
249
- $iterator = new \IteratorIterator($iterator);
250
-
251
- return $iterator;
252
  }
253
-
254
-
255
-
256
- }
2
 
3
  namespace WPStaging\Framework\Filesystem;
4
 
5
+ use DirectoryIterator;
6
+ use FilesystemIterator;
7
+ use IteratorIterator;
8
+ use RecursiveDirectoryIterator;
9
+ use RecursiveIteratorIterator;
10
  use WPStaging\Framework\Filesystem\Filters\DirectoryDotFilter;
11
  use WPStaging\Framework\Filesystem\Filters\PathExcludeFilter;
12
+ use WPStaging\Framework\Filesystem\Filters\RecursivePathExcludeFilter;
 
13
 
14
  class FilterableDirectoryIterator
15
  {
16
  /**
17
  * The directory to iterate
18
+ * @var string
19
  */
20
  private $directory;
21
 
22
  /**
23
  * list of files, directories or symlinks paths to be excluded
24
+ * @var array
25
  */
26
  private $paths = [];
27
 
 
 
 
 
 
 
28
  /**
29
  * Iterator recursively including sub folders or only items located in root of $this->$directory
30
+ * @var bool
31
  */
32
  private $isRecursive = false;
33
 
34
  /**
35
  * skip dot in non recursive iterator depending on value
36
+ * @var bool
37
  */
38
  private $isDotSkip = true;
39
 
50
  */
51
  public function __construct()
52
  {
53
+ $this->iteratorMode = RecursiveIteratorIterator::LEAVES_ONLY;
54
  }
55
 
56
  /**
135
  return $this;
136
  }
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  /**
139
  * @return int
140
  */
155
 
156
  /**
157
  * Get the final iterator for iterations
158
+ * @return RecursiveIteratorIterator|IteratorIterator
159
  * @throws FilesystemExceptions
160
  */
161
+ public function get()
162
  {
163
  if (!is_dir($this->directory)) {
164
+ throw new FilesystemExceptions(sprintf(__('Directory not found on the given path: %s.', 'wp-staging'), $this->directory));
165
  }
166
 
167
  if ($this->isRecursive) {
170
 
171
  return $this->getIterator();
172
  }
173
+
174
+ /**
175
  * Get recursive iterator for iterations
176
+ * @return RecursiveIteratorIterator
177
  */
178
  private function getRecursiveIterator()
179
+ {
180
  // force Dot Skip to avoid unlimited loop iteration.
181
  $this->isDotSkip = true;
182
 
183
+ $iterator = new RecursiveDirectoryIterator($this->directory, FilesystemIterator::SKIP_DOTS);
184
+
185
  if (count($this->paths) !== 0) {
186
+ $iterator = new RecursivePathExcludeFilter($iterator, $this->paths);
187
  }
188
 
189
+ $iterator = new RecursiveIteratorIterator($iterator, $this->iteratorMode);
 
 
190
 
191
+ return $iterator;
 
 
192
  }
193
 
194
  /**
195
  * Get non recursive iterator for iterations
196
+ * @return IteratorIterator
197
  */
198
+ private function getIterator()
199
+ {
200
+ $iterator = new DirectoryIterator($this->directory);
201
 
202
  if ($this->isDotSkip) {
203
  $iterator = new DirectoryDotFilter($iterator);
204
  }
205
+
206
  if (count($this->paths) !== 0) {
207
  $iterator = new PathExcludeFilter($iterator, $this->paths);
208
  }
209
 
210
+ $iterator = new IteratorIterator($iterator);
 
 
211
 
212
+ return $iterator;
 
 
213
  }
214
+ }
 
 
 
Framework/Filesystem/Filters/DirectoryDotFilter.php CHANGED
@@ -13,9 +13,9 @@ class DirectoryDotFilter extends \FilterIterator
13
  {
14
  $current = $this->getInnerIterator()->current();
15
 
16
- if ($current->isDot()) {
17
- return false;
18
- }
19
 
20
  return true;
21
  }
13
  {
14
  $current = $this->getInnerIterator()->current();
15
 
16
+ if ($current->isDot()) {
17
+ return false;
18
+ }
19
 
20
  return true;
21
  }
Framework/Filesystem/Filters/ExtensionExcludeFilter.php DELETED
@@ -1,25 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Framework\Filesystem\Filters;
4
-
5
- class ExtensionExcludeFilter extends \FilterIterator
6
- {
7
- protected $exclude = [];
8
-
9
- public function __construct(\Iterator $iterator, $exclude = [])
10
- {
11
- parent::__construct($iterator);
12
- $this->exclude = $exclude;
13
- }
14
-
15
- public function accept()
16
- {
17
- $current = $this->getInnerIterator()->current();
18
-
19
- if ($current->isDir()) {
20
- return true;
21
- }
22
-
23
- return !in_array($current->getExtension(), $this->exclude);
24
- }
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Filesystem/Filters/PathExcludeFilter.php CHANGED
@@ -2,33 +2,27 @@
2
 
3
  namespace WPStaging\Framework\Filesystem\Filters;
4
 
5
- class PathExcludeFilter extends \FilterIterator
 
 
 
 
6
  {
 
 
7
  protected $exclude = [];
8
 
9
- public function __construct(\Iterator $iterator, $exclude = [])
10
  {
11
  parent::__construct($iterator);
12
  $this->exclude = $exclude;
 
13
  }
14
 
15
  public function accept()
16
  {
17
- $path = $this->getInnerIterator()->getPathname();
18
-
19
- // new line character on linux
20
- if (strpos($path, "\n") !== false) {
21
- return false;
22
- }
23
- // new line character on Windows
24
- if (strpos($path, "\r") !== false) {
25
- return false;
26
- }
27
-
28
- if (in_array(wpstg_replace_windows_directory_separator($path), $this->exclude)) {
29
- return false;
30
- }
31
-
32
- return true;
33
  }
34
  }
2
 
3
  namespace WPStaging\Framework\Filesystem\Filters;
4
 
5
+ use FilterIterator;
6
+ use Iterator;
7
+ use WPStaging\Framework\Traits\ExcludeFilterTrait;
8
+
9
+ class PathExcludeFilter extends FilterIterator
10
  {
11
+ use ExcludeFilterTrait;
12
+
13
  protected $exclude = [];
14
 
15
+ public function __construct(Iterator $iterator, $exclude = [])
16
  {
17
  parent::__construct($iterator);
18
  $this->exclude = $exclude;
19
+ $this->categorizeExcludes($exclude);
20
  }
21
 
22
  public function accept()
23
  {
24
+ // Get the current SplFileInfo object
25
+ $fileInfo = $this->getInnerIterator()->current();
26
+ return !$this->isExcluded($fileInfo);
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
28
  }
Framework/Filesystem/Filters/RecursiveExtensionExcludeFilter.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Framework\Filesystem\Filters;
4
-
5
- use RecursiveFilterIterator;
6
- use RecursiveIterator;
7
-
8
- class RecursiveExtensionExcludeFilter extends RecursiveFilterIterator
9
- {
10
- protected $exclude = [];
11
-
12
- public function __construct(RecursiveIterator $iterator, $exclude = [])
13
- {
14
- parent::__construct($iterator);
15
- $this->exclude = $exclude;
16
- }
17
-
18
- public function accept()
19
- {
20
- $current = $this->getInnerIterator()->current();
21
-
22
- if ($current->isDir()) {
23
- return true;
24
- }
25
-
26
- return !in_array($current->getExtension(), $this->exclude);
27
- }
28
-
29
- public function getChildren()
30
- {
31
- return new self($this->getInnerIterator()->getChildren(), $this->exclude);
32
- }
33
-
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Filesystem/Filters/RecursivePathExcludeFilter.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Filesystem\Filters;
4
+
5
+ use RecursiveFilterIterator;
6
+ use RecursiveDirectoryIterator;
7
+ use WPStaging\Framework\Traits\ExcludeFilterTrait;
8
+
9
+ class RecursivePathExcludeFilter extends RecursiveFilterIterator
10
+ {
11
+ use ExcludeFilterTrait;
12
+
13
+ protected $excludePaths = [];
14
+
15
+ public function __construct(RecursiveDirectoryIterator $iterator, $excludePaths = [])
16
+ {
17
+ parent::__construct($iterator);
18
+ $this->excludePaths = $excludePaths;
19
+ $this->categorizeExcludes($excludePaths);
20
+ }
21
+
22
+ public function accept()
23
+ {
24
+ // Get the current SplFileInfo object
25
+ $fileInfo = $this->getInnerIterator()->current();
26
+ return !$this->isExcluded($fileInfo);
27
+ }
28
+
29
+ public function getChildren()
30
+ {
31
+ return new self($this->getInnerIterator()->getChildren(), $this->excludePaths);
32
+ }
33
+ }
Framework/Filesystem/Permissions.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
-
4
  namespace WPStaging\Framework\Filesystem;
5
 
6
-
7
  class Permissions
8
  {
9
  /**
@@ -30,6 +28,4 @@ class Permissions
30
 
31
  return 0644;
32
  }
33
-
34
-
35
- }
1
  <?php
2
 
 
3
  namespace WPStaging\Framework\Filesystem;
4
 
 
5
  class Permissions
6
  {
7
  /**
28
 
29
  return 0644;
30
  }
31
+ }
 
 
Framework/Filesystem/WpUploadsFolderSymlinker.php CHANGED
@@ -10,7 +10,7 @@ use WPStaging\Framework\Utils\WpDefaultDirectories;
10
  * to staging site
11
  * Symlink will only work if staging site is on same hosting as production site
12
  */
13
- class WpUploadsFolderSymlinker
14
  {
15
  /**
16
  * @var string
@@ -35,7 +35,7 @@ class WpUploadsFolderSymlinker
35
  /**
36
  * @param string $stagingWpPath
37
  */
38
- public function __construct($stagingWpPath)
39
  {
40
  $this->stagingWpPath = $stagingWpPath;
41
  // todo inject using dependency injection if possible
@@ -58,9 +58,9 @@ class WpUploadsFolderSymlinker
58
  return false;
59
  }
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;
@@ -74,7 +74,7 @@ class WpUploadsFolderSymlinker
74
  /**
75
  * Return error
76
  */
77
- public function getError()
78
  {
79
  return $this->error;
80
  }
10
  * to staging site
11
  * Symlink will only work if staging site is on same hosting as production site
12
  */
13
+ class WpUploadsFolderSymlinker
14
  {
15
  /**
16
  * @var string
35
  /**
36
  * @param string $stagingWpPath
37
  */
38
+ public function __construct($stagingWpPath)
39
  {
40
  $this->stagingWpPath = $stagingWpPath;
41
  // todo inject using dependency injection if possible
58
  return false;
59
  }
60
 
61
+ $uploadPath = $this->wpDirectories->getUploadsPath();
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;
74
  /**
75
  * Return error
76
  */
77
+ public function getError()
78
  {
79
  return $this->error;
80
  }
Framework/Interfaces/TransientInterface.php CHANGED
@@ -19,8 +19,8 @@ interface TransientInterface
19
  */
20
  public function setTransient();
21
 
22
- /**
23
- * @return bool
24
  */
25
  public function getTransient();
26
 
@@ -28,4 +28,4 @@ interface TransientInterface
28
  * Delete the transient
29
  */
30
  public function deleteTransient();
31
- }
19
  */
20
  public function setTransient();
21
 
22
+ /**
23
+ * @return bool
24
  */
25
  public function getTransient();
26
 
28
  * Delete the transient
29
  */
30
  public function deleteTransient();
31
+ }
Framework/Mails/Report/Report.php CHANGED
@@ -9,14 +9,14 @@ class Report
9
  /**
10
  * WP Staging Support Email
11
  *
12
- * @var string
13
  */
14
  const WPSTG_SUPPORT_EMAIL = "support@wp-staging.com";
15
 
16
  /**
17
  * Email Subject for Issue Email
18
  *
19
- * @var string
20
  */
21
  const EMAIL_SUBJECT = "Report Issue!";
22
 
@@ -49,14 +49,14 @@ class Report
49
  } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
50
  $errors[] = __('Email address is not valid.', 'wp-staging');
51
  }
52
-
53
  if (empty($message)) {
54
  $errors[] = __('Please enter your issue.', 'wp-staging');
55
  }
56
-
57
  if (empty($terms)) {
58
  $errors[] = __('Please accept our privacy policy.', 'wp-staging');
59
- }
60
 
61
  if (count($errors) !== 0) {
62
  return $errors;
@@ -66,7 +66,7 @@ class Report
66
  if ($provider) {
67
  $message .= "\n\n'Hosting provider: " . $provider;
68
  }
69
-
70
  if (!empty($syslog)) {
71
  $message .= "\n\n'" . $this->getSyslog();
72
  $debugLogFile = WP_CONTENT_DIR . '/debug.log';
@@ -76,7 +76,7 @@ class Report
76
  }
77
 
78
  $transient = new ReportSubmitTransient();
79
- if (!$forceSend && $transient->getTransient() ) {
80
  // to show alert using js
81
  $errors[] = [
82
  "status" => 'already_submitted',
@@ -121,9 +121,8 @@ class Report
121
 
122
  if ($success) {
123
  return true;
124
- }
125
 
126
  return false;
127
  }
128
-
129
  }
9
  /**
10
  * WP Staging Support Email
11
  *
12
+ * @var string
13
  */
14
  const WPSTG_SUPPORT_EMAIL = "support@wp-staging.com";
15
 
16
  /**
17
  * Email Subject for Issue Email
18
  *
19
+ * @var string
20
  */
21
  const EMAIL_SUBJECT = "Report Issue!";
22
 
49
  } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
50
  $errors[] = __('Email address is not valid.', 'wp-staging');
51
  }
52
+
53
  if (empty($message)) {
54
  $errors[] = __('Please enter your issue.', 'wp-staging');
55
  }
56
+
57
  if (empty($terms)) {
58
  $errors[] = __('Please accept our privacy policy.', 'wp-staging');
59
+ }
60
 
61
  if (count($errors) !== 0) {
62
  return $errors;
66
  if ($provider) {
67
  $message .= "\n\n'Hosting provider: " . $provider;
68
  }
69
+
70
  if (!empty($syslog)) {
71
  $message .= "\n\n'" . $this->getSyslog();
72
  $debugLogFile = WP_CONTENT_DIR . '/debug.log';
76
  }
77
 
78
  $transient = new ReportSubmitTransient();
79
+ if (!$forceSend && $transient->getTransient()) {
80
  // to show alert using js
81
  $errors[] = [
82
  "status" => 'already_submitted',
121
 
122
  if ($success) {
123
  return true;
124
+ }
125
 
126
  return false;
127
  }
 
128
  }
Framework/Mails/Report/ReportSubmitTransient.php CHANGED
@@ -18,7 +18,7 @@ class ReportSubmitTransient implements TransientInterface
18
  * Set expiry time to 3600 seconds = 1 hour
19
  */
20
  const EXPIRY_TIME_IN_SEC = 3600;
21
-
22
  /**
23
  * @return string
24
  */
@@ -34,4 +34,4 @@ class ReportSubmitTransient implements TransientInterface
34
  {
35
  return self::EXPIRY_TIME_IN_SEC;
36
  }
37
- }
18
  * Set expiry time to 3600 seconds = 1 hour
19
  */
20
  const EXPIRY_TIME_IN_SEC = 3600;
21
+
22
  /**
23
  * @return string
24
  */
34
  {
35
  return self::EXPIRY_TIME_IN_SEC;
36
  }
37
+ }
Framework/Queue/FinishedQueueException.php CHANGED
@@ -5,4 +5,5 @@ namespace WPStaging\Framework\Queue;
5
  use RuntimeException;
6
 
7
  class FinishedQueueException extends RuntimeException
8
- {}
 
5
  use RuntimeException;
6
 
7
  class FinishedQueueException extends RuntimeException
8
+ {
9
+ }
Framework/Queue/Storage/ArrayStorage.php CHANGED
@@ -12,6 +12,11 @@ class ArrayStorage implements StorageInterface
12
  /** @var array|null */
13
  private $items;
14
 
 
 
 
 
 
15
  /**
16
  * This does nothing due to nature of ArrayStorage
17
  * @inheritDoc
12
  /** @var array|null */
13
  private $items;
14
 
15
+ public function commit()
16
+ {
17
+ error_log('ArrayStorage does not implement commit.');
18
+ }
19
+
20
  /**
21
  * This does nothing due to nature of ArrayStorage
22
  * @inheritDoc
Framework/Queue/Storage/BufferedCacheStorage.php CHANGED
@@ -27,6 +27,8 @@ class BufferedCacheStorage implements StorageInterface
27
  /** @var bool */
28
  private $isUsePrefix;
29
 
 
 
30
  public function __construct(BufferedCache $cache)
31
  {
32
  $this->isUsePrefix = true;
@@ -35,6 +37,13 @@ class BufferedCacheStorage implements StorageInterface
35
  }
36
 
37
  public function __destruct()
 
 
 
 
 
 
 
38
  {
39
  if (!$this->key) {
40
  return;
@@ -46,7 +55,7 @@ class BufferedCacheStorage implements StorageInterface
46
  }
47
 
48
  if ($this->size() === 0) {
49
- $this->cache->delete();
50
  }
51
  }
52
 
27
  /** @var bool */
28
  private $isUsePrefix;
29
 
30
+ private $commited = false;
31
+
32
  public function __construct(BufferedCache $cache)
33
  {
34
  $this->isUsePrefix = true;
37
  }
38
 
39
  public function __destruct()
40
+ {
41
+ if (!$this->commited) {
42
+ $this->commit();
43
+ }
44
+ }
45
+
46
+ public function commit()
47
  {
48
  if (!$this->key) {
49
  return;
55
  }
56
 
57
  if ($this->size() === 0) {
58
+ #$this->cache->delete();
59
  }
60
  }
61
 
Framework/Queue/Storage/CacheStorage.php CHANGED
@@ -19,19 +19,31 @@ class CacheStorage implements StorageInterface
19
  /** @var array|null */
20
  private $items;
21
 
 
 
22
  public function __construct(Cache $cache)
23
  {
24
- $this->cache = clone $cache;
25
  }
26
 
27
  public function __destruct()
28
  {
 
 
 
 
 
 
 
 
 
29
  if (!$this->key) {
30
  return;
31
  }
32
 
33
  if (!$this->items) {
34
  $this->cache->delete();
 
35
  return;
36
  }
37
 
@@ -54,7 +66,7 @@ class CacheStorage implements StorageInterface
54
  */
55
  public function count()
56
  {
57
- return count((array) $this->items);
58
  }
59
 
60
  /**
19
  /** @var array|null */
20
  private $items;
21
 
22
+ private $commited = false;
23
+
24
  public function __construct(Cache $cache)
25
  {
26
+ $this->cache = $cache;
27
  }
28
 
29
  public function __destruct()
30
  {
31
+ if (!$this->commited) {
32
+ $this->commit();
33
+ }
34
+ }
35
+
36
+ public function commit()
37
+ {
38
+ $this->commited = true;
39
+
40
  if (!$this->key) {
41
  return;
42
  }
43
 
44
  if (!$this->items) {
45
  $this->cache->delete();
46
+
47
  return;
48
  }
49
 
66
  */
67
  public function count()
68
  {
69
+ return count((array)$this->items);
70
  }
71
 
72
  /**
Framework/Queue/Storage/StorageInterface.php CHANGED
@@ -18,6 +18,13 @@ interface StorageInterface
18
  */
19
  public function setKey($key);
20
 
 
 
 
 
 
 
 
21
  /**
22
  * Count all items in the given queue
23
  * @return int
18
  */
19
  public function setKey($key);
20
 
21
+ /**
22
+ * Persist the data
23
+ *
24
+ * @return void
25
+ */
26
+ public function commit();
27
+
28
  /**
29
  * Count all items in the given queue
30
  * @return int
Framework/Security/AccessToken.php CHANGED
@@ -39,7 +39,7 @@ class AccessToken
39
  public function generateNewToken()
40
  {
41
  // Early bail: Not enough privilege to generate a token. Todo: Remove "new" once we have DI
42
- if ( ! current_user_can((new Capabilities)->manageWPSTG())) {
43
  return false;
44
  }
45
 
@@ -64,7 +64,7 @@ class AccessToken
64
  public function getToken()
65
  {
66
  // Early bail: Not enough privilege to get a token. Todo: Remove "new" once we have DI
67
- if ( ! current_user_can((new Capabilities)->manageWPSTG())) {
68
  return false;
69
  }
70
 
39
  public function generateNewToken()
40
  {
41
  // Early bail: Not enough privilege to generate a token. Todo: Remove "new" once we have DI
42
+ if (! current_user_can((new Capabilities())->manageWPSTG())) {
43
  return false;
44
  }
45
 
64
  public function getToken()
65
  {
66
  // Early bail: Not enough privilege to get a token. Todo: Remove "new" once we have DI
67
+ if (! current_user_can((new Capabilities())->manageWPSTG())) {
68
  return false;
69
  }
70
 
Framework/Security/Auth.php CHANGED
@@ -52,16 +52,15 @@ class Auth
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
- }
52
  * @param string $nonce
53
  * @return bool
54
  */
55
+ public function isAuthenticatedRequest($nonce = Nonce::WPSTG_NONCE)
56
  {
57
  if (
58
  $this->nonce->requestHasValidNonce($nonce) &&
59
  current_user_can($this->capabilities->manageWPSTG())
 
60
  ) {
61
  return true;
62
  }
63
 
64
  return $this->accessToken->requestHasValidToken();
65
  }
66
+ }
Framework/SiteInfo.php CHANGED
@@ -2,6 +2,8 @@
2
 
3
  namespace WPStaging\Framework;
4
 
 
 
5
  /**
6
  * Class SiteInfo
7
  *
@@ -11,24 +13,73 @@ namespace WPStaging\Framework;
11
  */
12
  class SiteInfo
13
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  /**
15
  * @return bool True if is staging site. False otherwise.
16
  */
17
  public function isStaging()
18
  {
19
- if (file_exists(ABSPATH . '.wp-staging-cloneable')) {
20
- return false;
21
  }
22
 
23
- if (get_option("wpstg_is_staging_site") === "true") {
 
 
 
 
 
 
 
 
 
 
 
24
  return true;
25
  }
26
 
27
- if (file_exists(ABSPATH . '.wp-staging')) {
 
28
  return true;
29
  }
30
 
31
- return false;
 
32
  }
33
 
34
  /**
@@ -47,4 +98,53 @@ class SiteInfo
47
 
48
  return $home !== $siteurl;
49
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
2
 
3
  namespace WPStaging\Framework;
4
 
5
+ use WPStaging\Framework\Staging\CloneOptions;
6
+
7
  /**
8
  * Class SiteInfo
9
  *
13
  */
14
  class SiteInfo
15
  {
16
+ /**
17
+ * The key used in DB to store is cloneable feature in clone options
18
+ * @var string
19
+ */
20
+ const IS_CLONEABLE_KEY = 'isCloneable';
21
+
22
+ /**
23
+ * The file which make staging site cloneable
24
+ * This way is depreciated
25
+ * @var string
26
+ */
27
+ const CLONEABLE_FILE = '.wp-staging-cloneable';
28
+
29
+ /**
30
+ * The key used in DB to store whether site is staging or not
31
+ * @var string
32
+ */
33
+ const IS_STAGING_KEY = 'wpstg_is_staging_site';
34
+
35
+ /**
36
+ * The file which makes site a staging site
37
+ * @var string
38
+ */
39
+ const STAGING_FILE = '.wp-staging';
40
+
41
+ /**
42
+ * @var CloneOptions
43
+ */
44
+ private $cloneOptions;
45
+
46
+ public function __construct()
47
+ {
48
+ // TODO: inject using DI
49
+ $this->cloneOptions = new CloneOptions();
50
+ }
51
+
52
  /**
53
  * @return bool True if is staging site. False otherwise.
54
  */
55
  public function isStaging()
56
  {
57
+ if (get_option(self::IS_STAGING_KEY) === "true") {
58
+ return true;
59
  }
60
 
61
+ return file_exists(ABSPATH . self::STAGING_FILE);
62
+ }
63
+
64
+ /**
65
+ * @return bool True if is staging site. False otherwise.
66
+ *
67
+ * @todo update with WPStaging/Framework/Staging/CloneOption once PR #717 is merged
68
+ */
69
+ public function isCloneable()
70
+ {
71
+ // Site should be cloneable if not staging i.e. production site
72
+ if (!$this->isStaging()) {
73
  return true;
74
  }
75
 
76
+ // Old condition to check if staging site is cloneable
77
+ if (file_exists(ABSPATH . self::CLONEABLE_FILE)) {
78
  return true;
79
  }
80
 
81
+ // New condition for checking whether staging is cloneable or not
82
+ return $this->cloneOptions->get(self::IS_CLONEABLE_KEY, false);
83
  }
84
 
85
  /**
98
 
99
  return $home !== $siteurl;
100
  }
101
+
102
+ /**
103
+ * Enable the cloning for current staging site.
104
+ *
105
+ * @return bool
106
+ */
107
+ public function enableStagingSiteCloning()
108
+ {
109
+ // Early Bail: if site is not staging
110
+ if (!$this->isStaging()) {
111
+ return false;
112
+ }
113
+
114
+ // Early Bail: if cloning already enabled
115
+ if ($this->isCloneable()) {
116
+ return true;
117
+ }
118
+
119
+ return $this->cloneOptions->set(self::IS_CLONEABLE_KEY, true);
120
+ }
121
+
122
+ /**
123
+ * Enable the cloning for current staging site.
124
+ *
125
+ * @return bool
126
+ */
127
+ public function disableStagingSiteCloning()
128
+ {
129
+ // Early Bail: if site is not staging
130
+ if (!$this->isStaging()) {
131
+ return false;
132
+ }
133
+
134
+ // Early Bail: if cloning already disabled
135
+ if (!$this->isCloneable()) {
136
+ return true;
137
+ }
138
+
139
+ // First try disabling if cloneable feature exist due to old way.
140
+ $cloneableFile = trailingslashit(ABSPATH) . self::CLONEABLE_FILE;
141
+ if (file_exists($cloneableFile) && !unlink($cloneableFile)) {
142
+ // Error if files exists but unable to unlink
143
+ return false;
144
+ }
145
+
146
+ // Staging site may have been made cloneable through both ways
147
+ // So now try disabling through new way
148
+ return (!file_exists($cloneableFile) && $this->cloneOptions->delete(self::IS_CLONEABLE_KEY));
149
+ }
150
  }
Framework/Staging/CloneOptions.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Staging;
4
+
5
+ use stdClass;
6
+ use WPStaging\Framework\SiteInfo;
7
+
8
+ /**
9
+ * Class CloneOptions
10
+ *
11
+ * This is used to manage settings on the staging site
12
+ *
13
+ * @package WPStaging\Framework\Staging
14
+ */
15
+ class CloneOptions
16
+ {
17
+ /**
18
+ * The option_name that is stored in the database to check first run is executed or not
19
+ */
20
+ const WPSTG_CLONE_SETTINGS_KEY = 'wpstg_clone_settings';
21
+
22
+ /**
23
+ * Get the value of the given option,
24
+ * If no option given return all settings
25
+ *
26
+ * @param string $option
27
+ *
28
+ * @return mixed
29
+ */
30
+ public function get($option = null)
31
+ {
32
+ // Early bail if not a staging site
33
+ if (!(new SiteInfo())->isStaging()) {
34
+ return null;
35
+ }
36
+
37
+ $settings = get_option(self::WPSTG_CLONE_SETTINGS_KEY, null);
38
+
39
+ // Return settings if no options given
40
+ if ($option === null) {
41
+ return $settings;
42
+ }
43
+
44
+ // Early Bail: if settings is null or if settings isn't object
45
+ if ($settings === null || !is_object($settings)) {
46
+ return null;
47
+ }
48
+
49
+ // Early bail if given option not exists
50
+ if (!property_exists($settings, $option)) {
51
+ return null;
52
+ }
53
+
54
+ return $settings->{$option};
55
+ }
56
+
57
+ /**
58
+ * Set the value of given option
59
+ *
60
+ * @param string $option
61
+ * @param mixed $value
62
+ *
63
+ * @return bool
64
+ */
65
+ public function set($option, $value)
66
+ {
67
+ // Early bail if not a staging site
68
+ if (!(new SiteInfo())->isStaging()) {
69
+ return false;
70
+ }
71
+
72
+ $settings = get_option(self::WPSTG_CLONE_SETTINGS_KEY, null);
73
+
74
+ // If settings is null or if settings isn't object make settings a object
75
+ if ($settings === null || !is_object($settings)) {
76
+ $settings = new stdClass();
77
+ }
78
+
79
+ $settings->{$option} = $value;
80
+
81
+ return update_option(self::WPSTG_CLONE_SETTINGS_KEY, $settings);
82
+ }
83
+
84
+ /**
85
+ * Delete the given option
86
+ *
87
+ * @param string $option
88
+ *
89
+ * @return bool
90
+ */
91
+ public function delete($option)
92
+ {
93
+ // Early bail if not a staging site
94
+ if (!(new SiteInfo())->isStaging()) {
95
+ return false;
96
+ }
97
+
98
+ $settings = get_option(self::WPSTG_CLONE_SETTINGS_KEY, null);
99
+
100
+ // Early Bail: if settings is null or if settings isn't object
101
+ if ($settings === null || !is_object($settings)) {
102
+ return false;
103
+ }
104
+
105
+ // Early bail if given option not exists
106
+ if (!property_exists($settings, $option)) {
107
+ return true;
108
+ }
109
+
110
+ unset($settings->{$option});
111
+
112
+ return update_option(self::WPSTG_CLONE_SETTINGS_KEY, $settings);
113
+ }
114
+ }
Framework/Staging/FirstRun.php CHANGED
@@ -3,7 +3,7 @@
3
  namespace WPStaging\Framework\Staging;
4
 
5
  use WPStaging\Frontend\LoginNotice;
6
- use WPStaging\Backend\Notices\DisabledCacheNotice;
7
  use WPStaging\Framework\SiteInfo;
8
 
9
  /**
@@ -28,7 +28,7 @@ class FirstRun
28
 
29
  public function init()
30
  {
31
- if (!(new SiteInfo)->isStaging()) {
32
  return;
33
  }
34
 
@@ -50,13 +50,8 @@ class FirstRun
50
  // Show one time login notice on staging site.
51
  (new LoginNotice())->setTransient();
52
 
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');
@@ -72,5 +67,4 @@ class FirstRun
72
  {
73
  delete_option(static::FIRST_RUN_KEY);
74
  }
75
-
76
  }
3
  namespace WPStaging\Framework\Staging;
4
 
5
  use WPStaging\Frontend\LoginNotice;
6
+ use WPStaging\Backend\Notices\DisabledItemsNotice;
7
  use WPStaging\Framework\SiteInfo;
8
 
9
  /**
28
 
29
  public function init()
30
  {
31
+ if (!(new SiteInfo())->isStaging()) {
32
  return;
33
  }
34
 
50
  // Show one time login notice on staging site.
51
  (new LoginNotice())->setTransient();
52
 
53
+ // Enable the notice which show what WP Staging Disabled on staging site admin.
54
+ (new DisabledItemsNotice())->enable();
 
 
 
 
 
55
 
56
  // Allow users to attach custom actions by using this hook
57
  do_action('wpstg.clone_first_run');
67
  {
68
  delete_option(static::FIRST_RUN_KEY);
69
  }
 
70
  }
Framework/TemplateEngine/TemplateEngine.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  // TODO PHP7.x; declare(strict_types=1);
3
  // TODO PHP7.x; type-hints & return types;
4
 
@@ -38,7 +39,7 @@ class TemplateEngine implements TemplateEngineInterface
38
  */
39
  protected function getDateTimeFormat()
40
  {
41
- return (new DateTimeAdapter)->getDateTimeFormat();
42
  }
43
 
44
  /**
@@ -52,6 +53,6 @@ class TemplateEngine implements TemplateEngineInterface
52
  return '';
53
  }
54
 
55
- return (new DateTimeAdapter)->transformToWpFormat($dateTime);
56
  }
57
  }
1
  <?php
2
+
3
  // TODO PHP7.x; declare(strict_types=1);
4
  // TODO PHP7.x; type-hints & return types;
5
 
39
  */
40
  protected function getDateTimeFormat()
41
  {
42
+ return (new DateTimeAdapter())->getDateTimeFormat();
43
  }
44
 
45
  /**
53
  return '';
54
  }
55
 
56
+ return (new DateTimeAdapter())->transformToWpFormat($dateTime);
57
  }
58
  }
Framework/TemplateEngine/TemplateEngineException.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  // TODO PHP7.x; declare(strict_types=1);
3
 
4
  namespace WPStaging\Framework\TemplateEngine;
1
  <?php
2
+
3
  // TODO PHP7.x; declare(strict_types=1);
4
 
5
  namespace WPStaging\Framework\TemplateEngine;
Framework/TemplateEngine/TemplateEngineInterface.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  // TODO PHP7.x declare(strict_types=1);
3
  // TODO PHP7.x; type-hints & return types;
4
 
1
  <?php
2
+
3
  // TODO PHP7.x declare(strict_types=1);
4
  // TODO PHP7.x; type-hints & return types;
5
 
Framework/Traits/ArrayableTrait.php CHANGED
@@ -24,7 +24,7 @@ trait ArrayableTrait
24
 
25
  $data = [];
26
  /** @var ReflectionProperty $prop */
27
- foreach($props as $prop) {
28
  $prop->setAccessible(true);
29
  $value = $prop->getValue($this);
30
 
24
 
25
  $data = [];
26
  /** @var ReflectionProperty $prop */
27
+ foreach ($props as $prop) {
28
  $prop->setAccessible(true);
29
  $value = $prop->getValue($this);
30
 
Framework/Traits/BenchmarkTrait.php CHANGED
@@ -8,17 +8,21 @@ 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
 
@@ -27,7 +31,7 @@ trait BenchmarkTrait
27
  }
28
  }
29
 
30
- private function finishBenchmark($context)
31
  {
32
  if (defined('WPSTG_DEBUG') && WPSTG_DEBUG && $this->benchmarkLogger instanceof LoggerInterface) {
33
  $message = sprintf(
8
 
9
  trait BenchmarkTrait
10
  {
11
+ /** @var LoggerInterface To store the benchmark results. */
12
  private $benchmarkLogger;
13
+
14
+ /** @var int Timestamp when the benchmark started. */
15
  private $benchmarkStart = 0;
16
 
17
+ protected function startBenchmark()
18
  {
19
  if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
20
  /** @var Logger $logger */
21
  $this->benchmarkLogger = clone WPStaging::getInstance()->get(Logger::class);
22
 
23
  // Eg: JobSiteExport_Benchmark
24
+ $filename = sanitize_file_name(sprintf(
25
+ '%s_Benchmark',
26
  (new \ReflectionClass($this))->getShortName()
27
  ));
28
 
31
  }
32
  }
33
 
34
+ protected function finishBenchmark($context)
35
  {
36
  if (defined('WPSTG_DEBUG') && WPSTG_DEBUG && $this->benchmarkLogger instanceof LoggerInterface) {
37
  $message = sprintf(
Framework/Traits/BooleanTransientTrait.php CHANGED
@@ -16,8 +16,8 @@ trait BooleanTransientTrait
16
  set_transient($this->getTransientName(), true, $this->getExpiryTime());
17
  }
18
 
19
- /**
20
- * @return bool
21
  */
22
  public function getTransient()
23
  {
@@ -31,4 +31,4 @@ trait BooleanTransientTrait
31
  {
32
  delete_transient($this->getTransientName());
33
  }
34
- }
16
  set_transient($this->getTransientName(), true, $this->getExpiryTime());
17
  }
18
 
19
+ /**
20
+ * @return bool
21
  */
22
  public function getTransient()
23
  {
31
  {
32
  delete_transient($this->getTransientName());
33
  }
34
+ }
Framework/Traits/DbRowsGeneratorTrait.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Provides methods to fetch potentially unlimited rows from a database table
5
+ * with resource-usage awareness.
6
+ *
7
+ * @package WPStaging\Framework\Traits
8
+ */
9
+
10
+ namespace WPStaging\Framework\Traits;
11
+
12
+ use Generator;
13
+
14
+ /**
15
+ * Trait DbRowsGeneratorTrait
16
+ *
17
+ * @package WPStaging\Framework\Traits
18
+ */
19
+ trait DbRowsGeneratorTrait
20
+ {
21
+ use ResourceTrait;
22
+
23
+ /**
24
+ * Returns a generator of rows.
25
+ *
26
+ * The Generator will fetch the candidate rows to process in batches and return
27
+ * them transparently to the caller code.
28
+ * If the current thread is over 80% memory or execution time, then the Generator will yield `null` to stop
29
+ * the processing.
30
+ *
31
+ * @param string $table The prefixed name of the table to pull rows from.
32
+ * @param int $offset The number of row to start the work from.
33
+ * @param int $limit The maximum number of rows to try and process; the actual number of
34
+ * processed will depend on the server available memory and max request execution time.
35
+ * @param \wpdb|null A reference to the database instance to fetch rows from.
36
+ *
37
+ * @return Generator A generator yielding rows one by one; refetching them if and when required.
38
+ */
39
+ protected function rowsGenerator($table, $offset, $limit, \wpdb $db = null)
40
+ {
41
+ $this->initiateStartTime();
42
+
43
+ if (null === $db) {
44
+ global $wpdb;
45
+ $db = $wpdb;
46
+ }
47
+
48
+ // Sets the execution time limit to either a detected value below 10s and above 1s, or a safe 10s value.
49
+ $this->setTimeLimit(min(10, max((int)$this->findExecutionTimeLimit(), 1)));
50
+
51
+ $rows = [];
52
+ $processed = 0;
53
+ // More rows equals more memory; to process more let's reduce the memory footprint by using smaller fetch sizes.
54
+ $batchSize = $limit / 5;
55
+ $lastFetch = false;
56
+
57
+ do {
58
+ if (count($rows) === 0) {
59
+ if ($lastFetch) {
60
+ break;
61
+ }
62
+
63
+ $rows = $db->get_results("SELECT * FROM {$table} LIMIT {$offset}, {$batchSize}", ARRAY_A);
64
+
65
+ if (null === $rows) {
66
+ // We're done here.
67
+ break;
68
+ }
69
+
70
+ $offset += $batchSize;
71
+ // If we got less than the batch size, then this is the last fetch.
72
+ $lastFetch = count($rows) < $batchSize;
73
+ }
74
+
75
+ // Take the next row from the ready set.
76
+ $row = array_shift($rows);
77
+
78
+ if (null === $row) {
79
+ // We're done, no more rows to process.
80
+ break;
81
+ }
82
+
83
+ yield $row;
84
+
85
+ // It's actually processed when the caller code returns the control to the Generator.
86
+ $processed++;
87
+ } while (!$this->isThreshold() && $processed < $limit);
88
+ }
89
+ }
Framework/Traits/ExcludeFilterTrait.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Traits;
4
+
5
+ use WPStaging\Framework\Utils\Strings;
6
+
7
+ /**
8
+ * This trait is used in both RecursivePathExcludeFilter and PathExcludeFilter
9
+ * This will help in keeping the code DRY
10
+ */
11
+ trait ExcludeFilterTrait
12
+ {
13
+ /**
14
+ * @var array
15
+ */
16
+ private $extensionExcludes;
17
+
18
+ /**
19
+ * @var array
20
+ */
21
+ private $absolutePathExcludes;
22
+
23
+ /**
24
+ * @var array
25
+ */
26
+ private $anywherePathExcludes;
27
+
28
+ /**
29
+ * Categories Exclude in array
30
+ *
31
+ * @param array $excludes
32
+ */
33
+ protected function categorizeExcludes($excludes)
34
+ {
35
+ $this->extensionExcludes = [];
36
+ $this->absolutePathExcludes = [];
37
+ $this->anywherePathExcludes = [];
38
+ $strUtils = new Strings();
39
+ foreach ($excludes as $exclude) {
40
+ // *. is to check whether the exclude is extension exclude or not,
41
+ // If it is extension exclude then add to extension exclude array and move to next exclude
42
+ if ($strUtils->startsWith($exclude, '*.')) {
43
+ $this->extensionExcludes[] = $exclude;
44
+ continue;
45
+ }
46
+
47
+ // **/ is to check whether the exclude is anywhere/wildcard exclude or not,
48
+ // If it is anywhere exclude then add to anywhere exclude array and move to next exclude
49
+ if ($strUtils->startsWith($exclude, '**/')) {
50
+ $this->anywherePathExcludes[] = $exclude;
51
+ continue;
52
+ }
53
+
54
+ // If the exclude doesn't matches extension or anywhere exclude treat it as absolute path exclude
55
+ $this->absolutePathExcludes[] = $exclude;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Check whether the file extension satisfy any extension exclusion
61
+ *
62
+ * @param string $fileExt
63
+ * @return boolean
64
+ */
65
+ protected function isExcludedExtension($fileExt)
66
+ {
67
+ $fileExt = '*.' . $fileExt;
68
+ return in_array($fileExt, $this->extensionExcludes);
69
+ }
70
+
71
+ /**
72
+ * Check whether the given path satisfy any absolute path exclusion
73
+ *
74
+ * @param string $path
75
+ * @return boolean
76
+ */
77
+ protected function isExcludedAbsolutePath($path)
78
+ {
79
+ foreach ($this->absolutePathExcludes as $exclude) {
80
+ if ((new Strings())->startsWith($path, $exclude)) {
81
+ return true;
82
+ }
83
+ }
84
+
85
+ return false;
86
+ }
87
+
88
+ /**
89
+ * Check whether the given path satisfy any anywhere path exclusion
90
+ *
91
+ * @param string $path
92
+ * @return boolean
93
+ */
94
+ protected function isExcludedAnywherePath($path)
95
+ {
96
+ if (is_dir($path)) {
97
+ $path = trailingslashit($path);
98
+ }
99
+
100
+ foreach ($this->anywherePathExcludes as $exclude) {
101
+ $exclude = ltrim($exclude, '**');
102
+ if (is_dir($path)) {
103
+ $exclude = trailingslashit($exclude);
104
+ }
105
+
106
+ if (strpos($path, $exclude) !== false) {
107
+ return true;
108
+ }
109
+ }
110
+
111
+ return false;
112
+ }
113
+
114
+ /**
115
+ * Check whether the file meets any of the exclude criteria
116
+ * @param SplFileInfo $fileInfo
117
+ *
118
+ * @return boolean
119
+ */
120
+ protected function isExcluded($fileInfo)
121
+ {
122
+ $path = $fileInfo->getPathname();
123
+
124
+ // Check extension
125
+ if ($this->isExcludedExtension($fileInfo->getExtension())) {
126
+ return true;
127
+ }
128
+
129
+ // Check absolute path
130
+ if ($this->isExcludedAbsolutePath($path)) {
131
+ return true;
132
+ }
133
+
134
+ // Check anywhere path
135
+ if ($this->isExcludedAnywherePath($path)) {
136
+ return true;
137
+ }
138
+
139
+ return false;
140
+ }
141
+ }
Framework/Traits/FileScanToCacheTrait.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Framework\Traits;
4
+
5
+ use Exception;
6
+ use stdClass;
7
+ use WPStaging\Backend\Modules\Jobs\Scan;
8
+ use WPStaging\Framework\Filesystem\FilterableDirectoryIterator;
9
+ use WPStaging\Framework\Utils\Strings;
10
+
11
+ trait FileScanToCacheTrait
12
+ {
13
+ /**
14
+ * Write contents to a file
15
+ *
16
+ * @param resource $handle File handle to write to
17
+ * @param string $content Content to write to the file
18
+ * @return integer
19
+ * @throws Exception
20
+ */
21
+ abstract public function write($handle, $content);
22
+
23
+ /**
24
+ * Scan Recursively through DirectoryIterator as RecursiveDirectoryIterator is slow
25
+ *
26
+ * @param resource $filesHandle
27
+ * @param string $path
28
+ * @param bool $isRecursive
29
+ * @param array $excludePaths absolute path of dir/files to exclude
30
+ * @param string $wpRootPath
31
+ *
32
+ * @return int count of files path written to cache file
33
+ */
34
+ public function scanToCacheFile($filesHandle, $path, $isRecursive = false, $excludePaths = [], $wpRootPath = ABSPATH)
35
+ {
36
+ if (is_link($path)) {
37
+ return 0;
38
+ }
39
+
40
+ $strUtil = new Strings();
41
+ if (is_file($path)) {
42
+ $file = str_replace($strUtil->sanitizeDirectorySeparator($wpRootPath), '', $strUtil->sanitizeDirectorySeparator($path)) . PHP_EOL;
43
+ if ($this->write($filesHandle, $file)) {
44
+ return 1;
45
+ }
46
+
47
+ return 0;
48
+ }
49
+
50
+ $filesWrittenToCache = 0;
51
+ $iterator = (new FilterableDirectoryIterator())
52
+ ->setDirectory(trailingslashit($path))
53
+ ->setRecursive(false)
54
+ ->setDotSkip()
55
+ ->setExcludePaths($excludePaths)
56
+ ->get();
57
+ foreach ($iterator as $item) {
58
+ // Always check link first otherwise it may be treated as directory
59
+ if ($item->isLink()) {
60
+ continue;
61
+ }
62
+
63
+ if ($isRecursive && $item->isDir()) {
64
+ $filesWrittenToCache += $this->scanToCacheFile($filesHandle, $item->getPathname(), $isRecursive, $excludePaths, $wpRootPath);
65
+ }
66
+
67
+ if ($item->isFile()) {
68
+ $file = str_replace($strUtil->sanitizeDirectorySeparator($wpRootPath), '', $strUtil->sanitizeDirectorySeparator($item->getPathname())) . PHP_EOL;
69
+ if ($this->write($filesHandle, $file)) {
70
+ $filesWrittenToCache++;
71
+ }
72
+ }
73
+ }
74
+
75
+ return $filesWrittenToCache;
76
+ }
77
+
78
+ /**
79
+ * Filtered directories according to the directory given
80
+ * @param string $directoryPath
81
+ * @param array $directories list of selected directories in the form of directoryPath . separatorConst . scanFlag e.g. /var/www/wp-content/plugins/::1
82
+ *
83
+ * @return array
84
+ */
85
+ public function filteredSelectedDirectories($directoryPath, $directories)
86
+ {
87
+ $strUtil = new Strings();
88
+ $directoryPath = $strUtil->sanitizeDirectorySeparator($directoryPath);
89
+ return array_filter($this->mapSelectedDirectories($directories), function ($directory) use ($directoryPath, $strUtil) {
90
+ if ($strUtil->startsWith($strUtil->sanitizeDirectorySeparator($directory->path), $directoryPath)) {
91
+ return true;
92
+ }
93
+ });
94
+ }
95
+
96
+ /**
97
+ * Map included directories to object
98
+ * @param array $directories list of selected directories in the form of directoryPath . separatorConst . scanFlag
99
+ *
100
+ * @return array array of objects which contains information about directory path and whether it is scanned or not
101
+ */
102
+ protected function mapSelectedDirectories($directories)
103
+ {
104
+ return array_map(function ($directory) {
105
+ $directory = trim($directory, ' ');
106
+ list($directoryPath, $flag) = explode(Scan::DIRECTORY_PATH_FLAG_SEPARATOR, $directory);
107
+ $directoryInfo = new stdClass();
108
+ $directoryInfo->path = trim($directoryPath, ' ');
109
+ $directoryInfo->flag = trim($flag, ' ');
110
+ return $directoryInfo;
111
+ }, $directories);
112
+ }
113
+ }
Framework/Traits/HydrateTrait.php CHANGED
@@ -27,7 +27,7 @@ trait HydrateTrait
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
  }
@@ -58,7 +58,7 @@ trait HydrateTrait
58
  $params = $method->getParameters();
59
 
60
  if (!isset($params[0]) || count($params) > 1) {
61
- throw new EntityException(sprintf(
62
  'Class %s setter method %s does not have a first parameter or has more than one parameter',
63
  static::class,
64
  $method
@@ -86,10 +86,10 @@ trait HydrateTrait
86
  {
87
  $className = $class->getName();
88
  if (!$value instanceof DateTime && $className === 'DateTime') {
89
- return (new DateTimeAdapter)->getDateTime($value);
90
  }
91
 
92
- $obj = new $className;
93
  if (is_array($value) && method_exists($obj, 'hydrate')) {
94
  $obj->hydrate($value);
95
  }
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
  }
58
  $params = $method->getParameters();
59
 
60
  if (!isset($params[0]) || count($params) > 1) {
61
+ throw new Exception(sprintf(
62
  'Class %s setter method %s does not have a first parameter or has more than one parameter',
63
  static::class,
64
  $method
86
  {
87
  $className = $class->getName();
88
  if (!$value instanceof DateTime && $className === 'DateTime') {
89
+ return (new DateTimeAdapter())->getDateTime($value);
90
  }
91
 
92
+ $obj = new $className();
93
  if (is_array($value) && method_exists($obj, 'hydrate')) {
94
  $obj->hydrate($value);
95
  }
Framework/Traits/MaintenanceTrait.php CHANGED
@@ -8,11 +8,11 @@ trait MaintenanceTrait
8
  {
9
  public function enableMaintenance($isMaintenance)
10
  {
11
- (new Maintenance)->enableMaintenance($isMaintenance);
12
  }
13
 
14
  public function skipMaintenanceMode()
15
  {
16
  apply_filters('enable_maintenance_mode', false);
17
  }
18
- }
8
  {
9
  public function enableMaintenance($isMaintenance)
10
  {
11
+ (new Maintenance())->enableMaintenance($isMaintenance);
12
  }
13
 
14
  public function skipMaintenanceMode()
15
  {
16
  apply_filters('enable_maintenance_mode', false);
17
  }
18
+ }
Framework/Traits/RequestNotationTrait.php DELETED
@@ -1,46 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_types=1);
4
- // TODO PHP7.x type-hints & return types
5
-
6
- namespace WPStaging\Framework\Traits;
7
-
8
- use RuntimeException;
9
- use WPStaging\Component\Dto\AbstractDto;
10
-
11
- trait RequestNotationTrait
12
- {
13
- // TODO sanitize POST data
14
- /**
15
- * @param string $notation
16
- * @return array|string|object
17
- */
18
- public function resolvePostRequestData($notation)
19
- {
20
- $data = isset($_POST['wpstg'])? $_POST['wpstg'] : [];
21
- $keys = explode('.', $notation);
22
- foreach ($keys as $key) {
23
- if (!isset($data[$key])) {
24
- return [];
25
- }
26
- $data = $data[$key];
27
- }
28
- return $data;
29
- }
30
-
31
- /**
32
- * @param string $className
33
- * @param string $notation
34
- * @return object|AbstractDto
35
- */
36
- public function initializeRequestDto($className, $notation)
37
- {
38
- $data = $this->resolvePostRequestData($notation);
39
- $dto = (new $className);
40
- if (!method_exists($dto, 'hydrate')) {
41
- throw new RuntimeException(sprintf('DTO Class %s does not have hydrate method', $className));
42
- }
43
- $dto->hydrate($data);
44
- return $dto;
45
- }
46
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Traits/ResourceTrait.php CHANGED
@@ -2,8 +2,6 @@
2
 
3
  namespace WPStaging\Framework\Traits;
4
 
5
- use WPStaging\Framework\Utils\Size;
6
-
7
  trait ResourceTrait
8
  {
9
  use TimerTrait;
@@ -36,9 +34,9 @@ trait ResourceTrait
36
  return false;
37
  }
38
 
39
- $limit = (new Size)->toBytes(ini_get('memory_limit'));
40
 
41
- if (!is_int($limit) || $limit < 64000000){
42
  $limit = 64000000;
43
  }
44
  $allowed = $limit - 1024;
2
 
3
  namespace WPStaging\Framework\Traits;
4
 
 
 
5
  trait ResourceTrait
6
  {
7
  use TimerTrait;
34
  return false;
35
  }
36
 
37
+ $limit = wp_convert_hr_to_bytes(ini_get('memory_limit'));
38
 
39
+ if (!is_int($limit) || $limit < 64000000) {
40
  $limit = 64000000;
41
  }
42
  $allowed = $limit - 1024;
Framework/Traits/TimerTrait.php CHANGED
@@ -9,8 +9,7 @@ trait TimerTrait
9
 
10
  protected function initiateStartTime()
11
  {
12
- $time = explode(' ', microtime());
13
- $this->startTime = (float) $time[1] + (float) $time[0];
14
  }
15
 
16
  /**
@@ -18,6 +17,14 @@ trait TimerTrait
18
  */
19
  protected function getRunningTime()
20
  {
21
- return time() - $this->startTime;
 
 
 
 
 
 
 
 
22
  }
23
  }
9
 
10
  protected function initiateStartTime()
11
  {
12
+ $this->startTime = microtime(true);
 
13
  }
14
 
15
  /**
17
  */
18
  protected function getRunningTime()
19
  {
20
+ if ($this->startTime === null) {
21
+ throw new \LogicException(
22
+ sprintf(
23
+ 'You must call the "%s::initiateStartTime" method before trying to get the current run time.',
24
+ __TRAIT__
25
+ )
26
+ );
27
+ }
28
+ return microtime(true) - $this->startTime;
29
  }
30
  }
Framework/Utils/Cache/AbstractCache.php CHANGED
@@ -91,7 +91,7 @@ abstract class AbstractCache
91
  {
92
  $this->path = $path;
93
 
94
- (new Filesystem)->mkdir($path);
95
 
96
  $this->initializeFilePath();
97
  }
@@ -121,6 +121,16 @@ abstract class AbstractCache
121
  return $this->filePath;
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
124
  /**
125
  * @return bool
126
  */
91
  {
92
  $this->path = $path;
93
 
94
+ (new Filesystem())->mkdir($path, true);
95
 
96
  $this->initializeFilePath();
97
  }
121
  return $this->filePath;
122
  }
123
 
124
+ /**
125
+ * @param $fileName
126
+ *
127
+ * @return bool
128
+ */
129
+ public function cacheExists($fileName)
130
+ {
131
+ return is_file(trailingslashit($this->getPath()) . $fileName . '.' . static::EXTENSION);
132
+ }
133
+
134
  /**
135
  * @return bool
136
  */
Framework/Utils/Cache/BufferedCache.php CHANGED
@@ -50,7 +50,7 @@ class BufferedCache extends AbstractCache
50
  }
51
 
52
  $pos = ftell($handle);
53
- fseek($handle,$pos - strlen($buffer) - $offset);
54
  fwrite($handle, $buffer);
55
  fseek($handle, $pos);
56
  }
@@ -138,10 +138,10 @@ class BufferedCache extends AbstractCache
138
 
139
  if (!flock($handle, LOCK_EX)) {
140
  fclose($handle);
141
- throw new IOException('Failed to lock file: '. $this->filePath);
142
  }
143
 
144
- $offset= 0;
145
  clearstatcache();
146
  $size = filesize($this->filePath);
147
  $totalLines = 0;
@@ -178,7 +178,7 @@ class BufferedCache extends AbstractCache
178
 
179
  if (!flock($handle, LOCK_EX)) {
180
  fclose($handle);
181
- throw new IOException('Failed to lock file: '. $this->filePath);
182
  }
183
 
184
  $stats = fstat($handle);
@@ -257,7 +257,7 @@ class BufferedCache extends AbstractCache
257
  $data = [];
258
  $i = 0;
259
  while (($buffer = fgets($handle, self::AVERAGE_LINE_LENGTH)) !== false) {
260
- $data[]= trim($buffer);
261
  $i++;
262
  if ($i >= $lines) {
263
  break;
50
  }
51
 
52
  $pos = ftell($handle);
53
+ fseek($handle, $pos - strlen($buffer) - $offset);
54
  fwrite($handle, $buffer);
55
  fseek($handle, $pos);
56
  }
138
 
139
  if (!flock($handle, LOCK_EX)) {
140
  fclose($handle);
141
+ throw new IOException('Failed to lock file: ' . $this->filePath);
142
  }
143
 
144
+ $offset = 0;
145
  clearstatcache();
146
  $size = filesize($this->filePath);
147
  $totalLines = 0;
178
 
179
  if (!flock($handle, LOCK_EX)) {
180
  fclose($handle);
181
+ throw new IOException('Failed to lock file: ' . $this->filePath);
182
  }
183
 
184
  $stats = fstat($handle);
257
  $data = [];
258
  $i = 0;
259
  while (($buffer = fgets($handle, self::AVERAGE_LINE_LENGTH)) !== false) {
260
+ $data[] = trim($buffer);
261
  $i++;
262
  if ($i >= $lines) {
263
  break;
Framework/Utils/Cache/Cache.php CHANGED
@@ -10,7 +10,6 @@ use WPStaging\Framework\Filesystem\File;
10
 
11
  class Cache extends AbstractCache
12
  {
13
-
14
  /**
15
  * @inheritDoc
16
  */
@@ -20,16 +19,18 @@ class Cache extends AbstractCache
20
  return $default;
21
  }
22
 
23
- // unserialize() is not safe, vulnerable to remote code execution via PHP Object Injection
24
- // see https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection
25
  return json_decode(file_get_contents($this->filePath), true);
26
  }
27
 
28
  /**
29
  * @inheritDoc
30
  */
31
- public function save($value)
32
  {
33
- return (new File($this->filePath, File::MODE_WRITE))->fwriteSafe(json_encode($value));
 
 
 
 
34
  }
35
  }
10
 
11
  class Cache extends AbstractCache
12
  {
 
13
  /**
14
  * @inheritDoc
15
  */
19
  return $default;
20
  }
21
 
 
 
22
  return json_decode(file_get_contents($this->filePath), true);
23
  }
24
 
25
  /**
26
  * @inheritDoc
27
  */
28
+ public function save($value, $pretty = false)
29
  {
30
+ if ($pretty) {
31
+ return (new File($this->filePath, File::MODE_WRITE))->fwriteSafe(json_encode($value, JSON_PRETTY_PRINT));
32
+ } else {
33
+ return (new File($this->filePath, File::MODE_WRITE))->fwriteSafe(json_encode($value));
34
+ }
35
  }
36
  }
Framework/Utils/Size.php DELETED
@@ -1,27 +0,0 @@
1
- <?php
2
-
3
- // TODO PHP7.x; declare(strict_types=1);
4
-
5
- namespace WPStaging\Framework\Utils;
6
-
7
- class Size
8
- {
9
- public function toUnit($bytes)
10
- {
11
- $value = floor(log($bytes) / log(1024));
12
- $sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
13
- return (float) sprintf('%.02F', $bytes / pow(1024, $value)) * 1 . ' ' . $sizes[$value];
14
- }
15
-
16
- public function toBytes($value)
17
- {
18
- if (empty($value)) {
19
- return 0;
20
- }
21
- // Commented code below does not work in PHP 7.0.33 or due to another reason. Error: PHP Notice: Uninitialized string offset: -1
22
- //$unit = strtolower($value)[-1];
23
- $unit = strtolower(substr(trim($value), -1));
24
- $sizes = ['b' => 0, 'k' => 1, 'm' => 2, 'g' => 3];
25
- return (int)$value * pow(1024, $sizes[$unit]);
26
- }
27
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Framework/Utils/Strings.php CHANGED
@@ -31,14 +31,16 @@ class Strings
31
  * @param string $subject
32
  * @return string
33
  */
34
- public function str_replace_first( $search, $replace, $subject ) {
 
35
 
36
- if( empty( $search ) )
37
  return $subject;
 
38
 
39
- $pos = strpos( $subject, $search );
40
- if( $pos !== false ) {
41
- return substr_replace( $subject, $replace, $pos, strlen( $search ) );
42
  }
43
  return $subject;
44
  }
@@ -50,9 +52,10 @@ class Strings
50
  * @param string $haystack
51
  * @return string
52
  */
53
- public function getLastElemAfterString( $needle, $haystack ) {
54
- $pos = strrpos( $haystack, $needle );
55
- return $pos === false ? $haystack : substr( $haystack, $pos + 1 );
 
56
  }
57
 
58
  /**
@@ -60,8 +63,9 @@ class Strings
60
  * @param string $str
61
  * @return string
62
  */
63
- public function getUrlWithoutScheme( $str ) {
64
- return preg_replace( '#^https?://#', '', rtrim( $str, '/' ) );
 
65
  }
66
 
67
  /**
@@ -71,7 +75,21 @@ class Strings
71
  *
72
  * @return string
73
  */
74
- public function sanitizeDirectorySeparator( $path ) {
75
- return preg_replace( '/[\\\\]+/', '/', $path );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
  }
31
  * @param string $subject
32
  * @return string
33
  */
34
+ public function str_replace_first($search, $replace, $subject)
35
+ {
36
 
37
+ if (empty($search)) {
38
  return $subject;
39
+ }
40
 
41
+ $pos = strpos($subject, $search);
42
+ if ($pos !== false) {
43
+ return substr_replace($subject, $replace, $pos, strlen($search));
44
  }
45
  return $subject;
46
  }
52
  * @param string $haystack
53
  * @return string
54
  */
55
+ public function getLastElemAfterString($needle, $haystack)
56
+ {
57
+ $pos = strrpos($haystack, $needle);
58
+ return $pos === false ? $haystack : substr($haystack, $pos + 1);
59
  }
60
 
61
  /**
63
  * @param string $str
64
  * @return string
65
  */
66
+ public function getUrlWithoutScheme($str)
67
+ {
68
+ return preg_replace('#^https?://#', '', rtrim($str, '/'));
69
  }
70
 
71
  /**
75
  *
76
  * @return string
77
  */
78
+ public function sanitizeDirectorySeparator($path)
79
+ {
80
+ $string = preg_replace('/[\\\\]+/', '/', $path);
81
+ return str_replace('//', '/', $string);
82
+ }
83
+
84
+ /**
85
+ * Check if a strings start with a specific string
86
+ * @param string $haystack
87
+ * @param string $needle
88
+ * @return bool
89
+ */
90
+ public function startsWith($haystack, $needle)
91
+ {
92
+ $length = strlen($needle);
93
+ return ($needle === substr($haystack, 0, $length));
94
  }
95
  }
Framework/Utils/Urls.php CHANGED
@@ -2,7 +2,8 @@
2
 
3
  namespace WPStaging\Framework\Utils;
4
 
5
- class Urls {
 
6
 
7
 
8
  /**
@@ -12,24 +13,26 @@ class Urls {
12
  * if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
13
  * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
14
  */
15
- public function getHomeUrl($blog_id = null, $scheme = null ) {
 
16
 
17
- if( empty( $blog_id ) || !is_multisite() ) {
18
- $url = get_option( 'home' );
19
  } else {
20
- switch_to_blog( $blog_id );
21
- $url = get_option( 'home' );
22
  restore_current_blog();
23
  }
24
 
25
- if( !in_array( $scheme, ['http', 'https', 'relative'] ) ) {
26
- if( is_ssl())
27
  $scheme = 'https';
28
- else
29
- $scheme = parse_url( $url, PHP_URL_SCHEME );
 
30
  }
31
 
32
- $url = set_url_scheme( $url, $scheme );
33
 
34
  return $url;
35
  }
@@ -39,8 +42,9 @@ class Urls {
39
  * @param string $str
40
  * @return string
41
  */
42
- public function getHomeUrlWithoutScheme() {
43
- return preg_replace( '#^https?://#', '', rtrim( $this->getHomeUrl(), '/' ) );
 
44
  }
45
 
46
 
@@ -48,8 +52,9 @@ class Urls {
48
  * Get raw base URL e.g. https://blog.domain.com or https://domain.com without any subfolder
49
  * @return string
50
  */
51
- public function getBaseUrl() {
52
- $result = parse_url( $this->getHomeUrl() );
 
53
  return $result['scheme'] . "://" . $result['host'];
54
  }
55
 
@@ -59,10 +64,8 @@ class Urls {
59
  * @param string $str
60
  * @return string
61
  */
62
- public function getBaseUrlWithoutScheme() {
63
- return preg_replace( '#^https?://#', '', rtrim( $this->getBaseUrl(), '/' ) );
 
64
  }
65
-
66
-
67
-
68
- }
2
 
3
  namespace WPStaging\Framework\Utils;
4
 
5
+ class Urls
6
+ {
7
 
8
 
9
  /**
13
  * if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
14
  * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
15
  */
16
+ public function getHomeUrl($blog_id = null, $scheme = null)
17
+ {
18
 
19
+ if (empty($blog_id) || !is_multisite()) {
20
+ $url = get_option('home');
21
  } else {
22
+ switch_to_blog($blog_id);
23
+ $url = get_option('home');
24
  restore_current_blog();
25
  }
26
 
27
+ if (!in_array($scheme, ['http', 'https', 'relative'])) {
28
+ if (is_ssl()) {
29
  $scheme = 'https';
30
+ } else {
31
+ $scheme = parse_url($url, PHP_URL_SCHEME);
32
+ }
33
  }
34
 
35
+ $url = set_url_scheme($url, $scheme);
36
 
37
  return $url;
38
  }
42
  * @param string $str
43
  * @return string
44
  */
45
+ public function getHomeUrlWithoutScheme()
46
+ {
47
+ return preg_replace('#^https?://#', '', rtrim($this->getHomeUrl(), '/'));
48
  }
49
 
50
 
52
  * Get raw base URL e.g. https://blog.domain.com or https://domain.com without any subfolder
53
  * @return string
54
  */
55
+ public function getBaseUrl()
56
+ {
57
+ $result = parse_url($this->getHomeUrl());
58
  return $result['scheme'] . "://" . $result['host'];
59
  }
60
 
64
  * @param string $str
65
  * @return string
66
  */
67
+ public function getBaseUrlWithoutScheme()
68
+ {
69
+ return preg_replace('#^https?://#', '', rtrim($this->getBaseUrl(), '/'));
70
  }
71
+ }
 
 
 
Framework/Utils/WpDefaultDirectories.php CHANGED
@@ -4,6 +4,9 @@
4
 
5
  namespace WPStaging\Framework\Utils;
6
 
 
 
 
7
  // TODO PHP7.1; constant visibility
8
  class WpDefaultDirectories
9
  {
@@ -19,10 +22,37 @@ class WpDefaultDirectories
19
  */
20
  private $strUtils;
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  public function __construct()
23
  {
24
  // @todo inject using DI
25
  $this->strUtils = new Strings();
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
  /**
@@ -40,23 +70,23 @@ class WpDefaultDirectories
40
  */
41
  public function getRelativeUploadPath()
42
  {
43
- $relPath = str_replace($this->strUtils->sanitizeDirectorySeparator(ABSPATH), null, $this->getUploadPath());
44
 
45
  return trim($relPath, '/');
46
  }
47
 
48
  /*
49
- * Get the absolute path of upload directory
50
  * @return string
51
  */
52
- public function getUploadPath($refreshCache = false)
53
  {
54
  // Get upload directory information. Default is ABSPATH . 'wp-content/uploads'
55
  // Could have been customized by populating the db option upload_path or the constant UPLOADS in wp-config
56
  // If both are defined WordPress will uses the value of the UPLOADS constant
57
  // First two parameters in wp_upload_dir are default parameter and last parameter is to refresh the cache
58
  // Setting the 3rd and last parameter to true will refresh the cache return the latest value. Set to true for tests
59
- $uploads = wp_upload_dir(null, true, $refreshCache);
60
 
61
  // Adding slashes at before and end of absolute path to WordPress uploads directory
62
  $uploadsAbsPath = trailingslashit($uploads['basedir']);
@@ -64,13 +94,44 @@ class WpDefaultDirectories
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
  }
@@ -81,7 +142,8 @@ class WpDefaultDirectories
81
  */
82
  public function getRelativePluginPath()
83
  {
84
- $relPath = str_replace($this->strUtils->sanitizeDirectorySeparator(ABSPATH), null, WP_PLUGIN_DIR);
 
85
 
86
  return trim($relPath, '/');
87
  }
@@ -93,7 +155,150 @@ class WpDefaultDirectories
93
  public function getRelativeThemePath()
94
  {
95
  $relWpContentPath = $this->getRelativeWpContentPath();
96
- $relPath = $relWpContentPath . "/" . "themes";
97
- return $this->strUtils->sanitizeDirectorySeparator($relPath);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
99
  }
4
 
5
  namespace WPStaging\Framework\Utils;
6
 
7
+ use DirectoryIterator;
8
+ use WPStaging\Backend\Modules\Jobs\Scan;
9
+
10
  // TODO PHP7.1; constant visibility
11
  class WpDefaultDirectories
12
  {
22
  */
23
  private $strUtils;
24
 
25
+ /**
26
+ * WordPress core directories: wp-admin, wp-includes, wp-content
27
+ * @var array
28
+ */
29
+ private $coreDirectories;
30
+
31
+ /**
32
+ * Sanitized ABSPATH for comparing against windows iterator
33
+ * @var string
34
+ */
35
+ private $wpRoot;
36
+
37
+ /**
38
+ * refresh cache of upload path
39
+ * @var boolean default false
40
+ */
41
+ private $refreshUploadPathCache = false;
42
+
43
  public function __construct()
44
  {
45
  // @todo inject using DI
46
  $this->strUtils = new Strings();
47
+ $this->wpRoot = $this->strUtils->sanitizeDirectorySeparator(ABSPATH);
48
+ }
49
+
50
+ /**
51
+ * @param boolean $shouldRefresh
52
+ */
53
+ public function shouldRefreshUploadPathCache($shouldRefresh = true)
54
+ {
55
+ $this->refreshUploadPathCache = $shouldRefresh;
56
  }
57
 
58
  /**
70
  */
71
  public function getRelativeUploadPath()
72
  {
73
+ $relPath = str_replace($this->wpRoot, null, $this->getUploadsPath());
74
 
75
  return trim($relPath, '/');
76
  }
77
 
78
  /*
79
+ * Get the absolute path of uploads directory
80
  * @return string
81
  */
82
+ public function getUploadsPath()
83
  {
84
  // Get upload directory information. Default is ABSPATH . 'wp-content/uploads'
85
  // Could have been customized by populating the db option upload_path or the constant UPLOADS in wp-config
86
  // If both are defined WordPress will uses the value of the UPLOADS constant
87
  // First two parameters in wp_upload_dir are default parameter and last parameter is to refresh the cache
88
  // Setting the 3rd and last parameter to true will refresh the cache return the latest value. Set to true for tests
89
+ $uploads = wp_upload_dir(null, true, $this->refreshUploadPathCache);
90
 
91
  // Adding slashes at before and end of absolute path to WordPress uploads directory
92
  $uploadsAbsPath = trailingslashit($uploads['basedir']);
94
  return $this->strUtils->sanitizeDirectorySeparator($uploadsAbsPath);
95
  }
96
 
97
+ /**
98
+ * Get site specific absolute WP uploads path e.g.
99
+ * Multisites: /var/www/htdocs/example.com/wp-content/uploads/sites/1 or /var/www/htdocs/example.com/wp-content/blogs.dir/1/files
100
+ * Single sites: /var/www/htdocs/example.com/wp-content/uploads
101
+ * This is compatible to old WordPress multisite version which contained blogs.dir
102
+ * @return string
103
+ */
104
+ public function getSiteUploadsPath()
105
+ {
106
+ $uploads = wp_upload_dir(null, false);
107
+ $baseDir = $this->strUtils->sanitizeDirectorySeparator($uploads['basedir']);
108
+
109
+ // If multisite (and if not the main site in a post-MU network)
110
+ if (is_multisite() && !( is_main_network() && is_main_site() && defined('MULTISITE') )) {
111
+ // blogs.dir is used on WP 3.5 and lower
112
+ if (strpos($baseDir, 'blogs.dir') !== false) {
113
+ // remove this piece from the basedir: /blogs.dir/2/files
114
+ $uploadDir = wpstg_replace_first_match('/blogs.dir/' . get_current_blog_id() . '/files', null, $baseDir);
115
+ $dir = $this->strUtils->sanitizeDirectorySeparator($uploadDir . '/blogs.dir');
116
+ } else {
117
+ // remove this piece from the basedir: /sites/2
118
+ $uploadDir = wpstg_replace_first_match('/sites/' . get_current_blog_id(), null, $baseDir);
119
+ $dir = $this->strUtils->sanitizeDirectorySeparator($uploadDir . '/sites');
120
+ }
121
+
122
+ return $dir;
123
+ }
124
+ return $baseDir;
125
+ }
126
+
127
  /**
128
  * Get the relative path of wp content directory
129
  * @return string
130
  */
131
  public function getRelativeWpContentPath()
132
  {
133
+ $wpContentDir = $this->strUtils->sanitizeDirectorySeparator(WP_CONTENT_DIR);
134
+ $relPath = str_replace($this->wpRoot, null, $wpContentDir);
135
 
136
  return trim($relPath, '/');
137
  }
142
  */
143
  public function getRelativePluginPath()
144
  {
145
+ $wpPluginDir = $this->strUtils->sanitizeDirectorySeparator(WP_PLUGIN_DIR);
146
+ $relPath = str_replace($this->wpRoot, null, $wpPluginDir);
147
 
148
  return trim($relPath, '/');
149
  }
155
  public function getRelativeThemePath()
156
  {
157
  $relWpContentPath = $this->getRelativeWpContentPath();
158
+ return trailingslashit($relWpContentPath) . "themes";
159
+ }
160
+
161
+ /**
162
+ * Get array of wp core directories and their one level sub dir with flag 0|1
163
+ * i.e. wp-content, wp-admin, wp-includes, plugins etc
164
+ *
165
+ * @return array
166
+ */
167
+ public function getWpCoreDirectories()
168
+ {
169
+ $this->coreDirectories = [];
170
+
171
+ $directories = new DirectoryIterator(ABSPATH);
172
+ foreach ($directories as $directory) {
173
+ if ($directory->isDot() || $directory->isFile() || $directory->isLink()) {
174
+ continue;
175
+ }
176
+
177
+ $name = $directory->getBasename();
178
+ $path = $this->strUtils->sanitizeDirectorySeparator($directory->getPathname());
179
+
180
+ // should skip if not wp core directory
181
+ $shouldSkip = ($name !== 'wp-admin' &&
182
+ $name !== 'wp-includes' &&
183
+ $name !== 'wp-content' &&
184
+ $name !== 'sites') &&
185
+ strpos(strrev($path), strrev($this->wpRoot . "wp-admin")) === false &&
186
+ strpos(strrev($path), strrev($this->wpRoot . "wp-includes")) === false &&
187
+ strpos(strrev($path), strrev($this->wpRoot . "wp-content")) === false;
188
+
189
+ if ($shouldSkip) {
190
+ continue;
191
+ }
192
+
193
+ $this->handleSelfAndSubDirs($path);
194
+ }
195
+
196
+ // Gather Plugins
197
+ $pluginsDir = $this->wpRoot . $this->getRelativePluginPath();
198
+ $this->handleSelfAndSubDirs($pluginsDir);
199
+
200
+ // Gather Themes
201
+ $themesDir = $this->wpRoot . $this->getRelativeThemePath();
202
+ $this->handleSelfAndSubDirs($themesDir);
203
+
204
+ // Gather Uploads
205
+ $this->handleSelfAndSubDirs($this->getUploadsPath());
206
+
207
+ return $this->coreDirectories;
208
+ }
209
+
210
+ /**
211
+ * Add directory and it subdirectories to included directory list if not already added
212
+ *
213
+ * @param string $path
214
+ */
215
+ protected function handleSelfAndSubDirs($path)
216
+ {
217
+ $this->addDirectoryToList($path, Scan::IS_NON_RECURSIVE);
218
+ $directories = new DirectoryIterator($path);
219
+ foreach ($directories as $directory) {
220
+ if ($directory->isDot() || $directory->isFile() || $directory->isLink()) {
221
+ continue;
222
+ }
223
+
224
+ $this->addDirectoryToList($directory->getPathname(), Scan::IS_RECURSIVE);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Add directory if not already exist
230
+ * Override the flag value with scanned only if directory already present and was scanned
231
+ *
232
+ * @param string $path
233
+ * @param int $flag
234
+ */
235
+ protected function addDirectoryToList($path, $flag)
236
+ {
237
+ $path = untrailingslashit($this->strUtils->sanitizeDirectorySeparator($path));
238
+ $dirInfo = $path . Scan::DIRECTORY_PATH_FLAG_SEPARATOR . $flag;
239
+
240
+ if (in_array($dirInfo, $this->coreDirectories)) {
241
+ return;
242
+ }
243
+
244
+ if (in_array($path . Scan::DIRECTORY_PATH_FLAG_SEPARATOR . Scan::IS_NON_RECURSIVE, $this->coreDirectories)) {
245
+ return;
246
+ }
247
+
248
+ if ($flag === Scan::IS_NON_RECURSIVE) {
249
+ for ($i = 0, $iMax = count($this->coreDirectories); $i < $iMax; $i++) {
250
+ if ($this->coreDirectories[$i] === $path . Scan::DIRECTORY_PATH_FLAG_SEPARATOR . Scan::IS_RECURSIVE) {
251
+ $this->coreDirectories[$i] = $dirInfo;
252
+ return;
253
+ }
254
+ }
255
+ }
256
+
257
+ $this->coreDirectories[] = $dirInfo;
258
+ }
259
+
260
+ /**
261
+ * Get selected directories according to the flag and filter them
262
+ *
263
+ * @param string $directoriesRequest
264
+ * @param boolean $areDirectoriesIncluded default false
265
+ * @return array
266
+ *
267
+ * @todo find a better place
268
+ */
269
+ public function getSelectedDirectories($directoriesRequest, $areDirectoriesIncluded = false)
270
+ {
271
+ $directories = $this->getWpCoreDirectories();
272
+ if (empty($directoriesRequest) || $directoriesRequest === '') {
273
+ return $directories;
274
+ }
275
+
276
+ if ($areDirectoriesIncluded) {
277
+ $directories = wpstg_urldecode(explode(Scan::DIRECTORIES_SEPARATOR, $directoriesRequest));
278
+ $directories = array_map(function ($directory) {
279
+ return $this->wpRoot . $directory;
280
+ }, $directories);
281
+
282
+ return $directories;
283
+ }
284
+
285
+ $excludedDirectories = wpstg_urldecode(explode(Scan::DIRECTORIES_SEPARATOR, $directoriesRequest));
286
+ $excludedDirectories = array_map(function ($directory) {
287
+ return $this->wpRoot . $directory;
288
+ }, $excludedDirectories);
289
+
290
+ $directories = array_filter($directories, function ($directory) use ($excludedDirectories) {
291
+ $directory = explode(Scan::DIRECTORY_PATH_FLAG_SEPARATOR, $directory)[0];
292
+ foreach ($excludedDirectories as $excludedDirectory) {
293
+ $excludedDirectory = explode(Scan::DIRECTORY_PATH_FLAG_SEPARATOR, $excludedDirectory)[0];
294
+ if ($directory === $excludedDirectory) {
295
+ return false;
296
+ }
297
+ }
298
+
299
+ return true;
300
+ });
301
+
302
+ return array_values($directories);
303
  }
304
  }
Frontend/Frontend.php CHANGED
@@ -67,32 +67,15 @@ class Frontend
67
  $this->resetPermaLinks();
68
 
69
  if ($this->showLoginForm()) {
70
-
71
- $args = [
72
- 'echo' => true,
73
- // Default 'redirect' value takes the user back to the request URI.
74
- 'redirect' => (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
75
- 'form_id' => 'loginform',
76
- 'label_username' => __('Username or Email Address'),
77
- 'label_password' => __('Password'),
78
- 'label_remember' => __('Remember Me'),
79
- 'label_log_in' => __('Log In'),
80
- 'id_username' => 'user_login',
81
- 'id_password' => 'user_pass',
82
- 'id_remember' => 'rememberme',
83
- 'id_submit' => 'wp-submit',
84
- 'remember' => true,
85
- 'value_username' => '',
86
- // Set 'value_remember' to true to default the "Remember me" checkbox to checked.
87
- 'value_remember' => false,
88
- ];
89
-
90
  $login = new LoginForm();
91
  if ($this->accessDenied) {
92
  wp_logout();
93
  $login->setError(__('Access Denied'));
94
  }
95
- $login->renderForm($args);
 
 
 
96
  die();
97
  }
98
  }
@@ -110,7 +93,7 @@ class Frontend
110
  return false;
111
  }
112
 
113
- if (! $this->isStagingSite() ) {
114
  return false;
115
  }
116
 
@@ -136,7 +119,7 @@ class Frontend
136
  }
137
  }
138
 
139
- if( !is_user_logged_in() ) {
140
  return true;
141
  }
142
 
@@ -204,5 +187,4 @@ class Frontend
204
 
205
  update_option("wpstg_rmpermalinks_executed", "true");
206
  }
207
-
208
  }
67
  $this->resetPermaLinks();
68
 
69
  if ($this->showLoginForm()) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  $login = new LoginForm();
71
  if ($this->accessDenied) {
72
  wp_logout();
73
  $login->setError(__('Access Denied'));
74
  }
75
+ $overrides = [
76
+ 'label_username' => __('Username or Email Address'),
77
+ ];
78
+ $login->renderForm($login->getDefaultArguments($overrides));
79
  die();
80
  }
81
  }
93
  return false;
94
  }
95
 
96
+ if (! $this->isStagingSite()) {
97
  return false;
98
  }
99
 
119
  }
120
  }
121
 
122
+ if (!is_user_logged_in()) {
123
  return true;
124
  }
125
 
187
 
188
  update_option("wpstg_rmpermalinks_executed", "true");
189
  }
 
190
  }
Frontend/LoginForm.php CHANGED
@@ -51,7 +51,6 @@ class LoginForm
51
 
52
  // Validate provided password and login
53
  if (wp_check_password($_POST['wpstg-pass'], $user_data->user_pass, $user_data->ID)) {
54
-
55
  $rememberme = isset($_POST['rememberme']) ? true : false;
56
 
57
  wp_set_auth_cookie($user_data->ID, $rememberme);
@@ -125,27 +124,7 @@ class LoginForm
125
  */
126
  private function getLoginForm()
127
  {
128
- $arguments = [
129
- 'echo' => true,
130
- // Default 'redirect' value takes the user back to the request URI.
131
- 'redirect' => (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
132
- 'form_id' => 'loginform',
133
- 'label_username' => __('Username'),
134
- 'label_password' => __('Password'),
135
- 'label_remember' => __('Remember Me'),
136
- 'label_log_in' => __('Log In'),
137
- 'id_username' => 'user_login',
138
- 'id_password' => 'user_pass',
139
- 'id_remember' => 'rememberme',
140
- 'id_submit' => 'wp-submit',
141
- 'remember' => true,
142
- 'value_username' => '',
143
- // Set 'value_remember' to true to default the "Remember me" checkbox to checked.
144
- 'value_remember' => false,
145
- ];
146
-
147
-
148
- $args = empty($this->args) ? $arguments : $this->args;
149
 
150
  // Don't delete! This is used in the views below
151
  $notice = __('Enter your administrator credentials to access the cloned site. (This message will be displayed only once!)', 'wp-staging');
@@ -170,5 +149,42 @@ class LoginForm
170
  $this->error = $error;
171
  }
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
174
-
51
 
52
  // Validate provided password and login
53
  if (wp_check_password($_POST['wpstg-pass'], $user_data->user_pass, $user_data->ID)) {
 
54
  $rememberme = isset($_POST['rememberme']) ? true : false;
55
 
56
  wp_set_auth_cookie($user_data->ID, $rememberme);
124
  */
125
  private function getLoginForm()
126
  {
127
+ $args = empty($this->args) ? $this->getDefaultArguments() : $this->args;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
  // Don't delete! This is used in the views below
130
  $notice = __('Enter your administrator credentials to access the cloned site. (This message will be displayed only once!)', 'wp-staging');
149
  $this->error = $error;
150
  }
151
 
152
+ /**
153
+ * Returns the default set of arguments used to render the Login Form.
154
+ *
155
+ * @since TBD
156
+ *
157
+ * @param array<string,mixed> $overrides A set of values to override the default ones.
158
+ *
159
+ * @return array<string,mixed> The default set of arguments used to render the login form.
160
+ */
161
+ public function getDefaultArguments(array $overrides = [])
162
+ {
163
+ // Default 'redirect' value takes the user back to the request URI.
164
+ $redirect = (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
165
+ $lostPasswordUrl = wp_lostpassword_url($redirect);
166
+ $arguments = wp_parse_args(
167
+ $overrides,
168
+ [
169
+ 'echo' => true,
170
+ 'redirect' => $redirect,
171
+ 'lost_password_url' => $lostPasswordUrl,
172
+ 'form_id' => 'loginform',
173
+ 'label_username' => __('Username'),
174
+ 'label_password' => __('Password'),
175
+ 'label_remember' => __('Remember Me'),
176
+ 'label_log_in' => __('Log In'),
177
+ 'id_username' => 'user_login',
178
+ 'id_password' => 'user_pass',
179
+ 'id_remember' => 'rememberme',
180
+ 'id_submit' => 'wp-submit',
181
+ 'remember' => true,
182
+ 'value_username' => '',
183
+ // Set 'value_remember' to true to default the "Remember me" checkbox to checked.
184
+ 'value_remember' => false,
185
+ ]
186
+ );
187
+
188
+ return $arguments;
189
+ }
190
  }
 
Frontend/LoginNotice.php CHANGED
@@ -54,5 +54,4 @@ class LoginNotice implements TransientInterface
54
 
55
  return $expiredOrNot;
56
  }
57
-
58
  }
54
 
55
  return $expiredOrNot;
56
  }
 
57
  }
Frontend/public/css/wpstg-login-ui.css DELETED
@@ -1,156 +0,0 @@
1
- * {
2
- -webkit-box-sizing: border-box;
3
- box-sizing: border-box;
4
- }
5
-
6
- html {
7
- background: #f1f1f1;
8
- }
9
-
10
- body {
11
- color: #444;
12
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
13
- margin: 0;
14
- }
15
-
16
- .wp-staging-login {
17
- padding: 1rem;
18
- }
19
-
20
- .wp-staging-form {
21
- -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.13);
22
- box-shadow: 0 1px 3px rgba(0,0,0,0.13);
23
- max-width: 500px;
24
- width: 100%;
25
- margin: 3rem auto;
26
- background: #fff;
27
- padding: 1rem;
28
- overflow: hidden;
29
- }
30
-
31
- .form-control {
32
- width: 100%;
33
- border: 1px solid #ced4da;
34
- border-radius: .25rem;
35
- padding: 0.75rem 1rem;
36
- font-size: 14px;
37
- }
38
-
39
- .form-control:focus {
40
- outline: 0;
41
- -webkit-box-shadow: 0 0 0 0.1rem rgba(221,221,221,.35);
42
- box-shadow: 0 0 0 0.1rem rgba(221,221,221,.35);
43
- }
44
-
45
- .form-group {
46
- margin: 0 0 1em;
47
- }
48
-
49
- .form-group label {
50
- margin: 0 0 0.5em;
51
- display: block;
52
- }
53
-
54
- .login-remember input {
55
- margin-top: 0;
56
- vertical-align: middle;
57
- }
58
-
59
- .btn {
60
- background: #f7f7f7;
61
- border: 1px solid #ccc;
62
- color: #555;
63
- display: inline-block;
64
- text-decoration: none;
65
- font-size: 14px;
66
- margin: 0;
67
- padding: 0.65rem 1.1rem;
68
- cursor: pointer;
69
- -webkit-border-radius: 3px;
70
- -webkit-appearance: none;
71
- border-radius: 3px;
72
- white-space: nowrap;
73
- vertical-align: top;
74
- -webkit-transition: -webkit-box-shadow 0.2s ease;
75
- transition: -webkit-box-shadow 0.2s ease;
76
- -o-transition: box-shadow 0.2s ease;
77
- transition: box-shadow 0.2s ease;
78
- transition: box-shadow 0.2s ease, -webkit-box-shadow 0.2s ease;
79
- }
80
-
81
- .btn:hover {
82
- -webkit-box-shadow: 0 0 0 0.1rem rgba(221,221,221,.35);
83
- box-shadow: 0 0 0 0.1rem rgba(221,221,221,.35);
84
- }
85
-
86
- #error-page {
87
- margin-top: 50px;
88
- }
89
-
90
- #error-page p {
91
- font-size: 14px;
92
- line-height: 1.5;
93
- margin: 25px 0 20px;
94
- }
95
-
96
- #error-page code {
97
- font-family: Consolas, Monaco, monospace;
98
- }
99
-
100
- .error-msg {
101
- -webkit-animation: slideIn 0.3s ease;
102
- animation: slideIn 0.3s ease;
103
- color: #ff4c4c;
104
- }
105
-
106
- .password-lost{
107
- padding-top:20px;
108
- }
109
-
110
- .wpstg-text-center {
111
- text-align: center;
112
- }
113
- .wpstg-text-justify {
114
- text-align: justify;
115
- }
116
- .wpstg-text-center img {
117
- margin-top:30px;
118
- }
119
- .wpstg-alert {
120
- padding: 8px;
121
- border: 1px solid transparent;
122
- margin-bottom: 8px;
123
- }
124
- .wpstg-alert > b {
125
- margin-bottom: 8px;
126
- }
127
- .wpstg-alert > p {
128
- margin: 0px;
129
- }
130
- .wpstg-alert.wpstg-alert-info {
131
- color: #fff;
132
- background: #25a1f0;
133
- border-color: #2188c9;
134
- }
135
-
136
- @-webkit-keyframes slideIn {
137
- 0% {
138
- -webkit-transform: translateX(-100%);
139
- transform: translateX(-100%);
140
- }
141
- 100% {
142
- -webkit-transform: translateX(0);
143
- transform: translateX(0);
144
- }
145
- }
146
-
147
- @keyframes slideIn {
148
- 0% {
149
- -webkit-transform: translateX(-100%);
150
- transform: translateX(-100%);
151
- }
152
- 100% {
153
- -webkit-transform: translateX(0);
154
- transform: translateX(0);
155
- }
156
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Frontend/views/header.php CHANGED
@@ -5,7 +5,163 @@
5
  <meta name="viewport" content="width=device-width">
6
  <meta name='robots' content='noindex,follow' />
7
  <title>WordPress &rsaquo; You need to login to access that page</title>
8
- <link rel="stylesheet" type="text/css" href="<?php echo esc_url(plugins_url('public/css/wpstg-login-ui.css', dirname( __FILE__))) ?>">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </head>
10
  <body>
11
-
5
  <meta name="viewport" content="width=device-width">
6
  <meta name='robots' content='noindex,follow' />
7
  <title>WordPress &rsaquo; You need to login to access that page</title>
8
+ <style>
9
+ * {
10
+ -webkit-box-sizing: border-box;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ html {
15
+ background: #f1f1f1;
16
+ }
17
+
18
+ body {
19
+ color: #444;
20
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
21
+ margin: 0;
22
+ }
23
+
24
+ .wp-staging-login {
25
+ padding: 1rem;
26
+ }
27
+
28
+ .wp-staging-form {
29
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.13);
30
+ box-shadow: 0 1px 3px rgba(0,0,0,0.13);
31
+ max-width: 500px;
32
+ width: 100%;
33
+ margin: 3rem auto;
34
+ background: #fff;
35
+ padding: 1rem;
36
+ overflow: hidden;
37
+ }
38
+
39
+ .form-control {
40
+ width: 100%;
41
+ border: 1px solid #ced4da;
42
+ border-radius: .25rem;
43
+ padding: 0.75rem 1rem;
44
+ font-size: 14px;
45
+ }
46
+
47
+ .form-control:focus {
48
+ outline: 0;
49
+ -webkit-box-shadow: 0 0 0 0.1rem rgba(221,221,221,.35);
50
+ box-shadow: 0 0 0 0.1rem rgba(221,221,221,.35);
51
+ }
52
+
53
+ .form-group {
54
+ margin: 0 0 1em;
55
+ }
56
+
57
+ .form-group label {
58
+ margin: 0 0 0.5em;
59
+ display: block;
60
+ }
61
+
62
+ .login-remember input {
63
+ margin-top: 0;
64
+ vertical-align: middle;
65
+ }
66
+
67
+ .btn {
68
+ background: #f7f7f7;
69
+ border: 1px solid #ccc;
70
+ color: #555;
71
+ display: inline-block;
72
+ text-decoration: none;
73
+ font-size: 14px;
74
+ margin: 0;
75
+ padding: 0.65rem 1.1rem;
76
+ cursor: pointer;
77
+ -webkit-border-radius: 3px;
78
+ -webkit-appearance: none;
79
+ border-radius: 3px;
80
+ white-space: nowrap;
81
+ vertical-align: top;
82
+ -webkit-transition: -webkit-box-shadow 0.2s ease;
83
+ transition: -webkit-box-shadow 0.2s ease;
84
+ -o-transition: box-shadow 0.2s ease;
85
+ transition: box-shadow 0.2s ease;
86
+ transition: box-shadow 0.2s ease, -webkit-box-shadow 0.2s ease;
87
+ }
88
+
89
+ .btn:hover {
90
+ -webkit-box-shadow: 0 0 0 0.1rem rgba(221,221,221,.35);
91
+ box-shadow: 0 0 0 0.1rem rgba(221,221,221,.35);
92
+ }
93
+
94
+ #error-page {
95
+ margin-top: 50px;
96
+ }
97
+
98
+ #error-page p {
99
+ font-size: 14px;
100
+ line-height: 1.5;
101
+ margin: 25px 0 20px;
102
+ }
103
+
104
+ #error-page code {
105
+ font-family: Consolas, Monaco, monospace;
106
+ }
107
+
108
+ .error-msg {
109
+ -webkit-animation: slideIn 0.3s ease;
110
+ animation: slideIn 0.3s ease;
111
+ color: #ff4c4c;
112
+ }
113
+
114
+ .password-lost{
115
+ padding-top:20px;
116
+ }
117
+
118
+ .wpstg-text-center {
119
+ text-align: center;
120
+ }
121
+ .wpstg-text-justify {
122
+ text-align: justify;
123
+ }
124
+ .wpstg-text-center img {
125
+ margin-top:30px;
126
+ }
127
+ .wpstg-alert {
128
+ padding: 8px;
129
+ border: 1px solid transparent;
130
+ margin-bottom: 8px;
131
+ }
132
+ .wpstg-alert > b {
133
+ margin-bottom: 8px;
134
+ }
135
+ .wpstg-alert > p {
136
+ margin: 0px;
137
+ }
138
+ .wpstg-alert.wpstg-alert-info {
139
+ color: #fff;
140
+ background: #25a1f0;
141
+ border-color: #2188c9;
142
+ }
143
+
144
+ @-webkit-keyframes slideIn {
145
+ 0% {
146
+ -webkit-transform: translateX(-100%);
147
+ transform: translateX(-100%);
148
+ }
149
+ 100% {
150
+ -webkit-transform: translateX(0);
151
+ transform: translateX(0);
152
+ }
153
+ }
154
+
155
+ @keyframes slideIn {
156
+ 0% {
157
+ -webkit-transform: translateX(-100%);
158
+ transform: translateX(-100%);
159
+ }
160
+ 100% {
161
+ -webkit-transform: translateX(0);
162
+ transform: translateX(0);
163
+ }
164
+ }
165
+ </style>
166
  </head>
167
  <body>
 
Frontend/views/loginForm.php CHANGED
@@ -1,36 +1,36 @@
1
  <main class="wp-staging-login" >
2
  <div class="wpstg-text-center">
3
- <img src="<?php echo esc_url(plugins_url('../Backend/public/img/logo_clean_small_212_25.png', dirname( __FILE__))); ?>" alt="WP Staging Login" />
4
- </div>
5
  <form class="wp-staging-form" name="<?php echo $args['form_id']; ?>" id="<?php echo $args['form_id']; ?>" action="" method="post">
6
  <?php if ($showNotice) { ?>
7
  <div class="wpstg-alert wpstg-alert-info wpstg-text-justify">
8
- <p><?php echo esc_html( $notice ); ?></p>
9
  </div>
10
  <?php } ?>
11
  <div class="form-group login-username">
12
- <label for="<?php echo esc_attr( $args['id_username'] ); ?>"><?php echo esc_html( $args['label_username'] ); ?></label>
13
- <input type="text" name="wpstg-username" id="<?php echo esc_attr( $args['id_username'] ); ?>" class="input form-control" value="<?php echo esc_attr( $args['value_username'] ); ?>" size="20" />
14
  </div>
15
  <div class="form-group login-password">
16
- <label for="<?php echo esc_attr( $args['id_password'] ); ?>"><?php echo esc_html( $args['label_password'] ); ?></label>
17
- <input type="password" name="wpstg-pass" id="<?php echo esc_attr( $args['id_password'] ); ?>" class="input form-control" value="" size="20" />
18
  </div>
19
 
20
  <?php if ($args['remember']) { ?>
21
- <div class="form-group login-remember"><label><input name="rememberme" type="checkbox" id="<?php echo esc_attr( $args['id_remember'] ); ?>" value="forever"<?php echo ( $args['value_remember'] ? ' checked="checked"' : '' ); ?> /> <span><?php echo esc_html( $args['label_remember'] ); ?></span></label></div>
22
  <?php } ?>
23
 
24
  <div class="login-submit">
25
- <button type="submit" name="wpstg-submit" id="<?php echo esc_attr( $args['id_submit'] ); ?>" class="btn" value="<?php echo esc_attr( $args['label_log_in'] ); ?>">Login</button>
26
- <input type="hidden" name="redirect_to" value="<?php echo esc_url( $args['redirect'] ); ?>" />
27
  </div>
28
  <div class="password-lost">
29
- <a href="<?php echo trailingslashit(esc_url( $args['redirect'] )); ?>wp-login.php?action=lostpassword">Lost your password?</a>
30
  </div>
31
 
32
  <p class="error-msg">
33
  <?php echo $this->error; ?>
34
  </p>
35
  </form>
36
- </main>
1
  <main class="wp-staging-login" >
2
  <div class="wpstg-text-center">
3
+ <img src="<?php echo esc_url(WPSTG_PLUGIN_URL . 'assets/img/logo_clean_small_212_25.png'); ?>" alt="WP Staging Login" />
4
+ </div>
5
  <form class="wp-staging-form" name="<?php echo $args['form_id']; ?>" id="<?php echo $args['form_id']; ?>" action="" method="post">
6
  <?php if ($showNotice) { ?>
7
  <div class="wpstg-alert wpstg-alert-info wpstg-text-justify">
8
+ <p><?php echo esc_html($notice); ?></p>
9
  </div>
10
  <?php } ?>
11
  <div class="form-group login-username">
12
+ <label for="<?php echo esc_attr($args['id_username']); ?>"><?php echo esc_html($args['label_username']); ?></label>
13
+ <input type="text" name="wpstg-username" id="<?php echo esc_attr($args['id_username']); ?>" class="input form-control" value="<?php echo esc_attr($args['value_username']); ?>" size="20" />
14
  </div>
15
  <div class="form-group login-password">
16
+ <label for="<?php echo esc_attr($args['id_password']); ?>"><?php echo esc_html($args['label_password']); ?></label>
17
+ <input type="password" name="wpstg-pass" id="<?php echo esc_attr($args['id_password']); ?>" class="input form-control" value="" size="20" />
18
  </div>
19
 
20
  <?php if ($args['remember']) { ?>
21
+ <div class="form-group login-remember"><label><input name="rememberme" type="checkbox" id="<?php echo esc_attr($args['id_remember']); ?>" value="forever"<?php echo ( $args['value_remember'] ? ' checked="checked"' : '' ); ?> /> <span><?php echo esc_html($args['label_remember']); ?></span></label></div>
22
  <?php } ?>
23
 
24
  <div class="login-submit">
25
+ <button type="submit" name="wpstg-submit" id="<?php echo esc_attr($args['id_submit']); ?>" class="btn" value="<?php echo esc_attr($args['label_log_in']); ?>">Login</button>
26
+ <input type="hidden" name="redirect_to" value="<?php echo esc_url($args['redirect']); ?>" />
27
  </div>
28
  <div class="password-lost">
29
+ <a href="<?php echo esc_url($args['lost_password_url']); ?>">Lost your password?</a>
30
  </div>
31
 
32
  <p class="error-msg">
33
  <?php echo $this->error; ?>
34
  </p>
35
  </form>
36
+ </main>
{Backend/public/css → assets/css/src/frontend}/wpstg-admin-bar.css RENAMED
File without changes
{Frontend/public/css → assets/css/src}/wpstg-admin-bar.css RENAMED
File without changes
{Backend/public/css → assets/css/src}/wpstg-admin-feedback.css RENAMED
File without changes
{Backend/public/css → assets/css/src}/wpstg-admin.css RENAMED
@@ -219,18 +219,25 @@
219
  }
220
 
221
  .wpstg-clone {
222
- margin-bottom: 1px;
223
- padding: 10px 10px;
224
  position: relative;
225
- overflow: hidden;
226
  transition: border-color .2s ease-in-out;
227
  background-color: #25a1f0;
 
 
228
  }
229
 
230
  .wpstg-clone.active {
231
  border-color: #1d94cf;
232
  }
233
 
 
 
 
 
 
 
234
  .wpstg-clone-title {
235
  display: inline-block;
236
  font-size: 15px;
@@ -244,17 +251,65 @@
244
  color: #f1f1f1;
245
  }
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  .wpstg-clone-action {
248
- background: #ffffff;
249
- border-left: 1px solid #ccc;
250
- color: #bbb;
251
- padding: 0 5px;
252
- float: right;
253
  text-decoration: none;
254
  position: relative;
255
  transition: color .2s ease-in-out;
256
  }
257
 
 
 
 
 
258
  .wpstg-remove-clone:hover {
259
  color: #ef6d6d;
260
  }
@@ -347,7 +402,7 @@
347
  }
348
 
349
  .wpstg-loader {
350
- content: url('../img/loading.gif');
351
  margin-top: 5px;
352
  display: none;
353
  }
@@ -623,7 +678,8 @@
623
  display: block;
624
  background-color: #FFD0D0;
625
  padding: 20px;
626
- border: 1px solid #fff;
 
627
  max-width: 600px;
628
  margin-top: 10px;
629
  }
@@ -722,7 +778,7 @@
722
  display: inline;
723
  font-size: 12px;
724
  padding: 4px 8px;
725
- /* will be overided due to display: inline
726
  width: 40px; */
727
  }
728
 
@@ -854,8 +910,7 @@
854
  }
855
 
856
  .wpstg-staging-info {
857
- clear: both;
858
- float: left;
859
  color: white;
860
  font-size: 12px;
861
  }
@@ -912,6 +967,18 @@
912
  text-decoration: none;
913
  }
914
 
 
 
 
 
 
 
 
 
 
 
 
 
915
  .wpstg-blue-primary {
916
  display: inline-block;
917
  text-decoration: none;
@@ -1195,7 +1262,7 @@
1195
  visibility: visible;
1196
  }
1197
 
1198
- .wpstg--tooltiptext-snapshots {
1199
  width: 120px;
1200
  top: 100%;
1201
  left: -150%;
@@ -1209,7 +1276,7 @@
1209
  Tooltip top arrow
1210
  */
1211
 
1212
- .wpstg--tooltip .wpstg--tooltiptext-snapshots::after {
1213
  content: " ";
1214
  position: absolute;
1215
  bottom: 100%;
@@ -1236,7 +1303,7 @@ Tooltip top arrow
1236
  margin-bottom: 20px;
1237
  }
1238
 
1239
- #wpstg-snapshot-name {
1240
  font-size: 1.875em;
1241
  font-weight: 600;
1242
  }
@@ -1340,39 +1407,40 @@ body.toplevel_page_wpstg_clone .swal2-modal .wpstg-loader {
1340
  cursor: pointer;
1341
  }
1342
 
1343
- .wpstg--modal--snapshot--import--upload--title {
1344
  color: #505050;
1345
  }
1346
 
1347
- .wpstg--modal--snapshot--import--filesystem,
1348
- .wpstg--modal--snapshot--import--upload--status,
1349
- .wpstg--modal--snapshot--import--upload--container input[type="file"] {
 
1350
  display: none;
1351
  }
1352
 
1353
- #wpstg--snapshots--import--file-list {
1354
  font-size: 14px;
1355
  font-weight: bold;
1356
  }
1357
 
1358
- #wpstg--snapshots--import--file-list-empty {
1359
  color: red;
1360
  }
1361
 
1362
- .wpstg--modal--snapshot--import--filesystem label {
1363
  font-size: 14px;
1364
  }
1365
 
1366
- .wpstg--modal--snapshot--import--filesystem button {
1367
  margin-bottom: 20px;
1368
  }
1369
 
1370
- .wpstg--modal--snapshot--import--upload,
1371
- .wpstg--modal--snapshot--import--filesystem {
1372
  color: #505050;
1373
  }
1374
 
1375
- .wpstg--modal--snapshot--import--upload--container {
1376
  position: relative;
1377
  border-radius: 1em;
1378
  margin: .5em;
@@ -1382,42 +1450,42 @@ body.toplevel_page_wpstg_clone .swal2-modal .wpstg-loader {
1382
  flex-direction: column;
1383
  }
1384
 
1385
- .wpstg--modal--snapshot--import--upload--container .wpstg--uploader {
1386
  display: flex;
1387
  flex-direction: column;
1388
  }
1389
 
1390
- .wpstg--modal--snapshot--import--upload--container.wpstg--has-dragover {
1391
  background-color: #9a9a9a;
1392
  color: white;
1393
  }
1394
 
1395
- .wpstg--modal--snapshot--import--upload--container span.wpstg--drop,
1396
- .wpstg--modal--snapshot--import--upload--container.wpstg--has-dragover span.wpstg--drag,
1397
- span.wpstg--snapshot--import--selected-file,
1398
  span.wpstg--drag-or-upload {
1399
  display: none;
1400
  }
1401
 
1402
- .wpstg--modal--snapshot--import--upload--container.wpstg--has-dragover span.wpstg--drop {
1403
  display: inline-block;
1404
  }
1405
 
1406
- .wpstg--modal--snapshot--import--upload--container input[type='file'] {
1407
  display: none;
1408
  }
1409
 
1410
- .wpstg--modal--snapshot--import--upload--container img {
1411
  width: 4em;
1412
  align-self: center;
1413
  border: none;
1414
  }
1415
 
1416
- .wpstg--modal--snapshot--import--upload--container span {
1417
  margin-top: 1em;
1418
  }
1419
 
1420
- .wpstg--snapshot--import--options>button {
1421
  margin-top: 1em;
1422
  padding: 1em;
1423
  align-self: center;
@@ -1427,7 +1495,7 @@ span.wpstg--drag-or-upload {
1427
  }
1428
 
1429
 
1430
- /*.wpstg--snapshot--import--options.wpstg--show-options > button {
1431
  background: white;
1432
  border-radius: 3px 3px 0 0;
1433
  border: .25em solid #25a1f0;
@@ -1435,17 +1503,17 @@ span.wpstg--drag-or-upload {
1435
  color: #2e3436;
1436
  }*/
1437
 
1438
- .wpstg--snapshot--import--options {
1439
  position: relative;
1440
  display: flex;
1441
  justify-content: center;
1442
  }
1443
 
1444
- .wpstg--snapshot--import--options ul {
1445
  display: none;
1446
  }
1447
 
1448
- .wpstg--snapshot--import--options.wpstg--show-options ul {
1449
  padding: 0;
1450
  margin: 54px 0 0 0;
1451
  display: block;
@@ -1463,20 +1531,20 @@ span.wpstg--drag-or-upload {
1463
  text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
1464
  }
1465
 
1466
- .wpstg--snapshot--import--options.wpstg--show-options ul li {
1467
  border-bottom: .1em solid #25a1f0;
1468
  margin: 0px;
1469
  }
1470
 
1471
- .wpstg--snapshot--import--options.wpstg--show-options ul li:hover {
1472
  background-color: #25a1f0;
1473
  }
1474
 
1475
- .wpstg--snapshot--import--options.wpstg--show-options ul li:last-child {
1476
  border-bottom: none;
1477
  }
1478
 
1479
- .wpstg--snapshot--import--options ul li button {
1480
  cursor: pointer;
1481
  background: none;
1482
  border: none;
@@ -1487,67 +1555,80 @@ span.wpstg--drag-or-upload {
1487
  line-height: 40px;
1488
  }
1489
 
1490
- .wpstg--snapshot--import--options ul li button:hover {
1491
  background-color: #259be6;
1492
  }
1493
 
1494
- .wpstg--modal--snapshot--import--search-replace--info {
1495
- padding: 0 5.5em;
1496
- margin: 1.5em 0;
1497
  display: flex;
1498
  flex-direction: row;
1499
- padding: 0 5em;
1500
  }
1501
 
1502
- .wpstg--modal--snapshot--import--info p {
1503
  text-align: left;
1504
  margin: 0;
1505
  }
1506
 
1507
 
1508
  /* unused class should be removed or commented
1509
- .wpstg--modal--snapshot--import--search-replace--wrapper {
1510
  display: flex;
1511
  flex-direction: row;
1512
  padding: 0 5em;
1513
  }
1514
  */
1515
 
1516
- .wpstg--modal--snapshot--import--search-replace--wrapper button {
1517
  align-self: center;
1518
  }
1519
 
1520
- .wpstg--modal--snapshot--import--search-replace--new {
1521
- margin: 0 1em;
1522
- color: white;
1523
- background-color: #25a1f0;
1524
  border: 0px;
1525
  border-radius: 3px;
1526
  font-size: 18px;
1527
  text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
 
1528
  }
1529
 
1530
- .wpstg--modal--snapshot--import--search-replace--input--container {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1531
  flex: 1;
1532
  display: flex;
1533
  flex-direction: column;
1534
  }
1535
 
1536
- .wpstg--modal--snapshot--import--search-replace--input-group {
1537
  width: 100%;
1538
  border-bottom: 6px solid #f1f1f1;
1539
  margin-bottom: 10px;
1540
  }
1541
 
1542
- .wpstg--modal--snapshot--import--search-replace--input-group input {
1543
- display: block;
1544
- /*margin: 1em 0;*/
1545
- width: 100%;
1546
  line-height: 10px;
1547
  border: 1px solid #dedede;
1548
  border-radius: 3px;
1549
  color: #666;
1550
- padding: 7px;
1551
  margin-bottom: 12px;
1552
  }
1553
 
@@ -1656,16 +1737,16 @@ span.wpstg--drag-or-upload {
1656
  width: 100%;
1657
  }
1658
 
1659
- #wpstg-confirm-snapshot-restore-data {
1660
  margin: 40px;
1661
  text-align: left;
1662
  }
1663
 
1664
- #wpstg-confirm-snapshot-restore-wrapper {
1665
  margin: 30px;
1666
  }
1667
 
1668
- #wpstg-confirm-snapshot-restore-wrapper h3 {
1669
  color: #f56363;
1670
  }
1671
 
@@ -1704,8 +1785,8 @@ span.wpstg--drag-or-upload {
1704
  white-space: nowrap;
1705
  }
1706
 
1707
- .wpstg-edit-clone-data {
1708
- font-size: 10px;
1709
  }
1710
 
1711
  #wpstg_symlink_upload {
@@ -1902,16 +1983,16 @@ span.wpstg--drag-or-upload {
1902
  color: #abc116;
1903
  }
1904
 
1905
- #wpstg-confirm-snapshot-restore-data {
1906
  margin: 40px;
1907
  text-align: left;
1908
  }
1909
 
1910
- #wpstg-confirm-snapshot-restore-wrapper {
1911
  margin: 30px;
1912
  }
1913
 
1914
- #wpstg-confirm-snapshot-restore-wrapper h3 {
1915
  color: #f56363;
1916
  }
1917
 
@@ -1951,3 +2032,136 @@ span.wpstg--drag-or-upload {
1951
  body.toplevel_page_wpstg_clone .swal2-container .swal2-content {
1952
  z-index:2;
1953
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  }
220
 
221
  .wpstg-clone {
222
+ margin-bottom: 3px;
223
+ padding: 16px;
224
  position: relative;
 
225
  transition: border-color .2s ease-in-out;
226
  background-color: #25a1f0;
227
+ border-radius: .25rem;
228
+ box-shadow: 0 0 1px rgba(0,0,0,.125), 0 1px 3px rgba(0,0,0,.2);
229
  }
230
 
231
  .wpstg-clone.active {
232
  border-color: #1d94cf;
233
  }
234
 
235
+ .wpstg-clone-header {
236
+ display: flex;
237
+ justify-content: space-between;
238
+ align-items: center;
239
+ }
240
+
241
  .wpstg-clone-title {
242
  display: inline-block;
243
  font-size: 15px;
251
  color: #f1f1f1;
252
  }
253
 
254
+ .wpstg-clone-actions {
255
+ display: flex;
256
+ align-items: right;
257
+ }
258
+
259
+ .wpstg-dropdown {
260
+ position: relative;
261
+ }
262
+
263
+ .wpstg-clone-actions .wpstg-dropdown-toggler {
264
+ text-decoration: none;
265
+ background: #fff;
266
+ padding: 4px 10px;
267
+ border-radius: 2px;
268
+ font-size: 14px;
269
+ box-shadow: 0 0 1px rgba(0,0,0,.125), 0 1px 3px rgba(0,0,0,.2);
270
+ color: #002648;
271
+ }
272
+
273
+ .wpstg-clone-actions .wpstg-dropdown-toggler:hover {
274
+ background: #002648;
275
+ color: white;
276
+ }
277
+
278
+ .wpstg-dropdown {
279
+ position: relative;
280
+ }
281
+
282
+ .wpstg-dropdown>.wpstg-dropdown-menu {
283
+ background: #fff;
284
+ display: none;
285
+ flex-direction: column;
286
+ position: absolute;
287
+ right: 0;
288
+ top: calc(100% + 4px);
289
+ padding: 8px;
290
+ border-radius: 2px;
291
+ width: 100px;
292
+ box-shadow: 0 0 1px rgba(0,0,0,.125), 0 1px 3px rgba(0,0,0,.2);
293
+ z-index: 1000;
294
+ }
295
+
296
+ .wpstg-dropdown>.wpstg-dropdown-menu.shown {
297
+ display: flex;
298
+ }
299
+
300
  .wpstg-clone-action {
301
+ color: #002648;
302
+ padding: 5px 8px;
303
+ border-radius: 3px;
 
 
304
  text-decoration: none;
305
  position: relative;
306
  transition: color .2s ease-in-out;
307
  }
308
 
309
+ .wpstg-clone-action:hover {
310
+ background: rgba(0,0,0,0.05);
311
+ }
312
+
313
  .wpstg-remove-clone:hover {
314
  color: #ef6d6d;
315
  }
402
  }
403
 
404
  .wpstg-loader {
405
+ content: url('../../img/loading.gif');
406
  margin-top: 5px;
407
  display: none;
408
  }
678
  display: block;
679
  background-color: #FFD0D0;
680
  padding: 20px;
681
+ border-radius: 6px;
682
+ border: 1px solid #FFAAAA;
683
  max-width: 600px;
684
  margin-top: 10px;
685
  }
778
  display: inline;
779
  font-size: 12px;
780
  padding: 4px 8px;
781
+ /* will be overided due to display: inline
782
  width: 40px; */
783
  }
784
 
910
  }
911
 
912
  .wpstg-staging-info {
913
+ margin-top: 8px;
 
914
  color: white;
915
  font-size: 12px;
916
  }
967
  text-decoration: none;
968
  }
969
 
970
+ .wpstg-button.wpstg-button-light {
971
+ background-color: #f8f8f8;
972
+ border: 1px solid #eee;
973
+ color: #333;
974
+ animation: background-color 0.3s;
975
+ }
976
+
977
+ .wpstg-button.wpstg-button-light:hover {
978
+ background-color: #e0e0e0;
979
+ border: 1px solid #e0e0e0;
980
+ }
981
+
982
  .wpstg-blue-primary {
983
  display: inline-block;
984
  text-decoration: none;
1262
  visibility: visible;
1263
  }
1264
 
1265
+ .wpstg--tooltiptext-backups {
1266
  width: 120px;
1267
  top: 100%;
1268
  left: -150%;
1276
  Tooltip top arrow
1277
  */
1278
 
1279
+ .wpstg--tooltip .wpstg--tooltiptext-backups::after {
1280
  content: " ";
1281
  position: absolute;
1282
  bottom: 100%;
1303
  margin-bottom: 20px;
1304
  }
1305
 
1306
+ #wpstg-backup-name {
1307
  font-size: 1.875em;
1308
  font-weight: 600;
1309
  }
1407
  cursor: pointer;
1408
  }
1409
 
1410
+ .wpstg--modal--backup--import--upload--title {
1411
  color: #505050;
1412
  }
1413
 
1414
+ .wpstg--modal--backup--import--filesystem,
1415
+ .wpstg--modal--backup--import--configure,
1416
+ .wpstg--modal--backup--import--upload--status,
1417
+ .wpstg--modal--backup--import--upload--container input[type="file"] {
1418
  display: none;
1419
  }
1420
 
1421
+ #wpstg--backups--import--file-list {
1422
  font-size: 14px;
1423
  font-weight: bold;
1424
  }
1425
 
1426
+ #wpstg--backups--import--file-list-empty {
1427
  color: red;
1428
  }
1429
 
1430
+ .wpstg--modal--backup--import--filesystem label {
1431
  font-size: 14px;
1432
  }
1433
 
1434
+ .wpstg--modal--backup--import--filesystem button {
1435
  margin-bottom: 20px;
1436
  }
1437
 
1438
+ .wpstg--modal--backup--import--upload,
1439
+ .wpstg--modal--backup--import--filesystem {
1440
  color: #505050;
1441
  }
1442
 
1443
+ .wpstg--modal--backup--import--upload--container {
1444
  position: relative;
1445
  border-radius: 1em;
1446
  margin: .5em;
1450
  flex-direction: column;
1451
  }
1452
 
1453
+ .wpstg--modal--backup--import--upload--container .wpstg--uploader {
1454
  display: flex;
1455
  flex-direction: column;
1456
  }
1457
 
1458
+ .wpstg--modal--backup--import--upload--container.wpstg--has-dragover {
1459
  background-color: #9a9a9a;
1460
  color: white;
1461
  }
1462
 
1463
+ .wpstg--modal--backup--import--upload--container span.wpstg--drop,
1464
+ .wpstg--modal--backup--import--upload--container.wpstg--has-dragover span.wpstg--drag,
1465
+ span.wpstg--backup--import--selected-file,
1466
  span.wpstg--drag-or-upload {
1467
  display: none;
1468
  }
1469
 
1470
+ .wpstg--modal--backup--import--upload--container.wpstg--has-dragover span.wpstg--drop {
1471
  display: inline-block;
1472
  }
1473
 
1474
+ .wpstg--modal--backup--import--upload--container input[type='file'] {
1475
  display: none;
1476
  }
1477
 
1478
+ .wpstg--modal--backup--import--upload--container img {
1479
  width: 4em;
1480
  align-self: center;
1481
  border: none;
1482
  }
1483
 
1484
+ .wpstg--modal--backup--import--upload--container span {
1485
  margin-top: 1em;
1486
  }
1487
 
1488
+ .wpstg--backup--import--options>button {
1489
  margin-top: 1em;
1490
  padding: 1em;
1491
  align-self: center;
1495
  }
1496
 
1497
 
1498
+ /*.wpstg--backup--import--options.wpstg--show-options > button {
1499
  background: white;
1500
  border-radius: 3px 3px 0 0;
1501
  border: .25em solid #25a1f0;
1503
  color: #2e3436;
1504
  }*/
1505
 
1506
+ .wpstg--backup--import--options {
1507
  position: relative;
1508
  display: flex;
1509
  justify-content: center;
1510
  }
1511
 
1512
+ .wpstg--backup--import--options ul {
1513
  display: none;
1514
  }
1515
 
1516
+ .wpstg--backup--import--options.wpstg--show-options ul {
1517
  padding: 0;
1518
  margin: 54px 0 0 0;
1519
  display: block;
1531
  text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
1532
  }
1533
 
1534
+ .wpstg--backup--import--options.wpstg--show-options ul li {
1535
  border-bottom: .1em solid #25a1f0;
1536
  margin: 0px;
1537
  }
1538
 
1539
+ .wpstg--backup--import--options.wpstg--show-options ul li:hover {
1540
  background-color: #25a1f0;
1541
  }
1542
 
1543
+ .wpstg--backup--import--options.wpstg--show-options ul li:last-child {
1544
  border-bottom: none;
1545
  }
1546
 
1547
+ .wpstg--backup--import--options ul li button {
1548
  cursor: pointer;
1549
  background: none;
1550
  border: none;
1555
  line-height: 40px;
1556
  }
1557
 
1558
+ .wpstg--backup--import--options ul li button:hover {
1559
  background-color: #259be6;
1560
  }
1561
 
1562
+ .wpstg--modal--backup--import--search-replace--info {
1563
+ margin: 1em 0;
 
1564
  display: flex;
1565
  flex-direction: row;
 
1566
  }
1567
 
1568
+ .wpstg--modal--backup--import--info p {
1569
  text-align: left;
1570
  margin: 0;
1571
  }
1572
 
1573
 
1574
  /* unused class should be removed or commented
1575
+ .wpstg--modal--backup--import--search-replace--wrapper {
1576
  display: flex;
1577
  flex-direction: row;
1578
  padding: 0 5em;
1579
  }
1580
  */
1581
 
1582
+ .wpstg--modal--backup--import--search-replace--wrapper button {
1583
  align-self: center;
1584
  }
1585
 
1586
+ .wpstg--import--advanced-options--button {
 
 
 
1587
  border: 0px;
1588
  border-radius: 3px;
1589
  font-size: 18px;
1590
  text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
1591
+ cursor: pointer;
1592
  }
1593
 
1594
+ .wpstg--modal--backup--import--search-replace--new {
1595
+ color: white;
1596
+ background-color: #25a1f0;
1597
+ }
1598
+
1599
+ .wpstg--modal--backup--import--search-replace--remove {
1600
+ color: white;
1601
+ background-color: #25a1f0;
1602
+ width: 22px;
1603
+ height: 22px;
1604
+ margin-left:5px;
1605
+ }
1606
+
1607
+ .wpstg--modal--backup--import--search-replace--input-group:first-child button {
1608
+ display: none;
1609
+ }
1610
+
1611
+ .wpstg--modal--backup--import--search-replace--input--container {
1612
  flex: 1;
1613
  display: flex;
1614
  flex-direction: column;
1615
  }
1616
 
1617
+ .wpstg--modal--backup--import--search-replace--input-group {
1618
  width: 100%;
1619
  border-bottom: 6px solid #f1f1f1;
1620
  margin-bottom: 10px;
1621
  }
1622
 
1623
+ .wpstg--modal--backup--import--search-replace--input-group input {
1624
+ min-width: 250px;
1625
+ width: calc(50% - 4px - 11px - 5px); /* -4px is half of the padding; -11px is half of the button; -5 is the margin left of the button */
1626
+ display: inline-block;
1627
  line-height: 10px;
1628
  border: 1px solid #dedede;
1629
  border-radius: 3px;
1630
  color: #666;
1631
+ padding: 8px;
1632
  margin-bottom: 12px;
1633
  }
1634
 
1737
  width: 100%;
1738
  }
1739
 
1740
+ #wpstg-confirm-backup-restore-data {
1741
  margin: 40px;
1742
  text-align: left;
1743
  }
1744
 
1745
+ #wpstg-confirm-backup-restore-wrapper {
1746
  margin: 30px;
1747
  }
1748
 
1749
+ #wpstg-confirm-backup-restore-wrapper h3 {
1750
  color: #f56363;
1751
  }
1752
 
1785
  white-space: nowrap;
1786
  }
1787
 
1788
+ #wpstg-db-connect-output #wpstg-db-status {
1789
+ width: 390px;
1790
  }
1791
 
1792
  #wpstg_symlink_upload {
1983
  color: #abc116;
1984
  }
1985
 
1986
+ #wpstg-confirm-backup-restore-data {
1987
  margin: 40px;
1988
  text-align: left;
1989
  }
1990
 
1991
+ #wpstg-confirm-backup-restore-wrapper {
1992
  margin: 30px;
1993
  }
1994
 
1995
+ #wpstg-confirm-backup-restore-wrapper h3 {
1996
  color: #f56363;
1997
  }
1998
 
2032
  body.toplevel_page_wpstg_clone .swal2-container .swal2-content {
2033
  z-index:2;
2034
  }
2035
+
2036
+ div#exportUploadsWithoutDatabaseWarning {
2037
+ font-style: italic;
2038
+ font-size: 0.9rem;
2039
+ margin: 10px;
2040
+ padding: 10px;
2041
+ border: 1px solid #e3e3e3;
2042
+ border-radius: 5px;
2043
+ text-align: center;
2044
+ background-color: #fafafa;
2045
+ }
2046
+
2047
+ .wpstg-advanced-options-dropdown-wrapper {
2048
+ display: none; /* ENABLE WHEN WE HAVE ADVANCED OPTIONS FOR EXPORTING */
2049
+ margin-top: 20px;
2050
+ }
2051
+
2052
+ .wpstg--modal--backup--import--search-replace--wrapper {
2053
+ text-align: left;
2054
+ margin-top: 20px;
2055
+ }
2056
+
2057
+ .wpstg--modal--backup--import--search-replace--new--wrapper {
2058
+ text-align: center;
2059
+ }
2060
+
2061
+ .wpstg-import-backup-contains li {
2062
+ display: inline-block;
2063
+ }
2064
+
2065
+ .wpstg-import-backup-contains li .dashicons{
2066
+ border-radius: 3px;
2067
+ color: #979797;
2068
+ background-color: #e3e3e3;
2069
+ border: 1px solid #c2c2c2;
2070
+ width: 17px;
2071
+ height: 17px;
2072
+ font-size: 17px;
2073
+ }
2074
+
2075
+ .wpstg-import-backup-contains.wpstg-listing-single-backup li .dashicons{
2076
+ padding: 2px;
2077
+ color: #ffffff;
2078
+ background-color: #2896dd;
2079
+ border: 1px solid #0c75b8;
2080
+ }
2081
+
2082
+ .wpstg-import-backup-contains .wpstg--tooltiptext {
2083
+ width: 50px;
2084
+ font-size: x-small;
2085
+ padding: 5px;
2086
+ left: -25px;
2087
+ text-align: center;
2088
+ }
2089
+
2090
+ .wpstg-import-backup-contains-title {
2091
+ display: inline-block;
2092
+ }
2093
+
2094
+ ul.wpstg-import-backup-contains {
2095
+ display: inline-block;
2096
+ }
2097
+
2098
+ .wpstg-import-backup-name {
2099
+ display: inline-block;
2100
+ font-weight: bold;
2101
+ }
2102
+
2103
+ .wpstg-backup-more-info-toggle {
2104
+ font-size: x-small;
2105
+ display: inline-block;
2106
+ font-style: italic;
2107
+ cursor:pointer;
2108
+ }
2109
+
2110
+ .wpstg-backup-more-info-toggle::selection {
2111
+ background:none;
2112
+ }
2113
+
2114
+ ul.wpstg-import-backup-more-info {
2115
+ font-size: 14px;
2116
+ text-align: left;
2117
+ margin-bottom: 30px;
2118
+ margin-top: 10px;
2119
+ background-color: #f6f6f6;
2120
+ border: 1px solid #878787;
2121
+ border-radius: 3px;
2122
+ padding: 7px;
2123
+ cursor: pointer;
2124
+ }
2125
+
2126
+ ul.wpstg-import-backup-more-info:hover {
2127
+ background-color: #def2ff;
2128
+ border: 1px solid #25a1f0;
2129
+ }
2130
+
2131
+ ul.wpstg-import-backup-more-info li {
2132
+ height: 20px;
2133
+ }
2134
+
2135
+ .wpstg-backup-list ul ul {
2136
+ margin-block-start: 1em;
2137
+ margin-block-end: 1em;
2138
+ }
2139
+
2140
+ .wpstg-push-confirmation-message {
2141
+ text-align: justify;
2142
+ }
2143
+
2144
+ .wpstg-settings-row {
2145
+ padding-top: 10px;
2146
+ padding-bottom: 10px;
2147
+ }
2148
+
2149
+ .wpstg-settings-title {
2150
+ font-weight: 600;
2151
+ }
2152
+
2153
+ .wpstg-settings-form-group {
2154
+ display: flex;
2155
+ align-items: center;
2156
+ }
2157
+
2158
+ .wpstg-settings-form-group > .wpstg-settings-message {
2159
+ width: 30%;
2160
+ padding: 0px;
2161
+ margin: 0px;
2162
+ margin-top: 7px;
2163
+ }
2164
+
2165
+ .wpstg-swal-push-container.swal2-container {
2166
+ z-index: 9995;
2167
+ }
assets/css/vendor/notyf.min.css ADDED
@@ -0,0 +1 @@
 
1
+ @-webkit-keyframes notyf-fadeinup{0%{opacity:0;transform:translateY(25%)}to{opacity:1;transform:translateY(0)}}@keyframes notyf-fadeinup{0%{opacity:0;transform:translateY(25%)}to{opacity:1;transform:translateY(0)}}@-webkit-keyframes notyf-fadeinleft{0%{opacity:0;transform:translateX(25%)}to{opacity:1;transform:translateX(0)}}@keyframes notyf-fadeinleft{0%{opacity:0;transform:translateX(25%)}to{opacity:1;transform:translateX(0)}}@-webkit-keyframes notyf-fadeoutright{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(25%)}}@keyframes notyf-fadeoutright{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(25%)}}@-webkit-keyframes notyf-fadeoutdown{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(25%)}}@keyframes notyf-fadeoutdown{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(25%)}}@-webkit-keyframes ripple{0%{transform:scale(0) translateY(-45%) translateX(13%)}to{transform:scale(1) translateY(-45%) translateX(13%)}}@keyframes ripple{0%{transform:scale(0) translateY(-45%) translateX(13%)}to{transform:scale(1) translateY(-45%) translateX(13%)}}.notyf{position:fixed;top:0;left:0;height:100%;width:100%;color:#fff;z-index:9999;display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-end;pointer-events:none;box-sizing:border-box;padding:20px}.notyf__icon--error,.notyf__icon--success{height:21px;width:21px;background:#fff;border-radius:50%;display:block;margin:0 auto;position:relative}.notyf__icon--error:after,.notyf__icon--error:before{content:"";background:currentColor;display:block;position:absolute;width:3px;border-radius:3px;left:9px;height:12px;top:5px}.notyf__icon--error:after{transform:rotate(-45deg)}.notyf__icon--error:before{transform:rotate(45deg)}.notyf__icon--success:after,.notyf__icon--success:before{content:"";background:currentColor;display:block;position:absolute;width:3px;border-radius:3px}.notyf__icon--success:after{height:6px;transform:rotate(-45deg);top:9px;left:6px}.notyf__icon--success:before{height:11px;transform:rotate(45deg);top:5px;left:10px}.notyf__toast{display:block;overflow:hidden;pointer-events:auto;-webkit-animation:notyf-fadeinup .3s ease-in forwards;animation:notyf-fadeinup .3s ease-in forwards;box-shadow:0 3px 7px 0 rgba(0,0,0,.25);position:relative;padding:0 15px;border-radius:2px;max-width:300px;transform:translateY(25%);box-sizing:border-box;flex-shrink:0}.notyf__toast--disappear{transform:translateY(0);-webkit-animation:notyf-fadeoutdown .3s forwards;animation:notyf-fadeoutdown .3s forwards;-webkit-animation-delay:.25s;animation-delay:.25s}.notyf__toast--disappear .notyf__icon,.notyf__toast--disappear .notyf__message{-webkit-animation:notyf-fadeoutdown .3s forwards;animation:notyf-fadeoutdown .3s forwards;opacity:1;transform:translateY(0)}.notyf__toast--disappear .notyf__dismiss{-webkit-animation:notyf-fadeoutright .3s forwards;animation:notyf-fadeoutright .3s forwards;opacity:1;transform:translateX(0)}.notyf__toast--disappear .notyf__message{-webkit-animation-delay:.05s;animation-delay:.05s}.notyf__toast--upper{margin-bottom:20px}.notyf__toast--lower{margin-top:20px}.notyf__toast--dismissible .notyf__wrapper{padding-right:30px}.notyf__ripple{height:400px;width:400px;position:absolute;transform-origin:bottom right;right:0;top:0;border-radius:50%;transform:scale(0) translateY(-51%) translateX(13%);z-index:5;-webkit-animation:ripple .4s ease-out forwards;animation:ripple .4s ease-out forwards}.notyf__wrapper{display:flex;align-items:center;padding-top:17px;padding-bottom:17px;padding-right:15px;border-radius:3px;position:relative;z-index:10}.notyf__icon{width:22px;text-align:center;font-size:1.3em;opacity:0;-webkit-animation:notyf-fadeinup .3s forwards;animation:notyf-fadeinup .3s forwards;-webkit-animation-delay:.3s;animation-delay:.3s;margin-right:13px}.notyf__dismiss{position:absolute;top:0;right:0;height:100%;width:26px;margin-right:-15px;-webkit-animation:notyf-fadeinleft .3s forwards;animation:notyf-fadeinleft .3s forwards;-webkit-animation-delay:.35s;animation-delay:.35s;opacity:0}.notyf__dismiss-btn{background-color:rgba(0,0,0,.25);border:none;cursor:pointer;transition:opacity .2s ease,background-color .2s ease;outline:none;opacity:.35;height:100%;width:100%}.notyf__dismiss-btn:after,.notyf__dismiss-btn:before{content:"";background:#fff;height:12px;width:2px;border-radius:3px;position:absolute;left:calc(50% - 1px);top:calc(50% - 5px)}.notyf__dismiss-btn:after{transform:rotate(-45deg)}.notyf__dismiss-btn:before{transform:rotate(45deg)}.notyf__dismiss-btn:hover{opacity:.7;background-color:rgba(0,0,0,.15)}.notyf__dismiss-btn:active{opacity:.8}.notyf__message{vertical-align:middle;position:relative;opacity:0;-webkit-animation:notyf-fadeinup .3s forwards;animation:notyf-fadeinup .3s forwards;-webkit-animation-delay:.25s;animation-delay:.25s;line-height:1.5em}@media only screen and (max-width:480px){.notyf{padding:0}.notyf__ripple{height:600px;width:600px;-webkit-animation-duration:.5s;animation-duration:.5s}.notyf__toast{max-width:none;border-radius:0;box-shadow:0 -2px 7px 0 rgba(0,0,0,.13);width:100%}.notyf__dismiss{width:56px}}
Backend/public/vendor/sweetalert2/wordpress-admin.css → assets/css/vendor/sweetalert2.css RENAMED
File without changes
Backend/public/vendor/sweetalert2/wordpress-admin.min.css → assets/css/vendor/sweetalert2.min.css RENAMED
File without changes
{Backend/public → assets}/img/admin_dashboard.png RENAMED
File without changes
{Backend/public → assets}/img/contribute.txt RENAMED
File without changes
{Backend/public → assets}/img/loading.gif RENAMED
File without changes
{Backend/public → assets}/img/loading.svg RENAMED
File without changes
{Backend/public → assets}/img/loading_v1.gif RENAMED
File without changes
{Backend/public → assets}/img/logo_clean_small_212_25.png RENAMED
File without changes
{Backend/public → assets}/img/tail-spin.svg RENAMED
File without changes
{Backend/public → assets}/img/thumbnail.jpg RENAMED
File without changes
{Backend/public → assets}/img/upload.svg RENAMED
File without changes
{Backend/public → assets}/img/wpstaging-banner200x400-tryout.gif RENAMED
File without changes
{Backend/public → assets}/img/wpstaging-banner200x400.gif RENAMED
File without changes
assets/js/dist/wpstg-admin-beta.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function ($) {
2
+ $('.wpstg_hide_beta').on('click', function (e) {
3
+ e.preventDefault();
4
+ window.WPStaging.ajax({
5
+ action: 'wpstg_hide_beta'
6
+ }, function (response) {
7
+ if (true === response) {
8
+ $('.wpstg_beta_notice').slideUp('slow');
9
+ return true;
10
+ }
11
+
12
+ alert('Unexpected message received. This might mean the data was not saved ' + 'and you might see this message again');
13
+ });
14
+ });
15
+ });
assets/js/dist/wpstg-admin-plugins.js ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var strict;
2
+ jQuery(document).ready(function ($) {
3
+ /**
4
+ * DEACTIVATION FEEDBACK FORM
5
+ */
6
+ // show overlay when clicked on "deactivate"
7
+ wpstg_deactivate_link = $('.wp-admin.plugins-php tr[data-slug="wp-staging"] .row-actions .deactivate a');
8
+ wpstg_deactivate_link_url = wpstg_deactivate_link.attr('href');
9
+ wpstg_deactivate_link.on('click', function (e) {
10
+ e.preventDefault(); // only show feedback form once per 30 days
11
+
12
+ var c_value = wpstg_admin_get_cookie('wpstg_hide_feedback');
13
+
14
+ if (c_value === undefined) {
15
+ $('#wpstg-feedback-overlay').show();
16
+ } else {
17
+ // click on the link
18
+ window.location.href = wpstg_deactivate_link_url;
19
+ }
20
+ }); // show text fields
21
+
22
+ $('#wpstg-feedback-content input[type="radio"]').on('click', function () {
23
+ // show text field if there is one
24
+ $(this).parents('li').next('li').children('input[type="text"], textarea').show();
25
+ }); // send form or close it
26
+
27
+ $('#wpstg-feedback-content .button').on('click', function (e) {
28
+ e.preventDefault(); // set cookie for 30 days
29
+
30
+ var exdate = new Date();
31
+ exdate.setSeconds(exdate.getSeconds() + 2592000);
32
+ document.cookie = 'wpstg_hide_feedback=1; expires=' + exdate.toUTCString() + '; path=/';
33
+ $('#wpstg-feedback-overlay').hide();
34
+
35
+ if ('wpstg-feedback-submit' === this.id) {
36
+ // Send form data
37
+ $.ajax({
38
+ type: 'POST',
39
+ url: ajaxurl,
40
+ dataType: 'json',
41
+ data: {
42
+ action: 'wpstg_send_feedback',
43
+ data: $('#wpstg-feedback-content form').serialize()
44
+ },
45
+ complete: function complete(MLHttpRequest, textStatus, errorThrown) {
46
+ // deactivate the plugin and close the popup
47
+ $('#wpstg-feedback-overlay').remove();
48
+ window.location.href = wpstg_deactivate_link_url;
49
+ }
50
+ });
51
+ } else {
52
+ $('#wpstg-feedback-overlay').remove();
53
+ window.location.href = wpstg_deactivate_link_url;
54
+ }
55
+ }); // close form without doing anything
56
+
57
+ $('.wpstg-feedback-not-deactivate').on('click', function (e) {
58
+ $('#wpstg-feedback-overlay').hide();
59
+ });
60
+
61
+ function wpstg_admin_get_cookie(name) {
62
+ var i;
63
+ var x;
64
+ var y;
65
+ var wpstg_cookies = document.cookie.split(';');
66
+
67
+ for (i = 0; i < wpstg_cookies.length; i++) {
68
+ x = wpstg_cookies[i].substr(0, wpstg_cookies[i].indexOf('='));
69
+ y = wpstg_cookies[i].substr(wpstg_cookies[i].indexOf('=') + 1);
70
+ x = x.replace(/^\s+|\s+$/g, '');
71
+
72
+ if (x === name) {
73
+ return unescape(y);
74
+ }
75
+ }
76
+ }
77
+ });
assets/js/dist/wpstg-admin-poll.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function ($) {
2
+ $('.wpstg_hide_poll').on('click', function (e) {
3
+ e.preventDefault();
4
+ window.WPStaging.ajax({
5
+ action: 'wpstg_hide_poll'
6
+ }, function (response) {
7
+ if (true === response) {
8
+ $('.wpstg_poll').slideUp('fast');
9
+ return true;
10
+ } else {
11
+ alert('Unexpected message received. This might mean the data was not saved ' + 'and you might see this message again.');
12
+ }
13
+ });
14
+ });
15
+ });
assets/js/dist/wpstg-admin-rating.js ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var wpstgTimesWaited = 0;
2
+ /*
3
+ Let's wait for jQuery to be available to show the rating.
4
+ We need it to dispatch AJAX requests.
5
+ */
6
+
7
+ var wpstgWaitForJQuery = setInterval(function () {
8
+ if (wpstgTimesWaited > 100) {
9
+ // Give up waiting.
10
+ clearInterval(wpstgWaitForJQuery);
11
+ }
12
+
13
+ if (typeof jQuery != 'undefined') {
14
+ wpstgRegisterRatingEvents();
15
+ clearInterval(wpstgWaitForJQuery);
16
+ }
17
+
18
+ wpstgTimesWaited = wpstgTimesWaited + 1;
19
+ }, 100);
20
+
21
+ function wpstgRegisterRatingEvents() {
22
+ // Show the rating once jQuery is loaded.
23
+ jQuery('.wpstg_fivestar').show();
24
+ /**
25
+ * Dispatch the request to hide the video after user clicks to rate the plugin.
26
+ */
27
+
28
+ jQuery(document).on('click', '#wpstg_clicked_deserved_it', function (e) {
29
+ jQuery.ajax({
30
+ url: ajaxurl,
31
+ type: 'POST',
32
+ data: {
33
+ action: 'wpstg_hide_rating'
34
+ },
35
+ error: function error(xhr, textStatus, errorThrown) {
36
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
37
+ console.log(textStatus);
38
+ alert('Unknown error. Please get in contact with us to solve it support@wp-staging.com');
39
+ },
40
+ success: function success(data) {
41
+ jQuery('.wpstg_fivestar').slideUp('fast');
42
+ return true;
43
+ },
44
+ statusCode: {
45
+ 404: function _() {
46
+ alert('Something went wrong; can\'t find ajax request URL! Please get in contact with us to solve it support@wp-staging.com');
47
+ },
48
+ 500: function _() {
49
+ alert('Something went wrong; internal server error while processing the request! Please get in contact with us to solve it support@wp-staging.com');
50
+ }
51
+ }
52
+ });
53
+ });
54
+ jQuery('.wpstg_hide_rating').on('click', function (e) {
55
+ e.preventDefault();
56
+ jQuery.ajax({
57
+ url: ajaxurl,
58
+ type: 'POST',
59
+ data: {
60
+ action: 'wpstg_hide_rating'
61
+ },
62
+ error: function error(xhr, textStatus, errorThrown) {
63
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
64
+ console.log(textStatus);
65
+ alert('Unknown error. Please get in contact with us to solve it support@wp-staging.com');
66
+ },
67
+ success: function success(data) {
68
+ jQuery('.wpstg_fivestar').slideUp('fast');
69
+ return true;
70
+ },
71
+ statusCode: {
72
+ 404: function _() {
73
+ alert('Something went wrong; can\'t find ajax request URL! Please get in contact with us to solve it support@wp-staging.com');
74
+ },
75
+ 500: function _() {
76
+ alert('Something went wrong; internal server error while processing the request! Please get in contact with us to solve it support@wp-staging.com');
77
+ }
78
+ }
79
+ });
80
+ });
81
+ jQuery('.wpstg_rate_later').on('click', function (e) {
82
+ e.preventDefault();
83
+ jQuery.ajax({
84
+ url: ajaxurl,
85
+ type: 'POST',
86
+ data: {
87
+ action: 'wpstg_hide_later'
88
+ },
89
+ error: function error(xhr, textStatus, errorThrown) {
90
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
91
+ console.log(textStatus);
92
+ alert('Unknown error. Please get in contact with us to solve it support@wp-staging.com');
93
+ },
94
+ success: function success(data) {
95
+ jQuery('.wpstg_fivestar').slideUp('fast');
96
+ return true;
97
+ },
98
+ statusCode: {
99
+ 404: function _() {
100
+ alert('Something went wrong; can\'t find ajax request URL! Please get in contact with us to solve it support@wp-staging.com');
101
+ },
102
+ 500: function _() {
103
+ alert('Something went wrong; internal server error while processing the request! Please get in contact with us to solve it support@wp-staging.com');
104
+ }
105
+ }
106
+ });
107
+ });
108
+ }
109
+
110
+ document.styleSheets[0].insertRule('@media only screen and (max-width:600px){.wpstg-welcome-box{display:block !important}.wpstg-welcome-video-container{width:100% !important;height:auto !important}.wpstg-welcome-text{padding-left:8px !important}}', '');
111
+ document.addEventListener('DOMContentLoaded', function () {
112
+ var player;
113
+ var accepted = wpstgYouTubeConfig.accepted;
114
+ var playerPlaceholder = document.getElementById('welcomeNoticeFree');
115
+ playerPlaceholder.addEventListener('click', function () {
116
+ if (!accepted) {
117
+ var message = wpstgYouTubeConfig.message + '\n \n' + wpstgYouTubeConfig.regards;
118
+ var conf = confirm(message);
119
+
120
+ if (conf) {
121
+ accepted = true;
122
+ wpstgFetchVideo();
123
+ }
124
+ }
125
+ });
126
+ });
127
+
128
+ function wpstgFetchVideo() {
129
+ var tag = document.createElement('script');
130
+ tag.src = 'https://www.youtube.com/iframe_api';
131
+ var firstScriptTag = document.getElementsByTagName('script')[0];
132
+ firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
133
+ }
134
+
135
+ function onYouTubeIframeAPIReady() {
136
+ player = new YT.Player('welcomeNoticeFree', {
137
+ height: '225',
138
+ width: '400',
139
+ videoId: 'fsC9ZvbRQ5Y',
140
+ playerVars: {
141
+ rel: 0,
142
+ showinfo: 0,
143
+ ecver: 2
144
+ },
145
+ events: {
146
+ 'onReady': onPlayerReady
147
+ }
148
+ });
149
+ }
150
+
151
+ function onPlayerReady(event) {
152
+ event.target.playVideo();
153
+ }
assets/js/dist/wpstg-admin.js ADDED
@@ -0,0 +1,2837 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); }
2
+
3
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
+
5
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
6
+
7
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
8
+
9
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
10
+
11
+ import WpstgCloneStaging from './wpstg-clone-staging.js';
12
+
13
+ var WPStaging = function ($) {
14
+ var that = {
15
+ isCancelled: false,
16
+ isFinished: false,
17
+ getLogs: false,
18
+ time: 1,
19
+ executionTime: false,
20
+ progressBar: 0
21
+ };
22
+ var cache = {
23
+ elements: []
24
+ };
25
+ var timeout;
26
+ var ajaxSpinner;
27
+ /**
28
+ * Get / Set Cache for Selector
29
+ * @param {String} selector
30
+ * @return {*}
31
+ */
32
+
33
+ cache.get = function (selector) {
34
+ // It is already cached!
35
+ if ($.inArray(selector, cache.elements) !== -1) {
36
+ return cache.elements[selector];
37
+ } // Create cache and return
38
+
39
+
40
+ cache.elements[selector] = jQuery(selector);
41
+ return cache.elements[selector];
42
+ };
43
+ /**
44
+ * Refreshes given cache
45
+ * @param {String} selector
46
+ */
47
+
48
+
49
+ cache.refresh = function (selector) {
50
+ selector.elements[selector] = jQuery(selector);
51
+ };
52
+ /**
53
+ * Show and Log Error Message
54
+ * @param {String} message
55
+ */
56
+
57
+
58
+ var showError = function showError(message) {
59
+ cache.get('#wpstg-try-again').css('display', 'inline-block');
60
+ cache.get('#wpstg-cancel-cloning').text('Reset');
61
+ cache.get('#wpstg-resume-cloning').show();
62
+ cache.get('#wpstg-error-wrapper').show();
63
+ cache.get('#wpstg-error-details').show().html(message);
64
+ cache.get('#wpstg-removing-clone').removeClass('loading');
65
+ cache.get('.wpstg-loader').hide();
66
+ $('.wpstg--modal--process--generic-problem').show().html(message);
67
+ };
68
+ /**
69
+ * Show warning during cloning or push process when closing tab or browser, or changing page
70
+ * @param {beforeunload} event
71
+ * @return {null}
72
+ */
73
+
74
+
75
+ var warnIfClosingDuringProcess = function warnIfClosingDuringProcess(event) {
76
+ // Only some browsers show the message below, most say something like "Changes you made may not be saved" (Chrome) or "You have unsaved changes. Exit?"
77
+ event.returnValue = 'You MUST leave this window open while cloning/pushing. Please wait...';
78
+ return null;
79
+ };
80
+ /**
81
+ *
82
+ * @param obj
83
+ * @return {boolean}
84
+ */
85
+
86
+
87
+ function isEmpty(obj) {
88
+ for (var prop in obj) {
89
+ if (obj.hasOwnProperty(prop)) {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ return true;
95
+ }
96
+ /**
97
+ *
98
+ * @param response the error object
99
+ * @param prependMessage Overwrite default error message at beginning
100
+ * @param appendMessage Overwrite default error message at end
101
+ * @returns void
102
+ */
103
+
104
+
105
+ var showAjaxFatalError = function showAjaxFatalError(response, prependMessage, appendMessage) {
106
+ prependMessage = prependMessage ? prependMessage + '<br/><br/>' : 'Something went wrong! <br/><br/>';
107
+ appendMessage = appendMessage ? appendMessage + '<br/><br/>' : '<br/><br/>Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.';
108
+
109
+ if (response === false) {
110
+ showError(prependMessage + ' Error: No response.' + appendMessage);
111
+ window.removeEventListener('beforeunload', warnIfClosingDuringProcess);
112
+ return;
113
+ }
114
+
115
+ if (typeof response.error !== 'undefined' && response.error) {
116
+ console.error(response.message);
117
+ showError(prependMessage + ' Error: ' + response.message + appendMessage);
118
+ window.removeEventListener('beforeunload', warnIfClosingDuringProcess);
119
+ return;
120
+ }
121
+ };
122
+ /**
123
+ *
124
+ * @param response
125
+ * @return {{ok}|*}
126
+ */
127
+
128
+
129
+ var handleFetchErrors = function handleFetchErrors(response) {
130
+ if (!response.ok) {
131
+ showError('Error: ' + response.status + ' - ' + response.statusText + '. Please try again or contact support.');
132
+ }
133
+
134
+ return response;
135
+ };
136
+ /** Hide and reset previous thrown visible errors */
137
+
138
+
139
+ var resetErrors = function resetErrors() {
140
+ cache.get('#wpstg-error-details').hide().html('');
141
+ };
142
+
143
+ var slugify = function slugify(url) {
144
+ return url.toString().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/\s+/g, '-').replace(/&/g, '-and-').replace(/[^a-z0-9\-]/g, '').replace(/-+/g, '-').replace(/^-*/, '').replace(/-*$/, '');
145
+ };
146
+ /**
147
+ * Common Elements
148
+ */
149
+
150
+
151
+ var elements = function elements() {
152
+ var $workFlow = cache.get('#wpstg-workflow');
153
+ var isAllChecked = true;
154
+ var urlSpinner = ajaxurl.replace('/admin-ajax.php', '') + '/images/spinner';
155
+ var timer;
156
+
157
+ if (2 < window.devicePixelRatio) {
158
+ urlSpinner += '-2x';
159
+ }
160
+
161
+ urlSpinner += '.gif';
162
+ ajaxSpinner = '<img src=\'\'' + urlSpinner + '\' alt=\'\' class=\'ajax-spinner general-spinner\' />';
163
+
164
+ var getBaseValues = function getBaseValues() {
165
+ var path = $('#wpstg-use-target-dir').data('base-path');
166
+ var uri = $('#wpstg-use-target-hostname').data('base-uri');
167
+ return {
168
+ path: path
169
+ };
170
+ };
171
+
172
+ $workFlow // Check / Un-check All Database Tables New
173
+ .on('click', '.wpstg-button-unselect', function (e) {
174
+ e.preventDefault();
175
+
176
+ if (false === isAllChecked) {
177
+ console.log('true');
178
+ cache.get('#wpstg_select_tables_cloning .wpstg-db-table').prop('selected', 'selected');
179
+ cache.get('.wpstg-button-unselect').text('Unselect All');
180
+ cache.get('.wpstg-db-table-checkboxes').prop('checked', true);
181
+ isAllChecked = true;
182
+ } else {
183
+ console.log('false');
184
+ cache.get('#wpstg_select_tables_cloning .wpstg-db-table').prop('selected', false);
185
+ cache.get('.wpstg-button-unselect').text('Select All');
186
+ cache.get('.wpstg-db-table-checkboxes').prop('checked', false);
187
+ isAllChecked = false;
188
+ }
189
+ })
190
+ /**
191
+ * Select tables with certain tbl prefix | NEW
192
+ * @param obj e
193
+ * @returns {undefined}
194
+ */
195
+ .on('click', '.wpstg-button-select', function (e) {
196
+ e.preventDefault();
197
+ $('#wpstg_select_tables_cloning .wpstg-db-table').each(function () {
198
+ if (wpstg.isMultisite == 1) {
199
+ if ($(this).attr('name').match('^' + wpstg.tblprefix + '([^0-9])_*')) {
200
+ $(this).prop('selected', 'selected');
201
+ } else {
202
+ $(this).prop('selected', false);
203
+ }
204
+ }
205
+
206
+ if (wpstg.isMultisite == 0) {
207
+ if ($(this).attr('name').match('^' + wpstg.tblprefix)) {
208
+ $(this).prop('selected', 'selected');
209
+ } else {
210
+ $(this).prop('selected', false);
211
+ }
212
+ }
213
+ });
214
+ }) // Expand Directories
215
+ .on('click', '.wpstg-expand-dirs', function (e) {
216
+ e.preventDefault();
217
+ var $this = $(this);
218
+
219
+ if (!$this.hasClass('disabled')) {
220
+ $this.siblings('.wpstg-subdir').slideToggle();
221
+ }
222
+ }) // When a directory checkbox is Selected
223
+ .on('change', 'input.wpstg-check-dir', function () {
224
+ var $directory = $(this).parent('.wpstg-dir');
225
+
226
+ if (this.checked) {
227
+ $directory.parents('.wpstg-dir').children('.wpstg-check-dir').prop('checked', true);
228
+ $directory.find('.wpstg-expand-dirs').removeClass('disabled');
229
+ $directory.find('.wpstg-subdir .wpstg-check-dir').prop('checked', true);
230
+ } else {
231
+ $directory.find('.wpstg-dir .wpstg-check-dir').prop('checked', false);
232
+ $directory.find('.wpstg-expand-dirs, .wpstg-check-subdirs').addClass('disabled');
233
+ $directory.find('.wpstg-check-subdirs').data('action', 'check').text('check');
234
+ }
235
+ }) // When a directory name is Selected
236
+ .on('change', 'href.wpstg-check-dir', function () {
237
+ var $directory = $(this).parent('.wpstg-dir');
238
+
239
+ if (this.checked) {
240
+ $directory.parents('.wpstg-dir').children('.wpstg-check-dir').prop('checked', true);
241
+ $directory.find('.wpstg-expand-dirs').removeClass('disabled');
242
+ $directory.find('.wpstg-subdir .wpstg-check-dir').prop('checked', true);
243
+ } else {
244
+ $directory.find('.wpstg-dir .wpstg-check-dir').prop('checked', false);
245
+ $directory.find('.wpstg-expand-dirs, .wpstg-check-subdirs').addClass('disabled');
246
+ $directory.find('.wpstg-check-subdirs').data('action', 'check').text('check');
247
+ }
248
+ }) // Check the max length of the clone name and if the clone name already exists
249
+ .on('keyup', '#wpstg-new-clone-id', function () {
250
+ // Hide previous errors
251
+ document.getElementById('wpstg-error-details').style.display = 'none'; // This request was already sent, clear it up!
252
+
253
+ if ('number' === typeof timer) {
254
+ clearInterval(timer);
255
+ }
256
+
257
+ var cloneID = this.value;
258
+ timer = setTimeout(function () {
259
+ ajax({
260
+ action: 'wpstg_check_clone',
261
+ accessToken: wpstg.accessToken,
262
+ nonce: wpstg.nonce,
263
+ cloneID: cloneID
264
+ }, function (response) {
265
+ if (response.status === 'success') {
266
+ cache.get('#wpstg-new-clone-id').removeClass('wpstg-error-input');
267
+ cache.get('#wpstg-start-cloning').removeAttr('disabled');
268
+ cache.get('#wpstg-clone-id-error').text('').hide();
269
+ } else {
270
+ cache.get('#wpstg-new-clone-id').addClass('wpstg-error-input');
271
+ cache.get('#wpstg-start-cloning').prop('disabled', true);
272
+ cache.get('#wpstg-clone-id-error').text(response.message).show();
273
+ }
274
+ });
275
+ }, 500);
276
+ }) // Restart cloning process
277
+ .on('click', '#wpstg-start-cloning', function () {
278
+ resetErrors();
279
+ that.isCancelled = false;
280
+ that.getLogs = false;
281
+ that.progressBar = 0;
282
+ }).on('input', '#wpstg-new-clone-id', function () {
283
+ if ($('#wpstg-clone-directory').length < 1) {
284
+ return;
285
+ }
286
+
287
+ var slug = slugify(this.value);
288
+ var $targetDir = $('#wpstg-use-target-dir');
289
+ var $targetUri = $('#wpstg-use-target-hostname');
290
+ var path = $targetDir.data('base-path');
291
+ var uri = $targetUri.data('base-uri');
292
+
293
+ if (path) {
294
+ path = path.replace(/\/+$/g, '') + '/' + slug + '/';
295
+ }
296
+
297
+ if (uri) {
298
+ uri = uri.replace(/\/+$/g, '') + '/' + slug;
299
+ }
300
+
301
+ $('.wpstg-use-target-dir--value').text(path);
302
+ $('.wpstg-use-target-hostname--value').text(uri);
303
+ $targetDir.attr('data-path', path);
304
+ $targetUri.attr('data-uri', uri);
305
+ $('#wpstg_clone_dir').attr('placeholder', path);
306
+ $('#wpstg_clone_hostname').attr('placeholder', uri);
307
+ }).on('input', '#wpstg_clone_hostname', function () {
308
+ if ($(this).val() === '' || validateTargetHost()) {
309
+ $('#wpstg_clone_hostname_error').remove();
310
+ return;
311
+ }
312
+
313
+ if (!validateTargetHost() && !$('#wpstg_clone_hostname_error').length) {
314
+ $('#wpstg-clone-directory tr:last-of-type').after('<tr><td>&nbsp;</td><td><p id="wpstg_clone_hostname_error" style="color: red;">&nbsp;Invalid host name. Please provide it in a format like http://example.com</p></td></tr>');
315
+ }
316
+ });
317
+ cloneActions();
318
+ };
319
+ /* @returns {boolean} */
320
+
321
+
322
+ var validateTargetHost = function validateTargetHost() {
323
+ var the_domain = $('#wpstg_clone_hostname').val();
324
+
325
+ if (the_domain === '') {
326
+ return true;
327
+ }
328
+
329
+ var reg = /^http(s)?:\/\/.*$/;
330
+
331
+ if (reg.test(the_domain) === false) {
332
+ return false;
333
+ }
334
+
335
+ return true;
336
+ };
337
+ /**
338
+ * Clone actions
339
+ */
340
+
341
+
342
+ var cloneActions = function cloneActions() {
343
+ var $workFlow = cache.get('#wpstg-workflow');
344
+ $workFlow // Cancel cloning
345
+ .on('click', '#wpstg-cancel-cloning', function () {
346
+ if (!confirm('Are you sure you want to cancel cloning process?')) {
347
+ return false;
348
+ }
349
+
350
+ var $this = $(this);
351
+ $('#wpstg-try-again, #wpstg-home-link').hide();
352
+ $this.prop('disabled', true);
353
+ that.isCancelled = true;
354
+ that.progressBar = 0;
355
+ $('#wpstg-processing-status').text('Please wait...this can take up a while.');
356
+ $('.wpstg-loader, #wpstg-show-log-button').hide();
357
+ $this.parent().append(ajaxSpinner);
358
+ cancelCloning();
359
+ }) // Resume cloning
360
+ .on('click', '#wpstg-resume-cloning', function () {
361
+ resetErrors();
362
+ var $this = $(this);
363
+ $('#wpstg-try-again, #wpstg-home-link').hide();
364
+ that.isCancelled = false;
365
+ $('#wpstg-processing-status').text('Try to resume cloning process...');
366
+ $('#wpstg-error-details').hide();
367
+ $('.wpstg-loader').show();
368
+ $this.parent().append(ajaxSpinner);
369
+ that.startCloning();
370
+ }) // Cancel update cloning
371
+ .on('click', '#wpstg-cancel-cloning-update', function () {
372
+ resetErrors();
373
+ var $this = $(this);
374
+ $('#wpstg-try-again, #wpstg-home-link').hide();
375
+ $this.prop('disabled', true);
376
+ that.isCancelled = true;
377
+ $('#wpstg-cloning-result').text('Please wait...this can take up a while.');
378
+ $('.wpstg-loader, #wpstg-show-log-button').hide();
379
+ $this.parent().append(ajaxSpinner);
380
+ cancelCloningUpdate();
381
+ }) // Restart cloning
382
+ .on('click', '#wpstg-restart-cloning', function () {
383
+ resetErrors();
384
+ var $this = $(this);
385
+ $('#wpstg-try-again, #wpstg-home-link').hide();
386
+ $this.prop('disabled', true);
387
+ that.isCancelled = true;
388
+ $('#wpstg-cloning-result').text('Please wait...this can take up a while.');
389
+ $('.wpstg-loader, #wpstg-show-log-button').hide();
390
+ $this.parent().append(ajaxSpinner);
391
+ restart();
392
+ }) // Delete clone - confirmation
393
+ .on('click', '.wpstg-remove-clone[data-clone]', function (e) {
394
+ resetErrors();
395
+ e.preventDefault();
396
+ var $existingClones = cache.get('#wpstg-existing-clones');
397
+ $workFlow.removeClass('active');
398
+ cache.get('.wpstg-loader').show();
399
+ ajax({
400
+ action: 'wpstg_confirm_delete_clone',
401
+ accessToken: wpstg.accessToken,
402
+ nonce: wpstg.nonce,
403
+ clone: $(this).data('clone')
404
+ }, function (response) {
405
+ cache.get('#wpstg-removing-clone').html(response);
406
+ $existingClones.children('img').remove();
407
+ cache.get('.wpstg-loader').hide();
408
+ $('html, body').animate({
409
+ // This logic is meant to be a "scrollBottom"
410
+ scrollTop: $('#wpstg-remove-clone').offset().top - $(window).height() + $('#wpstg-remove-clone').height() + 50
411
+ }, 100);
412
+ }, 'HTML');
413
+ }) // Delete clone - confirmed
414
+ .on('click', '#wpstg-remove-clone', function (e) {
415
+ resetErrors();
416
+ e.preventDefault();
417
+ cache.get('#wpstg-removing-clone').addClass('loading');
418
+ cache.get('.wpstg-loader').show();
419
+ deleteClone($(this).data('clone'));
420
+ }) // Cancel deleting clone
421
+ .on('click', '#wpstg-cancel-removing', function (e) {
422
+ e.preventDefault();
423
+ $('.wpstg-clone').removeClass('active');
424
+ cache.get('#wpstg-removing-clone').html('');
425
+ }) // Update
426
+ .on('click', '.wpstg-execute-clone', function (e) {
427
+ e.preventDefault();
428
+ var clone = $(this).data('clone');
429
+ $workFlow.addClass('loading');
430
+ ajax({
431
+ action: 'wpstg_scanning',
432
+ clone: clone,
433
+ accessToken: wpstg.accessToken,
434
+ nonce: wpstg.nonce
435
+ }, function (response) {
436
+ if (response.length < 1) {
437
+ showError('Something went wrong! Error: No response. Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.');
438
+ }
439
+
440
+ $workFlow.removeClass('loading').html(response); // register check disk space function for clone update process.
441
+
442
+ checkDiskSpace();
443
+ that.switchStep(2);
444
+ }, 'HTML');
445
+ }) // Reset Clone
446
+ .on('click', '.wpstg-reset-clone', function (e) {
447
+ e.preventDefault();
448
+ var clone = $(this).data('clone');
449
+ Swal.fire({
450
+ title: '',
451
+ icon: 'warning',
452
+ html: 'Do you really want to reset this staging site with the current state of the production site? <br> <span style="color:red;">This will delete all your modifications!</span>',
453
+ width: '650px',
454
+ focusConfirm: false,
455
+ customClass: {
456
+ confirmButton: 'wpstg-confirm-reset-clone'
457
+ },
458
+ confirmButtonText: 'Reset Clone',
459
+ showCancelButton: true
460
+ }).then(function (result) {
461
+ if (result.value) {
462
+ resetClone(clone);
463
+ }
464
+ });
465
+ return;
466
+ });
467
+ };
468
+ /**
469
+ * Ajax Requests
470
+ * @param Object data
471
+ * @param Function callback
472
+ * @param string dataType
473
+ * @param bool showErrors
474
+ * @param int tryCount
475
+ * @param float incrementRatio
476
+ */
477
+
478
+
479
+ var ajax = function ajax(data, callback, dataType, showErrors, tryCount, incrementRatio) {
480
+ if (incrementRatio === void 0) {
481
+ incrementRatio = null;
482
+ }
483
+
484
+ if ('undefined' === typeof dataType) {
485
+ dataType = 'json';
486
+ }
487
+
488
+ if (false !== showErrors) {
489
+ showErrors = true;
490
+ }
491
+
492
+ tryCount = 'undefined' === typeof tryCount ? 0 : tryCount;
493
+ var retryLimit = 10;
494
+ var retryTimeout = 10000 * tryCount;
495
+ incrementRatio = parseInt(incrementRatio);
496
+
497
+ if (!isNaN(incrementRatio)) {
498
+ retryTimeout *= incrementRatio;
499
+ }
500
+
501
+ $.ajax({
502
+ url: ajaxurl + '?action=wpstg_processing&_=' + Date.now() / 1000,
503
+ type: 'POST',
504
+ dataType: dataType,
505
+ cache: false,
506
+ data: data,
507
+ error: function error(xhr, textStatus, errorThrown) {
508
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus); // try again after 10 seconds
509
+
510
+ tryCount++;
511
+
512
+ if (tryCount <= retryLimit) {
513
+ setTimeout(function () {
514
+ ajax(data, callback, dataType, showErrors, tryCount, incrementRatio);
515
+ return;
516
+ }, retryTimeout);
517
+ } else {
518
+ var errorCode = 'undefined' === typeof xhr.status ? 'Unknown' : xhr.status;
519
+ showError('Fatal Error: ' + errorCode + ' Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.');
520
+ }
521
+ },
522
+ success: function success(data) {
523
+ if ('function' === typeof callback) {
524
+ callback(data);
525
+ }
526
+ },
527
+ statusCode: {
528
+ 404: function _() {
529
+ if (tryCount >= retryLimit) {
530
+ showError('Error 404 - Can\'t find ajax request URL! Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.');
531
+ }
532
+ },
533
+ 500: function _() {
534
+ if (tryCount >= retryLimit) {
535
+ showError('Fatal Error 500 - Internal server error while processing the request! Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.');
536
+ }
537
+ },
538
+ 504: function _() {
539
+ if (tryCount > retryLimit) {
540
+ showError('Error 504 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ');
541
+ }
542
+ },
543
+ 502: function _() {
544
+ if (tryCount >= retryLimit) {
545
+ showError('Error 502 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ');
546
+ }
547
+ },
548
+ 503: function _() {
549
+ if (tryCount >= retryLimit) {
550
+ showError('Error 503 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ');
551
+ }
552
+ },
553
+ 429: function _() {
554
+ if (tryCount >= retryLimit) {
555
+ showError('Error 429 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ');
556
+ }
557
+ },
558
+ 403: function _() {
559
+ if (tryCount >= retryLimit) {
560
+ showError('Refresh page or login again! The process should be finished successfully. \n\ ');
561
+ }
562
+ }
563
+ }
564
+ });
565
+ };
566
+ /**
567
+ * Next / Previous Step Clicks to Navigate Through Staging Job
568
+ */
569
+
570
+
571
+ var stepButtons = function stepButtons() {
572
+ var $workFlow = cache.get('#wpstg-workflow');
573
+ $workFlow // Next Button
574
+ .on('click', '.wpstg-next-step-link', function (e) {
575
+ e.preventDefault();
576
+ var $this = $(this);
577
+ var isScan = false;
578
+
579
+ if ($('#wpstg_clone_hostname').length && !validateTargetHost()) {
580
+ $('#wpstg_clone_hostname').focus();
581
+ return false;
582
+ }
583
+
584
+ if ($this.data('action') === 'wpstg_update' || $this.data('action') === 'wpstg_reset') {
585
+ // Update / Reset Clone - confirmed
586
+ var onlyUpdateMessage = '';
587
+
588
+ if ($this.data('action') === 'wpstg_update') {
589
+ onlyUpdateMessage = ' \n\nExclude all tables and folders you do not want to overwrite, first! \n\nDo not cancel the updating process! This can break your staging site. \n\n\Create a backup of your staging website before you proceed.';
590
+ }
591
+
592
+ if (!confirm('STOP! This will overwrite your staging site with all selected data from the production site! This should be used only if you want to clone again your production site. Are you sure you want to do this?' + onlyUpdateMessage)) {
593
+ return false;
594
+ }
595
+ } // Button is disabled
596
+
597
+
598
+ if ($this.attr('disabled')) {
599
+ return false;
600
+ }
601
+
602
+ if ($this.data('action') === 'wpstg_cloning') {
603
+ // Verify External Database If Checked and Not Skipped
604
+ if ($('#wpstg-ext-db').is(':checked')) {
605
+ verifyExternalDatabase($this, $workFlow);
606
+ return;
607
+ }
608
+ }
609
+
610
+ proceedCloning($this, $workFlow);
611
+ }) // Previous Button
612
+ .on('click', '.wpstg-prev-step-link', function (e) {
613
+ e.preventDefault();
614
+ cache.get('.wpstg-loader').removeClass('wpstg-finished');
615
+ cache.get('.wpstg-loader').hide();
616
+ loadOverview();
617
+ });
618
+ };
619
+ /**
620
+ * Get Included (Checked) Database Tables
621
+ * @return {Array}
622
+ */
623
+
624
+
625
+ var getIncludedTables = function getIncludedTables() {
626
+ var includedTables = [];
627
+ $('#wpstg_select_tables_cloning option:selected').each(function () {
628
+ includedTables.push(this.value);
629
+ });
630
+ return includedTables;
631
+ };
632
+ /**
633
+ * Get Excluded (Unchecked) Database Tables
634
+ * Not used anymore!
635
+ * @return {Array}
636
+ */
637
+
638
+
639
+ var getExcludedTables = function getExcludedTables() {
640
+ var excludedTables = [];
641
+ $('.wpstg-db-table input:not(:checked)').each(function () {
642
+ excludedTables.push(this.name);
643
+ });
644
+ return excludedTables;
645
+ };
646
+ /**
647
+ * Get Included Directories
648
+ * @return {string}
649
+ */
650
+
651
+
652
+ var getIncludedDirectories = function getIncludedDirectories() {
653
+ var includedDirectories = [];
654
+ $('.wpstg-dir input:checked.wpstg-root').each(function () {
655
+ var $this = $(this);
656
+ includedDirectories.push(encodeURIComponent($this.val()));
657
+ });
658
+ return includedDirectories.join(wpstg.settings.directorySeparator);
659
+ };
660
+ /**
661
+ * Get Excluded Directories
662
+ * @return {string}
663
+ */
664
+
665
+
666
+ var getExcludedDirectories = function getExcludedDirectories() {
667
+ var excludedDirectories = [];
668
+ $('.wpstg-dir input:not(:checked).wpstg-root').each(function () {
669
+ var $this = $(this);
670
+ excludedDirectories.push(encodeURIComponent($this.val()));
671
+ });
672
+ return excludedDirectories.join(wpstg.settings.directorySeparator);
673
+ };
674
+ /**
675
+ * Get included extra directories of the root level
676
+ * All directories except wp-content, wp-admin, wp-includes
677
+ * @return {string}
678
+ */
679
+
680
+
681
+ var getIncludedExtraDirectories = function getIncludedExtraDirectories() {
682
+ // Add directories from the root level
683
+ var extraDirectories = [];
684
+ $('.wpstg-dir input:checked.wpstg-extra').each(function () {
685
+ var $this = $(this);
686
+ extraDirectories.push(encodeURIComponent($this.val()));
687
+ }); // Add any other custom selected extra directories
688
+
689
+ if (!$('#wpstg_extraDirectories').val()) {
690
+ return extraDirectories.join(wpstg.settings.directorySeparator);
691
+ }
692
+
693
+ var extraCustomDirectories = encodeURIComponent($('#wpstg_extraDirectories').val().split(/\r?\n/));
694
+ return extraDirectories.concat(extraCustomDirectories).join(wpstg.settings.directorySeparator);
695
+ };
696
+ /**
697
+ * Verify External Database for Cloning
698
+ */
699
+
700
+
701
+ var verifyExternalDatabase = function verifyExternalDatabase($this, workflow) {
702
+ cache.get('.wpstg-loader').show();
703
+ ajax({
704
+ action: 'wpstg_database_verification',
705
+ accessToken: wpstg.accessToken,
706
+ nonce: wpstg.nonce,
707
+ databaseUser: cache.get('#wpstg_db_username').val(),
708
+ databasePassword: cache.get('#wpstg_db_password').val(),
709
+ databaseServer: cache.get('#wpstg_db_server').val(),
710
+ databaseDatabase: cache.get('#wpstg_db_database').val()
711
+ }, function (response) {
712
+ // Undefined Error
713
+ if (false === response) {
714
+ showError('Something went wrong! Error: No response.' + 'Please try again. If that does not help, ' + '<a href=\'https://wp-staging.com/support/\' target=\'_blank\'>open a support ticket</a> ');
715
+ cache.get('.wpstg-loader').hide();
716
+ return;
717
+ } // Throw Error
718
+
719
+
720
+ if ('undefined' === typeof response.success) {
721
+ showError('Something went wrong! Error: Invalid response.' + 'Please try again. If that does not help, ' + '<a href=\'https://wp-staging.com/support/\' target=\'_blank\'>open a support ticket</a> ');
722
+ cache.get('.wpstg-loader').hide();
723
+ return;
724
+ }
725
+
726
+ if (response.success) {
727
+ cache.get('.wpstg-loader').hide();
728
+ proceedCloning($this, workflow);
729
+ return;
730
+ }
731
+
732
+ if (response.error_type === 'comparison') {
733
+ cache.get('.wpstg-loader').hide();
734
+ var render = '<table style="width: 100%;"><thead><tr><th>Property</th><th>Production DB</th><th>Staging DB</th><th>Status</th></tr></thead><tbody>';
735
+ response.checks.forEach(function (x) {
736
+ var icon = '<i style="color: #00ff00">✔</i>';
737
+
738
+ if (x.production !== x.staging) {
739
+ icon = '<i style="color: #ff0000">❌</i>';
740
+ }
741
+
742
+ render += '<tr><td>' + x.name + '</td><td>' + x.production + '</td><td>' + x.staging + '</td><td>' + icon + '</td></tr>';
743
+ });
744
+ render += '</tbody></table><p>Note: Some mySQL properties do not match. You may proceed but the staging site may not work as expected.</p>';
745
+ Swal.fire({
746
+ title: 'Different Database Properties',
747
+ icon: 'warning',
748
+ html: render,
749
+ width: '650px',
750
+ focusConfirm: false,
751
+ confirmButtonText: 'Proceed Anyway',
752
+ showCancelButton: true
753
+ }).then(function (result) {
754
+ if (result.value) {
755
+ proceedCloning($this, workflow);
756
+ }
757
+ });
758
+ return;
759
+ }
760
+
761
+ Swal.fire({
762
+ title: 'Different Database Properties',
763
+ icon: 'error',
764
+ html: response.message,
765
+ focusConfirm: true,
766
+ confirmButtonText: 'Ok',
767
+ showCancelButton: false
768
+ });
769
+ cache.get('.wpstg-loader').hide();
770
+ }, 'json', false);
771
+ };
772
+ /**
773
+ * Get Cloning Step Data
774
+ */
775
+
776
+
777
+ var getCloningData = function getCloningData() {
778
+ if ('wpstg_cloning' !== that.data.action && 'wpstg_update' !== that.data.action && 'wpstg_reset' !== that.data.action) {
779
+ return;
780
+ }
781
+
782
+ that.data.cloneID = $('#wpstg-new-clone-id').val() || new Date().getTime().toString(); // Remove this to keep &_POST[] small otherwise mod_security will throw error 404
783
+ // that.data.excludedTables = getExcludedTables();
784
+
785
+ var includedDirectories = getIncludedDirectories();
786
+ var excludedDirectories = getExcludedDirectories(); // only send that one whose size is low
787
+ // same reason as above to keep request size small
788
+
789
+ if (includedDirectories.length <= excludedDirectories.length) {
790
+ that.data.includedDirectories = includedDirectories;
791
+ that.data.areDirectoriesIncluded = true;
792
+ } else {
793
+ that.data.excludedDirectories = excludedDirectories;
794
+ that.data.areDirectoriesIncluded = false;
795
+ }
796
+
797
+ that.data.includedTables = getIncludedTables();
798
+ that.data.extraDirectories = getIncludedExtraDirectories();
799
+ that.data.databaseServer = $('#wpstg_db_server').val();
800
+ that.data.databaseUser = $('#wpstg_db_username').val();
801
+ that.data.databasePassword = $('#wpstg_db_password').val();
802
+ that.data.databaseDatabase = $('#wpstg_db_database').val();
803
+ that.data.databasePrefix = $('#wpstg_db_prefix').val();
804
+ var cloneDir = $('#wpstg_clone_dir').val();
805
+ that.data.cloneDir = encodeURIComponent($.trim(cloneDir));
806
+ that.data.cloneHostname = $('#wpstg_clone_hostname').val();
807
+ that.data.emailsAllowed = $('#wpstg_allow_emails').is(':checked');
808
+ that.data.uploadsSymlinked = $('#wpstg_symlink_upload').is(':checked');
809
+ that.data.cleanPluginsThemes = $('#wpstg-clean-plugins-themes').is(':checked');
810
+ that.data.cleanUploadsDir = $('#wpstg-clean-uploads').is(':checked');
811
+ };
812
+
813
+ var proceedCloning = function proceedCloning($this, workflow) {
814
+ // Add loading overlay
815
+ workflow.addClass('loading'); // Prepare data
816
+
817
+ that.data = {
818
+ action: $this.data('action'),
819
+ accessToken: wpstg.accessToken,
820
+ nonce: wpstg.nonce
821
+ }; // Cloning data
822
+
823
+ getCloningData();
824
+ console.log(that.data);
825
+ sendCloningAjax(workflow);
826
+ };
827
+
828
+ var sendCloningAjax = function sendCloningAjax(workflow) {
829
+ // Send ajax request
830
+ ajax(that.data, function (response) {
831
+ // Undefined Error
832
+ if (false === response) {
833
+ showError('Something went wrong!<br/><br/> Go to WP Staging > Settings and lower \'File Copy Limit\' and \'DB Query Limit\'. Also set \'CPU Load Priority to low \'' + 'and try again. If that does not help, ' + '<a href=\'https://wp-staging.com/support/\' target=\'_blank\'>open a support ticket</a> ');
834
+ }
835
+
836
+ if (response.length < 1) {
837
+ showError('Something went wrong! No response. Go to WP Staging > Settings and lower \'File Copy Limit\' and \'DB Query Limit\'. Also set \'CPU Load Priority to low \'' + 'and try again. If that does not help, ' + '<a href=\'https://wp-staging.com/support/\' target=\'_blank\'>open a support ticket</a> ');
838
+ } // Styling of elements
839
+
840
+
841
+ workflow.removeClass('loading').html(response);
842
+
843
+ if (that.data.action === 'wpstg_scanning') {
844
+ that.switchStep(2);
845
+ } else if (that.data.action === 'wpstg_cloning' || that.data.action === 'wpstg_update' || that.data.action === 'wpstg_reset') {
846
+ that.switchStep(3);
847
+ } // Start cloning
848
+
849
+
850
+ that.startCloning();
851
+ }, 'HTML');
852
+ };
853
+
854
+ var resetClone = function resetClone(clone) {
855
+ that.data = {
856
+ action: 'wpstg_reset',
857
+ accessToken: wpstg.accessToken,
858
+ nonce: wpstg.nonce,
859
+ cloneID: clone
860
+ };
861
+ var $workFlow = cache.get('#wpstg-workflow');
862
+ sendCloningAjax($workFlow);
863
+ };
864
+ /**
865
+ * Loads Overview (first step) of Staging Job
866
+ */
867
+
868
+
869
+ var loadOverview = function loadOverview() {
870
+ var $workFlow = cache.get('#wpstg-workflow');
871
+ $workFlow.addClass('loading');
872
+ ajax({
873
+ action: 'wpstg_overview',
874
+ accessToken: wpstg.accessToken,
875
+ nonce: wpstg.nonce
876
+ }, function (response) {
877
+ if (response.length < 1) {
878
+ showError('Something went wrong! No response. Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report.');
879
+ }
880
+
881
+ var $currentStep = cache.get('.wpstg-current-step'); // Styling of elements
882
+
883
+ $workFlow.removeClass('loading').html(response);
884
+ }, 'HTML');
885
+ that.switchStep(1);
886
+ cache.get('.wpstg-step3-cloning').show();
887
+ cache.get('.wpstg-step3-pushing').hide();
888
+ };
889
+ /**
890
+ * Load Tabs
891
+ */
892
+
893
+
894
+ var tabs = function tabs() {
895
+ cache.get('#wpstg-workflow').on('click', '.wpstg-tab-header', function (e) {
896
+ e.preventDefault();
897
+ var $this = $(this);
898
+ var $section = cache.get($this.data('id'));
899
+ $this.toggleClass('expand');
900
+ $section.slideToggle();
901
+
902
+ if ($this.hasClass('expand')) {
903
+ $this.find('.wpstg-tab-triangle').html('&#9660;');
904
+ } else {
905
+ $this.find('.wpstg-tab-triangle').html('&#9658;');
906
+ }
907
+ });
908
+ };
909
+ /**
910
+ * Delete Clone
911
+ * @param {String} clone
912
+ */
913
+
914
+
915
+ var deleteClone = function deleteClone(clone) {
916
+ var deleteDir = $('#deleteDirectory:checked').data('deletepath');
917
+ ajax({
918
+ action: 'wpstg_delete_clone',
919
+ clone: clone,
920
+ accessToken: wpstg.accessToken,
921
+ nonce: wpstg.nonce,
922
+ excludedTables: getExcludedTables(),
923
+ deleteDir: deleteDir
924
+ }, function (response) {
925
+ if (response) {
926
+ showAjaxFatalError(response); // Finished
927
+
928
+ if ('undefined' !== typeof response["delete"] && (response["delete"] === 'finished' || response["delete"] === 'unfinished')) {
929
+ cache.get('#wpstg-removing-clone').removeClass('loading').html('');
930
+
931
+ if (response["delete"] === 'finished') {
932
+ $('.wpstg-clone#' + clone).remove();
933
+ }
934
+
935
+ if ($('.wpstg-clone').length < 1) {
936
+ cache.get('#wpstg-existing-clones').find('h3').text('');
937
+ }
938
+
939
+ cache.get('.wpstg-loader').hide();
940
+ return;
941
+ }
942
+ } // continue
943
+
944
+
945
+ if (true !== response) {
946
+ deleteClone(clone);
947
+ return;
948
+ }
949
+ });
950
+ };
951
+ /**
952
+ * Cancel Cloning Process
953
+ */
954
+
955
+
956
+ var cancelCloning = function cancelCloning() {
957
+ that.timer('stop');
958
+
959
+ if (true === that.isFinished) {
960
+ return true;
961
+ }
962
+
963
+ ajax({
964
+ action: 'wpstg_cancel_clone',
965
+ clone: that.data.cloneID,
966
+ accessToken: wpstg.accessToken,
967
+ nonce: wpstg.nonce
968
+ }, function (response) {
969
+ if (response && 'undefined' !== typeof response["delete"] && response["delete"] === 'finished') {
970
+ cache.get('.wpstg-loader').hide(); // Load overview
971
+
972
+ loadOverview();
973
+ return;
974
+ }
975
+
976
+ if (true !== response) {
977
+ // continue
978
+ cancelCloning();
979
+ return;
980
+ } // Load overview
981
+
982
+
983
+ loadOverview();
984
+ });
985
+ };
986
+ /**
987
+ * Cancel Cloning Process
988
+ */
989
+
990
+
991
+ var cancelCloningUpdate = function cancelCloningUpdate() {
992
+ if (true === that.isFinished) {
993
+ return true;
994
+ }
995
+
996
+ ajax({
997
+ action: 'wpstg_cancel_update',
998
+ clone: that.data.cloneID,
999
+ accessToken: wpstg.accessToken,
1000
+ nonce: wpstg.nonce
1001
+ }, function (response) {
1002
+ if (response && 'undefined' !== typeof response["delete"] && response["delete"] === 'finished') {
1003
+ // Load overview
1004
+ loadOverview();
1005
+ return;
1006
+ }
1007
+
1008
+ if (true !== response) {
1009
+ // continue
1010
+ cancelCloningUpdate();
1011
+ return;
1012
+ } // Load overview
1013
+
1014
+
1015
+ loadOverview();
1016
+ });
1017
+ };
1018
+ /**
1019
+ * Cancel Cloning Process
1020
+ */
1021
+
1022
+
1023
+ var restart = function restart() {
1024
+ if (true === that.isFinished) {
1025
+ return true;
1026
+ }
1027
+
1028
+ ajax({
1029
+ action: 'wpstg_restart',
1030
+ // clone: that.data.cloneID,
1031
+ accessToken: wpstg.accessToken,
1032
+ nonce: wpstg.nonce
1033
+ }, function (response) {
1034
+ if (response && 'undefined' !== typeof response["delete"] && response["delete"] === 'finished') {
1035
+ // Load overview
1036
+ loadOverview();
1037
+ return;
1038
+ }
1039
+
1040
+ if (true !== response) {
1041
+ // continue
1042
+ cancelCloningUpdate();
1043
+ return;
1044
+ } // Load overview
1045
+
1046
+
1047
+ loadOverview();
1048
+ });
1049
+ };
1050
+ /**
1051
+ * Scroll the window log to bottom
1052
+ * @return void
1053
+ */
1054
+
1055
+
1056
+ var logscroll = function logscroll() {
1057
+ var $div = cache.get('.wpstg-log-details');
1058
+
1059
+ if ('undefined' !== typeof $div[0]) {
1060
+ $div.scrollTop($div[0].scrollHeight);
1061
+ }
1062
+ };
1063
+ /**
1064
+ * Append the log to the logging window
1065
+ * @param string log
1066
+ * @return void
1067
+ */
1068
+
1069
+
1070
+ var getLogs = function getLogs(log) {
1071
+ if (log != null && 'undefined' !== typeof log) {
1072
+ if (log.constructor === Array) {
1073
+ $.each(log, function (index, value) {
1074
+ if (value === null) {
1075
+ return;
1076
+ }
1077
+
1078
+ if (value.type === 'ERROR') {
1079
+ cache.get('.wpstg-log-details').append('<span style="color:red;">[' + value.type + ']</span>-' + '[' + value.date + '] ' + value.message + '</br>');
1080
+ } else {
1081
+ cache.get('.wpstg-log-details').append('[' + value.type + ']-' + '[' + value.date + '] ' + value.message + '</br>');
1082
+ }
1083
+ });
1084
+ } else {
1085
+ cache.get('.wpstg-log-details').append('[' + log.type + ']-' + '[' + log.date + '] ' + log.message + '</br>');
1086
+ }
1087
+ }
1088
+
1089
+ logscroll();
1090
+ };
1091
+ /**
1092
+ * Check diskspace
1093
+ * @return string json
1094
+ */
1095
+
1096
+
1097
+ var checkDiskSpace = function checkDiskSpace() {
1098
+ cache.get('#wpstg-check-space').on('click', function (e) {
1099
+ cache.get('.wpstg-loader').show();
1100
+ console.log('check disk space');
1101
+ ajax({
1102
+ action: 'wpstg_check_disk_space',
1103
+ accessToken: wpstg.accessToken,
1104
+ nonce: wpstg.nonce
1105
+ }, function (response) {
1106
+ if (false === response) {
1107
+ cache.get('#wpstg-clone-id-error').text('Can not detect required disk space').show();
1108
+ cache.get('.wpstg-loader').hide();
1109
+ return;
1110
+ } // Show required disk space
1111
+
1112
+
1113
+ cache.get('#wpstg-clone-id-error').html('Estimated necessary disk space: ' + response.usedspace + '<br> <span style="color:#444;">Before you proceed ensure your account has enough free disk space to hold the entire instance of the production site. You can check the available space from your hosting account (cPanel or similar).</span>').show();
1114
+ cache.get('.wpstg-loader').hide();
1115
+ }, 'json', false);
1116
+ });
1117
+ };
1118
+
1119
+ var mainTabs = function mainTabs() {
1120
+ $('.wpstg--tab--header a[data-target]').on('click', function () {
1121
+ var $this = $(this);
1122
+ var target = $this.attr('data-target');
1123
+ var $wrapper = $this.parents('.wpstg--tab--wrapper');
1124
+ var $menuItems = $wrapper.find('.wpstg--tab--header a[data-target]');
1125
+ var $contents = $wrapper.find('.wpstg--tab--contents > .wpstg--tab--content');
1126
+ $contents.filter('.wpstg--tab--active:not(.wpstg--tab--active' + target + ')').removeClass('wpstg--tab--active');
1127
+ $menuItems.not($this).removeClass('wpstg--tab--active');
1128
+ $this.addClass('wpstg--tab--active');
1129
+ $(target).addClass('wpstg--tab--active');
1130
+
1131
+ if ('#wpstg--tab--backup' === target) {
1132
+ that.backups.init();
1133
+ }
1134
+
1135
+ if ('#wpstg--tab--database-backups' === target) {
1136
+ window.dispatchEvent(new Event('database-backups-tab'));
1137
+ }
1138
+ });
1139
+ };
1140
+ /**
1141
+ * Show or hide animated loading icon
1142
+ * @param isLoading bool
1143
+ */
1144
+
1145
+
1146
+ var isLoading = function isLoading(_isLoading) {
1147
+ if (!_isLoading || _isLoading === false) {
1148
+ cache.get('.wpstg-loader').hide();
1149
+ } else {
1150
+ cache.get('.wpstg-loader').show();
1151
+ }
1152
+ };
1153
+ /**
1154
+ * Count up processing execution time
1155
+ * @param string status
1156
+ * @return html
1157
+ */
1158
+
1159
+
1160
+ that.timer = function (status) {
1161
+ if (status === 'stop') {
1162
+ var time = that.time;
1163
+ that.time = 1;
1164
+ clearInterval(that.executionTime);
1165
+ return that.convertSeconds(time);
1166
+ }
1167
+
1168
+ that.executionTime = setInterval(function () {
1169
+ if (null !== document.getElementById('wpstg-processing-timer')) {
1170
+ document.getElementById('wpstg-processing-timer').innerHTML = 'Elapsed Time: ' + that.convertSeconds(that.time);
1171
+ }
1172
+
1173
+ that.time++;
1174
+
1175
+ if (status === 'stop') {
1176
+ that.time = 1;
1177
+ clearInterval(that.executionTime);
1178
+ }
1179
+ }, 1000);
1180
+ };
1181
+ /**
1182
+ * Convert seconds to hourly format
1183
+ * @param int seconds
1184
+ * @return string
1185
+ */
1186
+
1187
+
1188
+ that.convertSeconds = function (seconds) {
1189
+ var date = new Date(null);
1190
+ date.setSeconds(seconds); // specify value for SECONDS here
1191
+
1192
+ return date.toISOString().substr(11, 8);
1193
+ };
1194
+ /**
1195
+ * Start Cloning Process
1196
+ * @type {Function}
1197
+ */
1198
+
1199
+
1200
+ that.startCloning = function () {
1201
+ resetErrors(); // Register function for checking disk space
1202
+
1203
+ checkDiskSpace();
1204
+
1205
+ if ('wpstg_cloning' !== that.data.action && 'wpstg_update' !== that.data.action && 'wpstg_reset' !== that.data.action) {
1206
+ return;
1207
+ }
1208
+
1209
+ that.isCancelled = false; // Start the process
1210
+
1211
+ start(); // Functions
1212
+ // Start
1213
+
1214
+ function start() {
1215
+ console.log('Starting cloning process...');
1216
+ cache.get('.wpstg-loader').show();
1217
+ cache.get('#wpstg-cancel-cloning').text('Cancel');
1218
+ cache.get('#wpstg-resume-cloning').hide();
1219
+ cache.get('#wpstg-error-details').hide(); // Clone Database
1220
+
1221
+ setTimeout(function () {
1222
+ // cloneDatabase();
1223
+ window.addEventListener('beforeunload', warnIfClosingDuringProcess);
1224
+ processing();
1225
+ }, wpstg.delayReq);
1226
+ that.timer('start');
1227
+ }
1228
+ /**
1229
+ * Start ajax processing
1230
+ * @return string
1231
+ */
1232
+
1233
+
1234
+ var processing = function processing() {
1235
+ if (true === that.isCancelled) {
1236
+ window.removeEventListener('beforeunload', warnIfClosingDuringProcess);
1237
+ return false;
1238
+ }
1239
+
1240
+ isLoading(true); // Show logging window
1241
+
1242
+ cache.get('.wpstg-log-details').show();
1243
+ WPStaging.ajax({
1244
+ action: 'wpstg_processing',
1245
+ accessToken: wpstg.accessToken,
1246
+ nonce: wpstg.nonce,
1247
+ excludedTables: getExcludedTables(),
1248
+ includedDirectories: getIncludedDirectories(),
1249
+ excludedDirectories: getExcludedDirectories(),
1250
+ extraDirectories: getIncludedExtraDirectories()
1251
+ }, function (response) {
1252
+ showAjaxFatalError(response); // Add Log messages
1253
+
1254
+ if ('undefined' !== typeof response.last_msg && response.last_msg) {
1255
+ getLogs(response.last_msg);
1256
+ } // Continue processing
1257
+
1258
+
1259
+ if (false === response.status) {
1260
+ progressBar(response);
1261
+ setTimeout(function () {
1262
+ cache.get('.wpstg-loader').show();
1263
+ processing();
1264
+ }, wpstg.delayReq);
1265
+ } else if (true === response.status && 'finished' !== response.status) {
1266
+ cache.get('#wpstg-error-details').hide();
1267
+ cache.get('#wpstg-error-wrapper').hide();
1268
+ progressBar(response, true);
1269
+ processing();
1270
+ } else if ('finished' === response.status || 'undefined' !== typeof response.job_done && response.job_done) {
1271
+ window.removeEventListener('beforeunload', warnIfClosingDuringProcess);
1272
+ finish(response);
1273
+ }
1274
+
1275
+ ;
1276
+ }, 'json', false);
1277
+ }; // Finish
1278
+
1279
+
1280
+ function finish(response) {
1281
+ if (true === that.getLogs) {
1282
+ getLogs();
1283
+ }
1284
+
1285
+ progressBar(response); // Add Log
1286
+
1287
+ if ('undefined' !== typeof response.last_msg) {
1288
+ getLogs(response.last_msg);
1289
+ }
1290
+
1291
+ console.log('Cloning process finished');
1292
+ cache.get('.wpstg-loader').hide();
1293
+ cache.get('#wpstg-processing-header').html('Processing Complete');
1294
+ $('#wpstg-processing-status').text('Succesfully finished');
1295
+ cache.get('#wpstg_staging_name').html(that.data.cloneID);
1296
+ cache.get('#wpstg-finished-result').show();
1297
+ cache.get('#wpstg-cancel-cloning').hide();
1298
+ cache.get('#wpstg-resume-cloning').hide();
1299
+ cache.get('#wpstg-cancel-cloning-update').prop('disabled', true);
1300
+ var $link1 = cache.get('#wpstg-clone-url-1');
1301
+ var $link = cache.get('#wpstg-clone-url');
1302
+ $link1.attr('href', response.url);
1303
+ $link1.html(response.url);
1304
+ $link.attr('href', response.url);
1305
+ cache.get('#wpstg-remove-clone').data('clone', that.data.cloneID); // Finished
1306
+
1307
+ that.isFinished = true;
1308
+ that.timer('stop');
1309
+ cache.get('.wpstg-loader').hide();
1310
+ cache.get('#wpstg-processing-header').html('Processing Complete');
1311
+ return false;
1312
+ }
1313
+ /**
1314
+ * Add percentage progress bar
1315
+ * @param object response
1316
+ * @return {Boolean}
1317
+ */
1318
+
1319
+
1320
+ var progressBar = function progressBar(response, restart) {
1321
+ if ('undefined' === typeof response.percentage) {
1322
+ return false;
1323
+ }
1324
+
1325
+ if (response.job === 'database') {
1326
+ cache.get('#wpstg-progress-db').width(response.percentage * 0.2 + '%').html(response.percentage + '%');
1327
+ cache.get('#wpstg-processing-status').html(response.percentage.toFixed(0) + '%' + ' - Step 1 of 4 Cloning Database Tables...');
1328
+ }
1329
+
1330
+ if (response.job === 'SearchReplace') {
1331
+ cache.get('#wpstg-progress-db').css('background-color', '#3bc36b');
1332
+ cache.get('#wpstg-progress-db').html('1. Database'); // Assumption: All previous steps are done.
1333
+ // This avoids bugs where some steps are skipped and the progress bar is incomplete as a result
1334
+
1335
+ cache.get('#wpstg-progress-db').width('20%');
1336
+ cache.get('#wpstg-progress-sr').width(response.percentage * 0.1 + '%').html(response.percentage + '%');
1337
+ cache.get('#wpstg-processing-status').html(response.percentage.toFixed(0) + '%' + ' - Step 2 of 4 Preparing Database Data...');
1338
+ }
1339
+
1340
+ if (response.job === 'directories') {
1341
+ cache.get('#wpstg-progress-sr').css('background-color', '#3bc36b');
1342
+ cache.get('#wpstg-progress-sr').html('2. Data');
1343
+ cache.get('#wpstg-progress-sr').width('10%');
1344
+ cache.get('#wpstg-progress-dirs').width(response.percentage * 0.1 + '%').html(response.percentage + '%');
1345
+ cache.get('#wpstg-processing-status').html(response.percentage.toFixed(0) + '%' + ' - Step 3 of 4 Getting files...');
1346
+ }
1347
+
1348
+ if (response.job === 'files') {
1349
+ cache.get('#wpstg-progress-dirs').css('background-color', '#3bc36b');
1350
+ cache.get('#wpstg-progress-dirs').html('3. Files');
1351
+ cache.get('#wpstg-progress-dirs').width('10%');
1352
+ cache.get('#wpstg-progress-files').width(response.percentage * 0.6 + '%').html(response.percentage + '%');
1353
+ cache.get('#wpstg-processing-status').html(response.percentage.toFixed(0) + '%' + ' - Step 4 of 4 Copy files...');
1354
+ }
1355
+
1356
+ if (response.job === 'finish') {
1357
+ cache.get('#wpstg-progress-files').css('background-color', '#3bc36b');
1358
+ cache.get('#wpstg-progress-files').html('4. Copy Files');
1359
+ cache.get('#wpstg-progress-files').width('60%');
1360
+ cache.get('#wpstg-processing-status').html(response.percentage.toFixed(0) + '%' + ' - Cloning Process Finished');
1361
+ }
1362
+ };
1363
+ };
1364
+
1365
+ that.switchStep = function (step) {
1366
+ cache.get('.wpstg-current-step').removeClass('wpstg-current-step');
1367
+ cache.get('.wpstg-step' + step).addClass('wpstg-current-step');
1368
+ };
1369
+ /**
1370
+ * Initiation
1371
+ * @type {Function}
1372
+ */
1373
+
1374
+
1375
+ that.init = function () {
1376
+ loadOverview();
1377
+ elements();
1378
+ stepButtons();
1379
+ tabs();
1380
+ mainTabs();
1381
+ new WpstgCloneStaging();
1382
+ };
1383
+ /**
1384
+ * Ajax call
1385
+ * @type {ajax}
1386
+ */
1387
+
1388
+
1389
+ that.ajax = ajax;
1390
+ that.showError = showError;
1391
+ that.getLogs = getLogs;
1392
+ that.loadOverview = loadOverview; // TODO RPoC (too big, scattered and unorganized)
1393
+
1394
+ that.backups = {
1395
+ type: null,
1396
+ isCancelled: false,
1397
+ processInfo: {
1398
+ title: null,
1399
+ interval: null
1400
+ },
1401
+ modal: {
1402
+ create: {
1403
+ html: null,
1404
+ confirmBtnTxt: null
1405
+ },
1406
+ process: {
1407
+ html: null,
1408
+ cancelBtnTxt: null,
1409
+ modal: null
1410
+ },
1411
+ download: {
1412
+ html: null
1413
+ },
1414
+ "import": {
1415
+ html: null,
1416
+ btnTxtNext: null,
1417
+ btnTxtConfirm: null,
1418
+ btnTxtCancel: null,
1419
+ searchReplaceForm: null,
1420
+ file: null,
1421
+ containerUpload: null,
1422
+ containerFilesystem: null,
1423
+ setFile: function setFile(file, upload) {
1424
+ if (upload === void 0) {
1425
+ upload = true;
1426
+ }
1427
+
1428
+ var toUnit = function toUnit(bytes) {
1429
+ var i = Math.floor(Math.log(bytes) / Math.log(1024));
1430
+ return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
1431
+ };
1432
+
1433
+ if (!file) {
1434
+ return;
1435
+ }
1436
+
1437
+ that.backups.modal["import"].file = file;
1438
+ that.backups.modal["import"].data.file = file.name;
1439
+ console.log("File " + file.name);
1440
+ $('.wpstg--backup--import--selected-file').html(file.name + " <br /> (" + toUnit(file.size) + ")").show();
1441
+ $('.wpstg--drag').hide();
1442
+ $('.wpstg--drag-or-upload').show();
1443
+
1444
+ if (upload) {
1445
+ $('.wpstg--modal--actions .swal2-confirm').prop('disabled', true);
1446
+ that.backups.upload.start();
1447
+ }
1448
+ },
1449
+ baseDirectory: null,
1450
+ data: {
1451
+ file: null,
1452
+ search: [],
1453
+ replace: []
1454
+ }
1455
+ }
1456
+ },
1457
+ messages: {
1458
+ WARNING: 'warning',
1459
+ ERROR: 'error',
1460
+ INFO: 'info',
1461
+ DEBUG: 'debug',
1462
+ CRITICAL: 'critical',
1463
+ data: {
1464
+ all: [],
1465
+ // TODO RPoC
1466
+ info: [],
1467
+ error: [],
1468
+ critical: [],
1469
+ warning: [],
1470
+ debug: []
1471
+ },
1472
+ shouldWarn: function shouldWarn() {
1473
+ return that.backups.messages.data.error.length > 0 || that.backups.messages.data.critical.length > 0;
1474
+ },
1475
+ countByType: function countByType(type) {
1476
+ if (type === void 0) {
1477
+ type = that.backups.messages.ERROR;
1478
+ }
1479
+
1480
+ return that.backups.messages.data[type].length;
1481
+ },
1482
+ addMessage: function addMessage(message) {
1483
+ if (Array.isArray(message)) {
1484
+ message.forEach(function (item) {
1485
+ that.backups.messages.addMessage(item);
1486
+ });
1487
+ return;
1488
+ }
1489
+
1490
+ var type = message.type.toLowerCase() || 'info';
1491
+
1492
+ if (!that.backups.messages.data[type]) {
1493
+ that.backups.messages.data[type] = [];
1494
+ }
1495
+
1496
+ that.backups.messages.data.all.push(message); // TODO RPoC
1497
+
1498
+ that.backups.messages.data[type].push(message);
1499
+ },
1500
+ reset: function reset() {
1501
+ that.backups.messages.data = {
1502
+ all: [],
1503
+ info: [],
1504
+ error: [],
1505
+ critical: [],
1506
+ warning: [],
1507
+ debug: []
1508
+ };
1509
+ }
1510
+ },
1511
+ timer: {
1512
+ totalSeconds: 0,
1513
+ interval: null,
1514
+ start: function start() {
1515
+ if (null !== that.backups.timer.interval) {
1516
+ return;
1517
+ }
1518
+
1519
+ var prettify = function prettify(seconds) {
1520
+ console.log("Process running for " + seconds + " seconds"); // If potentially anything can exceed 24h execution time than that;
1521
+ // const _seconds = parseInt(seconds, 10)
1522
+ // const hours = Math.floor(_seconds / 3600)
1523
+ // const minutes = Math.floor(_seconds / 60) % 60
1524
+ // seconds = _seconds % 60
1525
+ //
1526
+ // return [hours, minutes, seconds]
1527
+ // .map(v => v < 10 ? '0' + v : v)
1528
+ // .filter((v,i) => v !== '00' || i > 0)
1529
+ // .join(':')
1530
+ // ;
1531
+ // Are we sure we won't create anything that exceeds 24h execution time? If not then this;
1532
+
1533
+ return "" + new Date(seconds * 1000).toISOString().substr(11, 8);
1534
+ };
1535
+
1536
+ that.backups.timer.interval = setInterval(function () {
1537
+ $('.wpstg--modal--process--elapsed-time').text(prettify(that.backups.timer.totalSeconds));
1538
+ that.backups.timer.totalSeconds++;
1539
+ }, 1000);
1540
+ },
1541
+ stop: function stop() {
1542
+ that.backups.timer.totalSeconds = 0;
1543
+
1544
+ if (that.backups.timer.interval) {
1545
+ clearInterval(that.backups.timer.interval);
1546
+ that.backups.timer.interval = null;
1547
+ }
1548
+ }
1549
+ },
1550
+ upload: {
1551
+ reader: null,
1552
+ file: null,
1553
+ iop: 1000 * 1024,
1554
+ uploadInfo: function uploadInfo(isShow) {
1555
+ var $containerUpload = $('.wpstg--modal--import--upload--process');
1556
+ var $containerUploader = $('.wpstg--uploader');
1557
+
1558
+ if (isShow) {
1559
+ $containerUpload.css('display', 'flex');
1560
+ $containerUploader.hide();
1561
+ return;
1562
+ }
1563
+
1564
+ $containerUploader.css('display', 'flex');
1565
+ $containerUpload.hide();
1566
+ },
1567
+ start: function start() {
1568
+ console.log("file " + that.backups.modal["import"].data.file);
1569
+ that.backups.upload.reader = new FileReader();
1570
+ that.backups.upload.file = that.backups.modal["import"].file;
1571
+ that.backups.upload.uploadInfo(true);
1572
+ that.backups.upload.sendChunk();
1573
+ },
1574
+ sendChunk: function sendChunk(startsAt) {
1575
+ if (startsAt === void 0) {
1576
+ startsAt = 0;
1577
+ }
1578
+
1579
+ if (!that.backups.upload.file) {
1580
+ return;
1581
+ }
1582
+
1583
+ var isReset = startsAt < 1;
1584
+ var endsAt = startsAt + that.backups.upload.iop + 1;
1585
+ var blob = that.backups.upload.file.slice(startsAt, endsAt);
1586
+
1587
+ that.backups.upload.reader.onloadend = function (event) {
1588
+ if (event.target.readyState !== FileReader.DONE) {
1589
+ return;
1590
+ }
1591
+
1592
+ var body = new FormData();
1593
+ body.append('accessToken', wpstg.accessToken);
1594
+ body.append('nonce', wpstg.nonce);
1595
+ body.append('data', event.target.result);
1596
+ body.append('filename', that.backups.upload.file.name);
1597
+ body.append('reset', isReset ? '1' : '0');
1598
+ fetch(ajaxurl + "?action=wpstg--backups--import--file-upload", {
1599
+ method: 'POST',
1600
+ body: body
1601
+ }).then(handleFetchErrors).then(function (res) {
1602
+ return res.json();
1603
+ }).then(function (res) {
1604
+ showAjaxFatalError(res, '', 'Submit an error report.');
1605
+ var writtenBytes = startsAt + that.backups.upload.iop;
1606
+ var percent = Math.floor(writtenBytes / that.backups.upload.file.size * 100);
1607
+
1608
+ if (endsAt >= that.backups.upload.file.size) {
1609
+ that.backups.upload.uploadInfo(false);
1610
+ isLoading(false);
1611
+ that.backups.switchModalToConfigure();
1612
+ return;
1613
+ }
1614
+
1615
+ $('.wpstg--modal--import--upload--progress--title > span').text(percent);
1616
+ $('.wpstg--modal--import--upload--progress').css('width', percent + "%");
1617
+ that.backups.upload.sendChunk(endsAt);
1618
+ })["catch"](function (e) {
1619
+ return showAjaxFatalError(e, '', 'Submit an error report.');
1620
+ });
1621
+ };
1622
+
1623
+ that.backups.upload.reader.readAsDataURL(blob);
1624
+ }
1625
+ },
1626
+ status: {
1627
+ hasResponse: null,
1628
+ reTryAfter: 5000
1629
+ },
1630
+ init: function init() {
1631
+ this.create();
1632
+ this["delete"]();
1633
+ this.edit(); // noinspection JSIgnoredPromiseFromCall
1634
+
1635
+ that.backups.fetchListing();
1636
+ $('body').on('click', '.wpstg--backup--download', function () {
1637
+ var url = this.getAttribute('data-url');
1638
+
1639
+ if (url.length > 0) {
1640
+ window.location.href = url;
1641
+ return;
1642
+ }
1643
+
1644
+ that.backups.downloadModal({
1645
+ titleExport: this.getAttribute('data-title-export'),
1646
+ title: this.getAttribute('data-title'),
1647
+ id: this.getAttribute('data-id'),
1648
+ btnTxtCancel: this.getAttribute('data-btn-cancel-txt'),
1649
+ btnTxtConfirm: this.getAttribute('data-btn-download-txt')
1650
+ });
1651
+ }).on('click', '.wpstg--backup--import', function () {
1652
+ // Clicked "Import" on the backup list
1653
+ that.backups.importModal();
1654
+ that.backups.modal["import"].data.file = this.getAttribute('data-filePath');
1655
+ that.backups.switchModalToConfigure(); // $('.wpstg--modal--actions .swal2-confirm').show();
1656
+ // $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
1657
+ }).off('click', '#wpstg-import-backup').on('click', '#wpstg-import-backup', function () {
1658
+ that.backups.importModal();
1659
+ }) // Import
1660
+ .off('click', '.wpstg--backup--import--choose-option').on('click', '.wpstg--backup--import--choose-option', function () {
1661
+ var $this = $(this);
1662
+ var $parent = $this.parent();
1663
+
1664
+ if (!$parent.hasClass('wpstg--show-options')) {
1665
+ $parent.addClass('wpstg--show-options');
1666
+ $this.text($this.attr('data-txtChoose'));
1667
+ } else {
1668
+ $parent.removeClass('wpstg--show-options');
1669
+ $this.text($this.attr('data-txtOther'));
1670
+ }
1671
+ }).off('click', '.wpstg--modal--backup--import--search-replace--new').on('click', '.wpstg--modal--backup--import--search-replace--new', function (e) {
1672
+ e.preventDefault();
1673
+ var $container = $(Swal.getContainer()).find('.wpstg--modal--backup--import--search-replace--input--container');
1674
+ var total = $container.find('.wpstg--modal--backup--import--search-replace--input-group').length;
1675
+ $container.append(that.backups.modal["import"].searchReplaceForm.replace(/{i}/g, total));
1676
+ }).off('click', '.wpstg--modal--backup--import--search-replace--remove').on('click', '.wpstg--modal--backup--import--search-replace--remove', function (e) {
1677
+ e.preventDefault();
1678
+ $(e.target).closest('.wpstg--modal--backup--import--search-replace--input-group').remove();
1679
+ }).off('input', '.wpstg--backup--import--search').on('input', '.wpstg--backup--import--search', function () {
1680
+ var index = parseInt(this.getAttribute('data-index'));
1681
+
1682
+ if (!isNaN(index)) {
1683
+ that.backups.modal["import"].data.search[index] = this.value;
1684
+ }
1685
+ }).off('input', '.wpstg--backup--import--replace').on('input', '.wpstg--backup--import--replace', function () {
1686
+ var index = parseInt(this.getAttribute('data-index'));
1687
+
1688
+ if (!isNaN(index)) {
1689
+ that.backups.modal["import"].data.replace[index] = this.value;
1690
+ }
1691
+ }) // Other Options
1692
+ .off('click', '.wpstg--backup--import--option[data-option]').on('click', '.wpstg--backup--import--option[data-option]', function () {
1693
+ var option = this.getAttribute('data-option');
1694
+
1695
+ if (option === 'file') {
1696
+ $('input[type="file"][name="wpstg--backup--import--upload--file"]').trigger('click');
1697
+ return;
1698
+ }
1699
+
1700
+ if (option === 'upload') {
1701
+ that.backups.modal["import"].containerFilesystem.hide();
1702
+ that.backups.modal["import"].containerUpload.show();
1703
+ }
1704
+
1705
+ if (option !== 'filesystem') {
1706
+ return;
1707
+ }
1708
+
1709
+ that.backups.modal["import"].containerUpload.hide();
1710
+ var $containerFilesystem = that.backups.modal["import"].containerFilesystem;
1711
+ $containerFilesystem.show();
1712
+ fetch(ajaxurl + "?action=wpstg--backups--import--file-list&_=" + Math.random() + "&accessToken=" + wpstg.accessToken + "&nonce=" + wpstg.nonce).then(handleFetchErrors).then(function (res) {
1713
+ return res.json();
1714
+ }).then(function (res) {
1715
+ var $ul = $('.wpstg--modal--backup--import--filesystem ul');
1716
+ $ul.empty();
1717
+
1718
+ if (!res || isEmpty(res)) {
1719
+ $ul.append("<span id=\"wpstg--backups--import--file-list-empty\">" + wpstg.i18n.noImportFileFound + "</span><br />");
1720
+ $('.wpstg--modal--backup--import--search-replace--wrapper').hide();
1721
+ return;
1722
+ }
1723
+
1724
+ $ul.append("<span id=\"wpstg--backups--import--file-list\">" + wpstg.i18n.selectFileToImport + "</span><br />");
1725
+ res.forEach(function (file, index) {
1726
+ $ul.append("\n<li data-filepath=\"" + file.fullPath + "\" class=\"wpstg--backups--import--file-list--sigle-item\">\n <ul class=\"wpstg-import-backup-more-info wpstg-import-backup-more-info-" + index + ("\">\n <li style=\"font-weight: bold\">" + file.backupName + "</li>\n <li>Created on: " + file.dateCreatedFormatted + "</li>\n <li>Size: " + file.size + "</li>\n <li>\n <div class=\"wpstg-import-backup-contains-title\">This backup contains:</div>\n <ul class=\"wpstg-import-backup-contains\">\n ") + (file.isExportingDatabase ? '<li><span class="dashicons dashicons-database wpstg--tooltip"><div class=\'wpstg--tooltiptext\'>Database</div></span></li>' : '') + "\n " + (file.isExportingPlugins ? '<li><span class="dashicons dashicons-admin-plugins wpstg--tooltip"><div class=\'wpstg--tooltiptext\'>Plugins</div></span></li>' : '') + "\n " + (file.isExportingMuPlugins ? '<li><span class="dashicons dashicons-plugins-checked wpstg--tooltip"><div class=\'wpstg--tooltiptext\'>Mu-plugins</div></span></li>' : '') + "\n " + (file.isExportingThemes ? '<li><span class="dashicons dashicons-layout wpstg--tooltip"><div class=\'wpstg--tooltiptext\'>Themes</div></span></li>' : '') + "\n " + (file.isExportingUploads ? '<li><span class="dashicons dashicons-images-alt wpstg--tooltip"><div class=\'wpstg--tooltiptext\'>Uploads</div></span></li>' : '') + "\n " + (file.isExportingOtherWpContentFiles ? '<li><span class="dashicons dashicons-admin-generic wpstg--tooltip"><div class=\'wpstg--tooltiptext\'>Other files in wp-content</div></span></li>' : '') + ("\n </ul>\n </li>\n <li>Filename: " + file.name + "</li>\n </ul>\n</li>\n"));
1727
+ });
1728
+ return res;
1729
+ })["catch"](function (e) {
1730
+ return showAjaxFatalError(e, '', 'Submit an error report.');
1731
+ });
1732
+ }).off('change', 'input[type="file"][name="wpstg--backup--import--upload--file"]').on('change', 'input[type="file"][name="wpstg--backup--import--upload--file"]', function () {
1733
+ that.backups.modal["import"].setFile(this.files[0] || null);
1734
+ }).off('change', 'input[type="radio"][name="backup_import_file"]').on('change', 'input[type="radio"][name="backup_import_file"]', function () {
1735
+ $('.wpstg--modal--actions .swal2-confirm').show();
1736
+ $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
1737
+ that.backups.modal["import"].data.file = this.value;
1738
+ }) // Drag & Drop
1739
+ .on('drag dragstart dragend dragover dragenter dragleave drop', '.wpstg--modal--backup--import--upload--container', function (e) {
1740
+ e.preventDefault();
1741
+ e.stopPropagation();
1742
+ }).on('dragover dragenter', '.wpstg--modal--backup--import--upload--container', function () {
1743
+ $(this).addClass('wpstg--has-dragover');
1744
+ }).on('dragleave dragend drop', '.wpstg--modal--backup--import--upload--container', function () {
1745
+ $(this).removeClass('wpstg--has-dragover');
1746
+ }).on('drop', '.wpstg--modal--backup--import--upload--container', function (e) {
1747
+ // Uploaded a file through the "Import Backup modal"
1748
+ that.backups.modal["import"].setFile(e.originalEvent.dataTransfer.files[0] || null);
1749
+ }).on('click', '.wpstg--modal--backup--import--filesystem li', function (e) {
1750
+ // Selected an existing backup in the Import Backup modal
1751
+ var fullPath = $(e.target).closest('.wpstg--backups--import--file-list--sigle-item').data().filepath;
1752
+
1753
+ if (!fullPath.length) {
1754
+ alert('Error: Could not get file path.');
1755
+ return;
1756
+ }
1757
+
1758
+ that.backups.modal["import"].data.file = fullPath;
1759
+ that.backups.switchModalToConfigure(); // $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
1760
+ // that.backups.modal.gotoStart();
1761
+ }).on('change', '.wpstg-advanced-options-site input[type="checkbox"]', function (e) {
1762
+ /*
1763
+ * We use a timeout here to simulate an "afterChange" event.
1764
+ * This allows us to read from the DOM whether the checkbox is checked or not,
1765
+ * instead of having to read from the event, which would require a lengthier code
1766
+ * to account for all elements we might want to read.
1767
+ */
1768
+ setTimeout(function (e) {
1769
+ // Reset
1770
+ document.getElementById('exportUploadsWithoutDatabaseWarning').style.display = 'none'; // Exporting Media Library without Database
1771
+
1772
+ var databaseChecked = document.getElementById('includeDatabaseInBackup').checked;
1773
+ var mediaLibraryChecked = document.getElementById('includeMediaLibraryInBackup').checked;
1774
+
1775
+ if (mediaLibraryChecked && !databaseChecked) {
1776
+ document.getElementById('exportUploadsWithoutDatabaseWarning').style.display = 'block';
1777
+ }
1778
+ }, 100);
1779
+ });
1780
+ },
1781
+ fetchListing: function fetchListing(isResetErrors) {
1782
+ if (isResetErrors === void 0) {
1783
+ isResetErrors = true;
1784
+ }
1785
+
1786
+ isLoading(true);
1787
+
1788
+ if (isResetErrors) {
1789
+ resetErrors();
1790
+ }
1791
+
1792
+ return fetch(ajaxurl + "?action=wpstg--backups--listing&_=" + Math.random() + "&accessToken=" + wpstg.accessToken + "&nonce=" + wpstg.nonce).then(handleFetchErrors).then(function (res) {
1793
+ return res.json();
1794
+ }).then(function (res) {
1795
+ showAjaxFatalError(res, '', 'Submit an error report.');
1796
+ cache.get('#wpstg--tab--backup').html(res);
1797
+ isLoading(false);
1798
+ window.dispatchEvent(new Event('backupListingFinished'));
1799
+ return res;
1800
+ })["catch"](function (e) {
1801
+ return showAjaxFatalError(e, '', 'Submit an error report.');
1802
+ });
1803
+ },
1804
+ "delete": function _delete() {
1805
+ $('#wpstg--tab--backup').off('click', '.wpstg-delete-backup[data-md5]').on('click', '.wpstg-delete-backup[data-md5]', function (e) {
1806
+ e.preventDefault();
1807
+ resetErrors();
1808
+
1809
+ if (!confirm('Are you sure you want to delete this backup?')) {
1810
+ return;
1811
+ }
1812
+
1813
+ isLoading(true);
1814
+ cache.get('#wpstg-existing-backups').hide();
1815
+ var md5 = this.getAttribute('data-md5');
1816
+ that.ajax({
1817
+ action: 'wpstg--backups--delete',
1818
+ md5: md5,
1819
+ accessToken: wpstg.accessToken,
1820
+ nonce: wpstg.nonce
1821
+ }, function (response) {
1822
+ showAjaxFatalError(response, '', ' Please submit an error report by using the REPORT ISSUE button.');
1823
+ isLoading(false);
1824
+ that.backups.fetchListing();
1825
+ });
1826
+ }); // Force delete if backup tables do not exist
1827
+ // TODO This is bloated, no need extra ID, use existing one?
1828
+
1829
+ $('#wpstg-error-wrapper').off('click', '#wpstg-backup-force-delete').on('click', '#wpstg-backup-force-delete', function (e) {
1830
+ e.preventDefault();
1831
+ resetErrors();
1832
+ isLoading(true);
1833
+ var id = this.getAttribute('data-id');
1834
+
1835
+ if (!confirm('Do you want to delete this backup ' + id + ' from the listed backups?')) {
1836
+ isLoading(false);
1837
+ return false;
1838
+ }
1839
+
1840
+ that.ajax({
1841
+ action: 'wpstg--backups--delete',
1842
+ id: id,
1843
+ accessToken: wpstg.accessToken,
1844
+ nonce: wpstg.nonce
1845
+ }, function (response) {
1846
+ showAjaxFatalError(response, '', ' Please submit an error report by using the REPORT ISSUE button.'); // noinspection JSIgnoredPromiseFromCall
1847
+
1848
+ that.backups.fetchListing();
1849
+ isLoading(false);
1850
+ });
1851
+ });
1852
+ },
1853
+ create: function create() {
1854
+ var prepareBackup = function prepareBackup(data) {
1855
+ WPStaging.ajax({
1856
+ action: 'wpstg--backups--prepare-export',
1857
+ accessToken: wpstg.accessToken,
1858
+ nonce: wpstg.nonce,
1859
+ wpstgExportData: data
1860
+ }, function (response) {
1861
+ if (response.success) {
1862
+ that.backups.timer.start();
1863
+ createBackup();
1864
+ } else {
1865
+ showAjaxFatalError(response.data, '', 'Submit an error report.');
1866
+ }
1867
+ }, 'json', false, 0, 1.25);
1868
+ };
1869
+
1870
+ var createBackup = function createBackup() {
1871
+ resetErrors();
1872
+
1873
+ if (that.backups.isCancelled) {
1874
+ // Swal.close();
1875
+ return;
1876
+ }
1877
+
1878
+ var statusStop = function statusStop() {
1879
+ console.log('Status: Stop');
1880
+ clearInterval(that.backups.processInfo.interval);
1881
+ that.backups.processInfo.interval = null;
1882
+ };
1883
+
1884
+ var status = function status() {
1885
+ if (that.backups.processInfo.interval !== null) {
1886
+ return;
1887
+ }
1888
+
1889
+ console.log('Status: Start');
1890
+ that.backups.processInfo.interval = setInterval(function () {
1891
+ if (true === that.backups.isCancelled) {
1892
+ statusStop();
1893
+ return;
1894
+ }
1895
+
1896
+ if (that.backups.status.hasResponse === false) {
1897
+ return;
1898
+ }
1899
+
1900
+ that.backups.status.hasResponse = false;
1901
+ fetch(ajaxurl + "?action=wpstg--backups--status&accessToken=" + wpstg.accessToken + "&nonce=" + wpstg.nonce).then(function (res) {
1902
+ return res.json();
1903
+ }).then(function (res) {
1904
+ that.backups.status.hasResponse = true;
1905
+
1906
+ if (typeof res === 'undefined') {
1907
+ statusStop();
1908
+ }
1909
+
1910
+ if (that.backups.processInfo.title === res.currentStatusTitle) {
1911
+ return;
1912
+ }
1913
+
1914
+ that.backups.processInfo.title = res.currentStatusTitle;
1915
+ var $container = $(Swal.getContainer());
1916
+ $container.find('.wpstg--modal--process--title').text(res.currentStatusTitle);
1917
+ $container.find('.wpstg--modal--process--percent').text('0');
1918
+ })["catch"](function (e) {
1919
+ that.backups.status.hasResponse = true;
1920
+ showAjaxFatalError(e, '', 'Submit an error report.');
1921
+ });
1922
+ }, 5000);
1923
+ };
1924
+
1925
+ WPStaging.ajax({
1926
+ action: 'wpstg--backups--export',
1927
+ accessToken: wpstg.accessToken,
1928
+ nonce: wpstg.nonce
1929
+ }, function (response) {
1930
+ console.log(response);
1931
+
1932
+ if (typeof response === 'undefined') {
1933
+ setTimeout(function () {
1934
+ createBackup();
1935
+ }, wpstg.delayReq);
1936
+ return;
1937
+ }
1938
+
1939
+ that.backups.processResponse(response);
1940
+
1941
+ if (!that.backups.processInfo.interval) {
1942
+ status();
1943
+ }
1944
+
1945
+ if (response.status === false) {
1946
+ createBackup();
1947
+ } else if (response.status === true) {
1948
+ $('#wpstg--progress--status').text('Backup successfully created!');
1949
+ that.backups.type = null;
1950
+
1951
+ if (that.backups.messages.shouldWarn()) {
1952
+ // noinspection JSIgnoredPromiseFromCall
1953
+ that.backups.fetchListing();
1954
+ that.backups.logsModal();
1955
+ return;
1956
+ }
1957
+
1958
+ statusStop();
1959
+ Swal.close();
1960
+ that.backups.fetchListing().then(function () {
1961
+ if (!response.backupMd5) {
1962
+ showError('Failed to get backup md5 from response');
1963
+ return;
1964
+ } // Wait for fetchListing to populate the DOM with the backup data that we want to read
1965
+
1966
+
1967
+ var $el = '';
1968
+ var timesWaited = 0;
1969
+ var intervalWaitForBackupInDom = setInterval(function () {
1970
+ timesWaited++;
1971
+ $el = $(".wpstg--backup--download[data-md5=\"" + response.backupMd5 + "\"]"); // Could not find element, let's try again...
1972
+
1973
+ if (!$el.length) {
1974
+ if (timesWaited >= 10) {
1975
+ // Bail: We tried too many times and couldn't find.
1976
+ clearInterval(intervalWaitForBackupInDom);
1977
+ }
1978
+
1979
+ return;
1980
+ } // Found it. No more need for the interval.
1981
+
1982
+
1983
+ clearInterval(intervalWaitForBackupInDom);
1984
+ var backupSize = response.hasOwnProperty('backupSize') ? ' (' + response.backupSize + ')' : '';
1985
+ that.backups.downloadModal({
1986
+ id: $el.data('id'),
1987
+ url: $el.data('url'),
1988
+ title: $el.data('title'),
1989
+ titleExport: $el.data('title-export'),
1990
+ btnTxtCancel: $el.data('btn-cancel-txt'),
1991
+ btnTxtConfirm: $el.data('btn-download-txt') + backupSize
1992
+ });
1993
+ $('.wpstg--modal--download--logs--wrapper').show();
1994
+ var $logsContainer = $('.wpstg--modal--process--logs');
1995
+ that.backups.messages.data.all.forEach(function (message) {
1996
+ var msgClass = "wpstg--modal--process--msg--" + message.type.toLowerCase();
1997
+ $logsContainer.append("<p class=\"" + msgClass + "\">[" + message.type + "] - [" + message.date + "] - " + message.message + "</p>");
1998
+ });
1999
+ }, 200);
2000
+ });
2001
+ } else {
2002
+ setTimeout(function () {
2003
+ createBackup();
2004
+ }, wpstg.delayReq);
2005
+ }
2006
+ }, 'json', false, 0, // Don't retry upon failure
2007
+ 1.25);
2008
+ };
2009
+
2010
+ var $body = $('body');
2011
+ $body.off('click', '.wpstg--tab--toggle').on('click', '.wpstg--tab--toggle', function () {
2012
+ var $this = $(this);
2013
+ var $target = $($this.attr('data-target'));
2014
+ $target.toggle();
2015
+
2016
+ if ($target.is(':visible')) {
2017
+ $this.find('span').text('▼');
2018
+ } else {
2019
+ $this.find('span').text('►');
2020
+ }
2021
+ }).off('change', '[name="includedDirectories\[\]"], input#includeDatabaseInBackup, input#includeOtherFilesInWpContent').on('change', '[type="checkbox"][name="includedDirectories\[\]"], input#includeDatabaseInBackup, input#includeOtherFilesInWpContent', function () {
2022
+ var isExportingAnyDir = $('[type="checkbox"][name="includedDirectories\[\]"]:checked').length > 0;
2023
+ var isExportingDatabase = $('input#includeDatabaseInBackup:checked').length === 1;
2024
+ var isExportingOtherFilesInWpContent = $('input#includeOtherFilesInWpContent:checked').length === 1;
2025
+
2026
+ if (!isExportingAnyDir && !isExportingDatabase && !isExportingOtherFilesInWpContent) {
2027
+ $('.swal2-confirm').prop('disabled', true);
2028
+ } else {
2029
+ $('.swal2-confirm').prop('disabled', false);
2030
+ }
2031
+ }); // Add backup name and notes
2032
+
2033
+ $('#wpstg--tab--backup').off('click', '#wpstg-new-backup').on('click', '#wpstg-new-backup', /*#__PURE__*/function () {
2034
+ var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(e) {
2035
+ var $newBackupModal, html, btnTxt, _yield$Swal$fire, formValues;
2036
+
2037
+ return regeneratorRuntime.wrap(function _callee$(_context) {
2038
+ while (1) {
2039
+ switch (_context.prev = _context.next) {
2040
+ case 0:
2041
+ resetErrors();
2042
+ e.preventDefault();
2043
+ that.backups.isCancelled = false;
2044
+
2045
+ if (!that.backups.modal.create.html || !that.backups.modal.create.confirmBtnTxt) {
2046
+ $newBackupModal = $('#wpstg--modal--backup--new');
2047
+ html = $newBackupModal.html();
2048
+ btnTxt = $newBackupModal.attr('data-confirmButtonText');
2049
+ that.backups.modal.create.html = html || null;
2050
+ that.backups.modal.create.confirmBtnTxt = btnTxt || null;
2051
+ $newBackupModal.remove();
2052
+ }
2053
+
2054
+ _context.next = 6;
2055
+ return Swal.fire({
2056
+ title: '',
2057
+ html: that.backups.modal.create.html,
2058
+ focusConfirm: false,
2059
+ confirmButtonText: that.backups.modal.create.confirmBtnTxt,
2060
+ showCancelButton: true,
2061
+ preConfirm: function preConfirm() {
2062
+ var container = Swal.getContainer();
2063
+ return {
2064
+ name: container.querySelector('input[name="backup_name"]').value || null,
2065
+ isExportingPlugins: container.querySelector('#includePluginsInBackup:checked') !== null,
2066
+ isExportingMuPlugins: container.querySelector('#includeMuPluginsInBackup:checked') !== null,
2067
+ isExportingThemes: container.querySelector('#includeThemesInBackup:checked') !== null,
2068
+ isExportingUploads: container.querySelector('#includeMediaLibraryInBackup:checked') !== null,
2069
+ isExportingOtherWpContentFiles: container.querySelector('#includeOtherFilesInWpContent:checked') !== null,
2070
+ isExportingDatabase: container.querySelector('#includeDatabaseInBackup:checked') !== null
2071
+ };
2072
+ }
2073
+ });
2074
+
2075
+ case 6:
2076
+ _yield$Swal$fire = _context.sent;
2077
+ formValues = _yield$Swal$fire.value;
2078
+
2079
+ if (formValues) {
2080
+ _context.next = 10;
2081
+ break;
2082
+ }
2083
+
2084
+ return _context.abrupt("return");
2085
+
2086
+ case 10:
2087
+ that.backups.process({
2088
+ execute: function execute() {
2089
+ that.backups.messages.reset();
2090
+ prepareBackup(formValues);
2091
+ }
2092
+ });
2093
+
2094
+ case 11:
2095
+ case "end":
2096
+ return _context.stop();
2097
+ }
2098
+ }
2099
+ }, _callee);
2100
+ }));
2101
+
2102
+ return function (_x) {
2103
+ return _ref.apply(this, arguments);
2104
+ };
2105
+ }());
2106
+ },
2107
+ switchModalToConfigure: function switchModalToConfigure() {
2108
+ that.backups.modal["import"].containerUpload.hide();
2109
+ that.backups.modal["import"].containerFilesystem.hide();
2110
+ that.backups.modal["import"].containerConfigure.show();
2111
+ },
2112
+ // Edit backups name and notes
2113
+ edit: function edit() {
2114
+ $('#wpstg--tab--backup').off('click', '.wpstg--backup--edit[data-md5]').on('click', '.wpstg--backup--edit[data-md5]', /*#__PURE__*/function () {
2115
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(e) {
2116
+ var $this, name, notes, _yield$Swal$fire2, formValues;
2117
+
2118
+ return regeneratorRuntime.wrap(function _callee2$(_context2) {
2119
+ while (1) {
2120
+ switch (_context2.prev = _context2.next) {
2121
+ case 0:
2122
+ e.preventDefault();
2123
+ $this = $(this);
2124
+ name = $this.data('name');
2125
+ notes = $this.data('notes');
2126
+ _context2.next = 6;
2127
+ return Swal.fire({
2128
+ title: '',
2129
+ html: "\n <label id=\"wpstg-backup-edit-name\">Backup Name</label>\n <input id=\"wpstg-backup-edit-name-input\" class=\"swal2-input\" value=\"" + name + "\">\n <label>Additional Notes</label>\n <textarea id=\"wpstg-backup-edit-notes-textarea\" class=\"swal2-textarea\">" + notes + "</textarea>\n ",
2130
+ focusConfirm: false,
2131
+ confirmButtonText: 'Update Backup',
2132
+ showCancelButton: true,
2133
+ preConfirm: function preConfirm() {
2134
+ return {
2135
+ name: document.getElementById('wpstg-backup-edit-name-input').value || null,
2136
+ notes: document.getElementById('wpstg-backup-edit-notes-textarea').value || null
2137
+ };
2138
+ }
2139
+ });
2140
+
2141
+ case 6:
2142
+ _yield$Swal$fire2 = _context2.sent;
2143
+ formValues = _yield$Swal$fire2.value;
2144
+
2145
+ if (formValues) {
2146
+ _context2.next = 10;
2147
+ break;
2148
+ }
2149
+
2150
+ return _context2.abrupt("return");
2151
+
2152
+ case 10:
2153
+ that.ajax({
2154
+ action: 'wpstg--backups--edit',
2155
+ accessToken: wpstg.accessToken,
2156
+ nonce: wpstg.nonce,
2157
+ md5: $this.data('md5'),
2158
+ name: formValues.name,
2159
+ notes: formValues.notes
2160
+ }, function (response) {
2161
+ showAjaxFatalError(response, '', 'Submit an error report.'); // noinspection JSIgnoredPromiseFromCall
2162
+
2163
+ that.backups.fetchListing();
2164
+ });
2165
+
2166
+ case 11:
2167
+ case "end":
2168
+ return _context2.stop();
2169
+ }
2170
+ }
2171
+ }, _callee2, this);
2172
+ }));
2173
+
2174
+ return function (_x2) {
2175
+ return _ref2.apply(this, arguments);
2176
+ };
2177
+ }());
2178
+ },
2179
+ cancel: function cancel() {
2180
+ that.backups.timer.stop();
2181
+ that.backups.isCancelled = true;
2182
+ Swal.close();
2183
+ setTimeout(function () {
2184
+ return that.ajax({
2185
+ action: 'wpstg--backups--cancel',
2186
+ accessToken: wpstg.accessToken,
2187
+ nonce: wpstg.nonce,
2188
+ type: that.backups.type
2189
+ }, function (response) {
2190
+ showAjaxFatalError(response, '', 'Submit an error report.');
2191
+ });
2192
+ }, 500);
2193
+ },
2194
+
2195
+ /**
2196
+ * If process.execute exists, process.data and process.onResponse is not used
2197
+ * process = { data: {}, onResponse: (resp) => {}, onAfterClose: () => {}, execute: () => {}, isShowCancelButton: bool }
2198
+ * @param {object} process
2199
+ */
2200
+ process: function process(_process) {
2201
+ if (typeof _process.execute !== 'function' && (!_process.data || !_process.onResponse)) {
2202
+ Swal.close();
2203
+ showError('process.data and / or process.onResponse is not set');
2204
+ return;
2205
+ } // TODO move to backend and get the contents as xhr response?
2206
+
2207
+
2208
+ if (!that.backups.modal.process.html || !that.backups.modal.process.cancelBtnTxt) {
2209
+ var $modal = $('#wpstg--modal--backup--process');
2210
+ var html = $modal.html();
2211
+ var btnTxt = $modal.attr('data-cancelButtonText');
2212
+ that.backups.modal.process.html = html || null;
2213
+ that.backups.modal.process.cancelBtnTxt = btnTxt || null;
2214
+ $modal.remove();
2215
+ }
2216
+
2217
+ $('body').off('click', '.wpstg--modal--process--logs--tail').on('click', '.wpstg--modal--process--logs--tail', function (e) {
2218
+ e.preventDefault();
2219
+ var container = Swal.getContainer();
2220
+ var $logs = $(container).find('.wpstg--modal--process--logs');
2221
+ $logs.toggle();
2222
+
2223
+ if ($logs.is(':visible')) {
2224
+ container.childNodes[0].style.width = '100%';
2225
+ container.style['z-index'] = 9999;
2226
+ } else {
2227
+ container.childNodes[0].style.width = '600px';
2228
+ }
2229
+ });
2230
+ _process.isShowCancelButton = false !== _process.isShowCancelButton;
2231
+ that.backups.modal.process.modal = Swal.mixin({
2232
+ customClass: {
2233
+ cancelButton: 'wpstg--btn--cancel wpstg-blue-primary wpstg-link-btn',
2234
+ content: 'wpstg--process--content'
2235
+ },
2236
+ buttonsStyling: false
2237
+ }).fire({
2238
+ html: that.backups.modal.process.html,
2239
+ cancelButtonText: that.backups.modal.process.cancelBtnTxt,
2240
+ showCancelButton: _process.isShowCancelButton,
2241
+ showConfirmButton: false,
2242
+ allowOutsideClick: false,
2243
+ allowEscapeKey: false,
2244
+ width: 600,
2245
+ onRender: function onRender() {
2246
+ var _btnCancel = Swal.getContainer().getElementsByClassName('swal2-cancel wpstg--btn--cancel')[0];
2247
+
2248
+ var btnCancel = _btnCancel.cloneNode(true);
2249
+
2250
+ _btnCancel.parentNode.replaceChild(btnCancel, _btnCancel);
2251
+
2252
+ btnCancel.addEventListener('click', function (e) {
2253
+ if (confirm('Are You Sure? This will cancel the process!')) {
2254
+ Swal.close();
2255
+ }
2256
+ });
2257
+
2258
+ if (typeof _process.execute === 'function') {
2259
+ _process.execute();
2260
+
2261
+ return;
2262
+ }
2263
+
2264
+ if (!_process.data || !_process.onResponse) {
2265
+ Swal.close();
2266
+ showError('process.data and / or process.onResponse is not set');
2267
+ return;
2268
+ }
2269
+
2270
+ that.ajax(_process.data, _process.onResponse);
2271
+ },
2272
+ onAfterClose: function onAfterClose() {
2273
+ return typeof _process.onAfterClose === 'function' && _process.onAfterClose();
2274
+ },
2275
+ onClose: function onClose() {
2276
+ console.log('cancelled');
2277
+ that.backups.cancel();
2278
+ }
2279
+ });
2280
+ },
2281
+ processResponse: function processResponse(response, useTitle) {
2282
+ if (response === null) {
2283
+ Swal.close();
2284
+ showError('Invalid Response; null');
2285
+ throw new Error("Invalid Response; " + response);
2286
+ }
2287
+
2288
+ var $container = $(Swal.getContainer());
2289
+
2290
+ var title = function title() {
2291
+ if ((response.title || response.statusTitle) && useTitle === true) {
2292
+ $container.find('.wpstg--modal--process--title').text(response.title || response.statusTitle);
2293
+ }
2294
+ };
2295
+
2296
+ var percentage = function percentage() {
2297
+ if (response.percentage) {
2298
+ $container.find('.wpstg--modal--process--percent').text(response.percentage);
2299
+ }
2300
+ };
2301
+
2302
+ var logs = function logs() {
2303
+ if (!response.messages) {
2304
+ return;
2305
+ }
2306
+
2307
+ var $logsContainer = $container.find('.wpstg--modal--process--logs');
2308
+ var stoppingTypes = [that.backups.messages.ERROR, that.backups.messages.CRITICAL];
2309
+
2310
+ var appendMessage = function appendMessage(message) {
2311
+ if (Array.isArray(message)) {
2312
+ for (var _iterator = _createForOfIteratorHelperLoose(message), _step; !(_step = _iterator()).done;) {
2313
+ var item = _step.value;
2314
+ appendMessage(item);
2315
+ }
2316
+
2317
+ return;
2318
+ }
2319
+
2320
+ var msgClass = "wpstg--modal--process--msg--" + message.type.toLowerCase();
2321
+ $logsContainer.append("<p class=\"" + msgClass + "\">[" + message.type + "] - [" + message.date + "] - " + message.message + "</p>");
2322
+
2323
+ if (stoppingTypes.includes(message.type.toLowerCase())) {
2324
+ that.backups.cancel();
2325
+ setTimeout(that.backups.logsModal, 500);
2326
+ }
2327
+ };
2328
+
2329
+ for (var _iterator2 = _createForOfIteratorHelperLoose(response.messages), _step2; !(_step2 = _iterator2()).done;) {
2330
+ var message = _step2.value;
2331
+
2332
+ if (!message) {
2333
+ continue;
2334
+ }
2335
+
2336
+ that.backups.messages.addMessage(message);
2337
+ appendMessage(message);
2338
+ }
2339
+
2340
+ if ($logsContainer.is(':visible')) {
2341
+ $logsContainer.scrollTop($logsContainer[0].scrollHeight);
2342
+ }
2343
+
2344
+ if (!that.backups.messages.shouldWarn()) {
2345
+ return;
2346
+ }
2347
+
2348
+ var $btnShowLogs = $container.find('.wpstg--modal--process--logs--tail');
2349
+ $btnShowLogs.html($btnShowLogs.attr('data-txt-bad'));
2350
+ $btnShowLogs.find('.wpstg--modal--logs--critical-count').text(that.backups.messages.countByType(that.backups.messages.CRITICAL));
2351
+ $btnShowLogs.find('.wpstg--modal--logs--error-count').text(that.backups.messages.countByType(that.backups.messages.ERROR));
2352
+ $btnShowLogs.find('.wpstg--modal--logs--warning-count').text(that.backups.messages.countByType(that.backups.messages.WARNING));
2353
+ };
2354
+
2355
+ title();
2356
+ percentage();
2357
+ logs();
2358
+
2359
+ if (response.status === true && response.job_done === true) {
2360
+ that.backups.timer.stop();
2361
+ that.backups.isCancelled = true;
2362
+ }
2363
+ },
2364
+ requestData: function requestData(notation, data) {
2365
+ var obj = {};
2366
+ var keys = notation.split('.');
2367
+ var lastIndex = keys.length - 1;
2368
+ keys.reduce(function (accumulated, current, index) {
2369
+ return accumulated[current] = index >= lastIndex ? data : {};
2370
+ }, obj);
2371
+ return obj;
2372
+ },
2373
+ logsModal: function logsModal() {
2374
+ Swal.fire({
2375
+ html: "<div class=\"wpstg--modal--error--logs\" style=\"display:block\"></div><div class=\"wpstg--modal--process--logs\" style=\"display:block\"></div>",
2376
+ width: '95%',
2377
+ onRender: function onRender() {
2378
+ var $container = $(Swal.getContainer());
2379
+ $container[0].style['z-index'] = 9999;
2380
+ var $logsContainer = $container.find('.wpstg--modal--process--logs');
2381
+ var $errorContainer = $container.find('.wpstg--modal--error--logs');
2382
+ var $translations = $('#wpstg--js--translations');
2383
+ var messages = that.backups.messages;
2384
+ var title = $translations.attr('data-modal-logs-title').replace('{critical}', messages.countByType(messages.CRITICAL)).replace('{errors}', messages.countByType(messages.ERROR)).replace('{warnings}', messages.countByType(messages.WARNING));
2385
+ $errorContainer.before("<h3>" + title + "</h3>");
2386
+ var warnings = [that.backups.messages.CRITICAL, that.backups.messages.ERROR, that.backups.messages.WARNING];
2387
+
2388
+ if (!that.backups.messages.shouldWarn()) {
2389
+ $errorContainer.hide();
2390
+ }
2391
+
2392
+ for (var _iterator3 = _createForOfIteratorHelperLoose(messages.data.all), _step3; !(_step3 = _iterator3()).done;) {
2393
+ var message = _step3.value;
2394
+ var msgClass = "wpstg--modal--process--msg--" + message.type.toLowerCase(); // TODO RPoC
2395
+
2396
+ if (warnings.includes(message.type)) {
2397
+ $errorContainer.append("<p class=\"" + msgClass + "\">[" + message.type + "] - [" + message.date + "] - " + message.message + "</p>");
2398
+ }
2399
+
2400
+ $logsContainer.append("<p class=\"" + msgClass + "\">[" + message.type + "] - [" + message.date + "] - " + message.message + "</p>");
2401
+ }
2402
+ },
2403
+ onOpen: function onOpen(container) {
2404
+ var $logsContainer = $(container).find('.wpstg--modal--process--logs');
2405
+ $logsContainer.scrollTop($logsContainer[0].scrollHeight);
2406
+ }
2407
+ });
2408
+ },
2409
+ downloadModal: function downloadModal(_ref3) {
2410
+ var _ref3$title = _ref3.title,
2411
+ title = _ref3$title === void 0 ? null : _ref3$title,
2412
+ _ref3$titleExport = _ref3.titleExport,
2413
+ titleExport = _ref3$titleExport === void 0 ? null : _ref3$titleExport,
2414
+ _ref3$id = _ref3.id,
2415
+ id = _ref3$id === void 0 ? null : _ref3$id,
2416
+ _ref3$url = _ref3.url,
2417
+ url = _ref3$url === void 0 ? null : _ref3$url,
2418
+ _ref3$btnTxtCancel = _ref3.btnTxtCancel,
2419
+ btnTxtCancel = _ref3$btnTxtCancel === void 0 ? 'Cancel' : _ref3$btnTxtCancel,
2420
+ _ref3$btnTxtConfirm = _ref3.btnTxtConfirm,
2421
+ btnTxtConfirm = _ref3$btnTxtConfirm === void 0 ? 'Download' : _ref3$btnTxtConfirm;
2422
+
2423
+ if (null === that.backups.modal.download.html) {
2424
+ var $el = $('#wpstg--modal--backup--download');
2425
+ that.backups.modal.download.html = $el.html();
2426
+ $el.remove();
2427
+ }
2428
+
2429
+ var exportModal = function exportModal() {
2430
+ return Swal.fire({
2431
+ html: "<h2>" + titleExport + "</h2><span class=\"wpstg-loader\"></span>",
2432
+ showCancelButton: false,
2433
+ showConfirmButton: false,
2434
+ onRender: function onRender() {
2435
+ that.ajax({
2436
+ action: 'wpstg--backups--export',
2437
+ accessToken: wpstg.accessToken,
2438
+ nonce: wpstg.nonce,
2439
+ id: id
2440
+ }, function (response) {
2441
+ if (!response || !response.success || !response.data || response.data.length < 1) {
2442
+ return;
2443
+ }
2444
+
2445
+ var a = document.createElement('a');
2446
+ a.style.display = 'none';
2447
+ a.href = response.data;
2448
+ document.body.appendChild(a);
2449
+ a.click();
2450
+ document.body.removeChild(a);
2451
+ Swal.close();
2452
+ });
2453
+ }
2454
+ });
2455
+ };
2456
+
2457
+ Swal.mixin({
2458
+ customClass: {
2459
+ cancelButton: 'wpstg--btn--cancel wpstg-blue-primary wpstg-link-btn',
2460
+ confirmButton: 'wpstg--btn--confirm wpstg-blue-primary wpstg-button wpstg-link-btn',
2461
+ actions: 'wpstg--modal--actions'
2462
+ },
2463
+ buttonsStyling: false
2464
+ }).fire({
2465
+ icon: 'success',
2466
+ html: that.backups.modal.download.html.replace('{title}', title).replace('{btnTxtLog}', 'Show Logs'),
2467
+ cancelButtonText: btnTxtCancel,
2468
+ confirmButtonText: btnTxtConfirm,
2469
+ showCancelButton: true,
2470
+ showConfirmButton: true
2471
+ }).then(function (isConfirm) {
2472
+ if (!isConfirm || !isConfirm.value) {
2473
+ return;
2474
+ }
2475
+
2476
+ if (url && url.length > 0) {
2477
+ window.location.href = url;
2478
+ return;
2479
+ }
2480
+
2481
+ exportModal();
2482
+ });
2483
+ },
2484
+ importModal: function importModal() {
2485
+ var importSiteBackup = function importSiteBackup(data) {
2486
+ resetErrors();
2487
+
2488
+ if (that.backups.isCancelled) {
2489
+ console.log('cancelled'); // Swal.close();
2490
+
2491
+ return;
2492
+ }
2493
+
2494
+ that.backups.timer.start();
2495
+
2496
+ var statusStop = function statusStop() {
2497
+ console.log('Status: Stop');
2498
+ clearInterval(that.backups.processInfo.interval);
2499
+ that.backups.processInfo.interval = null;
2500
+ };
2501
+
2502
+ var status = function status() {
2503
+ if (that.backups.processInfo.interval !== null) {
2504
+ return;
2505
+ }
2506
+
2507
+ console.log('Status: Start');
2508
+ that.backups.processInfo.interval = setInterval(function () {
2509
+ if (true === that.backups.isCancelled) {
2510
+ statusStop();
2511
+ return;
2512
+ }
2513
+
2514
+ if (that.backups.status.hasResponse === false) {
2515
+ return;
2516
+ }
2517
+
2518
+ that.backups.status.hasResponse = false;
2519
+ fetch(ajaxurl + "?action=wpstg--backups--status&process=restore&accessToken=" + wpstg.accessToken + "&nonce=" + wpstg.nonce).then(function (res) {
2520
+ return res.json();
2521
+ }).then(function (res) {
2522
+ that.backups.status.hasResponse = true;
2523
+
2524
+ if (typeof res === 'undefined') {
2525
+ statusStop();
2526
+ }
2527
+
2528
+ if (that.backups.processInfo.title === res.currentStatusTitle) {
2529
+ return;
2530
+ }
2531
+
2532
+ that.backups.processInfo.title = res.currentStatusTitle;
2533
+ var $container = $(Swal.getContainer());
2534
+ $container.find('.wpstg--modal--process--title').text(res.currentStatusTitle);
2535
+ $container.find('.wpstg--modal--process--percent').text('0');
2536
+ })["catch"](function (e) {
2537
+ that.backups.status.hasResponse = true;
2538
+ showAjaxFatalError(e, '', 'Submit an error report.');
2539
+ });
2540
+ }, 5000);
2541
+ };
2542
+
2543
+ WPStaging.ajax({
2544
+ action: 'wpstg--backups--import',
2545
+ accessToken: wpstg.accessToken,
2546
+ nonce: wpstg.nonce
2547
+ }, function (response) {
2548
+ if (typeof response === 'undefined') {
2549
+ setTimeout(function () {
2550
+ importSiteBackup(data);
2551
+ }, wpstg.delayReq);
2552
+ return;
2553
+ }
2554
+
2555
+ that.backups.processResponse(response, true);
2556
+
2557
+ if (!that.backups.processInfo.interval) {
2558
+ status();
2559
+ }
2560
+
2561
+ if (response.status === false) {
2562
+ importSiteBackup(data);
2563
+ } else if (response.status === true) {
2564
+ $('#wpstg--progress--status').text('Backup successfully imported!');
2565
+ that.backups.type = null;
2566
+
2567
+ if (that.backups.messages.shouldWarn()) {
2568
+ // noinspection JSIgnoredPromiseFromCall
2569
+ that.backups.fetchListing();
2570
+ that.backups.logsModal();
2571
+ return;
2572
+ }
2573
+
2574
+ statusStop();
2575
+ var logEntries = $('.wpstg--modal--process--logs').get(1).innerHTML;
2576
+ var html = '<div class="wpstg--modal--process--logs">' + logEntries + '</div>';
2577
+ var issueFound = html.includes('wpstg--modal--process--msg--warning') || html.includes('wpstg--modal--process--msg--error') ? 'Issues(s) found! ' : '';
2578
+ console.log('errors found: ' + issueFound); // var errorMessage = html.includes('wpstg--modal--process--msg--error') ? 'Errors(s) found! ' : '';
2579
+ // var Message = warningMessage + errorMessage;
2580
+ // Swal.close();
2581
+
2582
+ Swal.fire({
2583
+ icon: 'success',
2584
+ title: 'Finished',
2585
+ html: 'System imported from backup. <br/><span class="wpstg--modal--process--msg-found">' + issueFound + '</span><button class="wpstg--modal--process--logs--tail" data-txt-bad="">Show Logs</button><br/>' + html
2586
+ }); // noinspection JSIgnoredPromiseFromCall
2587
+
2588
+ that.backups.fetchListing();
2589
+ } else {
2590
+ setTimeout(function () {
2591
+ importSiteBackup(data);
2592
+ }, wpstg.delayReq);
2593
+ }
2594
+ }, 'json', false, 0, // Don't retry upon failure
2595
+ 1.25);
2596
+ };
2597
+
2598
+ if (!that.backups.modal["import"].html) {
2599
+ var $modal = $('#wpstg--modal--backup--import'); // Search & Replace Form
2600
+
2601
+ var $form = $modal.find('.wpstg--modal--backup--import--search-replace--input--container');
2602
+ that.backups.modal["import"].searchReplaceForm = $form.html();
2603
+ $form.find('.wpstg--modal--backup--import--search-replace--input-group').remove();
2604
+ $form.html(that.backups.modal["import"].searchReplaceForm.replace(/{i}/g, 0));
2605
+ that.backups.modal["import"].html = $modal.html();
2606
+ that.backups.modal["import"].baseDirectory = $modal.attr('data-baseDirectory');
2607
+ that.backups.modal["import"].btnTxtNext = $modal.attr('data-nextButtonText');
2608
+ that.backups.modal["import"].btnTxtConfirm = $modal.attr('data-confirmButtonText');
2609
+ that.backups.modal["import"].btnTxtCancel = $modal.attr('data-cancelButtonText');
2610
+ $modal.remove();
2611
+ }
2612
+
2613
+ that.backups.modal["import"].data.search = [];
2614
+ that.backups.modal["import"].data.replace = [];
2615
+ var $btnConfirm = null;
2616
+ Swal.mixin({
2617
+ customClass: {
2618
+ confirmButton: 'wpstg--btn--confirm wpstg-blue-primary wpstg-button wpstg-link-btn',
2619
+ cancelButton: 'wpstg--btn--cancel wpstg-blue-primary wpstg-link-btn',
2620
+ actions: 'wpstg--modal--actions'
2621
+ },
2622
+ buttonsStyling: false // progressSteps: ['1', '2']
2623
+
2624
+ }).queue([{
2625
+ html: that.backups.modal["import"].html,
2626
+ confirmButtonText: that.backups.modal["import"].btnTxtNext,
2627
+ showCancelButton: false,
2628
+ showConfirmButton: true,
2629
+ showLoaderOnConfirm: true,
2630
+ width: 650,
2631
+ onRender: function onRender() {
2632
+ // $('.wpstg--modal--actions .swal2-confirm').hide();
2633
+ // todo: hide this again
2634
+ $('.wpstg--modal--actions .swal2-confirm').show();
2635
+ $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
2636
+ that.backups.modal["import"].containerUpload = $('.wpstg--modal--backup--import--upload');
2637
+ that.backups.modal["import"].containerFilesystem = $('.wpstg--modal--backup--import--filesystem');
2638
+ that.backups.modal["import"].containerConfigure = $('.wpstg--modal--backup--import--configure');
2639
+ },
2640
+ preConfirm: function preConfirm() {
2641
+ var body = new FormData();
2642
+ body.append('accessToken', wpstg.accessToken);
2643
+ body.append('nonce', wpstg.nonce);
2644
+ body.append('filePath', that.backups.modal["import"].data.file);
2645
+ that.backups.modal["import"].data.search.forEach(function (item, index) {
2646
+ body.append("search[" + index + "]", item);
2647
+ });
2648
+ that.backups.modal["import"].data.replace.forEach(function (item, index) {
2649
+ body.append("replace[" + index + "]", item);
2650
+ });
2651
+ return fetch(ajaxurl + "?action=wpstg--backups--import--file-info", {
2652
+ method: 'POST',
2653
+ body: body
2654
+ }).then(handleFetchErrors).then(function (res) {
2655
+ return res.json();
2656
+ }).then(function (html) {
2657
+ return Swal.insertQueueStep({
2658
+ html: html,
2659
+ confirmButtonText: that.backups.modal["import"].btnTxtConfirm,
2660
+ cancelButtonText: that.backups.modal["import"].btnTxtCancel,
2661
+ showCancelButton: true
2662
+ });
2663
+ })["catch"](function (e) {
2664
+ return showAjaxFatalError(e, '', 'Submit an error report.');
2665
+ });
2666
+ }
2667
+ }]).then(function (res) {
2668
+ if (!res || !res.value || !res.value[1] || res.value[1] !== true) {
2669
+ return;
2670
+ }
2671
+
2672
+ that.backups.isCancelled = false;
2673
+ var data = that.backups.modal["import"].data;
2674
+ data['file'] = that.backups.modal["import"].baseDirectory + data['file'];
2675
+ WPStaging.ajax({
2676
+ action: 'wpstg--backups--prepare-import',
2677
+ accessToken: wpstg.accessToken,
2678
+ nonce: wpstg.nonce,
2679
+ wpstgImportData: data
2680
+ }, function (response) {
2681
+ if (response.success) {
2682
+ that.backups.process({
2683
+ execute: function execute() {
2684
+ that.backups.messages.reset();
2685
+ importSiteBackup(data);
2686
+ }
2687
+ });
2688
+ } else {
2689
+ showAjaxFatalError(response.data, '', 'Submit an error report.');
2690
+ }
2691
+ }, 'json', false, 0, 1.25);
2692
+ });
2693
+ }
2694
+ };
2695
+ window.addEventListener('backupListingFinished', function () {
2696
+ fetch(ajaxurl + "?action=wpstg--backups--import--file-list&_=" + Math.random() + "&accessToken=" + wpstg.accessToken + "&nonce=" + wpstg.nonce + "&withTemplate=true").then(handleFetchErrors).then(function (res) {
2697
+ return res.json();
2698
+ }).then(function (res) {
2699
+ var $ul = $('.wpstg-backup-list ul');
2700
+ $ul.empty();
2701
+ $ul.html(res);
2702
+ })["catch"](function (e) {
2703
+ return showAjaxFatalError(e, '', 'Submit an error report.');
2704
+ });
2705
+ });
2706
+ return that;
2707
+ }(jQuery);
2708
+
2709
+ jQuery(document).ready(function () {
2710
+ WPStaging.init(); // This is necessary to make WPStaging var accessibile in WP Staging PRO js script
2711
+
2712
+ window.WPStaging = WPStaging;
2713
+ });
2714
+ /**
2715
+ * Report Issue modal
2716
+ */
2717
+
2718
+ jQuery(document).ready(function ($) {
2719
+ $('#wpstg-report-issue-button').on('click', function (e) {
2720
+ $('.wpstg-report-issue-form').toggleClass('wpstg-report-show');
2721
+ e.preventDefault();
2722
+ });
2723
+ $('body').on('click', '#wpstg-backups-report-issue-button', function (e) {
2724
+ $('.wpstg-report-issue-form').toggleClass('wpstg-report-show');
2725
+ console.log('test');
2726
+ e.preventDefault();
2727
+ });
2728
+ $('#wpstg-report-cancel').on('click', function (e) {
2729
+ $('.wpstg-report-issue-form').removeClass('wpstg-report-show');
2730
+ e.preventDefault();
2731
+ });
2732
+ /*
2733
+ * Close Success Modal
2734
+ */
2735
+
2736
+ $('body').on('click', '#wpstg-success-button', function (e) {
2737
+ e.preventDefault();
2738
+ $('.wpstg-report-issue-form').removeClass('wpstg-report-show');
2739
+ });
2740
+
2741
+ function sendIssueReport(button, forceSend) {
2742
+ if (forceSend === void 0) {
2743
+ forceSend = 'false';
2744
+ }
2745
+
2746
+ var spinner = button.next();
2747
+ var email = $('.wpstg-report-email').val();
2748
+ var hosting_provider = $('.wpstg-report-hosting-provider').val();
2749
+ var message = $('.wpstg-report-description').val();
2750
+ var syslog = $('.wpstg-report-syslog').is(':checked');
2751
+ var terms = $('.wpstg-report-terms').is(':checked');
2752
+ button.attr('disabled', true);
2753
+ spinner.css('visibility', 'visible');
2754
+ $.ajax({
2755
+ url: ajaxurl,
2756
+ type: 'POST',
2757
+ dataType: 'json',
2758
+ async: true,
2759
+ data: {
2760
+ 'action': 'wpstg_send_report',
2761
+ 'accessToken': wpstg.accessToken,
2762
+ 'nonce': wpstg.nonce,
2763
+ 'wpstg_email': email,
2764
+ 'wpstg_provider': hosting_provider,
2765
+ 'wpstg_message': message,
2766
+ 'wpstg_syslog': +syslog,
2767
+ 'wpstg_terms': +terms,
2768
+ 'wpstg_force_send': forceSend
2769
+ }
2770
+ }).done(function (data) {
2771
+ button.attr('disabled', false);
2772
+ spinner.css('visibility', 'hidden');
2773
+
2774
+ if (data.errors.length > 0) {
2775
+ $('.wpstg-report-issue-form .wpstg-message').remove();
2776
+ var errorMessage = $('<div />').addClass('wpstg-message wpstg-error-message');
2777
+ $.each(data.errors, function (key, value) {
2778
+ if (value.status === 'already_submitted') {
2779
+ errorMessage = '';
2780
+ Swal.fire({
2781
+ title: '',
2782
+ customClass: {
2783
+ container: 'wpstg-issue-resubmit-confirmation'
2784
+ },
2785
+ icon: 'warning',
2786
+ html: value.message,
2787
+ showCloseButton: true,
2788
+ showCancelButton: true,
2789
+ focusConfirm: false,
2790
+ confirmButtonText: 'Yes',
2791
+ cancelButtonText: 'No'
2792
+ }).then(function (result) {
2793
+ if (result.isConfirmed) {
2794
+ sendIssueReport(button, 'true');
2795
+ }
2796
+ });
2797
+ } else {
2798
+ errorMessage.append('<p>' + value + '</p>');
2799
+ }
2800
+ });
2801
+ $('.wpstg-report-issue-form').prepend(errorMessage);
2802
+ } else {
2803
+ var successMessage = $('<div />').addClass('wpstg-message wpstg-success-message');
2804
+ successMessage.append('<p>Thanks for submitting your request! You should receive an auto reply mail with your ticket ID immediately for confirmation!<br><br>If you do not get that mail please contact us directly at <strong>support@wp-staging.com</strong></p>');
2805
+ $('.wpstg-report-issue-form').html(successMessage);
2806
+ $('.wpstg-success-message').append('<div style="float:right;margin-top:10px;"><a id="wpstg-success-button" href="#">Close</a></div>'); // Hide message
2807
+
2808
+ setTimeout(function () {
2809
+ $('.wpstg-report-issue-form').removeClass('wpstg-report-active');
2810
+ }, 2000);
2811
+ }
2812
+ });
2813
+ }
2814
+
2815
+ $('#wpstg-report-submit').on('click', function (e) {
2816
+ var self = $(this);
2817
+ sendIssueReport(self, 'false');
2818
+ e.preventDefault();
2819
+ }); // Open/close actions drop down menu
2820
+
2821
+ $(document).on('click', '.wpstg-dropdown>.wpstg-dropdown-toggler', function (e) {
2822
+ e.preventDefault();
2823
+ $(e.target).next('.wpstg-dropdown-menu').toggleClass('shown');
2824
+ }); // Close action drop down menu if clicked anywhere outside
2825
+
2826
+ document.addEventListener('click', function (event) {
2827
+ var isClickInside = event.target.closest('.wpstg-dropdown-toggler');
2828
+
2829
+ if (!isClickInside) {
2830
+ var dropDown = document.getElementsByClassName('wpstg-dropdown-menu');
2831
+
2832
+ for (var i = 0; i < dropDown.length; i++) {
2833
+ dropDown[i].classList.remove('shown');
2834
+ }
2835
+ }
2836
+ });
2837
+ });
assets/js/dist/wpstg-clone-staging.js ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as dom from './wpstg-dom-utils.js';
2
+ /**
3
+ * Enable/Disable cloning for staging site
4
+ */
5
+
6
+ var WpstgCloneStaging = /*#__PURE__*/function () {
7
+ function WpstgCloneStaging(pageWrapperId, wpstgObject) {
8
+ if (pageWrapperId === void 0) {
9
+ pageWrapperId = '#wpstg-clonepage-wrapper';
10
+ }
11
+
12
+ if (wpstgObject === void 0) {
13
+ wpstgObject = wpstg;
14
+ }
15
+
16
+ this.pageWrapper = dom.qs(pageWrapperId);
17
+ this.wpstgObject = wpstgObject;
18
+ this.enableButtonId = '#wpstg-enable-staging-cloning';
19
+ this.enableAction = 'wpstg_enable_staging_cloning';
20
+ this.notyf = new Notyf({
21
+ duration: 10000,
22
+ position: {
23
+ x: 'center',
24
+ y: 'bottom'
25
+ },
26
+ dismissible: true,
27
+ types: [{
28
+ type: 'warning',
29
+ background: 'orange',
30
+ icon: false
31
+ }]
32
+ });
33
+ this.init();
34
+ }
35
+
36
+ var _proto = WpstgCloneStaging.prototype;
37
+
38
+ _proto.addEvents = function addEvents() {
39
+ var _this = this;
40
+
41
+ dom.addEvent(this.pageWrapper, 'click', this.enableButtonId, function () {
42
+ _this.sendRequest(_this.enableAction);
43
+ });
44
+ };
45
+
46
+ _proto.init = function init() {
47
+ this.addEvents();
48
+ };
49
+
50
+ _proto.sendRequest = function sendRequest(action) {
51
+ var _this2 = this;
52
+
53
+ fetch(this.wpstgObject.ajaxUrl, {
54
+ method: 'POST',
55
+ credentials: 'same-origin',
56
+ body: new URLSearchParams({
57
+ action: action,
58
+ accessToken: this.wpstgObject.accessToken,
59
+ nonce: this.wpstgObject.nonce
60
+ }),
61
+ headers: {
62
+ 'Content-Type': 'application/x-www-form-urlencoded'
63
+ }
64
+ }).then(function (response) {
65
+ if (response.ok) {
66
+ return response.json();
67
+ }
68
+
69
+ return Promise.reject(response);
70
+ }).then(function (data) {
71
+ // Reload current page if successful.
72
+ if ('undefined' !== typeof data.success && data.success) {
73
+ location.reload();
74
+ return;
75
+ } // There will be message probably in case of error
76
+
77
+
78
+ if ('undefined' !== typeof data.message) {
79
+ _this2.notyf.error(data.message);
80
+
81
+ return;
82
+ }
83
+
84
+ _this2.notyf.error(_this2.wpstgObject.i18n['somethingWentWrong']);
85
+ })["catch"](function (error) {
86
+ console.warn(_this2.wpstgObject.i18n['somethingWentWrong'], error);
87
+ });
88
+ };
89
+
90
+ return WpstgCloneStaging;
91
+ }();
92
+
93
+ export { WpstgCloneStaging as default };
assets/js/dist/wpstg-dom-utils.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WP Staging basic jQuery replacement
3
+ */
4
+
5
+ /**
6
+ * Shortcut for document.querySelector() or jQuery's $()
7
+ * Return single element only
8
+ */
9
+ export function qs(selector) {
10
+ return document.querySelector(selector);
11
+ }
12
+ /**
13
+ * alternative of jQuery - $(parent).on(event, selector, handler)
14
+ */
15
+
16
+ export function addEvent(parent, evt, selector, handler) {
17
+ for (var _len = arguments.length, args = new Array(_len > 4 ? _len - 4 : 0), _key = 4; _key < _len; _key++) {
18
+ args[_key - 4] = arguments[_key];
19
+ }
20
+
21
+ parent.addEventListener(evt, function (event) {
22
+ if (event.target.matches(selector + ', ' + selector + ' *')) {
23
+ handler.apply(event.target.closest(selector), args);
24
+ }
25
+ }, false);
26
+ }
assets/js/dist/wpstg-legacy-database.js ADDED
@@ -0,0 +1,1575 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); }
2
+
3
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
+
5
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
6
+
7
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
8
+
9
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
10
+
11
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
12
+
13
+ window.addEventListener('database-backups-tab', function () {
14
+ WPStagingLegacyDatabase.init();
15
+ });
16
+ var $ = jQuery;
17
+ var WPStagingLegacyDatabase = {
18
+ type: null,
19
+ isCancelled: false,
20
+ processInfo: {
21
+ title: null,
22
+ interval: null
23
+ },
24
+ cache: {
25
+ elements: [],
26
+ get: function get(selector) {
27
+ // It is already cached!
28
+ if ($.inArray(selector, this.elements) !== -1) {
29
+ return this.elements[selector];
30
+ } // Create cache and return
31
+
32
+
33
+ this.elements[selector] = $(selector);
34
+ return this.elements[selector];
35
+ },
36
+ refresh: function refresh(selector) {
37
+ selector.elements[selector] = $(selector);
38
+ }
39
+ },
40
+ init: function init() {
41
+ WPStagingLegacyDatabase.fetchListing();
42
+ this.create();
43
+ this["delete"]();
44
+ this.restore();
45
+ this.edit(); // noinspection JSIgnoredPromiseFromCall
46
+
47
+ $('body').off('change', '#wpstg--backups--filter').on('change', '#wpstg--backups--filter', function () {
48
+ var $records = $('#wpstg-existing-database-backups').find('> div[id][data-type].wpstg-backup');
49
+
50
+ if (this.value === '') {
51
+ $records.show();
52
+ } else if (this.value === 'database') {
53
+ $records.filter('[data-type="site"]').hide();
54
+ $records.filter('[data-type="database"]').show();
55
+ } else if (this.value === 'site') {
56
+ $records.filter('[data-type="database"]').hide();
57
+ $records.filter('[data-type="site"]').show();
58
+ }
59
+ }).on('click', '.wpstg--backup--download', function () {
60
+ var url = this.getAttribute('data-url');
61
+
62
+ if (url.length > 0) {
63
+ window.location.href = url;
64
+ return;
65
+ }
66
+
67
+ WPStagingLegacyDatabase.downloadModal({
68
+ titleExport: this.getAttribute('data-title-export'),
69
+ title: this.getAttribute('data-title'),
70
+ id: this.getAttribute('data-id'),
71
+ btnTxtCancel: this.getAttribute('data-btn-cancel-txt'),
72
+ btnTxtConfirm: this.getAttribute('data-btn-download-txt')
73
+ });
74
+ }).off('click', '#wpstg-import-backup').on('click', '#wpstg-import-backup', function () {
75
+ WPStagingLegacyDatabase.importModal();
76
+ }) // Import
77
+ .off('click', '.wpstg--backup--import--choose-option').on('click', '.wpstg--backup--import--choose-option', function () {
78
+ var $parent = $(this).parent();
79
+
80
+ if (!$parent.hasClass('wpstg--show-options')) {
81
+ $parent.addClass('wpstg--show-options');
82
+ $(this).text($(this).attr('data-txtChoose'));
83
+ } else {
84
+ $parent.removeClass('wpstg--show-options');
85
+ $(this).text($(this).attr('data-txtOther'));
86
+ }
87
+ }).off('click', '.wpstg--modal--backup--import--search-replace--new').on('click', '.wpstg--modal--backup--import--search-replace--new', function (e) {
88
+ e.preventDefault();
89
+ var $container = $(Swal.getContainer()).find('.wpstg--modal--backup--import--search-replace--input--container');
90
+ var total = $container.find('.wpstg--modal--backup--import--search-replace--input-group').length;
91
+ $container.append(WPStagingLegacyDatabase.modal["import"].searchReplaceForm.replace(/{i}/g, total));
92
+ }).off('input', '.wpstg--backup--import--search').on('input', '.wpstg--backup--import--search', function () {
93
+ var index = parseInt(this.getAttribute('data-index'));
94
+
95
+ if (!isNaN(index)) {
96
+ WPStagingLegacyDatabase.modal["import"].data.search[index] = this.value;
97
+ }
98
+ }).off('input', '.wpstg--backup--import--replace').on('input', '.wpstg--backup--import--replace', function () {
99
+ var index = parseInt(this.getAttribute('data-index'));
100
+
101
+ if (!isNaN(index)) {
102
+ WPStagingLegacyDatabase.modal["import"].data.replace[index] = this.value;
103
+ }
104
+ }) // Other Options
105
+ .off('click', '.wpstg--backup--import--option[data-option]').on('click', '.wpstg--backup--import--option[data-option]', function () {
106
+ var option = this.getAttribute('data-option');
107
+
108
+ if (option === 'file') {
109
+ $('input[type="file"][name="wpstg--backup--import--upload--file"]').trigger('click');
110
+ return;
111
+ }
112
+
113
+ if (option === 'upload') {
114
+ WPStagingLegacyDatabase.modal["import"].containerFilesystem.hide();
115
+ WPStagingLegacyDatabase.modal["import"].containerUpload.show();
116
+ $('.wpstg--backup--import--choose-option').trigger('click');
117
+ $('.wpstg--modal--backup--import--search-replace--wrapper').show();
118
+ }
119
+
120
+ if (option !== 'filesystem') {
121
+ return;
122
+ }
123
+
124
+ WPStagingLegacyDatabase.modal["import"].containerUpload.hide();
125
+ var $containerFilesystem = WPStagingLegacyDatabase.modal["import"].containerFilesystem;
126
+ $containerFilesystem.show();
127
+ fetch(ajaxurl + "?action=wpstg--backups--import--file-list&_=" + Math.random() + "&accessToken=" + wpstg.accessToken + "&nonce=" + wpstg.nonce).then(WPStagingLegacyDatabase.handleFetchErrors).then(function (res) {
128
+ return res.json();
129
+ }).then(function (res) {
130
+ var $ul = $('.wpstg--modal--backup--import--filesystem ul');
131
+ $ul.empty();
132
+
133
+ if (!res || WPStagingLegacyDatabase.isEmpty(res)) {
134
+ $ul.append("<span id=\"wpstg--backups--import--file-list-empty\">No import file found! Upload an import file to the folder above.</span><br />");
135
+ $('.wpstg--modal--backup--import--search-replace--wrapper').hide();
136
+ return;
137
+ }
138
+
139
+ $ul.append("<span id=\"wpstg--backups--import--file-list\">Select file to import:</span><br />");
140
+ res.forEach(function (file, index) {
141
+ // var checked = (index === 0) ? 'checked' : '';
142
+ $ul.append("<li><label><input name=\"backup_import_file\" type=\"radio\" value=\"" + file.fullPath + "\">" + file.name + " <br /> " + file.size + "</label></li>");
143
+ }); // $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
144
+
145
+ return res;
146
+ })["catch"](function (e) {
147
+ return WPStagingLegacyDatabase.showAjaxFatalError(e, '', 'Submit an error report.');
148
+ });
149
+ }).off('change', 'input[type="file"][name="wpstg--backup--import--upload--file"]').on('change', 'input[type="file"][name="wpstg--backup--import--upload--file"]', function () {
150
+ WPStagingLegacyDatabase.modal["import"].setFile(this.files[0] || null);
151
+ $('.wpstg--backup--import--choose-option').trigger('click');
152
+ }).off('change', 'input[type="radio"][name="backup_import_file"]').on('change', 'input[type="radio"][name="backup_import_file"]', function () {
153
+ $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
154
+ WPStagingLegacyDatabase.modal["import"].data.file = this.value;
155
+ }) // Drag & Drop
156
+ .on('drag dragstart dragend dragover dragenter dragleave drop', '.wpstg--modal--backup--import--upload--container', function (e) {
157
+ e.preventDefault();
158
+ e.stopPropagation();
159
+ }).on('dragover dragenter', '.wpstg--modal--backup--import--upload--container', function () {
160
+ $(this).addClass('wpstg--has-dragover');
161
+ }).on('dragleave dragend drop', '.wpstg--modal--backup--import--upload--container', function () {
162
+ $(this).removeClass('wpstg--has-dragover');
163
+ }).on('drop', '.wpstg--modal--backup--import--upload--container', function (e) {
164
+ WPStagingLegacyDatabase.modal["import"].setFile(e.originalEvent.dataTransfer.files[0] || null);
165
+ });
166
+ },
167
+ create: function create() {
168
+ var createBackup = function createBackup(data) {
169
+ WPStagingLegacyDatabase.resetErrors();
170
+
171
+ if (WPStagingLegacyDatabase.isCancelled) {
172
+ // Swal.close();
173
+ return;
174
+ }
175
+
176
+ var reset = data['reset'];
177
+ delete data['reset'];
178
+ var requestData = Object.assign({}, data);
179
+ var useResponseTitle = true;
180
+
181
+ if (data.type === 'database') {
182
+ WPStagingLegacyDatabase.type = data.type; // Only send to back-end what BE is expecting to receive.
183
+ // Prevent error: Trying to hydrate DTO with value that does not exist.
184
+
185
+ delete requestData['includedDirectories'];
186
+ delete requestData['wpContentDir'];
187
+ delete requestData['availableDirectories'];
188
+ delete requestData['wpStagingDir'];
189
+ delete requestData['exportDatabase'];
190
+ delete requestData['includeOtherFilesInWpContent'];
191
+ requestData = WPStagingLegacyDatabase.requestData('tasks.backup.database.create', _extends({}, requestData, {
192
+ type: 'manual'
193
+ }));
194
+ } else if (data.type === 'site') {
195
+ WPStagingLegacyDatabase.type = data.type; // Only send to back-end what BE is expecting to receive.
196
+ // Prevent error: Trying to hydrate DTO with value that does not exist.
197
+
198
+ delete requestData['type'];
199
+ requestData = WPStagingLegacyDatabase.requestData('jobs.backup.site.create', requestData);
200
+ useResponseTitle = false;
201
+ requestData.jobs.backup.site.create.directories = [data.wpContentDir];
202
+ requestData.jobs.backup.site.create.excludedDirectories = data.availableDirectories.split('|').filter(function (item) {
203
+ return !data.includedDirectories.includes(item);
204
+ }).map(function (item) {
205
+ return item;
206
+ });
207
+ requestData.jobs.backup.site.create.includeOtherFilesInWpContent = [data.includeOtherFilesInWpContent]; // Do not exclude the wp-content/uploads/wp-staging using regex by default
208
+ // This folder is excluded by PHP without REGEX.
209
+ // requestData.jobs.backup.site.create.excludedDirectories.push(`#${data.wpStagingDir}*#`);
210
+ // delete requestData.jobs.backup.site.create.includedDirectories;
211
+
212
+ delete requestData.jobs.backup.site.create.wpContentDir;
213
+ delete requestData.jobs.backup.site.create.wpStagingDir;
214
+ delete requestData.jobs.backup.site.create.availableDirectories;
215
+ } else {
216
+ WPStagingLegacyDatabase.type = null;
217
+ Swal.close();
218
+ WPStagingLegacyDatabase.showError('Invalid Backup Type');
219
+ return;
220
+ }
221
+
222
+ WPStagingLegacyDatabase.timer.start();
223
+
224
+ var statusStop = function statusStop() {
225
+ console.log('Status: Stop');
226
+ clearInterval(WPStagingLegacyDatabase.processInfo.interval);
227
+ WPStagingLegacyDatabase.processInfo.interval = null;
228
+ };
229
+
230
+ var status = function status() {
231
+ if (WPStagingLegacyDatabase.processInfo.interval !== null) {
232
+ return;
233
+ }
234
+
235
+ console.log('Status: Start');
236
+ WPStagingLegacyDatabase.processInfo.interval = setInterval(function () {
237
+ if (true === WPStagingLegacyDatabase.isCancelled) {
238
+ statusStop();
239
+ return;
240
+ }
241
+
242
+ if (WPStagingLegacyDatabase.status.hasResponse === false) {
243
+ return;
244
+ }
245
+
246
+ WPStagingLegacyDatabase.status.hasResponse = false;
247
+ fetch(ajaxurl + "?action=wpstg--backups--status&accessToken=" + wpstg.accessToken + "&nonce=" + wpstg.nonce).then(function (res) {
248
+ return res.json();
249
+ }).then(function (res) {
250
+ WPStagingLegacyDatabase.status.hasResponse = true;
251
+
252
+ if (typeof res === 'undefined') {
253
+ statusStop();
254
+ }
255
+
256
+ if (WPStagingLegacyDatabase.processInfo.title === res.currentStatusTitle) {
257
+ return;
258
+ }
259
+
260
+ WPStagingLegacyDatabase.processInfo.title = res.currentStatusTitle;
261
+ var $container = $(Swal.getContainer());
262
+ $container.find('.wpstg--modal--process--title').text(res.currentStatusTitle);
263
+ $container.find('.wpstg--modal--process--percent').text('0');
264
+ })["catch"](function (e) {
265
+ WPStagingLegacyDatabase.status.hasResponse = true;
266
+ WPStagingLegacyDatabase.showAjaxFatalError(e, '', 'Submit an error report.');
267
+ });
268
+ }, 5000);
269
+ };
270
+
271
+ WPStaging.ajax({
272
+ action: 'wpstg--backups--database-legacy-create',
273
+ accessToken: wpstg.accessToken,
274
+ nonce: wpstg.nonce,
275
+ reset: reset,
276
+ wpstg: requestData
277
+ }, function (response) {
278
+ if (typeof response === 'undefined') {
279
+ setTimeout(function () {
280
+ createBackup(data);
281
+ }, wpstg.delayReq);
282
+ return;
283
+ }
284
+
285
+ WPStagingLegacyDatabase.processResponse(response, useResponseTitle);
286
+
287
+ if (!useResponseTitle && !WPStagingLegacyDatabase.processInfo.interval) {
288
+ status();
289
+ }
290
+
291
+ if (response.status === false) {
292
+ createBackup(data);
293
+ } else if (response.status === true) {
294
+ $('#wpstg--progress--status').text('Backup successfully created!');
295
+ WPStagingLegacyDatabase.type = null;
296
+
297
+ if (WPStagingLegacyDatabase.messages.shouldWarn()) {
298
+ // noinspection JSIgnoredPromiseFromCall
299
+ WPStagingLegacyDatabase.fetchListing();
300
+ WPStagingLegacyDatabase.logsModal();
301
+ return;
302
+ }
303
+
304
+ statusStop();
305
+ Swal.close();
306
+ WPStagingLegacyDatabase.fetchListing().then(function () {
307
+ if (!response.backupId) {
308
+ WPStagingLegacyDatabase.showError('Failed to get backup ID from response');
309
+ return;
310
+ } // TODO RPoC
311
+
312
+
313
+ var $el = $(".wpstg--backup--download[data-id=\"" + response.backupId + "\"]");
314
+ WPStagingLegacyDatabase.downloadModal({
315
+ id: $el.data('id'),
316
+ url: $el.data('url'),
317
+ title: $el.data('title'),
318
+ titleExport: $el.data('title-export'),
319
+ btnTxtCancel: $el.data('btn-cancel-txt'),
320
+ btnTxtConfirm: $el.data('btn-download-txt')
321
+ });
322
+ $('.wpstg--modal--download--logs--wrapper').show();
323
+ var $logsContainer = $('.wpstg--modal--process--logs');
324
+ WPStagingLegacyDatabase.messages.data.all.forEach(function (message) {
325
+ var msgClass = "wpstg--modal--process--msg--" + message.type.toLowerCase();
326
+ $logsContainer.append("<p class=\"" + msgClass + "\">[" + message.type + "] - [" + message.date + "] - " + message.message + "</p>");
327
+ });
328
+ });
329
+ } else {
330
+ setTimeout(function () {
331
+ createBackup(data);
332
+ }, wpstg.delayReq);
333
+ }
334
+ }, 'json', false, 0, // Don't retry upon failure
335
+ 1.25);
336
+ };
337
+
338
+ var $body = $('body');
339
+ $body.off('click', 'input[name="backup_type"]').on('click', 'input[name="backup_type"]', function () {
340
+ var advancedOptions = $('.wpstg-advanced-options');
341
+
342
+ if (this.value === 'database') {
343
+ advancedOptions.hide();
344
+ return;
345
+ }
346
+
347
+ advancedOptions.show();
348
+ }).off('click', '.wpstg--tab--toggle').on('click', '.wpstg--tab--toggle', function () {
349
+ var $target = $($(this).attr('data-target'));
350
+ $target.toggle();
351
+
352
+ if ($target.is(':visible')) {
353
+ $(this).find('span').text('▼');
354
+ } else {
355
+ $(this).find('span').text('►');
356
+ }
357
+ }).off('change', '[name="includedDirectories\[\]"], [type="checkbox"][name="export_database"]').on('change', '[type="checkbox"][name="includedDirectories\[\]"], [type="checkbox"][name="export_database"]', function () {
358
+ var totalDirs = $('[type="checkbox"][name="includedDirectories\[\]"]:checked').length;
359
+ var isExportDatabase = $('[type="checkbox"][name="export_database"]:checked').length === 1;
360
+
361
+ if (totalDirs < 1 && !isExportDatabase) {
362
+ $('.swal2-confirm').prop('disabled', true);
363
+ } else {
364
+ $('.swal2-confirm').prop('disabled', false);
365
+ }
366
+ }); // Add backup name and notes
367
+
368
+ $('#wpstg--tab--database-backups').off('click', '#wpstg-new-database-backup').on('click', '#wpstg-new-database-backup', /*#__PURE__*/function () {
369
+ var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(e) {
370
+ var $newBackupModal, html, btnTxt, _yield$Swal$fire, formValues;
371
+
372
+ return regeneratorRuntime.wrap(function _callee$(_context) {
373
+ while (1) {
374
+ switch (_context.prev = _context.next) {
375
+ case 0:
376
+ WPStagingLegacyDatabase.resetErrors();
377
+ e.preventDefault();
378
+ WPStagingLegacyDatabase.isCancelled = false;
379
+
380
+ if (!WPStagingLegacyDatabase.modal.create.html || !WPStagingLegacyDatabase.modal.create.confirmBtnTxt) {
381
+ $newBackupModal = $('#wpstg--modal--database--new');
382
+ html = $newBackupModal.html();
383
+ btnTxt = $newBackupModal.attr('data-confirmButtonText');
384
+ WPStagingLegacyDatabase.modal.create.html = html || null;
385
+ WPStagingLegacyDatabase.modal.create.confirmBtnTxt = btnTxt || null;
386
+ $newBackupModal.remove();
387
+ }
388
+
389
+ _context.next = 6;
390
+ return Swal.fire({
391
+ title: '',
392
+ html: WPStagingLegacyDatabase.modal.create.html,
393
+ focusConfirm: false,
394
+ confirmButtonText: WPStagingLegacyDatabase.modal.create.confirmBtnTxt,
395
+ showCancelButton: true,
396
+ preConfirm: function preConfirm() {
397
+ var container = Swal.getContainer();
398
+ return {
399
+ type: 'database',
400
+ name: container.querySelector('input[name="backup_name"]').value || null,
401
+ notes: container.querySelector('textarea[name="backup_note"]').value || null,
402
+ includedDirectories: Array.from(container.querySelectorAll('input[name="includedDirectories\\[\\]"]:checked') || []).map(function (i) {
403
+ return i.value;
404
+ }),
405
+ wpContentDir: container.querySelector('input[name="wpContentDir"]').value || null,
406
+ availableDirectories: container.querySelector('input[name="availableDirectories"]').value || null,
407
+ wpStagingDir: container.querySelector('input[name="wpStagingDir"]').value || null,
408
+ exportDatabase: container.querySelector('input[name="export_database"]:checked') !== null,
409
+ includeOtherFilesInWpContent: container.querySelector('input[name="includeOtherFilesInWpContent"]:checked') !== null
410
+ };
411
+ }
412
+ });
413
+
414
+ case 6:
415
+ _yield$Swal$fire = _context.sent;
416
+ formValues = _yield$Swal$fire.value;
417
+
418
+ if (formValues) {
419
+ _context.next = 10;
420
+ break;
421
+ }
422
+
423
+ return _context.abrupt("return");
424
+
425
+ case 10:
426
+ formValues.reset = true;
427
+ WPStagingLegacyDatabase.process({
428
+ execute: function execute() {
429
+ WPStagingLegacyDatabase.messages.reset();
430
+ createBackup(formValues);
431
+ }
432
+ });
433
+
434
+ case 12:
435
+ case "end":
436
+ return _context.stop();
437
+ }
438
+ }
439
+ }, _callee);
440
+ }));
441
+
442
+ return function (_x) {
443
+ return _ref.apply(this, arguments);
444
+ };
445
+ }());
446
+ },
447
+ isEmpty: function isEmpty(obj) {
448
+ for (var prop in obj) {
449
+ if (obj.hasOwnProperty(prop)) {
450
+ return false;
451
+ }
452
+ }
453
+
454
+ return true;
455
+ },
456
+ isLoading: function isLoading(_isLoading) {
457
+ if (!_isLoading || _isLoading === false) {
458
+ WPStagingLegacyDatabase.cache.get('.wpstg-loader').hide();
459
+ } else {
460
+ WPStagingLegacyDatabase.cache.get('.wpstg-loader').show();
461
+ }
462
+ },
463
+ showAjaxFatalError: function showAjaxFatalError(response, prependMessage, appendMessage) {
464
+ prependMessage = prependMessage ? prependMessage + '<br/><br/>' : 'Something went wrong! <br/><br/>';
465
+ appendMessage = appendMessage ? appendMessage + '<br/><br/>' : '<br/><br/>Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.';
466
+
467
+ if (response === false) {
468
+ showError(prependMessage + ' Error: No response.' + appendMessage);
469
+ window.removeEventListener('beforeunload', WPStaging.warnIfClosingDuringProcess);
470
+ return;
471
+ }
472
+
473
+ if (typeof response.error !== 'undefined' && response.error) {
474
+ console.error(response.message);
475
+ showError(prependMessage + ' Error: ' + response.message + appendMessage);
476
+ window.removeEventListener('beforeunload', WPStaging.warnIfClosingDuringProcess);
477
+ return;
478
+ }
479
+ },
480
+ handleFetchErrors: function handleFetchErrors(response) {
481
+ if (!response.ok) {
482
+ showError('Error: ' + response.status + ' - ' + response.statusText + '. Please try again or contact support.');
483
+ }
484
+
485
+ return response;
486
+ },
487
+ showError: function showError(message) {
488
+ WPStagingLegacyDatabase.cache.get('#wpstg-try-again').css('display', 'inline-block');
489
+ WPStagingLegacyDatabase.cache.get('#wpstg-cancel-cloning').text('Reset');
490
+ WPStagingLegacyDatabase.cache.get('#wpstg-resume-cloning').show();
491
+ WPStagingLegacyDatabase.cache.get('#wpstg-error-wrapper').show();
492
+ WPStagingLegacyDatabase.cache.get('#wpstg-error-details').show().html(message);
493
+ WPStagingLegacyDatabase.cache.get('#wpstg-removing-clone').removeClass('loading');
494
+ WPStagingLegacyDatabase.cache.get('.wpstg-loader').hide();
495
+ $('.wpstg--modal--process--generic-problem').show().html(message);
496
+ },
497
+ resetErrors: function resetErrors() {
498
+ WPStagingLegacyDatabase.cache.get('#wpstg-error-details').hide().html('');
499
+ },
500
+
501
+ /**
502
+ * Ajax Requests
503
+ * @param {Object} data
504
+ * @param {Function} callback
505
+ * @param {string} dataType
506
+ * @param {bool} showErrors
507
+ * @param {int} tryCount
508
+ * @param {float} incrementRatio
509
+ */
510
+ ajax: function ajax(data, callback, dataType, showErrors, tryCount, incrementRatio) {
511
+ if (incrementRatio === void 0) {
512
+ incrementRatio = null;
513
+ }
514
+
515
+ if ('undefined' === typeof dataType) {
516
+ dataType = 'json';
517
+ }
518
+
519
+ if (false !== showErrors) {
520
+ showErrors = true;
521
+ }
522
+
523
+ tryCount = 'undefined' === typeof tryCount ? 0 : tryCount;
524
+ var retryLimit = 10;
525
+ var retryTimeout = 10000 * tryCount;
526
+ incrementRatio = parseInt(incrementRatio);
527
+
528
+ if (!isNaN(incrementRatio)) {
529
+ retryTimeout *= incrementRatio;
530
+ }
531
+
532
+ $.ajax({
533
+ url: ajaxurl + '?action=wpstg_processing&_=' + Date.now() / 1000,
534
+ type: 'POST',
535
+ dataType: dataType,
536
+ cache: false,
537
+ data: data,
538
+ error: function error(xhr, textStatus, errorThrown) {
539
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus); // try again after 10 seconds
540
+
541
+ tryCount++;
542
+
543
+ if (tryCount <= retryLimit) {
544
+ setTimeout(function () {
545
+ WPStagingLegacyDatabase.ajax(data, callback, dataType, showErrors, tryCount, incrementRatio);
546
+ return;
547
+ }, retryTimeout);
548
+ } else {
549
+ var errorCode = 'undefined' === typeof xhr.status ? 'Unknown' : xhr.status;
550
+ WPStagingLegacyDatabase.showError('Fatal Error: ' + errorCode + ' Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.');
551
+ }
552
+ },
553
+ success: function success(data) {
554
+ if ('function' === typeof callback) {
555
+ callback(data);
556
+ }
557
+ },
558
+ statusCode: {
559
+ 404: function _() {
560
+ if (tryCount >= retryLimit) {
561
+ WPStagingLegacyDatabase.showError('Error 404 - Can\'t find ajax request URL! Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.');
562
+ }
563
+ },
564
+ 500: function _() {
565
+ if (tryCount >= retryLimit) {
566
+ WPStagingLegacyDatabase.showError('Fatal Error 500 - Internal server error while processing the request! Please try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.');
567
+ }
568
+ },
569
+ 504: function _() {
570
+ if (tryCount > retryLimit) {
571
+ WPStagingLegacyDatabase.showError('Error 504 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ');
572
+ }
573
+ },
574
+ 502: function _() {
575
+ if (tryCount >= retryLimit) {
576
+ WPStagingLegacyDatabase.showError('Error 502 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ');
577
+ }
578
+ },
579
+ 503: function _() {
580
+ if (tryCount >= retryLimit) {
581
+ WPStagingLegacyDatabase.showError('Error 503 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ');
582
+ }
583
+ },
584
+ 429: function _() {
585
+ if (tryCount >= retryLimit) {
586
+ WPStagingLegacyDatabase.showError('Error 429 - It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href=\'https://wp-staging.com/docs/wp-staging-settings-for-small-servers/\' target=\'_blank\'>WP Staging Small Server Settings</a> or submit an error report and contact us.\n\ ');
587
+ }
588
+ },
589
+ 403: function _() {
590
+ if (tryCount >= retryLimit) {
591
+ WPStagingLegacyDatabase.showError('Refresh page or login again! The process should be finished successfully. \n\ ');
592
+ }
593
+ }
594
+ }
595
+ });
596
+ },
597
+ modal: {
598
+ create: {
599
+ html: null,
600
+ confirmBtnTxt: null
601
+ },
602
+ process: {
603
+ html: null,
604
+ cancelBtnTxt: null,
605
+ modal: null
606
+ },
607
+ download: {
608
+ html: null
609
+ },
610
+ "import": {
611
+ html: null,
612
+ btnTxtNext: null,
613
+ btnTxtConfirm: null,
614
+ btnTxtCancel: null,
615
+ searchReplaceForm: null,
616
+ file: null,
617
+ containerUpload: null,
618
+ containerFilesystem: null,
619
+ setFile: function setFile(file, upload) {
620
+ if (upload === void 0) {
621
+ upload = true;
622
+ }
623
+
624
+ var toUnit = function toUnit(bytes) {
625
+ var i = Math.floor(Math.log(bytes) / Math.log(1024));
626
+ return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
627
+ };
628
+
629
+ if (!file) {
630
+ return;
631
+ }
632
+
633
+ WPStagingLegacyDatabase.modal["import"].file = file;
634
+ WPStagingLegacyDatabase.modal["import"].data.file = file.name;
635
+ console.log("File " + file.name);
636
+ $('.wpstg--backup--import--selected-file').html(file.name + " <br /> (" + toUnit(file.size) + ")").show();
637
+ $('.wpstg--drag').hide();
638
+ $('.wpstg--drag-or-upload').show();
639
+
640
+ if (upload) {
641
+ $('.wpstg--modal--actions .swal2-confirm').prop('disabled', true);
642
+ WPStagingLegacyDatabase.upload.start();
643
+ }
644
+ },
645
+ baseDirectory: null,
646
+ data: {
647
+ file: null,
648
+ search: [],
649
+ replace: []
650
+ }
651
+ }
652
+ },
653
+ messages: {
654
+ WARNING: 'warning',
655
+ ERROR: 'error',
656
+ INFO: 'info',
657
+ DEBUG: 'debug',
658
+ CRITICAL: 'critical',
659
+ data: {
660
+ all: [],
661
+ // TODO RPoC
662
+ info: [],
663
+ error: [],
664
+ critical: [],
665
+ warning: [],
666
+ debug: []
667
+ },
668
+ shouldWarn: function shouldWarn() {
669
+ return WPStagingLegacyDatabase.messages.data.error.length > 0 || WPStagingLegacyDatabase.messages.data.critical.length > 0;
670
+ },
671
+ countByType: function countByType(type) {
672
+ if (type === void 0) {
673
+ type = WPStagingLegacyDatabase.messages.ERROR;
674
+ }
675
+
676
+ return WPStagingLegacyDatabase.messages.data[type].length;
677
+ },
678
+ addMessage: function addMessage(message) {
679
+ if (Array.isArray(message)) {
680
+ message.forEach(function (item) {
681
+ WPStagingLegacyDatabase.messages.addMessage(item);
682
+ });
683
+ return;
684
+ }
685
+
686
+ var type = message.type.toLowerCase() || 'info';
687
+
688
+ if (!WPStagingLegacyDatabase.messages.data[type]) {
689
+ WPStagingLegacyDatabase.messages.data[type] = [];
690
+ }
691
+
692
+ WPStagingLegacyDatabase.messages.data.all.push(message); // TODO RPoC
693
+
694
+ WPStagingLegacyDatabase.messages.data[type].push(message);
695
+ },
696
+ reset: function reset() {
697
+ WPStagingLegacyDatabase.messages.data = {
698
+ all: [],
699
+ info: [],
700
+ error: [],
701
+ critical: [],
702
+ warning: [],
703
+ debug: []
704
+ };
705
+ }
706
+ },
707
+ timer: {
708
+ totalSeconds: 0,
709
+ interval: null,
710
+ start: function start() {
711
+ if (null !== WPStagingLegacyDatabase.timer.interval) {
712
+ return;
713
+ }
714
+
715
+ var prettify = function prettify(seconds) {
716
+ console.log("Process running for " + seconds + " seconds"); // If potentially anything can exceed 24h execution time than that;
717
+ // const _seconds = parseInt(seconds, 10)
718
+ // const hours = Math.floor(_seconds / 3600)
719
+ // const minutes = Math.floor(_seconds / 60) % 60
720
+ // seconds = _seconds % 60
721
+ //
722
+ // return [hours, minutes, seconds]
723
+ // .map(v => v < 10 ? '0' + v : v)
724
+ // .filter((v,i) => v !== '00' || i > 0)
725
+ // .join(':')
726
+ // ;
727
+ // Are we sure we won't create anything that exceeds 24h execution time? If not then this;
728
+
729
+ return "" + new Date(seconds * 1000).toISOString().substr(11, 8);
730
+ };
731
+
732
+ WPStagingLegacyDatabase.timer.interval = setInterval(function () {
733
+ $('.wpstg--modal--process--elapsed-time').text(prettify(WPStagingLegacyDatabase.timer.totalSeconds));
734
+ WPStagingLegacyDatabase.timer.totalSeconds++;
735
+ }, 1000);
736
+ },
737
+ stop: function stop() {
738
+ WPStagingLegacyDatabase.timer.totalSeconds = 0;
739
+
740
+ if (WPStagingLegacyDatabase.timer.interval) {
741
+ clearInterval(WPStagingLegacyDatabase.timer.interval);
742
+ WPStagingLegacyDatabase.timer.interval = null;
743
+ }
744
+ }
745
+ },
746
+ upload: {
747
+ reader: null,
748
+ file: null,
749
+ iop: 1000 * 1024,
750
+ uploadInfo: function uploadInfo(isShow) {
751
+ var $containerUpload = $('.wpstg--modal--import--upload--process');
752
+ var $containerUploader = $('.wpstg--uploader');
753
+
754
+ if (isShow) {
755
+ $containerUpload.css('display', 'flex');
756
+ $containerUploader.hide();
757
+ return;
758
+ }
759
+
760
+ $containerUploader.css('display', 'flex');
761
+ $containerUpload.hide();
762
+ },
763
+ start: function start() {
764
+ console.log("file " + WPStagingLegacyDatabase.modal["import"].data.file);
765
+ WPStagingLegacyDatabase.upload.reader = new FileReader();
766
+ WPStagingLegacyDatabase.upload.file = WPStagingLegacyDatabase.modal["import"].file;
767
+ WPStagingLegacyDatabase.upload.uploadInfo(true);
768
+ WPStagingLegacyDatabase.upload.sendChunk();
769
+ },
770
+ sendChunk: function sendChunk(startsAt) {
771
+ if (startsAt === void 0) {
772
+ startsAt = 0;
773
+ }
774
+
775
+ if (!WPStagingLegacyDatabase.upload.file) {
776
+ return;
777
+ }
778
+
779
+ var isReset = startsAt < 1;
780
+ var endsAt = startsAt + WPStagingLegacyDatabase.upload.iop + 1;
781
+ var blob = WPStagingLegacyDatabase.upload.file.slice(startsAt, endsAt);
782
+
783
+ WPStagingLegacyDatabase.upload.reader.onloadend = function (event) {
784
+ if (event.target.readyState !== FileReader.DONE) {
785
+ return;
786
+ }
787
+
788
+ var body = new FormData();
789
+ body.append('accessToken', wpstg.accessToken);
790
+ body.append('nonce', wpstg.nonce);
791
+ body.append('data', event.target.result);
792
+ body.append('filename', WPStagingLegacyDatabase.upload.file.name);
793
+ body.append('reset', isReset ? '1' : '0');
794
+ fetch(ajaxurl + "?action=wpstg--backups--import--file-upload", {
795
+ method: 'POST',
796
+ body: body
797
+ }).then(WPStagingLegacyDatabase.handleFetchErrors).then(function (res) {
798
+ return res.json();
799
+ }).then(function (res) {
800
+ WPStagingLegacyDatabase.showAjaxFatalError(res, '', 'Submit an error report.');
801
+ var writtenBytes = startsAt + WPStagingLegacyDatabase.upload.iop;
802
+ var percent = Math.floor(writtenBytes / WPStagingLegacyDatabase.upload.file.size * 100);
803
+
804
+ if (endsAt >= WPStagingLegacyDatabase.upload.file.size) {
805
+ WPStagingLegacyDatabase.upload.uploadInfo(false);
806
+ WPStagingLegacyDatabase.isLoading(false);
807
+ $('.wpstg--modal--actions .swal2-confirm').prop('disabled', false);
808
+ return;
809
+ }
810
+
811
+ $('.wpstg--modal--import--upload--progress--title > span').text(percent);
812
+ $('.wpstg--modal--import--upload--progress').css('width', percent + "%");
813
+ WPStagingLegacyDatabase.upload.sendChunk(endsAt);
814
+ })["catch"](function (e) {
815
+ return WPStagingLegacyDatabase.showAjaxFatalError(e, '', 'Submit an error report.');
816
+ });
817
+ };
818
+
819
+ WPStagingLegacyDatabase.upload.reader.readAsDataURL(blob);
820
+ }
821
+ },
822
+ status: {
823
+ hasResponse: null,
824
+ reTryAfter: 5000
825
+ },
826
+ fetchListing: function fetchListing(isResetErrors) {
827
+ if (isResetErrors === void 0) {
828
+ isResetErrors = true;
829
+ }
830
+
831
+ WPStagingLegacyDatabase.isLoading(true);
832
+
833
+ if (isResetErrors) {
834
+ WPStagingLegacyDatabase.resetErrors();
835
+ }
836
+
837
+ return fetch(ajaxurl + "?action=wpstg--backups--database-legacy-listing&_=" + Math.random() + "&accessToken=" + wpstg.accessToken + "&nonce=" + wpstg.nonce).then(WPStagingLegacyDatabase.handleFetchErrors).then(function (res) {
838
+ return res.json();
839
+ }).then(function (res) {
840
+ WPStagingLegacyDatabase.showAjaxFatalError(res, '', 'Submit an error report.');
841
+ WPStagingLegacyDatabase.cache.get('#wpstg--tab--database-backups').html(res);
842
+ WPStagingLegacyDatabase.isLoading(false);
843
+ return res;
844
+ })["catch"](function (e) {
845
+ return WPStagingLegacyDatabase.showAjaxFatalError(e, '', 'Submit an error report.');
846
+ });
847
+ },
848
+ "delete": function _delete() {
849
+ $('#wpstg--tab--database-backups').off('click', '.wpstg-delete-backup[data-id]').on('click', '.wpstg-delete-backup[data-id]', function (e) {
850
+ e.preventDefault();
851
+ WPStagingLegacyDatabase.resetErrors();
852
+ WPStagingLegacyDatabase.isLoading(true);
853
+ WPStagingLegacyDatabase.cache.get('#wpstg-existing-database-backups').hide();
854
+ var id = this.getAttribute('data-id');
855
+ WPStagingLegacyDatabase.ajax({
856
+ action: 'wpstg--backups--database-legacy-delete-confirm',
857
+ id: id,
858
+ accessToken: wpstg.accessToken,
859
+ nonce: wpstg.nonce
860
+ }, function (response) {
861
+ WPStagingLegacyDatabase.showAjaxFatalError(response, '', ' Please submit an error report by using the REPORT ISSUE button.');
862
+ WPStagingLegacyDatabase.isLoading(false);
863
+ WPStagingLegacyDatabase.cache.get('#wpstg-delete-confirmation').html(response);
864
+ });
865
+ }) // Delete final confirmation page
866
+ .off('click', '#wpstg-delete-backup').on('click', '#wpstg-delete-backup', function (e) {
867
+ e.preventDefault();
868
+ WPStagingLegacyDatabase.resetErrors();
869
+ WPStagingLegacyDatabase.isLoading(true);
870
+ var id = this.getAttribute('data-id');
871
+ WPStagingLegacyDatabase.ajax({
872
+ action: 'wpstg--backups--database-legacy-delete',
873
+ id: id,
874
+ accessToken: wpstg.accessToken,
875
+ nonce: wpstg.nonce
876
+ }, function (response) {
877
+ WPStagingLegacyDatabase.showAjaxFatalError(response, '', ' Please submit an error report by using the REPORT ISSUE button.'); // noinspection JSIgnoredPromiseFromCall
878
+
879
+ WPStagingLegacyDatabase.fetchListing();
880
+ WPStagingLegacyDatabase.isLoading(false);
881
+ });
882
+ }).off('click', '#wpstg-cancel-backup-delete').on('click', '#wpstg-cancel-backup-delete', function (e) {
883
+ e.preventDefault();
884
+ WPStagingLegacyDatabase.isLoading(false); // noinspection JSIgnoredPromiseFromCall
885
+
886
+ WPStagingLegacyDatabase.fetchListing();
887
+ }); // Force delete if backup tables do not exist
888
+ // TODO This is bloated, no need extra ID, use existing one?
889
+
890
+ $('#wpstg-error-wrapper').off('click', '#wpstg-backup-force-delete').on('click', '#wpstg-backup-force-delete', function (e) {
891
+ e.preventDefault();
892
+ WPStagingLegacyDatabase.resetErrors();
893
+ WPStagingLegacyDatabase.isLoading(true);
894
+ var id = this.getAttribute('data-id');
895
+
896
+ if (!confirm('Do you want to delete this backup ' + id + ' from the listed backups?')) {
897
+ WPStagingLegacyDatabase.isLoading(false);
898
+ return false;
899
+ }
900
+
901
+ WPStagingLegacyDatabase.ajax({
902
+ action: 'wpstg--backups--database-legacy-delete',
903
+ id: id,
904
+ accessToken: wpstg.accessToken,
905
+ nonce: wpstg.nonce
906
+ }, function (response) {
907
+ WPStagingLegacyDatabase.showAjaxFatalError(response, '', ' Please submit an error report by using the REPORT ISSUE button.'); // noinspection JSIgnoredPromiseFromCall
908
+
909
+ WPStagingLegacyDatabase.fetchListing();
910
+ WPStagingLegacyDatabase.isLoading(false);
911
+ });
912
+ });
913
+ },
914
+ restore: function restore() {
915
+ var restoreBackup = function restoreBackup(prefix, reset) {
916
+ WPStagingLegacyDatabase.isLoading(true);
917
+ WPStagingLegacyDatabase.resetErrors();
918
+
919
+ if (typeof reset === 'undefined') {
920
+ reset = false;
921
+ }
922
+
923
+ WPStaging.ajax({
924
+ action: 'wpstg--backups--database-legacy-restore',
925
+ accessToken: wpstg.accessToken,
926
+ nonce: wpstg.nonce,
927
+ wpstg: {
928
+ tasks: {
929
+ backup: {
930
+ database: {
931
+ create: {
932
+ source: prefix,
933
+ reset: reset
934
+ }
935
+ }
936
+ }
937
+ }
938
+ }
939
+ }, function (response) {
940
+ if (typeof response === 'undefined') {
941
+ setTimeout(function () {
942
+ restoreBackup(prefix);
943
+ }, wpstg.delayReq);
944
+ return;
945
+ }
946
+
947
+ WPStagingLegacyDatabase.processResponse(response);
948
+
949
+ if (response.status === false || response.job_done === false) {
950
+ restoreBackup(prefix);
951
+ } else if (response.status === true && response.job_done === true) {
952
+ WPStagingLegacyDatabase.isLoading(false);
953
+ $('.wpstg--modal--process--title').text('Backup successfully restored');
954
+ setTimeout(function () {
955
+ Swal.close(); // noinspection JSIgnoredPromiseFromCall
956
+
957
+ alert('Backup successfully restored. You might have to log-in again to WordPress.');
958
+ WPStagingLegacyDatabase.fetchListing();
959
+ }, 1000);
960
+ } else {
961
+ setTimeout(function () {
962
+ restoreBackup(prefix);
963
+ }, wpstg.delayReq);
964
+ }
965
+ }, 'json', false, 0, 1.25);
966
+ };
967
+
968
+ $('#wpstg--tab--database-backups').off('click', '.wpstg--backup--restore[data-id]').on('click', '.wpstg--backup--restore[data-id]', function (e) {
969
+ e.preventDefault();
970
+ WPStagingLegacyDatabase.resetErrors();
971
+ WPStagingLegacyDatabase.ajax({
972
+ action: 'wpstg--backups--database-legacy-restore-confirm',
973
+ accessToken: wpstg.accessToken,
974
+ nonce: wpstg.nonce,
975
+ id: $(this).data('id')
976
+ }, function (data) {
977
+ WPStagingLegacyDatabase.cache.get('#wpstg--tab--database-backups').html(data);
978
+ });
979
+ }).off('click', '#wpstg--backup--restore--cancel').on('click', '#wpstg--backup--restore--cancel', function (e) {
980
+ WPStagingLegacyDatabase.resetErrors();
981
+ e.preventDefault(); // noinspection JSIgnoredPromiseFromCall
982
+
983
+ WPStagingLegacyDatabase.fetchListing();
984
+ }).off('click', '#wpstg--backup--restore[data-id]').on('click', '#wpstg--backup--restore[data-id]', function (e) {
985
+ e.preventDefault();
986
+ WPStagingLegacyDatabase.resetErrors();
987
+ var id = this.getAttribute('data-id');
988
+ WPStagingLegacyDatabase.process({
989
+ execute: function execute() {
990
+ WPStagingLegacyDatabase.messages.reset();
991
+ restoreBackup(id, true);
992
+ },
993
+ isShowCancelButton: false
994
+ });
995
+ });
996
+ },
997
+ // Edit backups name and notes
998
+ edit: function edit() {
999
+ $('#wpstg--tab--database-backups').off('click', '.wpstg--backup--edit[data-id]').on('click', '.wpstg--backup--edit[data-id]', /*#__PURE__*/function () {
1000
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(e) {
1001
+ var name, notes, _yield$Swal$fire2, formValues;
1002
+
1003
+ return regeneratorRuntime.wrap(function _callee2$(_context2) {
1004
+ while (1) {
1005
+ switch (_context2.prev = _context2.next) {
1006
+ case 0:
1007
+ e.preventDefault();
1008
+ name = $(this).data('name');
1009
+ notes = $(this).data('notes');
1010
+ _context2.next = 5;
1011
+ return Swal.fire({
1012
+ title: '',
1013
+ html: "\n <label id=\"wpstg-backup-edit-name\">Backup Name</label>\n <input id=\"wpstg-backup-edit-name-input\" class=\"swal2-input\" value=\"" + name + "\">\n <label>Additional Notes</label>\n <textarea id=\"wpstg-backup-edit-notes-textarea\" class=\"swal2-textarea\">" + notes + "</textarea>\n ",
1014
+ focusConfirm: false,
1015
+ confirmButtonText: 'Update Backup',
1016
+ showCancelButton: true,
1017
+ preConfirm: function preConfirm() {
1018
+ return {
1019
+ name: document.getElementById('wpstg-backup-edit-name-input').value || null,
1020
+ notes: document.getElementById('wpstg-backup-edit-notes-textarea').value || null
1021
+ };
1022
+ }
1023
+ });
1024
+
1025
+ case 5:
1026
+ _yield$Swal$fire2 = _context2.sent;
1027
+ formValues = _yield$Swal$fire2.value;
1028
+
1029
+ if (formValues) {
1030
+ _context2.next = 9;
1031
+ break;
1032
+ }
1033
+
1034
+ return _context2.abrupt("return");
1035
+
1036
+ case 9:
1037
+ WPStagingLegacyDatabase.ajax({
1038
+ action: 'wpstg--backups--database-legacy-edit',
1039
+ accessToken: wpstg.accessToken,
1040
+ nonce: wpstg.nonce,
1041
+ id: $(this).data('id'),
1042
+ name: formValues.name,
1043
+ notes: formValues.notes
1044
+ }, function (response) {
1045
+ WPStagingLegacyDatabase.showAjaxFatalError(response, '', 'Submit an error report.'); // noinspection JSIgnoredPromiseFromCall
1046
+
1047
+ WPStagingLegacyDatabase.fetchListing();
1048
+ });
1049
+
1050
+ case 10:
1051
+ case "end":
1052
+ return _context2.stop();
1053
+ }
1054
+ }
1055
+ }, _callee2, this);
1056
+ }));
1057
+
1058
+ return function (_x2) {
1059
+ return _ref2.apply(this, arguments);
1060
+ };
1061
+ }());
1062
+ },
1063
+ cancel: function cancel() {
1064
+ WPStagingLegacyDatabase.timer.stop();
1065
+ WPStagingLegacyDatabase.isCancelled = true;
1066
+ Swal.close();
1067
+ setTimeout(function () {
1068
+ return WPStagingLegacyDatabase.ajax({
1069
+ action: 'wpstg--backups--cancel',
1070
+ accessToken: wpstg.accessToken,
1071
+ nonce: wpstg.nonce,
1072
+ type: WPStagingLegacyDatabase.type
1073
+ }, function (response) {
1074
+ WPStagingLegacyDatabase.showAjaxFatalError(response, '', 'Submit an error report.');
1075
+ });
1076
+ }, 500);
1077
+ },
1078
+
1079
+ /**
1080
+ * If process.execute exists, process.data and process.onResponse is not used
1081
+ * process = { data: {}, onResponse: (resp) => {}, onAfterClose: () => {}, execute: () => {}, isShowCancelButton: bool }
1082
+ * @param {object} process
1083
+ */
1084
+ process: function process(_process) {
1085
+ if (typeof _process.execute !== 'function' && (!_process.data || !_process.onResponse)) {
1086
+ Swal.close();
1087
+ WPStagingLegacyDatabase.showError('process.data and / or process.onResponse is not set');
1088
+ return;
1089
+ } // TODO move to backend and get the contents as xhr response?
1090
+
1091
+
1092
+ if (!WPStagingLegacyDatabase.modal.process.html || !WPStagingLegacyDatabase.modal.process.cancelBtnTxt) {
1093
+ var $modal = $('#wpstg--modal--backup--process');
1094
+ var html = $modal.html();
1095
+ var btnTxt = $modal.attr('data-cancelButtonText');
1096
+ WPStagingLegacyDatabase.modal.process.html = html || null;
1097
+ WPStagingLegacyDatabase.modal.process.cancelBtnTxt = btnTxt || null;
1098
+ $modal.remove();
1099
+ }
1100
+
1101
+ $('body').off('click', '.wpstg--modal--process--logs--tail').on('click', '.wpstg--modal--process--logs--tail', function (e) {
1102
+ e.preventDefault();
1103
+ var container = Swal.getContainer();
1104
+ var $logs = $(container).find('.wpstg--modal--process--logs');
1105
+ $logs.toggle();
1106
+
1107
+ if ($logs.is(':visible')) {
1108
+ container.childNodes[0].style.width = '100%';
1109
+ container.style['z-index'] = 9999;
1110
+ } else {
1111
+ container.childNodes[0].style.width = '600px';
1112
+ }
1113
+ });
1114
+ _process.isShowCancelButton = false !== _process.isShowCancelButton;
1115
+ WPStagingLegacyDatabase.modal.process.modal = Swal.mixin({
1116
+ customClass: {
1117
+ cancelButton: 'wpstg--btn--cancel wpstg-blue-primary wpstg-link-btn',
1118
+ content: 'wpstg--process--content'
1119
+ },
1120
+ buttonsStyling: false
1121
+ }).fire({
1122
+ html: WPStagingLegacyDatabase.modal.process.html,
1123
+ cancelButtonText: WPStagingLegacyDatabase.modal.process.cancelBtnTxt,
1124
+ showCancelButton: _process.isShowCancelButton,
1125
+ showConfirmButton: false,
1126
+ allowOutsideClick: false,
1127
+ allowEscapeKey: false,
1128
+ width: 600,
1129
+ onRender: function onRender() {
1130
+ var _btnCancel = Swal.getContainer().getElementsByClassName('swal2-cancel wpstg--btn--cancel')[0];
1131
+
1132
+ var btnCancel = _btnCancel.cloneNode(true);
1133
+
1134
+ _btnCancel.parentNode.replaceChild(btnCancel, _btnCancel);
1135
+
1136
+ btnCancel.addEventListener('click', function (e) {
1137
+ if (confirm('Are You Sure? This will cancel the process!')) {
1138
+ Swal.close();
1139
+ }
1140
+ });
1141
+
1142
+ if (typeof _process.execute === 'function') {
1143
+ _process.execute();
1144
+
1145
+ return;
1146
+ }
1147
+
1148
+ if (!_process.data || !_process.onResponse) {
1149
+ Swal.close();
1150
+ WPStagingLegacyDatabase.showError('process.data and / or process.onResponse is not set');
1151
+ return;
1152
+ }
1153
+
1154
+ WPStagingLegacyDatabase.ajax(_process.data, _process.onResponse);
1155
+ },
1156
+ onAfterClose: function onAfterClose() {
1157
+ return typeof _process.onAfterClose === 'function' && _process.onAfterClose();
1158
+ },
1159
+ onClose: function onClose() {
1160
+ console.log('cancelled');
1161
+ WPStagingLegacyDatabase.cancel();
1162
+ }
1163
+ });
1164
+ },
1165
+ processResponse: function processResponse(response, useTitle) {
1166
+ if (response === null) {
1167
+ Swal.close();
1168
+ WPStagingLegacyDatabase.showError('Invalid Response; null');
1169
+ throw new Error("Invalid Response; " + response);
1170
+ }
1171
+
1172
+ var $container = $(Swal.getContainer());
1173
+
1174
+ var title = function title() {
1175
+ if ((response.title || response.statusTitle) && useTitle === true) {
1176
+ $container.find('.wpstg--modal--process--title').text(response.title || response.statusTitle);
1177
+ }
1178
+ };
1179
+
1180
+ var percentage = function percentage() {
1181
+ if (response.percentage) {
1182
+ $container.find('.wpstg--modal--process--percent').text(response.percentage);
1183
+ }
1184
+ };
1185
+
1186
+ var logs = function logs() {
1187
+ if (!response.messages) {
1188
+ return;
1189
+ }
1190
+
1191
+ var $logsContainer = $container.find('.wpstg--modal--process--logs');
1192
+ var stoppingTypes = [WPStagingLegacyDatabase.messages.ERROR, WPStagingLegacyDatabase.messages.CRITICAL];
1193
+
1194
+ var appendMessage = function appendMessage(message) {
1195
+ if (Array.isArray(message)) {
1196
+ for (var _iterator = _createForOfIteratorHelperLoose(message), _step; !(_step = _iterator()).done;) {
1197
+ var item = _step.value;
1198
+ appendMessage(item);
1199
+ }
1200
+
1201
+ return;
1202
+ }
1203
+
1204
+ var msgClass = "wpstg--modal--process--msg--" + message.type.toLowerCase();
1205
+ $logsContainer.append("<p class=\"" + msgClass + "\">[" + message.type + "] - [" + message.date + "] - " + message.message + "</p>");
1206
+
1207
+ if (stoppingTypes.includes(message.type.toLowerCase())) {
1208
+ WPStagingLegacyDatabase.cancel();
1209
+ setTimeout(WPStagingLegacyDatabase.logsModal, 500);
1210
+ }
1211
+ };
1212
+
1213
+ for (var _iterator2 = _createForOfIteratorHelperLoose(response.messages), _step2; !(_step2 = _iterator2()).done;) {
1214
+ var message = _step2.value;
1215
+
1216
+ if (!message) {
1217
+ continue;
1218
+ }
1219
+
1220
+ WPStagingLegacyDatabase.messages.addMessage(message);
1221
+ appendMessage(message);
1222
+ }
1223
+
1224
+ if ($logsContainer.is(':visible')) {
1225
+ $logsContainer.scrollTop($logsContainer[0].scrollHeight);
1226
+ }
1227
+
1228
+ if (!WPStagingLegacyDatabase.messages.shouldWarn()) {
1229
+ return;
1230
+ }
1231
+
1232
+ var $btnShowLogs = $container.find('.wpstg--modal--process--logs--tail');
1233
+ $btnShowLogs.html($btnShowLogs.attr('data-txt-bad'));
1234
+ $btnShowLogs.find('.wpstg--modal--logs--critical-count').text(WPStagingLegacyDatabase.messages.countByType(WPStagingLegacyDatabase.messages.CRITICAL));
1235
+ $btnShowLogs.find('.wpstg--modal--logs--error-count').text(WPStagingLegacyDatabase.messages.countByType(WPStagingLegacyDatabase.messages.ERROR));
1236
+ $btnShowLogs.find('.wpstg--modal--logs--warning-count').text(WPStagingLegacyDatabase.messages.countByType(WPStagingLegacyDatabase.messages.WARNING));
1237
+ };
1238
+
1239
+ title();
1240
+ percentage();
1241
+ logs();
1242
+
1243
+ if (response.status === true &&