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

Version Description

  • Fix: Updating staging site does not exclude Windows IIS configuration file web.config and can lead to server error
  • Fix: Redirect to the correct url after log in to staging site
  • Fix: Supports HTML entities in links created by WP Backery Page Builder Plugin
  • New: Support for custom and non-default wp-content and uploads folder
  • New: Option to show again the rating notice in a week
Download this release

Release Info

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

Code changes from version 2.4.1 to 2.4.3

apps/Backend/Administrator.php CHANGED
@@ -4,7 +4,7 @@ namespace WPStaging\Backend;
4
 
5
  // No Direct Access
6
  if( !defined( "WPINC" ) ) {
7
- die;
8
  }
9
 
10
  use WPStaging\WPStaging;
@@ -29,735 +29,748 @@ use WPStaging\Backend\Activation;
29
  */
30
  class Administrator extends InjectionAware {
31
 
32
- /**
33
- * @var string
34
- */
35
- private $path;
36
-
37
- /**
38
- * @var string
39
- */
40
- private $url;
41
-
42
- /**
43
- * Initialize class
44
- */
45
- public function initialize() {
46
- $this->defineHooks();
47
-
48
- // Path to backend
49
- $this->path = plugin_dir_path( __FILE__ );
50
-
51
- // URL to public backend folder
52
- $this->url = plugin_dir_url( __FILE__ ) . "public/";
53
-
54
- // Load plugins meta data
55
- $this->loadMeta();
56
- }
57
-
58
- /**
59
- * Load plugn meta data
60
- */
61
- public function loadMeta() {
62
- $run = new \WPStaging\Backend\Pluginmeta\Pluginmeta();
63
- }
64
-
65
- /**
66
- * Define Hooks
67
- */
68
- private function defineHooks() {
69
- // Get loader
70
- $loader = $this->di->get( "loader" );
71
-
72
- $Activation = new \WPStaging\Backend\Activation\Activation();
73
-
74
- $Welcome = new Activation\Welcome();
75
-
76
- $loader->addAction( "activated_plugin", $Activation, 'deactivate_other_instances' );
77
- //$loader->addAction( "activated_plugin", $Activation, 'install_dependancies' );
78
- $loader->addAction( "admin_menu", $this, "addMenu", 10 );
79
- $loader->addAction( "admin_init", $this, "setOptionFormElements" );
80
- $loader->addAction( "admin_init", $this, "upgrade" );
81
- $loader->addAction( "admin_post_wpstg_download_sysinfo", $this, "systemInfoDownload" );
82
- $loader->addAction( "admin_post_wpstg_export", $this, "export" );
83
- $loader->addAction( "admin_post_wpstg_import_settings", $this, "import" );
84
- $loader->addAction( "admin_notices", $this, "messages" );
85
-
86
- // Ajax Requests
87
- $loader->addAction( "wp_ajax_wpstg_overview", $this, "ajaxOverview" );
88
- $loader->addAction( "wp_ajax_wpstg_scanning", $this, "ajaxScan" );
89
- $loader->addAction( "wp_ajax_wpstg_check_clone", $this, "ajaxcheckCloneName" );
90
- $loader->addAction( "wp_ajax_wpstg_update", $this, "ajaxUpdateProcess" );
91
- $loader->addAction( "wp_ajax_wpstg_cloning", $this, "ajaxStartClone" );
92
- $loader->addAction( "wp_ajax_wpstg_processing", $this, "ajaxCloneDatabase" );
93
- $loader->addAction( "wp_ajax_wpstg_database_connect", $this, "ajaxDatabaseConnect" );
94
- $loader->addAction( "wp_ajax_wpstg_clone_prepare_directories", $this, "ajaxPrepareDirectories" );
95
- $loader->addAction( "wp_ajax_wpstg_clone_files", $this, "ajaxCopyFiles" );
96
- $loader->addAction( "wp_ajax_wpstg_clone_replace_data", $this, "ajaxReplaceData" );
97
- $loader->addAction( "wp_ajax_wpstg_clone_finish", $this, "ajaxFinish" );
98
- $loader->addAction( "wp_ajax_wpstg_confirm_delete_clone", $this, "ajaxDeleteConfirmation" );
99
- $loader->addAction( "wp_ajax_wpstg_delete_clone", $this, "ajaxDeleteClone" );
100
- $loader->addAction( "wp_ajax_wpstg_cancel_clone", $this, "ajaxCancelClone" );
101
- $loader->addAction( "wp_ajax_wpstg_cancel_update", $this, "ajaxCancelUpdate" );
102
- $loader->addAction( "wp_ajax_wpstg_hide_poll", $this, "ajaxHidePoll" );
103
- $loader->addAction( "wp_ajax_wpstg_hide_rating", $this, "ajaxHideRating" );
104
- $loader->addAction( "wp_ajax_wpstg_hide_beta", $this, "ajaxHideBeta" );
105
- $loader->addAction( "wp_ajax_wpstg_logs", $this, "ajaxLogs" );
106
- $loader->addAction( "wp_ajax_wpstg_check_disk_space", $this, "ajaxCheckFreeSpace" );
107
- $loader->addAction( "wp_ajax_wpstg_send_report", $this, "ajaxSendReport" );
108
-
109
- // Ajax hooks pro Version
110
- $loader->addAction( "wp_ajax_wpstg_scan", $this, "ajaxPushScan" );
111
- $loader->addAction( "wp_ajax_wpstg_push_processing", $this, "ajaxPushProcessing" );
112
- }
113
-
114
- /**
115
- * Register options form elements
116
- */
117
- public function setOptionFormElements() {
118
- register_setting( "wpstg_settings", "wpstg_settings", array($this, "sanitizeOptions") );
119
- }
120
-
121
- /**
122
- * Upgrade routine
123
- */
124
- public function upgrade() {
125
- $upgrade = new Upgrade\Upgrade();
126
- $upgrade->doUpgrade();
127
- }
128
-
129
- /**
130
- * Sanitize options data and delete the cache
131
- * @param array $data
132
- * @return array
133
- */
134
- public function sanitizeOptions( $data = array() ) {
135
- $sanitized = $this->sanitizeData( $data );
136
-
137
- add_settings_error( "wpstg-notices", '', __( "Settings updated.", "wp-staging" ), "updated" );
138
-
139
- return apply_filters( "wpstg-settings", $sanitized, $data );
140
- }
141
-
142
- /**
143
- * @param array $data
144
- * @return array
145
- */
146
- private function sanitizeData( $data = array() ) {
147
- $sanitized = array();
148
-
149
- foreach ( $data as $key => $value ) {
150
- $sanitized[$key] = (is_array( $value )) ? $this->sanitizeData( $value ) : htmlspecialchars( $value );
151
- }
152
-
153
- return $sanitized;
154
- }
155
-
156
- /**
157
- * Add Admin Menu(s)
158
- */
159
- public function addMenu() {
160
-
161
- $logo = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiIGZpbGw9Im5vbmUiPgo8Zz48Zz48cGF0aCBzdHlsZT0iZmlsbDojZmZmIiAgZD0iTTEzNy42LDU2MS4zSDEzLjhIMTB2MzA2LjNsOTAuNy04My40QzE4OS42LDkwOC43LDMzNS4zLDk5MCw1MDAsOTkwYzI0OS45LDAsNDU2LjEtMTg3LjEsNDg2LjItNDI4LjhIODYyLjRDODMzLjMsNzM1LjEsNjgyLjEsODY3LjUsNTAwLDg2Ny41Yy0xMjksMC0yNDIuNS02Ni41LTMwOC4xLTE2Ny4ybDE1MS4zLTEzOS4xSDEzNy42eiIvPjxwYXRoIHN0eWxlPSJmaWxsOiNmZmYiICBkPSJNNTAwLDEwQzI1MC4xLDEwLDQzLjksMTk3LjEsMTMuOCw0MzguOGgxMjMuOEMxNjYuNywyNjQuOSwzMTcuOSwxMzIuNSw1MDAsMTMyLjVjMTMyLjksMCwyNDkuMyw3MC41LDMxMy44LDE3Ni4yTDY4My44LDQzOC44aDEyMi41aDU2LjJoMTIzLjhoMy44VjEzMi41bC04Ny43LDg3LjdDODEzLjgsOTMuMSw2NjYuNiwxMCw1MDAsMTB6Ii8+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvZz4KPC9zdmc+';
162
-
163
- // Main WP Staging Menu
164
- add_menu_page(
165
- "WP-Staging", __( "WP Staging", "wp-staging" ), "manage_options", "wpstg_clone", array($this, "getClonePage"), $logo
166
- );
167
-
168
- // Page: Clone
169
- add_submenu_page(
170
- "wpstg_clone", __( "WP Staging Jobs", "wp-staging" ), __( "Sites / Start", "wp-staging" ), "manage_options", "wpstg_clone", array($this, "getClonePage")
171
- );
172
-
173
- // Page: Settings
174
- add_submenu_page(
175
- "wpstg_clone", __( "WP Staging Settings", "wp-staging" ), __( "Settings", "wp-staging" ), "manage_options", "wpstg-settings", array($this, "getSettingsPage")
176
- );
177
-
178
- // Page: Tools
179
- add_submenu_page(
180
- "wpstg_clone", __( "WP Staging Tools", "wp-staging" ), __( "Tools", "wp-staging" ), "manage_options", "wpstg-tools", array($this, "getToolsPage")
181
- );
182
- // Page: Tools
183
- add_submenu_page(
184
- "wpstg_clone", __( "WP Staging Welcome", "wp-staging" ), __( "Get WP Staging Pro", "wp-staging" ), "manage_options", "wpstg-welcome", array($this, "getWelcomePage")
185
- );
186
-
187
- if( class_exists( 'WPStaging\Backend\Pro\Licensing\Licensing' ) ) {
188
- // Page: License
189
- add_submenu_page(
190
- "wpstg_clone", __( "WP Staging License", "wp-staging" ), __( "License", "wp-staging" ), "manage_options", "wpstg-license", array($this, "getLicensePage")
191
- );
192
- }
193
- }
194
-
195
- /**
196
- * Settings Page
197
- */
198
- public function getSettingsPage() {
199
- // Tabs
200
- $tabs = new Tabs( array(
201
- "general" => __( "General", "wp-staging" )
202
- ) );
203
-
204
-
205
- $this->di
206
- // Set tabs
207
- ->set( "tabs", $tabs )
208
- // Forms
209
- ->set( "forms", new FormSettings( $tabs ) );
210
-
211
-
212
- require_once "{$this->path}views/settings/main-settings.php";
213
- }
214
-
215
- /**
216
- * Clone Page
217
- */
218
- public function getClonePage() {
219
- // Existing clones
220
- $availableClones = get_option( "wpstg_existing_clones_beta", array() );
221
-
222
- require_once "{$this->path}views/clone/index.php";
223
- }
224
-
225
- /**
226
- * Welcome Page
227
- */
228
- public function getWelcomePage() {
229
- require_once "{$this->path}views/welcome/welcome.php";
230
- }
231
-
232
- /**
233
- * Tools Page
234
- */
235
- public function getToolsPage() {
236
- // Tabs
237
- $tabs = new Tabs( array(
238
- "import_export" => __( "Import/Export", "wp-staging" ),
239
- "system_info" => __( "System Info", "wp-staging" )
240
- ) );
241
-
242
- $this->di->set( "tabs", $tabs );
243
-
244
- $this->di->set( "systemInfo", new SystemInfo( $this->di ) );
245
-
246
- require_once "{$this->path}views/tools/index.php";
247
- }
248
-
249
- /**
250
- * System Information Download
251
- */
252
- public function systemInfoDownload() {
253
- if( !current_user_can( "update_plugins" ) ) {
254
- return;
255
- }
256
-
257
- nocache_headers();
258
- header( "Content-Type: text/plain" );
259
- header( "Content-Disposition: attachment; filename='wpstg-system-info.txt'" );
260
- echo wp_strip_all_tags( new SystemInfo( $this->di ) );
261
- }
262
-
263
- /**
264
- * Import JSON settings file
265
- */
266
- public function import() {
267
- if( empty( $_POST["wpstg_import_nonce"] ) ) {
268
- return;
269
- }
270
-
271
- if( !wp_verify_nonce( $_POST["wpstg_import_nonce"], "wpstg_import_nonce" ) ) {
272
- return;
273
- }
274
-
275
- if( !current_user_can( "update_plugins" ) ) {
276
- return;
277
- }
278
-
279
- $fileExtension = explode( '.', $_FILES["import_file"]["name"] );
280
- $fileExtension = end( $fileExtension );
281
- if( "json" !== $fileExtension ) {
282
- wp_die( "Please upload a valid .json file", "wp-staging" );
283
- }
284
-
285
-
286
- $importFile = $_FILES["import_file"]["tmp_name"];
287
-
288
- if( empty( $importFile ) ) {
289
- wp_die( __( "Please upload a file to import", "wp-staging" ) );
290
- }
291
-
292
- update_option( "wpstg_settings", json_decode( file_get_contents( $importFile, true ) ) );
293
-
294
- wp_safe_redirect( admin_url( "admin.php?page=wpstg-tools&wpstg-message=settings-imported" ) );
295
-
296
- return;
297
- }
298
-
299
- /**
300
- * Export settings to JSON file
301
- */
302
- public function export() {
303
- if( empty( $_POST["wpstg_export_nonce"] ) ) {
304
- return;
305
- }
306
-
307
- if( !wp_verify_nonce( $_POST["wpstg_export_nonce"], "wpstg_export_nonce" ) ) {
308
- return;
309
- }
310
-
311
- if( !current_user_can( "manage_options" ) ) {
312
- return;
313
- }
314
-
315
- $settings = get_option( "wpstg_settings", array() );
316
-
317
- ignore_user_abort( true );
318
-
319
- if( !in_array( "set_time_limit", explode( ',', ini_get( "disable_functions" ) ) ) && !@ini_get( "safe_mode" ) ) {
320
- set_time_limit( 0 );
321
- }
322
-
323
- $fileName = apply_filters( "wpstg_settings_export_filename", "wpstg-settings-export-" . date( "m-d-Y" ) ) . ".json";
324
-
325
- nocache_headers();
326
- header( "Content-Type: application/json; charset=utf-8" );
327
- header( "Content-Disposition: attachment; filename={$fileName}" );
328
- header( "Expires: 0" );
329
-
330
- echo json_encode( $settings );
331
- }
332
-
333
- /**
334
- * Render a view file
335
- * @param string $file
336
- * @param array $vars
337
- * @return string
338
- */
339
- public function render( $file, $vars = array() ) {
340
- $fullPath = $this->path . "views" . DIRECTORY_SEPARATOR;
341
- $fullPath = str_replace( array('/', "\\"), DIRECTORY_SEPARATOR, $fullPath . $file . ".php" );
342
-
343
- if( !file_exists( $fullPath ) || !is_readable( $fullPath ) ) {
344
- return "Can't render : {$fullPath} either file doesn't exist or can't read it";
345
- }
346
-
347
- $contents = @file_get_contents( $fullPath );
348
-
349
- // Variables are set
350
- if( count( $vars ) > 0 ) {
351
- $vars = array_combine(
352
- array_map( function ($key) {
353
- return "{{" . $key . "}}";
354
- }, array_keys( $vars )
355
- ), $vars
356
- );
357
-
358
- $contents = str_replace( array_keys( $vars ), array_values( $vars ), $contents );
359
- }
360
-
361
- return $contents;
362
- }
363
-
364
- /**
365
- * Ajax Overview
366
- */
367
- public function ajaxOverview() {
368
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
369
-
370
- // Existing clones
371
- $availableClones = get_option( "wpstg_existing_clones_beta", array() );
372
-
373
- // Get license data
374
- $license = get_option( 'wpstg_license_status' );
375
-
376
- // Get db
377
- $db = WPStaging::getInstance()->get( 'wpdb' );
378
-
379
- if( \WPStaging\WPStaging::getSlug() === 'wp-staging-pro' ) {
380
- require_once "{$this->path}Pro/views/single-overview-pro.php";
381
- } else {
382
- require_once "{$this->path}views/clone/ajax/single-overview.php";
383
- }
384
 
385
- wp_die();
386
- }
387
-
388
- /**
389
- * Ajax Scan
390
- */
391
- public function ajaxScan() {
392
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
393
-
394
- $db = WPStaging::getInstance()->get( 'wpdb' );
395
-
396
- // Scan
397
- $scan = new Scan();
398
- $scan->start();
399
-
400
- // Get Options
401
- $options = $scan->getOptions();
402
-
403
- require_once "{$this->path}views/clone/ajax/scan.php";
404
-
405
- wp_die();
406
- }
407
-
408
- /**
409
- * Ajax Check Clone Name
410
- */
411
- public function ajaxCheckCloneName() {
412
- $cloneName = sanitize_key( $_POST["cloneID"] );
413
- $cloneNameLength = strlen( $cloneName );
414
- $clones = get_option( "wpstg_existing_clones_beta", array() );
415
-
416
- $clonePath = trailingslashit( get_home_path() ) . $cloneName;
417
-
418
- // Check clone name length
419
- if( $cloneNameLength < 1 || $cloneNameLength > 16 ) {
420
- echo wp_send_json( array(
421
- "status" => "failed",
422
- "message" => "Clone name must be between 1 - 16 characters"
423
- ) );
424
- } elseif( array_key_exists( $cloneName, $clones ) ) {
425
- echo wp_send_json( array(
426
- "status" => "failed",
427
- "message" => "Clone name is already in use, please choose an another clone name."
428
- ) );
429
- } elseif( is_dir( $clonePath ) ) {
430
- echo wp_send_json( array(
431
- "status" => "failed",
432
- "message" => "Clone directory " . $clonePath . " already exists. Use another clone name."
433
- ) );
434
- }
435
-
436
- echo wp_send_json( array("status" => "success") );
437
- }
438
-
439
- /**
440
- * Ajax Start Updating Clone (Basically just layout and saving data)
441
- */
442
- public function ajaxUpdateProcess() {
443
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
444
 
445
- $cloning = new Updating();
 
 
 
 
446
 
447
- if( !$cloning->save() ) {
448
- wp_die( 'can not save clone data' );
449
- }
 
 
450
 
451
- require_once "{$this->path}views/clone/ajax/update.php";
 
452
 
453
- wp_die();
454
- }
455
-
456
- /**
457
- * Ajax Start Clone (Basically just layout and saving data)
458
- */
459
- public function ajaxStartClone() {
460
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
 
462
- $cloning = new Cloning();
463
 
464
- if( !$cloning->save() ) {
465
- wp_die( 'can not save clone data' );
466
- }
467
 
468
- require_once "{$this->path}views/clone/ajax/start.php";
469
 
470
- wp_die();
471
- }
472
 
473
- /**
474
- * Ajax Clone Database
475
- */
476
- public function ajaxCloneDatabase() {
477
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
478
 
479
- $cloning = new Cloning();
480
 
481
- // Uncomment these lines to test different error codes
482
- //http_response_code(504);
483
- //wp_send_json( '<html><body><head></head><body>test</body></html>' );
484
 
485
- wp_send_json( $cloning->start() );
486
- }
487
 
488
- /**
489
- * Ajax Prepare Directories (get listing of files)
490
- */
491
- public function ajaxPrepareDirectories() {
492
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
493
 
494
- $cloning = new Cloning();
 
 
 
 
495
 
496
- wp_send_json( $cloning->start() );
497
- }
498
-
499
- /**
500
- * Ajax Clone Files
501
- */
502
- public function ajaxCopyFiles() {
503
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
504
-
505
- $cloning = new Cloning();
506
-
507
- wp_send_json( $cloning->start() );
508
- }
509
-
510
- /**
511
- * Ajax Replace Data
512
- */
513
- public function ajaxReplaceData() {
514
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
515
-
516
- $cloning = new Cloning();
517
-
518
- wp_send_json( $cloning->start() );
519
- }
520
-
521
- /**
522
- * Ajax Finish
523
- */
524
- public function ajaxFinish() {
525
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
526
-
527
- $cloning = new Cloning();
528
-
529
- wp_send_json( $cloning->start() );
530
- }
531
-
532
- /**
533
- * Ajax Delete Confirmation
534
- */
535
- public function ajaxDeleteConfirmation() {
536
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
537
-
538
- $delete = new Delete();
539
- $delete->setData();
540
-
541
- $clone = $delete->getClone();
542
-
543
- $dbname = $delete->getDbName();
544
-
545
- require_once "{$this->path}views/clone/ajax/delete-confirmation.php";
546
-
547
- wp_die();
548
- }
549
-
550
- /**
551
- * Delete clone
552
- */
553
- public function ajaxDeleteClone() {
554
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
555
-
556
- $delete = new Delete();
557
-
558
- wp_send_json( $delete->start() );
559
- }
560
 
561
- /**
562
- * Delete clone
563
- */
564
- public function ajaxCancelClone() {
565
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
566
-
567
- $cancel = new Cancel();
568
-
569
- wp_send_json( $cancel->start() );
570
- }
571
-
572
- /**
573
- * Cancel updating process / Do not delete clone!
574
- */
575
- public function ajaxCancelUpdate() {
576
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
577
-
578
- $cancel = new CancelUpdate();
579
- wp_send_json( $cancel->start() );
580
- }
581
-
582
- /**
583
- * Admin Messages
584
- */
585
- public function messages() {
586
- $notice = new Notices( $this->path, $this->url );
587
-
588
- $run = $notice->messages();
589
- }
590
-
591
- /**
592
- * Ajax Hide Poll
593
- * @return mixed boolean | json
594
- */
595
- public function ajaxHidePoll() {
596
- if( false !== update_option( "wpstg_poll", "no" ) ) {
597
- wp_send_json( true );
598
- }
599
- return wp_send_json();
600
- }
601
-
602
- /**
603
- * Ajax Hide Rating
604
- * @return mixed bool | json
605
- */
606
- public function ajaxHideRating() {
607
- if( false !== update_option( "wpstg_rating", "no" ) ) {
608
- wp_send_json( true );
609
- }
610
- return wp_send_json();
611
- }
612
-
613
- /**
614
- * Ajax Hide Beta
615
- */
616
- public function ajaxHideBeta() {
617
- wp_send_json( update_option( "wpstg_beta", "no" ) );
618
- }
619
-
620
- /**
621
- * Clone logs
622
- */
623
- public function ajaxLogs() {
624
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
625
-
626
- $logs = new Logs();
627
- wp_send_json( $logs->start() );
628
- }
629
-
630
- /**
631
- * Ajax Checks Free Disk Space
632
- */
633
- public function ajaxCheckFreeSpace() {
634
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
635
-
636
- $scan = new Scan();
637
- return $scan->hasFreeDiskSpace();
638
- }
639
-
640
- /**
641
- * Ajax Start Push Changes Process
642
- * Start with the module Scan
643
- */
644
- public function ajaxPushScan() {
645
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
646
-
647
- if( !class_exists( 'WPStaging\Backend\Pro\Modules\Jobs\Scan' ) ) {
648
- return false;
649
- }
650
-
651
- // Scan
652
- $scan = new Pro\Modules\Jobs\Scan();
653
-
654
- $scan->start();
655
-
656
- // Get Options
657
- $options = $scan->getOptions();
658
-
659
- require_once "{$this->path}Pro/views/scan.php";
660
-
661
- wp_die();
662
- }
663
-
664
- /**
665
- * Ajax Start Pushing. Needs wp quads pro
666
- */
667
- public function ajaxPushProcessing() {
668
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
669
-
670
- if( !class_exists( 'WPStaging\Backend\Pro\Modules\Jobs\Processing' ) ) {
671
- return false;
672
- }
673
- // Start the process
674
- $processing = new Processing();
675
- wp_send_json( $processing->start() );
676
- }
677
-
678
- /**
679
- * License Page
680
- */
681
- public function getLicensePage() {
682
-
683
- // Get license data
684
- $license = get_option( 'wpstg_license_status' );
685
-
686
- require_once "{$this->path}Pro/views/licensing.php";
687
- }
688
-
689
- /**
690
- *
691
- * @param type $args
692
- */
693
- public function ajaxSendReport( $args = array() ) {
694
- // Set params
695
- if( empty( $args ) ) {
696
- $args = stripslashes_deep( $_POST );
697
- }
698
- // Set e-mail
699
- $email = null;
700
- if( isset( $args['wpstg_email'] ) ) {
701
- $email = trim( $args['wpstg_email'] );
702
- }
703
-
704
- // Set message
705
- $message = null;
706
- if( isset( $args['wpstg_message'] ) ) {
707
- $message = trim( $args['wpstg_message'] );
708
- }
709
-
710
- // Set syslog
711
- $syslog = false;
712
- if( isset( $args['wpstg_syslog'] ) ) {
713
- $syslog = ( bool ) $args['wpstg_syslog'];
714
- }
715
-
716
- // Set terms
717
- $terms = false;
718
- if( isset( $args['wpstg_terms'] ) ) {
719
- $terms = ( bool ) $args['wpstg_terms'];
720
- }
721
-
722
- $report = new Report( $this->di );
723
- $errors = $report->send( $email, $message, $terms, $syslog );
724
-
725
- echo json_encode( array('errors' => $errors) );
726
- exit;
727
- }
728
-
729
- /**
730
- * Connect to external database for testing correct credentials
731
- */
732
- public function ajaxDatabaseConnect() {
733
- // Set params
734
- if( empty( $args ) ) {
735
- $args = stripslashes_deep( $_POST );
736
- }
737
 
738
- $user = !empty( $args['databaseUser'] ) ? $args['databaseUser'] : '';
739
- $password = !empty( $args['databasePassword'] ) ? $args['databasePassword'] : '';
740
- $database = !empty( $args['databaseDatabase'] ) ? $args['databaseDatabase'] : '';
741
- $server = !empty( $args['databaseServer'] ) ? $args['databaseServer'] : '';
742
-
743
- $db = new \wpdb( $user, $password, $database, $server );
744
-
745
- // Can not connect to mysql
746
- if( !empty( $db->error->errors['db_connect_fail']['0'] ) ) {
747
- echo json_encode( array('errors' => $db->error->errors['db_connect_fail']['0']) );
748
- exit;
749
- }
750
-
751
-
752
- // Can not connect to database
753
- $sql = "SHOW DATABASES LIKE '{$database}';";
754
- $results = $db->query( $sql );
755
- if( empty( $results ) ) {
756
- echo json_encode( array('errors' => " Database {$database} does not exist. You need to create it first. ") );
757
- exit;
758
- }
759
- echo json_encode( array('success' => 'true') );
760
- exit;
761
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
762
 
763
  }
4
 
5
  // No Direct Access
6
  if( !defined( "WPINC" ) ) {
7
+ die;
8
  }
9
 
10
  use WPStaging\WPStaging;
29
  */
30
  class Administrator extends InjectionAware {
31
 
32
+ /**
33
+ * @var string
34
+ */
35
+ private $path;
36
+
37
+ /**
38
+ * @var string
39
+ */
40
+ private $url;
41
+
42
+ /**
43
+ * Initialize class
44
+ */
45
+ public function initialize() {
46
+ $this->defineHooks();
47
+
48
+ // Path to backend
49
+ $this->path = plugin_dir_path( __FILE__ );
50
+
51
+ // URL to public backend folder
52
+ $this->url = plugin_dir_url( __FILE__ ) . "public/";
53
+
54
+ // Load plugins meta data
55
+ $this->loadMeta();
56
+ }
57
+
58
+ /**
59
+ * Load plugn meta data
60
+ */
61
+ public function loadMeta() {
62
+ $run = new \WPStaging\Backend\Pluginmeta\Pluginmeta();
63
+ }
64
+
65
+ /**
66
+ * Define Hooks
67
+ */
68
+ private function defineHooks() {
69
+ // Get loader
70
+ $loader = $this->di->get( "loader" );
71
+
72
+ $Activation = new \WPStaging\Backend\Activation\Activation();
73
+
74
+ $Welcome = new Activation\Welcome();
75
+
76
+ $loader->addAction( "activated_plugin", $Activation, 'deactivate_other_instances' );
77
+ //$loader->addAction( "activated_plugin", $Activation, 'install_dependancies' );
78
+ $loader->addAction( "admin_menu", $this, "addMenu", 10 );
79
+ $loader->addAction( "admin_init", $this, "setOptionFormElements" );
80
+ $loader->addAction( "admin_init", $this, "upgrade" );
81
+ $loader->addAction( "admin_post_wpstg_download_sysinfo", $this, "systemInfoDownload" );
82
+ $loader->addAction( "admin_post_wpstg_export", $this, "export" );
83
+ $loader->addAction( "admin_post_wpstg_import_settings", $this, "import" );
84
+ $loader->addAction( "admin_notices", $this, "messages" );
85
+
86
+ // Ajax Requests
87
+ $loader->addAction( "wp_ajax_wpstg_overview", $this, "ajaxOverview" );
88
+ $loader->addAction( "wp_ajax_wpstg_scanning", $this, "ajaxScan" );
89
+ $loader->addAction( "wp_ajax_wpstg_check_clone", $this, "ajaxcheckCloneName" );
90
+ $loader->addAction( "wp_ajax_wpstg_update", $this, "ajaxUpdateProcess" );
91
+ $loader->addAction( "wp_ajax_wpstg_cloning", $this, "ajaxStartClone" );
92
+ $loader->addAction( "wp_ajax_wpstg_processing", $this, "ajaxCloneDatabase" );
93
+ $loader->addAction( "wp_ajax_wpstg_database_connect", $this, "ajaxDatabaseConnect" );
94
+ $loader->addAction( "wp_ajax_wpstg_clone_prepare_directories", $this, "ajaxPrepareDirectories" );
95
+ $loader->addAction( "wp_ajax_wpstg_clone_files", $this, "ajaxCopyFiles" );
96
+ $loader->addAction( "wp_ajax_wpstg_clone_replace_data", $this, "ajaxReplaceData" );
97
+ $loader->addAction( "wp_ajax_wpstg_clone_finish", $this, "ajaxFinish" );
98
+ $loader->addAction( "wp_ajax_wpstg_confirm_delete_clone", $this, "ajaxDeleteConfirmation" );
99
+ $loader->addAction( "wp_ajax_wpstg_delete_clone", $this, "ajaxDeleteClone" );
100
+ $loader->addAction( "wp_ajax_wpstg_cancel_clone", $this, "ajaxCancelClone" );
101
+ $loader->addAction( "wp_ajax_wpstg_cancel_update", $this, "ajaxCancelUpdate" );
102
+ $loader->addAction( "wp_ajax_wpstg_hide_poll", $this, "ajaxHidePoll" );
103
+ $loader->addAction( "wp_ajax_wpstg_hide_rating", $this, "ajaxHideRating" );
104
+ $loader->addAction( "wp_ajax_wpstg_hide_later", $this, "ajaxHideLaterRating" );
105
+ $loader->addAction( "wp_ajax_wpstg_hide_beta", $this, "ajaxHideBeta" );
106
+ $loader->addAction( "wp_ajax_wpstg_logs", $this, "ajaxLogs" );
107
+ $loader->addAction( "wp_ajax_wpstg_check_disk_space", $this, "ajaxCheckFreeSpace" );
108
+ $loader->addAction( "wp_ajax_wpstg_send_report", $this, "ajaxSendReport" );
109
+
110
+ // Ajax hooks pro Version
111
+ $loader->addAction( "wp_ajax_wpstg_scan", $this, "ajaxPushScan" );
112
+ $loader->addAction( "wp_ajax_wpstg_push_processing", $this, "ajaxPushProcessing" );
113
+ }
114
+
115
+ /**
116
+ * Register options form elements
117
+ */
118
+ public function setOptionFormElements() {
119
+ register_setting( "wpstg_settings", "wpstg_settings", array($this, "sanitizeOptions") );
120
+ }
121
+
122
+ /**
123
+ * Upgrade routine
124
+ */
125
+ public function upgrade() {
126
+ $upgrade = new Upgrade\Upgrade();
127
+ $upgrade->doUpgrade();
128
+ }
129
+
130
+ /**
131
+ * Sanitize options data and delete the cache
132
+ * @param array $data
133
+ * @return array
134
+ */
135
+ public function sanitizeOptions( $data = array() ) {
136
+ $sanitized = $this->sanitizeData( $data );
137
+
138
+ add_settings_error( "wpstg-notices", '', __( "Settings updated.", "wp-staging" ), "updated" );
139
+
140
+ return apply_filters( "wpstg-settings", $sanitized, $data );
141
+ }
142
+
143
+ /**
144
+ * @param array $data
145
+ * @return array
146
+ */
147
+ private function sanitizeData( $data = array() ) {
148
+ $sanitized = array();
149
+
150
+ foreach ( $data as $key => $value ) {
151
+ $sanitized[$key] = (is_array( $value )) ? $this->sanitizeData( $value ) : htmlspecialchars( $value );
152
+ }
153
+
154
+ return $sanitized;
155
+ }
156
+
157
+ /**
158
+ * Add Admin Menu(s)
159
+ */
160
+ public function addMenu() {
161
+
162
+ $logo = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiIGZpbGw9Im5vbmUiPgo8Zz48Zz48cGF0aCBzdHlsZT0iZmlsbDojZmZmIiAgZD0iTTEzNy42LDU2MS4zSDEzLjhIMTB2MzA2LjNsOTAuNy04My40QzE4OS42LDkwOC43LDMzNS4zLDk5MCw1MDAsOTkwYzI0OS45LDAsNDU2LjEtMTg3LjEsNDg2LjItNDI4LjhIODYyLjRDODMzLjMsNzM1LjEsNjgyLjEsODY3LjUsNTAwLDg2Ny41Yy0xMjksMC0yNDIuNS02Ni41LTMwOC4xLTE2Ny4ybDE1MS4zLTEzOS4xSDEzNy42eiIvPjxwYXRoIHN0eWxlPSJmaWxsOiNmZmYiICBkPSJNNTAwLDEwQzI1MC4xLDEwLDQzLjksMTk3LjEsMTMuOCw0MzguOGgxMjMuOEMxNjYuNywyNjQuOSwzMTcuOSwxMzIuNSw1MDAsMTMyLjVjMTMyLjksMCwyNDkuMyw3MC41LDMxMy44LDE3Ni4yTDY4My44LDQzOC44aDEyMi41aDU2LjJoMTIzLjhoMy44VjEzMi41bC04Ny43LDg3LjdDODEzLjgsOTMuMSw2NjYuNiwxMCw1MDAsMTB6Ii8+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvZz4KPC9zdmc+';
163
+
164
+ // Main WP Staging Menu
165
+ add_menu_page(
166
+ "WP-Staging", __( "WP Staging", "wp-staging" ), "manage_options", "wpstg_clone", array($this, "getClonePage"), $logo
167
+ );
168
+
169
+ // Page: Clone
170
+ add_submenu_page(
171
+ "wpstg_clone", __( "WP Staging Jobs", "wp-staging" ), __( "Sites / Start", "wp-staging" ), "manage_options", "wpstg_clone", array($this, "getClonePage")
172
+ );
173
+
174
+ // Page: Settings
175
+ add_submenu_page(
176
+ "wpstg_clone", __( "WP Staging Settings", "wp-staging" ), __( "Settings", "wp-staging" ), "manage_options", "wpstg-settings", array($this, "getSettingsPage")
177
+ );
178
+
179
+ // Page: Tools
180
+ add_submenu_page(
181
+ "wpstg_clone", __( "WP Staging Tools", "wp-staging" ), __( "Tools", "wp-staging" ), "manage_options", "wpstg-tools", array($this, "getToolsPage")
182
+ );
183
+ // Page: Tools
184
+ add_submenu_page(
185
+ "wpstg_clone", __( "WP Staging Welcome", "wp-staging" ), __( "Get WP Staging Pro", "wp-staging" ), "manage_options", "wpstg-welcome", array($this, "getWelcomePage")
186
+ );
187
+
188
+ if( class_exists( 'WPStaging\Backend\Pro\Licensing\Licensing' ) ) {
189
+ // Page: License
190
+ add_submenu_page(
191
+ "wpstg_clone", __( "WP Staging License", "wp-staging" ), __( "License", "wp-staging" ), "manage_options", "wpstg-license", array($this, "getLicensePage")
192
+ );
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Settings Page
198
+ */
199
+ public function getSettingsPage() {
200
+ // Tabs
201
+ $tabs = new Tabs( array(
202
+ "general" => __( "General", "wp-staging" )
203
+ ) );
204
+
205
+
206
+ $this->di
207
+ // Set tabs
208
+ ->set( "tabs", $tabs )
209
+ // Forms
210
+ ->set( "forms", new FormSettings( $tabs ) );
211
+
212
+
213
+ require_once "{$this->path}views/settings/main-settings.php";
214
+ }
215
+
216
+ /**
217
+ * Clone Page
218
+ */
219
+ public function getClonePage() {
220
+ // Existing clones
221
+ $availableClones = get_option( "wpstg_existing_clones_beta", array() );
222
+
223
+ require_once "{$this->path}views/clone/index.php";
224
+ }
225
+
226
+ /**
227
+ * Welcome Page
228
+ */
229
+ public function getWelcomePage() {
230
+ require_once "{$this->path}views/welcome/welcome.php";
231
+ }
232
+
233
+ /**
234
+ * Tools Page
235
+ */
236
+ public function getToolsPage() {
237
+ // Tabs
238
+ $tabs = new Tabs( array(
239
+ "import_export" => __( "Import/Export", "wp-staging" ),
240
+ "system_info" => __( "System Info", "wp-staging" )
241
+ ) );
242
+
243
+ $this->di->set( "tabs", $tabs );
244
+
245
+ $this->di->set( "systemInfo", new SystemInfo( $this->di ) );
246
+
247
+ require_once "{$this->path}views/tools/index.php";
248
+ }
249
+
250
+ /**
251
+ * System Information Download
252
+ */
253
+ public function systemInfoDownload() {
254
+ if( !current_user_can( "update_plugins" ) ) {
255
+ return;
256
+ }
257
+
258
+ nocache_headers();
259
+ header( "Content-Type: text/plain" );
260
+ header( "Content-Disposition: attachment; filename='wpstg-system-info.txt'" );
261
+ echo wp_strip_all_tags( new SystemInfo( $this->di ) );
262
+ }
263
+
264
+ /**
265
+ * Import JSON settings file
266
+ */
267
+ public function import() {
268
+ if( empty( $_POST["wpstg_import_nonce"] ) ) {
269
+ return;
270
+ }
271
+
272
+ if( !wp_verify_nonce( $_POST["wpstg_import_nonce"], "wpstg_import_nonce" ) ) {
273
+ return;
274
+ }
275
+
276
+ if( !current_user_can( "update_plugins" ) ) {
277
+ return;
278
+ }
279
+
280
+ $fileExtension = explode( '.', $_FILES["import_file"]["name"] );
281
+ $fileExtension = end( $fileExtension );
282
+ if( "json" !== $fileExtension ) {
283
+ wp_die( "Please upload a valid .json file", "wp-staging" );
284
+ }
285
+
286
+
287
+ $importFile = $_FILES["import_file"]["tmp_name"];
288
+
289
+ if( empty( $importFile ) ) {
290
+ wp_die( __( "Please upload a file to import", "wp-staging" ) );
291
+ }
292
+
293
+ update_option( "wpstg_settings", json_decode( file_get_contents( $importFile, true ) ) );
294
+
295
+ wp_safe_redirect( admin_url( "admin.php?page=wpstg-tools&amp;wpstg-message=settings-imported" ) );
296
+
297
+ return;
298
+ }
299
+
300
+ /**
301
+ * Export settings to JSON file
302
+ */
303
+ public function export() {
304
+ if( empty( $_POST["wpstg_export_nonce"] ) ) {
305
+ return;
306
+ }
307
+
308
+ if( !wp_verify_nonce( $_POST["wpstg_export_nonce"], "wpstg_export_nonce" ) ) {
309
+ return;
310
+ }
311
+
312
+ if( !current_user_can( "manage_options" ) ) {
313
+ return;
314
+ }
315
+
316
+ $settings = get_option( "wpstg_settings", array() );
317
+
318
+ ignore_user_abort( true );
319
+
320
+ if( !in_array( "set_time_limit", explode( ',', ini_get( "disable_functions" ) ) ) && !@ini_get( "safe_mode" ) ) {
321
+ set_time_limit( 0 );
322
+ }
323
+
324
+ $fileName = apply_filters( "wpstg_settings_export_filename", "wpstg-settings-export-" . date( "m-d-Y" ) ) . ".json";
325
+
326
+ nocache_headers();
327
+ header( "Content-Type: application/json; charset=utf-8" );
328
+ header( "Content-Disposition: attachment; filename={$fileName}" );
329
+ header( "Expires: 0" );
330
+
331
+ echo json_encode( $settings );
332
+ }
333
+
334
+ /**
335
+ * Render a view file
336
+ * @param string $file
337
+ * @param array $vars
338
+ * @return string
339
+ */
340
+ public function render( $file, $vars = array() ) {
341
+ $fullPath = $this->path . "views" . DIRECTORY_SEPARATOR;
342
+ $fullPath = str_replace( array('/', "\\"), DIRECTORY_SEPARATOR, $fullPath . $file . ".php" );
343
+
344
+ if( !file_exists( $fullPath ) || !is_readable( $fullPath ) ) {
345
+ return "Can't render : {$fullPath} either file doesn't exist or can't read it";
346
+ }
347
+
348
+ $contents = @file_get_contents( $fullPath );
349
+
350
+ // Variables are set
351
+ if( count( $vars ) > 0 ) {
352
+ $vars = array_combine(
353
+ array_map( function ($key) {
354
+ return "{{" . $key . "}}";
355
+ }, array_keys( $vars )
356
+ ), $vars
357
+ );
358
+
359
+ $contents = str_replace( array_keys( $vars ), array_values( $vars ), $contents );
360
+ }
361
+
362
+ return $contents;
363
+ }
364
+
365
+ /**
366
+ * Ajax Overview
367
+ */
368
+ public function ajaxOverview() {
369
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
 
371
+ // Existing clones
372
+ $availableClones = get_option( "wpstg_existing_clones_beta", array() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
 
374
+ // Get license data
375
+ $license = get_option( 'wpstg_license_status' );
376
+
377
+ // Get db
378
+ $db = WPStaging::getInstance()->get( 'wpdb' );
379
 
380
+ if( \WPStaging\WPStaging::getSlug() === 'wp-staging-pro' ) {
381
+ require_once "{$this->path}Pro/views/single-overview-pro.php";
382
+ } else {
383
+ require_once "{$this->path}views/clone/ajax/single-overview.php";
384
+ }
385
 
386
+ wp_die();
387
+ }
388
 
389
+ /**
390
+ * Ajax Scan
391
+ */
392
+ public function ajaxScan() {
393
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
394
+
395
+ $db = WPStaging::getInstance()->get( 'wpdb' );
396
+
397
+ // Scan
398
+ $scan = new Scan();
399
+ $scan->start();
400
+
401
+ // Get Options
402
+ $options = $scan->getOptions();
403
+
404
+ require_once "{$this->path}views/clone/ajax/scan.php";
405
+
406
+ wp_die();
407
+ }
408
+
409
+ /**
410
+ * Ajax Check Clone Name
411
+ */
412
+ public function ajaxCheckCloneName() {
413
+ $cloneName = sanitize_key( $_POST["cloneID"] );
414
+ $cloneNameLength = strlen( $cloneName );
415
+ $clones = get_option( "wpstg_existing_clones_beta", array() );
416
+
417
+ $clonePath = trailingslashit( get_home_path() ) . $cloneName;
418
+
419
+ // Check clone name length
420
+ if( $cloneNameLength < 1 || $cloneNameLength > 16 ) {
421
+ echo wp_send_json( array(
422
+ "status" => "failed",
423
+ "message" => "Clone name must be between 1 - 16 characters"
424
+ ) );
425
+ } elseif( array_key_exists( $cloneName, $clones ) ) {
426
+ echo wp_send_json( array(
427
+ "status" => "failed",
428
+ "message" => "Clone name is already in use, please choose an another clone name."
429
+ ) );
430
+ } elseif( is_dir( $clonePath ) ) {
431
+ echo wp_send_json( array(
432
+ "status" => "failed",
433
+ "message" => "Clone directory " . $clonePath . " already exists. Use another clone name."
434
+ ) );
435
+ }
436
+
437
+ echo wp_send_json( array("status" => "success") );
438
+ }
439
+
440
+ /**
441
+ * Ajax Start Updating Clone (Basically just layout and saving data)
442
+ */
443
+ public function ajaxUpdateProcess() {
444
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
445
 
446
+ $cloning = new Updating();
447
 
448
+ if( !$cloning->save() ) {
449
+ wp_die( 'can not save clone data' );
450
+ }
451
 
452
+ require_once "{$this->path}views/clone/ajax/update.php";
453
 
454
+ wp_die();
455
+ }
456
 
457
+ /**
458
+ * Ajax Start Clone (Basically just layout and saving data)
459
+ */
460
+ public function ajaxStartClone() {
461
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
462
 
463
+ $cloning = new Cloning();
464
 
465
+ if( !$cloning->save() ) {
466
+ wp_die( 'can not save clone data' );
467
+ }
468
 
469
+ require_once "{$this->path}views/clone/ajax/start.php";
 
470
 
471
+ wp_die();
472
+ }
 
 
 
473
 
474
+ /**
475
+ * Ajax Clone Database
476
+ */
477
+ public function ajaxCloneDatabase() {
478
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
479
 
480
+ $cloning = new Cloning();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
 
482
+ // Uncomment these lines to test different error codes
483
+ //http_response_code(504);
484
+ //wp_send_json( '<html><body><head></head><body>test</body></html>' );
485
+
486
+ wp_send_json( $cloning->start() );
487
+ }
488
+
489
+ /**
490
+ * Ajax Prepare Directories (get listing of files)
491
+ */
492
+ public function ajaxPrepareDirectories() {
493
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
494
+
495
+ $cloning = new Cloning();
496
+
497
+ wp_send_json( $cloning->start() );
498
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
 
500
+ /**
501
+ * Ajax Clone Files
502
+ */
503
+ public function ajaxCopyFiles() {
504
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
505
+
506
+ $cloning = new Cloning();
507
+
508
+ wp_send_json( $cloning->start() );
509
+ }
510
+
511
+ /**
512
+ * Ajax Replace Data
513
+ */
514
+ public function ajaxReplaceData() {
515
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
516
+
517
+ $cloning = new Cloning();
518
+
519
+ wp_send_json( $cloning->start() );
520
+ }
521
+
522
+ /**
523
+ * Ajax Finish
524
+ */
525
+ public function ajaxFinish() {
526
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
527
+
528
+ $cloning = new Cloning();
529
+
530
+ wp_send_json( $cloning->start() );
531
+ }
532
+
533
+ /**
534
+ * Ajax Delete Confirmation
535
+ */
536
+ public function ajaxDeleteConfirmation() {
537
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
538
+
539
+ $delete = new Delete();
540
+ $delete->setData();
541
+
542
+ $clone = $delete->getClone();
543
+
544
+ $dbname = $delete->getDbName();
545
+
546
+ require_once "{$this->path}views/clone/ajax/delete-confirmation.php";
547
+
548
+ wp_die();
549
+ }
550
+
551
+ /**
552
+ * Delete clone
553
+ */
554
+ public function ajaxDeleteClone() {
555
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
556
+
557
+ $delete = new Delete();
558
+
559
+ wp_send_json( $delete->start() );
560
+ }
561
+
562
+ /**
563
+ * Delete clone
564
+ */
565
+ public function ajaxCancelClone() {
566
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
567
+
568
+ $cancel = new Cancel();
569
+
570
+ wp_send_json( $cancel->start() );
571
+ }
572
+
573
+ /**
574
+ * Cancel updating process / Do not delete clone!
575
+ */
576
+ public function ajaxCancelUpdate() {
577
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
578
+
579
+ $cancel = new CancelUpdate();
580
+ wp_send_json( $cancel->start() );
581
+ }
582
+
583
+ /**
584
+ * Admin Messages
585
+ */
586
+ public function messages() {
587
+ $notice = new Notices( $this->path, $this->url );
588
+
589
+ $run = $notice->messages();
590
+ }
591
+
592
+ /**
593
+ * Ajax Hide Poll
594
+ * @return mixed boolean | json
595
+ */
596
+ public function ajaxHidePoll() {
597
+ if( false !== update_option( "wpstg_poll", "no" ) ) {
598
+ wp_send_json( true );
599
+ }
600
+ return wp_send_json();
601
+ }
602
+
603
+ /**
604
+ * Ajax Hide Rating
605
+ * @return mixed bool | json
606
+ */
607
+ public function ajaxHideRating() {
608
+ if( false !== update_option( "wpstg_rating", "no" ) ) {
609
+ wp_send_json( true );
610
+ }
611
+ return wp_send_json();
612
+ }
613
+
614
+ /**
615
+ * Ajax Hide Rating and show it again after one week
616
+ * @return mixed bool | json
617
+ */
618
+ public function ajaxHideLaterRating() {
619
+ if( false !== update_option( 'wpstg_rating', date( 'Y-m-d' ) )
620
+ ) {
621
+ wp_send_json( true );
622
+ }
623
+ return wp_send_json(false);
624
+ }
625
+
626
+ /**
627
+ * Ajax Hide Beta
628
+ */
629
+ public function ajaxHideBeta() {
630
+ wp_send_json( update_option( "wpstg_beta", "no" ) );
631
+ }
632
+
633
+ /**
634
+ * Clone logs
635
+ */
636
+ public function ajaxLogs() {
637
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
638
+
639
+ $logs = new Logs();
640
+ wp_send_json( $logs->start() );
641
+ }
642
+
643
+ /**
644
+ * Ajax Checks Free Disk Space
645
+ */
646
+ public function ajaxCheckFreeSpace() {
647
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
648
+
649
+ $scan = new Scan();
650
+ return $scan->hasFreeDiskSpace();
651
+ }
652
+
653
+ /**
654
+ * Ajax Start Push Changes Process
655
+ * Start with the module Scan
656
+ */
657
+ public function ajaxPushScan() {
658
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
659
+
660
+ if( !class_exists( 'WPStaging\Backend\Pro\Modules\Jobs\Scan' ) ) {
661
+ return false;
662
+ }
663
+
664
+ // Scan
665
+ $scan = new Pro\Modules\Jobs\Scan();
666
+
667
+ $scan->start();
668
+
669
+ // Get Options
670
+ $options = $scan->getOptions();
671
+
672
+ require_once "{$this->path}Pro/views/scan.php";
673
+
674
+ wp_die();
675
+ }
676
+
677
+ /**
678
+ * Ajax Start Pushing. Needs wp quads pro
679
+ */
680
+ public function ajaxPushProcessing() {
681
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
682
+
683
+ if( !class_exists( 'WPStaging\Backend\Pro\Modules\Jobs\Processing' ) ) {
684
+ return false;
685
+ }
686
+ // Start the process
687
+ $processing = new Processing();
688
+ wp_send_json( $processing->start() );
689
+ }
690
+
691
+ /**
692
+ * License Page
693
+ */
694
+ public function getLicensePage() {
695
+
696
+ // Get license data
697
+ $license = get_option( 'wpstg_license_status' );
698
+
699
+ require_once "{$this->path}Pro/views/licensing.php";
700
+ }
701
+
702
+ /**
703
+ *
704
+ * @param type $args
705
+ */
706
+ public function ajaxSendReport( $args = array() ) {
707
+ // Set params
708
+ if( empty( $args ) ) {
709
+ $args = stripslashes_deep( $_POST );
710
+ }
711
+ // Set e-mail
712
+ $email = null;
713
+ if( isset( $args['wpstg_email'] ) ) {
714
+ $email = trim( $args['wpstg_email'] );
715
+ }
716
+
717
+ // Set message
718
+ $message = null;
719
+ if( isset( $args['wpstg_message'] ) ) {
720
+ $message = trim( $args['wpstg_message'] );
721
+ }
722
+
723
+ // Set syslog
724
+ $syslog = false;
725
+ if( isset( $args['wpstg_syslog'] ) ) {
726
+ $syslog = ( bool ) $args['wpstg_syslog'];
727
+ }
728
+
729
+ // Set terms
730
+ $terms = false;
731
+ if( isset( $args['wpstg_terms'] ) ) {
732
+ $terms = ( bool ) $args['wpstg_terms'];
733
+ }
734
+
735
+ $report = new Report( $this->di );
736
+ $errors = $report->send( $email, $message, $terms, $syslog );
737
+
738
+ echo json_encode( array('errors' => $errors) );
739
+ exit;
740
+ }
741
+
742
+ /**
743
+ * Connect to external database for testing correct credentials
744
+ */
745
+ public function ajaxDatabaseConnect() {
746
+ // Set params
747
+ if( empty( $args ) ) {
748
+ $args = stripslashes_deep( $_POST );
749
+ }
750
+
751
+ $user = !empty( $args['databaseUser'] ) ? $args['databaseUser'] : '';
752
+ $password = !empty( $args['databasePassword'] ) ? $args['databasePassword'] : '';
753
+ $database = !empty( $args['databaseDatabase'] ) ? $args['databaseDatabase'] : '';
754
+ $server = !empty( $args['databaseServer'] ) ? $args['databaseServer'] : '';
755
+
756
+ $db = new \wpdb( $user, $password, $database, $server );
757
+
758
+ // Can not connect to mysql
759
+ if( !empty( $db->error->errors['db_connect_fail']['0'] ) ) {
760
+ echo json_encode( array('errors' => $db->error->errors['db_connect_fail']['0']) );
761
+ exit;
762
+ }
763
+
764
+
765
+ // Can not connect to database
766
+ $sql = "SHOW DATABASES LIKE '{$database}';";
767
+ $results = $db->query( $sql );
768
+ if( empty( $results ) ) {
769
+ echo json_encode( array('errors' => " Database {$database} does not exist. You need to create it first. ") );
770
+ exit;
771
+ }
772
+ echo json_encode( array('success' => 'true') );
773
+ exit;
774
+ }
775
 
776
  }
apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -75,7 +75,7 @@ class Data extends JobExecutable {
75
  * @return void
76
  */
77
  protected function calculateTotalSteps() {
78
- $this->options->totalSteps = 15;
79
  }
80
 
81
  /**
@@ -841,7 +841,6 @@ class Data extends JobExecutable {
841
  preg_match( "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/", $content, $matches );
842
 
843
  if( !empty( $matches[1] ) ) {
844
- $matches[1];
845
 
846
  $pattern = "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/";
847
 
@@ -864,6 +863,87 @@ class Data extends JobExecutable {
864
  return true;
865
  }
866
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
867
  protected function getNewUploadPath() {
868
  $uploadPath = get_option( 'upload_path' );
869
 
75
  * @return void
76
  */
77
  protected function calculateTotalSteps() {
78
+ $this->options->totalSteps = 17;
79
  }
80
 
81
  /**
841
  preg_match( "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/", $content, $matches );
842
 
843
  if( !empty( $matches[1] ) ) {
 
844
 
845
  $pattern = "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/";
846
 
863
  return true;
864
  }
865
 
866
+ /**
867
+ * Remove WP_CONTENT_DIR in wp-config.php
868
+ * @return bool
869
+ */
870
+ protected function step15() {
871
+ $path = $this->options->destinationDir . "wp-config.php";
872
+
873
+ $this->log( "Preparing Data Step15: Set WP_CONTENT_DIR in wp-config.php to false" );
874
+
875
+ if( false === ($content = file_get_contents( $path )) ) {
876
+ $this->log( "Preparing Data Step15: Failed to update WP_CONTENT_DIR in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
877
+ return false;
878
+ }
879
+
880
+
881
+ // Get WP_CONTENT_DIR from wp-config.php
882
+ preg_match( "/define\s*\(\s*'WP_CONTENT_DIR'\s*,\s*(.*)\s*\);/", $content, $matches );
883
+
884
+ if( !empty( $matches[0] ) ) {
885
+
886
+ $pattern = "/define\s*\(\s*'WP_CONTENT_DIR'\s*,\s*(.*)\s*\);/";
887
+
888
+ $replace = "";
889
+
890
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
891
+ $this->log( "Preparing Data: Failed to change WP_CONTENT_DIR", Logger::TYPE_ERROR );
892
+ return false;
893
+ }
894
+ } else {
895
+ $this->log( "Preparing Data Step15: WP_CONTENT_DIR not defined in wp-config.php. Skipping this step." );
896
+ }
897
+
898
+ if( false === @file_put_contents( $path, $content ) ) {
899
+ $this->log( "Preparing Data Step15: Failed to update WP_CONTENT_DIR. Can't save contents", Logger::TYPE_ERROR );
900
+ return false;
901
+ }
902
+ $this->Log( "Preparing Data Step 15: Finished successfully" );
903
+ return true;
904
+ }
905
+ /**
906
+ * Remove WP_CONTENT_URL in wp-config.php
907
+ * @return bool
908
+ */
909
+ protected function step16() {
910
+ $path = $this->options->destinationDir . "wp-config.php";
911
+
912
+ $this->log( "Preparing Data Step16: Set WP_CONTENT_URL in wp-config.php to false" );
913
+
914
+ if( false === ($content = file_get_contents( $path )) ) {
915
+ $this->log( "Preparing Data Step16: Failed to update WP_CONTENT_URL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
916
+ return false;
917
+ }
918
+
919
+
920
+ // Get WP_CONTENT_DIR from wp-config.php
921
+ preg_match( "/define\s*\(\s*'WP_CONTENT_URL'\s*,\s*(.*)\s*\);/", $content, $matches );
922
+
923
+ if( !empty( $matches[0] ) ) {
924
+
925
+ $pattern = "/define\s*\(\s*'WP_CONTENT_URL'\s*,\s*(.*)\s*\);/";
926
+
927
+ $replace = "";
928
+
929
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
930
+ $this->log( "Preparing Data: Failed to change WP_CONTENT_URL", Logger::TYPE_ERROR );
931
+ return false;
932
+ }
933
+ } else {
934
+ $this->log( "Preparing Data Step16: WP_CONTENT_URL not defined in wp-config.php. Skipping this step." );
935
+ }
936
+
937
+ if( false === @file_put_contents( $path, $content ) ) {
938
+ $this->log( "Preparing Data Step16: Failed to update WP_CONTENT_URL. Can't save contents", Logger::TYPE_ERROR );
939
+ return false;
940
+ }
941
+ $this->Log( "Preparing Data Step 16: Finished successfully" );
942
+ return true;
943
+ }
944
+
945
+
946
+
947
  protected function getNewUploadPath() {
948
  $uploadPath = get_option( 'upload_path' );
949
 
apps/Backend/Modules/Jobs/Directories.php CHANGED
@@ -4,14 +4,13 @@ namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  // No Direct Access
6
  if( !defined( "WPINC" ) ) {
7
- die;
8
  }
9
 
10
  use WPStaging\WPStaging;
11
  use WPStaging\Utils\Logger;
12
  use WPStaging\Utils\Strings;
13
  use WPStaging\Iterators\RecursiveDirectoryIterator;
14
- use WPStaging\Iterators\RecursiveFilterNewLine;
15
  use WPStaging\Iterators\RecursiveFilterExclude;
16
 
17
  /**
@@ -20,487 +19,469 @@ use WPStaging\Iterators\RecursiveFilterExclude;
20
  */
21
  class Directories extends JobExecutable {
22
 
23
- /**
24
- * @var array
25
- */
26
- private $files = array();
27
-
28
- /**
29
- * Total steps to do
30
- * @var int
31
- */
32
- private $total = 4;
33
-
34
- /**
35
- * path to the cache file
36
- * @var string
37
- */
38
- private $filename;
39
-
40
- /**
41
- * Initialize
42
- */
43
- public function initialize() {
44
- $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
45
- }
46
-
47
- /**
48
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
49
- * @return void
50
- */
51
- protected function calculateTotalSteps() {
52
-
53
- $this->options->totalSteps = $this->total + count( $this->options->extraDirectories );
54
- }
55
-
56
- /**
57
- * Start Module
58
- * @return object
59
- */
60
- public function start() {
61
-
62
- // Execute steps
63
- $this->run();
64
-
65
- // Save option, progress
66
- $this->saveProgress();
67
-
68
- return ( object ) $this->response;
69
- }
70
-
71
- /**
72
- * Step 0
73
- * Get WP Root files
74
- * Does not collect any sub folders
75
- */
76
- private function getWpRootFiles() {
77
-
78
- // open file handle
79
- $files = $this->open( $this->filename, 'a' );
80
-
81
-
82
- try {
83
-
84
- // Iterate over wp root directory
85
- $iterator = new \DirectoryIterator( \WPStaging\WPStaging::getWPpath() );
86
-
87
- $this->log( "Scanning / for files" );
88
-
89
- // Write path line
90
- foreach ( $iterator as $item ) {
91
- if( !$item->isDot() && $item->isFile() ) {
92
- if( $this->write( $files, $iterator->getFilename() . PHP_EOL ) ) {
93
- $this->options->totalFiles++;
94
-
95
- // Too much cpu time
96
- //$this->options->totalFileSize += $iterator->getSize();
97
- }
 
98
  }
99
- }
100
- } catch ( \Exception $e ) {
101
- $this->returnException( 'Error: ' . $e->getMessage() );
102
- } catch ( \Exception $e ) {
103
- // Skip bad file permissions
104
- }
105
-
106
- $this->close( $files );
107
- return true;
108
- }
109
-
110
- /**
111
- * Step 2
112
- * Get WP Content Files
113
- */
114
- private function getWpContentFiles() {
115
-
116
- // Skip it
117
- if( $this->isDirectoryExcluded( WP_CONTENT_DIR ) ) {
118
  $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR );
119
- return true;
120
- }
121
- // open file handle
122
- $files = $this->open( $this->filename, 'a' );
123
-
124
- $excludeWpContent = array(
125
- 'cache',
126
- 'wps-hide-login',
127
  'node_modules'
128
- );
129
 
130
- try {
131
 
132
- // Iterate over content directory
133
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
134
 
135
- // Exclude new line file names
136
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
137
 
138
- // Exclude uploads, plugins or themes
139
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_excl_folders', $excludeWpContent ) );
140
- // Recursively iterate over content directory
141
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
142
 
143
- $this->log( "Scanning /wp-content for its sub-directories and files" );
144
 
145
- // Write path line
146
- foreach ( $iterator as $item ) {
147
- if( $item->isFile() ) {
148
- if( $this->write( $files, 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
149
- $this->options->totalFiles++;
 
150
 
151
- // Too much cpu time
152
- //$this->options->totalFileSize += $iterator->getSize();
153
- }
 
154
  }
155
- }
156
- } catch ( \Exception $e ) {
157
- //$this->returnException('Out of disk space.');
158
- throw new \Exception( 'Error: ' . $e->getMessage() );
159
- } catch ( \Exception $e ) {
160
- // Skip bad file permissions
161
- }
162
-
163
- // close the file handler
164
- $this->close( $files );
165
- return true;
166
- }
167
-
168
- /**
169
- * Step 2
170
- * @return boolean
171
- * @throws \Exception
172
- */
173
- private function getWpIncludesFiles() {
174
-
175
- // Skip it
176
- if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR ) ) {
177
  $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
178
- return true;
179
- }
180
 
181
- // open file handle and attach data to end of file
182
- $files = $this->open( $this->filename, 'a' );
183
 
184
- try {
185
 
186
- // Iterate over wp-admin directory
187
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
188
 
189
- // Exclude new line file names
190
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
191
 
192
- // Recursively iterate over wp-includes directory
193
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
194
 
195
- $this->log( "Scanning /wp-includes for its sub-directories and files" );
196
 
197
- // Write files
198
- foreach ( $iterator as $item ) {
199
- if( $item->isFile() ) {
200
- if( $this->write( $files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
201
- $this->options->totalFiles++;
202
 
203
- // Too much cpu time
204
- //$this->options->totalFileSize += $iterator->getSize();
205
- }
 
206
  }
207
- }
208
- } catch ( \Exception $e ) {
209
- //$this->returnException('Out of disk space.');
210
- throw new \Exception( 'Error: ' . $e->getMessage() );
211
- } catch ( \Exception $e ) {
212
- // Skip bad file permissions
213
- }
214
-
215
- // close the file handler
216
- $this->close( $files );
217
- return true;
218
- }
219
-
220
- /**
221
- * Step 3
222
- * @return boolean
223
- * @throws \Exception
224
- */
225
- private function getWpAdminFiles() {
226
-
227
- // Skip it
228
- if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR ) ) {
229
  $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
230
- return true;
231
- }
232
 
233
- // open file handle and attach data to end of file
234
- $files = $this->open( $this->filename, 'a' );
235
 
236
- try {
237
 
238
- // Iterate over wp-admin directory
239
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
240
 
241
- // Exclude new line file names
242
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
243
 
244
- // Recursively iterate over content directory
245
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
246
 
247
- $this->log( "Scanning /wp-admin for its sub-directories and files" );
248
 
249
- // Write path line
250
- foreach ( $iterator as $item ) {
251
- if( $item->isFile() ) {
252
- if( $this->write( $files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
253
- $this->options->totalFiles++;
254
- // Too much cpu time
255
- //$this->options->totalFileSize += $iterator->getSize();
256
- }
 
257
  }
258
- }
259
- } catch ( \Exception $e ) {
260
- $this->returnException( 'Error: ' . $e->getMessage() );
261
- //throw new \Exception('Error: ' . $e->getMessage());
262
- } catch ( \Exception $e ) {
263
- // Skip bad file permissions
264
- }
265
-
266
- // close the file handler
267
- $this->close( $files );
268
- return true;
269
- }
270
-
271
- /**
272
- * Step 4 - x
273
- * Get extra folders of the wp root level
274
- * Does not collect wp-includes, wp-admin and wp-content folder
275
- */
276
- private function getExtraFiles( $folder ) {
277
 
278
  if( !is_dir( $folder ) ) {
279
  return true;
280
  }
281
 
282
- // open file handle and attach data to end of file
283
- $files = $this->open( $this->filename, 'a' );
284
-
285
- try {
286
-
287
- // Iterate over extra directory
288
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
289
-
290
- // Exclude new line file names
291
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
292
-
293
- // Exclude wp core folders
294
- // $exclude = array('wp-includes',
295
- // 'wp-admin',
296
- // 'wp-content');
297
- //
298
- // $excludeMore = array();
299
- // foreach ($this->options->excludedDirectories as $key => $value){
300
- // $excludeMore[] = $this->getLastElemAfterString('/', $value);
301
- // }
302
- //$exclude = array_merge($exclude, $excludeMore);
303
-
304
- $exclude = array();
305
-
306
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
307
- // Recursively iterate over content directory
308
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
309
-
310
- $strings = new Strings();
311
- $this->log( "Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files" );
312
-
313
- // Write path line
314
- foreach ( $iterator as $item ) {
315
- if( $item->isFile() ) {
316
- //if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
317
- if( $this->write( $files, str_replace( \WPStaging\WPStaging::getWPpath(), '', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
318
- $this->options->totalFiles++;
319
- // Too much cpu time
320
- //$this->options->totalFileSize += $iterator->getSize();
321
- }
322
  }
323
- }
324
- } catch ( \Exception $e ) {
325
- $this->returnException( 'Error: ' . $e->getMessage() );
326
- } catch ( \Exception $e ) {
327
- // Skip bad file permissions
328
- }
329
-
330
- // close the file handler
331
- $this->close( $files );
332
- return true;
333
- }
334
-
335
- /**
336
- * Closes a file handle
337
- *
338
- * @param resource $handle File handle to close
339
- * @return boolean
340
- */
341
- public function close( $handle ) {
342
- return @fclose( $handle );
343
- }
344
-
345
- /**
346
- * Opens a file in specified mode
347
- *
348
- * @param string $file Path to the file to open
349
- * @param string $mode Mode in which to open the file
350
- * @return resource
351
- * @throws Exception
352
- */
353
- public function open( $file, $mode ) {
354
-
355
- $file_handle = @fopen( $file, $mode );
356
- if( false === $file_handle ) {
357
- $this->returnException( sprintf( __( 'Unable to open %s with mode %s', 'wp-staging' ), $file, $mode ) );
358
- //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wp-staging'), $file, $mode));
359
- }
360
-
361
- return $file_handle;
362
- }
363
-
364
- /**
365
- * Write contents to a file
366
- *
367
- * @param resource $handle File handle to write to
368
- * @param string $content Contents to write to the file
369
- * @return integer
370
- * @throws Exception
371
- * @throws Exception
372
- */
373
- public function write( $handle, $content ) {
374
- $write_result = @fwrite( $handle, $content );
375
- if( false === $write_result ) {
376
- if( ( $meta = \stream_get_meta_data( $handle ) ) ) {
377
- //$this->returnException(sprintf(__('Unable to write to: %s', 'wp-staging'), $meta['uri']));
378
- throw new \Exception( sprintf( __( 'Unable to write to: %s', 'wp-staging' ), $meta['uri'] ) );
379
- }
380
- } elseif( strlen( $content ) !== $write_result ) {
381
- //$this->returnException(__('Out of disk space.', 'wp-staging'));
382
- throw new \Exception( __( 'Out of disk space.', 'wp-staging' ) );
383
- }
384
-
385
- return $write_result;
386
- }
387
-
388
- /**
389
- * Execute the Current Step
390
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
391
- * @return bool
392
- */
393
- protected function execute() {
394
-
395
- // No job left to execute
396
- if( $this->isFinished() ) {
397
- $this->prepareResponse( true, false );
398
- return false;
399
- }
400
-
401
-
402
- if( $this->options->currentStep == 0 ) {
403
- $this->getWpRootFiles();
404
- $this->prepareResponse( false, true );
405
- return false;
406
- }
407
-
408
- if( $this->options->currentStep == 1 ) {
409
- $this->getWpContentFiles();
410
- $this->prepareResponse( false, true );
411
- return false;
412
- }
413
-
414
- if( $this->options->currentStep == 2 ) {
415
- $this->getWpIncludesFiles();
416
- $this->prepareResponse( false, true );
417
- return false;
418
- }
419
-
420
- if( $this->options->currentStep == 3 ) {
421
- $this->getWpAdminFiles();
422
- $this->prepareResponse( false, true );
423
- return false;
424
- }
425
-
426
- if( isset( $this->options->extraDirectories[$this->options->currentStep - $this->total] ) ) {
427
- $this->getExtraFiles( $this->options->extraDirectories[$this->options->currentStep - $this->total] );
428
- $this->prepareResponse( false, true );
429
- return false;
430
- }
431
-
432
-
433
- // Prepare response
434
- $this->prepareResponse( false, true );
435
- // Not finished
436
- return true;
437
- }
438
-
439
- /**
440
- * Checks Whether There is Any Job to Execute or Not
441
- * @return bool
442
- */
443
- protected function isFinished() {
444
- if( $this->options->currentStep >= $this->options->totalSteps ) {
445
- return true;
446
- }
447
-
448
- // return (
449
- // //$this->options->currentStep > $this->total ||
450
- // $this->options->currentStep >= $this->options->totalSteps
451
- // );
452
- }
453
-
454
- /**
455
- * Save files
456
- * @return bool
457
- */
458
- protected function saveProgress() {
459
- return $this->saveOptions();
460
- }
461
-
462
- /**
463
- * Get files
464
- * @return void
465
- */
466
- protected function getFiles() {
467
- $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
468
-
469
- if( false === ($this->files = @file_get_contents( $fileName )) ) {
470
- $this->files = array();
471
- return;
472
- }
473
-
474
- $this->files = explode( PHP_EOL, $this->files );
475
- }
476
-
477
- /**
478
- * Replace forward slash with current directory separator
479
- *
480
- * @param string $path Path
481
- *
482
- * @return string
483
- */
484
- private function sanitizeDirectorySeparator( $path ) {
485
- $string = str_replace( "/", "\\", $path );
486
- return str_replace( '\\\\', '\\', $string );
487
- }
488
-
489
- /**
490
- * Check if directory is excluded
491
- * @param string $directory
492
- * @return bool
493
- */
494
- protected function isDirectoryExcluded( $directory ) {
495
- $directory = $this->sanitizeDirectorySeparator( $directory );
496
- foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
497
- $excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
498
- if( strpos( trailingslashit( $directory ), trailingslashit( $excludedDirectory ) ) === 0 ) {
499
  return true;
500
- }
501
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
 
503
- return false;
504
- }
505
 
506
  }
4
 
5
  // No Direct Access
6
  if( !defined( "WPINC" ) ) {
7
+ die;
8
  }
9
 
10
  use WPStaging\WPStaging;
11
  use WPStaging\Utils\Logger;
12
  use WPStaging\Utils\Strings;
13
  use WPStaging\Iterators\RecursiveDirectoryIterator;
 
14
  use WPStaging\Iterators\RecursiveFilterExclude;
15
 
16
  /**
19
  */
20
  class Directories extends JobExecutable {
21
 
22
+ /**
23
+ * @var array
24
+ */
25
+ private $files = array();
26
+
27
+ /**
28
+ * Total steps to do
29
+ * @var int
30
+ */
31
+ private $total = 4;
32
+
33
+ /**
34
+ * path to the cache file
35
+ * @var string
36
+ */
37
+ private $filename;
38
+
39
+ /**
40
+ * Initialize
41
+ */
42
+ public function initialize() {
43
+ $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
44
+ }
45
+
46
+ /**
47
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
48
+ * @return void
49
+ */
50
+ protected function calculateTotalSteps() {
51
+
52
+ $this->options->totalSteps = $this->total + count( $this->options->extraDirectories );
53
+ }
54
+
55
+ /**
56
+ * Start Module
57
+ * @return object
58
+ */
59
+ public function start() {
60
+
61
+ // Execute steps
62
+ $this->run();
63
+
64
+ // Save option, progress
65
+ $this->saveProgress();
66
+
67
+ return ( object ) $this->response;
68
+ }
69
+
70
+ /**
71
+ * Step 0
72
+ * Get WP Root files
73
+ * Does not collect any sub folders
74
+ */
75
+ private function getWpRootFiles() {
76
+
77
+ // open file handle
78
+ $files = $this->open( $this->filename, 'a' );
79
+
80
+
81
+ try {
82
+
83
+ // Iterate over wp root directory
84
+ $iterator = new \DirectoryIterator( \WPStaging\WPStaging::getWPpath() );
85
+
86
+ $this->log( "Scanning / for files" );
87
+
88
+ // Write path line
89
+ foreach ( $iterator as $item ) {
90
+ if( !$item->isDot() && $item->isFile() ) {
91
+ if( $this->write( $files, $iterator->getFilename() . PHP_EOL ) ) {
92
+ $this->options->totalFiles++;
93
+
94
+ // Too much cpu time
95
+ //$this->options->totalFileSize += $iterator->getSize();
96
+ }
97
+ }
98
  }
99
+ } catch ( \Exception $e ) {
100
+ $this->returnException( 'Error: ' . $e->getMessage() );
101
+ } catch ( \Exception $e ) {
102
+ // Skip bad file permissions
103
+ }
104
+
105
+ $this->close( $files );
106
+ return true;
107
+ }
108
+
109
+ /**
110
+ * Step 2
111
+ * Get WP Content Files
112
+ */
113
+ private function getWpContentFiles() {
114
+
115
+ // Skip it
116
+ if( $this->isDirectoryExcluded( WP_CONTENT_DIR ) ) {
 
117
  $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR );
118
+ return true;
119
+ }
120
+ // open file handle
121
+ $files = $this->open( $this->filename, 'a' );
122
+
123
+ $excludeWpContent = array(
124
+ 'cache',
125
+ 'wps-hide-login',
126
  'node_modules'
127
+ );
128
 
129
+ try {
130
 
131
+ // Iterate over content directory
132
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
133
 
134
+ // Exclude new line file names Do not use this. Leads to error 500 on some systems
135
+ // $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
136
 
137
+ // Exclude uploads, plugins or themes
138
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_excl_folders', $excludeWpContent ) );
139
+ // Recursively iterate over content directory
140
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
141
 
142
+ $this->log( "Scanning wp-content folder " . WP_CONTENT_DIR );
143
 
144
+ // Write path line
145
+ foreach ( $iterator as $item ) {
146
+ if( $item->isFile() ) {
147
+ $wpContentDir = str_replace(ABSPATH, '', WP_CONTENT_DIR);
148
+ if( $this->write( $files, $wpContentDir . '/' . $iterator->getSubPathName() . PHP_EOL ) ) {
149
+ $this->options->totalFiles++;
150
 
151
+ // Too much cpu time
152
+ //$this->options->totalFileSize += $iterator->getSize();
153
+ }
154
+ }
155
  }
156
+ } catch ( \Exception $e ) {
157
+ $this->returnException( 'Error: ' . $e->getMessage() );
158
+ //throw new \Exception( 'Error: ' . $e->getMessage() );
159
+ }
160
+
161
+ // close the file handler
162
+ $this->close( $files );
163
+ return true;
164
+ }
165
+
166
+ /**
167
+ * Step 2
168
+ * @return boolean
169
+ * @throws \Exception
170
+ */
171
+ private function getWpIncludesFiles() {
172
+
173
+ // Skip it
174
+ if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR ) ) {
 
 
 
175
  $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
176
+ return true;
177
+ }
178
 
179
+ // open file handle and attach data to end of file
180
+ $files = $this->open( $this->filename, 'a' );
181
 
182
+ try {
183
 
184
+ // Iterate over wp-admin directory
185
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
186
 
187
+ // Exclude new line file names Do not use this. Leads to error 500 on some systems
188
+ // $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
189
 
190
+ // Recursively iterate over wp-includes directory
191
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
192
 
193
+ $this->log( "Scanning /wp-includes for its sub-directories and files" );
194
 
195
+ // Write files
196
+ foreach ( $iterator as $item ) {
197
+ if( $item->isFile() ) {
198
+ if( $this->write( $files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
199
+ $this->options->totalFiles++;
200
 
201
+ // Too much cpu time
202
+ //$this->options->totalFileSize += $iterator->getSize();
203
+ }
204
+ }
205
  }
206
+ } catch ( \Exception $e ) {
207
+ //$this->returnException('Out of disk space.');
208
+ throw new \Exception( 'Error: ' . $e->getMessage() );
209
+ } catch ( \Exception $e ) {
210
+ // Skip bad file permissions
211
+ }
212
+
213
+ // close the file handler
214
+ $this->close( $files );
215
+ return true;
216
+ }
217
+
218
+ /**
219
+ * Step 3
220
+ * @return boolean
221
+ * @throws \Exception
222
+ */
223
+ private function getWpAdminFiles() {
224
+
225
+ // Skip it
226
+ if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR ) ) {
 
227
  $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
228
+ return true;
229
+ }
230
 
231
+ // open file handle and attach data to end of file
232
+ $files = $this->open( $this->filename, 'a' );
233
 
234
+ try {
235
 
236
+ // Iterate over wp-admin directory
237
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
238
 
239
+ // Exclude new line file names Do not use this. Leads to error 500 on some systems
240
+ // $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
241
 
242
+ // Recursively iterate over content directory
243
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
244
 
245
+ $this->log( "Scanning /wp-admin for its sub-directories and files" );
246
 
247
+ // Write path line
248
+ foreach ( $iterator as $item ) {
249
+ if( $item->isFile() ) {
250
+ if( $this->write( $files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
251
+ $this->options->totalFiles++;
252
+ // Too much cpu time
253
+ //$this->options->totalFileSize += $iterator->getSize();
254
+ }
255
+ }
256
  }
257
+ } catch ( \Exception $e ) {
258
+ $this->returnException( 'Error: ' . $e->getMessage() );
259
+ } catch ( \Exception $e ) {
260
+ // Skip bad file permissions
261
+ }
262
+
263
+ // close the file handler
264
+ $this->close( $files );
265
+ return true;
266
+ }
267
+
268
+ /**
269
+ * Step 4 - x
270
+ * Get extra folders of the wp root level
271
+ * Does not collect wp-includes, wp-admin and wp-content folder
272
+ */
273
+ private function getExtraFiles( $folder ) {
 
 
274
 
275
  if( !is_dir( $folder ) ) {
276
  return true;
277
  }
278
 
279
+ // open file handle and attach data to end of file
280
+ $files = $this->open( $this->filename, 'a' );
281
+
282
+ try {
283
+
284
+ // Iterate over extra directory
285
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
286
+
287
+ // Exclude new line file names Do not use this. Leads to error 500 on some systems
288
+ // $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
289
+
290
+ $exclude = array();
291
+
292
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
293
+ // Recursively iterate over content directory
294
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
295
+
296
+ $strings = new Strings();
297
+ $this->log( "Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files" );
298
+
299
+ // Write path line
300
+ foreach ( $iterator as $item ) {
301
+ if( $item->isFile() ) {
302
+ //if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
303
+ if( $this->write( $files, str_replace( \WPStaging\WPStaging::getWPpath(), '', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
304
+ $this->options->totalFiles++;
305
+ // Too much cpu time
306
+ //$this->options->totalFileSize += $iterator->getSize();
307
+ }
308
+ }
 
 
 
 
 
 
 
 
 
 
309
  }
310
+ } catch ( \Exception $e ) {
311
+ $this->returnException( 'Error: ' . $e->getMessage() );
312
+ } catch ( \Exception $e ) {
313
+ // Skip bad file permissions
314
+ }
315
+
316
+ // close the file handler
317
+ $this->close( $files );
318
+ return true;
319
+ }
320
+
321
+ /**
322
+ * Closes a file handle
323
+ *
324
+ * @param resource $handle File handle to close
325
+ * @return boolean
326
+ */
327
+ public function close( $handle ) {
328
+ return @fclose( $handle );
329
+ }
330
+
331
+ /**
332
+ * Opens a file in specified mode
333
+ *
334
+ * @param string $file Path to the file to open
335
+ * @param string $mode Mode in which to open the file
336
+ * @return resource
337
+ * @throws Exception
338
+ */
339
+ public function open( $file, $mode ) {
340
+
341
+ $file_handle = @fopen( $file, $mode );
342
+ if( false === $file_handle ) {
343
+ $this->returnException( sprintf( __( 'Unable to open %s with mode %s', 'wp-staging' ), $file, $mode ) );
344
+ //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wp-staging'), $file, $mode));
345
+ }
346
+
347
+ return $file_handle;
348
+ }
349
+
350
+ /**
351
+ * Write contents to a file
352
+ *
353
+ * @param resource $handle File handle to write to
354
+ * @param string $content Contents to write to the file
355
+ * @return integer
356
+ * @throws Exception
357
+ * @throws Exception
358
+ */
359
+ public function write( $handle, $content ) {
360
+ $write_result = @fwrite( $handle, $content );
361
+ if( false === $write_result ) {
362
+ if( ( $meta = \stream_get_meta_data( $handle ) ) ) {
363
+ //$this->returnException(sprintf(__('Unable to write to: %s', 'wp-staging'), $meta['uri']));
364
+ throw new \Exception( sprintf( __( 'Unable to write to: %s', 'wp-staging' ), $meta['uri'] ) );
365
+ }
366
+ } elseif( strlen( $content ) !== $write_result ) {
367
+ //$this->returnException(__('Out of disk space.', 'wp-staging'));
368
+ throw new \Exception( __( 'Out of disk space.', 'wp-staging' ) );
369
+ }
370
+
371
+ return $write_result;
372
+ }
373
+
374
+ /**
375
+ * Execute the Current Step
376
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
377
+ * @return bool
378
+ */
379
+ protected function execute() {
380
+
381
+ // No job left to execute
382
+ if( $this->isFinished() ) {
383
+ $this->prepareResponse( true, false );
384
+ return false;
385
+ }
386
+
387
+
388
+ if( $this->options->currentStep == 0 ) {
389
+ $this->getWpRootFiles();
390
+ $this->prepareResponse( false, true );
391
+ return false;
392
+ }
393
+
394
+ if( $this->options->currentStep == 1 ) {
395
+ $this->getWpContentFiles();
396
+ $this->prepareResponse( false, true );
397
+ return false;
398
+ }
399
+
400
+ if( $this->options->currentStep == 2 ) {
401
+ $this->getWpIncludesFiles();
402
+ $this->prepareResponse( false, true );
403
+ return false;
404
+ }
405
+
406
+ if( $this->options->currentStep == 3 ) {
407
+ $this->getWpAdminFiles();
408
+ $this->prepareResponse( false, true );
409
+ return false;
410
+ }
411
+
412
+ if( isset( $this->options->extraDirectories[$this->options->currentStep - $this->total] ) ) {
413
+ $this->getExtraFiles( $this->options->extraDirectories[$this->options->currentStep - $this->total] );
414
+ $this->prepareResponse( false, true );
415
+ return false;
416
+ }
417
+
418
+
419
+ // Prepare response
420
+ $this->prepareResponse( false, true );
421
+ // Not finished
422
+ return true;
423
+ }
424
+
425
+ /**
426
+ * Checks Whether There is Any Job to Execute or Not
427
+ * @return bool
428
+ */
429
+ protected function isFinished() {
430
+ if( $this->options->currentStep >= $this->options->totalSteps ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  return true;
432
+ }
433
+ }
434
+
435
+ /**
436
+ * Save files
437
+ * @return bool
438
+ */
439
+ protected function saveProgress() {
440
+ return $this->saveOptions();
441
+ }
442
+
443
+ /**
444
+ * Get files
445
+ * @return void
446
+ */
447
+ protected function getFiles() {
448
+ $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
449
+
450
+ if( false === ($this->files = @file_get_contents( $fileName )) ) {
451
+ $this->files = array();
452
+ return;
453
+ }
454
+
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
+ $string = str_replace( "/", "\\", $path );
467
+ return str_replace( '\\\\', '\\', $string );
468
+ }
469
+
470
+ /**
471
+ * Check if directory is excluded
472
+ * @param string $directory
473
+ * @return bool
474
+ */
475
+ protected function isDirectoryExcluded( $directory ) {
476
+ $directory = $this->sanitizeDirectorySeparator( $directory );
477
+ foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
478
+ $excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
479
+ if( strpos( trailingslashit( $directory ), trailingslashit( $excludedDirectory ) ) === 0 ) {
480
+ return true;
481
+ }
482
+ }
483
 
484
+ return false;
485
+ }
486
 
487
  }
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -238,6 +238,34 @@ class Files extends JobExecutable {
238
  return true;
239
  }
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  /**
242
  * Set directory permissions
243
  * @param type $file
@@ -247,7 +275,7 @@ class Files extends JobExecutable {
247
  $dir = dirname( $file );
248
  if( is_dir( $dir ) ) {
249
  @chmod( $dir, wpstg_get_permissions_for_directory() );
250
- }
251
  return false;
252
  }
253
 
@@ -259,6 +287,9 @@ class Files extends JobExecutable {
259
  */
260
  private function getDestination( $file ) {
261
 
 
 
 
262
  $relativePath = str_replace( \WPStaging\WPStaging::getWPpath(), null, $file );
263
  $destinationPath = $this->destination . $relativePath;
264
  $destinationDirectory = dirname( $destinationPath );
238
  return true;
239
  }
240
 
241
+ /**
242
+ * Get wp-content and wp-content/uploads destination dir
243
+ * Necessary if these folders were customized and changed from the default ones.
244
+ *
245
+ * @return string
246
+ */
247
+ protected function getWpContentPath( $file ) {
248
+ // Get absolute custom upload dir
249
+ $uploads = wp_upload_dir();
250
+
251
+ // Get absolute upload dir from ABSPATH
252
+ $uploadsAbsPath = trailingslashit($uploads['basedir']);
253
+
254
+ // Get absolute custom wp-content dir
255
+ $wpContentDir = trailingslashit(WP_CONTENT_DIR);
256
+
257
+ // Check if there is a custom upload directory and do a search $ replace
258
+ $file = str_replace( $uploadsAbsPath, ABSPATH . 'wp-content/uploads/', $file, $count );
259
+
260
+ // If there is no custom upload directory do a search & replace of the custom wp-content directory
261
+ if( empty( $count ) || $count === 0 ) {
262
+ $file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
263
+ }
264
+
265
+
266
+ return $file;
267
+ }
268
+
269
  /**
270
  * Set directory permissions
271
  * @param type $file
275
  $dir = dirname( $file );
276
  if( is_dir( $dir ) ) {
277
  @chmod( $dir, wpstg_get_permissions_for_directory() );
278
+ }
279
  return false;
280
  }
281
 
287
  */
288
  private function getDestination( $file ) {
289
 
290
+ // Get custom wp-content and uploads folder
291
+ $file = $this->getWpContentPath( $file );
292
+
293
  $relativePath = str_replace( \WPStaging\WPStaging::getWPpath(), null, $file );
294
  $destinationPath = $this->destination . $relativePath;
295
  $destinationDirectory = dirname( $destinationPath );
apps/Backend/Modules/Jobs/SearchReplace.php CHANGED
@@ -28,7 +28,6 @@ class SearchReplace extends JobExecutable {
28
  */
29
  public $db;
30
 
31
-
32
  /**
33
  *
34
  * @var string
@@ -174,7 +173,7 @@ class SearchReplace extends JobExecutable {
174
  $host = $this->options->homeHostname;
175
 
176
  if( $this->isSubDir() ) {
177
- $host = trailingslashit($this->options->homeHostname) . $this->getSubDir();
178
  }
179
  return $this->strings->getUrlWithoutScheme( $host );
180
  }
@@ -299,17 +298,19 @@ class SearchReplace extends JobExecutable {
299
  $pages = $this->get_pages_in_table( $table );
300
 
301
 
302
- $args['search_for'] = array(
 
303
  '\/\/' . str_replace( '/', '\/', $this->sourceHostname ), // Escaped \/ used by revslider and several visual editors
304
  '//' . $this->sourceHostname,
305
- rtrim( ABSPATH, '/' ),
306
- );
307
 
308
- $args['replace_with'] = array(
 
309
  '\/\/' . str_replace( '/', '\/', $this->destinationHostname ),
310
  '//' . $this->destinationHostname,
311
- rtrim ($this->options->destinationDir, '/'),
312
- );
313
 
314
 
315
  $args['replace_guids'] = 'off';
@@ -387,7 +388,7 @@ class SearchReplace extends JobExecutable {
387
  }
388
 
389
  // Skip mail addresses
390
- if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' .$this->options->homeHostname ) ) {
391
  continue;
392
  }
393
 
@@ -759,4 +760,4 @@ class SearchReplace extends JobExecutable {
759
  return false;
760
  }
761
 
762
- }
28
  */
29
  public $db;
30
 
 
31
  /**
32
  *
33
  * @var string
173
  $host = $this->options->homeHostname;
174
 
175
  if( $this->isSubDir() ) {
176
+ $host = trailingslashit( $this->options->homeHostname ) . $this->getSubDir();
177
  }
178
  return $this->strings->getUrlWithoutScheme( $host );
179
  }
298
  $pages = $this->get_pages_in_table( $table );
299
 
300
 
301
+ $args['search_for'] = array(
302
+ '%2F%2F' . str_replace( '/', '%2F', $this->sourceHostname ), // HTML entitity for WP Backery Page Builder Plugin
303
  '\/\/' . str_replace( '/', '\/', $this->sourceHostname ), // Escaped \/ used by revslider and several visual editors
304
  '//' . $this->sourceHostname,
305
+ rtrim( ABSPATH, '/' ),
306
+ );
307
 
308
+ $args['replace_with'] = array(
309
+ '%2F%2F' . str_replace( '/', '%2F', $this->destinationHostname ),
310
  '\/\/' . str_replace( '/', '\/', $this->destinationHostname ),
311
  '//' . $this->destinationHostname,
312
+ rtrim( $this->options->destinationDir, '/' ),
313
+ );
314
 
315
 
316
  $args['replace_guids'] = 'off';
388
  }
389
 
390
  // Skip mail addresses
391
+ if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->options->homeHostname ) ) {
392
  continue;
393
  }
394
 
760
  return false;
761
  }
762
 
763
+ }
apps/Backend/Modules/Jobs/Updating.php CHANGED
@@ -61,7 +61,9 @@ class Updating extends Job {
61
  '.gitignore',
62
  '.log',
63
  'db.php',
64
- 'object-cache.php'
 
 
65
  );
66
 
67
  $this->options->excludedFilesFullPath = array(
61
  '.gitignore',
62
  '.log',
63
  'db.php',
64
+ 'object-cache.php',
65
+ 'web.config' // Important: Windows IIS configuartion file. Must not be in the staging site!
66
+
67
  );
68
 
69
  $this->options->excludedFilesFullPath = array(
apps/Backend/Notices/Notices.php CHANGED
@@ -31,7 +31,7 @@ class Notices {
31
 
32
  public function __construct( $path, $url ) {
33
  $this->path = $path;
34
- $this->url = $url;
35
  }
36
 
37
  /**
@@ -64,87 +64,106 @@ class Notices {
64
  return false;
65
  }
66
 
67
- $installDate = new \DateTime( get_option( "wpstg_installDate" ) );
 
68
  $now = new \DateTime( "now" );
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  // Get days difference
71
  $difference = $now->diff( $installDate )->days;
72
 
73
- return ($days <= $difference && "no" !== get_option( $option ));
 
 
 
74
 
75
  return false;
76
  }
77
 
78
  public function messages() {
79
 
80
- $viewsNoticesPath = "{$this->path}views/_includes/messages/";
81
-
82
- $this->plugin_deactivated_notice();
83
-
84
-
85
- // Show rating review message on all admin pages
86
- if( $this->canShow( "wpstg_rating", 7 ) ) {
87
- require_once "{$viewsNoticesPath}rating.php";
88
- }
89
-
90
-
91
- // Display messages below to admins only, only on admin panel
92
- if( !current_user_can( "update_plugins" ) || !$this->isAdminPage() ) {
93
- return;
94
- }
95
-
96
-
97
- $varsDirectory = \WPStaging\WPStaging::getContentDir();
98
- if( !wp_is_writable( $varsDirectory ) ) {
99
- require_once "{$viewsNoticesPath}/uploads-cache-directory-permission-problem.php";
100
- }
101
- // Staging directory is not writable
102
- if( !wp_is_writable( ABSPATH ) ) {
103
- require_once "{$viewsNoticesPath}/staging-directory-permission-problem.php";
104
- }
105
-
106
- // Version Control
107
- if( version_compare( WPStaging::WP_COMPATIBLE, get_bloginfo( "version" ), "<" ) ) {
108
- require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
109
- }
110
-
111
- // Beta
112
- if( false === get_option( "wpstg_beta" ) || "no" !== get_option( "wpstg_beta" ) ) {
113
- require_once "{$viewsNoticesPath}beta.php";
114
- }
115
-
116
- // WP Staging Pro and Free can not be activated both
117
- if( false !== ( $deactivatedNoticeID = get_transient( "wp_staging_deactivated_notice_id" ) ) ) {
118
- require_once "{$viewsNoticesPath}transient.php";
119
- delete_transient( "wp_staging_deactivated_notice_id" );
120
- }
121
- // Rating
122
- if( $this->canShow( "wpstg_rating", 7 ) ) {
123
- require_once "{$viewsNoticesPath}rating.php";
124
- }
125
-
126
- // Different scheme in home and siteurl
127
- if ($this->isDifferentScheme()){
128
- require_once "{$viewsNoticesPath}wrong-scheme.php";
129
- }
130
- }
131
-
132
- /**
133
- * Check if the url scheme of siteurl and home is identical
134
- * @return boolean
135
- */
136
- private function isDifferentScheme(){
137
- $siteurlScheme = parse_url(get_option('siteurl'), PHP_URL_SCHEME);
138
- $homeScheme = parse_url(get_option('home'), PHP_URL_SCHEME);
139
-
140
- if ($siteurlScheme === $homeScheme){
141
- return false;
142
- }
143
- return true;
144
-
145
- }
146
-
147
- /**
148
  * Show a message when pro or free plugin becomes deactivated
149
  *
150
  * @return void
@@ -164,4 +183,4 @@ class Notices {
164
  }
165
  }
166
 
167
- }
31
 
32
  public function __construct( $path, $url ) {
33
  $this->path = $path;
34
+ $this->url = $url;
35
  }
36
 
37
  /**
64
  return false;
65
  }
66
 
67
+ $dbOption = get_option( $option );
68
+
69
  $now = new \DateTime( "now" );
70
 
71
+
72
+ // Check if user clicked on "rate later" button
73
+ if( "no" !== $dbOption && wpstg_validate_date( $dbOption ) ) {
74
+ // Get days difference
75
+ $hideDate = new \DateTime( $dbOption );
76
+ $difference = $now->diff( $hideDate )->days;
77
+
78
+ if( $days <= $difference )
79
+ return true;
80
+ }
81
+
82
+
83
+ // Show X days after installation
84
+ $installDate = new \DateTime( get_option( "wpstg_installDate" ) );
85
+
86
  // Get days difference
87
  $difference = $now->diff( $installDate )->days;
88
 
89
+ if( $days <= $difference && "no" !== $dbOption ) {
90
+ return true;
91
+ }
92
+
93
 
94
  return false;
95
  }
96
 
97
  public function messages() {
98
 
99
+ $viewsNoticesPath = "{$this->path}views/_includes/messages/";
100
+
101
+ // Show notice when free and pro version have been activated at the same time
102
+ $this->plugin_deactivated_notice();
103
+
104
+
105
+ // Show rating review message on all admin pages
106
+ if( $this->canShow( "wpstg_rating", 7 ) ) {
107
+ require_once "{$viewsNoticesPath}rating.php";
108
+ }
109
+
110
+
111
+ // Display messages below to admins only, only on admin panel
112
+ if( !current_user_can( "update_plugins" ) || !$this->isAdminPage() ) {
113
+ return;
114
+ }
115
+
116
+
117
+ $varsDirectory = \WPStaging\WPStaging::getContentDir();
118
+ if( !wp_is_writable( $varsDirectory ) ) {
119
+ require_once "{$viewsNoticesPath}/uploads-cache-directory-permission-problem.php";
120
+ }
121
+ // Staging directory is not writable
122
+ if( !wp_is_writable( ABSPATH ) ) {
123
+ require_once "{$viewsNoticesPath}/staging-directory-permission-problem.php";
124
+ }
125
+
126
+ // Version Control
127
+ if( version_compare( WPStaging::WP_COMPATIBLE, get_bloginfo( "version" ), "<" ) ) {
128
+ require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
129
+ }
130
+
131
+ // Beta
132
+ if( false === get_option( "wpstg_beta" ) || "no" !== get_option( "wpstg_beta" ) ) {
133
+ require_once "{$viewsNoticesPath}beta.php";
134
+ }
135
+
136
+ // WP Staging Pro and Free can not be activated both
137
+ if( false !== ( $deactivatedNoticeID = get_transient( "wp_staging_deactivated_notice_id" ) ) ) {
138
+ require_once "{$viewsNoticesPath}transient.php";
139
+ delete_transient( "wp_staging_deactivated_notice_id" );
140
+ }
141
+ // Rating
142
+ if( $this->canShow( "wpstg_rating", 7 ) ) {
143
+ require_once "{$viewsNoticesPath}rating.php";
144
+ }
145
+
146
+ // Different scheme in home and siteurl
147
+ if( $this->isDifferentScheme() ) {
148
+ require_once "{$viewsNoticesPath}wrong-scheme.php";
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Check if the url scheme of siteurl and home is identical
154
+ * @return boolean
155
+ */
156
+ private function isDifferentScheme() {
157
+ $siteurlScheme = parse_url( get_option( 'siteurl' ), PHP_URL_SCHEME );
158
+ $homeScheme = parse_url( get_option( 'home' ), PHP_URL_SCHEME );
159
+
160
+ if( $siteurlScheme === $homeScheme ) {
161
+ return false;
162
+ }
163
+ return true;
164
+ }
165
+
166
+ /**
167
  * Show a message when pro or free plugin becomes deactivated
168
  *
169
  * @return void
183
  }
184
  }
185
 
186
+ }
apps/Backend/public/js/wpstg-admin-rating.js CHANGED
@@ -29,4 +29,39 @@ jQuery(document).ready(function ($) {
29
  }
30
  });
31
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  });
29
  }
30
  });
31
  });
32
+
33
+ $(".wpstg_rate_later").click(function (e) {
34
+ e.preventDefault();
35
+
36
+ $.ajax({
37
+ url: ajaxurl,
38
+ type: "POST",
39
+ data: {action: "wpstg_hide_later"},
40
+ error: function (xhr, textStatus, errorThrown) {
41
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
42
+ console.log(textStatus);
43
+
44
+
45
+ alert(
46
+ "Unknown error"
47
+ );
48
+ },
49
+ success: function (data) {
50
+ $(".wpstg_fivestar").slideUp("fast");
51
+ return true;
52
+ },
53
+ statusCode: {
54
+ 404: function () {
55
+ alert("Something went wrong; can't find ajax request URL!");
56
+ },
57
+ 500: function () {
58
+ alert("Something went wrong; internal server error while processing the request!");
59
+ }
60
+ }
61
+ });
62
+ });
63
+
64
+
65
+
66
+
67
  });
apps/Backend/views/_includes/messages/rating.php CHANGED
@@ -27,6 +27,11 @@
27
  <?php _e('No, not good enough','wp-staging')?>
28
  </a>
29
  </li>
 
 
 
 
 
30
  </ul>
31
  </div>
32
  <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-rating.js"?>"></script>
27
  <?php _e('No, not good enough','wp-staging')?>
28
  </a>
29
  </li>
30
+ <li>
31
+ <a href="javascript:void(0);" class="wpstg_rate_later" title="Ask me again in a week" style="font-weight:normal;color:white;">
32
+ <?php _e('I want to rate later - Ask me again in a week','wp-staging')?>
33
+ </a>
34
+ </li>
35
  </ul>
36
  </div>
37
  <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-rating.js"?>"></script>
apps/Core/Iterators/RecursiveFilterExclude.php CHANGED
@@ -4,20 +4,35 @@ namespace WPStaging\Iterators;
4
 
5
  class RecursiveFilterExclude extends \RecursiveFilterIterator {
6
 
7
- protected $exclude = array();
8
 
9
- public function __construct( \RecursiveIterator $iterator, $exclude = array() ) {
10
- parent::__construct( $iterator );
11
 
12
- // Set exclude filter
13
- $this->exclude = $exclude;
14
- }
15
 
16
- public function accept() {
17
- return ! in_array( $this->getInnerIterator()->getSubPathname(), $this->exclude );
18
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- public function getChildren() {
21
- return new self( $this->getInnerIterator()->getChildren(), $this->exclude );
22
- }
23
  }
4
 
5
  class RecursiveFilterExclude extends \RecursiveFilterIterator {
6
 
7
+ protected $exclude = array();
8
 
9
+ public function __construct( \RecursiveIterator $iterator, $exclude = array() ) {
10
+ parent::__construct( $iterator );
11
 
12
+ // Set exclude filter
13
+ $this->exclude = $exclude;
14
+ }
15
 
16
+ public function accept() {
17
+
18
+ // Path contains new line character on linux
19
+ if(strpos( $this->getInnerIterator()->getSubPathname(), "\n" ) !== false)
20
+ return false;
21
+
22
+ // Path contains new line character on Windows
23
+ if(strpos( $this->getInnerIterator()->getSubPathname(), "\r" ) !== false)
24
+ return false;
25
+
26
+ // Part of the path is excluded
27
+ if (in_array( $this->getInnerIterator()->getSubPathname(), $this->exclude ))
28
+ return false;
29
+
30
+ return true;
31
+
32
+ }
33
+
34
+ public function getChildren() {
35
+ return new self( $this->getInnerIterator()->getChildren(), $this->exclude );
36
+ }
37
 
 
 
 
38
  }
apps/Core/Iterators/RecursiveFilterNewLine.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- namespace WPStaging\Iterators;
4
-
5
- // No Direct Access
6
- if (!defined("WPINC")) {
7
- die;
8
- }
9
-
10
- /**
11
- * Filter files which contain new line character in name
12
- */
13
-
14
- class RecursiveFilterNewLine extends \RecursiveFilterIterator {
15
-
16
- public function accept() {
17
- return strpos( $this->getInnerIterator()->getSubPathname(), "\n" ) === false &&
18
- strpos( $this->getInnerIterator()->getSubPathname(), "\r" ) === false;
19
- }
20
- }
21
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apps/Core/Utils/functions.php CHANGED
@@ -73,3 +73,63 @@ function wpstg_escape_windows_directory_separator( $path ) {
73
  function wpstg_replace_windows_directory_separator( $path ) {
74
  return preg_replace( '/[\\\\]+/', '/', $path );
75
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  function wpstg_replace_windows_directory_separator( $path ) {
74
  return preg_replace( '/[\\\\]+/', '/', $path );
75
  }
76
+
77
+ /**
78
+ * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
79
+ * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
80
+ * @access public
81
+ * @param string $input The string to escape.
82
+ * @return string
83
+ */
84
+ function wpstg_mysql_escape_mimic( $input ) {
85
+ if( is_array( $input ) ) {
86
+ return array_map( __METHOD__, $input );
87
+ }
88
+ if( !empty( $input ) && is_string( $input ) ) {
89
+ return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
90
+ }
91
+
92
+ return $input;
93
+ }
94
+
95
+ /**
96
+ * Search & Replace first occurence of string in haystack
97
+ * @param type $haystack
98
+ * @param type $needle
99
+ * @return type
100
+ */
101
+ function wpstg_replace_first_match( $needle, $replace, $haystack ) {
102
+ $result = $haystack;
103
+ $pos = strpos( $haystack, $needle );
104
+ if( $pos !== false ) {
105
+ $result = substr_replace( $haystack, $replace, $pos, strlen( $needle ) );
106
+ }
107
+ return $result;
108
+ }
109
+
110
+ /**
111
+ * Search & Replace last occurence of string in haystack
112
+ * @param type $haystack
113
+ * @param type $needle
114
+ * @return type
115
+ */
116
+ function wpstg_replace_last_match( $needle, $replace, $haystack ) {
117
+ $result = $haystack;
118
+ $pos = strrpos( $haystack, $needle );
119
+ if( $pos !== false ) {
120
+ $result = substr_replace( $haystack, $replace, $pos, strlen( $needle ) );
121
+ }
122
+ return $result;
123
+ }
124
+
125
+ /**
126
+ * Check if string is valid date
127
+ * @param type $date
128
+ * @param type $format
129
+ * @return bool
130
+ */
131
+ function wpstg_validate_date( $date, $format = 'Y-m-d' ) {
132
+ $d = DateTime::createFromFormat( $format, $date );
133
+ // The Y ( 4 digits year ) returns TRUE for any integer with any number of digits so changing the comparison from == to === fixes the issue.
134
+ return $d && $d->format( $format ) === $date;
135
+ }
apps/Core/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
  /**
30
  * Plugin version
31
  */
32
- const VERSION = "2.4.1";
33
 
34
  /**
35
  * Plugin name
29
  /**
30
  * Plugin version
31
  */
32
+ const VERSION = "2.4.3";
33
 
34
  /**
35
  * Plugin name
apps/Frontend/loginForm.php CHANGED
@@ -49,7 +49,14 @@ class loginForm {
49
  wp_set_auth_cookie( $user_data->ID, $rememberme );
50
  wp_set_current_user( $user_data->ID, $_POST['wpstg-username'] );
51
  do_action( 'wp_login', $_POST['wpstg-username'], get_userdata( $user_data->ID ) );
52
- header( 'Location:' . get_site_url() . '/wp-admin/' );
 
 
 
 
 
 
 
53
  } else {
54
  $this->error = 'Username or password wrong!';
55
  }
49
  wp_set_auth_cookie( $user_data->ID, $rememberme );
50
  wp_set_current_user( $user_data->ID, $_POST['wpstg-username'] );
51
  do_action( 'wp_login', $_POST['wpstg-username'], get_userdata( $user_data->ID ) );
52
+
53
+ $redirect_to = get_site_url() . '/wp-admin/';
54
+
55
+ if( !empty( $_POST['redirect_to'] ) ) {
56
+ $redirectTo = $_POST['redirect_to'];
57
+ }
58
+
59
+ header( 'Location:' . $redirectTo );
60
  } else {
61
  $this->error = 'Username or password wrong!';
62
  }
readme.txt CHANGED
@@ -1,4 +1,4 @@
1
- === WP Staging - DB & File Duplicator & Migration ===
2
 
3
  Author URL: https://wordpress.org/plugins/wp-staging
4
  Plugin URL: https://wordpress.org/plugins/wp-staging
@@ -8,8 +8,8 @@ License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
  Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
  Requires at least: 3.6+
11
- Tested up to: 4.9
12
- Stable tag: 2.4.1
13
  Requires PHP: 5.3
14
 
15
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
@@ -81,7 +81,7 @@ I totally understand this and i am guilty as well here, but unfortunately this l
81
 
82
  <strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
83
 
84
- <h3> Can´t i just use my local wordpress development copy for testing like xampp / lampp? </h3>
85
 
86
  Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect
87
  of your local copy is working on your live website exactely as you would expect it.
@@ -146,6 +146,16 @@ https://wp-staging.com
146
 
147
  == Changelog ==
148
 
 
 
 
 
 
 
 
 
 
 
149
  = 2.4.1 =
150
  * Fix: Error notice if user tries to update staging site located in another database
151
  * Fix: Search & Replace adds staging site subfolder multiple times to replaced string when staging site is updated
@@ -168,9 +178,6 @@ https://wp-staging.com
168
  * Fix: Selected folders are not excluded under Windows IIS server
169
  * Fix: Windows IIS server compatibilility issues resolved
170
 
171
-
172
-
173
-
174
  = 2.3.9 =
175
  * New: Support for wp-config.php located in one level up of the root folder for multisites
176
  * New: Allow exclusion of custom options from wp_options from beeing pushed
@@ -181,50 +188,12 @@ https://wp-staging.com
181
  * Fix: Filesize() failed if file to copy is not a regular file
182
  * Fix: remove ? parameter from staging site
183
 
184
- = 2.3.8 =
185
- * Tweak: Lowering default performance settings for more reliability during cloning
186
- * New: Set WP_CACHE to false in wp-config.php after cloning to prevent log in issues to staging site
187
- * New: Compatibility mode to skip certain tables from third party plugins from beeing searched & replaced
188
- * Fix: Do not clone db.php, object-cache.php and advanced-cache.php
189
- * Fix: Show error message if ajax requests fail for any reason
190
-
191
-
192
- = 2.3.7 =
193
- * New: Increase cloning performance
194
- * New: Show admin notice if php version and wordpress version do not meet requirements.
195
- * New: Show error if there is not enough free diskspace
196
- * New: Add resume function in case the cloning process interrupts
197
- * New: Add italian translation
198
- * New: Better looking admin notices
199
- * Fix: Different scheme of siteurl and home leads to non available staging site. Show admin notice to ask user to fix that first before creating a staging site
200
- * Fix: WP Staging welcome screen shown when any plugin is activated
201
- * Fix: Lower default settings to 10000, cpu load low. File copy limit to 50
202
-
203
-
204
- = 2.3.6 =
205
- * Fix: Add version number to css and js files to prevent caching issues
206
- * Fix: Wrong text domain in a few language strings
207
- * Fix: Optimizer mu-plugin not installed all the time
208
- * Fix: Language files not loaded correctly
209
- * Fix: Remove heartbeat check only on wp staging admin pages
210
- * Fix: WordPress custom upload path variable upload_path could be wrong after cloning
211
- * Fix: Increase maximum memory consumption
212
-
213
- = 2.3.5 =
214
- * New: Enable Optimizer as default option
215
- * New: Add filter to exclude strings from search & replace, docs: https://wp-staging.com/docs/actions-and-filters/
216
- * New: Add filter to change search & replace parameters
217
- * New: Add language files and change text domain to slug of the plugin
218
- * New: Disable heartbeat api and user login check for wp staging processing
219
- * New: Add issue reporting form
220
- * New: Check if clone subfolder already exists before creating clone
221
- * Fix: Changing file copy limit not working
222
-
223
 
224
  Complete changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
225
 
226
  == Upgrade Notice ==
227
 
228
- = 2.4.0 =
229
- * New: Compatible to WordPress 5.0.0. Important fixes!
 
230
 
1
+ === WP Staging - DB & File Duplicator & Migration ===
2
 
3
  Author URL: https://wordpress.org/plugins/wp-staging
4
  Plugin URL: https://wordpress.org/plugins/wp-staging
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
  Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
  Requires at least: 3.6+
11
+ Tested up to: 5.0
12
+ Stable tag: 2.4.3
13
  Requires PHP: 5.3
14
 
15
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
81
 
82
  <strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
83
 
84
+ <h3> Can´t I just use my local wordpress development copy for testing like xampp / lampp? </h3>
85
 
86
  Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect
87
  of your local copy is working on your live website exactely as you would expect it.
146
 
147
  == Changelog ==
148
 
149
+ = 2.4.3 =
150
+ * Fix: Updating staging site does not exclude Windows IIS configuration file web.config and can lead to server error
151
+ * Fix: Redirect to the correct url after log in to staging site
152
+ * Fix: Supports HTML entities in links created by WP Backery Page Builder Plugin
153
+ * New: Support for custom and non-default wp-content and uploads folder
154
+ * New: Option to show again the rating notice in a week
155
+
156
+ = 2.4.2 =
157
+ * Fix: Error 500 on some systems when files are collected and plugin iterates through the directories
158
+
159
  = 2.4.1 =
160
  * Fix: Error notice if user tries to update staging site located in another database
161
  * Fix: Search & Replace adds staging site subfolder multiple times to replaced string when staging site is updated
178
  * Fix: Selected folders are not excluded under Windows IIS server
179
  * Fix: Windows IIS server compatibilility issues resolved
180
 
 
 
 
181
  = 2.3.9 =
182
  * New: Support for wp-config.php located in one level up of the root folder for multisites
183
  * New: Allow exclusion of custom options from wp_options from beeing pushed
188
  * Fix: Filesize() failed if file to copy is not a regular file
189
  * Fix: remove ? parameter from staging site
190
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
  Complete changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
193
 
194
  == Upgrade Notice ==
195
 
196
+ = 2.4.3 =
197
+ * New: Support for custom directories of wp-content and uploads folder
198
+
199
 
wp-staging.php CHANGED
@@ -7,7 +7,7 @@
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
- * Version: 2.4.1
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
 
@@ -51,7 +51,7 @@ if( !defined( 'WPSTG_PLUGIN_URL' ) ) {
51
 
52
  // Version
53
  if( !defined( 'WPSTG_VERSION' ) ) {
54
- define( 'WPSTG_VERSION', '2.4.1' );
55
  }
56
 
57
  /**
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
+ * Version: 2.4.3
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
 
51
 
52
  // Version
53
  if( !defined( 'WPSTG_VERSION' ) ) {
54
+ define( 'WPSTG_VERSION', '2.4.3' );
55
  }
56
 
57
  /**