PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.1.1
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.1.1
4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Backend / Administrator.php
wp-staging / Backend Last commit date
Activation 2 years ago Feedback 1 year ago Modules 1 year ago Optimizer 1 year ago Pluginmeta 1 year ago Upgrade 1 year ago Administrator.php 1 year ago
Administrator.php
1239 lines
1 <?php
2
3 namespace WPStaging\Backend;
4
5 use WPStaging\Backend\Modules\Jobs\Job;
6 use WPStaging\Core\WPStaging;
7 use WPStaging\Core\DTO\Settings;
8 use WPStaging\Framework\Analytics\Actions\AnalyticsStagingReset;
9 use WPStaging\Framework\Analytics\Actions\AnalyticsStagingUpdate;
10 use WPStaging\Framework\Assets\Assets;
11 use WPStaging\Framework\Facades\Hooks;
12 use WPStaging\Framework\SiteInfo;
13 use WPStaging\Framework\Security\Auth;
14 use WPStaging\Framework\Mails\Report\Report;
15 use WPStaging\Framework\Filesystem\Filters\ExcludeFilter;
16 use WPStaging\Framework\Filesystem\PathIdentifier;
17 use WPStaging\Framework\TemplateEngine\TemplateEngine;
18 use WPStaging\Framework\Utils\Math;
19 use WPStaging\Framework\Utils\WpDefaultDirectories;
20 use WPStaging\Framework\Notices\DismissNotice;
21 use WPStaging\Staging\Sites;
22 use WPStaging\Backend\Modules\Jobs\Cancel;
23 use WPStaging\Backend\Modules\Jobs\CancelUpdate;
24 use WPStaging\Backend\Modules\Jobs\Cloning;
25 use WPStaging\Backend\Modules\Jobs\Updating;
26 use WPStaging\Backend\Modules\Jobs\Scan;
27 use WPStaging\Backend\Modules\Jobs\Logs;
28 use WPStaging\Backend\Modules\Jobs\ProcessLock;
29 use WPStaging\Backend\Modules\SystemInfo;
30 use WPStaging\Backend\Modules\Views\Tabs\Tabs;
31 use WPStaging\Backend\Modules\Views\Forms\Settings as FormSettings;
32 use WPStaging\Backend\Activation;
33 use WPStaging\Backend\Pro\Modules\Jobs\Processing;
34 use WPStaging\Backend\Pro\Modules\Jobs\Backups\BackupUploadsDir;
35 use WPStaging\Backend\Pluginmeta\Pluginmeta;
36 use WPStaging\Framework\Database\SelectedTables;
37 use WPStaging\Framework\Utils\Sanitize;
38 use WPStaging\Backend\Pro\Modules\Jobs\Scan as ScanProModule;
39 use WPStaging\Backend\Feedback\Feedback;
40 use WPStaging\Core\CloningJobProvider;
41 use WPStaging\Framework\Utils\PluginInfo;
42 use WPStaging\Framework\Security\Nonce;
43
44 /**
45 * Class Administrator
46 * @package WPStaging\Backend
47 */
48 class Administrator
49 {
50 /**
51 * @var int Place WP Staging Menu below Plugins
52 */
53 const MENU_POSITION_ORDER = 65;
54
55 /**
56 * @var int Place WP Staging Menu below Plugins for multisite
57 */
58 const MENU_POSITION_ORDER_MULTISITE = 20;
59
60 /**
61 * @var string
62 */
63 const FILTER_MAIN_SETTING_TABS = 'wpstg.main_settings_tabs';
64
65 /** @var string */
66 private $viewsPath;
67
68 /**
69 * @var Assets
70 */
71 private $assets;
72
73 /**
74 * @var Auth
75 */
76 private $auth;
77
78 /**
79 * @var SiteInfo
80 */
81 private $siteInfo;
82
83 /** @var Sanitize */
84 private $sanitize;
85
86 /** @var Report */
87 private $report;
88
89 /** @var PluginInfo */
90 private $pluginInfo;
91
92 public function __construct()
93 {
94 $this->auth = WPStaging::make(Auth::class);
95 $this->assets = WPStaging::make(Assets::class);
96 $this->siteInfo = WPStaging::make(SiteInfo::class);
97 $this->report = WPStaging::make(Report::class);
98 $this->pluginInfo = WPStaging::make(PluginInfo::class);
99 $this->viewsPath = WPSTG_VIEWS_DIR;
100
101 $this->defineHooks();
102
103 $this->sanitize = WPStaging::make(Sanitize::class);
104
105 // Load plugins meta data
106 $this->loadMeta();
107 }
108
109 /**
110 * Load plugin meta data
111 */
112 public function loadMeta()
113 {
114 new Pluginmeta();
115 }
116
117 /**
118 * Define Hooks
119 */
120 private function defineHooks()
121 {
122 if (!defined('WPSTGPRO_VERSION')) {
123 new Activation\Welcome();
124 }
125
126 if ($this->pluginInfo->canShowAdminMenu()) {
127 add_action("admin_menu", [$this, "addMenu"], 10);
128 add_action('network_admin_menu', [$this, "addMenu"]);
129 }
130
131 add_action("admin_init", [$this, "upgrade"]);
132 add_action("admin_post_wpstg_download_sysinfo", [$this, "downloadSystemInfoAndLogFiles"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
133
134 if (defined('WPSTGPRO_VERSION') && class_exists('WPStaging\Backend\Pro\WpstgRestoreDownloader')) {
135 add_action("admin_post_wpstg_download_restorer", [$this, "downloadWpstgRestoreFile"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
136 }
137
138 if (!defined('WPSTGPRO_VERSION') && $this->isPluginsPage()) {
139 add_filter('admin_footer', [$this, 'loadFeedbackForm']);
140 }
141
142 // Ajax Requests
143 add_action("wp_ajax_wpstg_scanning", [$this, "ajaxCloneScan"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
144 add_action("wp_ajax_wpstg_check_clone", [$this, "ajaxCheckCloneDirectoryName"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
145 add_action("wp_ajax_wpstg_restart", [$this, "ajaxRestart"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
146 add_action("wp_ajax_wpstg_update", [$this, "ajaxUpdateProcess"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
147 add_action("wp_ajax_wpstg_reset", [$this, "ajaxResetProcess"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
148 add_action("wp_ajax_wpstg_cloning", [$this, "ajaxStartClone"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
149 add_action("wp_ajax_wpstg_processing", [$this, "ajaxCloneDatabase"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
150 add_action("wp_ajax_wpstg_clone_prepare_directories", [$this, "ajaxPrepareDirectories"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
151 add_action("wp_ajax_wpstg_clone_files", [$this, "ajaxCopyFiles"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
152 add_action("wp_ajax_wpstg_clone_replace_data", [$this, "ajaxReplaceData"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
153 add_action("wp_ajax_wpstg_clone_finish", [$this, "ajaxFinish"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
154 add_action("wp_ajax_wpstg_cancel_clone", [$this, "ajaxCancelClone"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
155 add_action("wp_ajax_wpstg_cancel_update", [$this, "ajaxCancelUpdate"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
156 add_action("wp_ajax_wpstg_hide_rating", [$this, "ajaxHideRating"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
157 add_action("wp_ajax_wpstg_hide_later", [$this, "ajaxHideLaterRating"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
158 add_action("wp_ajax_wpstg_hide_beta", [$this, "ajaxHideBeta"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
159 add_action("wp_ajax_wpstg_logs", [$this, "ajaxLogs"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
160 add_action("wp_ajax_wpstg_check_disk_space", [$this, "ajaxCheckFreeSpace"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
161 add_action("wp_ajax_wpstg_send_report", [$this, "ajaxSendReport"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
162 add_action("wp_ajax_wpstg_send_feedback", [$this, "sendFeedback"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
163 add_action("wp_ajax_wpstg_enable_staging_cloning", [$this, "ajaxEnableStagingCloning"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
164 add_action("wp_ajax_wpstg_clone_excludes_settings", [$this, "ajaxCloneExcludesSettings"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
165 add_action("wp_ajax_wpstg_fetch_dir_children", [$this, "ajaxFetchDirChildren"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
166 add_action("wp_ajax_wpstg_modal_error", [$this, "ajaxModalError"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
167 add_action("wp_ajax_wpstg_dismiss_notice", [$this, "ajaxDismissNotice"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
168 add_action("wp_ajax_wpstg_restore_settings", [$this, "ajaxRestoreSettings"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
169 add_action("wp_ajax_wpstg_send_debug_log_report", [$this->report, "ajaxSendDebugLog"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
170
171 // Ajax hooks pro Version
172 // TODO: move all below actions to pro service provider?
173 add_action("wp_ajax_wpstg_scan", [$this, "ajaxPushScan"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
174 add_action("wp_ajax_wpstg_push_tables", [$this, "ajaxPushTables"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
175 add_action("wp_ajax_wpstg_push_processing", [$this, "ajaxPushProcessing"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
176 add_action("wp_ajax_nopriv_wpstg_push_processing", [$this, "ajaxPushProcessing"]); // phpcs:ignore WPStaging.Security.AuthorizationChecked
177
178 // TODO: replace uploads backup during push once we have backups PR ready,
179 // Then there will be no need to have any cron to delete those backups
180 if (class_exists('WPStaging\Backend\Pro\Modules\Jobs\Backups\BackupUploadsDir')) {
181 add_action(BackupUploadsDir::BACKUP_DELETE_CRON_HOOK_NAME, [$this, "removeOldUploadsBackup"]); // phpcs:ignore WPStaging.Security.FirstArgNotAString -- Cron callback
182 }
183 }
184
185 /**
186 * Load Feedback Form on plugins.php
187 */
188 public function loadFeedbackForm()
189 {
190 $form = WPStaging::make(Feedback::class);
191 $form->loadForm();
192 }
193
194 /**
195 * Send Feedback data via mail
196 */
197 public function sendFeedback()
198 {
199 if (!$this->isAuthenticated()) {
200 return;
201 }
202
203 $form = WPStaging::make(Feedback::class);
204 $form->sendDeactivateFeedback();
205 }
206
207 /**
208 * Upgrade routine
209 * @action admin_init 10 0
210 * @see \WPStaging\Backend\Administrator::defineHooks
211 */
212 public function upgrade()
213 {
214 if (defined('WPSTGPRO_VERSION') && class_exists('WPStaging\Backend\Pro\Upgrade\Upgrade')) {
215 $upgrade = WPStaging::make('WPStaging\Backend\Pro\Upgrade\Upgrade');
216 } else {
217 $upgrade = WPStaging::make('WPStaging\Backend\Upgrade\Upgrade');
218 }
219 $upgrade->doUpgrade();
220 }
221
222 /**
223 * Add Admin Menu(s)
224 */
225 public function addMenu()
226 {
227 global $wp_version;
228 $logo = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiIGZpbGw9Im5vbmUiPgo8Zz48Zz48cGF0aCBzdHlsZT0iZmlsbDojZmZmIiAgZD0iTTEzNy42LDU2MS4zSDEzLjhIMTB2MzA2LjNsOTAuNy04My40QzE4OS42LDkwOC43LDMzNS4zLDk5MCw1MDAsOTkwYzI0OS45LDAsNDU2LjEtMTg3LjEsNDg2LjItNDI4LjhIODYyLjRDODMzLjMsNzM1LjEsNjgyLjEsODY3LjUsNTAwLDg2Ny41Yy0xMjksMC0yNDIuNS02Ni41LTMwOC4xLTE2Ny4ybDE1MS4zLTEzOS4xSDEzNy42eiIvPjxwYXRoIHN0eWxlPSJmaWxsOiNmZmYiICBkPSJNNTAwLDEwQzI1MC4xLDEwLDQzLjksMTk3LjEsMTMuOCw0MzguOGgxMjMuOEMxNjYuNywyNjQuOSwzMTcuOSwxMzIuNSw1MDAsMTMyLjVjMTMyLjksMCwyNDkuMyw3MC41LDMxMy44LDE3Ni4yTDY4My44LDQzOC44aDEyMi41aDU2LjJoMTIzLjhoMy44VjEzMi41bC04Ny43LDg3LjdDODEzLjgsOTMuMSw2NjYuNiwxMCw1MDAsMTB6Ii8+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvZz4KPC9zdmc+';
229
230 $pos = self::MENU_POSITION_ORDER;
231 if (is_multisite()) {
232 $pos = self::MENU_POSITION_ORDER_MULTISITE;
233 }
234
235 // Menu Position order needs to be unique for WordPress < 4.4
236 // We are not using a unique position by default to keep WP Staging directly below plugin menu
237 if (version_compare($wp_version, '4.4', '<')) {
238 $pos++;
239 }
240
241 $proSlug = defined('WPSTGPRO_VERSION') ? 'Pro' : '';
242
243 $defaultPageSlug = "wpstg_clone";
244 $defaultPageCallback = "getClonePage";
245 $defaultPageTitle = esc_html__("Staging Sites", "wp-staging");
246 $secondaryPageSlug = "wpstg_backup";
247 $secondaryPageCallback = "getBackupPage";
248 $secondaryPageTitle = esc_html__("Backup & Migration", "wp-staging");
249 /** @var SiteInfo */
250 $siteInfo = WPStaging::make(SiteInfo::class);
251 if ($siteInfo->isHostedOnWordPressCom()) {
252 $defaultPageSlug = "wpstg_backup";
253 $defaultPageCallback = "getBackupPage";
254 $defaultPageTitle = esc_html__("Backup & Migration", "wp-staging");
255 $secondaryPageSlug = "wpstg_clone";
256 $secondaryPageCallback = "getClonePage";
257 $secondaryPageTitle = esc_html__("Staging Sites", "wp-staging");
258 }
259
260 // Main WP Staging Menu
261 add_menu_page(
262 "WP STAGING",
263 __("WP Staging " . $proSlug, "wp-staging"),
264 "manage_options",
265 $defaultPageSlug,
266 [$this, $defaultPageCallback],
267 $logo,
268 $pos
269 );
270
271 // Clone page normally but backup page on WordPress.com
272 add_submenu_page(
273 $defaultPageSlug,
274 __("WP Staging Jobs", "wp-staging"),
275 $defaultPageTitle,
276 "manage_options",
277 $defaultPageSlug,
278 [$this, $defaultPageCallback]
279 );
280
281 // Backup page normally but clone page on WordPress.com
282 add_submenu_page(
283 $defaultPageSlug,
284 __("WP Staging Jobs", "wp-staging"),
285 $secondaryPageTitle,
286 "manage_options",
287 $secondaryPageSlug,
288 [$this, $secondaryPageCallback]
289 );
290
291 // Page: Temporary Logins
292 add_submenu_page(
293 $defaultPageSlug,
294 __("Temporary Logins", "wp-staging"),
295 __("Temporary Logins", "wp-staging"),
296 "manage_options",
297 "wpstg-settings&tab=temporary-login",
298 [$this, "getTempLoginsPage"]
299 );
300
301 // Page: Settings
302 add_submenu_page(
303 $defaultPageSlug,
304 __("WP Staging Settings", "wp-staging"),
305 __("Settings", "wp-staging"),
306 "manage_options",
307 "wpstg-settings",
308 [$this, "getSettingsPage"]
309 );
310
311 // Page: Tools
312 add_submenu_page(
313 $defaultPageSlug,
314 __("WP Staging Tools", "wp-staging"),
315 __("System Info", "wp-staging"),
316 "manage_options",
317 "wpstg-tools",
318 [$this, "getToolsPage"]
319 );
320
321 if (!defined('WPSTGPRO_VERSION')) {
322 // Page: Tools
323 add_submenu_page(
324 $defaultPageSlug,
325 __("WP Staging Welcome", "wp-staging"),
326 __("Get WP Staging Pro", "wp-staging"),
327 "manage_options",
328 "wpstg-welcome",
329 [$this, "getWelcomePage"]
330 );
331 }
332
333 if (defined('WPSTGPRO_VERSION')) {
334 // Page: wpstg-restorer
335 add_submenu_page(
336 $defaultPageSlug,
337 __("WP Staging | Restore", "wp-staging"),
338 '',
339 "manage_options",
340 "wpstg-restorer",
341 [$this, "getRestorerPage"]
342 );
343
344 // Remove wpstg-restorer side menu
345 add_filter('submenu_file', function ($submenu_file) use ($defaultPageSlug) {
346 remove_submenu_page($defaultPageSlug, 'wpstg-restorer');
347 });
348
349 // Page: License
350 add_submenu_page(
351 $defaultPageSlug,
352 __("WP Staging License", "wp-staging"),
353 __("License", "wp-staging"),
354 "manage_options",
355 "wpstg-license",
356 [$this, "getLicensePage"]
357 );
358 }
359 }
360
361 /**
362 * Settings Page
363 */
364 public function getSettingsPage()
365 {
366
367 $license = get_option('wpstg_license_status');
368
369 // Tabs
370 $tabs = new Tabs(Hooks::applyFilters(self::FILTER_MAIN_SETTING_TABS, [
371 "general" => __("General", "wp-staging")
372 ]));
373
374 WPStaging::getInstance()
375 // Set tabs
376 ->set("tabs", $tabs)
377 // Forms
378 ->set("forms", new FormSettings($tabs));
379
380 require_once "{$this->viewsPath}settings/main-settings.php";
381 }
382
383 /**
384 * Clone Page
385 */
386 public function getClonePage()
387 {
388
389 $license = get_option('wpstg_license_status');
390
391 $availableClones = get_option(Sites::STAGING_SITES_OPTION, []);
392
393 $isStagingPage = true;
394 $isBackupPage = false;
395 require_once "{$this->viewsPath}clone/index.php";
396 }
397
398 /**
399 * Backup & Migration Page
400 */
401 public function getBackupPage()
402 {
403 $license = get_option('wpstg_license_status');
404
405 // Existing clones
406 $availableClones = get_option(Sites::STAGING_SITES_OPTION, []);
407
408 $isBackupPage = true;
409 $isStagingPage = false;
410 require_once "{$this->viewsPath}clone/index.php";
411 }
412
413 /**
414 * Welcome Page
415 */
416 public function getWelcomePage()
417 {
418 if (defined('WPSTGPRO_VERSION')) {
419 return;
420 }
421
422 require_once "{$this->viewsPath}welcome/welcome.php";
423 }
424
425 /**
426 * Tools Page
427 */
428 public function getToolsPage()
429 {
430 // Tabs
431 $tabs = new Tabs([
432 "system-info" => __("System Info", "wp-staging")
433 ]);
434
435 WPStaging::getInstance()->set("tabs", $tabs);
436
437 WPStaging::getInstance()->set("systemInfo", new SystemInfo());
438
439 // Get license data
440 $license = get_option('wpstg_license_status');
441
442 require_once "{$this->viewsPath}tools/index.php";
443 }
444
445 /**
446 * WP Staging Restore Page
447 * @todo Move this to Pro namespace
448 */
449 public function getRestorerPage()
450 {
451 // Get license data
452 $license = get_option('wpstg_license_status');
453
454 require_once "{$this->viewsPath}pro/wpstg-restorer-ui.php";
455 }
456
457 /**
458 * Download wpstg-restore.php file.
459 * @see dev/docs/wpstg-restore/README.md
460 * @return void
461 * @todo Move this to Pro namespace
462 */
463 public function downloadWpstgRestoreFile()
464 {
465 if (!defined('WPSTGPRO_VERSION') || !class_exists('WPStaging\Backend\Pro\WpstgRestoreDownloader', false)) {
466 wp_die('Invalid access', 'WP Staging Restore', ['response' => 403, 'back_link' => true]);
467 }
468
469 $WpstgRestore = WPStaging::make('WPStaging\Backend\Pro\WpstgRestoreDownloader');
470 $WpstgRestore->downloadFile();
471 }
472
473 /**
474 * Download System Information and latest log files.
475 * @return void
476 */
477 public function downloadSystemInfoAndLogFiles()
478 {
479 if (!current_user_can("update_plugins")) {
480 return;
481 }
482
483 $reportHandle = WPStaging::make(Report::class);
484 $downloadFile = $reportHandle->getBundledLogs();
485 if (empty($downloadFile)) {
486 wp_die('Failed to get All Log Files', 'WP Staging', ['response' => 200, 'back_link' => true]);
487 }
488
489 $isZipFile = count($downloadFile) === 1 && substr($downloadFile[0], -4) === '.zip';
490
491 nocache_headers();
492
493 if ($isZipFile) {
494 header('Content-Type: application/zip');
495 header('Content-Disposition: attachment; filename="wpstg-bundled-logs.zip"');
496 readfile($downloadFile[0]); // phpcs:ignore
497 $reportHandle->deleteBundledLogs();
498 exit();
499 }
500
501 header('Content-Type: text/plain');
502 header('Content-Disposition: attachment; filename="wpstg-bundled-logs.txt"');
503
504 $separator = "\n\n" . str_repeat('-', 100) . "\n\n";
505
506 foreach ($downloadFile as $logFile) {
507 $header = $separator . 'Log File: ' . basename($logFile) . $separator;
508 echo esc_html($header);
509 readfile($logFile); // phpcs:ignore
510 }
511
512 $reportHandle->deleteBundledLogs();
513 exit();
514 }
515
516 /**
517 * Render a view file
518 * @param string $file
519 * @param array $vars
520 * @return string
521 */
522 public function render($file, $vars = [])
523 {
524 $fullPath = $this->viewsPath . $file . ".php";
525 $fullPath = wp_normalize_path($fullPath);
526
527 if (!file_exists($fullPath) || !is_readable($fullPath)) {
528 return "Can't render : {$fullPath} either file doesn't exist or can't read it";
529 }
530
531 $contents = @file_get_contents($fullPath);
532
533 // Variables are set
534 if (count($vars) > 0) {
535 $vars = array_combine(
536 array_map(function ($key) {
537 return "{{" . $key . "}}";
538 }, array_keys($vars)),
539 $vars
540 );
541
542 $contents = str_replace(array_keys($vars), array_values($vars), $contents);
543 }
544
545 return $contents;
546 }
547
548 /**
549 * @return bool Whether the current request is considered to be authenticated.
550 */
551 private function isAuthenticated($nonce = Nonce::WPSTG_NONCE)
552 {
553 return $this->auth->isAuthenticatedRequest($nonce);
554 }
555
556 /**
557 * Restart cloning process
558 */
559 public function ajaxRestart()
560 {
561 if (!$this->isAuthenticated()) {
562 return;
563 }
564
565 $process = WPStaging::make(ProcessLock::class);
566 $process->restart();
567 }
568
569 /**
570 * Ajax Scan
571 * @action wp_ajax_wpstg_scanning 10 0
572 * @see Administrator::defineHooks()
573 */
574 public function ajaxCloneScan()
575 {
576 if (!$this->isAuthenticated()) {
577 return;
578 }
579
580 // Check first if there is already a process running
581 $processLock = WPStaging::make(ProcessLock::class);
582 $response = $processLock->ajaxIsRunning();
583 if ($response !== false) {
584 echo json_encode($response);
585
586 exit();
587 }
588
589 $siteInfo = WPStaging::make(SiteInfo::class);
590
591 $db = WPStaging::make('wpdb');
592
593 // Scan
594 $scan = WPStaging::make(Scan::class);
595 $scan->setGifLoaderPath($this->assets->getAssetsUrl('img/spinner.gif'));
596 $scan->setInfoIcon($this->assets->getAssetsUrl('svg/info-outline.svg'));
597 $scan->start();
598
599 // Get Options
600 $options = $scan->getOptions();
601 $excludeUtils = WPStaging::make(ExcludeFilter::class);
602 $wpDefaultDirectories = WPStaging::make(WpDefaultDirectories::class);
603 $isPro = WPStaging::isPro();
604
605 if ($isPro) {
606 require_once "{$this->viewsPath}pro/clone/ajax/scan.php";
607 } else {
608 require_once "{$this->viewsPath}clone/ajax/scan.php";
609 }
610
611 wp_die();
612 }
613
614 /**
615 * Fetch children of the given directory
616 */
617 public function ajaxFetchDirChildren()
618 {
619 if (!$this->isAuthenticated()) {
620 wp_send_json(['success' => false]);
621 return;
622 }
623
624 $isChecked = isset($_POST['isChecked']) ? $this->sanitize->sanitizeBool($_POST['isChecked']) : false;
625 $forceDefault = isset($_POST['forceDefault']) ? $this->sanitize->sanitizeBool($_POST['forceDefault']) : false;
626 $path = isset($_POST['dirPath']) ? $this->sanitize->sanitizePath($_POST['dirPath']) : "";
627 $prefix = isset($_POST['prefix']) ? $this->sanitize->sanitizePath($_POST['prefix']) : "";
628 $basePath = ABSPATH;
629 if ($prefix === PathIdentifier::IDENTIFIER_WP_CONTENT) {
630 $basePath = WP_CONTENT_DIR;
631 }
632
633 $path = trailingslashit($basePath) . $path;
634 $scan = new Scan($path);
635 $scan->setBasePath($basePath);
636 $scan->setPathIdentifier($prefix);
637 $scan->setGifLoaderPath($this->assets->getAssetsUrl('img/spinner.gif'));
638 $scan->getDirectories($path);
639 wp_send_json([
640 "success" => true,
641 "directoryListing" => json_encode($scan->directoryListing($isChecked, $forceDefault)),
642 ]);
643 }
644
645 /**
646 * Ajax Check Clone Name
647 */
648 public function ajaxCheckCloneDirectoryName()
649 {
650 if (!$this->isAuthenticated()) {
651 return;
652 }
653
654 /** @var Sites $sitesHelper */
655 $sitesHelper = WPStaging::make(Sites::class);
656 $cloneDirectoryName = isset($_POST["directoryName"]) ? $sitesHelper->sanitizeDirectoryName($_POST["directoryName"]) : '';
657
658 if (strlen($cloneDirectoryName) < 1) {
659 return;
660 }
661
662 $result = $sitesHelper->isCloneExists($cloneDirectoryName);
663 if ($result === false) {
664 wp_send_json(["status" => "success"]);
665 return;
666 }
667
668 wp_send_json([
669 "status" => "failed",
670 "message" => $result
671 ]);
672 }
673
674 /**
675 * Ajax Start Updating Clone (Basically just layout and saving data)
676 */
677 public function ajaxUpdateProcess()
678 {
679 if (!$this->isAuthenticated()) {
680 return;
681 }
682
683 $cloning = WPStaging::make(Updating::class);
684
685 if (!$cloning->save()) {
686 wp_die('Can not save clone data');
687 }
688
689 $options = $cloning->getOptions();
690 WPStaging::make(AnalyticsStagingUpdate::class)->enqueueStartEvent($options->jobIdentifier, $options);
691
692 require_once "{$this->viewsPath}clone/ajax/update.php";
693
694 wp_die();
695 }
696
697 /**
698 * Ajax Start Resetting Clone
699 */
700 public function ajaxResetProcess()
701 {
702 if (!$this->isAuthenticated()) {
703 return;
704 }
705
706 $cloning = WPStaging::make(Updating::class);
707 $cloning->setMainJob(Job::RESET);
708 if (!$cloning->save()) {
709 wp_die('can not save clone data');
710 }
711
712 $options = $cloning->getOptions();
713 WPStaging::make(AnalyticsStagingReset::class)->enqueueStartEvent($options->jobIdentifier, $options);
714
715 require_once "{$this->viewsPath}clone/ajax/update.php";
716 wp_die();
717 }
718
719 /**
720 * Ajax Start Clone (Basically just layout and saving data)
721 */
722 public function ajaxStartClone()
723 {
724 if (!$this->isAuthenticated()) {
725 return;
726 }
727
728 // Check first if there is already a process running
729 $processLock = WPStaging::make(ProcessLock::class);
730 $processLock->isRunning();
731
732 $cloning = $this->getCloningJob();
733
734 if (!$cloning->save()) {
735 $message = $cloning->getErrorMessage();
736 wp_send_json([
737 'success' => false,
738 'message' => $message !== '' ? $message : 'Can not save clone data'
739 ]);
740
741 wp_die();
742 }
743
744 require_once "{$this->viewsPath}clone/ajax/start.php";
745
746 wp_die();
747 }
748
749 /**
750 * Ajax Clone Database
751 */
752 public function ajaxCloneDatabase()
753 {
754 if (!$this->isAuthenticated()) {
755 return;
756 }
757
758 $cloning = $this->getCloningJob();
759 wp_send_json($cloning->start());
760 }
761
762 /**
763 * Ajax Prepare Directories (get listing of files)
764 */
765 public function ajaxPrepareDirectories()
766 {
767 if (!$this->isAuthenticated()) {
768 return;
769 }
770
771 $cloning = $this->getCloningJob();
772 wp_send_json($cloning->start());
773 }
774
775 /**
776 * Ajax Clone Files
777 */
778 public function ajaxCopyFiles()
779 {
780 if (!$this->isAuthenticated()) {
781 return;
782 }
783
784 $cloning = $this->getCloningJob();
785 wp_send_json($cloning->start());
786 }
787
788 /**
789 * Ajax Replace Data
790 */
791 public function ajaxReplaceData()
792 {
793 if (!$this->isAuthenticated()) {
794 return;
795 }
796
797 $cloning = $this->getCloningJob();
798 wp_send_json($cloning->start());
799 }
800
801 /**
802 * Ajax Finish
803 */
804 public function ajaxFinish()
805 {
806 if (!$this->isAuthenticated()) {
807 return;
808 }
809
810 $cloning = $this->getCloningJob();
811 wp_send_json($cloning->start());
812 }
813
814 /**
815 * Cancel clone
816 */
817 public function ajaxCancelClone()
818 {
819 if (!$this->isAuthenticated()) {
820 return;
821 }
822
823 $cancel = WPStaging::make(Cancel::class);
824 wp_send_json($cancel->start());
825 }
826
827 /**
828 * Cancel updating process / Do not delete clone!
829 */
830 public function ajaxCancelUpdate()
831 {
832 if (!$this->isAuthenticated()) {
833 return;
834 }
835
836 $cancelUpdate = WPStaging::make(CancelUpdate::class);
837 wp_send_json($cancelUpdate->start());
838 }
839
840 /**
841 * Ajax Hide Rating
842 *
843 * Runs when the user dismisses the notice to rate the plugin.
844 */
845 public function ajaxHideRating()
846 {
847 if (!$this->isAuthenticated()) {
848 return;
849 }
850
851 if (update_option("wpstg_rating", "no") !== false) {
852 wp_send_json(true);
853 }
854
855 wp_send_json(null);
856 }
857
858 /**
859 * Ajax Hide Rating and show it again after one week
860 *
861 * Runs when the user chooses to rate the plugin later.
862 */
863 public function ajaxHideLaterRating()
864 {
865 if (!$this->isAuthenticated()) {
866 return;
867 }
868
869 $date = date('Y-m-d', strtotime(date('Y-m-d') . ' + 7 days'));
870 if (update_option('wpstg_rating', $date) !== false) {
871 wp_send_json(true);
872 }
873
874 wp_send_json(false);
875 }
876
877 /**
878 * Ajax Hide Beta
879 */
880 public function ajaxHideBeta()
881 {
882 if (!$this->isAuthenticated()) {
883 return;
884 }
885
886 wp_send_json(update_option("wpstg_beta", "no"));
887 }
888
889 /**
890 * @return void
891 */
892 public function ajaxDismissNotice()
893 {
894 if (!$this->isAuthenticated()) {
895 return;
896 }
897
898 // Early bail if no notice option available
899 if (!isset($_POST['wpstg_notice'])) {
900 wp_send_json(null);
901 return;
902 }
903
904 /** @var DismissNotice */
905 $dismissNotice = WPStaging::make(DismissNotice::class);
906 $dismissNotice->dismiss($this->sanitize->sanitizeString($_POST['wpstg_notice']));
907 }
908
909 /**
910 * Clone logs
911 */
912 public function ajaxLogs()
913 {
914 if (!$this->isAuthenticated()) {
915 return;
916 }
917
918 $logs = WPStaging::make(Logs::class);
919 wp_send_json($logs->start());
920 }
921
922 /**
923 * Ajax Checks Free Disk Space
924 */
925 public function ajaxCheckFreeSpace()
926 {
927 if (!$this->isAuthenticated()) {
928 return false;
929 }
930
931 $excludedDirectories = isset($_POST["excludedDirectories"]) ? $this->sanitize->sanitizeString($_POST["excludedDirectories"]) : '';
932 $extraDirectories = isset($_POST["extraDirectories"]) ? $this->sanitize->sanitizeString($_POST["extraDirectories"]) : '';
933 $isUploadsSymlinked = isset($_POST["isUploadsSymlinked"]) && $this->sanitize->sanitizeBool($_POST["isUploadsSymlinked"]);
934
935 $scan = WPStaging::make(Scan::class);
936 $scan->setIsUploadsSymlinked($isUploadsSymlinked);
937
938 return $scan->hasFreeDiskSpace($excludedDirectories, $extraDirectories);
939 }
940
941 /**
942 * Ajax Start Push Changes Process
943 * Start with the module Scan
944 * @action wp_ajax_wpstg_scans 10 0
945 * @see Administrator::defineHooks()
946 */
947 public function ajaxPushScan()
948 {
949 if (!$this->isAuthenticated()) {
950 return false;
951 }
952
953 if (!class_exists('WPStaging\Backend\Pro\Modules\Jobs\Scan')) {
954 return false;
955 }
956
957 // Scan
958 $scan = WPStaging::make(ScanProModule::class);
959
960 $scan->start();
961
962 // Get Options
963 $options = $scan->getOptions();
964
965 // Get Framework\Utils\Math
966 $utilsMath = WPStaging::make(Math::class);
967
968 require_once "{$this->viewsPath}pro/scan.php";
969
970 wp_die();
971 }
972
973 /**
974 * Fetch all tables for push process
975 */
976 public function ajaxPushTables()
977 {
978 if (!$this->isAuthenticated()) {
979 return false;
980 }
981
982 if (!class_exists('WPStaging\Backend\Pro\Modules\Jobs\Scan')) {
983 return false;
984 }
985
986 // Scan
987 $scan = WPStaging::make(ScanProModule::class);
988 $scan->loadStagingDBTables($onlyLoadStagingPrefixTables = false);
989 $scan->start();
990 $options = $scan->getOptions();
991
992 $includedTables = isset($_POST['includedTables']) ? $this->sanitize->sanitizeString($_POST['includedTables']) : '';
993 $excludedTables = isset($_POST['excludedTables']) ? $this->sanitize->sanitizeString($_POST['excludedTables']) : '';
994 $selectedTablesWithoutPrefix = isset($_POST['selectedTablesWithoutPrefix']) ? $this->sanitize->sanitizeString($_POST['selectedTablesWithoutPrefix']) : '';
995 $selectedTables = new SelectedTables($includedTables, $excludedTables, $selectedTablesWithoutPrefix);
996 $selectedTables->setDatabaseInfo($options->databaseServer, $options->databaseUser, $options->databasePassword, $options->databaseDatabase, empty($options->databasePrefix) ? $options->prefix : $options->databasePrefix, $options->databaseSsl);
997 $tables = $selectedTables->getSelectedTables($options->networkClone);
998
999 $templateEngine = WPStaging::make(TemplateEngine::class);
1000
1001 echo json_encode([
1002 'success' => true,
1003 "content" => $templateEngine->render("pro/selections/tables.php", [
1004 'isNetworkClone' => $scan->isNetworkClone(),
1005 'options' => $options,
1006 'showAll' => true,
1007 'selected' => $tables
1008 ])
1009 ]);
1010
1011 exit();
1012 }
1013
1014 /**
1015 * Ajax Start Pushing. Needs WP Staging Pro
1016 */
1017 public function ajaxPushProcessing()
1018 {
1019 if (!$this->isAuthenticated()) {
1020 return false;
1021 }
1022
1023 if (!class_exists('WPStaging\Backend\Pro\Modules\Jobs\Processing')) {
1024 return false;
1025 }
1026
1027 // Start the process
1028 wp_send_json(WPStaging::make(Processing::class)->start());
1029
1030 return false;
1031 }
1032
1033 /**
1034 * License Page
1035 */
1036 public function getLicensePage()
1037 {
1038 // Get license data
1039 $license = get_option('wpstg_license_status');
1040
1041 require_once "{$this->viewsPath}pro/licensing.php";
1042 }
1043
1044 /**
1045 * @return void
1046 */
1047 public function getTempLoginsPage()
1048 {
1049 Hooks::applyFilters(Administrator::FILTER_MAIN_SETTING_TABS, [
1050 "temporary-logins" => __("Temporary Logins", "wp-staging")
1051 ]);
1052 }
1053
1054 /**
1055 * Send mail via ajax
1056 * @param array $args
1057 */
1058 public function ajaxSendReport($args = [])
1059 {
1060 if (!$this->isAuthenticated()) {
1061 return;
1062 }
1063
1064 // Set params
1065 if (empty($args)) {
1066 $args = stripslashes_deep($_POST);
1067 }
1068
1069 // Set e-mail
1070 $emailRecipient = '';
1071 if (isset($args['wpstg_email'])) {
1072 $emailRecipient = trim($this->sanitize->sanitizeString($args['wpstg_email']));
1073 }
1074
1075 // Set hosting provider
1076 $providerName = '';
1077 if (!empty($args['wpstg_provider'])) {
1078 $providerName = trim($this->sanitize->sanitizeString($args['wpstg_provider']));
1079 }
1080
1081 // Set message
1082 $messageBody = '';
1083 if (!empty($args['wpstg_message'])) {
1084 $messageBody = trim($this->sanitize->sanitizeString($args['wpstg_message']));
1085 }
1086
1087 // Set syslog
1088 $sendLogFiles = false;
1089 if (isset($args['wpstg_syslog'])) {
1090 $sendLogFiles = $this->sanitize->sanitizeBool($args['wpstg_syslog']);
1091 }
1092
1093 // Set terms
1094 $termsAccepted = false;
1095 if (isset($args['wpstg_terms'])) {
1096 $termsAccepted = $this->sanitize->sanitizeBool($args['wpstg_terms']);
1097 }
1098
1099 // Set forceSend
1100 $forceSend = isset($_POST['wpstg_force_send']) && $this->sanitize->sanitizeBool($_POST['wpstg_force_send']);
1101
1102 $report = WPStaging::make(Report::class);
1103 $errors = $report->send($emailRecipient, $messageBody, $termsAccepted, $sendLogFiles, $providerName, $forceSend);
1104
1105 echo json_encode(['errors' => $errors]);
1106 exit;
1107 }
1108
1109 /**
1110 * Action to perform when error modal confirm button is clicked
1111 *
1112 * @todo use constants instead of hardcoded strings for error types
1113 */
1114 public function ajaxModalError()
1115 {
1116 if (!$this->isAuthenticated()) {
1117 return;
1118 }
1119
1120 $type = isset($_POST['type']) ? $this->sanitize->sanitizeString($_POST['type']) : null;
1121 if ($type === 'processLock') {
1122 $process = WPStaging::make(ProcessLock::class);
1123 $process->restart();
1124
1125 exit();
1126 }
1127 }
1128
1129 /**
1130 * Render tables and files selection for RESET function
1131 */
1132 public function ajaxCloneExcludesSettings()
1133 {
1134 if (!$this->isAuthenticated()) {
1135 return;
1136 }
1137
1138 $processLock = WPStaging::make(ProcessLock::class);
1139 $response = $processLock->ajaxIsRunning();
1140 if ($response !== false) {
1141 echo json_encode($response);
1142
1143 exit();
1144 }
1145
1146 $templateEngine = WPStaging::make(TemplateEngine::class);
1147
1148 // Scan
1149 $scan = WPStaging::make(Scan::class);
1150 $scan->setGifLoaderPath($this->assets->getAssetsUrl('img/spinner.gif'));
1151 $scan->start();
1152
1153 echo json_encode([
1154 'success' => true,
1155 "html" => $templateEngine->render("clone/ajax/exclude-settings.php", [
1156 'scan' => $scan,
1157 'options' => $scan->getOptions(),
1158 'excludeUtils' => WPStaging::make(ExcludeFilter::class),
1159 ])
1160 ]);
1161
1162 exit();
1163 }
1164
1165 /**
1166 * Enable cloning on staging site if it is not enabled already
1167 */
1168 public function ajaxEnableStagingCloning()
1169 {
1170 if (!$this->isAuthenticated()) {
1171 return;
1172 }
1173
1174 if ($this->siteInfo->enableStagingSiteCloning()) {
1175 echo json_encode(['success' => 'true']);
1176 exit();
1177 }
1178
1179 echo json_encode(['success' => 'false', 'message' => __('Unable to enable cloning in the staging site', 'wp-staging')]);
1180 exit();
1181 }
1182
1183 /**
1184 * Restore Settings, can be used when settings are corrupted
1185 */
1186 public function ajaxRestoreSettings()
1187 {
1188 if (!$this->isAuthenticated()) {
1189 return;
1190 }
1191
1192 // Delete old settings
1193 delete_option('wpstg_settings');
1194 $settings = WPStaging::make(Settings::class);
1195 $settings->setDefault();
1196 }
1197
1198 /**
1199 * Remove uploads backup
1200 */
1201 public function removeOldUploadsBackup()
1202 {
1203 $backup = new BackupUploadsDir(null);
1204 $backup->removeUploadsBackup();
1205 }
1206
1207 /**
1208 * Check if Plugin is Pro version
1209 * @return bool
1210 */
1211 protected function isPro()
1212 {
1213 if (!defined("WPSTGPRO_VERSION")) {
1214 return false;
1215 }
1216
1217 return true;
1218 }
1219
1220 /**
1221 * @return Cloning
1222 */
1223 private function getCloningJob(): Cloning
1224 {
1225 return WPStaging::make(CloningJobProvider::class)->getCloningJob();
1226 }
1227
1228 /**
1229 * Check if current page is plugins.php
1230 * @global array $pagenow
1231 * @return bool
1232 */
1233 private function isPluginsPage()
1234 {
1235 global $pagenow;
1236 return ($pagenow === 'plugins.php');
1237 }
1238 }
1239