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

Version Description

  • Fix: Old staging site is not listed and pushing is not working properly if plugin is updated from wp staging version 1.6 and lower
Download this release

Release Info

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

Code changes from version 2.1.8 to 2.2.0

Files changed (39) hide show
  1. apps/Backend/Administrator.php +695 -695
  2. apps/Backend/Modules/Jobs/Cancel.php +74 -74
  3. apps/Backend/Modules/Jobs/CancelUpdate.php +90 -90
  4. apps/Backend/Modules/Jobs/Cloning.php +249 -249
  5. apps/Backend/Modules/Jobs/Data.php +459 -408
  6. apps/Backend/Modules/Jobs/Database.php +264 -262
  7. apps/Backend/Modules/Jobs/Delete.php +497 -497
  8. apps/Backend/Modules/Jobs/Directories.php +428 -451
  9. apps/Backend/Modules/Jobs/Files.php +366 -365
  10. apps/Backend/Modules/Jobs/Finish.php +120 -261
  11. apps/Backend/Modules/Jobs/Job.php +487 -484
  12. apps/Backend/Modules/Jobs/JobExecutable.php +111 -111
  13. apps/Backend/Modules/Jobs/Scan.php +406 -401
  14. apps/Backend/Modules/Jobs/Updating.php +248 -261
  15. apps/Backend/Modules/SystemInfo.php +512 -512
  16. apps/Backend/Modules/Views/Forms/Settings.php +219 -219
  17. apps/Backend/Notices/Notices.php +154 -154
  18. apps/Backend/Upgrade/Upgrade.php +316 -229
  19. apps/Backend/public/css/wpstg-admin.css +783 -783
  20. apps/Backend/public/js/wpstg-admin-rating.js +31 -31
  21. apps/Backend/public/js/wpstg-admin.js +1177 -1260
  22. apps/Backend/views/_includes/messages/beta.php +19 -19
  23. apps/Backend/views/_includes/messages/rating.php +30 -30
  24. apps/Backend/views/_includes/messages/wp-version-compatible-message.php +12 -12
  25. apps/Backend/views/clone/ajax/scan.php +128 -128
  26. apps/Backend/views/clone/ajax/single-overview.php +55 -55
  27. apps/Backend/views/clone/ajax/start.php +112 -112
  28. apps/Backend/views/clone/ajax/update.php +45 -45
  29. apps/Backend/views/settings/index.php +1 -1
  30. apps/Core/Iterators/RecursiveDirectoryIterator.php +52 -52
  31. apps/Core/Iterators/RecursiveFilterExclude.php +23 -23
  32. apps/Core/Iterators/RecursiveFilterNewLine.php +21 -21
  33. apps/Core/Utils/Cache.php +211 -211
  34. apps/Core/Utils/Strings.php +27 -0
  35. apps/Core/WPStaging.php +429 -429
  36. package-lock.json +0 -3333
  37. readme.txt +317 -301
  38. uninstall.php +89 -89
  39. wp-staging.php +91 -91
apps/Backend/Administrator.php CHANGED
@@ -1,695 +1,695 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend;
4
-
5
- // No Direct Access
6
- if( !defined( "WPINC" ) ) {
7
- die;
8
- }
9
-
10
- use WPStaging\Backend\Modules\Jobs\Cancel;
11
- use WPStaging\Backend\Modules\Jobs\CancelUpdate;
12
- use WPStaging\Backend\Modules\Jobs\Cloning;
13
- use WPStaging\Backend\Modules\Jobs\Updating;
14
- use WPStaging\Backend\Modules\Jobs\Data;
15
- use WPStaging\Backend\Modules\Jobs\Database;
16
- use WPStaging\Backend\Modules\Jobs\Delete;
17
- use WPStaging\Backend\Modules\Jobs\Files;
18
- use WPStaging\Backend\Modules\Jobs\Scan;
19
- use WPStaging\Backend\Modules\Jobs\Logs;
20
- use WPStaging\Backend\Modules\Optimizer;
21
- use WPStaging\Backend\Modules\SystemInfo;
22
- use WPStaging\Backend\Modules\Views\Tabs\Tabs;
23
- use WPStaging\Backend\Notices\Notices;
24
- use WPStaging\DI\InjectionAware;
25
- use WPStaging\Backend\Modules\Views\Forms\Settings as FormSettings;
26
- use WPStaging\Backend\Activation;
27
- use WPStaging\WPStaging;
28
- use WPStaging\Backend\Pro\Modules\Jobs\Processing;
29
- use WPStaging\Backend\Pro\Licensing;
30
-
31
- /**
32
- * Class Administrator
33
- * @package WPStaging\Backend
34
- */
35
- class Administrator extends InjectionAware {
36
-
37
- /**
38
- * @var string
39
- */
40
- private $path;
41
-
42
- /**
43
- * @var string
44
- */
45
- private $url;
46
-
47
- /**
48
- * Initialize class
49
- */
50
- public function initialize() {
51
- $this->defineHooks();
52
-
53
- // Path to backend
54
- $this->path = plugin_dir_path( __FILE__ );
55
-
56
- // URL to public backend folder
57
- $this->url = plugin_dir_url( __FILE__ ) . "public/";
58
-
59
- // Load plugins meta data
60
- $this->loadMeta();
61
- }
62
-
63
- /**
64
- * Load plugn meta data
65
- */
66
- public function loadMeta() {
67
- $run = new \WPStaging\Backend\Pluginmeta\Pluginmeta();
68
- }
69
-
70
- /**
71
- * Define Hooks
72
- */
73
- private function defineHooks() {
74
- // Get loader
75
- $loader = $this->di->get( "loader" );
76
-
77
- $Activation = new \WPStaging\Backend\Activation\Activation();
78
-
79
- $Welcome = new Activation\Welcome();
80
-
81
- $loader->addAction( "activated_plugin", $Activation, 'deactivate_other_instances' );
82
- $loader->addAction( "admin_menu", $this, "addMenu", 10 );
83
- $loader->addAction( "admin_init", $this, "setOptionFormElements" );
84
- $loader->addAction( "admin_init", $this, "upgrade" );
85
- $loader->addAction( "admin_post_wpstg_download_sysinfo", $this, "systemInfoDownload" );
86
- $loader->addAction( "admin_post_wpstg_export", $this, "export" );
87
- $loader->addAction( "admin_post_wpstg_import_settings", $this, "import" );
88
- $loader->addAction( "admin_notices", $this, "messages" );
89
-
90
- // Ajax Requests
91
- $loader->addAction( "wp_ajax_wpstg_overview", $this, "ajaxOverview" );
92
- $loader->addAction( "wp_ajax_wpstg_scanning", $this, "ajaxScan" );
93
- $loader->addAction( "wp_ajax_wpstg_check_clone", $this, "ajaxcheckCloneName" );
94
- //$loader->addAction( "wp_ajax_wpstg_update_struc", $this, "ajaxStartUpdate" );
95
- $loader->addAction( "wp_ajax_wpstg_update", $this, "ajaxUpdateProcess" );
96
- $loader->addAction( "wp_ajax_wpstg_cloning", $this, "ajaxStartClone" );
97
- $loader->addAction( "wp_ajax_wpstg_clone_database", $this, "ajaxCloneDatabase" );
98
- $loader->addAction( "wp_ajax_wpstg_clone_prepare_directories", $this, "ajaxPrepareDirectories" );
99
- $loader->addAction( "wp_ajax_wpstg_clone_files", $this, "ajaxCopyFiles" );
100
- $loader->addAction( "wp_ajax_wpstg_clone_replace_data", $this, "ajaxReplaceData" );
101
- $loader->addAction( "wp_ajax_wpstg_clone_finish", $this, "ajaxFinish" );
102
- $loader->addAction( "wp_ajax_wpstg_confirm_delete_clone", $this, "ajaxDeleteConfirmation" );
103
- $loader->addAction( "wp_ajax_wpstg_delete_clone", $this, "ajaxDeleteClone" );
104
- $loader->addAction( "wp_ajax_wpstg_cancel_clone", $this, "ajaxCancelClone" );
105
- $loader->addAction( "wp_ajax_wpstg_cancel_update", $this, "ajaxCancelUpdate" );
106
- $loader->addAction( "wp_ajax_wpstg_hide_poll", $this, "ajaxHidePoll" );
107
- $loader->addAction( "wp_ajax_wpstg_hide_rating", $this, "ajaxHideRating" );
108
- $loader->addAction( "wp_ajax_wpstg_hide_beta", $this, "ajaxHideBeta" );
109
- $loader->addAction( "wp_ajax_wpstg_logs", $this, "ajaxLogs" );
110
- $loader->addAction( "wp_ajax_wpstg_check_disk_space", $this, "ajaxCheckFreeSpace" );
111
-
112
- // Ajax hooks pro Version
113
- $loader->addAction( "wp_ajax_wpstg_scan", $this, "ajaxPushScan" );
114
- $loader->addAction( "wp_ajax_wpstg_push_processing", $this, "ajaxPushProcessing" );
115
- //$loader->addAction( "wp_ajax_wpstg_copy_database", $this, "ajaxCopyDatabase" );
116
- }
117
-
118
- /**
119
- * Register options form elements
120
- */
121
- public function setOptionFormElements() {
122
- register_setting( "wpstg_settings", "wpstg_settings", array($this, "sanitizeOptions") );
123
- }
124
-
125
- /**
126
- * Upgrade routine
127
- */
128
- public function upgrade() {
129
- $upgrade = new Upgrade\Upgrade();
130
- $upgrade->doUpgrade();
131
- }
132
-
133
- /**
134
- * Sanitize options data and delete the cache
135
- * @param array $data
136
- * @return array
137
- */
138
- public function sanitizeOptions( $data = array() ) {
139
- $sanitized = $this->sanitizeData( $data );
140
-
141
- add_settings_error( "wpstg-notices", '', __( "Settings updated.", "wpstg" ), "updated" );
142
-
143
- // Return sanitized data
144
- //return $sanitized;
145
- return apply_filters( "wpstg-settings", $sanitized, $data );
146
- }
147
-
148
- /**
149
- * @param array $data
150
- * @return array
151
- */
152
- private function sanitizeData( $data = array() ) {
153
- $sanitized = array();
154
-
155
- foreach ( $data as $key => $value ) {
156
- $sanitized[$key] = (is_array( $value )) ? $this->sanitizeData( $value ) : htmlspecialchars( $value );
157
- }
158
-
159
- return $sanitized;
160
- }
161
-
162
- /**
163
- * Add Admin Menu(s)
164
- */
165
- public function addMenu() {
166
-
167
- $logo = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiIGZpbGw9Im5vbmUiPgo8Zz48Zz48cGF0aCBzdHlsZT0iZmlsbDojZmZmIiAgZD0iTTEzNy42LDU2MS4zSDEzLjhIMTB2MzA2LjNsOTAuNy04My40QzE4OS42LDkwOC43LDMzNS4zLDk5MCw1MDAsOTkwYzI0OS45LDAsNDU2LjEtMTg3LjEsNDg2LjItNDI4LjhIODYyLjRDODMzLjMsNzM1LjEsNjgyLjEsODY3LjUsNTAwLDg2Ny41Yy0xMjksMC0yNDIuNS02Ni41LTMwOC4xLTE2Ny4ybDE1MS4zLTEzOS4xSDEzNy42eiIvPjxwYXRoIHN0eWxlPSJmaWxsOiNmZmYiICBkPSJNNTAwLDEwQzI1MC4xLDEwLDQzLjksMTk3LjEsMTMuOCw0MzguOGgxMjMuOEMxNjYuNywyNjQuOSwzMTcuOSwxMzIuNSw1MDAsMTMyLjVjMTMyLjksMCwyNDkuMyw3MC41LDMxMy44LDE3Ni4yTDY4My44LDQzOC44aDEyMi41aDU2LjJoMTIzLjhoMy44VjEzMi41bC04Ny43LDg3LjdDODEzLjgsOTMuMSw2NjYuNiwxMCw1MDAsMTB6Ii8+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvZz4KPC9zdmc+';
168
-
169
- // Main WP Staging Menu
170
- add_menu_page(
171
- "WP-Staging", __( "WP Staging", "wpstg" ), "manage_options", "wpstg_clone", array($this, "getClonePage"), $logo
172
- );
173
-
174
- // Page: Clone
175
- add_submenu_page(
176
- "wpstg_clone", __( "WP Staging Jobs", "wpstg" ), __( "Start", "wpstg" ), "manage_options", "wpstg_clone", array($this, "getClonePage")
177
- );
178
-
179
- // Page: Settings
180
- add_submenu_page(
181
- "wpstg_clone", __( "WP Staging Settings", "wpstg" ), __( "Settings", "wpstg" ), "manage_options", "wpstg-settings", array($this, "getSettingsPage")
182
- );
183
-
184
- // Page: Tools
185
- add_submenu_page(
186
- "wpstg_clone", __( "WP Staging Tools", "wpstg" ), __( "Tools", "wpstg" ), "manage_options", "wpstg-tools", array($this, "getToolsPage")
187
- );
188
- // Page: Tools
189
- add_submenu_page(
190
- "wpstg_clone", __( "WP Staging Welcome", "wpstg" ), __( "Get WP Staging Pro", "wpstg" ), "manage_options", "wpstg-welcome", array($this, "getWelcomePage")
191
- );
192
-
193
- if( class_exists( 'WPStaging\Backend\Pro\Licensing\Licensing' ) ) {
194
- // Page: License
195
- add_submenu_page(
196
- "wpstg_clone", __( "WP Staging License", "wpstg" ), __( "License", "wpstg" ), "manage_options", "wpstg-license", array($this, "getLicensePage")
197
- );
198
- }
199
- }
200
-
201
- /**
202
- * Settings Page
203
- */
204
- public function getSettingsPage() {
205
- // Tabs
206
- $tabs = new Tabs( array(
207
- "general" => __( "General", "wpstg" )
208
- ) );
209
-
210
-
211
- $this->di
212
- // Set tabs
213
- ->set( "tabs", $tabs )
214
- // Forms
215
- ->set( "forms", new FormSettings( $tabs ) );
216
-
217
- require_once "{$this->path}views/settings/index.php";
218
- }
219
-
220
- /**
221
- * Clone Page
222
- */
223
- public function getClonePage() {
224
- // Existing clones
225
- $availableClones = get_option( "wpstg_existing_clones_beta", array() );
226
-
227
- require_once "{$this->path}views/clone/index.php";
228
- }
229
-
230
- /**
231
- * Welcome Page
232
- */
233
- public function getWelcomePage() {
234
- require_once "{$this->path}views/welcome/welcome.php";
235
- }
236
-
237
- /**
238
- * Tools Page
239
- */
240
- public function getToolsPage() {
241
- // Tabs
242
- $tabs = new Tabs( array(
243
- "import_export" => __( "Import/Export", "wpstg" ),
244
- "system_info" => __( "System Info", "wpstg" )
245
- ) );
246
-
247
- $this->di->set( "tabs", $tabs );
248
-
249
- $this->di->set( "systemInfo", new SystemInfo( $this->di ) );
250
-
251
- require_once "{$this->path}views/tools/index.php";
252
- }
253
-
254
- /**
255
- * System Information Download
256
- */
257
- public function systemInfoDownload() {
258
- if( !current_user_can( "update_plugins" ) ) {
259
- return;
260
- }
261
-
262
- nocache_headers();
263
- header( "Content-Type: text/plain" );
264
- header( "Content-Disposition: attachment; filename='wpstg-system-info.txt'" );
265
- echo wp_strip_all_tags( new SystemInfo( $this->di ) );
266
- }
267
-
268
- /**
269
- * Import JSON settings file
270
- */
271
- public function import() {
272
- if( empty( $_POST["wpstg_import_nonce"] ) ) {
273
- return;
274
- }
275
-
276
- if( !wp_verify_nonce( $_POST["wpstg_import_nonce"], "wpstg_import_nonce" ) ) {
277
- return;
278
- }
279
-
280
- if( !current_user_can( "update_plugins" ) ) {
281
- return;
282
- }
283
-
284
- $fileExtension = explode( '.', $_FILES["import_file"]["name"] );
285
- $fileExtension = end( $fileExtension );
286
- if( "json" !== $fileExtension ) {
287
- wp_die( "Please upload a valid .json file", "wpstg" );
288
- }
289
-
290
-
291
- $importFile = $_FILES["import_file"]["tmp_name"];
292
-
293
- if( empty( $importFile ) ) {
294
- wp_die( __( "Please upload a file to import", "wpstg" ) );
295
- }
296
-
297
- update_option( "wpstg_settings", json_decode( file_get_contents( $importFile, true ) ) );
298
-
299
- wp_safe_redirect( admin_url( "admin.php?page=wpstg-tools&amp;wpstg-message=settings-imported" ) );
300
-
301
- return;
302
- }
303
-
304
- /**
305
- * Export settings to JSON file
306
- */
307
- public function export() {
308
- if( empty( $_POST["wpstg_export_nonce"] ) ) {
309
- return;
310
- }
311
-
312
- if( !wp_verify_nonce( $_POST["wpstg_export_nonce"], "wpstg_export_nonce" ) ) {
313
- return;
314
- }
315
-
316
- if( !current_user_can( "manage_options" ) ) {
317
- return;
318
- }
319
-
320
- $settings = get_option( "wpstg_settings", array() );
321
-
322
- ignore_user_abort( true );
323
-
324
- if( !in_array( "set_time_limit", explode( ',', ini_get( "disable_functions" ) ) ) && !@ini_get( "safe_mode" ) ) {
325
- set_time_limit( 0 );
326
- }
327
-
328
- $fileName = apply_filters( "wpstg_settings_export_filename", "wpstg-settings-export-" . date( "m-d-Y" ) ) . ".json";
329
-
330
- nocache_headers();
331
- header( "Content-Type: application/json; charset=utf-8" );
332
- header( "Content-Disposition: attachment; filename={$fileName}" );
333
- header( "Expires: 0" );
334
-
335
- echo json_encode( $settings );
336
- }
337
-
338
- /**
339
- * Render a view file
340
- * @param string $file
341
- * @param array $vars
342
- * @return string
343
- */
344
- public function render( $file, $vars = array() ) {
345
- $fullPath = $this->path . "views" . DIRECTORY_SEPARATOR;
346
- $fullPath = str_replace( array('/', "\\"), DIRECTORY_SEPARATOR, $fullPath . $file . ".php" );
347
-
348
- if( !file_exists( $fullPath ) || !is_readable( $fullPath ) ) {
349
- return "Can't render : {$fullPath} either file doesn't exist or can't read it";
350
- }
351
-
352
- $contents = @file_get_contents( $fullPath );
353
-
354
- // Variables are set
355
- if( count( $vars ) > 0 ) {
356
- $vars = array_combine(
357
- array_map( function ($key) {
358
- return "{{" . $key . "}}";
359
- }, array_keys( $vars )
360
- ), $vars
361
- );
362
-
363
- $contents = str_replace( array_keys( $vars ), array_values( $vars ), $contents );
364
- }
365
-
366
- return $contents;
367
- }
368
-
369
- /**
370
- * Ajax Overview
371
- */
372
- public function ajaxOverview() {
373
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
374
-
375
- // Existing clones
376
- $availableClones = get_option( "wpstg_existing_clones_beta", array() );
377
-
378
- // Get license data
379
- $license = get_option( 'wpstg_license_status' );
380
-
381
-
382
- if( \WPStaging\WPStaging::getSlug() === 'wp-staging-pro' ) {
383
- require_once "{$this->path}Pro/views/single-overview-pro.php";
384
- } else {
385
- require_once "{$this->path}views/clone/ajax/single-overview.php";
386
- }
387
-
388
- wp_die();
389
- }
390
-
391
- /**
392
- * Ajax Scan
393
- */
394
- public function ajaxScan() {
395
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
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
- // Check clone name length
418
- if( $cloneNameLength < 1 || $cloneNameLength > 16 ) {
419
- echo wp_send_json( array(
420
- "status" => "failed",
421
- "message" => "Clone name must be between 1 - 16 characters"
422
- ) );
423
- } elseif( array_key_exists( $cloneName, $clones ) ) {
424
- echo wp_send_json( array(
425
- "status" => "failed",
426
- "message" => "Clone name is already in use, please choose an another clone name"
427
- ) );
428
- }
429
-
430
- echo wp_send_json( array("status" => "success") );
431
- }
432
-
433
-
434
-
435
-
436
- /**
437
- * Ajax Start Updating Clone (Basically just layout and saving data)
438
- */
439
- // public function ajaxStartUpdate() {
440
- // check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
441
- //
442
- // $cloning = new Updating();
443
- //
444
- // if( !$cloning->save() ) {
445
- // wp_die('can not save clone data');
446
- // }
447
- //
448
- // require_once "{$this->path}views/clone/ajax/update.php";
449
- //
450
- // wp_die();
451
- // }
452
- /**
453
- * Ajax Start Updating Clone (Basically just layout and saving data)
454
- */
455
- public function ajaxUpdateProcess() {
456
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
457
-
458
- $cloning = new Updating();
459
-
460
- if( !$cloning->save() ) {
461
- wp_die('can not save clone data');
462
- }
463
-
464
- require_once "{$this->path}views/clone/ajax/update.php";
465
-
466
- wp_die();
467
-
468
- //wp_send_json( $cloning->start() );
469
- }
470
- /**
471
- * Ajax Start Clone (Basically just layout and saving data)
472
- */
473
- public function ajaxStartClone() {
474
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
475
-
476
- $cloning = new Cloning();
477
-
478
- if( !$cloning->save() ) {
479
- wp_die('can not save clone data');
480
- }
481
-
482
- require_once "{$this->path}views/clone/ajax/start.php";
483
-
484
- wp_die();
485
- }
486
-
487
- /**
488
- * Ajax Clone Database
489
- */
490
- public function ajaxCloneDatabase() {
491
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
492
-
493
- $cloning = new Cloning();
494
-
495
- wp_send_json( $cloning->start() );
496
- }
497
-
498
- /**
499
- * Ajax Prepare Directories (get listing of files)
500
- */
501
- public function ajaxPrepareDirectories() {
502
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
503
-
504
- $cloning = new Cloning();
505
-
506
- wp_send_json( $cloning->start() );
507
- }
508
-
509
- /**
510
- * Ajax Clone Files
511
- */
512
- public function ajaxCopyFiles() {
513
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
514
-
515
- $cloning = new Cloning();
516
-
517
- wp_send_json( $cloning->start() );
518
- }
519
-
520
- /**
521
- * Ajax Replace Data
522
- */
523
- public function ajaxReplaceData() {
524
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
525
-
526
- $cloning = new Cloning();
527
-
528
- wp_send_json( $cloning->start() );
529
- }
530
-
531
- /**
532
- * Ajax Finish
533
- */
534
- public function ajaxFinish() {
535
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
536
-
537
- $cloning = new Cloning();
538
-
539
- wp_send_json( $cloning->start() );
540
- }
541
-
542
- /**
543
- * Ajax Delete Confirmation
544
- */
545
- public function ajaxDeleteConfirmation() {
546
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
547
-
548
- $delete = new Delete();
549
- $delete->setData();
550
-
551
- $clone = $delete->getClone();
552
-
553
- require_once "{$this->path}views/clone/ajax/delete-confirmation.php";
554
-
555
- wp_die();
556
- }
557
-
558
- /**
559
- * Delete clone
560
- */
561
- public function ajaxDeleteClone() {
562
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
563
-
564
- $delete = new Delete();
565
- wp_send_json( $delete->start() );
566
- }
567
-
568
- /**
569
- * Delete clone
570
- */
571
- public function ajaxCancelClone() {
572
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
573
-
574
- $cancel = new Cancel();
575
- wp_send_json( $cancel->start() );
576
- }
577
-
578
- /**
579
- * Cancel updating process / Do not delete clone!
580
- */
581
- public function ajaxCancelUpdate() {
582
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
583
-
584
- $cancel = new CancelUpdate();
585
- wp_send_json( $cancel->start() );
586
- }
587
-
588
- /**
589
- * Admin Messages
590
- */
591
- public function messages() {
592
- $notice = new Notices( $this->path, $this->url );
593
-
594
- $run = $notice->messages();
595
- }
596
-
597
- /**
598
- * Ajax Hide Poll
599
- * @return mixed boolean | json
600
- */
601
- public function ajaxHidePoll() {
602
- if( false !== update_option( "wpstg_poll", "no" ) ) {
603
- wp_send_json( true );
604
- }
605
- return wp_send_json();
606
- }
607
-
608
- /**
609
- * Ajax Hide Rating
610
- * @return mixed bool | json
611
- */
612
- public function ajaxHideRating() {
613
- if( false !== update_option( "wpstg_rating", "no" ) ) {
614
- wp_send_json( true );
615
- }
616
- return wp_send_json();
617
- }
618
-
619
- /**
620
- * Ajax Hide Beta
621
- */
622
- public function ajaxHideBeta() {
623
- wp_send_json( update_option( "wpstg_beta", "no" ) );
624
- }
625
-
626
- /**
627
- * Clone logs
628
- */
629
- public function ajaxLogs() {
630
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
631
-
632
- $logs = new Logs();
633
- wp_send_json( $logs->start() );
634
- }
635
-
636
- /**
637
- * Ajax Checks Free Disk Space
638
- */
639
- public function ajaxCheckFreeSpace() {
640
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
641
-
642
- $scan = new Scan();
643
- return $scan->hasFreeDiskSpace();
644
- }
645
-
646
- /**
647
- * Ajax Start Push Changes Process
648
- * Start with the module Scan
649
- */
650
- public function ajaxPushScan() {
651
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
652
-
653
- if( !class_exists( 'WPStaging\Backend\Pro\Modules\Jobs\Scan' ) ) {
654
- return false;
655
- }
656
-
657
- // Scan
658
- $scan = new Pro\Modules\Jobs\Scan();
659
-
660
- $scan->start();
661
-
662
- // Get Options
663
- $options = $scan->getOptions();
664
-
665
- require_once "{$this->path}Pro/views/scan.php";
666
-
667
- wp_die();
668
- }
669
-
670
- /**
671
- * Ajax Start Pushing. Needs wp quads pro)
672
- */
673
- public function ajaxPushProcessing() {
674
- check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
675
-
676
- if( !class_exists( 'WPStaging\Backend\Pro\Modules\Jobs\Processing' ) ) {
677
- return false;
678
- }
679
- // Start the process
680
- $processing = new Processing();
681
- wp_send_json( $processing->start() );
682
- }
683
-
684
- /**
685
- * License Page
686
- */
687
- public function getLicensePage() {
688
-
689
- // Get license data
690
- $license = get_option( 'wpstg_license_status' );
691
-
692
- require_once "{$this->path}Pro/views/licensing.php";
693
- }
694
-
695
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend;
4
+
5
+ // No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\Backend\Modules\Jobs\Cancel;
11
+ use WPStaging\Backend\Modules\Jobs\CancelUpdate;
12
+ use WPStaging\Backend\Modules\Jobs\Cloning;
13
+ use WPStaging\Backend\Modules\Jobs\Updating;
14
+ use WPStaging\Backend\Modules\Jobs\Data;
15
+ use WPStaging\Backend\Modules\Jobs\Database;
16
+ use WPStaging\Backend\Modules\Jobs\Delete;
17
+ use WPStaging\Backend\Modules\Jobs\Files;
18
+ use WPStaging\Backend\Modules\Jobs\Scan;
19
+ use WPStaging\Backend\Modules\Jobs\Logs;
20
+ use WPStaging\Backend\Modules\Optimizer;
21
+ use WPStaging\Backend\Modules\SystemInfo;
22
+ use WPStaging\Backend\Modules\Views\Tabs\Tabs;
23
+ use WPStaging\Backend\Notices\Notices;
24
+ use WPStaging\DI\InjectionAware;
25
+ use WPStaging\Backend\Modules\Views\Forms\Settings as FormSettings;
26
+ use WPStaging\Backend\Activation;
27
+ use WPStaging\WPStaging;
28
+ use WPStaging\Backend\Pro\Modules\Jobs\Processing;
29
+ use WPStaging\Backend\Pro\Licensing;
30
+
31
+ /**
32
+ * Class Administrator
33
+ * @package WPStaging\Backend
34
+ */
35
+ class Administrator extends InjectionAware {
36
+
37
+ /**
38
+ * @var string
39
+ */
40
+ private $path;
41
+
42
+ /**
43
+ * @var string
44
+ */
45
+ private $url;
46
+
47
+ /**
48
+ * Initialize class
49
+ */
50
+ public function initialize() {
51
+ $this->defineHooks();
52
+
53
+ // Path to backend
54
+ $this->path = plugin_dir_path( __FILE__ );
55
+
56
+ // URL to public backend folder
57
+ $this->url = plugin_dir_url( __FILE__ ) . "public/";
58
+
59
+ // Load plugins meta data
60
+ $this->loadMeta();
61
+ }
62
+
63
+ /**
64
+ * Load plugn meta data
65
+ */
66
+ public function loadMeta() {
67
+ $run = new \WPStaging\Backend\Pluginmeta\Pluginmeta();
68
+ }
69
+
70
+ /**
71
+ * Define Hooks
72
+ */
73
+ private function defineHooks() {
74
+ // Get loader
75
+ $loader = $this->di->get( "loader" );
76
+
77
+ $Activation = new \WPStaging\Backend\Activation\Activation();
78
+
79
+ $Welcome = new Activation\Welcome();
80
+
81
+ $loader->addAction( "activated_plugin", $Activation, 'deactivate_other_instances' );
82
+ $loader->addAction( "admin_menu", $this, "addMenu", 10 );
83
+ $loader->addAction( "admin_init", $this, "setOptionFormElements" );
84
+ $loader->addAction( "admin_init", $this, "upgrade" );
85
+ $loader->addAction( "admin_post_wpstg_download_sysinfo", $this, "systemInfoDownload" );
86
+ $loader->addAction( "admin_post_wpstg_export", $this, "export" );
87
+ $loader->addAction( "admin_post_wpstg_import_settings", $this, "import" );
88
+ $loader->addAction( "admin_notices", $this, "messages" );
89
+
90
+ // Ajax Requests
91
+ $loader->addAction( "wp_ajax_wpstg_overview", $this, "ajaxOverview" );
92
+ $loader->addAction( "wp_ajax_wpstg_scanning", $this, "ajaxScan" );
93
+ $loader->addAction( "wp_ajax_wpstg_check_clone", $this, "ajaxcheckCloneName" );
94
+ //$loader->addAction( "wp_ajax_wpstg_update_struc", $this, "ajaxStartUpdate" );
95
+ $loader->addAction( "wp_ajax_wpstg_update", $this, "ajaxUpdateProcess" );
96
+ $loader->addAction( "wp_ajax_wpstg_cloning", $this, "ajaxStartClone" );
97
+ $loader->addAction( "wp_ajax_wpstg_clone_database", $this, "ajaxCloneDatabase" );
98
+ $loader->addAction( "wp_ajax_wpstg_clone_prepare_directories", $this, "ajaxPrepareDirectories" );
99
+ $loader->addAction( "wp_ajax_wpstg_clone_files", $this, "ajaxCopyFiles" );
100
+ $loader->addAction( "wp_ajax_wpstg_clone_replace_data", $this, "ajaxReplaceData" );
101
+ $loader->addAction( "wp_ajax_wpstg_clone_finish", $this, "ajaxFinish" );
102
+ $loader->addAction( "wp_ajax_wpstg_confirm_delete_clone", $this, "ajaxDeleteConfirmation" );
103
+ $loader->addAction( "wp_ajax_wpstg_delete_clone", $this, "ajaxDeleteClone" );
104
+ $loader->addAction( "wp_ajax_wpstg_cancel_clone", $this, "ajaxCancelClone" );
105
+ $loader->addAction( "wp_ajax_wpstg_cancel_update", $this, "ajaxCancelUpdate" );
106
+ $loader->addAction( "wp_ajax_wpstg_hide_poll", $this, "ajaxHidePoll" );
107
+ $loader->addAction( "wp_ajax_wpstg_hide_rating", $this, "ajaxHideRating" );
108
+ $loader->addAction( "wp_ajax_wpstg_hide_beta", $this, "ajaxHideBeta" );
109
+ $loader->addAction( "wp_ajax_wpstg_logs", $this, "ajaxLogs" );
110
+ $loader->addAction( "wp_ajax_wpstg_check_disk_space", $this, "ajaxCheckFreeSpace" );
111
+
112
+ // Ajax hooks pro Version
113
+ $loader->addAction( "wp_ajax_wpstg_scan", $this, "ajaxPushScan" );
114
+ $loader->addAction( "wp_ajax_wpstg_push_processing", $this, "ajaxPushProcessing" );
115
+ //$loader->addAction( "wp_ajax_wpstg_copy_database", $this, "ajaxCopyDatabase" );
116
+ }
117
+
118
+ /**
119
+ * Register options form elements
120
+ */
121
+ public function setOptionFormElements() {
122
+ register_setting( "wpstg_settings", "wpstg_settings", array($this, "sanitizeOptions") );
123
+ }
124
+
125
+ /**
126
+ * Upgrade routine
127
+ */
128
+ public function upgrade() {
129
+ $upgrade = new Upgrade\Upgrade();
130
+ $upgrade->doUpgrade();
131
+ }
132
+
133
+ /**
134
+ * Sanitize options data and delete the cache
135
+ * @param array $data
136
+ * @return array
137
+ */
138
+ public function sanitizeOptions( $data = array() ) {
139
+ $sanitized = $this->sanitizeData( $data );
140
+
141
+ add_settings_error( "wpstg-notices", '', __( "Settings updated.", "wpstg" ), "updated" );
142
+
143
+ // Return sanitized data
144
+ //return $sanitized;
145
+ return apply_filters( "wpstg-settings", $sanitized, $data );
146
+ }
147
+
148
+ /**
149
+ * @param array $data
150
+ * @return array
151
+ */
152
+ private function sanitizeData( $data = array() ) {
153
+ $sanitized = array();
154
+
155
+ foreach ( $data as $key => $value ) {
156
+ $sanitized[$key] = (is_array( $value )) ? $this->sanitizeData( $value ) : htmlspecialchars( $value );
157
+ }
158
+
159
+ return $sanitized;
160
+ }
161
+
162
+ /**
163
+ * Add Admin Menu(s)
164
+ */
165
+ public function addMenu() {
166
+
167
+ $logo = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiIGZpbGw9Im5vbmUiPgo8Zz48Zz48cGF0aCBzdHlsZT0iZmlsbDojZmZmIiAgZD0iTTEzNy42LDU2MS4zSDEzLjhIMTB2MzA2LjNsOTAuNy04My40QzE4OS42LDkwOC43LDMzNS4zLDk5MCw1MDAsOTkwYzI0OS45LDAsNDU2LjEtMTg3LjEsNDg2LjItNDI4LjhIODYyLjRDODMzLjMsNzM1LjEsNjgyLjEsODY3LjUsNTAwLDg2Ny41Yy0xMjksMC0yNDIuNS02Ni41LTMwOC4xLTE2Ny4ybDE1MS4zLTEzOS4xSDEzNy42eiIvPjxwYXRoIHN0eWxlPSJmaWxsOiNmZmYiICBkPSJNNTAwLDEwQzI1MC4xLDEwLDQzLjksMTk3LjEsMTMuOCw0MzguOGgxMjMuOEMxNjYuNywyNjQuOSwzMTcuOSwxMzIuNSw1MDAsMTMyLjVjMTMyLjksMCwyNDkuMyw3MC41LDMxMy44LDE3Ni4yTDY4My44LDQzOC44aDEyMi41aDU2LjJoMTIzLjhoMy44VjEzMi41bC04Ny43LDg3LjdDODEzLjgsOTMuMSw2NjYuNiwxMCw1MDAsMTB6Ii8+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvZz4KPC9zdmc+';
168
+
169
+ // Main WP Staging Menu
170
+ add_menu_page(
171
+ "WP-Staging", __( "WP Staging", "wpstg" ), "manage_options", "wpstg_clone", array($this, "getClonePage"), $logo
172
+ );
173
+
174
+ // Page: Clone
175
+ add_submenu_page(
176
+ "wpstg_clone", __( "WP Staging Jobs", "wpstg" ), __( "Sites / Start", "wpstg" ), "manage_options", "wpstg_clone", array($this, "getClonePage")
177
+ );
178
+
179
+ // Page: Settings
180
+ add_submenu_page(
181
+ "wpstg_clone", __( "WP Staging Settings", "wpstg" ), __( "Settings", "wpstg" ), "manage_options", "wpstg-settings", array($this, "getSettingsPage")
182
+ );
183
+
184
+ // Page: Tools
185
+ add_submenu_page(
186
+ "wpstg_clone", __( "WP Staging Tools", "wpstg" ), __( "Tools", "wpstg" ), "manage_options", "wpstg-tools", array($this, "getToolsPage")
187
+ );
188
+ // Page: Tools
189
+ add_submenu_page(
190
+ "wpstg_clone", __( "WP Staging Welcome", "wpstg" ), __( "Get WP Staging Pro", "wpstg" ), "manage_options", "wpstg-welcome", array($this, "getWelcomePage")
191
+ );
192
+
193
+ if( class_exists( 'WPStaging\Backend\Pro\Licensing\Licensing' ) ) {
194
+ // Page: License
195
+ add_submenu_page(
196
+ "wpstg_clone", __( "WP Staging License", "wpstg" ), __( "License", "wpstg" ), "manage_options", "wpstg-license", array($this, "getLicensePage")
197
+ );
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Settings Page
203
+ */
204
+ public function getSettingsPage() {
205
+ // Tabs
206
+ $tabs = new Tabs( array(
207
+ "general" => __( "General", "wpstg" )
208
+ ) );
209
+
210
+
211
+ $this->di
212
+ // Set tabs
213
+ ->set( "tabs", $tabs )
214
+ // Forms
215
+ ->set( "forms", new FormSettings( $tabs ) );
216
+
217
+ require_once "{$this->path}views/settings/index.php";
218
+ }
219
+
220
+ /**
221
+ * Clone Page
222
+ */
223
+ public function getClonePage() {
224
+ // Existing clones
225
+ $availableClones = get_option( "wpstg_existing_clones_beta", array() );
226
+
227
+ require_once "{$this->path}views/clone/index.php";
228
+ }
229
+
230
+ /**
231
+ * Welcome Page
232
+ */
233
+ public function getWelcomePage() {
234
+ require_once "{$this->path}views/welcome/welcome.php";
235
+ }
236
+
237
+ /**
238
+ * Tools Page
239
+ */
240
+ public function getToolsPage() {
241
+ // Tabs
242
+ $tabs = new Tabs( array(
243
+ "import_export" => __( "Import/Export", "wpstg" ),
244
+ "system_info" => __( "System Info", "wpstg" )
245
+ ) );
246
+
247
+ $this->di->set( "tabs", $tabs );
248
+
249
+ $this->di->set( "systemInfo", new SystemInfo( $this->di ) );
250
+
251
+ require_once "{$this->path}views/tools/index.php";
252
+ }
253
+
254
+ /**
255
+ * System Information Download
256
+ */
257
+ public function systemInfoDownload() {
258
+ if( !current_user_can( "update_plugins" ) ) {
259
+ return;
260
+ }
261
+
262
+ nocache_headers();
263
+ header( "Content-Type: text/plain" );
264
+ header( "Content-Disposition: attachment; filename='wpstg-system-info.txt'" );
265
+ echo wp_strip_all_tags( new SystemInfo( $this->di ) );
266
+ }
267
+
268
+ /**
269
+ * Import JSON settings file
270
+ */
271
+ public function import() {
272
+ if( empty( $_POST["wpstg_import_nonce"] ) ) {
273
+ return;
274
+ }
275
+
276
+ if( !wp_verify_nonce( $_POST["wpstg_import_nonce"], "wpstg_import_nonce" ) ) {
277
+ return;
278
+ }
279
+
280
+ if( !current_user_can( "update_plugins" ) ) {
281
+ return;
282
+ }
283
+
284
+ $fileExtension = explode( '.', $_FILES["import_file"]["name"] );
285
+ $fileExtension = end( $fileExtension );
286
+ if( "json" !== $fileExtension ) {
287
+ wp_die( "Please upload a valid .json file", "wpstg" );
288
+ }
289
+
290
+
291
+ $importFile = $_FILES["import_file"]["tmp_name"];
292
+
293
+ if( empty( $importFile ) ) {
294
+ wp_die( __( "Please upload a file to import", "wpstg" ) );
295
+ }
296
+
297
+ update_option( "wpstg_settings", json_decode( file_get_contents( $importFile, true ) ) );
298
+
299
+ wp_safe_redirect( admin_url( "admin.php?page=wpstg-tools&amp;wpstg-message=settings-imported" ) );
300
+
301
+ return;
302
+ }
303
+
304
+ /**
305
+ * Export settings to JSON file
306
+ */
307
+ public function export() {
308
+ if( empty( $_POST["wpstg_export_nonce"] ) ) {
309
+ return;
310
+ }
311
+
312
+ if( !wp_verify_nonce( $_POST["wpstg_export_nonce"], "wpstg_export_nonce" ) ) {
313
+ return;
314
+ }
315
+
316
+ if( !current_user_can( "manage_options" ) ) {
317
+ return;
318
+ }
319
+
320
+ $settings = get_option( "wpstg_settings", array() );
321
+
322
+ ignore_user_abort( true );
323
+
324
+ if( !in_array( "set_time_limit", explode( ',', ini_get( "disable_functions" ) ) ) && !@ini_get( "safe_mode" ) ) {
325
+ set_time_limit( 0 );
326
+ }
327
+
328
+ $fileName = apply_filters( "wpstg_settings_export_filename", "wpstg-settings-export-" . date( "m-d-Y" ) ) . ".json";
329
+
330
+ nocache_headers();
331
+ header( "Content-Type: application/json; charset=utf-8" );
332
+ header( "Content-Disposition: attachment; filename={$fileName}" );
333
+ header( "Expires: 0" );
334
+
335
+ echo json_encode( $settings );
336
+ }
337
+
338
+ /**
339
+ * Render a view file
340
+ * @param string $file
341
+ * @param array $vars
342
+ * @return string
343
+ */
344
+ public function render( $file, $vars = array() ) {
345
+ $fullPath = $this->path . "views" . DIRECTORY_SEPARATOR;
346
+ $fullPath = str_replace( array('/', "\\"), DIRECTORY_SEPARATOR, $fullPath . $file . ".php" );
347
+
348
+ if( !file_exists( $fullPath ) || !is_readable( $fullPath ) ) {
349
+ return "Can't render : {$fullPath} either file doesn't exist or can't read it";
350
+ }
351
+
352
+ $contents = @file_get_contents( $fullPath );
353
+
354
+ // Variables are set
355
+ if( count( $vars ) > 0 ) {
356
+ $vars = array_combine(
357
+ array_map( function ($key) {
358
+ return "{{" . $key . "}}";
359
+ }, array_keys( $vars )
360
+ ), $vars
361
+ );
362
+
363
+ $contents = str_replace( array_keys( $vars ), array_values( $vars ), $contents );
364
+ }
365
+
366
+ return $contents;
367
+ }
368
+
369
+ /**
370
+ * Ajax Overview
371
+ */
372
+ public function ajaxOverview() {
373
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
374
+
375
+ // Existing clones
376
+ $availableClones = get_option( "wpstg_existing_clones_beta", array() );
377
+
378
+ // Get license data
379
+ $license = get_option( 'wpstg_license_status' );
380
+
381
+
382
+ if( \WPStaging\WPStaging::getSlug() === 'wp-staging-pro' ) {
383
+ require_once "{$this->path}Pro/views/single-overview-pro.php";
384
+ } else {
385
+ require_once "{$this->path}views/clone/ajax/single-overview.php";
386
+ }
387
+
388
+ wp_die();
389
+ }
390
+
391
+ /**
392
+ * Ajax Scan
393
+ */
394
+ public function ajaxScan() {
395
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
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
+ // Check clone name length
418
+ if( $cloneNameLength < 1 || $cloneNameLength > 16 ) {
419
+ echo wp_send_json( array(
420
+ "status" => "failed",
421
+ "message" => "Clone name must be between 1 - 16 characters"
422
+ ) );
423
+ } elseif( array_key_exists( $cloneName, $clones ) ) {
424
+ echo wp_send_json( array(
425
+ "status" => "failed",
426
+ "message" => "Clone name is already in use, please choose an another clone name"
427
+ ) );
428
+ }
429
+
430
+ echo wp_send_json( array("status" => "success") );
431
+ }
432
+
433
+
434
+
435
+
436
+ /**
437
+ * Ajax Start Updating Clone (Basically just layout and saving data)
438
+ */
439
+ // public function ajaxStartUpdate() {
440
+ // check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
441
+ //
442
+ // $cloning = new Updating();
443
+ //
444
+ // if( !$cloning->save() ) {
445
+ // wp_die('can not save clone data');
446
+ // }
447
+ //
448
+ // require_once "{$this->path}views/clone/ajax/update.php";
449
+ //
450
+ // wp_die();
451
+ // }
452
+ /**
453
+ * Ajax Start Updating Clone (Basically just layout and saving data)
454
+ */
455
+ public function ajaxUpdateProcess() {
456
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
457
+
458
+ $cloning = new Updating();
459
+
460
+ if( !$cloning->save() ) {
461
+ wp_die('can not save clone data');
462
+ }
463
+
464
+ require_once "{$this->path}views/clone/ajax/update.php";
465
+
466
+ wp_die();
467
+
468
+ //wp_send_json( $cloning->start() );
469
+ }
470
+ /**
471
+ * Ajax Start Clone (Basically just layout and saving data)
472
+ */
473
+ public function ajaxStartClone() {
474
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
475
+
476
+ $cloning = new Cloning();
477
+
478
+ if( !$cloning->save() ) {
479
+ wp_die('can not save clone data');
480
+ }
481
+
482
+ require_once "{$this->path}views/clone/ajax/start.php";
483
+
484
+ wp_die();
485
+ }
486
+
487
+ /**
488
+ * Ajax Clone Database
489
+ */
490
+ public function ajaxCloneDatabase() {
491
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
492
+
493
+ $cloning = new Cloning();
494
+
495
+ wp_send_json( $cloning->start() );
496
+ }
497
+
498
+ /**
499
+ * Ajax Prepare Directories (get listing of files)
500
+ */
501
+ public function ajaxPrepareDirectories() {
502
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
503
+
504
+ $cloning = new Cloning();
505
+
506
+ wp_send_json( $cloning->start() );
507
+ }
508
+
509
+ /**
510
+ * Ajax Clone Files
511
+ */
512
+ public function ajaxCopyFiles() {
513
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
514
+
515
+ $cloning = new Cloning();
516
+
517
+ wp_send_json( $cloning->start() );
518
+ }
519
+
520
+ /**
521
+ * Ajax Replace Data
522
+ */
523
+ public function ajaxReplaceData() {
524
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
525
+
526
+ $cloning = new Cloning();
527
+
528
+ wp_send_json( $cloning->start() );
529
+ }
530
+
531
+ /**
532
+ * Ajax Finish
533
+ */
534
+ public function ajaxFinish() {
535
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
536
+
537
+ $cloning = new Cloning();
538
+
539
+ wp_send_json( $cloning->start() );
540
+ }
541
+
542
+ /**
543
+ * Ajax Delete Confirmation
544
+ */
545
+ public function ajaxDeleteConfirmation() {
546
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
547
+
548
+ $delete = new Delete();
549
+ $delete->setData();
550
+
551
+ $clone = $delete->getClone();
552
+
553
+ require_once "{$this->path}views/clone/ajax/delete-confirmation.php";
554
+
555
+ wp_die();
556
+ }
557
+
558
+ /**
559
+ * Delete clone
560
+ */
561
+ public function ajaxDeleteClone() {
562
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
563
+
564
+ $delete = new Delete();
565
+ wp_send_json( $delete->start() );
566
+ }
567
+
568
+ /**
569
+ * Delete clone
570
+ */
571
+ public function ajaxCancelClone() {
572
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
573
+
574
+ $cancel = new Cancel();
575
+ wp_send_json( $cancel->start() );
576
+ }
577
+
578
+ /**
579
+ * Cancel updating process / Do not delete clone!
580
+ */
581
+ public function ajaxCancelUpdate() {
582
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
583
+
584
+ $cancel = new CancelUpdate();
585
+ wp_send_json( $cancel->start() );
586
+ }
587
+
588
+ /**
589
+ * Admin Messages
590
+ */
591
+ public function messages() {
592
+ $notice = new Notices( $this->path, $this->url );
593
+
594
+ $run = $notice->messages();
595
+ }
596
+
597
+ /**
598
+ * Ajax Hide Poll
599
+ * @return mixed boolean | json
600
+ */
601
+ public function ajaxHidePoll() {
602
+ if( false !== update_option( "wpstg_poll", "no" ) ) {
603
+ wp_send_json( true );
604
+ }
605
+ return wp_send_json();
606
+ }
607
+
608
+ /**
609
+ * Ajax Hide Rating
610
+ * @return mixed bool | json
611
+ */
612
+ public function ajaxHideRating() {
613
+ if( false !== update_option( "wpstg_rating", "no" ) ) {
614
+ wp_send_json( true );
615
+ }
616
+ return wp_send_json();
617
+ }
618
+
619
+ /**
620
+ * Ajax Hide Beta
621
+ */
622
+ public function ajaxHideBeta() {
623
+ wp_send_json( update_option( "wpstg_beta", "no" ) );
624
+ }
625
+
626
+ /**
627
+ * Clone logs
628
+ */
629
+ public function ajaxLogs() {
630
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
631
+
632
+ $logs = new Logs();
633
+ wp_send_json( $logs->start() );
634
+ }
635
+
636
+ /**
637
+ * Ajax Checks Free Disk Space
638
+ */
639
+ public function ajaxCheckFreeSpace() {
640
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
641
+
642
+ $scan = new Scan();
643
+ return $scan->hasFreeDiskSpace();
644
+ }
645
+
646
+ /**
647
+ * Ajax Start Push Changes Process
648
+ * Start with the module Scan
649
+ */
650
+ public function ajaxPushScan() {
651
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
652
+
653
+ if( !class_exists( 'WPStaging\Backend\Pro\Modules\Jobs\Scan' ) ) {
654
+ return false;
655
+ }
656
+
657
+ // Scan
658
+ $scan = new Pro\Modules\Jobs\Scan();
659
+
660
+ $scan->start();
661
+
662
+ // Get Options
663
+ $options = $scan->getOptions();
664
+
665
+ require_once "{$this->path}Pro/views/scan.php";
666
+
667
+ wp_die();
668
+ }
669
+
670
+ /**
671
+ * Ajax Start Pushing. Needs wp quads pro)
672
+ */
673
+ public function ajaxPushProcessing() {
674
+ check_ajax_referer( "wpstg_ajax_nonce", "nonce" );
675
+
676
+ if( !class_exists( 'WPStaging\Backend\Pro\Modules\Jobs\Processing' ) ) {
677
+ return false;
678
+ }
679
+ // Start the process
680
+ $processing = new Processing();
681
+ wp_send_json( $processing->start() );
682
+ }
683
+
684
+ /**
685
+ * License Page
686
+ */
687
+ public function getLicensePage() {
688
+
689
+ // Get license data
690
+ $license = get_option( 'wpstg_license_status' );
691
+
692
+ require_once "{$this->path}Pro/views/licensing.php";
693
+ }
694
+
695
+ }
apps/Backend/Modules/Jobs/Cancel.php CHANGED
@@ -1,74 +1,74 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs;
4
-
5
- /**
6
- * Class Cancel Processing
7
- * @package WPStaging\Backend\Modules\Jobs
8
- */
9
- class Cancel extends Job {
10
-
11
- /**
12
- * Start Module
13
- * @return bool
14
- */
15
- public function start() {
16
- $cloneData = $this->createCloneData();
17
-
18
- if (empty($cloneData)) {
19
- return true;
20
- }
21
-
22
-
23
- $delete = new Delete();
24
- return $delete->start($cloneData);
25
- }
26
-
27
- /**
28
- * @return array
29
- */
30
- protected function createCloneData() {
31
- $clone = array();
32
-
33
- if (!$this->check()) {
34
- return $clone;
35
- }
36
-
37
- $clone["name"] = $this->options->clone;
38
- $clone["number"] = $this->options->cloneNumber;
39
- $clone["path"] = ABSPATH . $this->options->cloneDirectoryName;
40
- $clone["prefix"] = ABSPATH . $this->options->prefix;
41
-
42
- return $clone;
43
- }
44
-
45
- /**
46
- * @return bool
47
- */
48
- public function check() {
49
- return (
50
- isset($this->options) &&
51
- isset($this->options->clone) &&
52
- isset($this->options->cloneNumber) &&
53
- isset($this->options->cloneDirectoryName) &&
54
- isset($_POST["clone"]) &&
55
- $_POST["clone"] === $this->options->clone
56
- );
57
- }
58
-
59
- /**
60
- * Get json response
61
- * return json
62
- */
63
- private function returnFinish($message = '') {
64
-
65
- wp_die(json_encode(array(
66
- 'job' => 'delete',
67
- 'status' => true,
68
- 'message' => $message,
69
- 'error' => false,
70
- 'delete' => 'finished'
71
- )));
72
- }
73
-
74
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs;
4
+
5
+ /**
6
+ * Class Cancel Processing
7
+ * @package WPStaging\Backend\Modules\Jobs
8
+ */
9
+ class Cancel extends Job {
10
+
11
+ /**
12
+ * Start Module
13
+ * @return bool
14
+ */
15
+ public function start() {
16
+ $cloneData = $this->createCloneData();
17
+
18
+ if (empty($cloneData)) {
19
+ return true;
20
+ }
21
+
22
+
23
+ $delete = new Delete();
24
+ return $delete->start($cloneData);
25
+ }
26
+
27
+ /**
28
+ * @return array
29
+ */
30
+ protected function createCloneData() {
31
+ $clone = array();
32
+
33
+ if (!$this->check()) {
34
+ return $clone;
35
+ }
36
+
37
+ $clone["name"] = $this->options->clone;
38
+ $clone["number"] = $this->options->cloneNumber;
39
+ $clone["path"] = ABSPATH . $this->options->cloneDirectoryName;
40
+ $clone["prefix"] = ABSPATH . $this->options->prefix;
41
+
42
+ return $clone;
43
+ }
44
+
45
+ /**
46
+ * @return bool
47
+ */
48
+ public function check() {
49
+ return (
50
+ isset($this->options) &&
51
+ isset($this->options->clone) &&
52
+ isset($this->options->cloneNumber) &&
53
+ isset($this->options->cloneDirectoryName) &&
54
+ isset($_POST["clone"]) &&
55
+ $_POST["clone"] === $this->options->clone
56
+ );
57
+ }
58
+
59
+ /**
60
+ * Get json response
61
+ * return json
62
+ */
63
+ private function returnFinish($message = '') {
64
+
65
+ wp_die(json_encode(array(
66
+ 'job' => 'delete',
67
+ 'status' => true,
68
+ 'message' => $message,
69
+ 'error' => false,
70
+ 'delete' => 'finished'
71
+ )));
72
+ }
73
+
74
+ }
apps/Backend/Modules/Jobs/CancelUpdate.php CHANGED
@@ -1,90 +1,90 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs;
4
-
5
- /**
6
- * Class Cancel Update Processing
7
- * @package WPStaging\Backend\Modules\Jobs
8
- */
9
- class CancelUpdate extends Job {
10
-
11
- /**
12
- * Start Module
13
- * @return bool
14
- */
15
- public function start() {
16
- $cloneData = $this->createCloneData();
17
-
18
- if (empty($cloneData)) {
19
- return true;
20
- }
21
- // Delete Cache Files
22
- $this->deleteCacheFiles();
23
-
24
- $this->returnFinish();
25
-
26
- }
27
-
28
- /**
29
- * @return array
30
- */
31
- protected function createCloneData() {
32
- $clone = array();
33
-
34
- if (!$this->check()) {
35
- return $clone;
36
- }
37
-
38
- $clone["name"] = $this->options->clone;
39
- $clone["number"] = $this->options->cloneNumber;
40
- $clone["path"] = ABSPATH . $this->options->cloneDirectoryName;
41
- $clone["prefix"] = ABSPATH . $this->options->prefix;
42
-
43
- return $clone;
44
- }
45
-
46
- /**
47
- * @return bool
48
- */
49
- public function check() {
50
- return (
51
- isset($this->options) &&
52
- isset($this->options->clone) &&
53
- isset($this->options->cloneNumber) &&
54
- isset($this->options->cloneDirectoryName) &&
55
- isset($_POST["clone"]) &&
56
- $_POST["clone"] === $this->options->clone
57
- );
58
- }
59
-
60
- /**
61
- * Get json response
62
- * return json
63
- */
64
- private function returnFinish($message = '') {
65
-
66
- wp_die(json_encode(array(
67
- 'job' => 'delete',
68
- 'status' => true,
69
- 'message' => $message,
70
- 'error' => false,
71
- 'delete' => 'finished'
72
- )));
73
- }
74
-
75
-
76
- /**
77
- * Delete Cache Files
78
- */
79
- protected function deleteCacheFiles()
80
- {
81
- $this->log("Cancel Updating: Deleting clone job's cache files...");
82
-
83
- // Clean cache files
84
- $this->cache->delete("clone_options");
85
- $this->cache->delete("files_to_copy");
86
-
87
- $this->log("Updating process canceled");
88
- }
89
-
90
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs;
4
+
5
+ /**
6
+ * Class Cancel Update Processing
7
+ * @package WPStaging\Backend\Modules\Jobs
8
+ */
9
+ class CancelUpdate extends Job {
10
+
11
+ /**
12
+ * Start Module
13
+ * @return bool
14
+ */
15
+ public function start() {
16
+ $cloneData = $this->createCloneData();
17
+
18
+ if (empty($cloneData)) {
19
+ return true;
20
+ }
21
+ // Delete Cache Files
22
+ $this->deleteCacheFiles();
23
+
24
+ $this->returnFinish();
25
+
26
+ }
27
+
28
+ /**
29
+ * @return array
30
+ */
31
+ protected function createCloneData() {
32
+ $clone = array();
33
+
34
+ if (!$this->check()) {
35
+ return $clone;
36
+ }
37
+
38
+ $clone["name"] = $this->options->clone;
39
+ $clone["number"] = $this->options->cloneNumber;
40
+ $clone["path"] = ABSPATH . $this->options->cloneDirectoryName;
41
+ $clone["prefix"] = ABSPATH . $this->options->prefix;
42
+
43
+ return $clone;
44
+ }
45
+
46
+ /**
47
+ * @return bool
48
+ */
49
+ public function check() {
50
+ return (
51
+ isset($this->options) &&
52
+ isset($this->options->clone) &&
53
+ isset($this->options->cloneNumber) &&
54
+ isset($this->options->cloneDirectoryName) &&
55
+ isset($_POST["clone"]) &&
56
+ $_POST["clone"] === $this->options->clone
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Get json response
62
+ * return json
63
+ */
64
+ private function returnFinish($message = '') {
65
+
66
+ wp_die(json_encode(array(
67
+ 'job' => 'delete',
68
+ 'status' => true,
69
+ 'message' => $message,
70
+ 'error' => false,
71
+ 'delete' => 'finished'
72
+ )));
73
+ }
74
+
75
+
76
+ /**
77
+ * Delete Cache Files
78
+ */
79
+ protected function deleteCacheFiles()
80
+ {
81
+ $this->log("Cancel Updating: Deleting clone job's cache files...");
82
+
83
+ // Clean cache files
84
+ $this->cache->delete("clone_options");
85
+ $this->cache->delete("files_to_copy");
86
+
87
+ $this->log("Updating process canceled");
88
+ }
89
+
90
+ }
apps/Backend/Modules/Jobs/Cloning.php CHANGED
@@ -1,250 +1,250 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
- use WPStaging\WPStaging;
6
-
7
- /**
8
- * Class Cloning
9
- * @package WPStaging\Backend\Modules\Jobs
10
- */
11
- class Cloning extends Job
12
- {
13
- /**
14
- * Initialize is called in \Job
15
- */
16
- public function initialize(){
17
- $this->db = WPStaging::getInstance()->get("wpdb");
18
- }
19
-
20
- /**
21
- * Save Chosen Cloning Settings
22
- * @return bool
23
- */
24
- public function save()
25
- {
26
- if (!isset($_POST) || !isset($_POST["cloneID"]))
27
- {
28
- return false;
29
- }
30
-
31
- // Generate Options
32
- // Clone
33
- $this->options->clone = $_POST["cloneID"];
34
- $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
35
- $this->options->cloneNumber = 1;
36
- $this->options->prefix = $this->getStagingPrefix();
37
-
38
- //$this->options->prefix = $this->getStagingPrefix();
39
- $this->options->includedDirectories = array();
40
- $this->options->excludedDirectories = array();
41
- $this->options->extraDirectories = array();
42
- $this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
43
-
44
- // Job
45
- $this->options->job = new \stdClass();
46
-
47
- // Check if clone data already exists and use that one
48
- if (isset($this->options->existingClones[$this->options->clone]) )
49
- {
50
-
51
- $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]->number;
52
-
53
- $this->options->prefix = isset($this->options->existingClones[$this->options->clone]->prefix) ?
54
- $this->options->existingClones[$this->options->clone]->prefix :
55
- $this->getStagingPrefix();
56
-
57
- } // Clone does not exist but there are other clones in db
58
- // Get data and increment it
59
- elseif (!empty($this->options->existingClones))
60
- {
61
- $this->options->cloneNumber = count($this->options->existingClones)+1;
62
- $this->options->prefix = $this->getStagingPrefix();
63
- }
64
-
65
- // Excluded Tables
66
- if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
67
- {
68
- $this->options->excludedTables = $_POST["excludedTables"];
69
- }
70
-
71
- // Excluded Directories
72
- if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
73
- {
74
- $this->options->excludedDirectories = $_POST["excludedDirectories"];
75
- }
76
-
77
- // Excluded Directories TOTAL
78
- // Do not copy these folders and plugins
79
- $excludedDirectories = array(
80
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
81
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
82
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wp-super-cache',
83
- );
84
-
85
- $this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
86
-
87
- // Included Directories
88
- if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
89
- {
90
- $this->options->includedDirectories = $_POST["includedDirectories"];
91
- }
92
-
93
- // Extra Directories
94
- if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
95
- {
96
- $this->options->extraDirectories = $_POST["extraDirectories"];
97
- }
98
-
99
- // Directories to Copy
100
- $this->options->directoriesToCopy = array_merge(
101
- $this->options->includedDirectories,
102
- $this->options->extraDirectories
103
- );
104
-
105
- array_unshift($this->options->directoriesToCopy, ABSPATH);
106
-
107
- // Delete files to copy listing
108
- $this->cache->delete("files_to_copy");
109
-
110
- return $this->saveOptions();
111
- }
112
-
113
- /**
114
- * Create a new staging prefix which does not already exists in database
115
- */
116
- public function getStagingPrefix(){
117
-
118
- // Find a new prefix that does not already exist in database.
119
- // 1000 different possible prefixes should be enough here
120
- for($i=0; $i <= 10000; $i++){
121
- $this->options->prefix = isset($this->options->existingClones) ?
122
- 'wpstg' . (count($this->options->existingClones)+$i) . '_' :
123
- 'wpstg' . $i . '_';
124
-
125
- $sql = "SHOW TABLE STATUS LIKE '{$this->options->prefix}%'";
126
- $tables = $this->db->get_results($sql);
127
-
128
- // Prefix does not exists. We can use it
129
- if (!$tables){
130
- //$this->returnException('new ' . $this->options->prefix);
131
- return $this->options->prefix;
132
- }
133
- }
134
- $this->returnException("Fatal Error: Can not create staging prefix. '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
135
- wp_die("Fatal Error: Can not create staging prefix. Prefix '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
136
- }
137
-
138
- /**
139
- * Check if potential new prefix of staging site would be identical with live site.
140
- * @return boolean
141
- */
142
- private function isPrefixIdentical(){
143
- $db = WPStaging::getInstance()->get("wpdb");
144
-
145
- $livePrefix = $db->prefix;
146
- $stagingPrefix = $this->options->prefix;
147
-
148
- if ($livePrefix == $stagingPrefix){
149
- return true;
150
- }
151
- return false;
152
- }
153
-
154
- /**
155
- * Start the cloning job
156
- */
157
- public function start()
158
- {
159
- if (null === $this->options->currentJob)
160
- {
161
- $this->log("Cloning job for {$this->options->clone} finished");
162
- return true;
163
- }
164
-
165
- $methodName = "job" . ucwords($this->options->currentJob);
166
-
167
- if (!method_exists($this, $methodName))
168
- {
169
- $this->log("Can't execute job; Job's method {$methodName} is not found");
170
- throw new JobNotFoundException($methodName);
171
- }
172
-
173
- // Call the job
174
- //$this->log("execute job: Job's method {$methodName}");
175
- return $this->{$methodName}();
176
- }
177
-
178
- /**
179
- * @param object $response
180
- * @param string $nextJob
181
- * @return object
182
- */
183
- private function handleJobResponse($response, $nextJob)
184
- {
185
- // Job is not done
186
- if (true !== $response->status)
187
- {
188
- return $response;
189
- }
190
-
191
- $this->options->currentJob = $nextJob;
192
- $this->options->currentStep = 0;
193
- $this->options->totalSteps = 0;
194
-
195
- // Save options
196
- $this->saveOptions();
197
-
198
- return $response;
199
- }
200
-
201
- /**
202
- * Clone Database
203
- * @return object
204
- */
205
- public function jobDatabase()
206
- {
207
- $database = new Database();
208
- return $this->handleJobResponse($database->start(), "directories");
209
- }
210
-
211
- /**
212
- * Get All Files From Selected Directories Recursively Into a File
213
- * @return object
214
- */
215
- public function jobDirectories()
216
- {
217
- $directories = new Directories();
218
- return $this->handleJobResponse($directories->start(), "files");
219
- }
220
-
221
- /**
222
- * Copy Files
223
- * @return object
224
- */
225
- public function jobFiles()
226
- {
227
- $files = new Files();
228
- return $this->handleJobResponse($files->start(), "data");
229
- }
230
-
231
- /**
232
- * Replace Data
233
- * @return object
234
- */
235
- public function jobData()
236
- {
237
- $data = new Data();
238
- return $this->handleJobResponse($data->start(), "finish");
239
- }
240
-
241
- /**
242
- * Save Clone Data
243
- * @return object
244
- */
245
- public function jobFinish()
246
- {
247
- $finish = new Finish();
248
- return $this->handleJobResponse($finish->start(), '');
249
- }
250
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
+ use WPStaging\WPStaging;
6
+
7
+ /**
8
+ * Class Cloning
9
+ * @package WPStaging\Backend\Modules\Jobs
10
+ */
11
+ class Cloning extends Job
12
+ {
13
+ /**
14
+ * Initialize is called in \Job
15
+ */
16
+ public function initialize(){
17
+ $this->db = WPStaging::getInstance()->get("wpdb");
18
+ }
19
+
20
+ /**
21
+ * Save Chosen Cloning Settings
22
+ * @return bool
23
+ */
24
+ public function save()
25
+ {
26
+ if (!isset($_POST) || !isset($_POST["cloneID"]))
27
+ {
28
+ return false;
29
+ }
30
+
31
+ // Generate Options
32
+ // Clone
33
+ $this->options->clone = $_POST["cloneID"];
34
+ $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
35
+ $this->options->cloneNumber = 1;
36
+ $this->options->prefix = $this->getStagingPrefix();
37
+
38
+ //$this->options->prefix = $this->getStagingPrefix();
39
+ $this->options->includedDirectories = array();
40
+ $this->options->excludedDirectories = array();
41
+ $this->options->extraDirectories = array();
42
+ $this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
43
+
44
+ // Job
45
+ $this->options->job = new \stdClass();
46
+
47
+ // Check if clone data already exists and use that one
48
+ if (isset($this->options->existingClones[$this->options->clone]) )
49
+ {
50
+
51
+ $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]->number;
52
+
53
+ $this->options->prefix = isset($this->options->existingClones[$this->options->clone]->prefix) ?
54
+ $this->options->existingClones[$this->options->clone]->prefix :
55
+ $this->getStagingPrefix();
56
+
57
+ } // Clone does not exist but there are other clones in db
58
+ // Get data and increment it
59
+ elseif (!empty($this->options->existingClones))
60
+ {
61
+ $this->options->cloneNumber = count($this->options->existingClones)+1;
62
+ $this->options->prefix = $this->getStagingPrefix();
63
+ }
64
+
65
+ // Excluded Tables
66
+ if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
67
+ {
68
+ $this->options->excludedTables = $_POST["excludedTables"];
69
+ }
70
+
71
+ // Excluded Directories
72
+ if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
73
+ {
74
+ $this->options->excludedDirectories = $_POST["excludedDirectories"];
75
+ }
76
+
77
+ // Excluded Directories TOTAL
78
+ // Do not copy these folders and plugins
79
+ $excludedDirectories = array(
80
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
81
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
82
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wp-super-cache',
83
+ );
84
+
85
+ $this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
86
+
87
+ // Included Directories
88
+ if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
89
+ {
90
+ $this->options->includedDirectories = $_POST["includedDirectories"];
91
+ }
92
+
93
+ // Extra Directories
94
+ if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
95
+ {
96
+ $this->options->extraDirectories = $_POST["extraDirectories"];
97
+ }
98
+
99
+ // Directories to Copy
100
+ $this->options->directoriesToCopy = array_merge(
101
+ $this->options->includedDirectories,
102
+ $this->options->extraDirectories
103
+ );
104
+
105
+ array_unshift($this->options->directoriesToCopy, ABSPATH);
106
+
107
+ // Delete files to copy listing
108
+ $this->cache->delete("files_to_copy");
109
+
110
+ return $this->saveOptions();
111
+ }
112
+
113
+ /**
114
+ * Create a new staging prefix which does not already exists in database
115
+ */
116
+ public function getStagingPrefix(){
117
+
118
+ // Find a new prefix that does not already exist in database.
119
+ // 1000 different possible prefixes should be enough here
120
+ for($i=0; $i <= 10000; $i++){
121
+ $this->options->prefix = isset($this->options->existingClones) ?
122
+ 'wpstg' . (count($this->options->existingClones)+$i) . '_' :
123
+ 'wpstg' . $i . '_';
124
+
125
+ $sql = "SHOW TABLE STATUS LIKE '{$this->options->prefix}%'";
126
+ $tables = $this->db->get_results($sql);
127
+
128
+ // Prefix does not exists. We can use it
129
+ if (!$tables){
130
+ //$this->returnException('new ' . $this->options->prefix);
131
+ return $this->options->prefix;
132
+ }
133
+ }
134
+ $this->returnException("Fatal Error: Can not create staging prefix. '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
135
+ wp_die("Fatal Error: Can not create staging prefix. Prefix '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
136
+ }
137
+
138
+ /**
139
+ * Check if potential new prefix of staging site would be identical with live site.
140
+ * @return boolean
141
+ */
142
+ private function isPrefixIdentical(){
143
+ $db = WPStaging::getInstance()->get("wpdb");
144
+
145
+ $livePrefix = $db->prefix;
146
+ $stagingPrefix = $this->options->prefix;
147
+
148
+ if ($livePrefix == $stagingPrefix){
149
+ return true;
150
+ }
151
+ return false;
152
+ }
153
+
154
+ /**
155
+ * Start the cloning job
156
+ */
157
+ public function start()
158
+ {
159
+ if (null === $this->options->currentJob)
160
+ {
161
+ $this->log("Cloning job for {$this->options->clone} finished");
162
+ return true;
163
+ }
164
+
165
+ $methodName = "job" . ucwords($this->options->currentJob);
166
+
167
+ if (!method_exists($this, $methodName))
168
+ {
169
+ $this->log("Can't execute job; Job's method {$methodName} is not found");
170
+ throw new JobNotFoundException($methodName);
171
+ }
172
+
173
+ // Call the job
174
+ //$this->log("execute job: Job's method {$methodName}");
175
+ return $this->{$methodName}();
176
+ }
177
+
178
+ /**
179
+ * @param object $response
180
+ * @param string $nextJob
181
+ * @return object
182
+ */
183
+ private function handleJobResponse($response, $nextJob)
184
+ {
185
+ // Job is not done
186
+ if (true !== $response->status)
187
+ {
188
+ return $response;
189
+ }
190
+
191
+ $this->options->currentJob = $nextJob;
192
+ $this->options->currentStep = 0;
193
+ $this->options->totalSteps = 0;
194
+
195
+ // Save options
196
+ $this->saveOptions();
197
+
198
+ return $response;
199
+ }
200
+
201
+ /**
202
+ * Clone Database
203
+ * @return object
204
+ */
205
+ public function jobDatabase()
206
+ {
207
+ $database = new Database();
208
+ return $this->handleJobResponse($database->start(), "directories");
209
+ }
210
+
211
+ /**
212
+ * Get All Files From Selected Directories Recursively Into a File
213
+ * @return object
214
+ */
215
+ public function jobDirectories()
216
+ {
217
+ $directories = new Directories();
218
+ return $this->handleJobResponse($directories->start(), "files");
219
+ }
220
+
221
+ /**
222
+ * Copy Files
223
+ * @return object
224
+ */
225
+ public function jobFiles()
226
+ {
227
+ $files = new Files();
228
+ return $this->handleJobResponse($files->start(), "data");
229
+ }
230
+
231
+ /**
232
+ * Replace Data
233
+ * @return object
234
+ */
235
+ public function jobData()
236
+ {
237
+ $data = new Data();
238
+ return $this->handleJobResponse($data->start(), "finish");
239
+ }
240
+
241
+ /**
242
+ * Save Clone Data
243
+ * @return object
244
+ */
245
+ public function jobFinish()
246
+ {
247
+ $finish = new Finish();
248
+ return $this->handleJobResponse($finish->start(), '');
249
+ }
250
  }
apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -1,409 +1,460 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
- use WPStaging\Utils\Logger;
11
- use WPStaging\WPStaging;
12
-
13
- /**
14
- * Class Data
15
- * @package WPStaging\Backend\Modules\Jobs
16
- */
17
- class Data extends JobExecutable
18
- {
19
-
20
- /**
21
- * @var \wpdb
22
- */
23
- private $db;
24
-
25
- /**
26
- * @var string
27
- */
28
- private $prefix;
29
-
30
- /**
31
- * Initialize
32
- */
33
- public function initialize()
34
- {
35
- $this->db = WPStaging::getInstance()->get("wpdb");
36
-
37
- //$this->prefix = "wpstg{$this->options->cloneNumber}_";
38
- $this->prefix = $this->options->prefix;
39
-
40
- // Fix current step
41
- if (0 == $this->options->currentStep)
42
- {
43
- $this->options->currentStep = 1;
44
- }
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 = 6;
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->saveOptions();
67
-
68
- // Prepare response
69
- $this->response = array(
70
- "status" => true,
71
- "percentage" => 100,
72
- "total" => $this->options->totalSteps,
73
- "step" => $this->options->totalSteps,
74
- "last_msg" => $this->logger->getLastLogMsg(),
75
- "running_time" => $this->time() - time(),
76
- "job_done" => true
77
- );
78
-
79
- return (object) $this->response;
80
- }
81
-
82
- /**
83
- * Execute the Current Step
84
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
85
- * @return bool
86
- */
87
- protected function execute()
88
- {
89
- // Fatal error. Let this happen never and break here immediately
90
- if ($this->isRoot()){
91
- return false;
92
- }
93
-
94
- // Over limits threshold
95
- if ($this->isOverThreshold())
96
- {
97
- // Prepare response and save current progress
98
- $this->prepareResponse(false, false);
99
- $this->saveOptions();
100
- return false;
101
- }
102
-
103
- // No more steps, finished
104
- if ($this->isFinished())
105
- {
106
- $this->prepareResponse(true, false);
107
- return false;
108
- }
109
-
110
- // Execute step
111
- $stepMethodName = "step" . $this->options->currentStep;
112
- if (!$this->{$stepMethodName}())
113
- {
114
- $this->prepareResponse(false, false);
115
- return false;
116
- }
117
-
118
- // Prepare Response
119
- $this->prepareResponse();
120
-
121
- // Not finished
122
- return true;
123
- }
124
-
125
- /**
126
- * Checks Whether There is Any Job to Execute or Not
127
- * @return bool
128
- */
129
- private function isFinished()
130
- {
131
- return (
132
- $this->options->currentStep > $this->options->totalSteps ||
133
- !method_exists($this, "step" . $this->options->currentStep)
134
- );
135
- }
136
-
137
- /**
138
- * Check if current operation is done on the root folder or on the live DB
139
- * @return boolean
140
- */
141
- private function isRoot(){
142
-
143
- // Prefix is the same as the one of live site
144
- $wpdb = WPStaging::getInstance()->get("wpdb");
145
- if ($wpdb->prefix === $this->prefix){
146
- return true;
147
- }
148
-
149
- // CloneName is empty
150
- $name = (array)$this->options->cloneDirectoryName;
151
- if (empty($name)){
152
- return true;
153
- }
154
-
155
- // Live Path === Staging path
156
- if (get_home_url() . $this->options->cloneDirectoryName === get_home_url()){
157
- return true;
158
- }
159
-
160
- return false;
161
- }
162
-
163
-
164
- /**
165
- * Check if table exists
166
- * @param string $table
167
- * @return boolean
168
- */
169
- protected function isTable($table){
170
- if($this->db->get_var("SHOW TABLES LIKE '{$table}'") != $table ){
171
- $this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
172
- return false;
173
- }
174
- return true;
175
- }
176
-
177
- /**
178
- * Replace "siteurl"
179
- * @return bool
180
- */
181
- protected function step1() {
182
- $this->log( "Search & Replace: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
183
-
184
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
185
- return true;
186
- }
187
-
188
- // Installed in sub-directory
189
- if( isset( $this->settings->wpSubDirectory ) && "1" === $this->settings->wpSubDirectory ) {
190
- $subDirectory = str_replace( get_home_path(), '', ABSPATH );
191
- $this->log( "Updating siteurl and homeurl to " . get_home_url() . '/' . $subDirectory . $this->options->cloneDirectoryName );
192
- // Replace URLs
193
- $result = $this->db->query(
194
- $this->db->prepare(
195
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", get_home_url() . '/' . $subDirectory . $this->options->cloneDirectoryName
196
- )
197
- );
198
- } else {
199
- $this->log( "Search & Replace:: Updating siteurl and homeurl to " . get_home_url() . '/' . $this->options->cloneDirectoryName );
200
- // Replace URLs
201
- $result = $this->db->query(
202
- $this->db->prepare(
203
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", get_home_url() . '/' . $this->options->cloneDirectoryName
204
- )
205
- );
206
- }
207
-
208
-
209
- // All good
210
- if( $result ) {
211
- return true;
212
- }
213
-
214
- $this->log( "Search & Replace: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
215
- return false;
216
- }
217
-
218
- /**
219
- * Update "wpstg_is_staging_site"
220
- * @return bool
221
- */
222
- protected function step2()
223
- {
224
-
225
- $this->log( "Search & Replace: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
226
-
227
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
228
- return true;
229
- }
230
-
231
- $result = $this->db->query(
232
- $this->db->prepare(
233
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'",
234
- "true"
235
- )
236
- );
237
-
238
- // No errors but no option name such as wpstg_is_staging_site
239
- if ('' === $this->db->last_error && 0 == $result)
240
- {
241
- $result = $this->db->query(
242
- $this->db->prepare(
243
- "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)",
244
- "true"
245
- )
246
- );
247
- }
248
-
249
- // All good
250
- if ($result)
251
- {
252
- return true;
253
- }
254
-
255
- $this->log("Search & Replace: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
256
- return false;
257
- }
258
-
259
- /**
260
- * Update rewrite_rules
261
- * @return bool
262
- */
263
- protected function step3()
264
- {
265
-
266
- $this->log("Search & Replace: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}");
267
-
268
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
269
- return true;
270
- }
271
-
272
- $result = $this->db->query(
273
- $this->db->prepare(
274
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'",
275
- ' '
276
- )
277
- );
278
-
279
- // All good
280
- if ($result)
281
- {
282
- return true;
283
- }
284
-
285
- $this->log("Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
286
- return true;
287
- }
288
-
289
- /**
290
- * Update Table Prefix in meta_keys
291
- * @return bool
292
- */
293
- protected function step4() {
294
- $this->log( "Search & Replace: Updating {$this->prefix}usermeta db prefix {$this->db->last_error}" );
295
-
296
- if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
297
- return true;
298
- }
299
-
300
- $resultOptions = $this->db->query(
301
- $this->db->prepare(
302
- "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
303
- )
304
- );
305
-
306
- if( !$resultOptions ) {
307
- $this->log( "Search & Replace: Failed to update usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
308
- return false;
309
- }
310
-
311
- $this->log( "Updating {$this->prefix}options, option_name database table prefixes; {$this->db->last_error}" );
312
-
313
- $resultUserMeta = $this->db->query(
314
- $this->db->prepare(
315
- "UPDATE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
316
- )
317
- );
318
-
319
- if( !$resultUserMeta ) {
320
- $this->log( "Search & Replace: Failed to update options, option_name database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
321
- return false;
322
- }
323
-
324
- return true;
325
- }
326
-
327
- /**
328
- * Update $table_prefix in wp-config.php
329
- * @return bool
330
- */
331
- protected function step5()
332
- {
333
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
334
-
335
- $this->log("Search & Replace: Updating table_prefix in {$path} to " . $this->prefix);
336
- if (false === ($content = file_get_contents($path)))
337
- {
338
- $this->log("Search & Replace: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR);
339
- return false;
340
- }
341
-
342
- // Replace table prefix
343
- $content = str_replace('$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content);
344
-
345
- // Replace URLs
346
- $content = str_replace(get_home_url(), get_home_url() . '/' . $this->options->cloneDirectoryName, $content);
347
-
348
- if (false === @file_put_contents($path, $content))
349
- {
350
- $this->log("Search & Replace: Failed to update $table_prefix in {$path} to " .$this->prefix . ". Can't save contents", Logger::TYPE_ERROR);
351
- return false;
352
- }
353
-
354
- return true;
355
- }
356
-
357
- /**
358
- * Reset index.php to original file
359
- * Check first if main wordpress is used in subfolder and index.php in parent directory
360
- * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
361
- * @return bool
362
- */
363
- protected function step6()
364
- {
365
- // No settings, all good
366
- if (!isset($this->settings->wpSubDirectory) || "1" !== $this->settings->wpSubDirectory)
367
- {
368
- $this->log("Search & Replace: WP installation is not in a subdirectory! All good, skipping this step");
369
- return true;
370
- }
371
-
372
- $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
373
-
374
- if (false === ($content = file_get_contents($path)))
375
- {
376
- $this->log("Search & Replace: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR);
377
- return false;
378
- }
379
-
380
-
381
- if (!preg_match("/(require(.*)wp-blog-header.php' \);)/", $content, $matches))
382
- {
383
- $this->log(
384
- "Search & Replace: Failed to reset index.php for sub directory; wp-blog-header.php is missing",
385
- Logger::TYPE_ERROR
386
- );
387
- return false;
388
- }
389
-
390
- $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
391
-
392
- $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
393
- $replace.= " // Changed by WP-Staging";
394
-
395
- if (null === preg_replace($pattern, $replace, $content))
396
- {
397
- $this->log("Search & Replace: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR);
398
- return false;
399
- }
400
-
401
- if (false === @file_put_contents($path, $content))
402
- {
403
- $this->log("Search & Replace: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR);
404
- return false;
405
- }
406
-
407
- return true;
408
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ // No Direct Access
5
+ if (!defined("WPINC"))
6
+ {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\Utils\Logger;
11
+ use WPStaging\WPStaging;
12
+
13
+ /**
14
+ * Class Data
15
+ * @package WPStaging\Backend\Modules\Jobs
16
+ */
17
+ class Data extends JobExecutable
18
+ {
19
+
20
+ /**
21
+ * @var \wpdb
22
+ */
23
+ private $db;
24
+
25
+ /**
26
+ * @var string
27
+ */
28
+ private $prefix;
29
+
30
+ /**
31
+ * Initialize
32
+ */
33
+ public function initialize()
34
+ {
35
+ $this->db = WPStaging::getInstance()->get("wpdb");
36
+
37
+ $this->prefix = $this->options->prefix;
38
+
39
+ // Fix current step
40
+ if (0 == $this->options->currentStep)
41
+ {
42
+ $this->options->currentStep = 1;
43
+ }
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 = 6;
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->saveOptions();
66
+
67
+ // Prepare response
68
+ // $this->response = array(
69
+ // "status" => true,
70
+ // "percentage" => 100,
71
+ // "total" => $this->options->totalSteps,
72
+ // "step" => $this->options->totalSteps,
73
+ // "last_msg" => $this->logger->getLastLogMsg(),
74
+ // "running_time" => $this->time() - time(),
75
+ // "job_done" => true
76
+ // );
77
+
78
+ return (object) $this->response;
79
+ }
80
+
81
+ /**
82
+ * Execute the Current Step
83
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
84
+ * @return bool
85
+ */
86
+ protected function execute()
87
+ {
88
+ // Fatal error. Let this happen never and break here immediately
89
+ if ($this->isRoot()){
90
+ return false;
91
+ }
92
+
93
+ // Over limits threshold
94
+ if ($this->isOverThreshold())
95
+ {
96
+ // Prepare response and save current progress
97
+ $this->prepareResponse(false, false);
98
+ $this->saveOptions();
99
+ return false;
100
+ }
101
+
102
+ // No more steps, finished
103
+ if ($this->isFinished())
104
+ {
105
+ $this->prepareResponse(true, false);
106
+ return false;
107
+ }
108
+
109
+ // Execute step
110
+ $stepMethodName = "step" . $this->options->currentStep;
111
+ if (!$this->{$stepMethodName}())
112
+ {
113
+ $this->prepareResponse(false, false);
114
+ return false;
115
+ }
116
+
117
+ // Prepare Response
118
+ $this->prepareResponse();
119
+
120
+ // Not finished
121
+ return true;
122
+ }
123
+
124
+ /**
125
+ * Checks Whether There is Any Job to Execute or Not
126
+ * @return bool
127
+ */
128
+ private function isFinished()
129
+ {
130
+ return (
131
+ $this->options->currentStep > $this->options->totalSteps ||
132
+ !method_exists($this, "step" . $this->options->currentStep)
133
+ );
134
+ }
135
+
136
+ /**
137
+ * Check if current operation is done on the root folder or on the live DB
138
+ * @return boolean
139
+ */
140
+ private function isRoot(){
141
+
142
+ // Prefix is the same as the one of live site
143
+ $wpdb = WPStaging::getInstance()->get("wpdb");
144
+ if ($wpdb->prefix === $this->prefix){
145
+ return true;
146
+ }
147
+
148
+ // CloneName is empty
149
+ $name = (array)$this->options->cloneDirectoryName;
150
+ if (empty($name)){
151
+ return true;
152
+ }
153
+
154
+ // Live Path === Staging path
155
+ if (get_home_url() . $this->options->cloneDirectoryName === get_home_url()){
156
+ return true;
157
+ }
158
+
159
+ return false;
160
+ }
161
+
162
+
163
+ /**
164
+ * Check if table exists
165
+ * @param string $table
166
+ * @return boolean
167
+ */
168
+ protected function isTable($table){
169
+ if($this->db->get_var("SHOW TABLES LIKE '{$table}'") != $table ){
170
+ $this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
171
+ return false;
172
+ }
173
+ return true;
174
+ }
175
+
176
+ /**
177
+ * Replace "siteurl"
178
+ * @return bool
179
+ */
180
+ protected function step1() {
181
+ $this->log( "Search & Replace: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
182
+
183
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
184
+ return true;
185
+ }
186
+
187
+ // Installed in sub-directory
188
+ //if( isset( $this->settings->wpSubDirectory ) && "1" === $this->settings->wpSubDirectory ) {
189
+ if( $this->isSubDir() ) {
190
+ $subDirectory = str_replace( get_home_path(), '', ABSPATH );
191
+ $this->log( "Updating siteurl and homeurl to " . get_home_url() . '/' . $subDirectory . $this->options->cloneDirectoryName );
192
+ // Replace URLs
193
+ $result = $this->db->query(
194
+ $this->db->prepare(
195
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", get_home_url() . '/' . $subDirectory . $this->options->cloneDirectoryName
196
+ )
197
+ );
198
+ } else {
199
+ $this->log( "Search & Replace:: Updating siteurl and homeurl to " . get_home_url() . '/' . $this->options->cloneDirectoryName );
200
+ // Replace URLs
201
+ $result = $this->db->query(
202
+ $this->db->prepare(
203
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", get_home_url() . '/' . $this->options->cloneDirectoryName
204
+ )
205
+ );
206
+ }
207
+
208
+
209
+ // All good
210
+ if( $result ) {
211
+ return true;
212
+ }
213
+
214
+ $this->log( "Search & Replace: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
215
+ return false;
216
+ }
217
+
218
+ /**
219
+ * Update "wpstg_is_staging_site"
220
+ * @return bool
221
+ */
222
+ protected function step2()
223
+ {
224
+
225
+ $this->log( "Search & Replace: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
226
+
227
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
228
+ return true;
229
+ }
230
+
231
+ $result = $this->db->query(
232
+ $this->db->prepare(
233
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'",
234
+ "true"
235
+ )
236
+ );
237
+
238
+ // No errors but no option name such as wpstg_is_staging_site
239
+ if ('' === $this->db->last_error && 0 == $result)
240
+ {
241
+ $result = $this->db->query(
242
+ $this->db->prepare(
243
+ "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)",
244
+ "true"
245
+ )
246
+ );
247
+ }
248
+
249
+ // All good
250
+ if ($result)
251
+ {
252
+ return true;
253
+ }
254
+
255
+ $this->log("Search & Replace: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
256
+ return false;
257
+ }
258
+
259
+ /**
260
+ * Update rewrite_rules
261
+ * @return bool
262
+ */
263
+ protected function step3()
264
+ {
265
+
266
+ $this->log("Search & Replace: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}");
267
+
268
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
269
+ return true;
270
+ }
271
+
272
+ $result = $this->db->query(
273
+ $this->db->prepare(
274
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'",
275
+ ' '
276
+ )
277
+ );
278
+
279
+ // All good
280
+ if ($result)
281
+ {
282
+ return true;
283
+ }
284
+
285
+ $this->log("Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
286
+ return true;
287
+ }
288
+
289
+ /**
290
+ * Update Table Prefix in meta_keys
291
+ * @return bool
292
+ */
293
+ protected function step4() {
294
+ $this->log( "Search & Replace: Updating {$this->prefix}usermeta db prefix {$this->db->last_error}" );
295
+
296
+ if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
297
+ return true;
298
+ }
299
+
300
+ $resultOptions = $this->db->query(
301
+ $this->db->prepare(
302
+ "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
303
+ )
304
+ );
305
+
306
+ if( !$resultOptions ) {
307
+ $this->log( "Search & Replace: Failed to update usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
308
+ return false;
309
+ }
310
+
311
+ $this->log( "Updating {$this->prefix}options, option_name database table prefixes; {$this->db->last_error}" );
312
+
313
+ $resultUserMeta = $this->db->query(
314
+ $this->db->prepare(
315
+ "UPDATE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
316
+ )
317
+ );
318
+
319
+ if( !$resultUserMeta ) {
320
+ $this->log( "Search & Replace: Failed to update options, option_name database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
321
+ return false;
322
+ }
323
+
324
+ return true;
325
+ }
326
+
327
+ /**
328
+ * Update $table_prefix in wp-config.php
329
+ * @return bool
330
+ */
331
+ protected function step5()
332
+ {
333
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
334
+
335
+ $this->log("Search & Replace: Updating table_prefix in {$path} to " . $this->prefix);
336
+ if (false === ($content = file_get_contents($path)))
337
+ {
338
+ $this->log("Search & Replace: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR);
339
+ return false;
340
+ }
341
+
342
+ // Replace table prefix
343
+ $content = str_replace('$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content);
344
+
345
+ // Replace URLs
346
+ $content = str_replace(get_home_url(), get_home_url() . '/' . $this->options->cloneDirectoryName, $content);
347
+
348
+ if (false === @file_put_contents($path, $content))
349
+ {
350
+ $this->log("Search & Replace: Failed to update $table_prefix in {$path} to " .$this->prefix . ". Can't save contents", Logger::TYPE_ERROR);
351
+ return false;
352
+ }
353
+
354
+ return true;
355
+ }
356
+
357
+ /**
358
+ * Reset index.php to original file
359
+ * This is needed if live site is located in subfolder
360
+ * Check first if main wordpress is used in subfolder and index.php in parent directory
361
+ * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
362
+ * @return bool
363
+ */
364
+ protected function step6()
365
+ {
366
+ // No settings, all good
367
+ // if (!isset($this->settings->wpSubDirectory) || "1" !== $this->settings->wpSubDirectory)
368
+ // {
369
+ // $this->log("Search & Replace: WP installation is not in a subdirectory! All good, skipping this step");
370
+ // return true;
371
+ // }
372
+ if (!$this->isSubDir())
373
+ {
374
+ $this->debugLog("Search & Replace: WP installation is not in a subdirectory! All good, skipping this step");
375
+ return true;
376
+ }
377
+
378
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
379
+
380
+ if (false === ($content = file_get_contents($path)))
381
+ {
382
+ $this->log("Search & Replace: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR);
383
+ return false;
384
+ }
385
+
386
+
387
+ if (!preg_match("/(require(.*)wp-blog-header.php' \);)/", $content, $matches))
388
+ {
389
+ $this->log(
390
+ "Search & Replace: Failed to reset index.php for sub directory; wp-blog-header.php is missing",
391
+ Logger::TYPE_ERROR
392
+ );
393
+ return false;
394
+ }
395
+ $this->log("Search & Replace: WP installation is in a subdirectory. Progressing...");
396
+
397
+ $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
398
+
399
+ $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
400
+ $replace.= " // Changed by WP-Staging";
401
+
402
+
403
+
404
+ if (null === ($content = preg_replace(array($pattern), $replace, $content)))
405
+ {
406
+ $this->log("Search & Replace: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR);
407
+ return false;
408
+ }
409
+
410
+ if (false === @file_put_contents($path, $content))
411
+ {
412
+ $this->log("Search & Replace: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR);
413
+ return false;
414
+ }
415
+ $this->Log("Search & Replace: Finished Step 6 successfully");
416
+
417
+ return true;
418
+ }
419
+
420
+ /**
421
+ * Update wpstg_rmpermalinks_executed
422
+ * @return bool
423
+ */
424
+ protected function step7()
425
+ {
426
+
427
+ $this->log("Search & Replace: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}");
428
+
429
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
430
+ return true;
431
+ }
432
+
433
+ $result = $this->db->query(
434
+ $this->db->prepare(
435
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'",
436
+ ' '
437
+ )
438
+ );
439
+
440
+ // All good
441
+ if ($result)
442
+ {
443
+ return true;
444
+ }
445
+
446
+ $this->log("Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING);
447
+ return true;
448
+ }
449
+
450
+ /**
451
+ * Check if WP is installed in subdir
452
+ * @return boolean
453
+ */
454
+ protected function isSubDir(){
455
+ if ( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
456
+ return true;
457
+ }
458
+ return false;
459
+ }
460
  }
apps/Backend/Modules/Jobs/Database.php CHANGED
@@ -1,263 +1,265 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
- use WPStaging\WPStaging;
11
-
12
- /**
13
- * Class Database
14
- * @package WPStaging\Backend\Modules\Jobs
15
- */
16
- class Database extends JobExecutable
17
- {
18
-
19
- /**
20
- * @var int
21
- */
22
- private $total = 0;
23
-
24
- /**
25
- * @var \WPDB
26
- */
27
- private $db;
28
-
29
- /**
30
- * Initialize
31
- */
32
- public function initialize()
33
- {
34
- // Variables
35
- $this->total = count($this->options->tables);
36
- $this->db = WPStaging::getInstance()->get("wpdb");
37
- }
38
-
39
- /**
40
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
41
- * @return void
42
- */
43
- protected function calculateTotalSteps()
44
- {
45
- $this->options->totalSteps = $this->total;
46
- }
47
-
48
- /**
49
- * Execute the Current Step
50
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
51
- * @return bool
52
- */
53
- protected function execute()
54
- {
55
- // Over limits threshold
56
- if ($this->isOverThreshold())
57
- {
58
- // Prepare response and save current progress
59
- $this->prepareResponse(false, false);
60
- $this->saveOptions();
61
- return false;
62
- }
63
-
64
- // No more steps, finished
65
- if ($this->options->currentStep > $this->total || !isset($this->options->tables[$this->options->currentStep]))
66
- {
67
- $this->prepareResponse(true, false);
68
- return false;
69
- }
70
-
71
- // Table is excluded
72
- if (in_array($this->options->tables[$this->options->currentStep]->name, $this->options->excludedTables))
73
- {
74
- $this->prepareResponse();
75
- return true;
76
- }
77
-
78
- // Copy table
79
- if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
80
- {
81
- // Prepare Response
82
- $this->prepareResponse(false, false);
83
-
84
- // Not finished
85
- return true;
86
- }
87
-
88
- // Prepare Response
89
- $this->prepareResponse();
90
-
91
- // Not finished
92
- return true;
93
- }
94
-
95
- /**
96
- * Get new prefix for the staging site
97
- * @return string
98
- */
99
- private function getStagingPrefix(){
100
- $stagingPrefix = $this->options->prefix;
101
- // Make sure prefix of staging site is NEVER identical to prefix of live site!
102
- if ( $stagingPrefix == $this->db->prefix ){
103
- wp_die('Fatal error 7: The new database table prefix '. $stagingPrefix .' would be identical to the table prefix of the live site. Please open a support ticket to support@wp-staging.com');
104
- }
105
- return $stagingPrefix;
106
- }
107
-
108
- /**
109
- * No worries, SQL queries don't eat from PHP execution time!
110
- * @param string $tableName
111
- * @return bool
112
- */
113
- private function copyTable($tableName)
114
- {
115
- //$this->returnException($this->getStagingPrefix());
116
-
117
- //$newTableName = "wpstg{$this->options->cloneNumber}_" . str_replace($this->db->prefix, null, $tableName);
118
- $newTableName = $this->getStagingPrefix() . str_replace($this->db->prefix, null, $tableName);
119
-
120
- // Drop table if necessary
121
- $this->dropTable($newTableName);
122
-
123
- // Save current job
124
- $this->setJob($newTableName);
125
-
126
- // Beginning of the job
127
- if (!$this->startJob($newTableName, $tableName))
128
- {
129
- return true;
130
- }
131
-
132
- // Copy data
133
- $this->copyData($newTableName, $tableName);
134
-
135
- // Finis the step
136
- return $this->finishStep();
137
- }
138
-
139
- /**
140
- * Copy data from old table to new table
141
- * @param string $new
142
- * @param string $old
143
- */
144
- private function copyData($new, $old)
145
- {
146
- $rows = $this->options->job->start+$this->settings->queryLimit;
147
- $this->log(
148
- "DB Copy: {$old} as {$new} between {$this->options->job->start} to {$rows} records"
149
- );
150
-
151
- $limitation = '';
152
-
153
- if (0 < (int) $this->settings->queryLimit)
154
- {
155
- $limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
156
- }
157
-
158
- $this->db->query(
159
- "INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
160
- );
161
-
162
- // Set new offset
163
- $this->options->job->start += $this->settings->queryLimit;
164
- }
165
-
166
- /**
167
- * Set the job
168
- * @param string $table
169
- */
170
- private function setJob($table)
171
- {
172
- if (isset($this->options->job->current))
173
- {
174
- return;
175
- }
176
-
177
- $this->options->job->current = $table;
178
- $this->options->job->start = 0;
179
- }
180
-
181
- /**
182
- * Start Job
183
- * @param string $new
184
- * @param string $old
185
- * @return bool
186
- */
187
- private function startJob($new, $old)
188
- {
189
- if (0 != $this->options->job->start)
190
- {
191
- return true;
192
- }
193
-
194
- $this->log("DB Copy: Creating table {$new} like {$old}");
195
-
196
- $this->db->query("CREATE TABLE {$new} LIKE {$old}");
197
-
198
- $this->options->job->total = (int) $this->db->get_var("SELECT COUNT(1) FROM {$old}");
199
-
200
- if (0 == $this->options->job->total)
201
- {
202
- $this->finishStep();
203
- return false;
204
- }
205
-
206
- return true;
207
- }
208
-
209
- /**
210
- * Finish the step
211
- */
212
- private function finishStep()
213
- {
214
- // This job is not finished yet
215
- if ($this->options->job->total > $this->options->job->start)
216
- {
217
- return false;
218
- }
219
-
220
- // Add it to cloned tables listing
221
- $this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
222
-
223
- // Reset job
224
- $this->options->job = new \stdClass();
225
-
226
- return true;
227
- }
228
-
229
- /**
230
- * Drop table if necessary
231
- * @param string $new
232
- */
233
- private function dropTable($new)
234
- {
235
- $old = $this->db->get_var($this->db->prepare("SHOW TABLES LIKE %s", $new));
236
-
237
- if (!$this->shouldDropTable($new, $old))
238
- {
239
- return;
240
- }
241
-
242
- $this->log("DB Copy: {$new} already exists, dropping it first");
243
- $this->db->query("DROP TABLE {$new}");
244
- }
245
-
246
- /**
247
- * Check if table needs to be dropped
248
- * @param string $new
249
- * @param string $old
250
- * @return bool
251
- */
252
- private function shouldDropTable($new, $old)
253
- {
254
- return (
255
- $old === $new &&
256
- (
257
- !isset($this->options->job->current) ||
258
- !isset($this->options->job->start) ||
259
- 0 == $this->options->job->start
260
- )
261
- );
262
- }
 
 
263
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ // No Direct Access
5
+ if (!defined("WPINC"))
6
+ {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\WPStaging;
11
+ use WPStaging\Utils\Strings;
12
+
13
+ /**
14
+ * Class Database
15
+ * @package WPStaging\Backend\Modules\Jobs
16
+ */
17
+ class Database extends JobExecutable
18
+ {
19
+
20
+ /**
21
+ * @var int
22
+ */
23
+ private $total = 0;
24
+
25
+ /**
26
+ * @var \WPDB
27
+ */
28
+ private $db;
29
+
30
+ /**
31
+ * Initialize
32
+ */
33
+ public function initialize()
34
+ {
35
+ // Variables
36
+ $this->total = count($this->options->tables);
37
+ $this->db = WPStaging::getInstance()->get("wpdb");
38
+ }
39
+
40
+ /**
41
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
42
+ * @return void
43
+ */
44
+ protected function calculateTotalSteps()
45
+ {
46
+ $this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
47
+ }
48
+
49
+ /**
50
+ * Execute the Current Step
51
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
52
+ * @return bool
53
+ */
54
+ protected function execute()
55
+ {
56
+ // Over limits threshold
57
+ if ($this->isOverThreshold())
58
+ {
59
+ // Prepare response and save current progress
60
+ $this->prepareResponse(false, false);
61
+ $this->saveOptions();
62
+ return false;
63
+ }
64
+
65
+ // No more steps, finished
66
+ if ($this->options->currentStep > $this->total || !isset($this->options->tables[$this->options->currentStep]))
67
+ {
68
+ $this->prepareResponse(true, false);
69
+ return false;
70
+ }
71
+
72
+ // Table is excluded
73
+ // if (in_array($this->options->tables[$this->options->currentStep]->name, $this->options->excludedTables))
74
+ // {
75
+ // $this->prepareResponse();
76
+ // return true;
77
+ // }
78
+
79
+ // Copy table
80
+ //if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
81
+ if (!$this->copyTable($this->options->tables[$this->options->currentStep]))
82
+ {
83
+ // Prepare Response
84
+ $this->prepareResponse(false, false);
85
+
86
+ // Not finished
87
+ return true;
88
+ }
89
+
90
+ // Prepare Response
91
+ $this->prepareResponse();
92
+
93
+ // Not finished
94
+ return true;
95
+ }
96
+
97
+ /**
98
+ * Get new prefix for the staging site
99
+ * @return string
100
+ */
101
+ private function getStagingPrefix(){
102
+ $stagingPrefix = $this->options->prefix;
103
+ // Make sure prefix of staging site is NEVER identical to prefix of live site!
104
+ if ( $stagingPrefix == $this->db->prefix ){
105
+ wp_die('Fatal error 7: The new database table prefix '. $stagingPrefix .' would be identical to the table prefix of the live site. Please open a support ticket to support@wp-staging.com');
106
+ }
107
+ return $stagingPrefix;
108
+ }
109
+
110
+ /**
111
+ * No worries, SQL queries don't eat from PHP execution time!
112
+ * @param string $tableName
113
+ * @return bool
114
+ */
115
+ private function copyTable($tableName)
116
+ {
117
+
118
+ $strings = new Strings();
119
+ $tableName = is_object($tableName) ? $tableName->name : $tableName;
120
+ $newTableName = $this->getStagingPrefix() . $strings->str_replace_first($this->db->prefix, null, $tableName);
121
+
122
+ // Drop table if necessary
123
+ $this->dropTable($newTableName);
124
+
125
+ // Save current job
126
+ $this->setJob($newTableName);
127
+
128
+ // Beginning of the job
129
+ if (!$this->startJob($newTableName, $tableName))
130
+ {
131
+ return true;
132
+ }
133
+
134
+ // Copy data
135
+ $this->copyData($newTableName, $tableName);
136
+
137
+ // Finis the step
138
+ return $this->finishStep();
139
+ }
140
+
141
+ /**
142
+ * Copy data from old table to new table
143
+ * @param string $new
144
+ * @param string $old
145
+ */
146
+ private function copyData($new, $old)
147
+ {
148
+ $rows = $this->options->job->start+$this->settings->queryLimit;
149
+ $this->log(
150
+ "DB Copy: {$old} as {$new} between {$this->options->job->start} to {$rows} records"
151
+ );
152
+
153
+ $limitation = '';
154
+
155
+ if (0 < (int) $this->settings->queryLimit)
156
+ {
157
+ $limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
158
+ }
159
+
160
+ $this->db->query(
161
+ "INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
162
+ );
163
+
164
+ // Set new offset
165
+ $this->options->job->start += $this->settings->queryLimit;
166
+ }
167
+
168
+ /**
169
+ * Set the job
170
+ * @param string $table
171
+ */
172
+ private function setJob($table)
173
+ {
174
+ if (isset($this->options->job->current))
175
+ {
176
+ return;
177
+ }
178
+
179
+ $this->options->job->current = $table;
180
+ $this->options->job->start = 0;
181
+ }
182
+
183
+ /**
184
+ * Start Job
185
+ * @param string $new
186
+ * @param string $old
187
+ * @return bool
188
+ */
189
+ private function startJob($new, $old)
190
+ {
191
+ if (0 != $this->options->job->start)
192
+ {
193
+ return true;
194
+ }
195
+
196
+ $this->log("DB Copy: Creating table {$new} like {$old}");
197
+
198
+ $this->db->query("CREATE TABLE {$new} LIKE {$old}");
199
+
200
+ $this->options->job->total = (int) $this->db->get_var("SELECT COUNT(1) FROM {$old}");
201
+
202
+ if (0 == $this->options->job->total)
203
+ {
204
+ $this->finishStep();
205
+ return false;
206
+ }
207
+
208
+ return true;
209
+ }
210
+
211
+ /**
212
+ * Finish the step
213
+ */
214
+ private function finishStep()
215
+ {
216
+ // This job is not finished yet
217
+ if ($this->options->job->total > $this->options->job->start)
218
+ {
219
+ return false;
220
+ }
221
+
222
+ // Add it to cloned tables listing
223
+ $this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
224
+
225
+ // Reset job
226
+ $this->options->job = new \stdClass();
227
+
228
+ return true;
229
+ }
230
+
231
+ /**
232
+ * Drop table if necessary
233
+ * @param string $new
234
+ */
235
+ private function dropTable($new)
236
+ {
237
+ $old = $this->db->get_var($this->db->prepare("SHOW TABLES LIKE %s", $new));
238
+
239
+ if (!$this->shouldDropTable($new, $old))
240
+ {
241
+ return;
242
+ }
243
+
244
+ $this->log("DB Copy: {$new} already exists, dropping it first");
245
+ $this->db->query("DROP TABLE {$new}");
246
+ }
247
+
248
+ /**
249
+ * Check if table needs to be dropped
250
+ * @param string $new
251
+ * @param string $old
252
+ * @return bool
253
+ */
254
+ private function shouldDropTable($new, $old)
255
+ {
256
+ return (
257
+ $old === $new &&
258
+ (
259
+ !isset($this->options->job->current) ||
260
+ !isset($this->options->job->start) ||
261
+ 0 == $this->options->job->start
262
+ )
263
+ );
264
+ }
265
  }
apps/Backend/Modules/Jobs/Delete.php CHANGED
@@ -1,497 +1,497 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs;
4
-
5
- use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
6
- use WPStaging\Utils\Directories;
7
- use WPStaging\Utils\Logger;
8
- use WPStaging\WPStaging;
9
-
10
- /**
11
- * Class Delete
12
- * @package WPStaging\Backend\Modules\Jobs
13
- */
14
- class Delete extends Job {
15
-
16
- /**
17
- * @var false
18
- */
19
- private $clone = false;
20
-
21
- /**
22
- * @var null|object
23
- */
24
- private $tables = null;
25
-
26
- /**
27
- * @var object|null
28
- */
29
- private $job = null;
30
-
31
- /**
32
- * @var bool
33
- */
34
- private $forceDeleteDirectories = false;
35
-
36
- /**
37
- *
38
- * @var object
39
- */
40
- public $wpdb;
41
-
42
- public function __construct() {
43
- parent::__construct();
44
- $this->wpdb = WPStaging::getInstance()->get("wpdb");
45
- }
46
-
47
- /**
48
- * Sets Clone and Table Records
49
- * @param null|array $clone
50
- */
51
- public function setData($clone = null) {
52
- if (!is_array($clone)) {
53
- $this->getCloneRecords();
54
- } else {
55
- $this->clone = (object) $clone;
56
- $this->forceDeleteDirectories = true;
57
- }
58
-
59
- $this->getTableRecords();
60
- }
61
-
62
- /**
63
- * Get clone
64
- * @param null|string $name
65
- * @throws CloneNotFoundException
66
- */
67
- private function getCloneRecords($name = null) {
68
- if (null === $name && !isset($_POST["clone"])) {
69
- $this->log("Clone name is not set", Logger::TYPE_FATAL);
70
- throw new CloneNotFoundException();
71
- }
72
-
73
- if (null === $name) {
74
- $name = $_POST["clone"];
75
- }
76
-
77
- $clones = get_option("wpstg_existing_clones_beta", array());
78
-
79
- if (empty($clones) || !isset($clones[$name])) {
80
- $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
81
- throw new CloneNotFoundException();
82
- }
83
-
84
- $this->clone = $clones[$name];
85
- $this->clone["name"] = $name;
86
-
87
- $this->clone = (object) $this->clone;
88
-
89
- unset($clones);
90
- }
91
-
92
- /**
93
- * Get Tables
94
- */
95
- private function getTableRecords() {
96
- //$wpdb = WPStaging::getInstance()->get("wpdb");
97
- $this->wpdb = WPStaging::getInstance()->get("wpdb");
98
-
99
- $stagingPrefix = $this->getStagingPrefix();
100
-
101
- $tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$stagingPrefix}%'");
102
-
103
- $this->tables = array();
104
-
105
- foreach ($tables as $table) {
106
- $this->tables[] = array(
107
- "name" => $table->Name,
108
- "size" => $this->formatSize(($table->Data_length + $table->Index_length))
109
- );
110
- }
111
-
112
- $this->tables = json_decode(json_encode($this->tables));
113
- }
114
-
115
- /**
116
- * Check and return prefix of the staging site
117
- */
118
- public function getStagingPrefix() {
119
- // Prefix not defined! Happens if staging site has ben generated with older version of wpstg
120
- // Try to get staging prefix from wp-config.php of staging site
121
- if (empty($this->clone->prefix)) {
122
- // Throw error
123
- $path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
124
- if (false === ($content = @file_get_contents($path))) {
125
- $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
126
- // Create a random prefix which hopefully never exists.
127
- $this->clone->prefix = rand(7, 15) . '_';
128
- } else {
129
-
130
- // Get prefix from wp-config.php
131
- //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
132
- preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
133
- //wp_die(var_dump($matches));
134
-
135
- if (!empty($matches[1])) {
136
- $this->clone->prefix = $matches[1];
137
- } else {
138
- $this->log("Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
139
- // Create a random prefix which hopefully never exists.
140
- return $this->clone->prefix = rand(7, 15) . '_';
141
- }
142
- }
143
- }
144
-
145
- // Check if staging prefix is the same as the live prefix
146
- if ($this->wpdb->prefix == $this->clone->prefix) {
147
- $this->log("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
148
- wp_die("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
149
- }
150
-
151
- // Else
152
- return $this->clone->prefix;
153
- }
154
-
155
- /**
156
- * Format bytes into human readable form
157
- * @param int $bytes
158
- * @param int $precision
159
- * @return string
160
- */
161
- public function formatSize($bytes, $precision = 2) {
162
- if ((int) $bytes < 1) {
163
- return '';
164
- }
165
-
166
- $units = array('B', "KB", "MB", "GB", "TB");
167
-
168
- $bytes = (int) $bytes;
169
- $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
170
- $pow = pow(1000, $base - floor($base)); // Same rule for 1000
171
-
172
- return round($pow, $precision) . ' ' . $units[(int) floor($base)];
173
- }
174
-
175
- /**
176
- * @return false
177
- */
178
- public function getClone() {
179
- return $this->clone;
180
- }
181
-
182
- /**
183
- * @return null|object
184
- */
185
- public function getTables() {
186
- return $this->tables;
187
- }
188
-
189
- /**
190
- * Start Module
191
- * @param null|array $clone
192
- * @return bool
193
- */
194
- public function start($clone = null) {
195
- // Set data
196
- $this->setData($clone);
197
-
198
- // Get the job first
199
- $this->getJob();
200
-
201
- $method = "delete" . ucwords($this->job->current);
202
- return $this->{$method}();
203
- }
204
-
205
- /**
206
- * Get job data
207
- */
208
- private function getJob() {
209
- $this->job = $this->cache->get("delete_job_{$this->clone->name}");
210
-
211
-
212
- if (null !== $this->job) {
213
- return;
214
- }
215
-
216
- // Generate JOB
217
- $this->job = (object) array(
218
- "current" => "tables",
219
- "nextDirectoryToDelete" => $this->clone->path,
220
- "name" => $this->clone->name
221
- );
222
-
223
- $this->cache->save("delete_job_{$this->clone->name}", $this->job);
224
- }
225
-
226
- /**
227
- * @return bool
228
- */
229
- private function updateJob() {
230
- $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
231
- return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
232
- }
233
-
234
- /**
235
- * @return array
236
- */
237
- private function getTablesToRemove() {
238
- $tables = $this->getTableNames();
239
-
240
- if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"])) {
241
- return $tables;
242
- }
243
-
244
- return array_diff($tables, $_POST["excludedTables"]);
245
- }
246
-
247
- /**
248
- * @return array
249
- */
250
- private function getTableNames() {
251
- return (!is_array($this->tables)) ? array() : array_map(function($value) {
252
- return ($value->name);
253
- }, $this->tables);
254
- }
255
-
256
- /**
257
- * Delete Tables
258
- */
259
- public function deleteTables() {
260
- if ($this->isOverThreshold()) {
261
- return;
262
- }
263
-
264
- //$wpdb = WPStaging::getInstance()->get("wpdb");
265
-
266
- foreach ($this->getTablesToRemove() as $table) {
267
- // PROTECTION: Never delete any table that beginns with wp prefix of live site
268
- if ($this->startsWith($table, $this->wpdb->prefix)) {
269
- $this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
270
- return false;
271
- } else {
272
- $this->wpdb->query("DROP TABLE {$table}");
273
- }
274
- }
275
-
276
- // Move on to the next
277
- $this->job->current = "directory";
278
- $this->updateJob();
279
- }
280
-
281
- /**
282
- * Check if a strings start with a specific string
283
- * @param string $haystack
284
- * @param string $needle
285
- * @return bool
286
- */
287
- protected function startsWith($haystack, $needle) {
288
- $length = strlen($needle);
289
- return (substr($haystack, 0, $length) === $needle);
290
- }
291
-
292
- /**
293
- * Delete complete directory including all files and subfolders
294
- *
295
- * @throws InvalidArgumentException
296
- */
297
- public function deleteDirectory() {
298
- if ($this->isFatalError()) {
299
- $this->returnException('Can not delete directory: ' . $this->clone->path . '. This seems to be the root directory. Please contact support@wp-staging.com');
300
- throw new \Exception('Can not delete directory: ' . $this->clone->path . ' This seems to be the root directory. Please contact support@wp-staging.com');
301
- }
302
- // Finished or path does not exist
303
- if (
304
- empty($this->clone->path) ||
305
- $this->clone->path == get_home_path() ||
306
- !is_dir($this->clone->path)) {
307
-
308
- $this->job->current = "finish";
309
- $this->updateJob();
310
- return $this->returnFinish();
311
- }
312
-
313
- $this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
314
-
315
- // Just to make sure the root dir is never deleted!
316
- if ($this->clone->path == get_home_path()) {
317
- $this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
318
- $this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
319
- }
320
-
321
- // Check if threshold is reached
322
- if ($this->isOverThreshold()) {
323
- //$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
324
- return;
325
- }
326
-
327
- $di = new \RecursiveDirectoryIterator($this->clone->path, \FilesystemIterator::SKIP_DOTS);
328
- $ri = new \RecursiveIteratorIterator($di, \RecursiveIteratorIterator::CHILD_FIRST);
329
- foreach ($ri as $file) {
330
- //$file->isDir() ? @rmdir($file) : unlink($file);
331
- $this->deleteFile($file);
332
- if ($this->isOverThreshold()) {
333
- //$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
334
- return;
335
- }
336
- }
337
-
338
- if (@rmdir($this->clone->path)) {
339
- return $this->returnFinish();
340
- }
341
- return;
342
- }
343
-
344
- /**
345
- * Delete file
346
- * @param object iterator $file
347
- */
348
- private function deleteFile($file) {
349
- if ($file->isDir()) {
350
- if (!@rmdir($file)) {
351
- $this->returnException('Permission Error: Can not delete folder ' . $file);
352
- }
353
- } else {
354
- if (!unlink($file)) {
355
- $this->returnException('Permission Error: Can not delete file ' . $file);
356
- }
357
- }
358
- }
359
-
360
- /**
361
- * @return bool
362
- */
363
- public function isDirectoryDeletingFinished() {
364
- return (
365
- (false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
366
- !is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
367
- );
368
- }
369
-
370
- /**
371
- * Delete contents of the directory if there are no directories in it and then delete itself
372
- * @param string $path
373
- * @return mixed
374
- */
375
- // private function processDirectory($path) {
376
- // // We hit the limit, stop
377
- // if ($this->shouldStop($path)) {
378
- // $this->updateJob();
379
- // return false;
380
- // }
381
- //
382
- // $this->totalRecursion++;
383
- //
384
- // $contents = new \DirectoryIterator($path);
385
- //
386
- // foreach ($contents as $content => $value) {
387
- //
388
- // // Skip dots
389
- // if ($content->isDot())
390
- //
391
- //
392
- // // Get into the directory
393
- // if (!$content->isLink() && $content->isDir()) {
394
- // return $this->processDirectory($content->getRealPath());
395
- // }
396
- //
397
- // // Delete file
398
- // if ($content->isFile()) {
399
- // @unlink($content->getRealPath());
400
- // }
401
- // }
402
- //
403
- // // Delete directory
404
- // $this->job->lastDeletedDirectory = realpath($path . "/..");
405
- // @rmdir($path);
406
- // $this->updateJob();
407
- // $this->processDirectory($this->job->nextDirectoryToDelete);
408
- // }
409
-
410
- /**
411
- * @param string $path
412
- * @return bool
413
- */
414
- // private function shouldStop($path) {
415
- // // Just to make sure the root dir is never deleted!
416
- // if ($path === get_home_path()) {
417
- // $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
418
- // return true;
419
- // }
420
- //
421
- // // Check if threshold is reached and is valid dir
422
- // return (
423
- // $this->isOverThreshold() ||
424
- // !is_dir($path) ||
425
- // $this->isDirectoryDeletingFinished()
426
- // );
427
- // }
428
-
429
- /**
430
- *
431
- * @return boolean
432
- */
433
- public function isFatalError() {
434
- if (rtrim($this->clone->path, "/") == rtrim(get_home_path(), "/")) {
435
- return true;
436
- }
437
- return false;
438
- }
439
-
440
- /**
441
- * Finish / Update Existing Clones
442
- */
443
- public function deleteFinish() {
444
- $existingClones = get_option("wpstg_existing_clones_beta", array());
445
-
446
- // Check if clones still exist
447
- $this->log("Verifying existing clones...");
448
- foreach ($existingClones as $name => $clone) {
449
- if (!is_dir($clone["path"])) {
450
- unset($existingClones[$name]);
451
- }
452
- }
453
- $this->log("Existing clones verified!");
454
-
455
- if (false === update_option("wpstg_existing_clones_beta", $existingClones)) {
456
- $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
457
- }
458
-
459
- // Delete cached file
460
- $this->cache->delete("delete_job_{$this->clone->name}");
461
- $this->cache->delete("delete_directories_{$this->clone->name}");
462
-
463
- //return true;
464
- $response = array('delete' => 'finished');
465
- wp_die(json_encode($response));
466
- }
467
-
468
- /**
469
- * Get json response
470
- * return json
471
- */
472
- // private function returnException($message = ''){
473
- // wp_die( json_encode(array(
474
- // 'job' => 'delete',
475
- // 'status' => false,
476
- // 'message' => $message,
477
- // 'error' => true
478
- // )));
479
- // }
480
- /**
481
- * Get json response
482
- * return json
483
- */
484
- private function returnFinish($message = '') {
485
-
486
- $this->deleteFinish();
487
-
488
- wp_die(json_encode(array(
489
- 'job' => 'delete',
490
- 'status' => true,
491
- 'message' => $message,
492
- 'error' => false,
493
- 'delete' => 'finished'
494
- )));
495
- }
496
-
497
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs;
4
+
5
+ use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
6
+ use WPStaging\Utils\Directories;
7
+ use WPStaging\Utils\Logger;
8
+ use WPStaging\WPStaging;
9
+
10
+ /**
11
+ * Class Delete
12
+ * @package WPStaging\Backend\Modules\Jobs
13
+ */
14
+ class Delete extends Job {
15
+
16
+ /**
17
+ * @var false
18
+ */
19
+ private $clone = false;
20
+
21
+ /**
22
+ * @var null|object
23
+ */
24
+ private $tables = null;
25
+
26
+ /**
27
+ * @var object|null
28
+ */
29
+ private $job = null;
30
+
31
+ /**
32
+ * @var bool
33
+ */
34
+ private $forceDeleteDirectories = false;
35
+
36
+ /**
37
+ *
38
+ * @var object
39
+ */
40
+ public $wpdb;
41
+
42
+ public function __construct() {
43
+ parent::__construct();
44
+ $this->wpdb = WPStaging::getInstance()->get("wpdb");
45
+ }
46
+
47
+ /**
48
+ * Sets Clone and Table Records
49
+ * @param null|array $clone
50
+ */
51
+ public function setData($clone = null) {
52
+ if (!is_array($clone)) {
53
+ $this->getCloneRecords();
54
+ } else {
55
+ $this->clone = (object) $clone;
56
+ $this->forceDeleteDirectories = true;
57
+ }
58
+
59
+ $this->getTableRecords();
60
+ }
61
+
62
+ /**
63
+ * Get clone
64
+ * @param null|string $name
65
+ * @throws CloneNotFoundException
66
+ */
67
+ private function getCloneRecords($name = null) {
68
+ if (null === $name && !isset($_POST["clone"])) {
69
+ $this->log("Clone name is not set", Logger::TYPE_FATAL);
70
+ throw new CloneNotFoundException();
71
+ }
72
+
73
+ if (null === $name) {
74
+ $name = $_POST["clone"];
75
+ }
76
+
77
+ $clones = get_option("wpstg_existing_clones_beta", array());
78
+
79
+ if (empty($clones) || !isset($clones[$name])) {
80
+ $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
81
+ throw new CloneNotFoundException();
82
+ }
83
+
84
+ $this->clone = $clones[$name];
85
+ $this->clone["name"] = $name;
86
+
87
+ $this->clone = (object) $this->clone;
88
+
89
+ unset($clones);
90
+ }
91
+
92
+ /**
93
+ * Get Tables
94
+ */
95
+ private function getTableRecords() {
96
+ //$wpdb = WPStaging::getInstance()->get("wpdb");
97
+ $this->wpdb = WPStaging::getInstance()->get("wpdb");
98
+
99
+ $stagingPrefix = $this->getStagingPrefix();
100
+
101
+ $tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$stagingPrefix}%'");
102
+
103
+ $this->tables = array();
104
+
105
+ foreach ($tables as $table) {
106
+ $this->tables[] = array(
107
+ "name" => $table->Name,
108
+ "size" => $this->formatSize(($table->Data_length + $table->Index_length))
109
+ );
110
+ }
111
+
112
+ $this->tables = json_decode(json_encode($this->tables));
113
+ }
114
+
115
+ /**
116
+ * Check and return prefix of the staging site
117
+ */
118
+ public function getStagingPrefix() {
119
+ // Prefix not defined! Happens if staging site has ben generated with older version of wpstg
120
+ // Try to get staging prefix from wp-config.php of staging site
121
+ if (empty($this->clone->prefix)) {
122
+ // Throw error
123
+ $path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
124
+ if (false === ($content = @file_get_contents($path))) {
125
+ $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
126
+ // Create a random prefix which hopefully never exists.
127
+ $this->clone->prefix = rand(7, 15) . '_';
128
+ } else {
129
+
130
+ // Get prefix from wp-config.php
131
+ //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
132
+ preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
133
+ //wp_die(var_dump($matches));
134
+
135
+ if (!empty($matches[1])) {
136
+ $this->clone->prefix = $matches[1];
137
+ } else {
138
+ $this->log("Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
139
+ // Create a random prefix which hopefully never exists.
140
+ return $this->clone->prefix = rand(7, 15) . '_';
141
+ }
142
+ }
143
+ }
144
+
145
+ // Check if staging prefix is the same as the live prefix
146
+ if ($this->wpdb->prefix == $this->clone->prefix) {
147
+ $this->log("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
148
+ wp_die("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
149
+ }
150
+
151
+ // Else
152
+ return $this->clone->prefix;
153
+ }
154
+
155
+ /**
156
+ * Format bytes into human readable form
157
+ * @param int $bytes
158
+ * @param int $precision
159
+ * @return string
160
+ */
161
+ public function formatSize($bytes, $precision = 2) {
162
+ if ((int) $bytes < 1) {
163
+ return '';
164
+ }
165
+
166
+ $units = array('B', "KB", "MB", "GB", "TB");
167
+
168
+ $bytes = (int) $bytes;
169
+ $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
170
+ $pow = pow(1000, $base - floor($base)); // Same rule for 1000
171
+
172
+ return round($pow, $precision) . ' ' . $units[(int) floor($base)];
173
+ }
174
+
175
+ /**
176
+ * @return false
177
+ */
178
+ public function getClone() {
179
+ return $this->clone;
180
+ }
181
+
182
+ /**
183
+ * @return null|object
184
+ */
185
+ public function getTables() {
186
+ return $this->tables;
187
+ }
188
+
189
+ /**
190
+ * Start Module
191
+ * @param null|array $clone
192
+ * @return bool
193
+ */
194
+ public function start($clone = null) {
195
+ // Set data
196
+ $this->setData($clone);
197
+
198
+ // Get the job first
199
+ $this->getJob();
200
+
201
+ $method = "delete" . ucwords($this->job->current);
202
+ return $this->{$method}();
203
+ }
204
+
205
+ /**
206
+ * Get job data
207
+ */
208
+ private function getJob() {
209
+ $this->job = $this->cache->get("delete_job_{$this->clone->name}");
210
+
211
+
212
+ if (null !== $this->job) {
213
+ return;
214
+ }
215
+
216
+ // Generate JOB
217
+ $this->job = (object) array(
218
+ "current" => "tables",
219
+ "nextDirectoryToDelete" => $this->clone->path,
220
+ "name" => $this->clone->name
221
+ );
222
+
223
+ $this->cache->save("delete_job_{$this->clone->name}", $this->job);
224
+ }
225
+
226
+ /**
227
+ * @return bool
228
+ */
229
+ private function updateJob() {
230
+ $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
231
+ return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
232
+ }
233
+
234
+ /**
235
+ * @return array
236
+ */
237
+ private function getTablesToRemove() {
238
+ $tables = $this->getTableNames();
239
+
240
+ if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"])) {
241
+ return $tables;
242
+ }
243
+
244
+ return array_diff($tables, $_POST["excludedTables"]);
245
+ }
246
+
247
+ /**
248
+ * @return array
249
+ */
250
+ private function getTableNames() {
251
+ return (!is_array($this->tables)) ? array() : array_map(function($value) {
252
+ return ($value->name);
253
+ }, $this->tables);
254
+ }
255
+
256
+ /**
257
+ * Delete Tables
258
+ */
259
+ public function deleteTables() {
260
+ if ($this->isOverThreshold()) {
261
+ return;
262
+ }
263
+
264
+ //$wpdb = WPStaging::getInstance()->get("wpdb");
265
+
266
+ foreach ($this->getTablesToRemove() as $table) {
267
+ // PROTECTION: Never delete any table that beginns with wp prefix of live site
268
+ if ($this->startsWith($table, $this->wpdb->prefix)) {
269
+ $this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
270
+ return false;
271
+ } else {
272
+ $this->wpdb->query("DROP TABLE {$table}");
273
+ }
274
+ }
275
+
276
+ // Move on to the next
277
+ $this->job->current = "directory";
278
+ $this->updateJob();
279
+ }
280
+
281
+ /**
282
+ * Check if a strings start with a specific string
283
+ * @param string $haystack
284
+ * @param string $needle
285
+ * @return bool
286
+ */
287
+ protected function startsWith($haystack, $needle) {
288
+ $length = strlen($needle);
289
+ return (substr($haystack, 0, $length) === $needle);
290
+ }
291
+
292
+ /**
293
+ * Delete complete directory including all files and subfolders
294
+ *
295
+ * @throws InvalidArgumentException
296
+ */
297
+ public function deleteDirectory() {
298
+ if ($this->isFatalError()) {
299
+ $this->returnException('Can not delete directory: ' . $this->clone->path . '. This seems to be the root directory. Please contact support@wp-staging.com');
300
+ throw new \Exception('Can not delete directory: ' . $this->clone->path . ' This seems to be the root directory. Please contact support@wp-staging.com');
301
+ }
302
+ // Finished or path does not exist
303
+ if (
304
+ empty($this->clone->path) ||
305
+ $this->clone->path == get_home_path() ||
306
+ !is_dir($this->clone->path)) {
307
+
308
+ $this->job->current = "finish";
309
+ $this->updateJob();
310
+ return $this->returnFinish();
311
+ }
312
+
313
+ $this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
314
+
315
+ // Just to make sure the root dir is never deleted!
316
+ if ($this->clone->path == get_home_path()) {
317
+ $this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
318
+ $this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
319
+ }
320
+
321
+ // Check if threshold is reached
322
+ if ($this->isOverThreshold()) {
323
+ //$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
324
+ return;
325
+ }
326
+
327
+ $di = new \RecursiveDirectoryIterator($this->clone->path, \FilesystemIterator::SKIP_DOTS);
328
+ $ri = new \RecursiveIteratorIterator($di, \RecursiveIteratorIterator::CHILD_FIRST);
329
+ foreach ($ri as $file) {
330
+ //$file->isDir() ? @rmdir($file) : unlink($file);
331
+ $this->deleteFile($file);
332
+ if ($this->isOverThreshold()) {
333
+ //$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
334
+ return;
335
+ }
336
+ }
337
+
338
+ if (@rmdir($this->clone->path)) {
339
+ return $this->returnFinish();
340
+ }
341
+ return;
342
+ }
343
+
344
+ /**
345
+ * Delete file
346
+ * @param object iterator $file
347
+ */
348
+ private function deleteFile($file) {
349
+ if ($file->isDir()) {
350
+ if (!@rmdir($file)) {
351
+ $this->returnException('Permission Error: Can not delete folder ' . $file);
352
+ }
353
+ } else {
354
+ if (!unlink($file)) {
355
+ $this->returnException('Permission Error: Can not delete file ' . $file);
356
+ }
357
+ }
358
+ }
359
+
360
+ /**
361
+ * @return bool
362
+ */
363
+ public function isDirectoryDeletingFinished() {
364
+ return (
365
+ (false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
366
+ !is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
367
+ );
368
+ }
369
+
370
+ /**
371
+ * Delete contents of the directory if there are no directories in it and then delete itself
372
+ * @param string $path
373
+ * @return mixed
374
+ */
375
+ // private function processDirectory($path) {
376
+ // // We hit the limit, stop
377
+ // if ($this->shouldStop($path)) {
378
+ // $this->updateJob();
379
+ // return false;
380
+ // }
381
+ //
382
+ // $this->totalRecursion++;
383
+ //
384
+ // $contents = new \DirectoryIterator($path);
385
+ //
386
+ // foreach ($contents as $content => $value) {
387
+ //
388
+ // // Skip dots
389
+ // if ($content->isDot())
390
+ //
391
+ //
392
+ // // Get into the directory
393
+ // if (!$content->isLink() && $content->isDir()) {
394
+ // return $this->processDirectory($content->getRealPath());
395
+ // }
396
+ //
397
+ // // Delete file
398
+ // if ($content->isFile()) {
399
+ // @unlink($content->getRealPath());
400
+ // }
401
+ // }
402
+ //
403
+ // // Delete directory
404
+ // $this->job->lastDeletedDirectory = realpath($path . "/..");
405
+ // @rmdir($path);
406
+ // $this->updateJob();
407
+ // $this->processDirectory($this->job->nextDirectoryToDelete);
408
+ // }
409
+
410
+ /**
411
+ * @param string $path
412
+ * @return bool
413
+ */
414
+ // private function shouldStop($path) {
415
+ // // Just to make sure the root dir is never deleted!
416
+ // if ($path === get_home_path()) {
417
+ // $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
418
+ // return true;
419
+ // }
420
+ //
421
+ // // Check if threshold is reached and is valid dir
422
+ // return (
423
+ // $this->isOverThreshold() ||
424
+ // !is_dir($path) ||
425
+ // $this->isDirectoryDeletingFinished()
426
+ // );
427
+ // }
428
+
429
+ /**
430
+ *
431
+ * @return boolean
432
+ */
433
+ public function isFatalError() {
434
+ if (rtrim($this->clone->path, "/") == rtrim(get_home_path(), "/")) {
435
+ return true;
436
+ }
437
+ return false;
438
+ }
439
+
440
+ /**
441
+ * Finish / Update Existing Clones
442
+ */
443
+ public function deleteFinish() {
444
+ $existingClones = get_option("wpstg_existing_clones_beta", array());
445
+
446
+ // Check if clones still exist
447
+ $this->log("Verifying existing clones...");
448
+ foreach ($existingClones as $name => $clone) {
449
+ if (!is_dir($clone["path"])) {
450
+ unset($existingClones[$name]);
451
+ }
452
+ }
453
+ $this->log("Existing clones verified!");
454
+
455
+ if (false === update_option("wpstg_existing_clones_beta", $existingClones)) {
456
+ $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
457
+ }
458
+
459
+ // Delete cached file
460
+ $this->cache->delete("delete_job_{$this->clone->name}");
461
+ $this->cache->delete("delete_directories_{$this->clone->name}");
462
+
463
+ //return true;
464
+ $response = array('delete' => 'finished');
465
+ wp_die(json_encode($response));
466
+ }
467
+
468
+ /**
469
+ * Get json response
470
+ * return json
471
+ */
472
+ // private function returnException($message = ''){
473
+ // wp_die( json_encode(array(
474
+ // 'job' => 'delete',
475
+ // 'status' => false,
476
+ // 'message' => $message,
477
+ // 'error' => true
478
+ // )));
479
+ // }
480
+ /**
481
+ * Get json response
482
+ * return json
483
+ */
484
+ private function returnFinish($message = '') {
485
+
486
+ $this->deleteFinish();
487
+
488
+ wp_die(json_encode(array(
489
+ 'job' => 'delete',
490
+ 'status' => true,
491
+ 'message' => $message,
492
+ 'error' => false,
493
+ 'delete' => 'finished'
494
+ )));
495
+ }
496
+
497
+ }
apps/Backend/Modules/Jobs/Directories.php CHANGED
@@ -1,451 +1,428 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs;
4
-
5
- // No Direct Access
6
- if (!defined("WPINC")) {
7
- die;
8
- }
9
-
10
- use WPStaging\WPStaging;
11
- use WPStaging\Utils\Logger;
12
- use WPStaging\Iterators\RecursiveDirectoryIterator;
13
- use WPStaging\Iterators\RecursiveFilterNewLine;
14
- use WPStaging\Iterators\RecursiveFilterExclude;
15
-
16
- /**
17
- * Class Files
18
- * @package WPStaging\Backend\Modules\Directories
19
- */
20
- class Directories extends JobExecutable {
21
-
22
- /**
23
- * @var array
24
- */
25
- private $files = array();
26
-
27
- /**
28
- * Total steps to do
29
- * @var int
30
- */
31
- private $total = 4;
32
- private $fileHandle;
33
-
34
- private $filename;
35
-
36
- /**
37
- * Initialize
38
- */
39
- public function initialize() {
40
- $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
41
- }
42
-
43
- /**
44
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
45
- * @return void
46
- */
47
- protected function calculateTotalSteps() {
48
- $this->options->totalSteps = $this->total;
49
- }
50
-
51
- /**
52
- * Start Module
53
- * @return object
54
- */
55
- public function start() {
56
-
57
- // Execute steps
58
- $this->run();
59
-
60
- // Save option, progress
61
- $this->saveProgress();
62
-
63
- return (object) $this->response;
64
- }
65
-
66
- /**
67
- * Step 1
68
- * Get WP Root files
69
- */
70
- private function getWpRootFiles() {
71
-
72
- // open file handle
73
- $files = $this->open($this->filename, 'a');
74
-
75
-
76
- try {
77
-
78
- // Iterate over wp root directory
79
- $iterator = new \DirectoryIterator(ABSPATH);
80
-
81
- // Write path line
82
- foreach ($iterator as $item) {
83
- if (!$item->isDot() && $item->isFile()) {
84
- if ($this->write($files, $iterator->getFilename() . PHP_EOL)) {
85
- $this->options->totalFiles++;
86
-
87
- // Add current file size
88
- $this->options->totalFileSize += $iterator->getSize();
89
- }
90
- }
91
- }
92
- } catch (\Exception $e) {
93
- $this->returnException('Error: ' . $e->getMessage());
94
- //throw new \Exception('Out of disk space.');
95
- } catch (\Exception $e) {
96
- // Skip bad file permissions
97
- }
98
-
99
- $this->close($files);
100
- return true;
101
-
102
- }
103
-
104
- /**
105
- * Step 2
106
- * Get WP Content Files
107
- */
108
- public function getWpContentFiles() {
109
-
110
- // if (1 < $this->options->totalFiles) {
111
- // return;
112
- // }
113
-
114
- // open file handle
115
- $files = $this->open($this->filename, 'a');
116
-
117
- $excludeWpContent = array(
118
- 'HUGE-FOLDER',
119
- 'cache',
120
- 'wps-hide-login',
121
- 'node_modules',
122
- 'nbproject'
123
- );
124
-
125
- try {
126
-
127
- // Iterate over content directory
128
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(WP_CONTENT_DIR);
129
-
130
- // Exclude new line file names
131
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
132
-
133
- // Exclude uploads, plugins or themes
134
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $excludeWpContent));
135
- // Recursively iterate over content directory
136
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
137
-
138
- // Write path line
139
- foreach ($iterator as $item) {
140
- if ($item->isFile()) {
141
- if ($this->write($files, 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
142
- $this->options->totalFiles++;
143
-
144
- // Add current file size
145
- $this->options->totalFileSize += $iterator->getSize();
146
- }
147
- }
148
- }
149
- } catch (\Exception $e) {
150
- //$this->returnException('Out of disk space.');
151
- throw new \Exception('Error: ' . $e->getMessage());
152
- } catch (\Exception $e) {
153
- // Skip bad file permissions
154
- }
155
-
156
- // close the file handler
157
- $this->close($files);
158
- return true;
159
- }
160
-
161
- /**
162
- * Step 3
163
- * @return boolean
164
- * @throws \Exception
165
- */
166
- public function getWpIncludesFiles() {
167
-
168
- // open file handle and attach data to end of file
169
- $files = $this->open($this->filename, 'a');
170
-
171
- try {
172
-
173
- // Iterate over wp-admin directory
174
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR);
175
-
176
- // Exclude new line file names
177
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
178
-
179
- // Exclude uploads, plugins or themes
180
- //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
181
- // Recursively iterate over wp-includes directory
182
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
183
-
184
- // Write files
185
- foreach ($iterator as $item) {
186
- if ($item->isFile()) {
187
- if ($this->write($files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
188
- $this->options->totalFiles++;
189
-
190
- // Add current file size
191
- $this->options->totalFileSize += $iterator->getSize();
192
- }
193
- }
194
- }
195
- } catch (\Exception $e) {
196
- //$this->returnException('Out of disk space.');
197
- throw new \Exception('Error: ' . $e->getMessage());
198
- } catch (\Exception $e) {
199
- // Skip bad file permissions
200
- }
201
-
202
- // close the file handler
203
- $this->close($files);
204
- return true;
205
- }
206
-
207
- /**
208
- * Step 4
209
- * @return boolean
210
- * @throws \Exception
211
- */
212
- public function getWpAdminFiles() {
213
-
214
- // open file handle and attach data to end of file
215
- $files = $this->open($this->filename, 'a');
216
-
217
- try {
218
-
219
- // Iterate over wp-admin directory
220
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR);
221
-
222
- // Exclude new line file names
223
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
224
-
225
- // Exclude uploads, plugins or themes
226
- //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
227
- // Recursively iterate over content directory
228
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
229
-
230
- // Write path line
231
- foreach ($iterator as $item) {
232
- if ($item->isFile()) {
233
- if ($this->write($files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
234
- $this->options->totalFiles++;
235
- // Add current file size
236
- $this->options->totalFileSize += $iterator->getSize();
237
- }
238
- }
239
- }
240
- } catch (\Exception $e) {
241
- //$this->returnException('Out of disk space.');
242
- throw new \Exception('Error: ' . $e->getMessage());
243
- } catch (\Exception $e) {
244
- // Skip bad file permissions
245
- }
246
-
247
- // close the file handler
248
- $this->close($files);
249
- return true;
250
- }
251
-
252
- /**
253
- * Closes a file handle
254
- *
255
- * @param resource $handle File handle to close
256
- * @return boolean
257
- */
258
- public function close($handle) {
259
- return @fclose($handle);
260
- }
261
-
262
- /**
263
- * Opens a file in specified mode
264
- *
265
- * @param string $file Path to the file to open
266
- * @param string $mode Mode in which to open the file
267
- * @return resource
268
- * @throws Exception
269
- */
270
- public function open($file, $mode) {
271
-
272
- $file_handle = @fopen($file, $mode);
273
- if (false === $file_handle) {
274
- $this->returnException(sprintf(__('Unable to open %s with mode %s', 'wpstg'), $file, $mode));
275
- //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wpstg'), $file, $mode));
276
- }
277
-
278
- return $file_handle;
279
- }
280
-
281
- /**
282
- * Write contents to a file
283
- *
284
- * @param resource $handle File handle to write to
285
- * @param string $content Contents to write to the file
286
- * @return integer
287
- * @throws Exception
288
- * @throws Exception
289
- */
290
- public function write($handle, $content) {
291
- $write_result = @fwrite($handle, $content);
292
- if (false === $write_result) {
293
- if (( $meta = \stream_get_meta_data($handle))) {
294
- //$this->returnException(sprintf(__('Unable to write to: %s', 'wpstg'), $meta['uri']));
295
- throw new \Exception(sprintf(__('Unable to write to: %s', 'wpstg'), $meta['uri']));
296
- }
297
- } elseif (strlen($content) !== $write_result) {
298
- //$this->returnException(__('Out of disk space.', 'wpstg'));
299
- throw new \Exception(__('Out of disk space.', 'wpstg'));
300
- }
301
-
302
- return $write_result;
303
- }
304
-
305
- /**
306
- * Execute the Current Step
307
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
308
- * @return bool
309
- */
310
- protected function execute() {
311
-
312
- // No job left to execute
313
- if ($this->isFinished()) {
314
- $this->prepareResponse(true, false);
315
- return false;
316
- }
317
-
318
-
319
- if ($this->options->currentStep == 0) {
320
- $this->getWpRootFiles();
321
- $this->prepareResponse(false, true);
322
- return false;
323
- }
324
-
325
- if ($this->options->currentStep == 1) {
326
- $this->getWpContentFiles();
327
- $this->prepareResponse(false, true);
328
- return false;
329
- }
330
-
331
- if ($this->options->currentStep == 2) {
332
- $this->getWpIncludesFiles();
333
- $this->prepareResponse(false, true);
334
- return false;
335
- }
336
-
337
- if ($this->options->currentStep == 3) {
338
- $this->getWpAdminFiles();
339
- $this->prepareResponse(false, true);
340
- return false;
341
- }
342
-
343
-
344
- // Prepare response
345
- $this->prepareResponse(false, true);
346
- // Not finished
347
- return true;
348
- }
349
-
350
- /**
351
- * Checks Whether There is Any Job to Execute or Not
352
- * @return bool
353
- */
354
- protected function isFinished() {
355
- return (
356
- $this->options->currentStep > $this->total ||
357
- $this->options->currentStep == 4
358
- );
359
- }
360
-
361
- /**
362
- * Get Path from $directory
363
- * @param \SplFileInfo $directory
364
- * @return string|false
365
- */
366
- // protected function getPath($directory) {
367
- //
368
- // /*
369
- // * Do not follow root path like src/web/..
370
- // * This must be done before \SplFileInfo->isDir() is used!
371
- // * Prevents open base dir restriction fatal errors
372
- // */
373
- // if (strpos($directory->getRealPath(), ABSPATH) !== 0) {
374
- // return false;
375
- // }
376
- //
377
- // $path = str_replace(ABSPATH, null, $directory->getRealPath());
378
- //
379
- // // Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
380
- // if (!$directory->isDir() || strlen($path) < 1) {
381
- // return false;
382
- // }
383
- //
384
- // return $path;
385
- // }
386
-
387
- /**
388
- * Check if directory is excluded from copying
389
- * @param string $directory
390
- * @return bool
391
- */
392
- // protected function isDirectoryExcluded($directory) {
393
- // foreach ($this->options->excludedDirectories as $excludedDirectory) {
394
- // if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
395
- // return true;
396
- // }
397
- // }
398
- //
399
- // return false;
400
- // }
401
-
402
- /**
403
- * Check if directory is an extra directory and should be copied
404
- * @param string $directory
405
- * @return boolean
406
- */
407
- // protected function isExtraDirectory($directory) {
408
- // foreach ($this->options->extraDirectories as $extraDirectory) {
409
- // if (strpos($directory, $extraDirectory) === 0) {
410
- // return true;
411
- // }
412
- // }
413
- //
414
- // return false;
415
- // }
416
-
417
- /**
418
- * Save files
419
- * @return bool
420
- */
421
- protected function saveProgress() {
422
- return $this->saveOptions();
423
-
424
- // $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
425
- // $files = implode(PHP_EOL, $this->files);
426
- //
427
- // if (strlen($files) > 0) {
428
- // //$files .= PHP_EOL;
429
- // }
430
- //
431
- // return (false !== @file_put_contents($fileName, $files));
432
- }
433
-
434
- /**
435
- * Get files
436
- * @return void
437
- */
438
- protected function getFiles() {
439
- $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
440
-
441
- if (false === ($this->files = @file_get_contents($fileName))) {
442
- $this->files = array();
443
- return;
444
- }
445
-
446
- $this->files = explode(PHP_EOL, $this->files);
447
- }
448
-
449
-
450
-
451
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs;
4
+
5
+ // No Direct Access
6
+ if (!defined("WPINC")) {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\WPStaging;
11
+ use WPStaging\Utils\Logger;
12
+ use WPStaging\Iterators\RecursiveDirectoryIterator;
13
+ use WPStaging\Iterators\RecursiveFilterNewLine;
14
+ use WPStaging\Iterators\RecursiveFilterExclude;
15
+
16
+ /**
17
+ * Class Files
18
+ * @package WPStaging\Backend\Modules\Directories
19
+ */
20
+ class Directories extends JobExecutable {
21
+
22
+ /**
23
+ * @var array
24
+ */
25
+ private $files = array();
26
+
27
+ /**
28
+ * Total steps to do
29
+ * @var int
30
+ */
31
+ private $total = 4;
32
+ private $fileHandle;
33
+
34
+ private $filename;
35
+
36
+ /**
37
+ * Initialize
38
+ */
39
+ public function initialize() {
40
+ $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
41
+ }
42
+
43
+ /**
44
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
45
+ * @return void
46
+ */
47
+ protected function calculateTotalSteps() {
48
+ $this->options->totalSteps = $this->total;
49
+ }
50
+
51
+ /**
52
+ * Start Module
53
+ * @return object
54
+ */
55
+ public function start() {
56
+
57
+ // Execute steps
58
+ $this->run();
59
+
60
+ // Save option, progress
61
+ $this->saveProgress();
62
+
63
+ return (object) $this->response;
64
+ }
65
+
66
+ /**
67
+ * Step 1
68
+ * Get WP Root files
69
+ */
70
+ private function getWpRootFiles() {
71
+
72
+ // Skip it
73
+ // if ($this->isDirectoryExcluded(ABSPATH)){
74
+ // return true;
75
+ // }
76
+
77
+ // open file handle
78
+ $files = $this->open($this->filename, 'a');
79
+
80
+
81
+ try {
82
+
83
+ // Iterate over wp root directory
84
+ $iterator = new \DirectoryIterator(ABSPATH);
85
+
86
+ $this->log( "Scanning ".ABSPATH." for its sub-directories and files" );
87
+
88
+ // Write path line
89
+ foreach ($iterator as $item) {
90
+ if (!$item->isDot() && $item->isFile()) {
91
+ if ($this->write($files, $iterator->getFilename() . PHP_EOL)) {
92
+ $this->options->totalFiles++;
93
+
94
+ // Add current file size
95
+ $this->options->totalFileSize += $iterator->getSize();
96
+ }
97
+ }
98
+ }
99
+ } catch (\Exception $e) {
100
+ $this->returnException('Error: ' . $e->getMessage());
101
+ //throw new \Exception('Out of disk space.');
102
+ } catch (\Exception $e) {
103
+ // Skip bad file permissions
104
+ }
105
+
106
+ $this->close($files);
107
+ return true;
108
+
109
+ }
110
+
111
+ /**
112
+ * Step 2
113
+ * Get WP Content Files
114
+ */
115
+ public function getWpContentFiles() {
116
+
117
+ // Skip it
118
+ if ($this->isDirectoryExcluded(WP_CONTENT_DIR)){
119
+ return true;
120
+ }
121
+ // open file handle
122
+ $files = $this->open($this->filename, 'a');
123
+
124
+ $excludeWpContent = array(
125
+ 'HUGE-FOLDER',
126
+ 'cache',
127
+ 'wps-hide-login',
128
+ 'node_modules',
129
+ 'nbproject'
130
+ );
131
+
132
+ try {
133
+
134
+ // Iterate over content directory
135
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(WP_CONTENT_DIR);
136
+
137
+ // Exclude new line file names
138
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
139
+
140
+ // Exclude uploads, plugins or themes
141
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $excludeWpContent));
142
+ // Recursively iterate over content directory
143
+ $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
144
+
145
+ $path = 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
146
+ $this->log( "Scanning {$path} for its sub-directories and files" );
147
+
148
+ // Write path line
149
+ foreach ($iterator as $item) {
150
+ if ($item->isFile()) {
151
+ if ($this->write($files, 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
152
+ $this->options->totalFiles++;
153
+
154
+ // Add current file size
155
+ $this->options->totalFileSize += $iterator->getSize();
156
+ }
157
+ }
158
+ }
159
+ } catch (\Exception $e) {
160
+ //$this->returnException('Out of disk space.');
161
+ throw new \Exception('Error: ' . $e->getMessage());
162
+ } catch (\Exception $e) {
163
+ // Skip bad file permissions
164
+ }
165
+
166
+ // close the file handler
167
+ $this->close($files);
168
+ return true;
169
+ }
170
+
171
+ /**
172
+ * Step 3
173
+ * @return boolean
174
+ * @throws \Exception
175
+ */
176
+ public function getWpIncludesFiles() {
177
+
178
+ // Skip it
179
+ if ($this->isDirectoryExcluded(ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR)){
180
+ return true;
181
+ }
182
+
183
+ // open file handle and attach data to end of file
184
+ $files = $this->open($this->filename, 'a');
185
+
186
+ try {
187
+
188
+ // Iterate over wp-admin directory
189
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR);
190
+
191
+ // Exclude new line file names
192
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
193
+
194
+ // Exclude uploads, plugins or themes
195
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
196
+ // Recursively iterate over wp-includes directory
197
+ $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
198
+
199
+ $path = ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR;
200
+ $this->log( "Scanning {$path} for its sub-directories and files" );
201
+
202
+ // Write files
203
+ foreach ($iterator as $item) {
204
+ if ($item->isFile()) {
205
+ if ($this->write($files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
206
+ $this->options->totalFiles++;
207
+
208
+ // Add current file size
209
+ $this->options->totalFileSize += $iterator->getSize();
210
+ }
211
+ }
212
+ }
213
+ } catch (\Exception $e) {
214
+ //$this->returnException('Out of disk space.');
215
+ throw new \Exception('Error: ' . $e->getMessage());
216
+ } catch (\Exception $e) {
217
+ // Skip bad file permissions
218
+ }
219
+
220
+ // close the file handler
221
+ $this->close($files);
222
+ return true;
223
+ }
224
+
225
+ /**
226
+ * Step 4
227
+ * @return boolean
228
+ * @throws \Exception
229
+ */
230
+ public function getWpAdminFiles() {
231
+
232
+ // Skip it
233
+ if ($this->isDirectoryExcluded(ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR)) {
234
+ return true;
235
+ }
236
+
237
+ // open file handle and attach data to end of file
238
+ $files = $this->open($this->filename, 'a');
239
+
240
+ try {
241
+
242
+ // Iterate over wp-admin directory
243
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR);
244
+
245
+ // Exclude new line file names
246
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
247
+
248
+ // Exclude uploads, plugins or themes
249
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
250
+ // Recursively iterate over content directory
251
+ $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
252
+
253
+ $path = ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR;
254
+ $this->log("Scanning {$path} for its sub-directories and files");
255
+
256
+ // Write path line
257
+ foreach ($iterator as $item) {
258
+ if ($item->isFile()) {
259
+ if ($this->write($files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
260
+ $this->options->totalFiles++;
261
+ // Add current file size
262
+ $this->options->totalFileSize += $iterator->getSize();
263
+ }
264
+ }
265
+ }
266
+ } catch (\Exception $e) {
267
+ $this->returnException('Error: ' . $e->getMessage());
268
+ //throw new \Exception('Error: ' . $e->getMessage());
269
+ } catch (\Exception $e) {
270
+ // Skip bad file permissions
271
+ }
272
+
273
+ // close the file handler
274
+ $this->close($files);
275
+ return true;
276
+ }
277
+
278
+ /**
279
+ * Closes a file handle
280
+ *
281
+ * @param resource $handle File handle to close
282
+ * @return boolean
283
+ */
284
+ public function close($handle) {
285
+ return @fclose($handle);
286
+ }
287
+
288
+ /**
289
+ * Opens a file in specified mode
290
+ *
291
+ * @param string $file Path to the file to open
292
+ * @param string $mode Mode in which to open the file
293
+ * @return resource
294
+ * @throws Exception
295
+ */
296
+ public function open($file, $mode) {
297
+
298
+ $file_handle = @fopen($file, $mode);
299
+ if (false === $file_handle) {
300
+ $this->returnException(sprintf(__('Unable to open %s with mode %s', 'wpstg'), $file, $mode));
301
+ //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wpstg'), $file, $mode));
302
+ }
303
+
304
+ return $file_handle;
305
+ }
306
+
307
+ /**
308
+ * Write contents to a file
309
+ *
310
+ * @param resource $handle File handle to write to
311
+ * @param string $content Contents to write to the file
312
+ * @return integer
313
+ * @throws Exception
314
+ * @throws Exception
315
+ */
316
+ public function write($handle, $content) {
317
+ $write_result = @fwrite($handle, $content);
318
+ if (false === $write_result) {
319
+ if (( $meta = \stream_get_meta_data($handle))) {
320
+ //$this->returnException(sprintf(__('Unable to write to: %s', 'wpstg'), $meta['uri']));
321
+ throw new \Exception(sprintf(__('Unable to write to: %s', 'wpstg'), $meta['uri']));
322
+ }
323
+ } elseif (strlen($content) !== $write_result) {
324
+ //$this->returnException(__('Out of disk space.', 'wpstg'));
325
+ throw new \Exception(__('Out of disk space.', 'wpstg'));
326
+ }
327
+
328
+ return $write_result;
329
+ }
330
+
331
+ /**
332
+ * Execute the Current Step
333
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
334
+ * @return bool
335
+ */
336
+ protected function execute() {
337
+
338
+ // No job left to execute
339
+ if ($this->isFinished()) {
340
+ $this->prepareResponse(true, false);
341
+ return false;
342
+ }
343
+
344
+
345
+ if ($this->options->currentStep == 0) {
346
+ $this->getWpRootFiles();
347
+ $this->prepareResponse(false, true);
348
+ return false;
349
+ }
350
+
351
+ if ($this->options->currentStep == 1) {
352
+ $this->getWpContentFiles();
353
+ $this->prepareResponse(false, true);
354
+ return false;
355
+ }
356
+
357
+ if ($this->options->currentStep == 2) {
358
+ $this->getWpIncludesFiles();
359
+ $this->prepareResponse(false, true);
360
+ return false;
361
+ }
362
+
363
+ if ($this->options->currentStep == 3) {
364
+ $this->getWpAdminFiles();
365
+ $this->prepareResponse(false, true);
366
+ return false;
367
+ }
368
+
369
+
370
+ // Prepare response
371
+ $this->prepareResponse(false, true);
372
+ // Not finished
373
+ return true;
374
+ }
375
+
376
+ /**
377
+ * Checks Whether There is Any Job to Execute or Not
378
+ * @return bool
379
+ */
380
+ protected function isFinished() {
381
+ return (
382
+ $this->options->currentStep > $this->total ||
383
+ $this->options->currentStep == 4
384
+ );
385
+ }
386
+
387
+
388
+
389
+
390
+ /**
391
+ * Save files
392
+ * @return bool
393
+ */
394
+ protected function saveProgress() {
395
+ return $this->saveOptions();
396
+ }
397
+
398
+ /**
399
+ * Get files
400
+ * @return void
401
+ */
402
+ protected function getFiles() {
403
+ $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
404
+
405
+ if (false === ($this->files = @file_get_contents($fileName))) {
406
+ $this->files = array();
407
+ return;
408
+ }
409
+
410
+ $this->files = explode(PHP_EOL, $this->files);
411
+ }
412
+
413
+ /**
414
+ * Check if directory is excluded from colec
415
+ * @param string $directory
416
+ * @return bool
417
+ */
418
+ protected function isDirectoryExcluded($directory) {
419
+ foreach ($this->options->excludedDirectories as $excludedDirectory) {
420
+ if (strpos($directory, $excludedDirectory) === 0) {
421
+ return true;
422
+ }
423
+ }
424
+
425
+ return false;
426
+ }
427
+
428
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -1,365 +1,366 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs;
4
-
5
- // No Direct Access
6
- use WPStaging\Utils\Logger;
7
-
8
- if (!defined("WPINC")) {
9
- die;
10
- }
11
-
12
- /**
13
- * Class Files
14
- * @package WPStaging\Backend\Modules\Jobs
15
- */
16
- class Files extends JobExecutable {
17
-
18
- /**
19
- * @var \SplFileObject
20
- */
21
- private $file;
22
-
23
- /**
24
- * @var int
25
- */
26
- private $maxFilesPerRun;
27
-
28
- /**
29
- * File Object
30
- */
31
- //private $verifyFiles = array();
32
-
33
- /**
34
- * @var string
35
- */
36
- private $destination;
37
-
38
- /**
39
- * Initialization
40
- */
41
- public function initialize() {
42
-
43
- $this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
44
-
45
- $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
46
-
47
- if (is_file($filePath)) {
48
- $this->file = new \SplFileObject($filePath, 'r');
49
- }
50
-
51
- // Informational logs
52
- if (0 == $this->options->currentStep) {
53
- $this->log("Copying files...");
54
- }
55
-
56
- $this->settings->batchSize = $this->settings->batchSize * 1000000;
57
- $this->maxFilesPerRun = $this->settings->fileLimit;
58
- }
59
-
60
- /**
61
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
62
- * @return void
63
- */
64
- protected function calculateTotalSteps() {
65
- $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
66
- }
67
-
68
- /**
69
- * Execute the Current Step
70
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
71
- * @return bool
72
- */
73
- protected function execute() {
74
- // Finished
75
- if ($this->isFinished()) {
76
- $this->log("Copying files finished");
77
- $this->prepareResponse(true, false);
78
- return false;
79
- }
80
-
81
- // Get files and copy'em
82
- if (!$this->getFilesAndCopy()) {
83
- $this->prepareResponse(false, false);
84
- return false;
85
- }
86
-
87
- // Prepare and return response
88
- $this->prepareResponse();
89
-
90
- // Not finished
91
- return true;
92
- }
93
-
94
- /**
95
- * Get files and copy
96
- * @return bool
97
- */
98
- private function getFilesAndCopy() {
99
- // Over limits threshold
100
- if ($this->isOverThreshold()) {
101
- // Prepare response and save current progress
102
- $this->prepareResponse(false, false);
103
- $this->saveOptions();
104
- return false;
105
- }
106
-
107
- // Go to last copied line and than to next one
108
- //if ($this->options->copiedFiles != 0) {
109
- if (isset($this->options->copiedFiles) && $this->options->copiedFiles != 0) {
110
- $this->file->seek($this->options->copiedFiles - 1);
111
- }
112
-
113
- $this->file->setFlags(\SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
114
-
115
-
116
- // Loop x files at a time
117
- for ($i = 0; $i < $this->maxFilesPerRun; $i++) {
118
-
119
- // Increment copied files
120
- // Do this anytime to make sure to not stuck in the same step / files
121
- $this->options->copiedFiles++;
122
-
123
- // End of file
124
- if ($this->file->eof()) {
125
- break;
126
- }
127
-
128
-
129
- $file = $this->file->fgets();
130
- $this->debugLog('copy file ' . $file, Logger::TYPE_DEBUG);
131
- $this->copyFile($file);
132
- }
133
-
134
- //$totalFiles = $this->maxFilesPerRun + $this->options->copiedFiles;
135
- $totalFiles = $this->options->copiedFiles;
136
- $this->log("Total {$totalFiles} files processed");
137
-
138
- return true;
139
- }
140
-
141
- /**
142
- * Checks Whether There is Any Job to Execute or Not
143
- * @return bool
144
- */
145
- private function isFinished() {
146
- return (
147
- $this->options->currentStep > $this->options->totalSteps ||
148
- $this->options->copiedFiles >= $this->options->totalFiles
149
- );
150
- }
151
-
152
- /**
153
- * @param string $file
154
- * @return bool
155
- */
156
- private function copyFile($file) {
157
- $file = trim(ABSPATH . $file);
158
-
159
- $directory = dirname($file);
160
-
161
- // Get file size
162
- $fileSize = filesize($file);
163
-
164
- // Directory is excluded
165
- if ($this->isDirectoryExcluded($directory)) {
166
- $this->log("Skipping directory by rule: {$file}", Logger::TYPE_INFO);
167
- return false;
168
- }
169
-
170
- // File is excluded
171
- if ($this->isFileExcluded($file)) {
172
- $this->log("Skipping file by rule: {$file}", Logger::TYPE_INFO);
173
- return false;
174
- }
175
-
176
- // File is over maximum allowed file size (8MB)
177
- if ($fileSize >= 8000000) {
178
- $this->log("Skipping big file: {$file}", Logger::TYPE_INFO);
179
- return false;
180
- }
181
-
182
- // Invalid file, skipping it as if succeeded
183
- if (!is_file($file) || !is_readable($file)) {
184
- $this->log("Can't read file or file doesn't exist {$file}");
185
- return true;
186
- }
187
-
188
- // Failed to get destination
189
- if (false === ($destination = $this->getDestination($file))) {
190
- $this->log("Can't get the destination of {$file}");
191
- return false;
192
- }
193
-
194
- // File is over batch size
195
- if ($fileSize >= $this->settings->batchSize) {
196
- $this->log("Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO);
197
- return $this->copyBig($file, $destination, $this->settings->batchSize);
198
- }
199
-
200
- // Attempt to copy
201
- if (!@copy($file, $destination)) {
202
- $this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
203
- return false;
204
- }
205
-
206
- return true;
207
-
208
- // Good old PHP
209
- //return $this->copy($file, $destination);
210
- }
211
-
212
- /**
213
- * Gets destination file and checks if the directory exists, if it does not attempts to create it.
214
- * If creating destination directory fails, it returns false, gives destination full path otherwise
215
- * @param string $file
216
- * @return bool|string
217
- */
218
- private function getDestination($file) {
219
- $relativePath = str_replace(ABSPATH, null, $file);
220
- $destinationPath = $this->destination . $relativePath;
221
- $destinationDirectory = dirname($destinationPath);
222
-
223
- if (!is_dir($destinationDirectory) && !@mkdir($destinationDirectory, 0775, true)) {
224
- $this->log("Destination directory doesn't exist; {$destinationDirectory}", Logger::TYPE_ERROR);
225
- return false;
226
- }
227
-
228
- return $destinationPath;
229
- }
230
-
231
- /**
232
- * Copy File using PHP
233
- * @param string $file
234
- * @param string $destination
235
- * @return bool
236
- */
237
- // private function copy($file, $destination) {
238
- // // Get file size
239
- // $fileSize = filesize($file);
240
- //
241
- //
242
- // // File is over batch size
243
- // if ($fileSize >= $this->settings->batchSize) {
244
- // $this->log("Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO);
245
- // return $this->copyBig($file, $destination, $this->settings->batchSize);
246
- // }
247
- //
248
- // // Attempt to copy
249
- // if (!@copy($file, $destination)) {
250
- // $this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
251
- // return false;
252
- // }
253
- //
254
- // return true;
255
- // }
256
-
257
- /**
258
- * Copy bigger files than $this->settings->batchSize
259
- * @param string $file
260
- * @param string $destination
261
- * @return bool
262
- *
263
- * @deprecated since version 2.0.0 (Supported only in php 5.5.11 and later)
264
- */
265
- // private function copyBig($file, $destination)
266
- // {
267
- // $bytes = 0;
268
- // $fileInput = new \SplFileObject($file, "rb");
269
- // $fileOutput = new \SplFileObject($destination, 'w');
270
- //
271
- // $this->log("Copying big file; {$file} -> {$destination}");
272
- //
273
- // while (!$fileInput->eof())
274
- // {
275
- // $bytes += $fileOutput->fwrite($fileInput->fread($this->settings->batchSize));
276
- // }
277
- //
278
- // $fileInput = null;
279
- // $fileOutput= null;
280
- //
281
- // return ($bytes > 0);
282
- // }
283
-
284
- /**
285
- * Copy bigger files than $this->settings->batchSize
286
- * @param string $src
287
- * @param string $dst
288
- * @param int $buffersize
289
- * @return boolean
290
- */
291
- private function copyBig($src, $dst, $buffersize) {
292
- $src = fopen($src, 'r');
293
- $dest = fopen($dst, 'w');
294
-
295
- // Try first method:
296
- while (!feof($src)) {
297
- if (false === fwrite($dest, fread($src, $buffersize))) {
298
- $error = true;
299
- }
300
- }
301
- // Try second method if first one failed
302
- if (isset($error) && ($error === true)) {
303
- while (!feof($src)) {
304
- if (false === stream_copy_to_stream($src, $dest, 1024)) {
305
- $this->log("Can not copy big file; {$src} -> {$dest}");
306
- fclose($src);
307
- fclose($dest);
308
- return false;
309
- }
310
- }
311
- }
312
- // Close any open handler
313
- fclose($src);
314
- fclose($dest);
315
- return true;
316
- }
317
-
318
- /**
319
- * Check if file is excluded from copying process
320
- *
321
- * @param string $file filename including ending
322
- * @return boolean
323
- */
324
- private function isFileExcluded($file) {
325
- $excluded = false;
326
- foreach ($this->options->excludedFiles as $excludedFile) {
327
- if (stripos(strrev($file), strrev($excludedFile)) === 0) {
328
- $excluded = true;
329
- break;
330
- }
331
- }
332
- return $excluded;
333
- }
334
-
335
- /**
336
- * Check if directory is excluded from copying
337
- * @param string $directory
338
- * @return bool
339
- */
340
- private function isDirectoryExcluded($directory) {
341
- foreach ($this->options->excludedDirectories as $excludedDirectory) {
342
- if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
343
- return true;
344
- }
345
- }
346
-
347
- return false;
348
- }
349
-
350
- /**
351
- * Check if directory is an extra directory and should be copied
352
- * @param string $directory
353
- * @return boolean
354
- */
355
- protected function isExtraDirectory($directory) {
356
- foreach ($this->options->extraDirectories as $extraDirectory) {
357
- if (strpos($directory, $extraDirectory) === 0) {
358
- return true;
359
- }
360
- }
361
-
362
- return false;
363
- }
364
-
365
- }
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs;
4
+
5
+ // No Direct Access
6
+ use WPStaging\Utils\Logger;
7
+
8
+ if (!defined("WPINC")) {
9
+ die;
10
+ }
11
+
12
+ /**
13
+ * Class Files
14
+ * @package WPStaging\Backend\Modules\Jobs
15
+ */
16
+ class Files extends JobExecutable {
17
+
18
+ /**
19
+ * @var \SplFileObject
20
+ */
21
+ private $file;
22
+
23
+ /**
24
+ * @var int
25
+ */
26
+ private $maxFilesPerRun;
27
+
28
+ /**
29
+ * File Object
30
+ */
31
+ //private $verifyFiles = array();
32
+
33
+ /**
34
+ * @var string
35
+ */
36
+ private $destination;
37
+
38
+ /**
39
+ * Initialization
40
+ */
41
+ public function initialize() {
42
+
43
+ $this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
44
+
45
+ $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
46
+
47
+ if (is_file($filePath)) {
48
+ $this->file = new \SplFileObject($filePath, 'r');
49
+ }
50
+
51
+ // Informational logs
52
+ if (0 == $this->options->currentStep) {
53
+ $this->log("Copying files...");
54
+ }
55
+
56
+ $this->settings->batchSize = $this->settings->batchSize * 1000000;
57
+ $this->maxFilesPerRun = $this->settings->fileLimit;
58
+ }
59
+
60
+ /**
61
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
62
+ * @return void
63
+ */
64
+ protected function calculateTotalSteps() {
65
+ $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
66
+ }
67
+
68
+ /**
69
+ * Execute the Current Step
70
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
71
+ * @return bool
72
+ */
73
+ protected function execute() {
74
+ // Finished
75
+ if ($this->isFinished()) {
76
+ $this->log("Copying files finished");
77
+ $this->prepareResponse(true, false);
78
+ return false;
79
+ }
80
+
81
+ // Get files and copy'em
82
+ if (!$this->getFilesAndCopy()) {
83
+ $this->prepareResponse(false, false);
84
+ return false;
85
+ }
86
+
87
+ // Prepare and return response
88
+ $this->prepareResponse();
89
+
90
+ // Not finished
91
+ return true;
92
+ }
93
+
94
+ /**
95
+ * Get files and copy
96
+ * @return bool
97
+ */
98
+ private function getFilesAndCopy() {
99
+ // Over limits threshold
100
+ if ($this->isOverThreshold()) {
101
+ // Prepare response and save current progress
102
+ $this->prepareResponse(false, false);
103
+ $this->saveOptions();
104
+ return false;
105
+ }
106
+
107
+ // Go to last copied line and than to next one
108
+ //if ($this->options->copiedFiles != 0) {
109
+ if (isset($this->options->copiedFiles) && $this->options->copiedFiles != 0) {
110
+ $this->file->seek($this->options->copiedFiles - 1);
111
+ }
112
+
113
+ $this->file->setFlags(\SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
114
+
115
+
116
+ // Loop x files at a time
117
+ for ($i = 0; $i < $this->maxFilesPerRun; $i++) {
118
+
119
+ // Increment copied files
120
+ // Do this anytime to make sure to not stuck in the same step / files
121
+ $this->options->copiedFiles++;
122
+
123
+ // End of file
124
+ if ($this->file->eof()) {
125
+ break;
126
+ }
127
+
128
+
129
+ $file = $this->file->fgets();
130
+ //$this->debugLog('copy file ' . $file, Logger::TYPE_DEBUG);
131
+ $this->copyFile($file);
132
+ }
133
+
134
+ $totalFiles = $this->options->copiedFiles;
135
+ // Log this only every 50 entries to keep the log small and to not block the rendering browser
136
+ if ($this->options->copiedFiles %50 == 0){
137
+ $this->log("Total {$totalFiles} files processed");
138
+ }
139
+
140
+ return true;
141
+ }
142
+
143
+ /**
144
+ * Checks Whether There is Any Job to Execute or Not
145
+ * @return bool
146
+ */
147
+ private function isFinished() {
148
+ return (
149
+ $this->options->currentStep > $this->options->totalSteps ||
150
+ $this->options->copiedFiles >= $this->options->totalFiles
151
+ );
152
+ }
153
+
154
+ /**
155
+ * @param string $file
156
+ * @return bool
157
+ */
158
+ private function copyFile($file) {
159
+ $file = trim(ABSPATH . $file);
160
+
161
+ $directory = dirname($file);
162
+
163
+ // Get file size
164
+ $fileSize = filesize($file);
165
+
166
+ // Directory is excluded
167
+ if ($this->isDirectoryExcluded($directory)) {
168
+ $this->debugLog("Skipping directory by rule: {$file}", Logger::TYPE_INFO);
169
+ return false;
170
+ }
171
+
172
+ // File is excluded
173
+ if ($this->isFileExcluded($file)) {
174
+ $this->log("Skipping file by rule: {$file}", Logger::TYPE_INFO);
175
+ return false;
176
+ }
177
+
178
+ // File is over maximum allowed file size (8MB)
179
+ if ($fileSize >= 8000000) {
180
+ $this->log("Skipping big file: {$file}", Logger::TYPE_INFO);
181
+ return false;
182
+ }
183
+
184
+ // Invalid file, skipping it as if succeeded
185
+ if (!is_file($file) || !is_readable($file)) {
186
+ $this->log("Can't read file or file doesn't exist {$file}");
187
+ return true;
188
+ }
189
+
190
+ // Failed to get destination
191
+ if (false === ($destination = $this->getDestination($file))) {
192
+ $this->log("Can't get the destination of {$file}");
193
+ return false;
194
+ }
195
+
196
+ // File is over batch size
197
+ if ($fileSize >= $this->settings->batchSize) {
198
+ $this->log("Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO);
199
+ return $this->copyBig($file, $destination, $this->settings->batchSize);
200
+ }
201
+
202
+ // Attempt to copy
203
+ if (!@copy($file, $destination)) {
204
+ $this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
205
+ return false;
206
+ }
207
+
208
+ return true;
209
+
210
+ // Good old PHP
211
+ //return $this->copy($file, $destination);
212
+ }
213
+
214
+ /**
215
+ * Gets destination file and checks if the directory exists, if it does not attempts to create it.
216
+ * If creating destination directory fails, it returns false, gives destination full path otherwise
217
+ * @param string $file
218
+ * @return bool|string
219
+ */
220
+ private function getDestination($file) {
221
+ $relativePath = str_replace(ABSPATH, null, $file);
222
+ $destinationPath = $this->destination . $relativePath;
223
+ $destinationDirectory = dirname($destinationPath);
224
+
225
+ if (!is_dir($destinationDirectory) && !@mkdir($destinationDirectory, 0775, true)) {
226
+ $this->log("Files: Can not create directory {$destinationDirectory}", Logger::TYPE_ERROR);
227
+ return false;
228
+ }
229
+
230
+ return $destinationPath;
231
+ }
232
+
233
+ /**
234
+ * Copy File using PHP
235
+ * @param string $file
236
+ * @param string $destination
237
+ * @return bool
238
+ */
239
+ // private function copy($file, $destination) {
240
+ // // Get file size
241
+ // $fileSize = filesize($file);
242
+ //
243
+ //
244
+ // // File is over batch size
245
+ // if ($fileSize >= $this->settings->batchSize) {
246
+ // $this->log("Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO);
247
+ // return $this->copyBig($file, $destination, $this->settings->batchSize);
248
+ // }
249
+ //
250
+ // // Attempt to copy
251
+ // if (!@copy($file, $destination)) {
252
+ // $this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
253
+ // return false;
254
+ // }
255
+ //
256
+ // return true;
257
+ // }
258
+
259
+ /**
260
+ * Copy bigger files than $this->settings->batchSize
261
+ * @param string $file
262
+ * @param string $destination
263
+ * @return bool
264
+ *
265
+ * @deprecated since version 2.0.0 (Supported only in php 5.5.11 and later)
266
+ */
267
+ // private function copyBig($file, $destination)
268
+ // {
269
+ // $bytes = 0;
270
+ // $fileInput = new \SplFileObject($file, "rb");
271
+ // $fileOutput = new \SplFileObject($destination, 'w');
272
+ //
273
+ // $this->log("Copying big file; {$file} -> {$destination}");
274
+ //
275
+ // while (!$fileInput->eof())
276
+ // {
277
+ // $bytes += $fileOutput->fwrite($fileInput->fread($this->settings->batchSize));
278
+ // }
279
+ //
280
+ // $fileInput = null;
281
+ // $fileOutput= null;
282
+ //
283
+ // return ($bytes > 0);
284
+ // }
285
+
286
+ /**
287
+ * Copy bigger files than $this->settings->batchSize
288
+ * @param string $src
289
+ * @param string $dst
290
+ * @param int $buffersize
291
+ * @return boolean
292
+ */
293
+ private function copyBig($src, $dst, $buffersize) {
294
+ $src = fopen($src, 'r');
295
+ $dest = fopen($dst, 'w');
296
+
297
+ // Try first method:
298
+ while (!feof($src)) {
299
+ if (false === fwrite($dest, fread($src, $buffersize))) {
300
+ $error = true;
301
+ }
302
+ }
303
+ // Try second method if first one failed
304
+ if (isset($error) && ($error === true)) {
305
+ while (!feof($src)) {
306
+ if (false === stream_copy_to_stream($src, $dest, 1024)) {
307
+ $this->log("Can not copy big file; {$src} -> {$dest}");
308
+ fclose($src);
309
+ fclose($dest);
310
+ return false;
311
+ }
312
+ }
313
+ }
314
+ // Close any open handler
315
+ fclose($src);
316
+ fclose($dest);
317
+ return true;
318
+ }
319
+
320
+ /**
321
+ * Check if file is excluded from copying process
322
+ *
323
+ * @param string $file filename including ending
324
+ * @return boolean
325
+ */
326
+ private function isFileExcluded($file) {
327
+ $excluded = false;
328
+ foreach ($this->options->excludedFiles as $excludedFile) {
329
+ if (stripos(strrev($file), strrev($excludedFile)) === 0) {
330
+ $excluded = true;
331
+ break;
332
+ }
333
+ }
334
+ return $excluded;
335
+ }
336
+
337
+ /**
338
+ * Check if directory is excluded from copying
339
+ * @param string $directory
340
+ * @return bool
341
+ */
342
+ private function isDirectoryExcluded($directory) {
343
+ foreach ($this->options->excludedDirectories as $excludedDirectory) {
344
+ if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
345
+ return true;
346
+ }
347
+ }
348
+
349
+ return false;
350
+ }
351
+
352
+ /**
353
+ * Check if directory is an extra directory and should be copied
354
+ * @param string $directory
355
+ * @return boolean
356
+ */
357
+ protected function isExtraDirectory($directory) {
358
+ foreach ($this->options->extraDirectories as $extraDirectory) {
359
+ if (strpos($directory, $extraDirectory) === 0) {
360
+ return true;
361
+ }
362
+ }
363
+
364
+ return false;
365
+ }
366
+ }
apps/Backend/Modules/Jobs/Finish.php CHANGED
@@ -1,262 +1,121 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\WPStaging;
5
-
6
- //error_reporting( E_ALL );
7
-
8
- /**
9
- * Class Finish
10
- * @package WPStaging\Backend\Modules\Jobs
11
- */
12
- class Finish extends Job
13
- {
14
- /**
15
- * Clone Key
16
- * @var string
17
- */
18
- private $clone = '';
19
-
20
- /**
21
- * Start Module
22
- * @return object
23
- */
24
- public function start()
25
- {
26
- // sanitize the clone name before saving
27
- $this->clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
28
-
29
- // Delete Cache Files
30
- $this->deleteCacheFiles();
31
-
32
- // Prepare clone records & save scanned directories for delete job later
33
- $this->prepareCloneDataRecords();
34
-
35
-
36
- $return = array(
37
- "directoryName" => $this->options->cloneDirectoryName,
38
- "path" => ABSPATH . $this->options->cloneDirectoryName,
39
- "url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
40
- "number" => $this->options->cloneNumber,
41
- "version" => \WPStaging\WPStaging::VERSION,
42
- "status" => false,
43
- "prefix" => $this->options->prefix,
44
- "last_msg" => $this->logger->getLastLogMsg(),
45
- "job" => $this->options->currentJob
46
- );
47
-
48
-
49
- //return (object) $this->options->existingClones[$this->options->clone];
50
- //return (object) $this->options->existingClones[$this->clone];
51
- return (object) $return;
52
- }
53
-
54
- /**
55
- * Delete Cache Files
56
- */
57
- protected function deleteCacheFiles()
58
- {
59
- $this->log("Finish: Deleting clone job's cache files...");
60
-
61
- // Clean cache files
62
- $this->cache->delete("clone_options");
63
- $this->cache->delete("files_to_copy");
64
-
65
- $this->log("Finish: Clone job's cache files have been deleted!");
66
- }
67
-
68
- /**
69
- * Prepare clone records
70
- * @return bool
71
- */
72
- protected function prepareCloneDataRecords()
73
- {
74
- // Check if clones still exist
75
- $this->log("Finish: Verifying existing clones...");
76
-
77
- // Clone data already exists
78
- if (isset($this->options->existingClones[$this->options->clone]))
79
- {
80
- $this->log("Finish: Clone data already exists, no need to update, the job finished");
81
- return true;
82
- }
83
-
84
- // Save new clone data
85
- $this->log("Finish: {$this->options->clone}'s clone job's data is not in database, generating data");
86
-
87
- // sanitize the clone name before saving
88
- //$clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
89
-
90
- $this->options->existingClones[$this->clone] = array(
91
- "directoryName" => $this->options->cloneDirectoryName,
92
- "path" => ABSPATH . $this->options->cloneDirectoryName,
93
- "url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
94
- "number" => $this->options->cloneNumber,
95
- "version" => \WPStaging\WPStaging::VERSION,
96
- "status" => false,
97
- "prefix" => $this->options->prefix,
98
- );
99
-
100
- if (false === update_option("wpstg_existing_clones_beta", $this->options->existingClones))
101
- {
102
- $this->log("Finish: Failed to save {$this->options->clone}'s clone job data to database'");
103
- return false;
104
- }
105
-
106
- // Save scanned directories for a delete job
107
- //$this->saveScannedDirectories();
108
-
109
- return true;
110
- }
111
-
112
- /**
113
- * Save Scanned Directories for Delete Job Later
114
- */
115
- // protected function saveScannedDirectories()
116
- // {
117
- // // Save scanned directories for delete job
118
- // $this->cache->save("delete_directories_" . $this->options->clone, $this->options->scannedDirectories);
119
- //
120
- // $this->log("Successfully saved {$this->options->clone}'s clone job data to database'");
121
- // $this->log("Cloning job has finished!");
122
- // }
123
-
124
- /**
125
- * Get Upload Directory
126
- * @return string
127
- */
128
- // private function getUploadDirectory()
129
- // {
130
- // $wpUploadDirectory = wp_get_upload_dir();
131
- // $uploadDirectory = $wpUploadDirectory["basedir"] . DIRECTORY_SEPARATOR . WPStaging::SLUG;
132
- //
133
- // // Failed to create upload directory
134
- // if (!is_dir($uploadDirectory) && !wp_mkdir_p($uploadDirectory))
135
- // {
136
- // $this->log("Upload directory ({$uploadDirectory}) doesn't exist and failed to create!");
137
- // }
138
- //
139
- // $uploadDirectory = apply_filters("wpstg_get_upload_dir", $uploadDirectory);
140
- //
141
- // return rtrim($uploadDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
142
- // }
143
-
144
-
145
-
146
- /**
147
- * Get .htaccess rules
148
- * @return string
149
- */
150
- // private function getHtaccessRules()
151
- // {
152
- // // Prevent directory browsing and direct access to all files
153
- // $rules = "<Files \"*\">\n";
154
- // $rules .= "<IfModule mod_access.c>\n";
155
- // $rules .= "Deny from all\n";
156
- // $rules .= "</IfModule>\n";
157
- // $rules .= "<IfModule !mod_access_compat>\n";
158
- // $rules .= "<IfModule mod_authz_host.c>\n";
159
- // $rules .= "Deny from all\n";
160
- // $rules .= "</IfModule>\n";
161
- // $rules .= "</IfModule>\n";
162
- // $rules .= "<IfModule mod_access_compat>\n";
163
- // $rules .= "Deny from all\n";
164
- // $rules .= "</IfModule>\n";
165
- // $rules .= "</Files>\n";
166
- //
167
- // return apply_filters("wpstg_protected_directory_htaccess_rules", $rules);
168
- // }
169
-
170
- /**
171
- * Update .htaccess file and its rules
172
- * @param string $file
173
- * @param string $contents
174
- * @return bool
175
- */
176
- // private function updateHTAccess($file, $contents)
177
- // {
178
- // return (
179
- // (!$contents || $this->getHtaccessRules() !== $contents) &&
180
- // false === @file_put_contents($file, $this->getHtaccessRules())
181
- // );
182
- // }
183
-
184
- /**
185
- * Save HTAccess file
186
- */
187
- // private function saveHTAccess()
188
- // {
189
- // $uploadDir = $this->getUploadDirectory();
190
- // $htAccessFile = $uploadDir . ".htaccess";
191
- //
192
- // // .htaccess exists
193
- // if (file_exists($htAccessFile))
194
- // {
195
- // $contents = @file_get_contents($htAccessFile);
196
- //
197
- // // Rules doesn't match, update .htaccess rules
198
- // if (false === $this->updateHTAccess($htAccessFile, $contents))
199
- // {
200
- // $this->log("Failed to update {$htAccessFile}");
201
- // }
202
- // }
203
- // // .htaccess doesn't exists and
204
- // else if (wp_is_writable($uploadDir) && false === @file_put_contents($htAccessFile, $this->getHtaccessRules()))
205
- // {
206
- // $this->log("Failed to create {$htAccessFile}");
207
- // }
208
- // }
209
-
210
- /**
211
- * Save blank index file
212
- * @return bool
213
- */
214
- // private function saveBlankIndex()
215
- // {
216
- // $uploadDir = $this->getUploadDirectory();
217
- // $indexFile = $uploadDir . "index.php";
218
- //
219
- // if (file_exists($indexFile))
220
- // {
221
- // return true;
222
- // }
223
- //
224
- // $contents = "<?php" . PHP_EOL . "// WP-Staging protection file";
225
- //
226
- // if (!wp_is_writable($uploadDir) || false === @file_put_contents($indexFile, $contents))
227
- // {
228
- // $this->log("{$uploadDir} is not writable or couldn't generate {$indexFile}");
229
- // return false;
230
- // }
231
- //
232
- // return true;
233
- // }
234
-
235
- /**
236
- * Prepare protect directories and files
237
- * @param bool $force
238
- */
239
- // protected function protectDirectoriesAndFiles($force = false)
240
- // {
241
- // // Don't execute
242
- // if (true !== get_transient("wpstg_check_protection_files") && false === $force)
243
- // {
244
- // return;
245
- // }
246
- //
247
- // // Save .htaccess file
248
- // $this->saveHTAccess();
249
- //
250
- // // Save blank index.php file
251
- // $this->saveBlankIndex();
252
- //
253
- // // TODO put blank index to upload directories?? Why??
254
- //
255
- // // Check files once a day
256
- // set_transient("wpstg_check_protection_files", true, DAY_IN_SECONDS); // 24 hours in seconds
257
- //
258
- //
259
- // }
260
-
261
-
262
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ use WPStaging\WPStaging;
5
+
6
+ //error_reporting( E_ALL );
7
+
8
+ /**
9
+ * Class Finish
10
+ * @package WPStaging\Backend\Modules\Jobs
11
+ */
12
+ class Finish extends Job
13
+ {
14
+ /**
15
+ * Clone Key
16
+ * @var string
17
+ */
18
+ private $clone = '';
19
+
20
+ /**
21
+ * Start Module
22
+ * @return object
23
+ */
24
+ public function start()
25
+ {
26
+ // sanitize the clone name before saving
27
+ $this->clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
28
+
29
+ // Delete Cache Files
30
+ $this->deleteCacheFiles();
31
+
32
+ // Prepare clone records & save scanned directories for delete job later
33
+ $this->prepareCloneDataRecords();
34
+
35
+
36
+ $return = array(
37
+ "directoryName" => $this->options->cloneDirectoryName,
38
+ "path" => ABSPATH . $this->options->cloneDirectoryName,
39
+ "url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
40
+ "number" => $this->options->cloneNumber,
41
+ "version" => \WPStaging\WPStaging::VERSION,
42
+ "status" => false,
43
+ "prefix" => $this->options->prefix,
44
+ "last_msg" => $this->logger->getLastLogMsg(),
45
+ "job" => $this->options->currentJob
46
+ );
47
+
48
+ //$this->flush();
49
+
50
+ return (object) $return;
51
+ }
52
+
53
+ /**
54
+ * Delete Cache Files
55
+ */
56
+ protected function deleteCacheFiles()
57
+ {
58
+ $this->log("Finish: Deleting clone job's cache files...");
59
+
60
+ // Clean cache files
61
+ $this->cache->delete("clone_options");
62
+ $this->cache->delete("files_to_copy");
63
+
64
+ $this->log("Finish: Clone job's cache files have been deleted!");
65
+ }
66
+
67
+ /**
68
+ * Prepare clone records
69
+ * @return bool
70
+ */
71
+ protected function prepareCloneDataRecords()
72
+ {
73
+ // Check if clones still exist
74
+ $this->log("Finish: Verifying existing clones...");
75
+
76
+ // Clone data already exists
77
+ if (isset($this->options->existingClones[$this->options->clone]))
78
+ {
79
+ $this->log("Finish: Clone data already exists, no need to update, the job finished");
80
+ return true;
81
+ }
82
+
83
+ // Save new clone data
84
+ $this->log("Finish: {$this->options->clone}'s clone job's data is not in database, generating data");
85
+
86
+ // sanitize the clone name before saving
87
+ //$clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
88
+
89
+ $this->options->existingClones[$this->clone] = array(
90
+ "directoryName" => $this->options->cloneDirectoryName,
91
+ "path" => ABSPATH . $this->options->cloneDirectoryName,
92
+ "url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
93
+ "number" => $this->options->cloneNumber,
94
+ "version" => \WPStaging\WPStaging::VERSION,
95
+ "status" => false,
96
+ "prefix" => $this->options->prefix,
97
+ );
98
+
99
+ if (false === update_option("wpstg_existing_clones_beta", $this->options->existingClones))
100
+ {
101
+ $this->log("Finish: Failed to save {$this->options->clone}'s clone job data to database'");
102
+ return false;
103
+ }
104
+
105
+ return true;
106
+ }
107
+
108
+
109
+ /**
110
+ * Flush wpdb cache and permalinks
111
+ * @global type $wp_rewrite
112
+ */
113
+ // protected function flush() {
114
+ // // flush rewrite rules to prevent 404s
115
+ // // and other oddities
116
+ // wp_cache_flush();
117
+ // global $wp_rewrite;
118
+ // $wp_rewrite->init();
119
+ // flush_rewrite_rules(true); // true = hard refresh, recreates the .htaccess file
120
+ // }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
apps/Backend/Modules/Jobs/Job.php CHANGED
@@ -1,485 +1,488 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
- use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
11
- use WPStaging\Utils\Logger;
12
- use WPStaging\WPStaging;
13
- use WPStaging\Utils\Cache;
14
-
15
- /**
16
- * Class Job
17
- * @package WPStaging\Backend\Modules\Jobs
18
- */
19
- abstract class Job implements JobInterface
20
- {
21
-
22
- const EXECUTION_TIME_RATIO = 0.8;
23
-
24
- const MAX_MEMORY_RATIO = 0.8;
25
-
26
- /**
27
- * @var Cache
28
- */
29
- protected $cache;
30
-
31
- /**
32
- * @var Logger
33
- */
34
- protected $logger;
35
-
36
- /**
37
- * @var bool
38
- */
39
- protected $hasLoggedFileNameSet = false;
40
-
41
- /**
42
- * @var object
43
- */
44
- protected $options;
45
-
46
- /**
47
- * @var object
48
- */
49
- protected $settings;
50
-
51
- /**
52
- * System total maximum memory consumption
53
- * @var int
54
- */
55
- protected $maxMemoryLimit;
56
-
57
- /**
58
- * Script maximum memory consumption
59
- * @var int
60
- */
61
- protected $memoryLimit;
62
-
63
- /**
64
- * @var int
65
- */
66
- protected $maxExecutionTime;
67
-
68
-
69
- /**
70
- * @var int
71
- */
72
- protected $executionLimit;
73
-
74
- /**
75
- * @var int
76
- */
77
- protected $totalRecursion;
78
-
79
- /**
80
- * @var int
81
- */
82
- protected $maxRecursionLimit;
83
-
84
- /**
85
- * @var int
86
- */
87
- protected $start;
88
-
89
- /**
90
- * Job constructor.
91
- */
92
- public function __construct()
93
- {
94
- // Get max limits
95
- $this->start = $this->time();
96
- $this->maxMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
97
- //$this->maxExecutionTime = (int) ini_get("max_execution_time");
98
- $this->maxExecutionTime = (int) 30;
99
-
100
- // if ($this->maxExecutionTime < 1 || $this->maxExecutionTime > 30)
101
- // {
102
- // $this->maxExecutionTime = 30;
103
- // }
104
-
105
- // Services
106
- $this->cache = new Cache(-1, \WPStaging\WPStaging::getContentDir());
107
- $this->logger = WPStaging::getInstance()->get("logger");
108
-
109
- // Settings and Options
110
- $this->options = $this->cache->get("clone_options");
111
- //$this->settings = json_decode(json_encode(get_option("wpstg_settings", array())));
112
- $this->settings = (object) get_option("wpstg_settings", array());
113
-
114
- if (!$this->options)
115
- {
116
- $this->options = new \stdClass();
117
- }
118
-
119
- if (isset($this->options->existingClones) && is_object($this->options->existingClones))
120
- {
121
- $this->options->existingClones = json_decode(json_encode($this->options->existingClones), true);
122
- }
123
-
124
- // check default options
125
- if ( !isset($this->settings) ||
126
- !isset($this->settings->queryLimit) ||
127
- !isset($this->settings->batchSize) ||
128
- !isset($this->settings->cpuLoad) ||
129
- !isset($this->settings->fileLimit)
130
- )
131
-
132
- {
133
- $this->settings = new \stdClass();
134
- $this->setDefaultSettings();
135
- }
136
-
137
- // Set limits accordingly to CPU LIMITS
138
- $this->setLimits();
139
-
140
- $this->maxRecursionLimit = (int) ini_get("xdebug.max_nesting_level");
141
-
142
- /*
143
- * This is needed to make sure that maxRecursionLimit = -1
144
- * if xdebug is not used in production env.
145
- * For using xdebug, maxRecursionLimit must be larger
146
- * otherwise xdebug is throwing an error 500 while debugging
147
- */
148
- if ($this->maxRecursionLimit < 1)
149
- {
150
- $this->maxRecursionLimit = -1;
151
- }
152
- else
153
- {
154
- $this->maxRecursionLimit = $this->maxRecursionLimit - 50; // just to make sure
155
- }
156
-
157
- if (method_exists($this, "initialize"))
158
- {
159
- $this->initialize();
160
- }
161
- }
162
-
163
- /**
164
- * Job destructor
165
- */
166
- public function __destruct()
167
- {
168
- // Commit logs
169
- $this->logger->commit();
170
- }
171
-
172
- /**
173
- * Set default settings
174
- */
175
- protected function setDefaultSettings(){
176
- $this->settings->queryLimit = "1000";
177
- $this->settings->fileLimit = "1";
178
- $this->settings->batchSize = "2";
179
- $this->settings->cpuLoad = 'medium';
180
- update_option('wpstg_settings', $this->settings);
181
- }
182
-
183
- /**
184
- * Set limits accordingly to
185
- */
186
- protected function setLimits()
187
- {
188
-
189
- if (!isset($this->settings->cpuLoad))
190
- {
191
- $this->settings->cpuLoad = "medium";
192
- }
193
-
194
- //$memoryLimit= self::MAX_MEMORY_RATIO;
195
- $memoryLimit= 1;
196
- $timeLimit = self::EXECUTION_TIME_RATIO;
197
-
198
- switch($this->settings->cpuLoad)
199
- {
200
- case "medium":
201
- //$memoryLimit= $memoryLimit / 2; // 0.4
202
- $timeLimit = $timeLimit / 2;
203
- break;
204
- case "low":
205
- //$memoryLimit= $memoryLimit / 4; // 0.2
206
- $timeLimit = $timeLimit / 4;
207
- break;
208
-
209
- case "fast": // 0.8
210
- default:
211
- break;
212
- }
213
-
214
- $this->memoryLimit = $this->maxMemoryLimit * $memoryLimit;
215
- $this->executionLimit = $this->maxExecutionTime * $timeLimit;
216
- }
217
-
218
- /**
219
- * Save options
220
- * @param null|array|object $options
221
- * @return bool
222
- */
223
- protected function saveOptions($options = null)
224
- {
225
- // Get default options
226
- if (null === $options)
227
- {
228
- $options = $this->options;
229
- }
230
-
231
- // Ensure that it is an object
232
- $options = json_decode(json_encode($options));
233
- return $this->cache->save("clone_options", $options);
234
- }
235
-
236
- /**
237
- * @return object
238
- */
239
- public function getOptions()
240
- {
241
- return $this->options;
242
- }
243
-
244
- /**
245
- * @param string $memory
246
- * @return int
247
- */
248
- protected function getMemoryInBytes($memory)
249
- {
250
- // Handle unlimited ones
251
- if (1 > (int) $memory)
252
- {
253
- return (int) $memory;
254
- }
255
-
256
- $bytes = (int) $memory; // grab only the number
257
- $size = trim(str_replace($bytes, null, strtolower($memory))); // strip away number and lower-case it
258
-
259
- // Actual calculation
260
- switch($size)
261
- {
262
- case 'k':
263
- $bytes *= 1024;
264
- break;
265
- case 'm':
266
- $bytes *= (1024 * 1024);
267
- break;
268
- case 'g':
269
- $bytes *= (1024 * 1024 * 1024);
270
- break;
271
- }
272
-
273
- return $bytes;
274
- }
275
-
276
- /**
277
- * Format bytes into ini_set favorable form
278
- * @param int $bytes
279
- * @return string
280
- */
281
- protected function formatBytes($bytes)
282
- {
283
- if ((int) $bytes < 1)
284
- {
285
- return '';
286
- }
287
-
288
- $units = array('B', 'K', 'M', 'G'); // G since PHP 5.1.x so we are good!
289
-
290
- $bytes = (int) $bytes;
291
- $base = log($bytes) / log(1000);
292
- $pow = pow(1000, $base - floor($base));
293
-
294
- return round($pow, 0) . $units[(int) floor($base)];
295
- }
296
-
297
- /**
298
- * Get current time in seconds
299
- * @return float
300
- */
301
- protected function time()
302
- {
303
- $time = microtime();
304
- $time = explode(' ', $time);
305
- $time = $time[1] + $time[0];
306
- return $time;
307
- }
308
-
309
- /**
310
- * @return bool
311
- */
312
- protected function isOverThreshold()
313
- {
314
- // Check if the memory is over threshold
315
- $usedMemory = (int) @memory_get_usage(true);
316
-
317
- $this->debugLog('Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Max Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script Memory Limit: ' . $this->formatBytes( $this->memoryLimit), Logger::TYPE_DEBUG );
318
-
319
- if ($usedMemory >= $this->memoryLimit)
320
- {
321
- $this->log('Used Memory: ' . $this->formatBytes($usedMemory) . ' Memory Limit: ' . $this->formatBytes($this->maxMemoryLimit) . ' Max Script memory limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_ERROR );
322
- //$this->resetMemory();
323
- return true;
324
- }
325
-
326
- if ($this->isRecursionLimit())
327
- {
328
- //$this->log('RESET RECURSION');
329
- return true;
330
- }
331
-
332
- // Check if execution time is over threshold
333
- ///$time = round($this->start + $this->time(), 4);
334
- $time = round($this->time() - $this->start, 4);
335
-
336
- if ($time >= $this->executionLimit)
337
- {
338
- $this->log('RESET TIME: current time: ' . $time . ', Start Time: ' . $this->start . ', exec time limit: ' . $this->executionLimit);
339
- return true;
340
- }
341
-
342
- return false;
343
- }
344
-
345
- /**
346
- * Attempt to reset memory
347
- * @return bool
348
- *
349
- */
350
- protected function resetMemory()
351
- {
352
- $newMemoryLimit = $this->maxMemoryLimit * 2;
353
-
354
- // Failed to set
355
- if (false === ini_set("memory_limit", $this->formatBytes($newMemoryLimit)))
356
- {
357
- $this->log('Can not free some memory', Logger::TYPE_CRITICAL);
358
- return false;
359
- }
360
-
361
- // Double checking
362
- $newMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
363
- if ($newMemoryLimit <= $this->maxMemoryLimit)
364
- {
365
- return false;
366
- }
367
-
368
- // Set the new Maximum memory limit
369
- $this->maxMemoryLimit = $newMemoryLimit;
370
-
371
- // Calculate threshold limit
372
- $this->memoryLimit = $newMemoryLimit * self::MAX_MEMORY_RATIO;
373
-
374
- return true;
375
- }
376
-
377
- /**
378
- * Attempt to reset time
379
- * @return bool
380
- *
381
- * @deprecated since version 2.0.0
382
-
383
- */
384
- // protected function resetTime()
385
- // {
386
- // // Attempt to reset timeout
387
- // if (!@set_time_limit($this->maxExecutionTime))
388
- // {
389
- // return false;
390
- // }
391
- //
392
- // // Increase execution limit
393
- // $this->executionLimit = $this->executionLimit * 2;
394
- //
395
- // return true;
396
- // }
397
-
398
- /**
399
- * Reset time limit and memory
400
- * @return bool
401
- *
402
- * @deprecated since version 2.0.0
403
- */
404
- // protected function reset()
405
- // {
406
- // // Attempt to reset time
407
- // if (!$this->resetTime())
408
- // {
409
- // return false;
410
- // }
411
- //
412
- // // Attempt to reset memory
413
- // if (!$this->resetMemory())
414
- // {
415
- // return false;
416
- // }
417
- //
418
- // return true;
419
- // }
420
-
421
- /**
422
- * Checks if calls are over recursion limit
423
- * @return bool
424
- */
425
- protected function isRecursionLimit()
426
- {
427
- return ($this->maxRecursionLimit > 0 && $this->totalRecursion >= $this->maxRecursionLimit);
428
- }
429
-
430
- /**
431
- * @param string $msg
432
- * @param string $type
433
- */
434
- protected function log($msg, $type = Logger::TYPE_INFO)
435
- {
436
-
437
- if (!isset($this->options->clone)){
438
- $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
439
- }
440
-
441
- if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
442
- {
443
- $this->logger->setFileName($this->options->clone);
444
- $this->hasLoggedFileNameSet = true;
445
- }
446
-
447
- $this->logger->add($msg, $type);
448
- }
449
- /**
450
- * @param string $msg
451
- * @param string $type
452
- */
453
- protected function debugLog($msg, $type = Logger::TYPE_INFO)
454
- {
455
-
456
- if (!isset($this->options->clone)){
457
- $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
458
- }
459
-
460
- if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
461
- {
462
- $this->logger->setFileName($this->options->clone);
463
- $this->hasLoggedFileNameSet = true;
464
- }
465
-
466
-
467
- if (isset($this->settings->debugMode)){
468
- $this->logger->add($msg, $type);
469
- }
470
-
471
- }
472
-
473
- /**
474
- * Throw a errror message via json and stop further execution
475
- * @param string $message
476
- */
477
- protected function returnException($message = ''){
478
- wp_die( json_encode(array(
479
- 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
480
- 'status' => false,
481
- 'message' => $message,
482
- 'error' => true
483
- )));
484
- }
 
 
 
485
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ // No Direct Access
5
+ if (!defined("WPINC"))
6
+ {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
11
+ use WPStaging\Utils\Logger;
12
+ use WPStaging\WPStaging;
13
+ use WPStaging\Utils\Cache;
14
+
15
+ /**
16
+ * Class Job
17
+ * @package WPStaging\Backend\Modules\Jobs
18
+ */
19
+ abstract class Job implements JobInterface
20
+ {
21
+
22
+ const EXECUTION_TIME_RATIO = 0.8;
23
+
24
+ const MAX_MEMORY_RATIO = 0.8;
25
+
26
+ /**
27
+ * @var Cache
28
+ */
29
+ protected $cache;
30
+
31
+ /**
32
+ * @var Logger
33
+ */
34
+ protected $logger;
35
+
36
+ /**
37
+ * @var bool
38
+ */
39
+ protected $hasLoggedFileNameSet = false;
40
+
41
+ /**
42
+ * @var object
43
+ */
44
+ protected $options;
45
+
46
+ /**
47
+ * @var object
48
+ */
49
+ protected $settings;
50
+
51
+ /**
52
+ * System total maximum memory consumption
53
+ * @var int
54
+ */
55
+ protected $maxMemoryLimit;
56
+
57
+ /**
58
+ * Script maximum memory consumption
59
+ * @var int
60
+ */
61
+ protected $memoryLimit;
62
+
63
+ /**
64
+ * @var int
65
+ */
66
+ protected $maxExecutionTime;
67
+
68
+
69
+ /**
70
+ * @var int
71
+ */
72
+ protected $executionLimit;
73
+
74
+ /**
75
+ * @var int
76
+ */
77
+ protected $totalRecursion;
78
+
79
+ /**
80
+ * @var int
81
+ */
82
+ protected $maxRecursionLimit;
83
+
84
+ /**
85
+ * @var int
86
+ */
87
+ protected $start;
88
+
89
+ /**
90
+ * Job constructor.
91
+ */
92
+ public function __construct()
93
+ {
94
+ // Get max limits
95
+ $this->start = $this->time();
96
+ $this->maxMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
97
+
98
+
99
+ //$this->maxExecutionTime = (int) ini_get("max_execution_time");
100
+ $this->maxExecutionTime = (int) 30;
101
+
102
+ // if ($this->maxExecutionTime < 1 || $this->maxExecutionTime > 30)
103
+ // {
104
+ // $this->maxExecutionTime = 30;
105
+ // }
106
+
107
+ // Services
108
+ $this->cache = new Cache(-1, \WPStaging\WPStaging::getContentDir());
109
+ $this->logger = WPStaging::getInstance()->get("logger");
110
+
111
+ // Settings and Options
112
+ $this->options = $this->cache->get("clone_options");
113
+ //$this->settings = json_decode(json_encode(get_option("wpstg_settings", array())));
114
+ $this->settings = (object) get_option("wpstg_settings", array());
115
+
116
+ if (!$this->options)
117
+ {
118
+ $this->options = new \stdClass();
119
+ }
120
+
121
+ if (isset($this->options->existingClones) && is_object($this->options->existingClones))
122
+ {
123
+ $this->options->existingClones = json_decode(json_encode($this->options->existingClones), true);
124
+ }
125
+
126
+ // check default options
127
+ if ( !isset($this->settings) ||
128
+ !isset($this->settings->queryLimit) ||
129
+ !isset($this->settings->batchSize) ||
130
+ !isset($this->settings->cpuLoad) ||
131
+ !isset($this->settings->fileLimit)
132
+ )
133
+
134
+ {
135
+ $this->settings = new \stdClass();
136
+ $this->setDefaultSettings();
137
+ }
138
+
139
+ // Set limits accordingly to CPU LIMITS
140
+ $this->setLimits();
141
+
142
+ $this->maxRecursionLimit = (int) ini_get("xdebug.max_nesting_level");
143
+
144
+ /*
145
+ * This is needed to make sure that maxRecursionLimit = -1
146
+ * if xdebug is not used in production env.
147
+ * For using xdebug, maxRecursionLimit must be larger
148
+ * otherwise xdebug is throwing an error 500 while debugging
149
+ */
150
+ if ($this->maxRecursionLimit < 1)
151
+ {
152
+ $this->maxRecursionLimit = -1;
153
+ }
154
+ else
155
+ {
156
+ $this->maxRecursionLimit = $this->maxRecursionLimit - 50; // just to make sure
157
+ }
158
+
159
+ if (method_exists($this, "initialize"))
160
+ {
161
+ $this->initialize();
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Job destructor
167
+ */
168
+ public function __destruct()
169
+ {
170
+ // Commit logs
171
+ $this->logger->commit();
172
+ }
173
+
174
+ /**
175
+ * Set default settings
176
+ */
177
+ protected function setDefaultSettings(){
178
+ $this->settings->queryLimit = "5000";
179
+ $this->settings->fileLimit = "1";
180
+ $this->settings->batchSize = "2";
181
+ $this->settings->cpuLoad = 'medium';
182
+ update_option('wpstg_settings', $this->settings);
183
+ }
184
+
185
+ /**
186
+ * Set limits accordingly to
187
+ */
188
+ protected function setLimits()
189
+ {
190
+
191
+ if (!isset($this->settings->cpuLoad))
192
+ {
193
+ $this->settings->cpuLoad = "medium";
194
+ }
195
+
196
+ $memoryLimit= self::MAX_MEMORY_RATIO;
197
+ $timeLimit = self::EXECUTION_TIME_RATIO;
198
+
199
+ switch($this->settings->cpuLoad)
200
+ {
201
+ case "medium":
202
+ //$memoryLimit= $memoryLimit / 2; // 0.4
203
+ $timeLimit = $timeLimit / 2;
204
+ break;
205
+ case "low":
206
+ //$memoryLimit= $memoryLimit / 4; // 0.2
207
+ $timeLimit = $timeLimit / 4;
208
+ break;
209
+
210
+ case "fast": // 0.8
211
+ default:
212
+ break;
213
+ }
214
+
215
+ $this->memoryLimit = $this->maxMemoryLimit * $memoryLimit;
216
+ $this->executionLimit = $this->maxExecutionTime * $timeLimit;
217
+ }
218
+
219
+ /**
220
+ * Save options
221
+ * @param null|array|object $options
222
+ * @return bool
223
+ */
224
+ protected function saveOptions($options = null)
225
+ {
226
+ // Get default options
227
+ if (null === $options)
228
+ {
229
+ $options = $this->options;
230
+ }
231
+
232
+ // Ensure that it is an object
233
+ $options = json_decode(json_encode($options));
234
+ return $this->cache->save("clone_options", $options);
235
+ }
236
+
237
+ /**
238
+ * @return object
239
+ */
240
+ public function getOptions()
241
+ {
242
+ return $this->options;
243
+ }
244
+
245
+ /**
246
+ * @param string $memory
247
+ * @return int
248
+ */
249
+ protected function getMemoryInBytes($memory)
250
+ {
251
+ // Handle unlimited ones
252
+ if (1 > (int) $memory)
253
+ {
254
+ //return (int) $memory;
255
+ // 128 MB default value
256
+ return (int) 134217728;
257
+ }
258
+
259
+ $bytes = (int) $memory; // grab only the number
260
+ $size = trim(str_replace($bytes, null, strtolower($memory))); // strip away number and lower-case it
261
+
262
+ // Actual calculation
263
+ switch($size)
264
+ {
265
+ case 'k':
266
+ $bytes *= 1024;
267
+ break;
268
+ case 'm':
269
+ $bytes *= (1024 * 1024);
270
+ break;
271
+ case 'g':
272
+ $bytes *= (1024 * 1024 * 1024);
273
+ break;
274
+ }
275
+
276
+ return $bytes;
277
+ }
278
+
279
+ /**
280
+ * Format bytes into ini_set favorable form
281
+ * @param int $bytes
282
+ * @return string
283
+ */
284
+ protected function formatBytes($bytes)
285
+ {
286
+ if ((int) $bytes < 1)
287
+ {
288
+ return '';
289
+ }
290
+
291
+ $units = array('B', 'K', 'M', 'G'); // G since PHP 5.1.x so we are good!
292
+
293
+ $bytes = (int) $bytes;
294
+ $base = log($bytes) / log(1000);
295
+ $pow = pow(1000, $base - floor($base));
296
+
297
+ return round($pow, 0) . $units[(int) floor($base)];
298
+ }
299
+
300
+ /**
301
+ * Get current time in seconds
302
+ * @return float
303
+ */
304
+ protected function time()
305
+ {
306
+ $time = microtime();
307
+ $time = explode(' ', $time);
308
+ $time = $time[1] + $time[0];
309
+ return $time;
310
+ }
311
+
312
+ /**
313
+ * @return bool
314
+ */
315
+ protected function isOverThreshold()
316
+ {
317
+ // Check if the memory is over threshold
318
+ $usedMemory = (int) @memory_get_usage(true);
319
+
320
+ $this->debugLog('Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Max Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script Memory Limit: ' . $this->formatBytes( $this->memoryLimit), Logger::TYPE_DEBUG );
321
+
322
+ if ($usedMemory >= $this->memoryLimit)
323
+ {
324
+ $this->log('Used Memory: ' . $this->formatBytes($usedMemory) . ' Memory Limit: ' . $this->formatBytes($this->maxMemoryLimit) . ' Max Script memory limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_ERROR );
325
+ //$this->resetMemory();
326
+ return true;
327
+ }
328
+
329
+ if ($this->isRecursionLimit())
330
+ {
331
+ //$this->log('RESET RECURSION');
332
+ return true;
333
+ }
334
+
335
+ // Check if execution time is over threshold
336
+ ///$time = round($this->start + $this->time(), 4);
337
+ $time = round($this->time() - $this->start, 4);
338
+
339
+ if ($time >= $this->executionLimit)
340
+ {
341
+ $this->debugLog('RESET TIME: current time: ' . $time . ', Start Time: ' . $this->start . ', exec time limit: ' . $this->executionLimit);
342
+ return true;
343
+ }
344
+
345
+ return false;
346
+ }
347
+
348
+ /**
349
+ * Attempt to reset memory
350
+ * @return bool
351
+ * memory
352
+ */
353
+ // protected function resetMemory()
354
+ // {
355
+ // $newMemoryLimit = $this->maxMemoryLimit * 2;
356
+ //
357
+ // // Failed to set
358
+ // if (false === ini_set("memory_limit", $this->formatBytes($newMemoryLimit)))
359
+ // {
360
+ // $this->log('Can not free some memory', Logger::TYPE_CRITICAL);
361
+ // return false;
362
+ // }
363
+ //
364
+ // // Double checking
365
+ // $newMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
366
+ // if ($newMemoryLimit <= $this->maxMemoryLimit)
367
+ // {
368
+ // return false;
369
+ // }
370
+ //
371
+ // // Set the new Maximum memory limit
372
+ // $this->maxMemoryLimit = $newMemoryLimit;
373
+ //
374
+ // // Calculate threshold limit
375
+ // $this->memoryLimit = $newMemoryLimit * self::MAX_MEMORY_RATIO;
376
+ //
377
+ // return true;
378
+ // }
379
+
380
+ /**
381
+ * Attempt to reset time
382
+ * @return bool
383
+ *
384
+ * @deprecated since version 2.0.0
385
+
386
+ */
387
+ // protected function resetTime()
388
+ // {
389
+ // // Attempt to reset timeout
390
+ // if (!@set_time_limit($this->maxExecutionTime))
391
+ // {
392
+ // return false;
393
+ // }
394
+ //
395
+ // // Increase execution limit
396
+ // $this->executionLimit = $this->executionLimit * 2;
397
+ //
398
+ // return true;
399
+ // }
400
+
401
+ /**
402
+ * Reset time limit and memory
403
+ * @return bool
404
+ *
405
+ * @deprecated since version 2.0.0
406
+ */
407
+ // protected function reset()
408
+ // {
409
+ // // Attempt to reset time
410
+ // if (!$this->resetTime())
411
+ // {
412
+ // return false;
413
+ // }
414
+ //
415
+ // // Attempt to reset memory
416
+ // if (!$this->resetMemory())
417
+ // {
418
+ // return false;
419
+ // }
420
+ //
421
+ // return true;
422
+ // }
423
+
424
+ /**
425
+ * Checks if calls are over recursion limit
426
+ * @return bool
427
+ */
428
+ protected function isRecursionLimit()
429
+ {
430
+ return ($this->maxRecursionLimit > 0 && $this->totalRecursion >= $this->maxRecursionLimit);
431
+ }
432
+
433
+ /**
434
+ * @param string $msg
435
+ * @param string $type
436
+ */
437
+ protected function log($msg, $type = Logger::TYPE_INFO)
438
+ {
439
+
440
+ if (!isset($this->options->clone)){
441
+ $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
442
+ }
443
+
444
+ if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
445
+ {
446
+ $this->logger->setFileName($this->options->clone);
447
+ $this->hasLoggedFileNameSet = true;
448
+ }
449
+
450
+ $this->logger->add($msg, $type);
451
+ }
452
+ /**
453
+ * @param string $msg
454
+ * @param string $type
455
+ */
456
+ protected function debugLog($msg, $type = Logger::TYPE_INFO)
457
+ {
458
+
459
+ if (!isset($this->options->clone)){
460
+ $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
461
+ }
462
+
463
+ if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
464
+ {
465
+ $this->logger->setFileName($this->options->clone);
466
+ $this->hasLoggedFileNameSet = true;
467
+ }
468
+
469
+
470
+ if (isset($this->settings->debugMode)){
471
+ $this->logger->add($msg, $type);
472
+ }
473
+
474
+ }
475
+
476
+ /**
477
+ * Throw a errror message via json and stop further execution
478
+ * @param string $message
479
+ */
480
+ protected function returnException($message = ''){
481
+ wp_die( json_encode(array(
482
+ 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
483
+ 'status' => false,
484
+ 'message' => $message,
485
+ 'error' => true
486
+ )));
487
+ }
488
  }
apps/Backend/Modules/Jobs/JobExecutable.php CHANGED
@@ -1,112 +1,112 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
-
11
- /**
12
- * Class JobExecutable
13
- * I'm sorry for such mess, we need to support PHP 5.3
14
- * @package WPStaging\Backend\Modules\Jobs
15
- */
16
- abstract class JobExecutable extends Job
17
- {
18
-
19
- /**
20
- * @var array
21
- */
22
- protected $response = array(
23
- "status" => false,
24
- "percentage" => 0,
25
- "total" => 0,
26
- "step" => 0,
27
- "last_msg" => '',
28
- );
29
-
30
- /**
31
- * JobExecutable constructor.
32
- */
33
- public function __construct()
34
- {
35
- parent::__construct();
36
-
37
- // Calculate total steps
38
- $this->calculateTotalSteps();
39
- }
40
-
41
- /**
42
- * Prepare Response Array
43
- * @param bool $status
44
- * @param bool $incrementCurrentStep
45
- * @return array
46
- */
47
- protected function prepareResponse($status = false, $incrementCurrentStep = true)
48
- {
49
- if ($incrementCurrentStep)
50
- {
51
- $this->options->currentStep++;
52
- }
53
-
54
- $percentage = round(($this->options->currentStep / $this->options->totalSteps) * 100);
55
- $percentage = (100 < $percentage) ? 100 : $percentage;
56
-
57
- return $this->response = array(
58
- "status" => $status,
59
- "percentage" => $percentage,
60
- "total" => $this->options->totalSteps,
61
- "step" => $this->options->currentStep,
62
- "job" => $this->options->currentJob,
63
- "last_msg" => $this->logger->getLastLogMsg(),
64
- "running_time" => $this->time() - time(),
65
- "job_done" => $status
66
- );
67
- }
68
-
69
- /**
70
- * Start Module
71
- * @return object
72
- */
73
- public function start()
74
- {
75
- // Execute steps
76
- $this->run();
77
-
78
- // Save option, progress
79
- $this->saveOptions();
80
-
81
- return (object) $this->response;
82
- }
83
-
84
- /**
85
- * Run Steps
86
- */
87
- protected function run()
88
- {
89
- // Execute steps
90
- for ($i = 0; $i < $this->options->totalSteps; $i++)
91
- {
92
- // Job is finished or over threshold limits was hit
93
- if (!$this->execute())
94
- {
95
- break;
96
- }
97
- }
98
- }
99
-
100
- /**
101
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
102
- * @return void
103
- */
104
- abstract protected function calculateTotalSteps();
105
-
106
- /**
107
- * Execute the Current Step
108
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
109
- * @return bool
110
- */
111
- abstract protected function execute();
112
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ // No Direct Access
5
+ if (!defined("WPINC"))
6
+ {
7
+ die;
8
+ }
9
+
10
+
11
+ /**
12
+ * Class JobExecutable
13
+ * I'm sorry for such mess, we need to support PHP 5.3
14
+ * @package WPStaging\Backend\Modules\Jobs
15
+ */
16
+ abstract class JobExecutable extends Job
17
+ {
18
+
19
+ /**
20
+ * @var array
21
+ */
22
+ protected $response = array(
23
+ "status" => false,
24
+ "percentage" => 0,
25
+ "total" => 0,
26
+ "step" => 0,
27
+ "last_msg" => '',
28
+ );
29
+
30
+ /**
31
+ * JobExecutable constructor.
32
+ */
33
+ public function __construct()
34
+ {
35
+ parent::__construct();
36
+
37
+ // Calculate total steps
38
+ $this->calculateTotalSteps();
39
+ }
40
+
41
+ /**
42
+ * Prepare Response Array
43
+ * @param bool $status
44
+ * @param bool $incrementCurrentStep
45
+ * @return array
46
+ */
47
+ protected function prepareResponse($status = false, $incrementCurrentStep = true)
48
+ {
49
+ if ($incrementCurrentStep)
50
+ {
51
+ $this->options->currentStep++;
52
+ }
53
+
54
+ $percentage = round(($this->options->currentStep / $this->options->totalSteps) * 100);
55
+ $percentage = (100 < $percentage) ? 100 : $percentage;
56
+
57
+ return $this->response = array(
58
+ "status" => $status,
59
+ "percentage" => $percentage,
60
+ "total" => $this->options->totalSteps,
61
+ "step" => $this->options->currentStep,
62
+ "job" => $this->options->currentJob,
63
+ "last_msg" => $this->logger->getLastLogMsg(),
64
+ "running_time" => $this->time() - time(),
65
+ "job_done" => $status
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Start Module
71
+ * @return object
72
+ */
73
+ public function start()
74
+ {
75
+ // Execute steps
76
+ $this->run();
77
+
78
+ // Save option, progress
79
+ $this->saveOptions();
80
+
81
+ return (object) $this->response;
82
+ }
83
+
84
+ /**
85
+ * Run Steps
86
+ */
87
+ protected function run()
88
+ {
89
+ // Execute steps
90
+ for ($i = 0; $i < $this->options->totalSteps; $i++)
91
+ {
92
+ // Job is finished or over threshold limits was hit
93
+ if (!$this->execute())
94
+ {
95
+ break;
96
+ }
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
102
+ * @return void
103
+ */
104
+ abstract protected function calculateTotalSteps();
105
+
106
+ /**
107
+ * Execute the Current Step
108
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
109
+ * @return bool
110
+ */
111
+ abstract protected function execute();
112
  }
apps/Backend/Modules/Jobs/Scan.php CHANGED
@@ -1,402 +1,407 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
- use WPStaging\Utils\Directories;
11
- use WPStaging\WPStaging;
12
-
13
- /**
14
- * Class Scan
15
- * @package WPStaging\Backend\Modules\Jobs
16
- */
17
- class Scan extends Job
18
- {
19
-
20
- /**
21
- * @var array
22
- */
23
- private $directories = array();
24
-
25
- /**
26
- * @var Directories
27
- */
28
- private $objDirectories;
29
-
30
-
31
- /**
32
- * Upon class initialization
33
- */
34
- protected function initialize()
35
- {
36
- $this->objDirectories = new Directories();
37
-
38
- // Database Tables
39
- $this->getTables();
40
-
41
- // Get directories
42
- $this->directories();
43
-
44
- $this->db = WPStaging::getInstance()->get('wpdb');
45
- $this->prefix = $this->db->prefix;
46
-
47
-
48
- }
49
-
50
- /**
51
- * Start Module
52
- * @return $this
53
- */
54
- public function start()
55
- {
56
- // Basic Options
57
- $this->options->root = str_replace(array("\\", '/'), DIRECTORY_SEPARATOR, ABSPATH);
58
- $this->options->existingClones = get_option("wpstg_existing_clones_beta", array());
59
- $this->options->current = null;
60
-
61
- if (isset($_POST["clone"]) && array_key_exists($_POST["clone"], $this->options->existingClones))
62
- {
63
- $this->options->current = $_POST["clone"];
64
- }
65
-
66
- // Tables
67
- //$this->options->excludedTables = array();
68
- $this->options->clonedTables = array();
69
-
70
- // Files
71
- $this->options->totalFiles = 0;
72
- $this->options->totalFileSize = 0;
73
- $this->options->copiedFiles = 0;
74
-
75
-
76
- // Directories
77
- $this->options->includedDirectories = array();
78
- $this->options->excludedDirectories = array();
79
- $this->options->extraDirectories = array();
80
- $this->options->directoriesToCopy = array();
81
- $this->options->scannedDirectories = array();
82
-
83
- // Job
84
- $this->options->currentJob = "database";
85
- $this->options->currentStep = 0;
86
- $this->options->totalSteps = 0;
87
-
88
- // Delete previous cached files
89
- $this->cache->delete("files_to_copy");
90
- $this->cache->delete("clone_options");
91
- //$this->cache->delete("files_to_verify");
92
- //$this->cache->delete("files_verified");
93
-
94
- // Save options
95
- $this->saveOptions();
96
-
97
- return $this;
98
- }
99
-
100
- /**
101
- * Format bytes into human readable form
102
- * @param int $bytes
103
- * @param int $precision
104
- * @return string
105
- */
106
- public function formatSize($bytes, $precision = 2)
107
- {
108
- if ((double) $bytes < 1)
109
- {
110
- return '';
111
- }
112
-
113
- $units = array('B', "KB", "MB", "GB", "TB");
114
-
115
- $bytes = (double) $bytes;
116
- $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
117
- $pow = pow(1000, $base - floor($base)); // Same rule for 1000
118
-
119
- return round($pow, $precision) . ' ' . $units[(int) floor($base)];
120
- }
121
-
122
- /**
123
- * @param null|string $directories
124
- * @param bool $forceDisabled
125
- * @return string
126
- */
127
- public function directoryListing($directories = null, $forceDisabled = false)
128
- {
129
- if (null == $directories)
130
- {
131
- $directories = $this->directories;
132
- }
133
-
134
- $output = '';
135
- foreach ($directories as $name => $directory)
136
- {
137
- // Need to preserve keys so no array_shift()
138
- $data = reset($directory);
139
- unset($directory[key($directory)]);
140
-
141
- $isChecked = (
142
- empty($this->options->includedDirectories) ||
143
- in_array($data["path"], $this->options->includedDirectories)
144
- );
145
-
146
- $isDisabled = ($this->options->existingClones && isset($this->options->existingClones[$name]));
147
-
148
- $output .= "<div class='wpstg-dir'>";
149
- $output .= "<input type='checkbox' class='wpstg-check-dir'";
150
-
151
- if ($isChecked && !$isDisabled && !$forceDisabled) $output .= " checked";
152
- if ($forceDisabled || $isDisabled) $output .= " disabled";
153
-
154
- $output .= " name='selectedDirectories[]' value='{$data["path"]}'>";
155
-
156
- $output .= "<a href='#' class='wpstg-expand-dirs";
157
- if (!$isChecked || $isDisabled) $output .= " disabled";
158
- $output .= "'>{$name}";
159
- $output .= "</a>";
160
-
161
- $output .= "<span class='wpstg-size-info'>{$this->formatSize($data["size"])}</span>";
162
-
163
- if (!empty($directory))
164
- {
165
- $output .= "<div class='wpstg-dir wpstg-subdir'>";
166
- $output .= $this->directoryListing($directory, $isDisabled);
167
- $output .= "</div>";
168
- }
169
-
170
- $output .= "</div>";
171
- }
172
-
173
- return $output;
174
- }
175
-
176
- /**
177
- * Checks if there is enough free disk space to create staging site
178
- * Returns null when can't run disk_free_space function one way or another
179
- * @return bool|null
180
- */
181
- public function hasFreeDiskSpace() {
182
- if( !function_exists( "disk_free_space" ) ) {
183
- return null;
184
- }
185
-
186
- $freeSpace = @disk_free_space( ABSPATH );
187
-
188
- if( false === $freeSpace ) {
189
- $data = array(
190
- 'freespace' => false,
191
- 'usedspace' => $this->formatSize($this->getDirectorySizeInclSubdirs(ABSPATH))
192
- );
193
- echo json_encode($data);
194
- die();
195
- }
196
-
197
-
198
- $data = array(
199
- 'freespace' => $this->formatSize($freeSpace),
200
- 'usedspace' => $this->formatSize($this->getDirectorySizeInclSubdirs(ABSPATH))
201
- );
202
-
203
- echo json_encode( $data );
204
- die();
205
- }
206
-
207
- /**
208
- * Get Database Tables
209
- */
210
- protected function getTables()
211
- {
212
- $wpDB = WPStaging::getInstance()->get("wpdb");
213
-
214
- if (strlen($wpDB->prefix) > 0)
215
- {
216
- $prefix = str_replace('_', '', $wpDB->prefix);
217
- //$sql = "SHOW TABLE STATUS LIKE '{$prefix}\%'";
218
- $sql = "SHOW TABLE STATUS LIKE '{$wpDB->prefix}%'";
219
- }
220
- else
221
- {
222
- $sql = "SHOW TABLE STATUS";
223
- }
224
-
225
- $tables = $wpDB->get_results($sql);
226
-
227
- $currentTables = array();
228
-
229
- // Reset excluded Tables than loop through all tables
230
- $this->options->excludedTables = array();
231
- foreach ($tables as $table)
232
- {
233
-
234
- // Exclude WP Staging Tables
235
- // if (0 === strpos($table->Name, "wpstg"))
236
- // {
237
- // continue;
238
- // }
239
- // Create array of unchecked tables
240
- if (0 !== strpos($table->Name, $wpDB->prefix))
241
- {
242
- $this->options->excludedTables[] = $table->Name;
243
- }
244
-
245
-
246
- $currentTables[] = array(
247
- "name" => $table->Name,
248
- "size" => ($table->Data_length + $table->Index_length)
249
- );
250
- }
251
-
252
- $this->options->tables = json_decode(json_encode($currentTables));
253
- }
254
-
255
- /**
256
- * Get directories and main meta data about'em recursively
257
- */
258
- protected function directories()
259
- {
260
- $directories = new \DirectoryIterator(ABSPATH);
261
-
262
- foreach($directories as $directory)
263
- {
264
- // Not a valid directory
265
- if (false === ($path = $this->getPath($directory)))
266
- {
267
- continue;
268
- }
269
-
270
- $this->handleDirectory($path);
271
-
272
- // Get Sub-directories
273
- $this->getSubDirectories($directory->getRealPath());
274
- }
275
-
276
- // Gather Plugins
277
- $this->getSubDirectories(WP_PLUGIN_DIR);
278
-
279
- // Gather Themes
280
- $this->getSubDirectories(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "themes");
281
-
282
- // Gather Uploads
283
- $this->getSubDirectories(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "uploads");
284
- }
285
-
286
- /**
287
- * @param string $path
288
- */
289
- protected function getSubDirectories($path)
290
- {
291
- $directories = new \DirectoryIterator($path);
292
-
293
- foreach($directories as $directory)
294
- {
295
- // Not a valid directory
296
- if (false === ($path = $this->getPath($directory)))
297
- {
298
- continue;
299
- }
300
-
301
- $this->handleDirectory($path);
302
- }
303
- }
304
-
305
- /**
306
- * Get Path from $directory
307
- * @param \SplFileInfo $directory
308
- * @return string|false
309
- */
310
- protected function getPath($directory)
311
- {
312
-
313
- /*
314
- * Do not follow root path like src/web/..
315
- * This must be done before \SplFileInfo->isDir() is used!
316
- * Prevents open base dir restriction fatal errors
317
- */
318
- if (strpos( $directory->getRealPath(), ABSPATH ) !== 0 ) {
319
- return false;
320
- }
321
- $path = str_replace(ABSPATH, null, $directory->getRealPath());
322
-
323
- // Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
324
- if (!$directory->isDir() || strlen($path) < 1)
325
- {
326
- return false;
327
- }
328
-
329
- return $path;
330
- }
331
-
332
- /**
333
- * Organizes $this->directories
334
- * @param string $path
335
- */
336
- protected function handleDirectory($path)
337
- {
338
- $directoryArray = explode(DIRECTORY_SEPARATOR, $path);
339
- $total = count($directoryArray);
340
-
341
- if (count($total) < 1)
342
- {
343
- return;
344
- }
345
-
346
- $total = $total - 1;
347
- $currentArray = &$this->directories;
348
-
349
- for ($i = 0; $i <= $total; $i++)
350
- {
351
- if (!isset($currentArray[$directoryArray[$i]]))
352
- {
353
- $currentArray[$directoryArray[$i]] = array();
354
- }
355
-
356
- $currentArray = &$currentArray[$directoryArray[$i]];
357
-
358
- // Attach meta data to the end
359
- if ($i < $total)
360
- {
361
- continue;
362
- }
363
-
364
- $fullPath = ABSPATH . $path;
365
- $size = $this->getDirectorySize($fullPath);
366
-
367
- $currentArray["metaData"] = array(
368
- "size" => $size,
369
- "path" => ABSPATH . $path,
370
- );
371
- }
372
- }
373
-
374
- /**
375
- * Gets size of given directory
376
- * @param string $path
377
- * @return int|null
378
- */
379
- protected function getDirectorySize($path)
380
- {
381
- if (!isset($this->settings->checkDirectorySize) || '1' !== $this->settings->checkDirectorySize)
382
- {
383
- return null;
384
- }
385
-
386
- return $this->objDirectories->size($path);
387
- }
388
-
389
- /**
390
- * Get total size of a directory including all its subdirectories
391
- * @param string $dir
392
- * @return int
393
- */
394
- function getDirectorySizeInclSubdirs( $dir ) {
395
- $size = 0;
396
- foreach ( glob( rtrim( $dir, '/' ) . '/*', GLOB_NOSORT ) as $each ) {
397
- $size += is_file( $each ) ? filesize( $each ) : $this->getDirectorySizeInclSubdirs( $each );
398
- }
399
- return $size;
400
- }
401
-
 
 
 
 
 
402
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ // No Direct Access
5
+ if (!defined("WPINC"))
6
+ {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\Utils\Directories;
11
+ use WPStaging\WPStaging;
12
+
13
+ /**
14
+ * Class Scan
15
+ * @package WPStaging\Backend\Modules\Jobs
16
+ */
17
+ class Scan extends Job
18
+ {
19
+
20
+ /**
21
+ * @var array
22
+ */
23
+ private $directories = array();
24
+
25
+ /**
26
+ * @var Directories
27
+ */
28
+ private $objDirectories;
29
+
30
+
31
+ /**
32
+ * Upon class initialization
33
+ */
34
+ protected function initialize()
35
+ {
36
+ $this->objDirectories = new Directories();
37
+
38
+ // Database Tables
39
+ $this->getTables();
40
+
41
+ // Get directories
42
+ $this->directories();
43
+
44
+ $this->db = WPStaging::getInstance()->get('wpdb');
45
+ $this->prefix = $this->db->prefix;
46
+
47
+
48
+ }
49
+
50
+ /**
51
+ * Start Module
52
+ * @return $this
53
+ */
54
+ public function start()
55
+ {
56
+ // Basic Options
57
+ $this->options->root = str_replace(array("\\", '/'), DIRECTORY_SEPARATOR, ABSPATH);
58
+ $this->options->existingClones = get_option("wpstg_existing_clones_beta", array());
59
+ $this->options->current = null;
60
+
61
+ if (isset($_POST["clone"]) && array_key_exists($_POST["clone"], $this->options->existingClones))
62
+ {
63
+ $this->options->current = $_POST["clone"];
64
+ }
65
+
66
+ // Tables
67
+ //$this->options->excludedTables = array();
68
+ $this->options->clonedTables = array();
69
+
70
+ // Files
71
+ $this->options->totalFiles = 0;
72
+ $this->options->totalFileSize = 0;
73
+ $this->options->copiedFiles = 0;
74
+
75
+
76
+ // Directories
77
+ $this->options->includedDirectories = array();
78
+ $this->options->excludedDirectories = array();
79
+ $this->options->extraDirectories = array();
80
+ $this->options->directoriesToCopy = array();
81
+ $this->options->scannedDirectories = array();
82
+
83
+ // Job
84
+ $this->options->currentJob = "database";
85
+ $this->options->currentStep = 0;
86
+ $this->options->totalSteps = 0;
87
+
88
+ // Delete previous cached files
89
+ $this->cache->delete("files_to_copy");
90
+ $this->cache->delete("clone_options");
91
+ //$this->cache->delete("files_to_verify");
92
+ //$this->cache->delete("files_verified");
93
+
94
+ // Save options
95
+ $this->saveOptions();
96
+
97
+ return $this;
98
+ }
99
+
100
+ /**
101
+ * Format bytes into human readable form
102
+ * @param int $bytes
103
+ * @param int $precision
104
+ * @return string
105
+ */
106
+ public function formatSize($bytes, $precision = 2)
107
+ {
108
+ if ((double) $bytes < 1)
109
+ {
110
+ return '';
111
+ }
112
+
113
+ $units = array('B', "KB", "MB", "GB", "TB");
114
+
115
+ $bytes = (double) $bytes;
116
+ $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
117
+ $pow = pow(1000, $base - floor($base)); // Same rule for 1000
118
+
119
+ return round($pow, $precision) . ' ' . $units[(int) floor($base)];
120
+ }
121
+
122
+ /**
123
+ * @param null|string $directories
124
+ * @param bool $forceDisabled
125
+ * @return string
126
+ */
127
+ public function directoryListing($directories = null, $forceDisabled = false)
128
+ {
129
+ if (null == $directories)
130
+ {
131
+ $directories = $this->directories;
132
+ }
133
+
134
+ $output = '';
135
+ foreach ($directories as $name => $directory)
136
+ {
137
+
138
+ if (!is_array($directory)) {
139
+ // Not a directory, possibly a symlink, therefore we will skip it
140
+ continue;
141
+ }
142
+
143
+ // Need to preserve keys so no array_shift()
144
+ $data = reset($directory);
145
+ unset($directory[key($directory)]);
146
+
147
+ $isChecked = (
148
+ empty($this->options->includedDirectories) ||
149
+ in_array($data["path"], $this->options->includedDirectories)
150
+ );
151
+
152
+ $isDisabled = ($this->options->existingClones && isset($this->options->existingClones[$name]));
153
+
154
+ $output .= "<div class='wpstg-dir'>";
155
+ $output .= "<input type='checkbox' class='wpstg-check-dir'";
156
+
157
+ if ($isChecked && !$isDisabled && !$forceDisabled) $output .= " checked";
158
+ if ($forceDisabled || $isDisabled) $output .= " disabled";
159
+
160
+ $output .= " name='selectedDirectories[]' value='{$data["path"]}'>";
161
+
162
+ $output .= "<a href='#' class='wpstg-expand-dirs";
163
+ if (!$isChecked || $isDisabled) $output .= " disabled";
164
+ $output .= "'>{$name}";
165
+ $output .= "</a>";
166
+
167
+ $output .= "<span class='wpstg-size-info'>{$this->formatSize($data["size"])}</span>";
168
+
169
+ if (!empty($directory))
170
+ {
171
+ $output .= "<div class='wpstg-dir wpstg-subdir'>";
172
+ $output .= $this->directoryListing($directory, $isDisabled);
173
+ $output .= "</div>";
174
+ }
175
+
176
+ $output .= "</div>";
177
+ }
178
+
179
+ return $output;
180
+ }
181
+
182
+ /**
183
+ * Checks if there is enough free disk space to create staging site
184
+ * Returns null when can't run disk_free_space function one way or another
185
+ * @return bool|null
186
+ */
187
+ public function hasFreeDiskSpace() {
188
+ if( !function_exists( "disk_free_space" ) ) {
189
+ return null;
190
+ }
191
+
192
+ $freeSpace = @disk_free_space( ABSPATH );
193
+
194
+ if( false === $freeSpace ) {
195
+ $data = array(
196
+ 'freespace' => false,
197
+ 'usedspace' => $this->formatSize($this->getDirectorySizeInclSubdirs(ABSPATH))
198
+ );
199
+ echo json_encode($data);
200
+ die();
201
+ }
202
+
203
+
204
+ $data = array(
205
+ 'freespace' => $this->formatSize($freeSpace),
206
+ 'usedspace' => $this->formatSize($this->getDirectorySizeInclSubdirs(ABSPATH))
207
+ );
208
+
209
+ echo json_encode( $data );
210
+ die();
211
+ }
212
+
213
+ /**
214
+ * Get Database Tables
215
+ */
216
+ protected function getTables()
217
+ {
218
+ $wpDB = WPStaging::getInstance()->get("wpdb");
219
+
220
+ if (strlen($wpDB->prefix) > 0)
221
+ {
222
+ $prefix = str_replace('_', '', $wpDB->prefix);
223
+ $sql = "SHOW TABLE STATUS LIKE '{$wpDB->prefix}%'";
224
+ }
225
+ else
226
+ {
227
+ $sql = "SHOW TABLE STATUS";
228
+ }
229
+
230
+ $tables = $wpDB->get_results($sql);
231
+
232
+ $currentTables = array();
233
+
234
+ // Reset excluded Tables than loop through all tables
235
+ $this->options->excludedTables = array();
236
+ foreach ($tables as $table)
237
+ {
238
+
239
+ // Exclude WP Staging Tables
240
+ // if (0 === strpos($table->Name, "wpstg"))
241
+ // {
242
+ // continue;
243
+ // }
244
+ // Create array of unchecked tables
245
+ if (0 !== strpos($table->Name, $wpDB->prefix))
246
+ {
247
+ $this->options->excludedTables[] = $table->Name;
248
+ }
249
+
250
+
251
+ $currentTables[] = array(
252
+ "name" => $table->Name,
253
+ "size" => ($table->Data_length + $table->Index_length)
254
+ );
255
+ }
256
+
257
+ $this->options->tables = json_decode(json_encode($currentTables));
258
+ }
259
+
260
+ /**
261
+ * Get directories and main meta data about'em recursively
262
+ */
263
+ protected function directories()
264
+ {
265
+ $directories = new \DirectoryIterator(ABSPATH);
266
+
267
+ foreach($directories as $directory)
268
+ {
269
+ // Not a valid directory
270
+ if (false === ($path = $this->getPath($directory)))
271
+ {
272
+ continue;
273
+ }
274
+
275
+ $this->handleDirectory($path);
276
+
277
+ // Get Sub-directories
278
+ $this->getSubDirectories($directory->getRealPath());
279
+ }
280
+
281
+ // Gather Plugins
282
+ $this->getSubDirectories(WP_PLUGIN_DIR);
283
+
284
+ // Gather Themes
285
+ $this->getSubDirectories(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "themes");
286
+
287
+ // Gather Uploads
288
+ $this->getSubDirectories(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "uploads");
289
+ }
290
+
291
+ /**
292
+ * @param string $path
293
+ */
294
+ protected function getSubDirectories($path)
295
+ {
296
+ $directories = new \DirectoryIterator($path);
297
+
298
+ foreach($directories as $directory)
299
+ {
300
+ // Not a valid directory
301
+ if (false === ($path = $this->getPath($directory)))
302
+ {
303
+ continue;
304
+ }
305
+
306
+ $this->handleDirectory($path);
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Get Path from $directory
312
+ * @param \SplFileInfo $directory
313
+ * @return string|false
314
+ */
315
+ protected function getPath($directory)
316
+ {
317
+
318
+ /*
319
+ * Do not follow root path like src/web/..
320
+ * This must be done before \SplFileInfo->isDir() is used!
321
+ * Prevents open base dir restriction fatal errors
322
+ */
323
+ if (strpos( $directory->getRealPath(), ABSPATH ) !== 0 ) {
324
+ return false;
325
+ }
326
+ $path = str_replace(ABSPATH, null, $directory->getRealPath());
327
+
328
+ // Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
329
+ if (!$directory->isDir() || strlen($path) < 1)
330
+ {
331
+ return false;
332
+ }
333
+
334
+ return $path;
335
+ }
336
+
337
+ /**
338
+ * Organizes $this->directories
339
+ * @param string $path
340
+ */
341
+ protected function handleDirectory($path)
342
+ {
343
+ $directoryArray = explode(DIRECTORY_SEPARATOR, $path);
344
+ $total = count($directoryArray);
345
+
346
+ if (count($total) < 1)
347
+ {
348
+ return;
349
+ }
350
+
351
+ $total = $total - 1;
352
+ $currentArray = &$this->directories;
353
+
354
+ for ($i = 0; $i <= $total; $i++)
355
+ {
356
+ if (!isset($currentArray[$directoryArray[$i]]))
357
+ {
358
+ $currentArray[$directoryArray[$i]] = array();
359
+ }
360
+
361
+ $currentArray = &$currentArray[$directoryArray[$i]];
362
+
363
+ // Attach meta data to the end
364
+ if ($i < $total)
365
+ {
366
+ continue;
367
+ }
368
+
369
+ $fullPath = ABSPATH . $path;
370
+ $size = $this->getDirectorySize($fullPath);
371
+
372
+ $currentArray["metaData"] = array(
373
+ "size" => $size,
374
+ "path" => ABSPATH . $path,
375
+ );
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Gets size of given directory
381
+ * @param string $path
382
+ * @return int|null
383
+ */
384
+ protected function getDirectorySize($path)
385
+ {
386
+ if (!isset($this->settings->checkDirectorySize) || '1' !== $this->settings->checkDirectorySize)
387
+ {
388
+ return null;
389
+ }
390
+
391
+ return $this->objDirectories->size($path);
392
+ }
393
+
394
+ /**
395
+ * Get total size of a directory including all its subdirectories
396
+ * @param string $dir
397
+ * @return int
398
+ */
399
+ function getDirectorySizeInclSubdirs( $dir ) {
400
+ $size = 0;
401
+ foreach ( glob( rtrim( $dir, '/' ) . '/*', GLOB_NOSORT ) as $each ) {
402
+ $size += is_file( $each ) ? filesize( $each ) : $this->getDirectorySizeInclSubdirs( $each );
403
+ }
404
+ return $size;
405
+ }
406
+
407
  }
apps/Backend/Modules/Jobs/Updating.php CHANGED
@@ -1,262 +1,249 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
- use WPStaging\WPStaging;
6
-
7
- /**
8
- * Class Cloning
9
- * @package WPStaging\Backend\Modules\Jobs
10
- */
11
- class Updating extends Job
12
- {
13
- /**
14
- * Initialize is called in \Job
15
- */
16
- public function initialize(){
17
- $this->db = WPStaging::getInstance()->get("wpdb");
18
- }
19
-
20
- /**
21
- * Save Chosen Cloning Settings
22
- * @return bool
23
- */
24
- public function save()
25
- {
26
- if (!isset($_POST) || !isset($_POST["cloneID"]))
27
- {
28
- return false;
29
- }
30
-
31
- // Generate Options
32
- // Clone
33
- $this->options->clone = $_POST["cloneID"];
34
- $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
35
- $this->options->cloneNumber = 1;
36
- $this->options->includedDirectories = array();
37
- $this->options->excludedDirectories = array();
38
- $this->options->extraDirectories = array();
39
- $this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
40
-
41
- // Job
42
- $this->options->job = new \stdClass();
43
-
44
- // Check if clone data already exists and use that one
45
- if (isset($this->options->existingClones[$this->options->clone]) )
46
- {
47
- $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]['number'];
48
- $this->options->prefix = $this->getStagingPrefix();
49
- } else {
50
- wp_die('Fatal Error: Can not update clone because there is no clone data.');
51
- }
52
-
53
-
54
- // Excluded Tables
55
- if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
56
- {
57
- $this->options->excludedTables = $_POST["excludedTables"];
58
- }
59
-
60
- // Excluded Directories
61
- if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
62
- {
63
- $this->options->excludedDirectories = $_POST["excludedDirectories"];
64
- }
65
-
66
- // Excluded Directories TOTAL
67
- // Do not copy these folders and plugins
68
- $excludedDirectories = array(
69
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
70
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login'
71
- );
72
-
73
- $this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
74
-
75
- // Included Directories
76
- if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
77
- {
78
- $this->options->includedDirectories = $_POST["includedDirectories"];
79
- }
80
-
81
- // Extra Directories
82
- if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
83
- {
84
- $this->options->extraDirectories = $_POST["extraDirectories"];
85
- }
86
-
87
- // Directories to Copy
88
- $this->options->directoriesToCopy = array_merge(
89
- $this->options->includedDirectories,
90
- $this->options->extraDirectories
91
- );
92
-
93
- array_unshift($this->options->directoriesToCopy, ABSPATH);
94
-
95
- // Delete files to copy listing
96
- $this->cache->delete("files_to_copy");
97
-
98
- return $this->saveOptions();
99
- }
100
-
101
- /**
102
- * Check and return prefix of the staging site
103
- */
104
-
105
-
106
- // public function getStagingPrefix_old(){
107
- // $prefix = !empty($this->options->existingClones[$this->options->clone]['prefix']) ?
108
- // $this->options->existingClones[$this->options->clone]['prefix'] :
109
- // false;
110
- //
111
- // if(!$prefix){
112
- // $this->returnException("Fatal Error: Can not update staging site. Can not find Prefix. '{$prefix}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
113
- // wp_die("Fatal Error: Can not update staging site. Can not find Prefix. '{$prefix}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
114
- // }
115
- //
116
- // if ($this->options->existingClones[$this->options->clone]['prefix'] == $this->db->prefix){
117
- // $this->returnException("Fatal Error: Can not update staging site. Prefix. '{$prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
118
- // wp_die("Fatal Error: Can not update staging site. Prefix. '{$prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
119
- // }
120
- //
121
- // return $prefix;
122
- // }
123
-
124
- /**
125
- * Check and return prefix of the staging site
126
- */
127
- public function getStagingPrefix() {
128
- // prefix not defined! Happens if staging site has ben generated with older version of wpstg
129
- // Try to get staging prefix from wp-config.php of staging site
130
- $this->options->prefix = $this->options->existingClones[$this->options->clone]['prefix'];
131
- //wp_die($this->options->prefix);
132
- if (empty($this->options->prefix)) {
133
- // Throw error if wp-config.php is not readable
134
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
135
- //wp_die($path);
136
- if (false === ($content = @file_get_contents($path))) {
137
- $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
138
- $this->returnException("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
139
- wp_die("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
140
- } else {
141
- // Get prefix from wp-config.php
142
- preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
143
- //wp_die(var_dump($matches));
144
-
145
- if (!empty($matches[1])) {
146
- $this->options->prefix = $matches[1];
147
- } else {
148
- $this->returnException("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
149
- wp_die("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
150
- }
151
- }
152
- }
153
-
154
- // Die() if staging prefix is the same as the live prefix
155
- if ($this->db->prefix == $this->options->prefix) {
156
- $this->log("Fatal Error: Can not updatte staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
157
- wp_die("Fatal Error: Can not update staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
158
- }
159
-
160
- // Else
161
- //$this->returnException($this->options->prefix);
162
- //wp_die($this->options->prefix);
163
- return $this->options->prefix;
164
- }
165
-
166
- /**
167
- * Start the cloning job
168
- */
169
- public function start()
170
- {
171
- if (null === $this->options->currentJob)
172
- {
173
- $this->log("Cloning job for {$this->options->clone} finished");
174
- return true;
175
- }
176
-
177
- $methodName = "job" . ucwords($this->options->currentJob);
178
-
179
- if (!method_exists($this, $methodName))
180
- {
181
- $this->log("Can't execute job; Job's method {$methodName} is not found");
182
- throw new JobNotFoundException($methodName);
183
- }
184
-
185
- // Call the job
186
- //$this->log("execute job: Job's method {$methodName}");
187
- return $this->{$methodName}();
188
- }
189
-
190
- /**
191
- * @param object $response
192
- * @param string $nextJob
193
- * @return object
194
- */
195
- private function handleJobResponse($response, $nextJob)
196
- {
197
- // Job is not done
198
- if (true !== $response->status)
199
- {
200
- return $response;
201
- }
202
-
203
- $this->options->currentJob = $nextJob;
204
- $this->options->currentStep = 0;
205
- $this->options->totalSteps = 0;
206
-
207
- // Save options
208
- $this->saveOptions();
209
-
210
- return $response;
211
- }
212
-
213
- /**
214
- * Clone Database
215
- * @return object
216
- */
217
- public function jobDatabase()
218
- {
219
- $database = new Database();
220
- return $this->handleJobResponse($database->start(), "directories");
221
- }
222
-
223
- /**
224
- * Get All Files From Selected Directories Recursively Into a File
225
- * @return object
226
- */
227
- public function jobDirectories()
228
- {
229
- $directories = new Directories();
230
- return $this->handleJobResponse($directories->start(), "files");
231
- }
232
-
233
- /**
234
- * Copy Files
235
- * @return object
236
- */
237
- public function jobFiles()
238
- {
239
- $files = new Files();
240
- return $this->handleJobResponse($files->start(), "data");
241
- }
242
-
243
- /**
244
- * Replace Data
245
- * @return object
246
- */
247
- public function jobData()
248
- {
249
- $data = new Data();
250
- return $this->handleJobResponse($data->start(), "finish");
251
- }
252
-
253
- /**
254
- * Save Clone Data
255
- * @return object
256
- */
257
- public function jobFinish()
258
- {
259
- $finish = new Finish();
260
- return $this->handleJobResponse($finish->start(), '');
261
- }
262
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
+ use WPStaging\WPStaging;
6
+
7
+ /**
8
+ * Class Cloning
9
+ * @package WPStaging\Backend\Modules\Jobs
10
+ */
11
+ class Updating extends Job
12
+ {
13
+ /**
14
+ * Initialize is called in \Job
15
+ */
16
+ public function initialize(){
17
+ $this->db = WPStaging::getInstance()->get("wpdb");
18
+ }
19
+
20
+ /**
21
+ * Save Chosen Cloning Settings
22
+ * @return bool
23
+ */
24
+ public function save()
25
+ {
26
+ if (!isset($_POST) || !isset($_POST["cloneID"]))
27
+ {
28
+ return false;
29
+ }
30
+
31
+ // Generate Options
32
+ // Clone
33
+ $this->options->clone = $_POST["cloneID"];
34
+ $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
35
+ $this->options->cloneNumber = 1;
36
+ $this->options->includedDirectories = array();
37
+ $this->options->excludedDirectories = array();
38
+ $this->options->extraDirectories = array();
39
+ $this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
40
+
41
+ // Job
42
+ $this->options->job = new \stdClass();
43
+
44
+ // Check if clone data already exists and use that one
45
+ if (isset($this->options->existingClones[$this->options->clone]) )
46
+ {
47
+ $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]['number'];
48
+ $this->options->prefix = $this->getStagingPrefix();
49
+ } else {
50
+ wp_die('Fatal Error: Can not update clone because there is no clone data.');
51
+ }
52
+
53
+
54
+ // Excluded Tables
55
+ // if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
56
+ // {
57
+ // $this->options->excludedTables = $_POST["excludedTables"];
58
+ // }
59
+ // Included Tables
60
+ if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"]))
61
+ {
62
+ $this->options->tables = $_POST["includedTables"];
63
+ } else {
64
+ $this->options->tables = array();
65
+ }
66
+
67
+ // Excluded Directories
68
+ if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
69
+ {
70
+ $this->options->excludedDirectories = $_POST["excludedDirectories"];
71
+ }
72
+
73
+ // Excluded Directories TOTAL
74
+ // Do not copy these folders and plugins
75
+ $excludedDirectories = array(
76
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
77
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login'
78
+ );
79
+
80
+ $this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
81
+
82
+ // Included Directories
83
+ if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
84
+ {
85
+ $this->options->includedDirectories = $_POST["includedDirectories"];
86
+ }
87
+
88
+ // Extra Directories
89
+ if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
90
+ {
91
+ $this->options->extraDirectories = $_POST["extraDirectories"];
92
+ }
93
+
94
+ // Directories to Copy
95
+ $this->options->directoriesToCopy = array_merge(
96
+ $this->options->includedDirectories,
97
+ $this->options->extraDirectories
98
+ );
99
+
100
+ array_unshift($this->options->directoriesToCopy, ABSPATH);
101
+
102
+ // Delete files to copy listing
103
+ $this->cache->delete("files_to_copy");
104
+
105
+ return $this->saveOptions();
106
+ }
107
+
108
+
109
+ /**
110
+ * Check and return prefix of the staging site
111
+ */
112
+ public function getStagingPrefix() {
113
+ // prefix not defined! Happens if staging site has ben generated with older version of wpstg
114
+ // Try to get staging prefix from wp-config.php of staging site
115
+ $this->options->prefix = $this->options->existingClones[$this->options->clone]['prefix'];
116
+ //wp_die($this->options->prefix);
117
+ if (empty($this->options->prefix)) {
118
+ // Throw error if wp-config.php is not readable
119
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
120
+ //wp_die($path);
121
+ if (false === ($content = @file_get_contents($path))) {
122
+ $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
123
+ $this->returnException("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
124
+ wp_die("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
125
+ } else {
126
+ // Get prefix from wp-config.php
127
+ preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
128
+ //wp_die(var_dump($matches));
129
+
130
+ if (!empty($matches[1])) {
131
+ $this->options->prefix = $matches[1];
132
+ } else {
133
+ $this->returnException("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
134
+ wp_die("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
135
+ }
136
+ }
137
+ }
138
+
139
+ // Die() if staging prefix is the same as the live prefix
140
+ if ($this->db->prefix == $this->options->prefix) {
141
+ $this->log("Fatal Error: Can not updatte staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
142
+ wp_die("Fatal Error: Can not update staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
143
+ }
144
+
145
+ // Else
146
+ //wp_die($this->options->prefix);
147
+ return $this->options->prefix;
148
+ }
149
+
150
+ /**
151
+ * Start the cloning job
152
+ */
153
+ public function start()
154
+ {
155
+ if (null === $this->options->currentJob)
156
+ {
157
+ $this->log("Cloning job for {$this->options->clone} finished");
158
+ return true;
159
+ }
160
+
161
+ $methodName = "job" . ucwords($this->options->currentJob);
162
+
163
+ if (!method_exists($this, $methodName))
164
+ {
165
+ $this->log("Can't execute job; Job's method {$methodName} is not found");
166
+ throw new JobNotFoundException($methodName);
167
+ }
168
+
169
+ // Call the job
170
+ //$this->log("execute job: Job's method {$methodName}");
171
+ return $this->{$methodName}();
172
+ }
173
+
174
+ /**
175
+ * @param object $response
176
+ * @param string $nextJob
177
+ * @return object
178
+ */
179
+ private function handleJobResponse($response, $nextJob)
180
+ {
181
+ // Job is not done
182
+ if (true !== $response->status)
183
+ {
184
+ return $response;
185
+ }
186
+
187
+ $this->options->currentJob = $nextJob;
188
+ $this->options->currentStep = 0;
189
+ $this->options->totalSteps = 0;
190
+
191
+ // Save options
192
+ $this->saveOptions();
193
+
194
+ return $response;
195
+ }
196
+
197
+
198
+
199
+
200
+ /**
201
+ * Clone Database
202
+ * @return object
203
+ */
204
+ public function jobDatabase()
205
+ {
206
+ $database = new Database();
207
+ return $this->handleJobResponse($database->start(), "directories");
208
+ }
209
+
210
+ /**
211
+ * Get All Files From Selected Directories Recursively Into a File
212
+ * @return object
213
+ */
214
+ public function jobDirectories()
215
+ {
216
+ $directories = new Directories();
217
+ return $this->handleJobResponse($directories->start(), "files");
218
+ }
219
+
220
+ /**
221
+ * Copy Files
222
+ * @return object
223
+ */
224
+ public function jobFiles()
225
+ {
226
+ $files = new Files();
227
+ return $this->handleJobResponse($files->start(), "data");
228
+ }
229
+
230
+ /**
231
+ * Replace Data
232
+ * @return object
233
+ */
234
+ public function jobData()
235
+ {
236
+ $data = new Data();
237
+ return $this->handleJobResponse($data->start(), "finish");
238
+ }
239
+
240
+ /**
241
+ * Save Clone Data
242
+ * @return object
243
+ */
244
+ public function jobFinish()
245
+ {
246
+ $finish = new Finish();
247
+ return $this->handleJobResponse($finish->start(), '');
248
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  }
apps/Backend/Modules/SystemInfo.php CHANGED
@@ -1,513 +1,513 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules;
3
-
4
- use WPStaging\DI\InjectionAware;
5
- use WPStaging\Library\Browser;
6
- use WPStaging\WPStaging;
7
-
8
- // No Direct Access
9
- if (!defined("WPINC"))
10
- {
11
- die;
12
- }
13
-
14
- /**
15
- * Class SystemInfo
16
- * @package WPStaging\Backend\Modules
17
- */
18
- class SystemInfo extends InjectionAware
19
- {
20
-
21
- /**
22
- * @var bool
23
- */
24
- private $isMultiSite;
25
-
26
-
27
- /**
28
- * Initialize class
29
- */
30
- public function initialize()
31
- {
32
- $this->isMultiSite = is_multisite();
33
- }
34
-
35
- /**
36
- * Magic method
37
- * @return string
38
- */
39
- public function __toString()
40
- {
41
- return $this->get();
42
- }
43
-
44
- /**
45
- * Get System Information as text
46
- * @return string
47
- */
48
- public function get()
49
- {
50
- $output = "### Begin System Info ###" . PHP_EOL . PHP_EOL;
51
-
52
- $output .= $this->wpstaging();
53
-
54
- $output .= $this->site();
55
-
56
- $output .= $this->browser();
57
-
58
- $output .= $this->wp();
59
-
60
- $output .= $this->plugins();
61
-
62
- $output .= $this->multiSitePlugins();
63
-
64
- $output .= $this->server();
65
-
66
- $output .= $this->php();
67
-
68
- $output .= $this->phpExtensions();
69
-
70
- $output .= PHP_EOL . "### End System Info ###";
71
-
72
- return $output;
73
- }
74
-
75
-
76
-
77
- /**
78
- * @param string $string
79
- * @return string
80
- */
81
- public function header($string)
82
- {
83
- return PHP_EOL . "-- {$string}" . PHP_EOL . PHP_EOL;
84
- }
85
-
86
- /**
87
- * Formating title and the value
88
- * @param string $title
89
- * @param string $value
90
- * @return string
91
- */
92
- public function info($title, $value)
93
- {
94
- return str_pad($title, 56, ' ', STR_PAD_RIGHT) . $value . PHP_EOL;
95
- }
96
-
97
- /**
98
- * Theme Information
99
- * @return string
100
- */
101
- public function theme()
102
- {
103
- // Versions earlier than 3.4
104
- if (get_bloginfo("version") < "3.4" )
105
- {
106
- $themeData = get_theme_data(get_stylesheet_directory() . "/style.css");
107
- return "{$themeData["Name"]} {$themeData["Version"]}";
108
- }
109
-
110
- $themeData = wp_get_theme();
111
- return "{$themeData->Name} {$themeData->Version}";
112
- }
113
-
114
- /**
115
- * Site Information
116
- * @return string
117
- */
118
- public function site()
119
- {
120
- $output = "-- Site Info" . PHP_EOL . PHP_EOL;
121
- $output .= $this->info("Site URL:", site_url());
122
- $output .= $this->info("Home URL:", home_url());
123
- $output .= $this->info("Home Path:", get_home_path());
124
- $output .= $this->info("Installed in subdir:", ( $this->isSubDir() ? 'Yes' : 'No' )) ;
125
- $output .= $this->info("Multisite:", ($this->isMultiSite ? "Yes" : "No" ));
126
-
127
- return apply_filters("wpstg_sysinfo_after_site_info", $output);
128
- }
129
-
130
- /**
131
- * Wp Staging plugin Information
132
- * @return string
133
- */
134
- public function wpstaging() {
135
- // Get wpstg settings
136
- $settings = ( object ) get_option( 'wpstg_settings', array() );
137
-
138
- // Clones data < 1.6.x
139
- $clones = ( object ) get_option( 'wpstg_existing_clones', array() );
140
- // Clones data version > 2.x
141
- $clonesBeta = get_option( 'wpstg_existing_clones_beta' );
142
-
143
-
144
- $output = "-- WP Staging Settings" . PHP_EOL . PHP_EOL;
145
- $output .= $this->info( "Query Limit:", isset( $settings->queryLimit ) ? $settings->queryLimit : 'undefined' );
146
- $output .= $this->info( "File Copy Limit:", isset( $settings->fileLimit ) ? $settings->fileLimit : 'undefined' );
147
- $output .= $this->info( "Batch Size:", isset( $settings->batchSize ) ? $settings->batchSize : 'undefined' );
148
- $output .= $this->info( "CPU Load:", isset( $settings->cpuLoad ) ? $settings->cpuLoad : 'undefined' );
149
- $output .= $this->info( "WP in Subdir:", isset( $settings->wpSubDirectory ) ? $settings->wpSubDirectory : 'false' );
150
-
151
- $output .= PHP_EOL . PHP_EOL . "-- Available Sites Version < 1.6.x" . PHP_EOL . PHP_EOL;
152
-
153
- $i = 1;
154
- foreach ( $clones as $key => $value ) {
155
- $output .= $this->info( "Site name & subfolder :", $value );
156
- }
157
- $output .= PHP_EOL . PHP_EOL . "-- Available Sites Version > 2.0.x" . PHP_EOL . PHP_EOL;
158
-
159
- foreach ( $clonesBeta as $key => $clone ) {
160
- $output .= $this->info( "Number:", isset( $clone['number'] ) ? $clone['number'] : 'undefined' );
161
- $output .= $this->info( "directoryName:", isset( $clone['directoryName'] ) ? $clone['directoryName'] : 'undefined' );
162
- $output .= $this->info( "Path:", isset( $clone['path'] ) ? $clone['path'] : 'undefined' );
163
- $output .= $this->info( "URL:", isset( $clone['url'] ) ? $clone['url'] : 'undefined' );
164
- $output .= $this->info( "DB Prefix:", isset( $clone['prefix'] ) ? $clone['prefix'] : 'undefined' );
165
- $output .= $this->info( "DB Prefix wp-config.php:", $this->getStagingPrefix($clone));
166
- $output .= $this->info( "Version:", isset( $clone['version'] ) ? $clone['version'] : 'undefined' ) . PHP_EOL . PHP_EOL;
167
- }
168
-
169
- //$output .= PHP_EOL . PHP_EOL;
170
-
171
- $output .= $this->info( "Plugin Version:", get_option('wpstg_version', 'undefined') );
172
- $output .= $this->info( "Install Date:", get_option('wpstg_installDate', 'undefined') );
173
- $output .= $this->info( "Upgraded from:", get_option('wpstg_version_upgraded_from', 'undefined') );
174
- $output .= $this->info( "Is Staging Site:", get_option('wpstg_is_staging_site', 'undefined') ) . PHP_EOL . PHP_EOL;
175
-
176
-
177
- return apply_filters( "wpstg_sysinfo_after_wpstaging_info", $output );
178
- }
179
-
180
- /**
181
- * Browser Information
182
- * @return string
183
- */
184
- public function browser()
185
- {
186
- $output = $this->header("User Browser");
187
- $output .= (new Browser);
188
-
189
- return apply_filters("wpstg_sysinfo_after_user_browser", $output);
190
- }
191
-
192
- /**
193
- * Frontpage Information when frontpage is set to "page"
194
- * @return string
195
- */
196
- public function frontPage()
197
- {
198
- if (get_option("show_on_front") !== "page")
199
- {
200
- return '';
201
- }
202
-
203
- $frontPageID = get_option("page_on_front");
204
- $blogPageID = get_option("page_for_posts");
205
-
206
- // Front Page
207
- $pageFront = ($frontPageID != 0) ? get_the_title($frontPageID) . " (#{$frontPageID})" : "Unset";
208
- // Blog Page ID
209
- $pageBlog = ($blogPageID != 0) ? get_the_title($blogPageID) . " (#{$blogPageID})" : "Unset";
210
-
211
- $output = $this->info("Page On Front:", $pageFront);
212
- $output .= $this->info("Page For Posts:", $pageBlog);
213
-
214
- return $output;
215
- }
216
-
217
- /**
218
- * Check wp_remote_post() functionality
219
- * @return string
220
- */
221
- public function wpRemotePost()
222
- {
223
- // Make sure wp_remote_post() is working
224
- $wpRemotePost = "wp_remote_post() does not work";
225
-
226
- // Send request
227
- $response = wp_remote_post(
228
- "https://www.paypal.com/cgi-bin/webscr",
229
- array(
230
- "sslverify" => false,
231
- "timeout" => 60,
232
- "user-agent" => "WPSTG/" . WPStaging::VERSION,
233
- "body" => array("cmd" => "_notify-validate")
234
- )
235
- );
236
-
237
- // Validate it worked
238
- if (!is_wp_error($response) && 200 <= $response["response"]["code"] && 300> $response["response"]["code"])
239
- {
240
- $wpRemotePost = "wp_remote_post() works";
241
- }
242
-
243
- return $this->info("Remote Post:", $wpRemotePost);
244
- }
245
-
246
- /**
247
- * WordPress Configuration
248
- * @return string
249
- */
250
- public function wp()
251
- {
252
- $output = $this->header("WordPress Configuration");
253
- $output .= $this->info("Version:", get_bloginfo("version"));
254
- $output .= $this->info("Language:", (defined("WPLANG") && WPLANG) ? WPLANG : "en_US");
255
-
256
- $permalinkStructure = get_option("permalink_structure");;
257
- $output .= $this->info("Permalink Structure:", ($permalinkStructure) ? $permalinkStructure : "Default");
258
-
259
- $output .= $this->info("Active Theme:", $this->theme());
260
- $output .= $this->info("Show On Front:", get_option("show_on_front"));
261
-
262
- // Frontpage information
263
- $output .= $this->frontPage();
264
-
265
- // WP Remote Post
266
- $output .= $this->wpRemotePost();
267
-
268
- // Table Prefix
269
- $wpDB = $this->di->get("wpdb");
270
- $tablePrefix = "DB Prefix: " . $wpDB->prefix . ' ';
271
- $tablePrefix .= "Length: " . strlen($wpDB->prefix) . " Status: ";
272
- $tablePrefix .= (strlen($wpDB->prefix) > 16) ? " ERROR: Too long" : " Acceptable";
273
-
274
- $output .= $this->info("Table Prefix:", $tablePrefix);
275
-
276
- // WP Debug
277
- $output .= $this->info("WP_DEBUG:", (defined("WP_DEBUG")) ? WP_DEBUG ? "Enabled" : "Disabled" : "Not set");
278
- $output .= $this->info("Memory Limit:", WP_MEMORY_LIMIT);
279
- $output .= $this->info("Registered Post Stati:", implode(", ", \get_post_stati()));
280
-
281
- return apply_filters("wpstg_sysinfo_after_wpstg_config", $output);
282
- }
283
-
284
- /**
285
- * List of Active Plugins
286
- * @param array $plugins
287
- * @param array $activePlugins
288
- * @return string
289
- */
290
- public function activePlugins($plugins, $activePlugins)
291
- {
292
- $output = $this->header("WordPress Active Plugins");
293
-
294
- foreach ($plugins as $path => $plugin)
295
- {
296
- if (!in_array($path, $activePlugins))
297
- {
298
- continue;
299
- }
300
-
301
- $output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
302
- }
303
-
304
- return apply_filters("wpstg_sysinfo_after_wordpress_plugins", $output);
305
- }
306
-
307
- /**
308
- * List of Inactive Plugins
309
- * @param array $plugins
310
- * @param array $activePlugins
311
- * @return string
312
- */
313
- public function inactivePlugins($plugins, $activePlugins)
314
- {
315
- $output = $this->header("WordPress Inactive Plugins");
316
-
317
- foreach ($plugins as $path => $plugin)
318
- {
319
- if (in_array($path, $activePlugins))
320
- {
321
- continue;
322
- }
323
-
324
- $output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
325
- }
326
-
327
- return apply_filters("wpstg_sysinfo_after_wordpress_plugins_inactive", $output);
328
- }
329
-
330
- /**
331
- * Get list of active and inactive plugins
332
- * @return string
333
- */
334
- public function plugins()
335
- {
336
- // Get plugins and active plugins
337
- $plugins = get_plugins();
338
- $activePlugins = get_option("active_plugins", array());
339
-
340
- // Active plugins
341
- $output = $this->activePlugins($plugins, $activePlugins);
342
- $output .= $this->inactivePlugins($plugins, $activePlugins);
343
-
344
- return $output;
345
- }
346
-
347
- /**
348
- * Multisite Plugins
349
- * @return string
350
- */
351
- public function multiSitePlugins()
352
- {
353
- if (!$this->isMultiSite)
354
- {
355
- return '';
356
- }
357
-
358
- $output = $this->header("Network Active Plugins");
359
-
360
- $plugins = wp_get_active_network_plugins();
361
- $activePlugins = get_site_option("active_sitewide_plugins", array());
362
-
363
- foreach ($plugins as $pluginPath)
364
- {
365
- $pluginBase = plugin_basename($pluginPath);
366
-
367
- if (!array_key_exists($pluginBase, $activePlugins))
368
- {
369
- continue;
370
- }
371
-
372
- $plugin = get_plugin_data($pluginPath);
373
-
374
- $output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
375
- }
376
- unset($plugins, $activePlugins);
377
-
378
- return $output;
379
- }
380
-
381
- /**
382
- * Server Information
383
- * @return string
384
- */
385
- public function server()
386
- {
387
- // Server Configuration
388
- $output = $this->header("Webserver Configuration");
389
-
390
- $output .= $this->info("PHP Version:", PHP_VERSION);
391
- $output .= $this->info("MySQL Version:", $this->di->get("wpdb")->db_version());
392
- $output .= $this->info("Webserver Info:", $_SERVER["SERVER_SOFTWARE"]);
393
-
394
- return apply_filters("wpstg_sysinfo_after_webserver_config", $output);
395
- }
396
-
397
- /**
398
- * PHP Configuration
399
- * @return string
400
- */
401
- public function php()
402
- {
403
- $output = $this->header("PHP Configuration");
404
- $output .= $this->info("Safe Mode:", ($this->isSafeModeEnabled() ? "Enabled" : "Disabled"));
405
- $output .= $this->info("Memory Limit:", ini_get("memory_limit"));
406
- $output .= $this->info("Upload Max Size:", ini_get("upload_max_filesize"));
407
- $output .= $this->info("Post Max Size:", ini_get("post_max_size"));
408
- $output .= $this->info("Upload Max Filesize:", ini_get("upload_max_filesize"));
409
- $output .= $this->info("Time Limit:", ini_get("max_execution_time"));
410
- $output .= $this->info("Max Input Vars:", ini_get("max_input_vars"));
411
-
412
- $displayErrors = ini_get("display_errors");
413
- $output .= $this->info("Display Errors:", ($displayErrors) ? "On ({$displayErrors})" : "N/A");
414
-
415
- return apply_filters("wpstg_sysinfo_after_php_config", $output);
416
- }
417
-
418
- /**
419
- * Check if PHP is on Safe Mode
420
- * @return bool
421
- */
422
- public function isSafeModeEnabled()
423
- {
424
- return (
425
- version_compare(PHP_VERSION, "5.4.0", '<') &&
426
- @ini_get("safe_mode")
427
- );
428
- }
429
-
430
- /**
431
- * Checks if function exists or not
432
- * @param string $functionName
433
- * @return string
434
- */
435
- public function isSupported($functionName)
436
- {
437
- return (function_exists($functionName)) ? "Supported" : "Not Supported";
438
- }
439
-
440
- /**
441
- * Checks if class or extension is loaded / exists to determine if it is installed or not
442
- * @param string $name
443
- * @param bool $isClass
444
- * @return string
445
- */
446
- public function isInstalled($name, $isClass = true)
447
- {
448
- if (true === $isClass)
449
- {
450
- return (class_exists($name)) ? "Installed" : "Not Installed";
451
- }
452
- else
453
- {
454
- return (extension_loaded($name)) ? "Installed" : "Not Installed";
455
- }
456
- }
457
-
458
- /**
459
- * Gets Installed Important PHP Extensions
460
- * @return string
461
- */
462
- public function phpExtensions()
463
- {
464
- // Important PHP Extensions
465
- $output = $this->header("PHP Extensions");
466
- $output .= $this->info("cURL:", $this->isSupported("curl_init"));
467
- $output .= $this->info("fsockopen:", $this->isSupported("fsockopen"));
468
- $output .= $this->info("SOAP Client:", $this->isInstalled("SoapClient"));
469
- $output .= $this->info("Suhosin:", $this->isInstalled("suhosin", false));
470
-
471
- return apply_filters("wpstg_sysinfo_after_php_ext", $output);
472
- }
473
-
474
- /**
475
- * Check if WP is installed in subdir
476
- * @return boolean
477
- */
478
- private function isSubDir(){
479
- if ( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
480
- return true;
481
- }
482
- return false;
483
- }
484
-
485
- /**
486
- * Check and return prefix of the staging site
487
- */
488
- /**
489
- * Try to get the staging prefix from wp-config.php of staging site
490
- * @param array $clone
491
- * @return sting
492
- */
493
- private function getStagingPrefix($clone=array()) {
494
- // Throw error
495
- $path = ABSPATH . $clone['directoryName'] . "/wp-config.php";
496
- if (false === ($content = @file_get_contents($path))) {
497
- return 'Can\'t find staging wp-config.php';
498
- } else {
499
-
500
- // Get prefix from wp-config.php
501
- //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
502
- preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
503
- //wp_die(var_dump($matches));
504
-
505
- if (!empty($matches[1])) {
506
- return $matches[1];
507
- } else {
508
- return 'No table_prefix in wp-config.php';
509
- }
510
- }
511
-
512
- }
513
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules;
3
+
4
+ use WPStaging\DI\InjectionAware;
5
+ use WPStaging\Library\Browser;
6
+ use WPStaging\WPStaging;
7
+
8
+ // No Direct Access
9
+ if (!defined("WPINC"))
10
+ {
11
+ die;
12
+ }
13
+
14
+ /**
15
+ * Class SystemInfo
16
+ * @package WPStaging\Backend\Modules
17
+ */
18
+ class SystemInfo extends InjectionAware
19
+ {
20
+
21
+ /**
22
+ * @var bool
23
+ */
24
+ private $isMultiSite;
25
+
26
+
27
+ /**
28
+ * Initialize class
29
+ */
30
+ public function initialize()
31
+ {
32
+ $this->isMultiSite = is_multisite();
33
+ }
34
+
35
+ /**
36
+ * Magic method
37
+ * @return string
38
+ */
39
+ public function __toString()
40
+ {
41
+ return $this->get();
42
+ }
43
+
44
+ /**
45
+ * Get System Information as text
46
+ * @return string
47
+ */
48
+ public function get()
49
+ {
50
+ $output = "### Begin System Info ###" . PHP_EOL . PHP_EOL;
51
+
52
+ $output .= $this->wpstaging();
53
+
54
+ $output .= $this->site();
55
+
56
+ $output .= $this->browser();
57
+
58
+ $output .= $this->wp();
59
+
60
+ $output .= $this->plugins();
61
+
62
+ $output .= $this->multiSitePlugins();
63
+
64
+ $output .= $this->server();
65
+
66
+ $output .= $this->php();
67
+
68
+ $output .= $this->phpExtensions();
69
+
70
+ $output .= PHP_EOL . "### End System Info ###";
71
+
72
+ return $output;
73
+ }
74
+
75
+
76
+
77
+ /**
78
+ * @param string $string
79
+ * @return string
80
+ */
81
+ public function header($string)
82
+ {
83
+ return PHP_EOL . "-- {$string}" . PHP_EOL . PHP_EOL;
84
+ }
85
+
86
+ /**
87
+ * Formating title and the value
88
+ * @param string $title
89
+ * @param string $value
90
+ * @return string
91
+ */
92
+ public function info($title, $value)
93
+ {
94
+ return str_pad($title, 56, ' ', STR_PAD_RIGHT) . $value . PHP_EOL;
95
+ }
96
+
97
+ /**
98
+ * Theme Information
99
+ * @return string
100
+ */
101
+ public function theme()
102
+ {
103
+ // Versions earlier than 3.4
104
+ if (get_bloginfo("version") < "3.4" )
105
+ {
106
+ $themeData = get_theme_data(get_stylesheet_directory() . "/style.css");
107
+ return "{$themeData["Name"]} {$themeData["Version"]}";
108
+ }
109
+
110
+ $themeData = wp_get_theme();
111
+ return "{$themeData->Name} {$themeData->Version}";
112
+ }
113
+
114
+ /**
115
+ * Site Information
116
+ * @return string
117
+ */
118
+ public function site()
119
+ {
120
+ $output = "-- Site Info" . PHP_EOL . PHP_EOL;
121
+ $output .= $this->info("Site URL:", site_url());
122
+ $output .= $this->info("Home URL:", home_url());
123
+ $output .= $this->info("Home Path:", get_home_path());
124
+ $output .= $this->info("Installed in subdir:", ( $this->isSubDir() ? 'Yes' : 'No' )) ;
125
+ $output .= $this->info("Multisite:", ($this->isMultiSite ? "Yes" : "No" ));
126
+
127
+ return apply_filters("wpstg_sysinfo_after_site_info", $output);
128
+ }
129
+
130
+ /**
131
+ * Wp Staging plugin Information
132
+ * @return string
133
+ */
134
+ public function wpstaging() {
135
+ // Get wpstg settings
136
+ $settings = ( object ) get_option( 'wpstg_settings', array() );
137
+
138
+ // Clones data < 1.6.x
139
+ $clones = ( object ) get_option( 'wpstg_existing_clones', array() );
140
+ // Clones data version > 2.x
141
+ $clonesBeta = get_option( 'wpstg_existing_clones_beta' );
142
+
143
+
144
+ $output = "-- WP Staging Settings" . PHP_EOL . PHP_EOL;
145
+ $output .= $this->info( "Query Limit:", isset( $settings->queryLimit ) ? $settings->queryLimit : 'undefined' );
146
+ $output .= $this->info( "File Copy Limit:", isset( $settings->fileLimit ) ? $settings->fileLimit : 'undefined' );
147
+ $output .= $this->info( "Batch Size:", isset( $settings->batchSize ) ? $settings->batchSize : 'undefined' );
148
+ $output .= $this->info( "CPU Load:", isset( $settings->cpuLoad ) ? $settings->cpuLoad : 'undefined' );
149
+ $output .= $this->info( "WP in Subdir:", isset( $settings->wpSubDirectory ) ? $settings->wpSubDirectory : 'false' );
150
+
151
+ $output .= PHP_EOL . PHP_EOL . "-- Available Sites Version < 1.6.x" . PHP_EOL . PHP_EOL;
152
+
153
+ $i = 1;
154
+ foreach ( $clones as $key => $value ) {
155
+ $output .= $this->info( "Site name & subfolder :", $value );
156
+ }
157
+ $output .= PHP_EOL . PHP_EOL . "-- Available Sites Version > 2.0.x" . PHP_EOL . PHP_EOL;
158
+
159
+ foreach ( $clonesBeta as $key => $clone ) {
160
+ $output .= $this->info( "Number:", isset( $clone['number'] ) ? $clone['number'] : 'undefined' );
161
+ $output .= $this->info( "directoryName:", isset( $clone['directoryName'] ) ? $clone['directoryName'] : 'undefined' );
162
+ $output .= $this->info( "Path:", isset( $clone['path'] ) ? $clone['path'] : 'undefined' );
163
+ $output .= $this->info( "URL:", isset( $clone['url'] ) ? $clone['url'] : 'undefined' );
164
+ $output .= $this->info( "DB Prefix:", isset( $clone['prefix'] ) ? $clone['prefix'] : 'undefined' );
165
+ $output .= $this->info( "DB Prefix wp-config.php:", $this->getStagingPrefix($clone));
166
+ $output .= $this->info( "Version:", isset( $clone['version'] ) ? $clone['version'] : 'undefined' ) . PHP_EOL . PHP_EOL;
167
+ }
168
+
169
+ //$output .= PHP_EOL . PHP_EOL;
170
+
171
+ $output .= $this->info( "Plugin Version:", get_option('wpstg_version', 'undefined') );
172
+ $output .= $this->info( "Install Date:", get_option('wpstg_installDate', 'undefined') );
173
+ $output .= $this->info( "Upgraded from:", get_option('wpstg_version_upgraded_from', 'undefined') );
174
+ $output .= $this->info( "Is Staging Site:", get_option('wpstg_is_staging_site', 'undefined') ) . PHP_EOL . PHP_EOL;
175
+
176
+
177
+ return apply_filters( "wpstg_sysinfo_after_wpstaging_info", $output );
178
+ }
179
+
180
+ /**
181
+ * Browser Information
182
+ * @return string
183
+ */
184
+ public function browser()
185
+ {
186
+ $output = $this->header("User Browser");
187
+ $output .= (new Browser);
188
+
189
+ return apply_filters("wpstg_sysinfo_after_user_browser", $output);
190
+ }
191
+
192
+ /**
193
+ * Frontpage Information when frontpage is set to "page"
194
+ * @return string
195
+ */
196
+ public function frontPage()
197
+ {
198
+ if (get_option("show_on_front") !== "page")
199
+ {
200
+ return '';
201
+ }
202
+
203
+ $frontPageID = get_option("page_on_front");
204
+ $blogPageID = get_option("page_for_posts");
205
+
206
+ // Front Page
207
+ $pageFront = ($frontPageID != 0) ? get_the_title($frontPageID) . " (#{$frontPageID})" : "Unset";
208
+ // Blog Page ID
209
+ $pageBlog = ($blogPageID != 0) ? get_the_title($blogPageID) . " (#{$blogPageID})" : "Unset";
210
+
211
+ $output = $this->info("Page On Front:", $pageFront);
212
+ $output .= $this->info("Page For Posts:", $pageBlog);
213
+
214
+ return $output;
215
+ }
216
+
217
+ /**
218
+ * Check wp_remote_post() functionality
219
+ * @return string
220
+ */
221
+ public function wpRemotePost()
222
+ {
223
+ // Make sure wp_remote_post() is working
224
+ $wpRemotePost = "wp_remote_post() does not work";
225
+
226
+ // Send request
227
+ $response = wp_remote_post(
228
+ "https://www.paypal.com/cgi-bin/webscr",
229
+ array(
230
+ "sslverify" => false,
231
+ "timeout" => 60,
232
+ "user-agent" => "WPSTG/" . WPStaging::VERSION,
233
+ "body" => array("cmd" => "_notify-validate")
234
+ )
235
+ );
236
+
237
+ // Validate it worked
238
+ if (!is_wp_error($response) && 200 <= $response["response"]["code"] && 300> $response["response"]["code"])
239
+ {
240
+ $wpRemotePost = "wp_remote_post() works";
241
+ }
242
+
243
+ return $this->info("Remote Post:", $wpRemotePost);
244
+ }
245
+
246
+ /**
247
+ * WordPress Configuration
248
+ * @return string
249
+ */
250
+ public function wp()
251
+ {
252
+ $output = $this->header("WordPress Configuration");
253
+ $output .= $this->info("Version:", get_bloginfo("version"));
254
+ $output .= $this->info("Language:", (defined("WPLANG") && WPLANG) ? WPLANG : "en_US");
255
+
256
+ $permalinkStructure = get_option("permalink_structure");;
257
+ $output .= $this->info("Permalink Structure:", ($permalinkStructure) ? $permalinkStructure : "Default");
258
+
259
+ $output .= $this->info("Active Theme:", $this->theme());
260
+ $output .= $this->info("Show On Front:", get_option("show_on_front"));
261
+
262
+ // Frontpage information
263
+ $output .= $this->frontPage();
264
+
265
+ // WP Remote Post
266
+ $output .= $this->wpRemotePost();
267
+
268
+ // Table Prefix
269
+ $wpDB = $this->di->get("wpdb");
270
+ $tablePrefix = "DB Prefix: " . $wpDB->prefix . ' ';
271
+ $tablePrefix .= "Length: " . strlen($wpDB->prefix) . " Status: ";
272
+ $tablePrefix .= (strlen($wpDB->prefix) > 16) ? " ERROR: Too long" : " Acceptable";
273
+
274
+ $output .= $this->info("Table Prefix:", $tablePrefix);
275
+
276
+ // WP Debug
277
+ $output .= $this->info("WP_DEBUG:", (defined("WP_DEBUG")) ? WP_DEBUG ? "Enabled" : "Disabled" : "Not set");
278
+ $output .= $this->info("Memory Limit:", WP_MEMORY_LIMIT);
279
+ $output .= $this->info("Registered Post Stati:", implode(", ", \get_post_stati()));
280
+
281
+ return apply_filters("wpstg_sysinfo_after_wpstg_config", $output);
282
+ }
283
+
284
+ /**
285
+ * List of Active Plugins
286
+ * @param array $plugins
287
+ * @param array $activePlugins
288
+ * @return string
289
+ */
290
+ public function activePlugins($plugins, $activePlugins)
291
+ {
292
+ $output = $this->header("WordPress Active Plugins");
293
+
294
+ foreach ($plugins as $path => $plugin)
295
+ {
296
+ if (!in_array($path, $activePlugins))
297
+ {
298
+ continue;
299
+ }
300
+
301
+ $output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
302
+ }
303
+
304
+ return apply_filters("wpstg_sysinfo_after_wordpress_plugins", $output);
305
+ }
306
+
307
+ /**
308
+ * List of Inactive Plugins
309
+ * @param array $plugins
310
+ * @param array $activePlugins
311
+ * @return string
312
+ */
313
+ public function inactivePlugins($plugins, $activePlugins)
314
+ {
315
+ $output = $this->header("WordPress Inactive Plugins");
316
+
317
+ foreach ($plugins as $path => $plugin)
318
+ {
319
+ if (in_array($path, $activePlugins))
320
+ {
321
+ continue;
322
+ }
323
+
324
+ $output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
325
+ }
326
+
327
+ return apply_filters("wpstg_sysinfo_after_wordpress_plugins_inactive", $output);
328
+ }
329
+
330
+ /**
331
+ * Get list of active and inactive plugins
332
+ * @return string
333
+ */
334
+ public function plugins()
335
+ {
336
+ // Get plugins and active plugins
337
+ $plugins = get_plugins();
338
+ $activePlugins = get_option("active_plugins", array());
339
+
340
+ // Active plugins
341
+ $output = $this->activePlugins($plugins, $activePlugins);
342
+ $output .= $this->inactivePlugins($plugins, $activePlugins);
343
+
344
+ return $output;
345
+ }
346
+
347
+ /**
348
+ * Multisite Plugins
349
+ * @return string
350
+ */
351
+ public function multiSitePlugins()
352
+ {
353
+ if (!$this->isMultiSite)
354
+ {
355
+ return '';
356
+ }
357
+
358
+ $output = $this->header("Network Active Plugins");
359
+
360
+ $plugins = wp_get_active_network_plugins();
361
+ $activePlugins = get_site_option("active_sitewide_plugins", array());
362
+
363
+ foreach ($plugins as $pluginPath)
364
+ {
365
+ $pluginBase = plugin_basename($pluginPath);
366
+
367
+ if (!array_key_exists($pluginBase, $activePlugins))
368
+ {
369
+ continue;
370
+ }
371
+
372
+ $plugin = get_plugin_data($pluginPath);
373
+
374
+ $output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
375
+ }
376
+ unset($plugins, $activePlugins);
377
+
378
+ return $output;
379
+ }
380
+
381
+ /**
382
+ * Server Information
383
+ * @return string
384
+ */
385
+ public function server()
386
+ {
387
+ // Server Configuration
388
+ $output = $this->header("Webserver Configuration");
389
+
390
+ $output .= $this->info("PHP Version:", PHP_VERSION);
391
+ $output .= $this->info("MySQL Version:", $this->di->get("wpdb")->db_version());
392
+ $output .= $this->info("Webserver Info:", $_SERVER["SERVER_SOFTWARE"]);
393
+
394
+ return apply_filters("wpstg_sysinfo_after_webserver_config", $output);
395
+ }
396
+
397
+ /**
398
+ * PHP Configuration
399
+ * @return string
400
+ */
401
+ public function php()
402
+ {
403
+ $output = $this->header("PHP Configuration");
404
+ $output .= $this->info("Safe Mode:", ($this->isSafeModeEnabled() ? "Enabled" : "Disabled"));
405
+ $output .= $this->info("Memory Limit:", ini_get("memory_limit"));
406
+ $output .= $this->info("Upload Max Size:", ini_get("upload_max_filesize"));
407
+ $output .= $this->info("Post Max Size:", ini_get("post_max_size"));
408
+ $output .= $this->info("Upload Max Filesize:", ini_get("upload_max_filesize"));
409
+ $output .= $this->info("Time Limit:", ini_get("max_execution_time"));
410
+ $output .= $this->info("Max Input Vars:", ini_get("max_input_vars"));
411
+
412
+ $displayErrors = ini_get("display_errors");
413
+ $output .= $this->info("Display Errors:", ($displayErrors) ? "On ({$displayErrors})" : "N/A");
414
+
415
+ return apply_filters("wpstg_sysinfo_after_php_config", $output);
416
+ }
417
+
418
+ /**
419
+ * Check if PHP is on Safe Mode
420
+ * @return bool
421
+ */
422
+ public function isSafeModeEnabled()
423
+ {
424
+ return (
425
+ version_compare(PHP_VERSION, "5.4.0", '<') &&
426
+ @ini_get("safe_mode")
427
+ );
428
+ }
429
+
430
+ /**
431
+ * Checks if function exists or not
432
+ * @param string $functionName
433
+ * @return string
434
+ */
435
+ public function isSupported($functionName)
436
+ {
437
+ return (function_exists($functionName)) ? "Supported" : "Not Supported";
438
+ }
439
+
440
+ /**
441
+ * Checks if class or extension is loaded / exists to determine if it is installed or not
442
+ * @param string $name
443
+ * @param bool $isClass
444
+ * @return string
445
+ */
446
+ public function isInstalled($name, $isClass = true)
447
+ {
448
+ if (true === $isClass)
449
+ {
450
+ return (class_exists($name)) ? "Installed" : "Not Installed";
451
+ }
452
+ else
453
+ {
454
+ return (extension_loaded($name)) ? "Installed" : "Not Installed";
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Gets Installed Important PHP Extensions
460
+ * @return string
461
+ */
462
+ public function phpExtensions()
463
+ {
464
+ // Important PHP Extensions
465
+ $output = $this->header("PHP Extensions");
466
+ $output .= $this->info("cURL:", $this->isSupported("curl_init"));
467
+ $output .= $this->info("fsockopen:", $this->isSupported("fsockopen"));
468
+ $output .= $this->info("SOAP Client:", $this->isInstalled("SoapClient"));
469
+ $output .= $this->info("Suhosin:", $this->isInstalled("suhosin", false));
470
+
471
+ return apply_filters("wpstg_sysinfo_after_php_ext", $output);
472
+ }
473
+
474
+ /**
475
+ * Check if WP is installed in subdir
476
+ * @return boolean
477
+ */
478
+ private function isSubDir(){
479
+ if ( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
480
+ return true;
481
+ }
482
+ return false;
483
+ }
484
+
485
+ /**
486
+ * Check and return prefix of the staging site
487
+ */
488
+ /**
489
+ * Try to get the staging prefix from wp-config.php of staging site
490
+ * @param array $clone
491
+ * @return sting
492
+ */
493
+ private function getStagingPrefix($clone=array()) {
494
+ // Throw error
495
+ $path = ABSPATH . $clone['directoryName'] . "/wp-config.php";
496
+ if (false === ($content = @file_get_contents($path))) {
497
+ return 'Can\'t find staging wp-config.php';
498
+ } else {
499
+
500
+ // Get prefix from wp-config.php
501
+ //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
502
+ preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
503
+ //wp_die(var_dump($matches));
504
+
505
+ if (!empty($matches[1])) {
506
+ return $matches[1];
507
+ } else {
508
+ return 'No table_prefix in wp-config.php';
509
+ }
510
+ }
511
+
512
+ }
513
  }
apps/Backend/Modules/Views/Forms/Settings.php CHANGED
@@ -1,220 +1,220 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Views\Forms;
3
-
4
- use WPStaging\Forms\Elements\Check;
5
- use WPStaging\Forms\Elements\Numerical;
6
- use WPStaging\Forms\Elements\Select;
7
- use WPStaging\Forms\Elements\SelectMultiple;
8
- use WPStaging\Forms\Form;
9
- use WPStaging\Backend\Modules\Views\Tabs\Tabs;
10
-
11
- /**
12
- * Class Settings
13
- * @package WPStaging\Backend\Modules\Views\Forms
14
- */
15
- class Settings
16
- {
17
-
18
- /**
19
- * @var array
20
- */
21
- private $form = array();
22
-
23
- /**
24
- * @var Tabs
25
- */
26
- private $tabs;
27
-
28
- /**
29
- * Settings constructor.
30
- * @param Tabs $tabs
31
- */
32
- public function __construct($tabs)
33
- {
34
- $this->tabs = $tabs;
35
-
36
- foreach ($this->tabs->get() as $id => $name)
37
- {
38
- if (!method_exists($this, $id))
39
- {
40
- continue;
41
- }
42
-
43
- $this->{$id}();
44
- }
45
- }
46
-
47
- private function general()
48
- {
49
- $this->form["general"] = new Form();
50
-
51
- $settings = json_decode(json_encode(get_option("wpstg_settings", array())));
52
-
53
- // DB Copy Query Limit
54
- $element = new Numerical(
55
- "wpstg_settings[queryLimit]",
56
- array(
57
- "class" => "medium-text",
58
- "step" => 1,
59
- "max" => 999999,
60
- "min" => 0
61
- )
62
- );
63
-
64
- $this->form["general"]->add(
65
- $element->setLabel("DB Copy Query Limit")
66
- ->setDefault(isset($settings->queryLimit) ? $settings->queryLimit : 1000)
67
- );
68
-
69
- $options = array('1' => '1', '250' => '250' ,'500' => '500', '1000' => '1000');
70
- // DB Copy Query Limit
71
- $element = new Select(
72
- "wpstg_settings[fileLimit]",
73
- $options,
74
- array(
75
- "class" => "medium-text",
76
- "step" => 1,
77
- "max" => 999999,
78
- "min" => 0
79
- )
80
- );
81
-
82
- $this->form["general"]->add(
83
- $element->setLabel("File Copy Limit")->setDefault(isset($settings->fileLimit) ? $settings->fileLimit : 250)
84
- );
85
-
86
-
87
- // File Copy Batch Size
88
- $element = new Numerical(
89
- "wpstg_settings[batchSize]",
90
- array(
91
- "class" => "medium-text",
92
- "step" => 1,
93
- "max" => 999999,
94
- "min" => 0
95
- )
96
- );
97
-
98
- $this->form["general"]->add(
99
- $element->setLabel("File Copy Batch Size")
100
- ->setDefault(isset($settings->batchSize) ? $settings->batchSize : 2)
101
- );
102
-
103
- // CPU load priority
104
- $element = new Select(
105
- "wpstg_settings[cpuLoad]",
106
- array(
107
- "high" => "High (fast)",
108
- "medium" => "Medium (average)",
109
- "low" => "Low (slow)"
110
- )
111
- );
112
-
113
- $this->form["general"]->add(
114
- $element->setLabel("CPU load priority")
115
- ->setDefault(isset($settings->cpuLoad) ? $settings->cpuLoad : "fast")
116
- );
117
-
118
-
119
- // Optimizer
120
- $element = new Check(
121
- "wpstg_settings[optimizer]",
122
- array('1' => "Select the plugins you wish to disable during clone process")
123
- );
124
-
125
- $this->form["general"]->add(
126
- $element->setLabel("Optimizer")
127
- ->setDefault((isset($settings->optimizer)) ? $settings->optimizer : null)
128
- );
129
-
130
- // Plugins
131
- $plugins = array();
132
-
133
- foreach (get_plugins() as $key => $data)
134
- {
135
- if ("wp-staging/wp-staging.php" === $key)
136
- {
137
- continue;
138
- }
139
-
140
- $plugins[$key] = $data["Name"];
141
- }
142
-
143
- $element = new Select(
144
- "wpstg_settings[blackListedPlugins][]",
145
- $plugins,
146
- array(
147
- "multiple" => "multiple",
148
- "style" => "min-height:400px;"
149
- )
150
- );
151
-
152
- $this->form["general"]->add(
153
- $element->setDefault((isset($settings->blackListedPlugins)) ? $settings->blackListedPlugins : null)
154
- );
155
-
156
- // Disable admin authorization
157
- $element = new Check(
158
- "wpstg_settings[disableAdminLogin]",
159
- array('1' => '')
160
- );
161
-
162
- $this->form["general"]->add(
163
- $element->setLabel("Disable admin authorization")
164
- ->setDefault((isset($settings->disableAdminLogin)) ? $settings->disableAdminLogin : null)
165
- );
166
-
167
- // WordPress in subdirectory
168
- $element = new Check(
169
- "wpstg_settings[wpSubDirectory]",
170
- array('1' => '')
171
- );
172
-
173
- $this->form["general"]->add(
174
- $element->setLabel("Wordpress in subdirectory")
175
- ->setDefault((isset($settings->wpSubDirectory)) ? $settings->wpSubDirectory : null)
176
- );
177
-
178
- // Debug Mode
179
- $element = new Check(
180
- "wpstg_settings[debugMode]",
181
- array('1' => '')
182
- );
183
-
184
- $this->form["general"]->add(
185
- $element->setLabel("Debug Mode")
186
- ->setDefault((isset($settings->debugMode)) ? $settings->debugMode : null)
187
- );
188
-
189
- // Remove Data on Uninstall?
190
- $element = new Check(
191
- "wpstg_settings[unInstallOnDelete]",
192
- array('1' => '')
193
- );
194
-
195
- $this->form["general"]->add(
196
- $element->setLabel("Remove Data on Uninstall?")
197
- ->setDefault((isset($settings->unInstallOnDelete)) ? $settings->unInstallOnDelete : null)
198
- );
199
-
200
- // Check Directory Sizes
201
- $element = new Check(
202
- "wpstg_settings[checkDirectorySize]",
203
- array('1' => '')
204
- );
205
-
206
- $this->form["general"]->add(
207
- $element->setLabel("Check Directory Size")
208
- ->setDefault((isset($settings->checkDirectorySize)) ? $settings->checkDirectorySize : null)
209
- );
210
- }
211
-
212
- /**
213
- * @param string $name
214
- * @return array|Form
215
- */
216
- public function get($name = null)
217
- {
218
- return (null === $name) ? $this->form : $this->form[$name];
219
- }
220
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Views\Forms;
3
+
4
+ use WPStaging\Forms\Elements\Check;
5
+ use WPStaging\Forms\Elements\Numerical;
6
+ use WPStaging\Forms\Elements\Select;
7
+ use WPStaging\Forms\Elements\SelectMultiple;
8
+ use WPStaging\Forms\Form;
9
+ use WPStaging\Backend\Modules\Views\Tabs\Tabs;
10
+
11
+ /**
12
+ * Class Settings
13
+ * @package WPStaging\Backend\Modules\Views\Forms
14
+ */
15
+ class Settings
16
+ {
17
+
18
+ /**
19
+ * @var array
20
+ */
21
+ private $form = array();
22
+
23
+ /**
24
+ * @var Tabs
25
+ */
26
+ private $tabs;
27
+
28
+ /**
29
+ * Settings constructor.
30
+ * @param Tabs $tabs
31
+ */
32
+ public function __construct($tabs)
33
+ {
34
+ $this->tabs = $tabs;
35
+
36
+ foreach ($this->tabs->get() as $id => $name)
37
+ {
38
+ if (!method_exists($this, $id))
39
+ {
40
+ continue;
41
+ }
42
+
43
+ $this->{$id}();
44
+ }
45
+ }
46
+
47
+ private function general()
48
+ {
49
+ $this->form["general"] = new Form();
50
+
51
+ $settings = json_decode(json_encode(get_option("wpstg_settings", array())));
52
+
53
+ // DB Copy Query Limit
54
+ $element = new Numerical(
55
+ "wpstg_settings[queryLimit]",
56
+ array(
57
+ "class" => "medium-text",
58
+ "step" => 1,
59
+ "max" => 999999,
60
+ "min" => 0
61
+ )
62
+ );
63
+
64
+ $this->form["general"]->add(
65
+ $element->setLabel("DB Copy Query Limit")
66
+ ->setDefault(isset($settings->queryLimit) ? $settings->queryLimit : 5000)
67
+ );
68
+
69
+ $options = array('1' => '1', '250' => '250' ,'500' => '500', '1000' => '1000');
70
+ // DB Copy Query Limit
71
+ $element = new Select(
72
+ "wpstg_settings[fileLimit]",
73
+ $options,
74
+ array(
75
+ "class" => "medium-text",
76
+ "step" => 1,
77
+ "max" => 999999,
78
+ "min" => 0
79
+ )
80
+ );
81
+
82
+ $this->form["general"]->add(
83
+ $element->setLabel("File Copy Limit")->setDefault(isset($settings->fileLimit) ? $settings->fileLimit : 250)
84
+ );
85
+
86
+
87
+ // File Copy Batch Size
88
+ $element = new Numerical(
89
+ "wpstg_settings[batchSize]",
90
+ array(
91
+ "class" => "medium-text",
92
+ "step" => 1,
93
+ "max" => 999999,
94
+ "min" => 0
95
+ )
96
+ );
97
+
98
+ $this->form["general"]->add(
99
+ $element->setLabel("File Copy Batch Size")
100
+ ->setDefault(isset($settings->batchSize) ? $settings->batchSize : 2)
101
+ );
102
+
103
+ // CPU load priority
104
+ $element = new Select(
105
+ "wpstg_settings[cpuLoad]",
106
+ array(
107
+ "high" => "High (fast)",
108
+ "medium" => "Medium (average)",
109
+ "low" => "Low (slow)"
110
+ )
111
+ );
112
+
113
+ $this->form["general"]->add(
114
+ $element->setLabel("CPU load priority")
115
+ ->setDefault(isset($settings->cpuLoad) ? $settings->cpuLoad : "fast")
116
+ );
117
+
118
+
119
+ // Optimizer
120
+ $element = new Check(
121
+ "wpstg_settings[optimizer]",
122
+ array('1' => "Select the plugins you wish to disable during clone process")
123
+ );
124
+
125
+ $this->form["general"]->add(
126
+ $element->setLabel("Optimizer")
127
+ ->setDefault((isset($settings->optimizer)) ? $settings->optimizer : null)
128
+ );
129
+
130
+ // Plugins
131
+ $plugins = array();
132
+
133
+ foreach (get_plugins() as $key => $data)
134
+ {
135
+ if ("wp-staging/wp-staging.php" === $key)
136
+ {
137
+ continue;
138
+ }
139
+
140
+ $plugins[$key] = $data["Name"];
141
+ }
142
+
143
+ $element = new Select(
144
+ "wpstg_settings[blackListedPlugins][]",
145
+ $plugins,
146
+ array(
147
+ "multiple" => "multiple",
148
+ "style" => "min-height:400px;"
149
+ )
150
+ );
151
+
152
+ $this->form["general"]->add(
153
+ $element->setDefault((isset($settings->blackListedPlugins)) ? $settings->blackListedPlugins : null)
154
+ );
155
+
156
+ // Disable admin authorization
157
+ $element = new Check(
158
+ "wpstg_settings[disableAdminLogin]",
159
+ array('1' => '')
160
+ );
161
+
162
+ $this->form["general"]->add(
163
+ $element->setLabel("Disable admin authorization")
164
+ ->setDefault((isset($settings->disableAdminLogin)) ? $settings->disableAdminLogin : null)
165
+ );
166
+
167
+ // WordPress in subdirectory
168
+ $element = new Check(
169
+ "wpstg_settings[wpSubDirectory]",
170
+ array('1' => '')
171
+ );
172
+
173
+ $this->form["general"]->add(
174
+ $element->setLabel("Wordpress in subdirectory")
175
+ ->setDefault((isset($settings->wpSubDirectory)) ? $settings->wpSubDirectory : null)
176
+ );
177
+
178
+ // Debug Mode
179
+ $element = new Check(
180
+ "wpstg_settings[debugMode]",
181
+ array('1' => '')
182
+ );
183
+
184
+ $this->form["general"]->add(
185
+ $element->setLabel("Debug Mode")
186
+ ->setDefault((isset($settings->debugMode)) ? $settings->debugMode : null)
187
+ );
188
+
189
+ // Remove Data on Uninstall?
190
+ $element = new Check(
191
+ "wpstg_settings[unInstallOnDelete]",
192
+ array('1' => '')
193
+ );
194
+
195
+ $this->form["general"]->add(
196
+ $element->setLabel("Remove Data on Uninstall?")
197
+ ->setDefault((isset($settings->unInstallOnDelete)) ? $settings->unInstallOnDelete : null)
198
+ );
199
+
200
+ // Check Directory Sizes
201
+ $element = new Check(
202
+ "wpstg_settings[checkDirectorySize]",
203
+ array('1' => '')
204
+ );
205
+
206
+ $this->form["general"]->add(
207
+ $element->setLabel("Check Directory Size")
208
+ ->setDefault((isset($settings->checkDirectorySize)) ? $settings->checkDirectorySize : null)
209
+ );
210
+ }
211
+
212
+ /**
213
+ * @param string $name
214
+ * @return array|Form
215
+ */
216
+ public function get($name = null)
217
+ {
218
+ return (null === $name) ? $this->form : $this->form[$name];
219
+ }
220
  }
apps/Backend/Notices/Notices.php CHANGED
@@ -1,155 +1,155 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Notices;
4
-
5
- /*
6
- * Admin Notices | Warnings | Messages
7
- */
8
-
9
- // No Direct Access
10
- if( !defined( "WPINC" ) ) {
11
- die;
12
- }
13
-
14
- use WPStaging\WPStaging;
15
-
16
- /**
17
- * Class Notices
18
- * @package WPStaging\Backend\Notices
19
- */
20
- class Notices {
21
-
22
- /**
23
- * @var string
24
- */
25
- private $path;
26
-
27
- /**
28
- * @var string
29
- */
30
- private $url;
31
-
32
- public function __construct( $path, $url ) {
33
- $this->path = $path;
34
- $this->url = $url;
35
- }
36
-
37
- /**
38
- * Check whether the page is an WP QUADS admin settings page or not
39
- * @return bool
40
- */
41
- private function isAdminPage() {
42
- $currentPage = (isset( $_GET["page"] )) ? $_GET["page"] : null;
43
-
44
- $availablePages = array(
45
- "wpstg-settings", "wpstg-addons", "wpstg-tools", "wpstg-clone", "wpstg_clone"
46
- );
47
-
48
- //if( !is_admin() || !did_action( "wp_loaded" ) || !in_array( $currentPage, $availablePages, true ) ) {
49
- if( !is_admin() || !in_array( $currentPage, $availablePages, true ) ) {
50
- return false;
51
- }
52
-
53
- return true;
54
- }
55
-
56
- /**
57
- * Check if notice should be shown after certain days of installation
58
- * @param int $days default 10
59
- * @return bool
60
- */
61
- private function canShow( $option, $days = 10 ) {
62
-
63
- if( empty( $option ) ) {
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
- $this->plugin_deactivated_notice();
81
-
82
- // Do not display notices to user_roles lower than 'update_plugins'
83
- if( !current_user_can( 'update_plugins' ) ) {
84
- return;
85
- }
86
-
87
- $viewsNoticesPath = "{$this->path}views/_includes/messages/";
88
-
89
- // Show rating review message on all admin pages
90
- if( $this->canShow( "wpstg_rating", 7 ) ) {
91
- require_once "{$viewsNoticesPath}rating.php";
92
- }
93
-
94
-
95
- // Display messages below on wp quads admin page only
96
- if( !$this->isAdminPage() ) {
97
- return;
98
- }
99
-
100
-
101
- $varsDirectory = \WPStaging\WPStaging::getContentDir();
102
-
103
-
104
- // Poll do not show any longer
105
- /* if( $this->canShow( "wpstg_poll", 7 ) ) {
106
- require_once "{$viewsNoticesPath}poll.php";
107
- } */
108
-
109
- // Cache directory in uploads is not writable
110
- if( !wp_is_writable( $varsDirectory ) ) {
111
- require_once "{$viewsNoticesPath}/uploads-cache-directory-permission-problem.php";
112
- }
113
- // Staging directory is not writable
114
- if( !wp_is_writable( get_home_path() ) ) {
115
- require_once "{$viewsNoticesPath}/staging-directory-permission-problem.php";
116
- }
117
-
118
- // Version Control
119
- if( version_compare( WPStaging::WP_COMPATIBLE, get_bloginfo( "version" ), "<" ) ) {
120
- require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
121
- }
122
-
123
- // Beta
124
- if( false === get_option( "wpstg_beta" ) || "no" !== get_option( "wpstg_beta" ) ) {
125
- require_once "{$viewsNoticesPath}beta.php";
126
- }
127
-
128
- // WP Staging Pro and Free can not be activated both
129
- if( false !== ( $deactivatedNoticeID = get_transient( "wp_staging_deactivated_notice_id" ) ) ) {
130
- require_once "{$viewsNoticesPath}transient.php";
131
- delete_transient( "wp_staging_deactivated_notice_id" );
132
- }
133
- }
134
-
135
- /**
136
- * Show a message when pro or free plugin becomes deactivated
137
- *
138
- * @return void
139
- */
140
- private function plugin_deactivated_notice() {
141
- if( false !== ( $deactivated_notice_id = get_transient( 'wp_staging_deactivated_notice_id' ) ) ) {
142
- if( '1' === $deactivated_notice_id ) {
143
- $message = __( "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging.", 'wpstg' );
144
- } else {
145
- $message = __( "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging Pro.", 'wpstg' );
146
- }
147
- ?>
148
- <div class="updated notice is-dismissible" style="border-left: 4px solid #ffba00;">
149
- <p><?php echo esc_html( $message ); ?></p>
150
- </div> <?php
151
- delete_transient( 'wp_staging_deactivated_notice_id' );
152
- }
153
- }
154
-
155
  }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Notices;
4
+
5
+ /*
6
+ * Admin Notices | Warnings | Messages
7
+ */
8
+
9
+ // No Direct Access
10
+ if( !defined( "WPINC" ) ) {
11
+ die;
12
+ }
13
+
14
+ use WPStaging\WPStaging;
15
+
16
+ /**
17
+ * Class Notices
18
+ * @package WPStaging\Backend\Notices
19
+ */
20
+ class Notices {
21
+
22
+ /**
23
+ * @var string
24
+ */
25
+ private $path;
26
+
27
+ /**
28
+ * @var string
29
+ */
30
+ private $url;
31
+
32
+ public function __construct( $path, $url ) {
33
+ $this->path = $path;
34
+ $this->url = $url;
35
+ }
36
+
37
+ /**
38
+ * Check whether the page is an WP QUADS admin settings page or not
39
+ * @return bool
40
+ */
41
+ private function isAdminPage() {
42
+ $currentPage = (isset( $_GET["page"] )) ? $_GET["page"] : null;
43
+
44
+ $availablePages = array(
45
+ "wpstg-settings", "wpstg-addons", "wpstg-tools", "wpstg-clone", "wpstg_clone"
46
+ );
47
+
48
+ //if( !is_admin() || !did_action( "wp_loaded" ) || !in_array( $currentPage, $availablePages, true ) ) {
49
+ if( !is_admin() || !in_array( $currentPage, $availablePages, true ) ) {
50
+ return false;
51
+ }
52
+
53
+ return true;
54
+ }
55
+
56
+ /**
57
+ * Check if notice should be shown after certain days of installation
58
+ * @param int $days default 10
59
+ * @return bool
60
+ */
61
+ private function canShow( $option, $days = 10 ) {
62
+
63
+ if( empty( $option ) ) {
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
+ $this->plugin_deactivated_notice();
81
+
82
+ // Do not display notices to user_roles lower than 'update_plugins'
83
+ if( !current_user_can( 'update_plugins' ) ) {
84
+ return;
85
+ }
86
+
87
+ $viewsNoticesPath = "{$this->path}views/_includes/messages/";
88
+
89
+ // Show rating review message on all admin pages
90
+ if( $this->canShow( "wpstg_rating", 7 ) ) {
91
+ require_once "{$viewsNoticesPath}rating.php";
92
+ }
93
+
94
+
95
+ // Display messages below on wp quads admin page only
96
+ if( !$this->isAdminPage() ) {
97
+ return;
98
+ }
99
+
100
+
101
+ $varsDirectory = \WPStaging\WPStaging::getContentDir();
102
+
103
+
104
+ // Poll do not show any longer
105
+ /* if( $this->canShow( "wpstg_poll", 7 ) ) {
106
+ require_once "{$viewsNoticesPath}poll.php";
107
+ } */
108
+
109
+ // Cache directory in uploads is not writable
110
+ if( !wp_is_writable( $varsDirectory ) ) {
111
+ require_once "{$viewsNoticesPath}/uploads-cache-directory-permission-problem.php";
112
+ }
113
+ // Staging directory is not writable
114
+ if( !wp_is_writable( get_home_path() ) ) {
115
+ require_once "{$viewsNoticesPath}/staging-directory-permission-problem.php";
116
+ }
117
+
118
+ // Version Control
119
+ if( version_compare( WPStaging::WP_COMPATIBLE, get_bloginfo( "version" ), "<" ) ) {
120
+ require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
121
+ }
122
+
123
+ // Beta
124
+ if( false === get_option( "wpstg_beta" ) || "no" !== get_option( "wpstg_beta" ) ) {
125
+ require_once "{$viewsNoticesPath}beta.php";
126
+ }
127
+
128
+ // WP Staging Pro and Free can not be activated both
129
+ if( false !== ( $deactivatedNoticeID = get_transient( "wp_staging_deactivated_notice_id" ) ) ) {
130
+ require_once "{$viewsNoticesPath}transient.php";
131
+ delete_transient( "wp_staging_deactivated_notice_id" );
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Show a message when pro or free plugin becomes deactivated
137
+ *
138
+ * @return void
139
+ */
140
+ private function plugin_deactivated_notice() {
141
+ if( false !== ( $deactivated_notice_id = get_transient( 'wp_staging_deactivated_notice_id' ) ) ) {
142
+ if( '1' === $deactivated_notice_id ) {
143
+ $message = __( "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging.", 'wpstg' );
144
+ } else {
145
+ $message = __( "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging Pro.", 'wpstg' );
146
+ }
147
+ ?>
148
+ <div class="updated notice is-dismissible" style="border-left: 4px solid #ffba00;">
149
+ <p><?php echo esc_html( $message ); ?></p>
150
+ </div> <?php
151
+ delete_transient( 'wp_staging_deactivated_notice_id' );
152
+ }
153
+ }
154
+
155
  }
apps/Backend/Upgrade/Upgrade.php CHANGED
@@ -1,229 +1,316 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Upgrade;
4
-
5
- use WPStaging\WPStaging;
6
- use WPStaging\Utils\Logger;
7
-
8
- /**
9
- * Upgrade Class
10
- * This must be loaded on every page init to ensure all settings are
11
- * adjusted correctly and to run any upgrade process if necessary.
12
- */
13
-
14
- // No Direct Access
15
- if( !defined( "WPINC" ) ) {
16
- die;
17
- }
18
-
19
- class Upgrade {
20
-
21
- /**
22
- * Previous Version number
23
- * @var string
24
- */
25
- private $previousVersion;
26
-
27
- /**
28
- * Clone data
29
- * @var obj
30
- */
31
- private $clones;
32
-
33
- /**
34
- * Clone data
35
- * @var obj
36
- */
37
- private $clonesBeta;
38
-
39
- /**
40
- * Cron data
41
- * @var obj
42
- */
43
- private $cron;
44
-
45
- /**
46
- * Logger
47
- * @var obj
48
- */
49
- private $logger;
50
-
51
- public function __construct() {
52
-
53
- // add wpstg_weekly_event to cron events
54
- $this->cron = new \WPStaging\Cron\Cron;
55
-
56
- // Previous version
57
- $this->previousVersion = preg_replace( '/[^0-9.].*/', '', get_option( 'wpstg_version' ) );
58
-
59
- // Options earlier than version 2.0.0
60
- $this->clones = get_option( "wpstg_existing_clones", array() );
61
-
62
- // Current options
63
- $this->clonesBeta = get_option( "wpstg_existing_clones_beta", array() );
64
-
65
- // Logger
66
- $this->logger = new Logger;
67
- }
68
-
69
- public function doUpgrade() {
70
- $this->upgrade2_0_3();
71
- //$this->upgrade2_0_4();
72
- $this->upgrade2_1_2();
73
- $this->setVersion();
74
- }
75
-
76
- /**
77
- * Upgrade method 2.0.3
78
- */
79
- public function upgrade2_0_3() {
80
- // Previous version lower than 2.0.2 or new install
81
- if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.0.2', '<' ) ) {
82
- $this->upgradeOptions();
83
- $this->upgradeClonesBeta();
84
- $this->upgradeNotices();
85
- }
86
- }
87
-
88
- /**
89
- * Upgrade method 2.0.4
90
- */
91
- // public function upgrade2_0_4() {
92
- // if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.0.4', '<' ) ) {
93
- //
94
- // // Register cron job.
95
- // $this->cron->schedule_event();
96
- //
97
- // // Install Optimizer
98
- // $optimizer = new Optimizer();
99
- // $optimizer->installOptimizer();
100
- // }
101
- // }
102
-
103
- /**
104
- * Upgrade method 2.1.2
105
- * Sanitize the clone key value.
106
- */
107
- private function upgrade2_1_2(){
108
- if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.1.7', '<' ) ) {
109
- foreach ( $this->clonesBeta as $key => $value){
110
- unset($this->clonesBeta[$key]);
111
- $this->clonesBeta[preg_replace("#\W+#", '-', strtolower($key))] = $value;
112
- }
113
- update_option('wpstg_existing_clones_beta', $this->clonesBeta);
114
- }
115
- }
116
-
117
- /**
118
- * Upgrade routine for new install
119
- */
120
- private function upgradeOptions() {
121
- // Write some default vars
122
- add_option( 'wpstg_installDate', date( 'Y-m-d h:i:s' ) );
123
- }
124
-
125
- /**
126
- * Write new version number into db
127
- * return bool
128
- */
129
- private function setVersion() {
130
- // Check if version number in DB is lower than version number in current plugin
131
- if( version_compare( $this->previousVersion, \WPStaging\WPStaging::VERSION, '<' ) ) {
132
- // Update Version number
133
- update_option( 'wpstg_version', preg_replace( '/[^0-9.].*/', '', \WPStaging\WPStaging::VERSION ) );
134
- // Update "upgraded from" version number
135
- update_option( 'wpstg_version_upgraded_from', preg_replace( '/[^0-9.].*/', '', $this->previousVersion ) );
136
-
137
- return true;
138
- }
139
- return false;
140
- }
141
-
142
- /**
143
- * Create a new db option for beta version 2.0.2
144
- * @return bool
145
- */
146
- private function upgradeClonesBeta() {
147
-
148
- // Copy old data to new option
149
- //update_option( 'wpstg_existing_clones_beta', $this->clones );
150
-
151
- $new = array();
152
-
153
- if( empty( $this->clones ) ) {
154
- return false;
155
- }
156
-
157
-
158
- foreach ( $this->clones as $key => &$value ) {
159
-
160
- // Skip the rest of the loop if data is already compatible to wpstg 2.0.2
161
- if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
162
- continue;
163
- }
164
-
165
- $new[$value]['directoryName'] = $value;
166
- $new[$value]['path'] = get_home_path() . $value;
167
- $new[$value]['url'] = get_home_url() . "/" . $value;
168
- $new[$value]['number'] = $key + 1;
169
- $new[$value]['version'] = $this->previousVersion;
170
- }
171
- unset( $value );
172
-
173
- if( empty( $new ) || false === update_option( 'wpstg_existing_clones_beta', $new ) ) {
174
- $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
175
- }
176
- }
177
-
178
- /**
179
- * Convert clone data from wpstg 1.x to wpstg 2.x
180
- * Only use this later when wpstg 2.x is ready for production
181
- */
182
- private function upgradeClones() {
183
-
184
- $new = array();
185
-
186
- if( empty( $this->clones ) ) {
187
- return false;
188
- }
189
-
190
- foreach ( $this->clones as $key => &$value ) {
191
-
192
- // Skip the rest of the loop if data is already compatible to wpstg 2.0.1
193
- if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
194
- continue;
195
- }
196
- $new[$value]['directoryName'] = $value;
197
- $new[$value]['path'] = get_home_path() . $value;
198
- $new[$value]['url'] = get_home_url() . "/" . $value;
199
- $new[$value]['number'] = $key + 1;
200
- $new[$value]['version'] = $this->previousVersion;
201
- }
202
- unset( $value );
203
-
204
- if( empty( $new ) || false === update_option( 'wpstg_existing_clones', $new ) ) {
205
- $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
206
- }
207
- }
208
-
209
- /**
210
- * Upgrade Notices db options from wpstg 1.3 -> 2.0.1
211
- * Fix some logical db options
212
- */
213
- private function upgradeNotices() {
214
- $poll = get_option( "wpstg_start_poll", false );
215
- $beta = get_option( "wpstg_hide_beta", false );
216
- $rating = get_option( "wpstg_RatingDiv", false );
217
-
218
- if( $poll && $poll === "no" ) {
219
- update_option( 'wpstg_poll', 'no' );
220
- }
221
- if( $beta && $beta === "yes" ) {
222
- update_option( 'wpstg_beta', 'no' );
223
- }
224
- if( $rating && $rating === 'yes' ) {
225
- update_option( 'wpstg_rating', 'no' );
226
- }
227
- }
228
-
229
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Upgrade;
4
+
5
+ use WPStaging\WPStaging;
6
+ use WPStaging\Utils\Logger;
7
+
8
+
9
+ /**
10
+ * Upgrade Class
11
+ * This must be loaded on every page init to ensure all settings are
12
+ * adjusted correctly and to run any upgrade process if necessary.
13
+ */
14
+
15
+ // No Direct Access
16
+ if( !defined( "WPINC" ) ) {
17
+ die;
18
+ }
19
+
20
+ class Upgrade {
21
+
22
+ /**
23
+ * Previous Version number
24
+ * @var string
25
+ */
26
+ private $previousVersion;
27
+
28
+ /**
29
+ * Clone data
30
+ * @var obj
31
+ */
32
+ private $clones;
33
+
34
+ /**
35
+ * Clone data
36
+ * @var obj
37
+ */
38
+ //private $clonesBeta;
39
+
40
+ /**
41
+ * Cron data
42
+ * @var obj
43
+ */
44
+ private $cron;
45
+
46
+ /**
47
+ * Logger
48
+ * @var obj
49
+ */
50
+ private $logger;
51
+
52
+ /**
53
+ * db object
54
+ * @var obj
55
+ */
56
+ private $db;
57
+
58
+
59
+ public function __construct() {
60
+
61
+ // add wpstg_weekly_event to cron events
62
+ $this->cron = new \WPStaging\Cron\Cron;
63
+
64
+ // Previous version
65
+ $this->previousVersion = preg_replace( '/[^0-9.].*/', '', get_option( 'wpstg_version' ) );
66
+
67
+ // Options earlier than version 2.0.0
68
+ //$this->clones = get_option( "wpstg_existing_clones", array() );
69
+
70
+ // Logger
71
+ $this->logger = new Logger;
72
+
73
+ // db
74
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
75
+ }
76
+
77
+ public function doUpgrade() {
78
+ $this->upgrade2_0_3();
79
+ $this->upgrade2_1_2();
80
+ $this->upgrade2_2_0();
81
+ $this->setVersion();
82
+ }
83
+
84
+ /**
85
+ * Upgrade method 2.2.0
86
+ */
87
+ public function upgrade2_2_0(){
88
+ // Previous version lower than 2.2.0
89
+ if( version_compare( $this->previousVersion, '2.2.0', '<' ) ) {
90
+ $this->upgradeElements();
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Add missing elements
96
+ */
97
+ private function upgradeElements() {
98
+ // Current options
99
+ $sites = get_option( "wpstg_existing_clones_beta", array() );
100
+
101
+ if (false === $sites){
102
+ return;
103
+ }
104
+
105
+ // Check if key prefix is missing and add it
106
+ foreach ( $sites as $key => $value ) {
107
+ if (empty( $sites[$key]['directoryName'] )){
108
+ continue;
109
+ }
110
+ //!empty( $sites[$key]['prefix'] ) ? $sites[$key]['prefix'] = $value['prefix'] : $sites[$key]['prefix'] = $key . '_';
111
+ !empty( $sites[$key]['prefix'] ) ?
112
+ $sites[$key]['prefix'] = $value['prefix'] :
113
+ $sites[$key]['prefix'] = $this->getStagingPrefix( $sites[$key]['directoryName'] );
114
+ }
115
+
116
+ if( !empty($sites) ) {
117
+ update_option( 'wpstg_existing_clones_beta', $sites );
118
+ }
119
+ }
120
+
121
+
122
+
123
+ /**
124
+ * Check and return prefix of the staging site
125
+ * @param string $directory
126
+ * @return string
127
+ */
128
+ private function getStagingPrefix( $directory ) {
129
+ // Try to get staging prefix from wp-config.php of staging site
130
+
131
+ $path = ABSPATH . $directory . "/wp-config.php";
132
+ if( false === ($content = @file_get_contents( $path )) ) {
133
+ $prefix = "";
134
+ } else {
135
+ // Get prefix from wp-config.php
136
+ preg_match( "/table_prefix\s*=\s*'(\w*)';/", $content, $matches );
137
+
138
+ if( !empty( $matches[1] ) ) {
139
+ $prefix = $matches[1];
140
+ } else {
141
+ $prefix = "";
142
+ }
143
+ }
144
+ // return result: Check if staging prefix is the same as the live prefix
145
+ if( $this->db->prefix != $prefix ) {
146
+ return $prefix;
147
+ } else {
148
+ return "";
149
+ }
150
+ }
151
+
152
+
153
+ /**
154
+ * Upgrade method 2.0.3
155
+ */
156
+ public function upgrade2_0_3() {
157
+ // Previous version lower than 2.0.2 or new install
158
+ if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.0.2', '<' ) ) {
159
+ $this->upgradeOptions();
160
+ $this->upgradeClonesBeta();
161
+ $this->upgradeNotices();
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Upgrade method 2.1.2
167
+ * Sanitize the clone key value.
168
+ */
169
+ private function upgrade2_1_2() {
170
+
171
+ // Current options
172
+ $this->clonesBeta = get_option( "wpstg_existing_clones_beta", array() );
173
+
174
+ if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.1.7', '<' ) ) {
175
+ foreach ( $this->clonesBeta as $key => $value ) {
176
+ unset( $this->clonesBeta[$key] );
177
+ $this->clonesBeta[preg_replace( "#\W+#", '-', strtolower( $key ) )] = $value;
178
+ }
179
+ if( empty( $this->clonesBeta ) )
180
+ return false;
181
+
182
+ update_option( 'wpstg_existing_clones_beta', $this->clonesBeta );
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Upgrade routine for new install
188
+ */
189
+ private function upgradeOptions() {
190
+ // Write some default vars
191
+ add_option( 'wpstg_installDate', date( 'Y-m-d h:i:s' ) );
192
+ }
193
+
194
+ /**
195
+ * Write new version number into db
196
+ * return bool
197
+ */
198
+ private function setVersion() {
199
+ // Check if version number in DB is lower than version number in current plugin
200
+ if( version_compare( $this->previousVersion, \WPStaging\WPStaging::VERSION, '<' ) ) {
201
+ // Update Version number
202
+ update_option( 'wpstg_version', preg_replace( '/[^0-9.].*/', '', \WPStaging\WPStaging::VERSION ) );
203
+ // Update "upgraded from" version number
204
+ update_option( 'wpstg_version_upgraded_from', preg_replace( '/[^0-9.].*/', '', $this->previousVersion ) );
205
+
206
+ return true;
207
+ }
208
+ return false;
209
+ }
210
+
211
+ /**
212
+ * Create a new db option for beta version 2.0.2
213
+ * @return bool
214
+ */
215
+ private function upgradeClonesBeta() {
216
+
217
+ // Get options
218
+ $this->clones = get_option( "wpstg_existing_clones", array() );
219
+
220
+ $new = array();
221
+
222
+ if( empty( $this->clones ) ) {
223
+ return false;
224
+ }
225
+
226
+
227
+ foreach ( $this->clones as $key => &$value ) {
228
+
229
+ // Skip the rest of the loop if data is already compatible to wpstg 2.0.2
230
+ if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
231
+ continue;
232
+ }
233
+
234
+ $new[$value]['directoryName'] = $value;
235
+ $new[$value]['path'] = get_home_path() . $value;
236
+ $new[$value]['url'] = get_home_url() . "/" . $value;
237
+ $new[$value]['number'] = $key + 1;
238
+ $new[$value]['version'] = $this->previousVersion;
239
+ //$new[$value]['prefix'] = $value . '_';
240
+
241
+ }
242
+ unset( $value );
243
+
244
+ if( empty( $new ) || false === update_option( 'wpstg_existing_clones_beta', $new ) ) {
245
+ $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Convert clone data from wpstg 1.x to wpstg 2.x
251
+ * Only use this later when wpstg 2.x is ready for production
252
+ */
253
+ // private function upgradeClones() {
254
+ //
255
+ // $new = array();
256
+ //
257
+ // // Get options
258
+ // $this->clones = get_option( "wpstg_existing_clones", array() );
259
+ //
260
+ // if( empty( $this->clones ) ) {
261
+ // return false;
262
+ // }
263
+ //
264
+ // foreach ( $this->clones as $key => &$value ) {
265
+ //
266
+ // // Skip the rest of the loop if data is already compatible to wpstg 2.0.1
267
+ // if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
268
+ // continue;
269
+ // }
270
+ // $new[$value]['directoryName'] = $value;
271
+ // $new[$value]['path'] = get_home_path() . $value;
272
+ // $new[$value]['url'] = get_home_url() . "/" . $value;
273
+ // $new[$value]['number'] = $key + 1;
274
+ // $new[$value]['version'] = $this->previousVersion;
275
+ // }
276
+ // unset( $value );
277
+ //
278
+ // if( empty( $new ) || false === update_option( 'wpstg_existing_clones', $new ) ) {
279
+ // $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
280
+ // }
281
+ // }
282
+
283
+ /**
284
+ * Upgrade Notices db options from wpstg 1.3 -> 2.0.1
285
+ * Fix some logical db options
286
+ */
287
+ private function upgradeNotices() {
288
+ $poll = get_option( "wpstg_start_poll", false );
289
+ $beta = get_option( "wpstg_hide_beta", false );
290
+ $rating = get_option( "wpstg_RatingDiv", false );
291
+
292
+ if( $poll && $poll === "no" ) {
293
+ update_option( 'wpstg_poll', 'no' );
294
+ }
295
+ if( $beta && $beta === "yes" ) {
296
+ update_option( 'wpstg_beta', 'no' );
297
+ }
298
+ if( $rating && $rating === 'yes' ) {
299
+ update_option( 'wpstg_rating', 'no' );
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Throw a errror message via json and stop further execution
305
+ * @param string $message
306
+ */
307
+ private function returnException($message = ''){
308
+ wp_die( json_encode(array(
309
+ 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
310
+ 'status' => false,
311
+ 'message' => $message,
312
+ 'error' => true
313
+ )));
314
+ }
315
+
316
+ }
apps/Backend/public/css/wpstg-admin.css CHANGED
@@ -1,784 +1,784 @@
1
- /**
2
- * WPSTG Admin CSS
3
- *
4
- * @package WPSTG
5
- * @subpackage Admin CSS
6
- * @copyright Copyright (c) 2015, René Hermenau
7
- * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8
- */
9
-
10
-
11
- /* CSS for Tabs */
12
-
13
- #tab_container ul {
14
- /*height: 200px;*/
15
- list-style: none;
16
- margin: 0;
17
- padding: 0;
18
- background: #f1f1f1;
19
- float: left;
20
- /*list-style-type: square;*/
21
- }
22
-
23
- #tab_container ul li:first-child.selected-tab {
24
- border-top: none;
25
- }
26
-
27
- #tab_container ul li a.selected-tab {
28
- font-weight: bold;
29
- text-decoration: none;
30
- }
31
-
32
-
33
-
34
- #tab_container .row{
35
- padding-top:10px;
36
- padding-bottom:12px;
37
- }
38
-
39
- #wpstg-tools .nav-tab-wrapper{
40
- padding:0;
41
- }
42
-
43
-
44
- #tab_container .row label strong, #tab_container .row strong {
45
- font-weight: bold;
46
- }
47
-
48
- .wpstg-tabs a {
49
- padding:5px;
50
- }
51
-
52
- #tab_container > ul > li.wpstg-tabs.active {
53
- background-color:white;
54
- }
55
-
56
-
57
-
58
- #wpstg_settingsgeneral_header .row:nth-child(3), #wpstg_settingsgeneral_header .row:nth-child(4){
59
- display:none;
60
- }
61
-
62
- /* Layout of admin table and rows
63
- */
64
-
65
- #tab_container .panel-container {
66
- background: #FFF;
67
- padding:0 20px 20px 20px;
68
- overflow:auto;
69
- }
70
-
71
- #tab_container .form-table th {
72
- vertical-align: top;
73
- text-align: left;
74
- padding: 20px 10px 20px 0;
75
- line-height: 1.3;
76
- font-weight: bold;
77
- font-size: 14px;
78
- color:#484848;
79
- width: 30%;
80
- }
81
-
82
- #tab_container .form-table tr {
83
- border-bottom: 1px solid #E7E7E7;
84
- }
85
-
86
- #tab_container span.description{
87
- display: block;
88
- font-weight: 400;
89
- font-style: normal;
90
- font-size: 13px;
91
- margin-top: 7px;
92
- color:#484848;
93
- }
94
-
95
- #tab_container .col-title{
96
- color:#484848;
97
- }
98
-
99
- @media only screen and (max-width:680px) {
100
- #tab_container ul {
101
- float:none;
102
- }
103
- #tab_container .form-table tr > th {
104
- width:100%;
105
- }
106
- #tab_container span.description{
107
- font-size: 14px;
108
- }
109
- #tab_container .form-table tr > th, #tab_container .form-table tr > td {
110
- padding:10px;
111
- }
112
- }
113
-
114
- #tab_container ul li {
115
- margin-bottom:0;
116
- }
117
-
118
- #tab_container ul li a {
119
- display: block;
120
- padding:10px 4px 10px 14px;
121
- border-width: 1px 0;
122
- border-style: solid;
123
- border-top-color:white;
124
- border-bottom-color:#e7e7e7;
125
- text-decoration: none;
126
- color: #0097DF;
127
- font-weight: bold;
128
- }
129
- #tab_container ul li a:hover {
130
- background-color: #e5e5e5;
131
- color:#777777;
132
- }
133
-
134
-
135
- .wp-staginglogo{
136
- display: block;
137
- font-size:16px;
138
- padding-top:20px;
139
- width:220px;
140
- float:left;
141
- }
142
-
143
- .wpstg-version{
144
- display: block;
145
- padding-top:29px
146
- }
147
-
148
-
149
- .wpstg_admin .nav-tab {
150
- color: #3C3C3C;
151
- }
152
-
153
-
154
- #tab_container table tbody tr:nth-child(1) > th > div {
155
- font-size: 20px;
156
- }
157
-
158
- .wpstg_hidden{
159
- display: none;
160
- }
161
-
162
- /* End layout of admin table and rows
163
- */
164
- #mashtabcontainer > .mashtabs {
165
- background-color: #ffffff;
166
- }
167
-
168
- #mashtabcontainer ul .active {
169
- background-color: #00adef;
170
- color: white;
171
- border-bottom-color: #0098D2;
172
- }
173
-
174
- #mashtabcontainer ul .active:hover {
175
- background-color: #00A4E2;
176
- color: white;
177
- border-bottom-color: #0098D2;
178
- }
179
-
180
- #mashtabcontainer ul li a {
181
- padding: 10px 14px 10px 14px;
182
- background-color: #f3f3f3
183
-
184
- }
185
-
186
- #mashtabcontainer .mashtab-container {
187
- border: 0 solid #ececec;
188
- }
189
-
190
- /* Cloning workflow */
191
- #wpstg-clonepage-wrapper {
192
- margin-bottom: 20px;
193
- /*width: 690px; */
194
- }
195
-
196
- @media screen and (min-width:1090px){
197
- #wpstg-clonepage-wrapper {
198
- float: left;
199
- margin-bottom: 20px;
200
- /*width: 690px;*/
201
- }
202
- .wpstg-sidebar{
203
- display: none;
204
- margin-left: 700px;
205
- margin-top: 138px;
206
- }
207
- }
208
-
209
- .wpstg-sidebar{
210
- display: none;
211
- padding: 10px;
212
- border: 1px solid #DFDFDF;
213
- max-width: 250px;
214
- height: 250px;
215
- }
216
-
217
- #wpstg-steps {
218
- margin-top:30px;
219
- }
220
-
221
- #wpstg-steps li {
222
- color: #444;
223
- line-height: 20px;
224
- padding-right:10px;
225
- float:left;
226
- }
227
-
228
-
229
- .wpstg-step-num {
230
- border: 1px solid #444;
231
- border-radius: 3px;
232
- display: inline-block;
233
- width: 20px;
234
- height: 20px;
235
- text-align: center;
236
- margin-right: 5px;
237
- }
238
-
239
- .wpstg-current-step {
240
- font-weight: bold;
241
- }
242
-
243
- .wpstg-current-step .wpstg-step-num {
244
- background: #444;
245
- color: #eee;
246
- }
247
-
248
- .wpstg-clone {
249
- border: 3px solid #ffffff;
250
- margin-bottom: 5px;
251
- padding: 5px 10px;
252
- width: 300px;
253
- position: relative;
254
- overflow: hidden;
255
- transition: border-color .2s ease-in-out;
256
- background-color: #DDDDDD;
257
- }
258
-
259
- .wpstg-clone.active {
260
- border-color: #1d94cf;;
261
- }
262
-
263
- .wpstg-clone-title {
264
- display: inline-block;
265
- font-size: 15px;
266
- max-width: 130px;
267
- text-decoration: none;
268
- font-weight: bold;
269
- color:#0285AE;
270
- }
271
-
272
- .wpstg-clone-action {
273
- background: #ffffff;
274
- border-left: 1px solid #ccc;
275
- color: #bbb;
276
- padding: 0 5px;
277
- float: right;
278
- text-decoration: none;
279
- position: relative;
280
- transition: color .2s ease-in-out;
281
- }
282
-
283
- .wpstg-remove-clone:hover {
284
- color: #ef6d6d;
285
- }
286
-
287
- .wpstg-clone-action:last-child {
288
- border: none;
289
- }
290
-
291
- .wpstg-clone:hover .wpstg-clone-action {
292
- display: inline-block;
293
- }
294
-
295
- #wpstg-show-error-details:focus,
296
- #wpstg-workflow .wpstg-clone-action {
297
- outline: none;
298
- box-shadow: none;
299
- }
300
-
301
- .wpstg-link-btn {
302
- background: #45a1c9;
303
- color: #fff;
304
- display: inline-block;
305
- padding: 5px 10px;
306
- text-decoration: none;
307
- vertical-align: baseline;
308
- transition: all .2s ease-in-out;
309
- }
310
-
311
- .wpstg-link-btn:hover,
312
- .wpstg-link-btn:focus {
313
- color: #fff;
314
- outline: none;
315
- box-shadow: none;
316
- }
317
-
318
- #wpstg-workflow .wpstg-link-btn:active {
319
- vertical-align: baseline;
320
- }
321
-
322
- .wpstg-link-btn[disabled] {
323
- background: #777 !important;
324
- pointer-events: none;
325
- }
326
-
327
- #wpstg-cancel-cloning {
328
- background: #ff3428;
329
- border-color: #e72f24;
330
- margin-top: 5px;
331
- }
332
-
333
- #wpstg-cancel-cloning.success {
334
- background: #64dd58;
335
- border-color: #54bd4a;
336
- }
337
-
338
- #wpstg-error-wrapper,
339
- #wpstg-error-details {
340
- display:none;
341
- padding-top: 10px;
342
- font-size: 13px;
343
- clear:both;
344
- }
345
-
346
- #wpstg-show-error-details {
347
- display: inline-block;
348
- margin-left: 5px;
349
- color: #555;
350
- text-decoration: none;
351
- transition: color .2s ease-in-out;
352
- }
353
-
354
- #wpstg-show-error-details:hover {
355
- color: #1d94cf;
356
- }
357
-
358
- #wpstg-error-details {
359
- border-left: 5px solid #ef6d6d;
360
- padding: 10px;
361
- width: 500px;
362
- }
363
-
364
- #wpstg-home-link,
365
- #wpstg-try-again {
366
- display: none;
367
- }
368
-
369
-
370
- #wpstg-loader {
371
- content: url('../img/loading.gif');
372
- margin-top:-5px;
373
- }
374
-
375
- #wpstg-loader.wpstg-finished {
376
- content:"Finished";
377
- background-color:#00c89a;
378
- color:white;
379
- padding:2px;
380
- margin-top:6px;
381
- }
382
-
383
- #wpstg-workflow {
384
- position: relative;
385
- clear:both;
386
- padding-top:20px;
387
- margin-right:20px;
388
- float:left;
389
- min-width: 500px;
390
- border-right: 1px solid #DFDFDF;
391
- min-height: 380px;
392
- padding-right: 20px;
393
- }
394
-
395
- #wpstg-sidebar {
396
- float: left;
397
- max-width: 400px;
398
- display: block;
399
- }
400
-
401
- @media screen and (max-width:1150px){
402
- #wpstg-sidebar img{
403
- margin-top: 30px;
404
- }
405
- }
406
-
407
- #wpstg-workflow.loading::after,
408
- #wpstg-removing-clone.loading::after {
409
- background: rgba(255, 255, 255, .7);
410
- content: 'Loading... may take a while for huge websites';
411
- display: block;
412
- width: 100%;
413
- height: 100%;
414
- font-size: 20px;
415
- padding-top: 100px;
416
- text-align: center;
417
- position: absolute;
418
- top: 0;
419
- left: 0;
420
- z-index: 99;
421
- }
422
-
423
- #wpstg-removing-clone.loading::after {
424
- content: 'REMOVING' !important;
425
- }
426
-
427
- #wpstg-existing-clones,
428
- #wpstg-removing-clone {
429
- position: relative;
430
- }
431
-
432
- .wpstg-progress-bar {
433
- max-width: 900px;
434
- height: 27px;
435
- padding: 0;
436
- background-color: #d6d8d7;
437
- }
438
-
439
- .wpstg-progress {
440
- background: #1d94cf;
441
- width: 0;
442
- height: 100%;
443
- transition: width 1s;
444
- color:white;
445
- line-height:25px;
446
- text-align:center;
447
- }
448
-
449
- #wpstg-new-clone-id.wpstg-error-input,
450
- #wpstg-clone-path.wpstg-error-input {
451
- border: 1px solid #ff4235;
452
- box-shadow: 0 0 2px rgba(255, 66, 53, .8);
453
- }
454
-
455
- #wpstg-clone-path {
456
- margin-left: 10px;
457
- width: 350px;
458
- }
459
-
460
- .wpstg-error-msg {
461
- color: #ff4235;
462
- }
463
-
464
- #wpstg-clone-id-error {
465
- display: block;
466
- text-align: right;
467
- margin-right: 90px;
468
- }
469
-
470
- #wpstg-start-cloning + .wpstg-error-msg {
471
- display: block;
472
- margin-top: 5px;
473
- }
474
-
475
- .wpstg-size-info {
476
- color: #999;
477
- font-weight: normal;
478
- position: relative;
479
- left: 2px;
480
- }
481
-
482
- .wpstg-db-table .wpstg-size-info {
483
- top: 2px;
484
- }
485
-
486
- #wpstg-workflow #wpstg-start-cloning {
487
- display: inline-block;
488
- margin-left: 5px;
489
- font-size: 14px;
490
- vertical-align: baseline;
491
- }
492
-
493
- /* Tabs */
494
- .wpstg-tabs-wrapper {
495
- max-width: 640px;
496
- margin: 10px 0;
497
- }
498
-
499
- #wpstg-path-wrapper {
500
- border-bottom: 2px dashed #ccc;
501
- padding-bottom: 10px;
502
- margin-bottom: 10px;
503
- }
504
-
505
- .wpstg-tabs-wrapper {
506
- border: 1px solid #ddd;
507
- border-right: none;
508
- border-left: none;
509
- }
510
-
511
- .wpstg-tab-section {
512
- border: 1px solid #ddd;
513
- border-right: none;
514
- border-left: none;
515
- display: none;
516
- padding: 20px;
517
- }
518
-
519
- .wpstg-tab-section::after {
520
- display: block;
521
- content: '';
522
- clear: both;
523
- }
524
-
525
- .wpstg-tab-header {
526
- border: 1px solid #ddd;
527
- border-right: none;
528
- border-left: none;
529
- color: #444;
530
- font-size: 16px;
531
- font-weight: bolder;
532
- display: block;
533
- padding: 10px;;
534
- text-decoration: none;
535
- }
536
-
537
- .wpstg-tab-triangle {
538
- font-family: arial;
539
- display: inline-block;
540
- margin-right: 10px;
541
- }
542
-
543
- .wpstg-tab-header:focus {
544
- color: #444;
545
- outline: none;
546
- box-shadow: none;
547
- }
548
-
549
- #wpstg-large-files {
550
- display:none;
551
- border: 1px dashed #ccc;
552
- /*float: right;*/
553
- padding: 10px 10px 10px;
554
- margin-top:20px;
555
- position: relative;
556
- font-size:12px;
557
- }
558
-
559
- #wpstg-large-files h3 {
560
- background: #fff;
561
- margin: 0;
562
- padding: 0 5px;
563
- position: absolute;
564
- top: -10px;
565
- left: 5px;
566
- }
567
-
568
- /* tmp */
569
- .wpstg-subdir {
570
- display: none;
571
- margin-left: 20px;
572
- }
573
-
574
- .wpstg-dir a.disabled {
575
- color: #888;
576
- cursor: default;
577
- text-decoration: none;
578
- }
579
-
580
- .wpstg-check-subdirs {
581
- display: inline-block;
582
- margin-left: 10px;
583
- }
584
-
585
- .wpstg-notice-alert{
586
- display:block;
587
- background-color:#FFD0D0;
588
- padding:20px;
589
- border: 1px solid #fff;
590
- max-width: 600px;
591
- }
592
-
593
- .wpstg-header{
594
- font-weight: 400;
595
- line-height: 1.6em;
596
- font-size: 19px;
597
- border-bottom: 1px solid #DFDFDF;
598
- clear:both;
599
- }
600
-
601
-
602
- #wpstg-clone-label{
603
- font-size: 14px;
604
- font-weight: bold;
605
- }
606
-
607
- #wpstg-log-details{
608
- height: 300px;
609
- max-width: 700px;
610
- overflow: scroll;
611
- /*max-width: 650px;*/
612
- font-family: monospace;
613
- font-size: 12px;
614
- line-height: 15px;
615
- border: 1px solid #FFF;
616
- background-color: black;
617
- color: #c0c0c0;
618
- padding:3px;
619
- white-space: nowrap;
620
- margin-top: 15px;
621
- }
622
-
623
- #wpstg-finished-result {
624
- display:none;
625
- }
626
-
627
- #wpstg-remove-cloning {
628
- background: #ff3428;
629
- border-color: #e72f24;
630
- margin-top: 5px;
631
- }
632
-
633
- #wpstg-success-notice{
634
- padding: 10px;
635
- background-color: white;
636
- max-width: 900px;
637
- border: 1px solid #ccc;
638
- margin-top: 20px;
639
- }
640
-
641
- .wpstg_beta_notice {
642
- margin-bottom:20px;
643
- }
644
-
645
- .wpstg-sysinfo {
646
- width:700px;
647
- height: 500px;
648
- }
649
-
650
- .form-table .col-title label {
651
- font-weight: 600;
652
- }
653
-
654
- .form-table td:first-child {
655
- width:30%;
656
- }
657
-
658
-
659
- .wpstg-share-button-container{
660
- margin: 5px 0;
661
- }
662
-
663
- .wpstg-share-button-container p{
664
- margin:0px 0 10px 0;
665
- }
666
-
667
- .wpstg-share-button {
668
- display: inline-block;
669
- }
670
-
671
- .wpstg-share-button a{
672
- text-decoration:none;
673
- }
674
-
675
- .wpstg-share-button .wpstg-share {
676
- font-family:sans-serif;
677
- font-weight:bold;
678
- text-decoration:none;
679
- text-align:center;
680
- }
681
-
682
-
683
- .wpstg-share-button .wpstg-share {
684
- -webkit-border-radius:2px;
685
- -moz-border-radius:2px;
686
- border-radius:2px;
687
- color:#FFF;
688
- display:inline;
689
- font-size:16px;
690
- width:40px;
691
- padding:4px 8px;
692
- }
693
-
694
- .wpstg-share-button-twitter .wpstg-share {
695
- background-color:#00ABF0;
696
- }
697
-
698
- .wpstg-share-button-facebook .wpstg-share {
699
- background-color:#3b5998;
700
- }
701
-
702
- .wpstg-share-button-googleplus .wpstg-share {
703
- background-color:#F53424;
704
- }
705
-
706
- .wpstg-share-button-twitter .share:active,.wpstg-share-button-facebook .share:active,.wpstg-share-button-googleplus .share:active {
707
- background-color:#353535;
708
- }
709
-
710
- #wpstg-check-space {
711
- margin-left:8px;
712
- }
713
-
714
- /* welcome screen */
715
- .wpstg-button.green {
716
- display: inline-block;
717
- background-color: #83c11f;
718
- padding: 10px;
719
- min-width: 170px;
720
- color: white;
721
- font-size: 16px;
722
- text-decoration: none;
723
- text-align: center;
724
- margin-top: 20px;
725
- }
726
- #wpstg-welcome li {
727
- font-size: 18px;
728
- line-height: 29px;
729
- position: relative;
730
- padding-left: 23px;
731
- list-style: none!important;
732
- }
733
- #wpstg-welcome {
734
- margin-top:20px;
735
- margin-right: 20px;
736
- background-color: white;
737
- }
738
- .wpstg-heading-pro {
739
- color: #0080ff;
740
- font-weight: bold;
741
- }
742
- .wpstg-h2 {
743
- margin-top: 0px;
744
- margin-bottom: 1.2rem;
745
- font-size: 30px;
746
- line-height: 2.5rem;
747
- }
748
- #wpstg-welcome li:before {
749
- width: 1em;
750
- height: 100%;
751
- background: url(data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%221792%22%20height%3D%221792%22%20viewBox%3D%220%200%201792%201792%22%20xmlns%3D%22http%3A%2F%2Fwww%2Ew3%2Eorg%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22%2377B227%22%20d%3D%22M1671%20566q0%2040%2D28%2068l%2D724%20724%2D136%20136q%2D28%2028%2D68%2028t%2D68%2D28l%2D136%2D136%2D362%2D362q%2D28%2D28%2D28%2D68t28%2D68l136%2D136q28%2D28%2068%2D28t68%2028l294%20295%20656%2D657q28%2D28%2068%2D28t68%2028l136%20136q28%2028%2028%2068z%22%2F%3E%3C%2Fsvg%3E) left .4em no-repeat;
752
- background-size: contain;
753
- content: "";
754
- position: absolute;
755
- top: 0;
756
- left: 0;
757
- color: #77b227;
758
- }
759
- .wpstg-h1 {
760
- font-size: 2.75em;
761
- margin-bottom: 1.35rem;
762
- font-size: 2.5em;
763
- line-height: 3.68rem;
764
- letter-spacing: normal;
765
- }
766
- #wpstg-welcome h2 {
767
- margin: 0 0 15px;
768
- }
769
- #wpstg-welcome .wpstg-footer {
770
- clear: both;
771
- margin-top: 20px;
772
- font-style: italic;
773
- }
774
-
775
- .wpstg-staging-info {
776
- clear:both;
777
- float: right;
778
- color:grey;
779
- font-size: 12px;
780
- }
781
-
782
- .wpstg-bold{
783
- font-weight: 600;
784
  }
1
+ /**
2
+ * WPSTG Admin CSS
3
+ *
4
+ * @package WPSTG
5
+ * @subpackage Admin CSS
6
+ * @copyright Copyright (c) 2015, René Hermenau
7
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8
+ */
9
+
10
+
11
+ /* CSS for Tabs */
12
+
13
+ #tab_container ul {
14
+ /*height: 200px;*/
15
+ list-style: none;
16
+ margin: 0;
17
+ padding: 0;
18
+ background: #f1f1f1;
19
+ float: left;
20
+ /*list-style-type: square;*/
21
+ }
22
+
23
+ #tab_container ul li:first-child.selected-tab {
24
+ border-top: none;
25
+ }
26
+
27
+ #tab_container ul li a.selected-tab {
28
+ font-weight: bold;
29
+ text-decoration: none;
30
+ }
31
+
32
+
33
+
34
+ #tab_container .row{
35
+ padding-top:10px;
36
+ padding-bottom:12px;
37
+ }
38
+
39
+ #wpstg-tools .nav-tab-wrapper{
40
+ padding:0;
41
+ }
42
+
43
+
44
+ #tab_container .row label strong, #tab_container .row strong {
45
+ font-weight: bold;
46
+ }
47
+
48
+ .wpstg-tabs a {
49
+ padding:5px;
50
+ }
51
+
52
+ #tab_container > ul > li.wpstg-tabs.active {
53
+ background-color:white;
54
+ }
55
+
56
+
57
+
58
+ #wpstg_settingsgeneral_header .row:nth-child(3), #wpstg_settingsgeneral_header .row:nth-child(4){
59
+ display:none;
60
+ }
61
+
62
+ /* Layout of admin table and rows
63
+ */
64
+
65
+ #tab_container .panel-container {
66
+ background: #FFF;
67
+ padding:0 20px 20px 20px;
68
+ overflow:auto;
69
+ }
70
+
71
+ #tab_container .form-table th {
72
+ vertical-align: top;
73
+ text-align: left;
74
+ padding: 20px 10px 20px 0;
75
+ line-height: 1.3;
76
+ font-weight: bold;
77
+ font-size: 14px;
78
+ color:#484848;
79
+ width: 30%;
80
+ }
81
+
82
+ #tab_container .form-table tr {
83
+ border-bottom: 1px solid #E7E7E7;
84
+ }
85
+
86
+ #tab_container span.description{
87
+ display: block;
88
+ font-weight: 400;
89
+ font-style: normal;
90
+ font-size: 13px;
91
+ margin-top: 7px;
92
+ color:#484848;
93
+ }
94
+
95
+ #tab_container .col-title{
96
+ color:#484848;
97
+ }
98
+
99
+ @media only screen and (max-width:680px) {
100
+ #tab_container ul {
101
+ float:none;
102
+ }
103
+ #tab_container .form-table tr > th {
104
+ width:100%;
105
+ }
106
+ #tab_container span.description{
107
+ font-size: 14px;
108
+ }
109
+ #tab_container .form-table tr > th, #tab_container .form-table tr > td {
110
+ padding:10px;
111
+ }
112
+ }
113
+
114
+ #tab_container ul li {
115
+ margin-bottom:0;
116
+ }
117
+
118
+ #tab_container ul li a {
119
+ display: block;
120
+ padding:10px 4px 10px 14px;
121
+ border-width: 1px 0;
122
+ border-style: solid;
123
+ border-top-color:white;
124
+ border-bottom-color:#e7e7e7;
125
+ text-decoration: none;
126
+ color: #0097DF;
127
+ font-weight: bold;
128
+ }
129
+ #tab_container ul li a:hover {
130
+ background-color: #e5e5e5;
131
+ color:#777777;
132
+ }
133
+
134
+
135
+ .wp-staginglogo{
136
+ display: block;
137
+ font-size:16px;
138
+ padding-top:20px;
139
+ width:220px;
140
+ float:left;
141
+ }
142
+
143
+ .wpstg-version{
144
+ display: block;
145
+ padding-top:29px
146
+ }
147
+
148
+
149
+ .wpstg_admin .nav-tab {
150
+ color: #3C3C3C;
151
+ }
152
+
153
+
154
+ #tab_container table tbody tr:nth-child(1) > th > div {
155
+ font-size: 20px;
156
+ }
157
+
158
+ .wpstg_hidden{
159
+ display: none;
160
+ }
161
+
162
+ /* End layout of admin table and rows
163
+ */
164
+ #mashtabcontainer > .mashtabs {
165
+ background-color: #ffffff;
166
+ }
167
+
168
+ #mashtabcontainer ul .active {
169
+ background-color: #00adef;
170
+ color: white;
171
+ border-bottom-color: #0098D2;
172
+ }
173
+
174
+ #mashtabcontainer ul .active:hover {
175
+ background-color: #00A4E2;
176
+ color: white;
177
+ border-bottom-color: #0098D2;
178
+ }
179
+
180
+ #mashtabcontainer ul li a {
181
+ padding: 10px 14px 10px 14px;
182
+ background-color: #f3f3f3
183
+
184
+ }
185
+
186
+ #mashtabcontainer .mashtab-container {
187
+ border: 0 solid #ececec;
188
+ }
189
+
190
+ /* Cloning workflow */
191
+ #wpstg-clonepage-wrapper {
192
+ margin-bottom: 20px;
193
+ /*width: 690px; */
194
+ }
195
+
196
+ @media screen and (min-width:1090px){
197
+ #wpstg-clonepage-wrapper {
198
+ float: left;
199
+ margin-bottom: 20px;
200
+ /*width: 690px;*/
201
+ }
202
+ .wpstg-sidebar{
203
+ display: none;
204
+ margin-left: 700px;
205
+ margin-top: 138px;
206
+ }
207
+ }
208
+
209
+ .wpstg-sidebar{
210
+ display: none;
211
+ padding: 10px;
212
+ border: 1px solid #DFDFDF;
213
+ max-width: 250px;
214
+ height: 250px;
215
+ }
216
+
217
+ #wpstg-steps {
218
+ margin-top:30px;
219
+ }
220
+
221
+ #wpstg-steps li {
222
+ color: #444;
223
+ line-height: 20px;
224
+ padding-right:10px;
225
+ float:left;
226
+ }
227
+
228
+
229
+ .wpstg-step-num {
230
+ border: 1px solid #444;
231
+ border-radius: 3px;
232
+ display: inline-block;
233
+ width: 20px;
234
+ height: 20px;
235
+ text-align: center;
236
+ margin-right: 5px;
237
+ }
238
+
239
+ .wpstg-current-step {
240
+ font-weight: bold;
241
+ }
242
+
243
+ .wpstg-current-step .wpstg-step-num {
244
+ background: #444;
245
+ color: #eee;
246
+ }
247
+
248
+ .wpstg-clone {
249
+ border: 3px solid #ffffff;
250
+ margin-bottom: 5px;
251
+ padding: 5px 10px;
252
+ width: 300px;
253
+ position: relative;
254
+ overflow: hidden;
255
+ transition: border-color .2s ease-in-out;
256
+ background-color: #DDDDDD;
257
+ }
258
+
259
+ .wpstg-clone.active {
260
+ border-color: #1d94cf;;
261
+ }
262
+
263
+ .wpstg-clone-title {
264
+ display: inline-block;
265
+ font-size: 15px;
266
+ max-width: 130px;
267
+ text-decoration: none;
268
+ font-weight: bold;
269
+ color:#0285AE;
270
+ }
271
+
272
+ .wpstg-clone-action {
273
+ background: #ffffff;
274
+ border-left: 1px solid #ccc;
275
+ color: #bbb;
276
+ padding: 0 5px;
277
+ float: right;
278
+ text-decoration: none;
279
+ position: relative;
280
+ transition: color .2s ease-in-out;
281
+ }
282
+
283
+ .wpstg-remove-clone:hover {
284
+ color: #ef6d6d;
285
+ }
286
+
287
+ .wpstg-clone-action:last-child {
288
+ border: none;
289
+ }
290
+
291
+ .wpstg-clone:hover .wpstg-clone-action {
292
+ display: inline-block;
293
+ }
294
+
295
+ #wpstg-show-error-details:focus,
296
+ #wpstg-workflow .wpstg-clone-action {
297
+ outline: none;
298
+ box-shadow: none;
299
+ }
300
+
301
+ .wpstg-link-btn {
302
+ background: #45a1c9;
303
+ color: #fff;
304
+ display: inline-block;
305
+ padding: 5px 10px;
306
+ text-decoration: none;
307
+ vertical-align: baseline;
308
+ transition: all .2s ease-in-out;
309
+ }
310
+
311
+ .wpstg-link-btn:hover,
312
+ .wpstg-link-btn:focus {
313
+ color: #fff;
314
+ outline: none;
315
+ box-shadow: none;
316
+ }
317
+
318
+ #wpstg-workflow .wpstg-link-btn:active {
319
+ vertical-align: baseline;
320
+ }
321
+
322
+ .wpstg-link-btn[disabled] {
323
+ background: #777 !important;
324
+ pointer-events: none;
325
+ }
326
+
327
+ #wpstg-cancel-cloning {
328
+ background: #ff3428;
329
+ border-color: #e72f24;
330
+ margin-top: 5px;
331
+ }
332
+
333
+ #wpstg-cancel-cloning.success {
334
+ background: #64dd58;
335
+ border-color: #54bd4a;
336
+ }
337
+
338
+ #wpstg-error-wrapper,
339
+ #wpstg-error-details {
340
+ display:none;
341
+ padding-top: 10px;
342
+ font-size: 13px;
343
+ clear:both;
344
+ }
345
+
346
+ #wpstg-show-error-details {
347
+ display: inline-block;
348
+ margin-left: 5px;
349
+ color: #555;
350
+ text-decoration: none;
351
+ transition: color .2s ease-in-out;
352
+ }
353
+
354
+ #wpstg-show-error-details:hover {
355
+ color: #1d94cf;
356
+ }
357
+
358
+ #wpstg-error-details {
359
+ border-left: 5px solid #ef6d6d;
360
+ padding: 10px;
361
+ width: 500px;
362
+ }
363
+
364
+ #wpstg-home-link,
365
+ #wpstg-try-again {
366
+ display: none;
367
+ }
368
+
369
+
370
+ #wpstg-loader {
371
+ content: url('../img/loading.gif');
372
+ margin-top:-5px;
373
+ }
374
+
375
+ #wpstg-loader.wpstg-finished {
376
+ content:"Finished";
377
+ background-color:#00c89a;
378
+ color:white;
379
+ padding:2px;
380
+ margin-top:6px;
381
+ }
382
+
383
+ #wpstg-workflow {
384
+ position: relative;
385
+ clear:both;
386
+ padding-top:20px;
387
+ margin-right:20px;
388
+ float:left;
389
+ min-width: 500px;
390
+ border-right: 1px solid #DFDFDF;
391
+ min-height: 380px;
392
+ padding-right: 20px;
393
+ }
394
+
395
+ #wpstg-sidebar {
396
+ float: left;
397
+ max-width: 400px;
398
+ display: block;
399
+ }
400
+
401
+ @media screen and (max-width:1150px){
402
+ #wpstg-sidebar img{
403
+ margin-top: 30px;
404
+ }
405
+ }
406
+
407
+ #wpstg-workflow.loading::after,
408
+ #wpstg-removing-clone.loading::after {
409
+ background: rgba(255, 255, 255, .7);
410
+ content: 'Loading... may take a while for huge websites';
411
+ display: block;
412
+ width: 100%;
413
+ height: 100%;
414
+ font-size: 20px;
415
+ padding-top: 100px;
416
+ text-align: center;
417
+ position: absolute;
418
+ top: 0;
419
+ left: 0;
420
+ z-index: 99;
421
+ }
422
+
423
+ #wpstg-removing-clone.loading::after {
424
+ content: 'REMOVING' !important;
425
+ }
426
+
427
+ #wpstg-existing-clones,
428
+ #wpstg-removing-clone {
429
+ position: relative;
430
+ }
431
+
432
+ .wpstg-progress-bar {
433
+ max-width: 900px;
434
+ height: 27px;
435
+ padding: 0;
436
+ background-color: #d6d8d7;
437
+ }
438
+
439
+ .wpstg-progress {
440
+ background: #1d94cf;
441
+ width: 0;
442
+ height: 100%;
443
+ transition: width 1s;
444
+ color:white;
445
+ line-height:25px;
446
+ text-align:center;
447
+ }
448
+
449
+ #wpstg-new-clone-id.wpstg-error-input,
450
+ #wpstg-clone-path.wpstg-error-input {
451
+ border: 1px solid #ff4235;
452
+ box-shadow: 0 0 2px rgba(255, 66, 53, .8);
453
+ }
454
+
455
+ #wpstg-clone-path {
456
+ margin-left: 10px;
457
+ width: 350px;
458
+ }
459
+
460
+ .wpstg-error-msg {
461
+ color: #ff4235;
462
+ }
463
+
464
+ #wpstg-clone-id-error {
465
+ display: block;
466
+ text-align: right;
467
+ margin-right: 90px;
468
+ }
469
+
470
+ #wpstg-start-cloning + .wpstg-error-msg {
471
+ display: block;
472
+ margin-top: 5px;
473
+ }
474
+
475
+ .wpstg-size-info {
476
+ color: #999;
477
+ font-weight: normal;
478
+ position: relative;
479
+ left: 2px;
480
+ }
481
+
482
+ .wpstg-db-table .wpstg-size-info {
483
+ top: 2px;
484
+ }
485
+
486
+ #wpstg-workflow #wpstg-start-cloning {
487
+ display: inline-block;
488
+ margin-left: 5px;
489
+ font-size: 14px;
490
+ vertical-align: baseline;
491
+ }
492
+
493
+ /* Tabs */
494
+ .wpstg-tabs-wrapper {
495
+ max-width: 640px;
496
+ margin: 10px 0;
497
+ }
498
+
499
+ #wpstg-path-wrapper {
500
+ border-bottom: 2px dashed #ccc;
501
+ padding-bottom: 10px;
502
+ margin-bottom: 10px;
503
+ }
504
+
505
+ .wpstg-tabs-wrapper {
506
+ border: 1px solid #ddd;
507
+ border-right: none;
508
+ border-left: none;
509
+ }
510
+
511
+ .wpstg-tab-section {
512
+ border: 1px solid #ddd;
513
+ border-right: none;
514
+ border-left: none;
515
+ display: none;
516
+ padding: 20px;
517
+ }
518
+
519
+ .wpstg-tab-section::after {
520
+ display: block;
521
+ content: '';
522
+ clear: both;
523
+ }
524
+
525
+ .wpstg-tab-header {
526
+ border: 1px solid #ddd;
527
+ border-right: none;
528
+ border-left: none;
529
+ color: #444;
530
+ font-size: 16px;
531
+ font-weight: bolder;
532
+ display: block;
533
+ padding: 10px;;
534
+ text-decoration: none;
535
+ }
536
+
537
+ .wpstg-tab-triangle {
538
+ font-family: arial;
539
+ display: inline-block;
540
+ margin-right: 10px;
541
+ }
542
+
543
+ .wpstg-tab-header:focus {
544
+ color: #444;
545
+ outline: none;
546
+ box-shadow: none;
547
+ }
548
+
549
+ #wpstg-large-files {
550
+ display:none;
551
+ border: 1px dashed #ccc;
552
+ /*float: right;*/
553
+ padding: 10px 10px 10px;
554
+ margin-top:20px;
555
+ position: relative;
556
+ font-size:12px;
557
+ }
558
+
559
+ #wpstg-large-files h3 {
560
+ background: #fff;
561
+ margin: 0;
562
+ padding: 0 5px;
563
+ position: absolute;
564
+ top: -10px;
565
+ left: 5px;
566
+ }
567
+
568
+ /* tmp */
569
+ .wpstg-subdir {
570
+ display: none;
571
+ margin-left: 20px;
572
+ }
573
+
574
+ .wpstg-dir a.disabled {
575
+ color: #888;
576
+ cursor: default;
577
+ text-decoration: none;
578
+ }
579
+
580
+ .wpstg-check-subdirs {
581
+ display: inline-block;
582
+ margin-left: 10px;
583
+ }
584
+
585
+ .wpstg-notice-alert{
586
+ display:block;
587
+ background-color:#FFD0D0;
588
+ padding:20px;
589
+ border: 1px solid #fff;
590
+ max-width: 600px;
591
+ }
592
+
593
+ .wpstg-header{
594
+ font-weight: 400;
595
+ line-height: 1.6em;
596
+ font-size: 19px;
597
+ border-bottom: 1px solid #DFDFDF;
598
+ clear:both;
599
+ }
600
+
601
+
602
+ #wpstg-clone-label{
603
+ font-size: 14px;
604
+ font-weight: bold;
605
+ }
606
+
607
+ #wpstg-log-details{
608
+ height: 300px;
609
+ max-width: 700px;
610
+ overflow: scroll;
611
+ /*max-width: 650px;*/
612
+ font-family: monospace;
613
+ font-size: 12px;
614
+ line-height: 15px;
615
+ border: 1px solid #FFF;
616
+ background-color: black;
617
+ color: #c0c0c0;
618
+ padding:3px;
619
+ white-space: nowrap;
620
+ margin-top: 15px;
621
+ }
622
+
623
+ #wpstg-finished-result {
624
+ display:none;
625
+ }
626
+
627
+ #wpstg-remove-cloning {
628
+ background: #ff3428;
629
+ border-color: #e72f24;
630
+ margin-top: 5px;
631
+ }
632
+
633
+ #wpstg-success-notice{
634
+ padding: 10px;
635
+ background-color: white;
636
+ max-width: 900px;
637
+ border: 1px solid #ccc;
638
+ margin-top: 20px;
639
+ }
640
+
641
+ .wpstg_beta_notice {
642
+ margin-bottom:20px;
643
+ }
644
+
645
+ .wpstg-sysinfo {
646
+ width:700px;
647
+ height: 500px;
648
+ }
649
+
650
+ .form-table .col-title label {
651
+ font-weight: 600;
652
+ }
653
+
654
+ .form-table td:first-child {
655
+ width:30%;
656
+ }
657
+
658
+
659
+ .wpstg-share-button-container{
660
+ margin: 5px 0;
661
+ }
662
+
663
+ .wpstg-share-button-container p{
664
+ margin:0px 0 10px 0;
665
+ }
666
+
667
+ .wpstg-share-button {
668
+ display: inline-block;
669
+ }
670
+
671
+ .wpstg-share-button a{
672
+ text-decoration:none;
673
+ }
674
+
675
+ .wpstg-share-button .wpstg-share {
676
+ font-family:sans-serif;
677
+ font-weight:bold;
678
+ text-decoration:none;
679
+ text-align:center;
680
+ }
681
+
682
+
683
+ .wpstg-share-button .wpstg-share {
684
+ -webkit-border-radius:2px;
685
+ -moz-border-radius:2px;
686
+ border-radius:2px;
687
+ color:#FFF;
688
+ display:inline;
689
+ font-size:16px;
690
+ width:40px;
691
+ padding:4px 8px;
692
+ }
693
+
694
+ .wpstg-share-button-twitter .wpstg-share {
695
+ background-color:#00ABF0;
696
+ }
697
+
698
+ .wpstg-share-button-facebook .wpstg-share {
699
+ background-color:#3b5998;
700
+ }
701
+
702
+ .wpstg-share-button-googleplus .wpstg-share {
703
+ background-color:#F53424;
704
+ }
705
+
706
+ .wpstg-share-button-twitter .share:active,.wpstg-share-button-facebook .share:active,.wpstg-share-button-googleplus .share:active {
707
+ background-color:#353535;
708
+ }
709
+
710
+ #wpstg-check-space {
711
+ margin-left:8px;
712
+ }
713
+
714
+ /* welcome screen */
715
+ .wpstg-button.green {
716
+ display: inline-block;
717
+ background-color: #83c11f;
718
+ padding: 10px;
719
+ min-width: 170px;
720
+ color: white;
721
+ font-size: 16px;
722
+ text-decoration: none;
723
+ text-align: center;
724
+ margin-top: 20px;
725
+ }
726
+ #wpstg-welcome li {
727
+ font-size: 18px;
728
+ line-height: 29px;
729
+ position: relative;
730
+ padding-left: 23px;
731
+ list-style: none!important;
732
+ }
733
+ #wpstg-welcome {
734
+ margin-top:20px;
735
+ margin-right: 20px;
736
+ background-color: white;
737
+ }
738
+ .wpstg-heading-pro {
739
+ color: #0080ff;
740
+ font-weight: bold;
741
+ }
742
+ .wpstg-h2 {
743
+ margin-top: 0px;
744
+ margin-bottom: 1.2rem;
745
+ font-size: 30px;
746
+ line-height: 2.5rem;
747
+ }
748
+ #wpstg-welcome li:before {
749
+ width: 1em;
750
+ height: 100%;
751
+ background: url(data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%221792%22%20height%3D%221792%22%20viewBox%3D%220%200%201792%201792%22%20xmlns%3D%22http%3A%2F%2Fwww%2Ew3%2Eorg%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22%2377B227%22%20d%3D%22M1671%20566q0%2040%2D28%2068l%2D724%20724%2D136%20136q%2D28%2028%2D68%2028t%2D68%2D28l%2D136%2D136%2D362%2D362q%2D28%2D28%2D28%2D68t28%2D68l136%2D136q28%2D28%2068%2D28t68%2028l294%20295%20656%2D657q28%2D28%2068%2D28t68%2028l136%20136q28%2028%2028%2068z%22%2F%3E%3C%2Fsvg%3E) left .4em no-repeat;
752
+ background-size: contain;
753
+ content: "";
754
+ position: absolute;
755
+ top: 0;
756
+ left: 0;
757
+ color: #77b227;
758
+ }
759
+ .wpstg-h1 {
760
+ font-size: 2.75em;
761
+ margin-bottom: 1.35rem;
762
+ font-size: 2.5em;
763
+ line-height: 3.68rem;
764
+ letter-spacing: normal;
765
+ }
766
+ #wpstg-welcome h2 {
767
+ margin: 0 0 15px;
768
+ }
769
+ #wpstg-welcome .wpstg-footer {
770
+ clear: both;
771
+ margin-top: 20px;
772
+ font-style: italic;
773
+ }
774
+
775
+ .wpstg-staging-info {
776
+ clear:both;
777
+ float: right;
778
+ color:grey;
779
+ font-size: 12px;
780
+ }
781
+
782
+ .wpstg-bold{
783
+ font-weight: 600;
784
  }
apps/Backend/public/js/wpstg-admin-rating.js CHANGED
@@ -1,32 +1,32 @@
1
- jQuery(document).ready(function ($) {
2
- $(".wpstg_hide_rating").click(function (e) {
3
- e.preventDefault();
4
-
5
- $.ajax({
6
- url: ajaxurl,
7
- type: "POST",
8
- data: {action: "wpstg_hide_rating"},
9
- error: function (xhr, textStatus, errorThrown) {
10
- console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
11
- console.log(textStatus);
12
-
13
-
14
- alert(
15
- "Unknown error"
16
- );
17
- },
18
- success: function (data) {
19
- $(".wpstg_fivestar").slideUp("fast");
20
- return true;
21
- },
22
- statusCode: {
23
- 404: function () {
24
- alert("Something went wrong; can't find ajax request URL!");
25
- },
26
- 500: function () {
27
- alert("Something went wrong; internal server error while processing the request!");
28
- }
29
- }
30
- });
31
- });
32
  });
1
+ jQuery(document).ready(function ($) {
2
+ $(".wpstg_hide_rating").click(function (e) {
3
+ e.preventDefault();
4
+
5
+ $.ajax({
6
+ url: ajaxurl,
7
+ type: "POST",
8
+ data: {action: "wpstg_hide_rating"},
9
+ error: function (xhr, textStatus, errorThrown) {
10
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
11
+ console.log(textStatus);
12
+
13
+
14
+ alert(
15
+ "Unknown error"
16
+ );
17
+ },
18
+ success: function (data) {
19
+ $(".wpstg_fivestar").slideUp("fast");
20
+ return true;
21
+ },
22
+ statusCode: {
23
+ 404: function () {
24
+ alert("Something went wrong; can't find ajax request URL!");
25
+ },
26
+ 500: function () {
27
+ alert("Something went wrong; internal server error while processing the request!");
28
+ }
29
+ }
30
+ });
31
+ });
32
  });
apps/Backend/public/js/wpstg-admin.js CHANGED
@@ -1,1261 +1,1178 @@
1
- "use strict";
2
-
3
- var WPStaging = (function ($)
4
- {
5
- var that = {
6
- isCancelled: false,
7
- isFinished: false,
8
- getLogs: false
9
- },
10
- cache = {elements: []},
11
- timeout, ajaxSpinner;
12
-
13
- /**
14
- * Get / Set Cache for Selector
15
- * @param {String} selector
16
- * @returns {*}
17
- */
18
- cache.get = function (selector)
19
- {
20
- // It is already cached!
21
- if ($.inArray(selector, cache.elements) !== -1)
22
- {
23
- return cache.elements[selector];
24
- }
25
-
26
- // Create cache and return
27
- cache.elements[selector] = jQuery(selector);
28
-
29
- return cache.elements[selector];
30
- };
31
-
32
- /**
33
- * Refreshes given cache
34
- * @param {String} selector
35
- */
36
- cache.refresh = function (selector)
37
- {
38
- selector.elements[selector] = jQuery(selector);
39
- };
40
-
41
- /**
42
- * Show and Log Error Message
43
- * @param {String} message
44
- */
45
- var showError = function (message)
46
- {
47
- cache.get("#wpstg-try-again").css("display", "inline-block");
48
- cache.get("#wpstg-cancel-cloning").text("Reset");
49
- cache.get("#wpstg-cloning-result").text("Fail");
50
- cache.get("#wpstg-error-wrapper").show();
51
- cache.get("#wpstg-error-details")
52
- .show()
53
- .html(message);
54
- cache.get("#wpstg-removing-clone").removeClass("loading");
55
- cache.get("#wpstg-loader").hide();
56
- };
57
-
58
- /**
59
- * Common Elements
60
- */
61
- var elements = function ()
62
- {
63
- var $workFlow = cache.get("#wpstg-workflow"),
64
- isAllChecked = true,
65
- urlSpinner = ajaxurl.replace("/admin-ajax.php", '') + "/images/spinner",
66
- timer;
67
-
68
- if (2 < window.devicePixelRatio)
69
- {
70
- urlSpinner += "-2x";
71
- }
72
-
73
- urlSpinner += ".gif";
74
-
75
- ajaxSpinner = "<img src=''" + urlSpinner + "' alt='' class='ajax-spinner general-spinner' />";
76
-
77
- $workFlow
78
- // Check / Un-check Database Tables
79
- .on("click", ".wpstg-button-unselect", function (e) {
80
- e.preventDefault();
81
-
82
- if (false === isAllChecked)
83
- {
84
- cache.get(".wpstg-db-table-checkboxes").prop("checked", true);
85
- cache.get(".wpstg-button-unselect").text("Un-check All");
86
- isAllChecked = true;
87
- }
88
- else
89
- {
90
- cache.get(".wpstg-db-table-checkboxes").prop("checked", false);
91
- cache.get(".wpstg-button-unselect").text("Check All");
92
- isAllChecked = false;
93
- }
94
- })
95
- .on("click", ".wpstg-button-select", function (e) {
96
- console.log('test1');
97
-
98
- e.preventDefault();
99
-
100
- $(".wpstg-db-table input").each(function () {
101
- if ($(this).attr('name').match("^" + wpstg.tblprefix)) {
102
- $(this).prop("checked", true);
103
- } else {
104
- $(this).prop("checked", false);
105
-
106
- }
107
- });
108
- })
109
- // Expand Directories
110
- .on("click", ".wpstg-expand-dirs", function (e) {
111
- e.preventDefault();
112
-
113
- var $this = $(this);
114
-
115
- if (!$this.hasClass("disabled"))
116
- {
117
- $this.siblings(".wpstg-subdir").slideToggle();
118
- }
119
- })
120
- // When a Directory is Selected
121
- .on("change", ".wpstg-check-dir", function () {
122
- var $directory = $(this).parent(".wpstg-dir");
123
-
124
- if (this.checked)
125
- {
126
- $directory.parents(".wpstg-dir").children(".wpstg-check-dir").prop("checked", true);
127
- $directory.find(".wpstg-expand-dirs").removeClass("disabled");
128
- $directory.find(".wpstg-subdir .wpstg-check-dir").prop("checked", true);
129
- }
130
- else
131
- {
132
- $directory.find(".wpstg-dir .wpstg-check-dir").prop("checked", false);
133
- $directory.find(".wpstg-expand-dirs, .wpstg-check-subdirs").addClass("disabled");
134
- $directory.find(".wpstg-check-subdirs").data("action", "check").text("check");
135
- $directory.children(".wpstg-subdir").slideUp();
136
- }
137
- })
138
- // Check the max length of the clone name and if the clone name already exists
139
- .on("keyup", "#wpstg-new-clone-id", function () {
140
-
141
- // This request was already sent, clear it up!
142
- if ("number" === typeof (timer))
143
- {
144
- clearInterval(timer);
145
- }
146
-
147
- var cloneID = this.value;
148
-
149
- timer = setTimeout(
150
- function () {
151
- ajax(
152
- {
153
- action: "wpstg_check_clone",
154
- cloneID: cloneID
155
- },
156
- function (response)
157
- {
158
- if (response.status === "success")
159
- {
160
- cache.get("#wpstg-new-clone-id").removeClass("wpstg-error-input");
161
- cache.get("#wpstg-start-cloning").removeAttr("disabled");
162
- cache.get("#wpstg-clone-id-error").text('').hide();
163
- }
164
- else
165
- {
166
- cache.get("#wpstg-new-clone-id").addClass("wpstg-error-input");
167
- cache.get("#wpstg-start-cloning").prop("disabled", true);
168
- cache.get("#wpstg-clone-id-error").text(response.message).show();
169
- }
170
- }
171
- );
172
- },
173
- 500
174
- );
175
- })
176
- // Restart cloning process
177
- .on("click", "#wpstg-start-cloning", function () {
178
- that.isCancelled = false;
179
- that.getLogs = false;
180
- })
181
- // Display logs
182
- // .on("click", "#wpstg-show-log-button", function (e) {
183
- // e.preventDefault();
184
- // var $logDetails = cache.get("#wpstg-log-details");
185
- //
186
- // $logDetails.toggle();
187
- //
188
- // logscroll();
189
- //
190
- // that.getLogs = (false === that.getLogs);
191
- // });
192
-
193
- cloneActions();
194
- };
195
-
196
-
197
- /**
198
- * Clone actions
199
- */
200
- var cloneActions = function ()
201
- {
202
- var $workFlow = cache.get("#wpstg-workflow");
203
-
204
- $workFlow
205
- // Cancel cloning
206
- .on("click", "#wpstg-cancel-cloning", function () {
207
- if (!confirm("Are you sure you want to cancel cloning process?"))
208
- {
209
- return false;
210
- }
211
-
212
- var $this = $(this);
213
-
214
- $("#wpstg-try-again, #wpstg-home-link").hide();
215
- $this.prop("disabled", true);
216
-
217
- that.isCancelled = true;
218
-
219
- $("#wpstg-cloning-result").text("Please wait...this can take up a while.");
220
- $("#wpstg-loader, #wpstg-show-log-button").hide();
221
-
222
- $this.parent().append(ajaxSpinner);
223
-
224
- cancelCloning();
225
- })
226
- // Cancel update cloning
227
- .on("click", "#wpstg-cancel-cloning-update", function () {
228
- if (!confirm("Are you sure you want to cancel clone updating process?"))
229
- {
230
- return false;
231
- }
232
-
233
- var $this = $(this);
234
-
235
- $("#wpstg-try-again, #wpstg-home-link").hide();
236
- $this.prop("disabled", true);
237
-
238
- that.isCancelled = true;
239
-
240
- $("#wpstg-cloning-result").text("Please wait...this can take up a while.");
241
- $("#wpstg-loader, #wpstg-show-log-button").hide();
242
-
243
- $this.parent().append(ajaxSpinner);
244
-
245
- cancelCloningUpdate();
246
- })
247
- // Delete clone - confirmation
248
- .on("click", ".wpstg-remove-clone[data-clone]", function (e) {
249
- e.preventDefault();
250
-
251
- var $existingClones = cache.get("#wpstg-existing-clones");
252
-
253
- $workFlow.removeClass('active');
254
-
255
- cache.get("#wpstg-loader").show();
256
-
257
- ajax(
258
- {
259
- action: "wpstg_confirm_delete_clone",
260
- nonce: wpstg.nonce,
261
- clone: $(this).data("clone")
262
- },
263
- function (response)
264
- {
265
- cache.get("#wpstg-removing-clone").html(response);
266
-
267
- $existingClones.children("img").remove();
268
-
269
- cache.get("#wpstg-loader").hide();
270
- },
271
- "HTML"
272
- );
273
- })
274
- // Delete clone - confirmed
275
- .on("click", "#wpstg-remove-clone", function (e) {
276
- e.preventDefault();
277
-
278
- cache.get("#wpstg-removing-clone").addClass("loading");
279
-
280
- cache.get("#wpstg-loader").show();
281
-
282
- deleteClone($(this).data("clone"));
283
- })
284
- // Cancel deleting clone
285
- .on("click", "#wpstg-cancel-removing", function (e) {
286
- e.preventDefault();
287
- $(".wpstg-clone").removeClass("active");
288
- cache.get("#wpstg-removing-clone").html('');
289
- })
290
- // Update
291
- .on("click", ".wpstg-execute-clone", function (e) {
292
- e.preventDefault();
293
-
294
- if (!confirm("Are you sure you want to update the staging site? All your staging site modifications will be overwritten with the data from the live site. So make sure that your live site is up to date."))
295
- {
296
- return false;
297
- }
298
-
299
- var clone = $(this).data("clone");
300
-
301
- $workFlow.addClass("loading");
302
-
303
- ajax(
304
- {
305
- action: "wpstg_scanning",
306
- clone: clone,
307
- nonce: wpstg.nonce
308
- },
309
- function (response)
310
- {
311
- if (response.length < 1)
312
- {
313
- showError("Something went wrong, please try again");
314
- }
315
-
316
- $workFlow.removeClass("loading").html(response);
317
-
318
- cache.get(".wpstg-current-step")
319
- .removeClass("wpstg-current-step")
320
- .next("li")
321
- .addClass("wpstg-current-step");
322
- },
323
- "HTML"
324
- );
325
- });
326
- };
327
-
328
-
329
- /**
330
- * Ajax Requests
331
- * @param {Object} data
332
- * @param {Function} callback
333
- * @param {String} dataType
334
- * @param {Boolean} showErrors
335
- */
336
- var ajax = function (data, callback, dataType, showErrors)
337
- {
338
- if ("undefined" === typeof (dataType))
339
- {
340
- dataType = "json";
341
- }
342
-
343
- if (false !== showErrors)
344
- {
345
- showErrors = true;
346
- }
347
-
348
- $.ajax({
349
- url: ajaxurl,
350
- type: "POST",
351
- dataType: dataType,
352
- cache: false,
353
- data: data,
354
- error: function (xhr, textStatus, errorThrown) {
355
- console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
356
- console.log(textStatus);
357
-
358
- if (false === showErrors)
359
- {
360
- return false;
361
- }
362
-
363
- showError(
364
- "Fatal Unknown Error. Go to WP Staging > Settings and set 'File Copy Limit' to 1. Also try to lower 'cpu load' there." +
365
- "Than try again. If this does not help, " +
366
- "<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
367
- );
368
- },
369
- success: function (data) {
370
- if ("function" === typeof (callback))
371
- {
372
- callback(data);
373
- }
374
- },
375
- statusCode: {
376
- 404: function () {
377
- showError("Something went wrong; can't find ajax request URL!");
378
- },
379
- 500: function () {
380
- showError("Something went wrong; internal server error while processing the request!");
381
- }
382
- }
383
- });
384
- };
385
-
386
- /**
387
- * Next / Previous Step Clicks to Navigate Through Staging Job
388
- */
389
- var stepButtons = function ()
390
- {
391
- var $workFlow = cache.get("#wpstg-workflow");
392
-
393
- $workFlow
394
- // Next Button
395
- .on("click", ".wpstg-next-step-link", function (e) {
396
- e.preventDefault();
397
-
398
- var $this = $(this),
399
- isScan = false;
400
-
401
- // Button is disabled
402
- if ($this.attr("disabled"))
403
- {
404
- return false;
405
- }
406
-
407
- // Add loading overlay
408
- $workFlow.addClass("loading");
409
-
410
- // Prepare data
411
- that.data = {
412
- action: $this.data("action"),
413
- nonce: wpstg.nonce
414
- };
415
-
416
- // Cloning data
417
- getCloningData();
418
-
419
- console.log(that.data);
420
-
421
- isScan = ("wpstg_scanning" === that.action);
422
-
423
- // Send ajax request
424
- ajax(
425
- that.data,
426
- function (response) {
427
-
428
- if (response.length < 1)
429
- {
430
- showError("Something went wrong, please try again");
431
- }
432
-
433
- // Styling of elements
434
- $workFlow.removeClass("loading").html(response);
435
-
436
- cache.get(".wpstg-current-step")
437
- .removeClass("wpstg-current-step")
438
- .next("li")
439
- .addClass("wpstg-current-step");
440
-
441
- // Start cloning
442
- that.startCloning();
443
- //processing();
444
- },
445
- "HTML"
446
- );
447
- })
448
- // Previous Button
449
- .on("click", ".wpstg-prev-step-link", function (e) {
450
- e.preventDefault();
451
- cache.get("#wpstg-loader").removeClass('wpstg-finished');
452
- cache.get("#wpstg-loader").hide();
453
- loadOverview();
454
- });
455
- };
456
-
457
- /**
458
- * Get Excluded (Unchecked) Database Tables
459
- * @returns {Array}
460
- */
461
- var getExcludedTables = function ()
462
- {
463
- var excludedTables = [];
464
-
465
- $(".wpstg-db-table input:not(:checked)").each(function () {
466
- excludedTables.push(this.name);
467
- });
468
-
469
- return excludedTables;
470
- };
471
-
472
- /**
473
- * Get Included Directories
474
- * @returns {Array}
475
- */
476
- var getIncludedDirectories = function ()
477
- {
478
- var includedDirectories = [];
479
-
480
- $(".wpstg-dir input:checked").each(function () {
481
- var $this = $(this);
482
- if (!$this.parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled"))
483
- {
484
- includedDirectories.push($this.val());
485
- }
486
- });
487
-
488
- return includedDirectories;
489
- };
490
-
491
- /**
492
- * Get Excluded Directories
493
- * @returns {Array}
494
- */
495
- var getExcludedDirectories = function ()
496
- {
497
- var excludedDirectories = [];
498
-
499
- $(".wpstg-dir input:not(:checked)").each(function () {
500
- var $this = $(this);
501
- if (!$this.parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled"))
502
- {
503
- excludedDirectories.push($this.val());
504
- }
505
- });
506
-
507
- return excludedDirectories;
508
- };
509
-
510
- /**
511
- * Get Included Extra Directories
512
- * @returns {Array}
513
- */
514
- var getIncludedExtraDirectories = function ()
515
- {
516
- var extraDirectories = [];
517
-
518
- if (!$("#wpstg_extraDirectories").val()) {
519
- return extraDirectories;
520
- }
521
-
522
- var extraDirectories = $("#wpstg_extraDirectories").val().split(/\r?\n/);
523
- console.log(extraDirectories);
524
-
525
- //excludedDirectories.push($this.val());
526
-
527
- return extraDirectories;
528
- };
529
-
530
-
531
-
532
- /**
533
- * Get Cloning Step Data
534
- */
535
- var getCloningData = function ()
536
- {
537
- if ("wpstg_cloning" !== that.data.action && "wpstg_update" !== that.data.action)
538
- {
539
- return;
540
- }
541
-
542
- that.data.cloneID = $("#wpstg-new-clone-id").val() || new Date().getTime().toString();
543
- that.data.excludedTables = getExcludedTables();
544
- that.data.includedDirectories = getIncludedDirectories();
545
- that.data.excludedDirectories = getExcludedDirectories();
546
- that.data.extraDirectories = getIncludedExtraDirectories();
547
- console.log(that.data);
548
-
549
- };
550
-
551
- /**
552
- * Loads Overview (first step) of Staging Job
553
- */
554
- var loadOverview = function ()
555
- {
556
- var $workFlow = cache.get("#wpstg-workflow");
557
-
558
- $workFlow.addClass("loading");
559
-
560
- ajax(
561
- {
562
- action: "wpstg_overview",
563
- nonce: wpstg.nonce
564
- },
565
- function (response) {
566
-
567
- if (response.length < 1)
568
- {
569
- showError("Something went wrong, please try again");
570
- }
571
-
572
- var $currentStep = cache.get(".wpstg-current-step");
573
-
574
- // Styling of elements
575
- $workFlow.removeClass("loading").html(response);
576
-
577
- },
578
- "HTML"
579
- );
580
- };
581
-
582
- /**
583
- * Load Tabs
584
- */
585
- var tabs = function ()
586
- {
587
- // var $loaded = false;
588
- //
589
- // if ($loaded === false) {
590
- // console.log('select default tables');
591
- //
592
- // $(".wpstg-db-table input").each(function () {
593
- //
594
- // $loaded = true;
595
- //
596
- // if ($(this).attr('name').match("^" + wpstg.tblprefix)) {
597
- // $(this).prop("checked", true);
598
- // } else {
599
- // $(this).prop("checked", false);
600
- // }
601
- // });
602
- // }
603
-
604
- cache.get("#wpstg-workflow").on("click", ".wpstg-tab-header", function (e) {
605
- e.preventDefault();
606
-
607
- var $this = $(this);
608
- var $section = cache.get($this.data("id"));
609
-
610
- $this.toggleClass("expand");
611
-
612
- $section.slideToggle();
613
-
614
- if ($this.hasClass("expand"))
615
- {
616
- $this.find(".wpstg-tab-triangle").html("&#9660;");
617
- }
618
- else
619
- {
620
- $this.find(".wpstg-tab-triangle").html("&#9658;");
621
- }
622
-
623
-
624
-
625
- });
626
- };
627
-
628
- /**
629
- * Delete Clone
630
- * @param {String} clone
631
- */
632
- var deleteClone = function (clone)
633
- {
634
-
635
- ajax(
636
- {
637
- action: "wpstg_delete_clone",
638
- clone: clone,
639
- nonce: wpstg.nonce,
640
- excludedTables: getExcludedTables(),
641
- deleteDir: $("#deleteDirectory:checked").val()
642
- },
643
- function (response)
644
- {
645
- if (response) {
646
- // Error
647
- if ("undefined" !== typeof response.error && "undefined" !== typeof response.message) {
648
- showError(response.message);
649
- console.log(response.message);
650
- }
651
-
652
- // Finished
653
- if ("undefined" !== typeof response.delete && response.delete === 'finished') {
654
-
655
- cache.get("#wpstg-removing-clone").removeClass("loading").html('');
656
- $(".wpstg-clone#" + clone).remove();
657
-
658
- if ($(".wpstg-clone").length < 1)
659
- {
660
- cache.get("#wpstg-existing-clones").find("h3").text('');
661
- }
662
-
663
- cache.get("#wpstg-loader").hide();
664
- return;
665
- }
666
- }
667
- // continue
668
- if (true !== response)
669
- {
670
- deleteClone(clone);
671
- return;
672
- }
673
-
674
- }
675
- );
676
- };
677
-
678
- /**
679
- * Cancel Cloning Process
680
- */
681
- var cancelCloning = function ()
682
- {
683
- if (true === that.isFinished)
684
- {
685
- return true;
686
- }
687
-
688
- ajax(
689
- {
690
- action: "wpstg_cancel_clone",
691
- clone: that.data.cloneID,
692
- nonce: wpstg.nonce
693
- },
694
- function (response)
695
- {
696
-
697
-
698
- if (response && "undefined" !== typeof (response.delete) && response.delete === "finished") {
699
- // Load overview
700
- loadOverview();
701
- return;
702
- }
703
-
704
- if (true !== response)
705
- {
706
- // continue
707
- cancelCloning();
708
- return;
709
- }
710
-
711
- // Load overview
712
- loadOverview();
713
- }
714
- );
715
- };
716
- /**
717
- * Cancel Cloning Process
718
- */
719
- var cancelCloningUpdate = function ()
720
- {
721
- if (true === that.isFinished)
722
- {
723
- return true;
724
- }
725
-
726
- //alert(that.data.cloneID);
727
- ajax(
728
- {
729
- action: "wpstg_cancel_update",
730
- clone: that.data.cloneID,
731
- nonce: wpstg.nonce
732
- },
733
- function (response)
734
- {
735
-
736
-
737
- if (response && "undefined" !== typeof (response.delete) && response.delete === "finished") {
738
- // Load overview
739
- loadOverview();
740
- return;
741
- }
742
-
743
- if (true !== response)
744
- {
745
- // continue
746
- cancelCloningUpdate();
747
- return;
748
- }
749
-
750
- // Load overview
751
- loadOverview();
752
- }
753
- );
754
- };
755
-
756
- /**
757
- * Scroll the window log to bottom
758
- * @returns void
759
- */
760
- var logscroll = function () {
761
- var $div = cache.get("#wpstg-log-details");
762
- if ("undefined" !== typeof ($div[0])) {
763
- $div.scrollTop($div[0].scrollHeight);
764
- }
765
- }
766
-
767
- /**
768
- * Append the log to the logging window
769
- * @param string log
770
- * @returns void
771
- */
772
- var getLogs = function (log)
773
- {
774
- if (log != null && "undefined" !== typeof (log)) {
775
- if (log.constructor === Array) {
776
- $.each(log, function (index, value) {
777
- if (value === null) {
778
- return;
779
- }
780
- if (value.type === 'ERROR') {
781
- cache.get("#wpstg-log-details").append('<span style="color:red;">[' + value.type + ']</span>-' + '[' + value.date + '] ' + value.message + '</br>');
782
- } else {
783
- cache.get("#wpstg-log-details").append('[' + value.type + ']-' + '[' + value.date + '] ' + value.message + '</br>');
784
- }
785
- })
786
- } else {
787
- cache.get("#wpstg-log-details").append('[' + log.type + ']-' + '[' + log.date + '] ' + log.message + '</br>');
788
- }
789
- }
790
- logscroll();
791
-
792
- };
793
-
794
- /**
795
- * Check diskspace
796
- * @returns string json
797
- */
798
- var checkDiskSpace = function () {
799
- cache.get("#wpstg-check-space").on("click", function (e) {
800
- cache.get("#wpstg-loader").show();
801
- console.log("check disk space");
802
- ajax(
803
- {
804
- action: "wpstg_check_disk_space",
805
- nonce: wpstg.nonce
806
- },
807
- function (response)
808
- {
809
- if (false === response)
810
- {
811
- cache.get("#wpstg-clone-id-error").text('Can not detect disk space').show();
812
- cache.get("#wpstg-loader").hide();
813
- return;
814
- }
815
-
816
- // Not enough disk space
817
- cache.get("#wpstg-clone-id-error").text('Available free disk space ' + response.freespace + ' | Estimated necessary disk space: ' + response.usedspace).show();
818
- cache.get("#wpstg-loader").hide();
819
- },
820
- "json",
821
- false
822
- );
823
- });
824
-
825
- }
826
-
827
- /**
828
- * Fire the update cloning process
829
- * @returns {undefined}
830
- */
831
- // var startUpdate = function (){
832
- // var $workFlow = cache.get("#wpstg-workflow");
833
- // $workFlow.on("click", "#wpstg-start-updating", function (e) {
834
- // e.preventDefault();
835
- //
836
- // var cloneID = $("#wpstg-new-clone-id").val();
837
- // updateStruc(cloneID);
838
- // });
839
- // };
840
- //
841
- // var updateStruc = function (cloneID){
842
- // ajax(
843
- // {
844
- // action: "wpstg_update_struc",
845
- // nonce: wpstg.nonce,
846
- // cloneID: cloneID,
847
- // excludedTables: getExcludedTables(),
848
- // includedDirectories: getIncludedDirectories(),
849
- // excludedDirectories: getExcludedDirectories(),
850
- // extraDirectories: getIncludedExtraDirectories()
851
- // },
852
- // function (response) {
853
- // //console.log(response);
854
- // updating(cloneID);
855
- // },
856
- // "HTML",
857
- // false
858
- // );
859
- // }
860
-
861
- /**
862
- * Start ajax updating process
863
- * @returns string
864
- */
865
- // var updating = function (cloneID) {
866
- //
867
- // console.log("Start updating");
868
- //
869
- // // Show loader gif
870
- // cache.get("#wpstg-loader").show();
871
- // cache.get(".wpstg-loader").show();
872
- //
873
- // ajax(
874
- // {
875
- // action: "wpstg_update",
876
- // nonce: wpstg.nonce,
877
- // cloneID: cloneID,
878
- // excludedTables: getExcludedTables(),
879
- // includedDirectories: getIncludedDirectories(),
880
- // excludedDirectories: getExcludedDirectories(),
881
- // extraDirectories: getIncludedExtraDirectories()
882
- // },
883
- // function (response) {
884
- //
885
- // // Throw Error
886
- // if ("undefined" !== typeof(response.error) && response.error){
887
- // console.log(response.message);
888
- // showError(response.message);
889
- // return;
890
- // }
891
- //
892
- // // Add percentage
893
- // if ("undefined" !== typeof (response.percentage))
894
- // {
895
- // cache.get("#wpstg-db-progress").width(response.percentage + '%');
896
- // }
897
- // // Add Log
898
- // if ("undefined" !== typeof (response.last_msg))
899
- // {
900
- // getLogs(response.last_msg);
901
- // }
902
- //
903
- // // Continue clone DB
904
- // if (false === response.status)
905
- // {
906
- // setTimeout(function () {
907
- // updating(cloneID);
908
- // }, wpstg.cpuLoad);
909
- // }
910
- // // Next Step
911
- // else if (true === response.status)
912
- // {
913
- // console.log('startCloning ' + response.status);
914
- // setTimeout(function () {
915
- // // Prepare data
916
- // that.data = {
917
- // action: 'wpstg_cloning',
918
- // nonce: wpstg.nonce
919
- // };
920
- // that.startCloning();
921
- // }, wpstg.cpuLoad);
922
- // }
923
- // },
924
- // "json",
925
- // false
926
- // );
927
- // };
928
-
929
- /**
930
- * Start Cloning Process
931
- * @type {Function}
932
- */
933
- that.startCloning = (function () {
934
-
935
- // Register function for checking disk space
936
- checkDiskSpace();
937
-
938
- if ("wpstg_cloning" !== that.data.action && "wpstg_update" !== that.data.action)
939
- {
940
- return;
941
- }
942
-
943
- // Start the process
944
- start();
945
-
946
- // Functions
947
- // Start
948
- function start()
949
- {
950
- console.log("Starting cloning process...");
951
-
952
- cache.get("#wpstg-loader").show();
953
-
954
- // Clone Database
955
- setTimeout(function () {
956
- cloneDatabase();
957
- }, wpstg.cpuLoad);
958
- }
959
-
960
- // Step 1: Clone Database
961
- function cloneDatabase()
962
- {
963
- if (true === that.isCancelled)
964
- {
965
- return false;
966
- }
967
-
968
- if (true === that.getLogs)
969
- {
970
- getLogs();
971
- }
972
-
973
- setTimeout(
974
- function () {
975
- ajax(
976
- {
977
- action: "wpstg_clone_database",
978
- nonce: wpstg.nonce
979
- },
980
- function (response) {
981
- // Add percentage
982
- if ("undefined" !== typeof (response.percentage))
983
- {
984
- cache.get("#wpstg-db-progress").width(response.percentage + '%');
985
- }
986
- // Add Log
987
- if ("undefined" !== typeof (response.last_msg))
988
- {
989
- getLogs(response.last_msg);
990
- }
991
-
992
- // Continue clone DB
993
- if (false === response.status)
994
- {
995
- setTimeout(function () {
996
- cloneDatabase();
997
- }, wpstg.cpuLoad);
998
- }
999
- // Next Step
1000
- else if (true === response.status)
1001
- {
1002
- //console.log('prepareDirectories ' + response.status);
1003
- setTimeout(function () {
1004
- prepareDirectories();
1005
- }, wpstg.cpuLoad);
1006
- }
1007
- }
1008
- );
1009
- },
1010
- 500
1011
- );
1012
- }
1013
-
1014
- // Step 2: Prepare Directories
1015
- function prepareDirectories()
1016
- {
1017
- if (true === that.isCancelled)
1018
- {
1019
- return false;
1020
- }
1021
-
1022
- if (true === that.getLogs)
1023
- {
1024
- getLogs();
1025
- }
1026
-
1027
- setTimeout(
1028
- function () {
1029
- ajax(
1030
- {
1031
- action: "wpstg_clone_prepare_directories",
1032
- nonce: wpstg.nonce
1033
- },
1034
- function (response) {
1035
-
1036
- // Error
1037
- if ("undefined" !== typeof response.error && "undefined" !== typeof response.message) {
1038
- showError(response.message);
1039
- console.log(response.message);
1040
- }
1041
-
1042
- // Add percentage
1043
- if ("undefined" !== typeof (response.percentage))
1044
- {
1045
- cache.get("#wpstg-directories-progress").width(response.percentage + '%');
1046
- }
1047
-
1048
- // Add Log
1049
- if ("undefined" !== typeof (response.last_msg))
1050
- {
1051
- getLogs(response.last_msg);
1052
- }
1053
-
1054
- if (false === response.status)
1055
- {
1056
- setTimeout(function () {
1057
- prepareDirectories();
1058
- }, wpstg.cpuLoad);
1059
- }
1060
- else if (true === response.status)
1061
- {
1062
- console.log('prepareDirectories' + response.status);
1063
- cloneFiles();
1064
- }
1065
- }
1066
- );
1067
- },
1068
- 500
1069
- );
1070
- }
1071
-
1072
- // Step 3: Clone Files
1073
- function cloneFiles()
1074
- {
1075
- if (true === that.isCancelled)
1076
- {
1077
- return false;
1078
- }
1079
-
1080
- if (true === that.getLogs)
1081
- {
1082
- getLogs();
1083
- }
1084
-
1085
- ajax(
1086
- {
1087
- action: "wpstg_clone_files",
1088
- nonce: wpstg.nonce
1089
- },
1090
- function (response) {
1091
- // Add percentage
1092
- if ("undefined" !== typeof (response.percentage))
1093
- {
1094
- cache.get("#wpstg-files-progress").width(response.percentage + '%');
1095
- }
1096
-
1097
- // Add Log
1098
- if ("undefined" !== typeof (response.last_msg))
1099
- {
1100
- getLogs(response.last_msg);
1101
- }
1102
-
1103
- if (false === response.status)
1104
- {
1105
- setTimeout(function () {
1106
- cloneFiles();
1107
- }, wpstg.cpuLoad);
1108
- }
1109
- else if (true === response.status)
1110
- {
1111
- setTimeout(function () {
1112
- replaceData();
1113
- }, wpstg.cpuLoad);
1114
- }
1115
- }
1116
- );
1117
- }
1118
-
1119
- // Step 4: Replace Data
1120
- function replaceData()
1121
- {
1122
- if (true === that.isCancelled)
1123
- {
1124
- return false;
1125
- }
1126
-
1127
- if (true === that.getLogs)
1128
- {
1129
- console.log('getLogs1')
1130
- getLogs();
1131
- }
1132
-
1133
- ajax(
1134
- {
1135
- action: "wpstg_clone_replace_data",
1136
- nonce: wpstg.nonce
1137
- },
1138
- function (response) {
1139
- // Add percentage
1140
- if ("undefined" !== typeof (response.percentage))
1141
- {
1142
- cache.get("#wpstg-links-progress").width(response.percentage + '%');
1143
- }
1144
-
1145
- // Add Log
1146
- if ("undefined" !== typeof (response.last_msg))
1147
- {
1148
- console.log('get Logs');
1149
- getLogs(response.last_msg);
1150
- }
1151
-
1152
- if (false === response.status)
1153
- {
1154
- setTimeout(function () {
1155
- console.log('replace data');
1156
- replaceData();
1157
- }, wpstg.cpuLoad);
1158
- }
1159
- else if (true === response.status)
1160
- {
1161
- console.log('finish');
1162
- finish();
1163
- }
1164
- }
1165
- );
1166
- }
1167
-
1168
- // Finish
1169
- function finish()
1170
- {
1171
- if (true === that.getLogs)
1172
- {
1173
- getLogs();
1174
- }
1175
-
1176
- if (true === that.isCancelled || true === that.isFinished)
1177
- {
1178
- cache.get("#wpstg-loader").hide();
1179
- return false;
1180
- }
1181
-
1182
- ajax(
1183
- {
1184
- action: "wpstg_clone_finish",
1185
- nonce: wpstg.nonce
1186
- },
1187
- function (response)
1188
- {
1189
- // Invalid response
1190
- if ("object" !== typeof (response))
1191
- {
1192
- showError(
1193
- "Couldn't finish the cloning process properly. " +
1194
- "Your clone has been copied but failed to do clean up and " +
1195
- "saving its records to the database." +
1196
- "Please contact support and provide your logs."
1197
- );
1198
-
1199
- return;
1200
- }
1201
-
1202
- // Add Log
1203
- if ("undefined" !== typeof (response.last_msg))
1204
- {
1205
- getLogs(response.last_msg);
1206
- }
1207
-
1208
- console.log("Cloning process finished");
1209
-
1210
- var $link1 = cache.get("#wpstg-clone-url-1");
1211
- var $link = cache.get("#wpstg-clone-url");
1212
-
1213
- cache.get("#wpstg_staging_name").html(that.data.cloneID);
1214
- cache.get("#wpstg-finished-result").show();
1215
- cache.get("#wpstg-cancel-cloning").prop("disabled", true);
1216
- cache.get("#wpstg-cancel-cloning-update").prop("disabled", true);
1217
- // $link1.attr("href", $link1.attr("href") + '/' + response.directoryName);
1218
- // $link1.append('/' + response.directoryName);
1219
- // $link.attr("href", $link.attr("href") + '/' + response.directoryName);
1220
- $link1.attr("href", response.url);
1221
- $link.attr("href", response.url);
1222
- cache.get("#wpstg-remove-clone").data("clone", that.data.cloneID);
1223
-
1224
- // Finished
1225
- that.isFinished = true;
1226
-
1227
- finish();
1228
- }
1229
- );
1230
- }
1231
- });
1232
-
1233
-
1234
- /**
1235
- * Initiation
1236
- * @type {Function}
1237
- */
1238
- that.init = (function () {
1239
- loadOverview();
1240
- elements();
1241
- //startUpdate();
1242
- stepButtons();
1243
- tabs();
1244
- //optimizer();
1245
- });
1246
-
1247
- /**
1248
- * Ajax call
1249
- * @type {ajax}
1250
- */
1251
- that.ajax = ajax;
1252
- that.showError = showError;
1253
- that.getLogs = getLogs;
1254
- that.loadOverview = loadOverview;
1255
-
1256
- return that;
1257
- })(jQuery);
1258
-
1259
- jQuery(document).ready(function () {
1260
- WPStaging.init();
1261
  });
1
+ "use strict";
2
+
3
+ var WPStaging = (function ($)
4
+ {
5
+ var that = {
6
+ isCancelled: false,
7
+ isFinished: false,
8
+ getLogs: false
9
+ },
10
+ cache = {elements: []},
11
+ timeout, ajaxSpinner;
12
+
13
+ /**
14
+ * Get / Set Cache for Selector
15
+ * @param {String} selector
16
+ * @returns {*}
17
+ */
18
+ cache.get = function (selector)
19
+ {
20
+ // It is already cached!
21
+ if ($.inArray(selector, cache.elements) !== -1)
22
+ {
23
+ return cache.elements[selector];
24
+ }
25
+
26
+ // Create cache and return
27
+ cache.elements[selector] = jQuery(selector);
28
+
29
+ return cache.elements[selector];
30
+ };
31
+
32
+ /**
33
+ * Refreshes given cache
34
+ * @param {String} selector
35
+ */
36
+ cache.refresh = function (selector)
37
+ {
38
+ selector.elements[selector] = jQuery(selector);
39
+ };
40
+
41
+ /**
42
+ * Show and Log Error Message
43
+ * @param {String} message
44
+ */
45
+ var showError = function (message)
46
+ {
47
+ cache.get("#wpstg-try-again").css("display", "inline-block");
48
+ cache.get("#wpstg-cancel-cloning").text("Reset");
49
+ cache.get("#wpstg-cloning-result").text("Fail");
50
+ cache.get("#wpstg-error-wrapper").show();
51
+ cache.get("#wpstg-error-details")
52
+ .show()
53
+ .html(message);
54
+ cache.get("#wpstg-removing-clone").removeClass("loading");
55
+ cache.get("#wpstg-loader").hide();
56
+ };
57
+
58
+ /**
59
+ * Common Elements
60
+ */
61
+ var elements = function ()
62
+ {
63
+ var $workFlow = cache.get("#wpstg-workflow"),
64
+ isAllChecked = true,
65
+ urlSpinner = ajaxurl.replace("/admin-ajax.php", '') + "/images/spinner",
66
+ timer;
67
+
68
+ if (2 < window.devicePixelRatio)
69
+ {
70
+ urlSpinner += "-2x";
71
+ }
72
+
73
+ urlSpinner += ".gif";
74
+
75
+ ajaxSpinner = "<img src=''" + urlSpinner + "' alt='' class='ajax-spinner general-spinner' />";
76
+
77
+ $workFlow
78
+ // Check / Un-check Database Tables
79
+ .on("click", ".wpstg-button-unselect", function (e) {
80
+ e.preventDefault();
81
+
82
+ if (false === isAllChecked)
83
+ {
84
+ cache.get(".wpstg-db-table-checkboxes").prop("checked", true);
85
+ cache.get(".wpstg-button-unselect").text("Un-check All");
86
+ isAllChecked = true;
87
+ }
88
+ else
89
+ {
90
+ cache.get(".wpstg-db-table-checkboxes").prop("checked", false);
91
+ cache.get(".wpstg-button-unselect").text("Check All");
92
+ isAllChecked = false;
93
+ }
94
+ })
95
+ .on("click", ".wpstg-button-select", function (e) {
96
+ console.log('test1');
97
+
98
+ e.preventDefault();
99
+
100
+ $(".wpstg-db-table input").each(function () {
101
+ if ($(this).attr('name').match("^" + wpstg.tblprefix)) {
102
+ $(this).prop("checked", true);
103
+ } else {
104
+ $(this).prop("checked", false);
105
+
106
+ }
107
+ });
108
+ })
109
+ // Expand Directories
110
+ .on("click", ".wpstg-expand-dirs", function (e) {
111
+ e.preventDefault();
112
+
113
+ var $this = $(this);
114
+
115
+ if (!$this.hasClass("disabled"))
116
+ {
117
+ $this.siblings(".wpstg-subdir").slideToggle();
118
+ }
119
+ })
120
+ // When a Directory is Selected
121
+ .on("change", ".wpstg-check-dir", function () {
122
+ var $directory = $(this).parent(".wpstg-dir");
123
+
124
+ if (this.checked)
125
+ {
126
+ $directory.parents(".wpstg-dir").children(".wpstg-check-dir").prop("checked", true);
127
+ $directory.find(".wpstg-expand-dirs").removeClass("disabled");
128
+ $directory.find(".wpstg-subdir .wpstg-check-dir").prop("checked", true);
129
+ }
130
+ else
131
+ {
132
+ $directory.find(".wpstg-dir .wpstg-check-dir").prop("checked", false);
133
+ $directory.find(".wpstg-expand-dirs, .wpstg-check-subdirs").addClass("disabled");
134
+ $directory.find(".wpstg-check-subdirs").data("action", "check").text("check");
135
+ $directory.children(".wpstg-subdir").slideUp();
136
+ }
137
+ })
138
+ // Check the max length of the clone name and if the clone name already exists
139
+ .on("keyup", "#wpstg-new-clone-id", function () {
140
+
141
+ // This request was already sent, clear it up!
142
+ if ("number" === typeof (timer))
143
+ {
144
+ clearInterval(timer);
145
+ }
146
+
147
+ var cloneID = this.value;
148
+
149
+ timer = setTimeout(
150
+ function () {
151
+ ajax(
152
+ {
153
+ action: "wpstg_check_clone",
154
+ cloneID: cloneID
155
+ },
156
+ function (response)
157
+ {
158
+ if (response.status === "success")
159
+ {
160
+ cache.get("#wpstg-new-clone-id").removeClass("wpstg-error-input");
161
+ cache.get("#wpstg-start-cloning").removeAttr("disabled");
162
+ cache.get("#wpstg-clone-id-error").text('').hide();
163
+ }
164
+ else
165
+ {
166
+ cache.get("#wpstg-new-clone-id").addClass("wpstg-error-input");
167
+ cache.get("#wpstg-start-cloning").prop("disabled", true);
168
+ cache.get("#wpstg-clone-id-error").text(response.message).show();
169
+ }
170
+ }
171
+ );
172
+ },
173
+ 500
174
+ );
175
+ })
176
+ // Restart cloning process
177
+ .on("click", "#wpstg-start-cloning", function () {
178
+ that.isCancelled = false;
179
+ that.getLogs = false;
180
+ })
181
+ // Display logs
182
+ // .on("click", "#wpstg-show-log-button", function (e) {
183
+ // e.preventDefault();
184
+ // var $logDetails = cache.get("#wpstg-log-details");
185
+ //
186
+ // $logDetails.toggle();
187
+ //
188
+ // logscroll();
189
+ //
190
+ // that.getLogs = (false === that.getLogs);
191
+ // });
192
+
193
+ cloneActions();
194
+ };
195
+
196
+
197
+ /**
198
+ * Clone actions
199
+ */
200
+ var cloneActions = function ()
201
+ {
202
+ var $workFlow = cache.get("#wpstg-workflow");
203
+
204
+ $workFlow
205
+ // Cancel cloning
206
+ .on("click", "#wpstg-cancel-cloning", function () {
207
+ if (!confirm("Are you sure you want to cancel cloning process?"))
208
+ {
209
+ return false;
210
+ }
211
+
212
+ var $this = $(this);
213
+
214
+ $("#wpstg-try-again, #wpstg-home-link").hide();
215
+ $this.prop("disabled", true);
216
+
217
+ that.isCancelled = true;
218
+
219
+ $("#wpstg-cloning-result").text("Please wait...this can take up a while.");
220
+ $("#wpstg-loader, #wpstg-show-log-button").hide();
221
+
222
+ $this.parent().append(ajaxSpinner);
223
+
224
+ cancelCloning();
225
+ })
226
+ // Cancel update cloning
227
+ .on("click", "#wpstg-cancel-cloning-update", function () {
228
+ if (!confirm("Are you sure you want to cancel clone updating process?"))
229
+ {
230
+ return false;
231
+ }
232
+
233
+ var $this = $(this);
234
+
235
+ $("#wpstg-try-again, #wpstg-home-link").hide();
236
+ $this.prop("disabled", true);
237
+
238
+ that.isCancelled = true;
239
+
240
+ $("#wpstg-cloning-result").text("Please wait...this can take up a while.");
241
+ $("#wpstg-loader, #wpstg-show-log-button").hide();
242
+
243
+ $this.parent().append(ajaxSpinner);
244
+
245
+ cancelCloningUpdate();
246
+ })
247
+ // Delete clone - confirmation
248
+ .on("click", ".wpstg-remove-clone[data-clone]", function (e) {
249
+ e.preventDefault();
250
+
251
+ var $existingClones = cache.get("#wpstg-existing-clones");
252
+
253
+ $workFlow.removeClass('active');
254
+
255
+ cache.get("#wpstg-loader").show();
256
+
257
+ ajax(
258
+ {
259
+ action: "wpstg_confirm_delete_clone",
260
+ nonce: wpstg.nonce,
261
+ clone: $(this).data("clone")
262
+ },
263
+ function (response)
264
+ {
265
+ cache.get("#wpstg-removing-clone").html(response);
266
+
267
+ $existingClones.children("img").remove();
268
+
269
+ cache.get("#wpstg-loader").hide();
270
+ },
271
+ "HTML"
272
+ );
273
+ })
274
+ // Delete clone - confirmed
275
+ .on("click", "#wpstg-remove-clone", function (e) {
276
+ e.preventDefault();
277
+
278
+ cache.get("#wpstg-removing-clone").addClass("loading");
279
+
280
+ cache.get("#wpstg-loader").show();
281
+
282
+ deleteClone($(this).data("clone"));
283
+ })
284
+ // Cancel deleting clone
285
+ .on("click", "#wpstg-cancel-removing", function (e) {
286
+ e.preventDefault();
287
+ $(".wpstg-clone").removeClass("active");
288
+ cache.get("#wpstg-removing-clone").html('');
289
+ })
290
+ // Update
291
+ .on("click", ".wpstg-execute-clone", function (e) {
292
+ e.preventDefault();
293
+
294
+ if (!confirm("Are you sure you want to update the staging site? All your staging site modifications will be overwritten with the data from the live site. So make sure that your live site is up to date."))
295
+ {
296
+ return false;
297
+ }
298
+
299
+ var clone = $(this).data("clone");
300
+
301
+ $workFlow.addClass("loading");
302
+
303
+ ajax(
304
+ {
305
+ action: "wpstg_scanning",
306
+ clone: clone,
307
+ nonce: wpstg.nonce
308
+ },
309
+ function (response)
310
+ {
311
+ if (response.length < 1)
312
+ {
313
+ showError("Something went wrong, please try again");
314
+ }
315
+
316
+ $workFlow.removeClass("loading").html(response);
317
+
318
+ cache.get(".wpstg-current-step")
319
+ .removeClass("wpstg-current-step")
320
+ .next("li")
321
+ .addClass("wpstg-current-step");
322
+ },
323
+ "HTML"
324
+ );
325
+ });
326
+ };
327
+
328
+
329
+ /**
330
+ * Ajax Requests
331
+ * @param {Object} data
332
+ * @param {Function} callback
333
+ * @param {String} dataType
334
+ * @param {Boolean} showErrors
335
+ */
336
+ var ajax = function (data, callback, dataType, showErrors)
337
+ {
338
+ if ("undefined" === typeof (dataType))
339
+ {
340
+ dataType = "json";
341
+ }
342
+
343
+ if (false !== showErrors)
344
+ {
345
+ showErrors = true;
346
+ }
347
+
348
+ $.ajax({
349
+ url: ajaxurl,
350
+ type: "POST",
351
+ dataType: dataType,
352
+ cache: false,
353
+ data: data,
354
+ error: function (xhr, textStatus, errorThrown) {
355
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
356
+ console.log(textStatus);
357
+
358
+ if (false === showErrors)
359
+ {
360
+ return false;
361
+ }
362
+
363
+
364
+ showError(
365
+ "Fatal Unknown Error. Go to WP Staging > Settings and lower 'File Copy Limit' and DB query limit" +
366
+ "Than try again. If this does not help, " +
367
+ "<a href='https://wpquads.com/support/' target='_blank'>open a support ticket</a> "
368
+ );
369
+ },
370
+ success: function (data) {
371
+ if ("function" === typeof (callback))
372
+ {
373
+ callback(data);
374
+ }
375
+ },
376
+ statusCode: {
377
+ 404: function (data) {
378
+ showError("Something went wrong; can't find ajax request URL!");
379
+ },
380
+ 500: function () {
381
+ showError("Something went wrong; internal server error while processing the request!");
382
+ }
383
+ }
384
+ });
385
+ };
386
+
387
+ /**
388
+ * Next / Previous Step Clicks to Navigate Through Staging Job
389
+ */
390
+ var stepButtons = function ()
391
+ {
392
+ var $workFlow = cache.get("#wpstg-workflow");
393
+
394
+ $workFlow
395
+ // Next Button
396
+ .on("click", ".wpstg-next-step-link", function (e) {
397
+ e.preventDefault();
398
+
399
+ var $this = $(this),
400
+ isScan = false;
401
+
402
+ // Button is disabled
403
+ if ($this.attr("disabled"))
404
+ {
405
+ return false;
406
+ }
407
+
408
+ // Add loading overlay
409
+ $workFlow.addClass("loading");
410
+
411
+ // Prepare data
412
+ that.data = {
413
+ action: $this.data("action"),
414
+ nonce: wpstg.nonce
415
+ };
416
+
417
+ // Cloning data
418
+ getCloningData();
419
+
420
+ console.log(that.data);
421
+
422
+ isScan = ("wpstg_scanning" === that.action);
423
+
424
+ // Send ajax request
425
+ ajax(
426
+ that.data,
427
+ function (response) {
428
+
429
+ if (response.length < 1)
430
+ {
431
+ showError("Something went wrong, please try again");
432
+ }
433
+
434
+ // Styling of elements
435
+ $workFlow.removeClass("loading").html(response);
436
+
437
+ cache.get(".wpstg-current-step")
438
+ .removeClass("wpstg-current-step")
439
+ .next("li")
440
+ .addClass("wpstg-current-step");
441
+
442
+ // Start cloning
443
+ that.startCloning();
444
+ //processing();
445
+ },
446
+ "HTML"
447
+ );
448
+ })
449
+ // Previous Button
450
+ .on("click", ".wpstg-prev-step-link", function (e) {
451
+ e.preventDefault();
452
+ cache.get("#wpstg-loader").removeClass('wpstg-finished');
453
+ cache.get("#wpstg-loader").hide();
454
+ loadOverview();
455
+ });
456
+ };
457
+
458
+ /**
459
+ * Get Included (Checked) Database Tables
460
+ * @returns {Array}
461
+ */
462
+ var getIncludedTables = function ()
463
+ {
464
+ var includedTables = [];
465
+
466
+ $(".wpstg-db-table input:checked").each(function () {
467
+ includedTables.push(this.name);
468
+ });
469
+
470
+ return includedTables;
471
+ };
472
+ /**
473
+ * Get Excluded (Unchecked) Database Tables
474
+ * @returns {Array}
475
+ */
476
+ var getExcludedTables = function ()
477
+ {
478
+ var excludedTables = [];
479
+
480
+ $(".wpstg-db-table input:not(:checked)").each(function () {
481
+ excludedTables.push(this.name);
482
+ });
483
+
484
+ return excludedTables;
485
+ };
486
+
487
+ /**
488
+ * Get Included Directories
489
+ * @returns {Array}
490
+ */
491
+ var getIncludedDirectories = function ()
492
+ {
493
+ var includedDirectories = [];
494
+
495
+ $(".wpstg-dir input:checked").each(function () {
496
+ var $this = $(this);
497
+ if (!$this.parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled"))
498
+ {
499
+ includedDirectories.push($this.val());
500
+ }
501
+ });
502
+
503
+ return includedDirectories;
504
+ };
505
+
506
+ /**
507
+ * Get Excluded Directories
508
+ * @returns {Array}
509
+ */
510
+ var getExcludedDirectories = function ()
511
+ {
512
+ var excludedDirectories = [];
513
+
514
+ $(".wpstg-dir input:not(:checked)").each(function () {
515
+ var $this = $(this);
516
+ if (!$this.parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled"))
517
+ {
518
+ excludedDirectories.push($this.val());
519
+ }
520
+ });
521
+
522
+ return excludedDirectories;
523
+ };
524
+
525
+ /**
526
+ * Get Included Extra Directories
527
+ * @returns {Array}
528
+ */
529
+ var getIncludedExtraDirectories = function ()
530
+ {
531
+ var extraDirectories = [];
532
+
533
+ if (!$("#wpstg_extraDirectories").val()) {
534
+ return extraDirectories;
535
+ }
536
+
537
+ var extraDirectories = $("#wpstg_extraDirectories").val().split(/\r?\n/);
538
+ console.log(extraDirectories);
539
+
540
+ //excludedDirectories.push($this.val());
541
+
542
+ return extraDirectories;
543
+ };
544
+
545
+
546
+
547
+ /**
548
+ * Get Cloning Step Data
549
+ */
550
+ var getCloningData = function ()
551
+ {
552
+ if ("wpstg_cloning" !== that.data.action && "wpstg_update" !== that.data.action)
553
+ {
554
+ return;
555
+ }
556
+
557
+ that.data.cloneID = $("#wpstg-new-clone-id").val() || new Date().getTime().toString();
558
+ // Remove this to keep &_POST[] small otherwise mod_security will throw error 404
559
+ //that.data.excludedTables = getExcludedTables();
560
+ that.data.includedTables = getIncludedTables();
561
+ that.data.includedDirectories = getIncludedDirectories();
562
+ that.data.excludedDirectories = getExcludedDirectories();
563
+ that.data.extraDirectories = getIncludedExtraDirectories();
564
+ console.log(that.data);
565
+
566
+ };
567
+
568
+ /**
569
+ * Loads Overview (first step) of Staging Job
570
+ */
571
+ var loadOverview = function ()
572
+ {
573
+ var $workFlow = cache.get("#wpstg-workflow");
574
+
575
+ $workFlow.addClass("loading");
576
+
577
+ ajax(
578
+ {
579
+ action: "wpstg_overview",
580
+ nonce: wpstg.nonce
581
+ },
582
+ function (response) {
583
+
584
+ if (response.length < 1)
585
+ {
586
+ showError("Something went wrong, please try again");
587
+ }
588
+
589
+ var $currentStep = cache.get(".wpstg-current-step");
590
+
591
+ // Styling of elements
592
+ $workFlow.removeClass("loading").html(response);
593
+
594
+ },
595
+ "HTML"
596
+ );
597
+ };
598
+
599
+ /**
600
+ * Load Tabs
601
+ */
602
+ var tabs = function ()
603
+ {
604
+ // var $loaded = false;
605
+ //
606
+ // if ($loaded === false) {
607
+ // console.log('select default tables');
608
+ //
609
+ // $(".wpstg-db-table input").each(function () {
610
+ //
611
+ // $loaded = true;
612
+ //
613
+ // if ($(this).attr('name').match("^" + wpstg.tblprefix)) {
614
+ // $(this).prop("checked", true);
615
+ // } else {
616
+ // $(this).prop("checked", false);
617
+ // }
618
+ // });
619
+ // }
620
+
621
+ cache.get("#wpstg-workflow").on("click", ".wpstg-tab-header", function (e) {
622
+ e.preventDefault();
623
+
624
+ var $this = $(this);
625
+ var $section = cache.get($this.data("id"));
626
+
627
+ $this.toggleClass("expand");
628
+
629
+ $section.slideToggle();
630
+
631
+ if ($this.hasClass("expand"))
632
+ {
633
+ $this.find(".wpstg-tab-triangle").html("&#9660;");
634
+ }
635
+ else
636
+ {
637
+ $this.find(".wpstg-tab-triangle").html("&#9658;");
638
+ }
639
+
640
+
641
+
642
+ });
643
+ };
644
+
645
+ /**
646
+ * Delete Clone
647
+ * @param {String} clone
648
+ */
649
+ var deleteClone = function (clone)
650
+ {
651
+
652
+ ajax(
653
+ {
654
+ action: "wpstg_delete_clone",
655
+ clone: clone,
656
+ nonce: wpstg.nonce,
657
+ excludedTables: getExcludedTables(),
658
+ deleteDir: $("#deleteDirectory:checked").val()
659
+ },
660
+ function (response)
661
+ {
662
+ if (response) {
663
+ // Error
664
+ if ("undefined" !== typeof response.error && "undefined" !== typeof response.message) {
665
+ showError(response.message);
666
+ console.log(response.message);
667
+ }
668
+
669
+ // Finished
670
+ if ("undefined" !== typeof response.delete && response.delete === 'finished') {
671
+
672
+ cache.get("#wpstg-removing-clone").removeClass("loading").html('');
673
+ $(".wpstg-clone#" + clone).remove();
674
+
675
+ if ($(".wpstg-clone").length < 1)
676
+ {
677
+ cache.get("#wpstg-existing-clones").find("h3").text('');
678
+ }
679
+
680
+ cache.get("#wpstg-loader").hide();
681
+ return;
682
+ }
683
+ }
684
+ // continue
685
+ if (true !== response)
686
+ {
687
+ deleteClone(clone);
688
+ return;
689
+ }
690
+
691
+ }
692
+ );
693
+ };
694
+
695
+ /**
696
+ * Cancel Cloning Process
697
+ */
698
+ var cancelCloning = function ()
699
+ {
700
+ if (true === that.isFinished)
701
+ {
702
+ return true;
703
+ }
704
+
705
+ ajax(
706
+ {
707
+ action: "wpstg_cancel_clone",
708
+ clone: that.data.cloneID,
709
+ nonce: wpstg.nonce
710
+ },
711
+ function (response)
712
+ {
713
+
714
+
715
+ if (response && "undefined" !== typeof (response.delete) && response.delete === "finished") {
716
+ // Load overview
717
+ loadOverview();
718
+ return;
719
+ }
720
+
721
+ if (true !== response)
722
+ {
723
+ // continue
724
+ cancelCloning();
725
+ return;
726
+ }
727
+
728
+ // Load overview
729
+ loadOverview();
730
+ }
731
+ );
732
+ };
733
+ /**
734
+ * Cancel Cloning Process
735
+ */
736
+ var cancelCloningUpdate = function ()
737
+ {
738
+ if (true === that.isFinished)
739
+ {
740
+ return true;
741
+ }
742
+
743
+ //alert(that.data.cloneID);
744
+ ajax(
745
+ {
746
+ action: "wpstg_cancel_update",
747
+ clone: that.data.cloneID,
748
+ nonce: wpstg.nonce
749
+ },
750
+ function (response)
751
+ {
752
+
753
+
754
+ if (response && "undefined" !== typeof (response.delete) && response.delete === "finished") {
755
+ // Load overview
756
+ loadOverview();
757
+ return;
758
+ }
759
+
760
+ if (true !== response)
761
+ {
762
+ // continue
763
+ cancelCloningUpdate();
764
+ return;
765
+ }
766
+
767
+ // Load overview
768
+ loadOverview();
769
+ }
770
+ );
771
+ };
772
+
773
+ /**
774
+ * Scroll the window log to bottom
775
+ * @returns void
776
+ */
777
+ var logscroll = function () {
778
+ var $div = cache.get("#wpstg-log-details");
779
+ if ("undefined" !== typeof ($div[0])) {
780
+ $div.scrollTop($div[0].scrollHeight);
781
+ }
782
+ }
783
+
784
+ /**
785
+ * Append the log to the logging window
786
+ * @param string log
787
+ * @returns void
788
+ */
789
+ var getLogs = function (log)
790
+ {
791
+ if (log != null && "undefined" !== typeof (log)) {
792
+ if (log.constructor === Array) {
793
+ $.each(log, function (index, value) {
794
+ if (value === null) {
795
+ return;
796
+ }
797
+ if (value.type === 'ERROR') {
798
+ cache.get("#wpstg-log-details").append('<span style="color:red;">[' + value.type + ']</span>-' + '[' + value.date + '] ' + value.message + '</br>');
799
+ } else {
800
+ cache.get("#wpstg-log-details").append('[' + value.type + ']-' + '[' + value.date + '] ' + value.message + '</br>');
801
+ }
802
+ })
803
+ } else {
804
+ cache.get("#wpstg-log-details").append('[' + log.type + ']-' + '[' + log.date + '] ' + log.message + '</br>');
805
+ }
806
+ }
807
+ logscroll();
808
+
809
+ };
810
+
811
+ /**
812
+ * Check diskspace
813
+ * @returns string json
814
+ */
815
+ var checkDiskSpace = function () {
816
+ cache.get("#wpstg-check-space").on("click", function (e) {
817
+ cache.get("#wpstg-loader").show();
818
+ console.log("check disk space");
819
+ ajax(
820
+ {
821
+ action: "wpstg_check_disk_space",
822
+ nonce: wpstg.nonce
823
+ },
824
+ function (response)
825
+ {
826
+ if (false === response)
827
+ {
828
+ cache.get("#wpstg-clone-id-error").text('Can not detect disk space').show();
829
+ cache.get("#wpstg-loader").hide();
830
+ return;
831
+ }
832
+
833
+ // Not enough disk space
834
+ cache.get("#wpstg-clone-id-error").text('Available free disk space ' + response.freespace + ' | Estimated necessary disk space: ' + response.usedspace).show();
835
+ cache.get("#wpstg-loader").hide();
836
+ },
837
+ "json",
838
+ false
839
+ );
840
+ });
841
+
842
+ }
843
+
844
+
845
+
846
+ /**
847
+ * Start Cloning Process
848
+ * @type {Function}
849
+ */
850
+ that.startCloning = (function () {
851
+
852
+ // Register function for checking disk space
853
+ checkDiskSpace();
854
+
855
+ if ("wpstg_cloning" !== that.data.action && "wpstg_update" !== that.data.action)
856
+ {
857
+ return;
858
+ }
859
+
860
+ // Start the process
861
+ start();
862
+
863
+ // Functions
864
+ // Start
865
+ function start()
866
+ {
867
+ console.log("Starting cloning process...");
868
+
869
+ cache.get("#wpstg-loader").show();
870
+
871
+ // Clone Database
872
+ setTimeout(function () {
873
+ cloneDatabase();
874
+ }, wpstg.cpuLoad);
875
+ }
876
+
877
+ // Step 1: Clone Database
878
+ function cloneDatabase()
879
+ {
880
+ if (true === that.isCancelled)
881
+ {
882
+ return false;
883
+ }
884
+
885
+ if (true === that.getLogs)
886
+ {
887
+ getLogs();
888
+ }
889
+
890
+ setTimeout(
891
+ function () {
892
+ ajax(
893
+ {
894
+ action: "wpstg_clone_database",
895
+ nonce: wpstg.nonce
896
+ },
897
+ function (response) {
898
+ // Add percentage
899
+ if ("undefined" !== typeof (response.percentage))
900
+ {
901
+ cache.get("#wpstg-db-progress").width(response.percentage + '%');
902
+ }
903
+ // Add Log
904
+ if ("undefined" !== typeof (response.last_msg))
905
+ {
906
+ getLogs(response.last_msg);
907
+ }
908
+
909
+ // Continue clone DB
910
+ if (false === response.status)
911
+ {
912
+ setTimeout(function () {
913
+ cloneDatabase();
914
+ }, wpstg.cpuLoad);
915
+ }
916
+ // Next Step
917
+ else if (true === response.status)
918
+ {
919
+ //console.log('prepareDirectories ' + response.status);
920
+ setTimeout(function () {
921
+ prepareDirectories();
922
+ }, wpstg.cpuLoad);
923
+ }
924
+ }
925
+ );
926
+ },
927
+ 500
928
+ );
929
+ }
930
+
931
+ // Step 2: Prepare Directories
932
+ function prepareDirectories()
933
+ {
934
+ if (true === that.isCancelled)
935
+ {
936
+ return false;
937
+ }
938
+
939
+ if (true === that.getLogs)
940
+ {
941
+ getLogs();
942
+ }
943
+
944
+ setTimeout(
945
+ function () {
946
+ ajax(
947
+ {
948
+ action: "wpstg_clone_prepare_directories",
949
+ nonce: wpstg.nonce
950
+ },
951
+ function (response) {
952
+
953
+ // Error
954
+ if ("undefined" !== typeof response.error && "undefined" !== typeof response.message) {
955
+ showError(response.message);
956
+ console.log(response.message);
957
+ }
958
+
959
+ // Add percentage
960
+ if ("undefined" !== typeof (response.percentage))
961
+ {
962
+ cache.get("#wpstg-directories-progress").width(response.percentage + '%');
963
+ }
964
+
965
+ // Add Log
966
+ if ("undefined" !== typeof (response.last_msg))
967
+ {
968
+ getLogs(response.last_msg);
969
+ }
970
+
971
+ if (false === response.status)
972
+ {
973
+ setTimeout(function () {
974
+ prepareDirectories();
975
+ }, wpstg.cpuLoad);
976
+ }
977
+ else if (true === response.status)
978
+ {
979
+ console.log('prepareDirectories' + response.status);
980
+ cloneFiles();
981
+ }
982
+ }
983
+ );
984
+ },
985
+ 500
986
+ );
987
+ }
988
+
989
+ // Step 3: Clone Files
990
+ function cloneFiles()
991
+ {
992
+ if (true === that.isCancelled)
993
+ {
994
+ return false;
995
+ }
996
+
997
+ if (true === that.getLogs)
998
+ {
999
+ getLogs();
1000
+ }
1001
+
1002
+ ajax(
1003
+ {
1004
+ action: "wpstg_clone_files",
1005
+ nonce: wpstg.nonce
1006
+ },
1007
+ function (response) {
1008
+ // Add percentage
1009
+ if ("undefined" !== typeof (response.percentage))
1010
+ {
1011
+ cache.get("#wpstg-files-progress").width(response.percentage + '%');
1012
+ }
1013
+
1014
+ // Add Log
1015
+ if ("undefined" !== typeof (response.last_msg))
1016
+ {
1017
+ getLogs(response.last_msg);
1018
+ }
1019
+
1020
+ if (false === response.status)
1021
+ {
1022
+ setTimeout(function () {
1023
+ cloneFiles();
1024
+ }, wpstg.cpuLoad);
1025
+ }
1026
+ else if (true === response.status)
1027
+ {
1028
+ setTimeout(function () {
1029
+ replaceData();
1030
+ }, wpstg.cpuLoad);
1031
+ }
1032
+ }
1033
+ );
1034
+ }
1035
+
1036
+ // Step 4: Replace Data
1037
+ function replaceData()
1038
+ {
1039
+ if (true === that.isCancelled)
1040
+ {
1041
+ return false;
1042
+ }
1043
+
1044
+ if (true === that.getLogs)
1045
+ {
1046
+ console.log('getLogs1')
1047
+ getLogs();
1048
+ }
1049
+
1050
+ ajax(
1051
+ {
1052
+ action: "wpstg_clone_replace_data",
1053
+ nonce: wpstg.nonce
1054
+ },
1055
+ function (response) {
1056
+ // Add percentage
1057
+ if ("undefined" !== typeof (response.percentage))
1058
+ {
1059
+ cache.get("#wpstg-links-progress").width(response.percentage + '%');
1060
+ }
1061
+
1062
+ // Add Log
1063
+ if ("undefined" !== typeof (response.last_msg))
1064
+ {
1065
+ console.log('get Logs');
1066
+ getLogs(response.last_msg);
1067
+ }
1068
+
1069
+ if (false === response.status)
1070
+ {
1071
+ setTimeout(function () {
1072
+ console.log('replace data');
1073
+ replaceData();
1074
+ }, wpstg.cpuLoad);
1075
+ }
1076
+ else if (true === response.status)
1077
+ {
1078
+ console.log('finish');
1079
+ finish();
1080
+ }
1081
+ }
1082
+ );
1083
+ }
1084
+
1085
+ // Finish
1086
+ function finish()
1087
+ {
1088
+ if (true === that.getLogs)
1089
+ {
1090
+ getLogs();
1091
+ }
1092
+
1093
+ if (true === that.isCancelled || true === that.isFinished)
1094
+ {
1095
+ cache.get("#wpstg-loader").hide();
1096
+ return false;
1097
+ }
1098
+
1099
+ ajax(
1100
+ {
1101
+ action: "wpstg_clone_finish",
1102
+ nonce: wpstg.nonce
1103
+ },
1104
+ function (response)
1105
+ {
1106
+ // Invalid response
1107
+ if ("object" !== typeof (response))
1108
+ {
1109
+ showError(
1110
+ "Couldn't finish the cloning process properly. " +
1111
+ "Your clone has been copied but failed to do clean up and " +
1112
+ "saving its records to the database." +
1113
+ "Please contact support and provide your logs."
1114
+ );
1115
+
1116
+ return;
1117
+ }
1118
+
1119
+ // Add Log
1120
+ if ("undefined" !== typeof (response.last_msg))
1121
+ {
1122
+ getLogs(response.last_msg);
1123
+ }
1124
+
1125
+ console.log("Cloning process finished");
1126
+
1127
+ var $link1 = cache.get("#wpstg-clone-url-1");
1128
+ var $link = cache.get("#wpstg-clone-url");
1129
+
1130
+ cache.get("#wpstg_staging_name").html(that.data.cloneID);
1131
+ cache.get("#wpstg-finished-result").show();
1132
+ cache.get("#wpstg-cancel-cloning").prop("disabled", true);
1133
+ cache.get("#wpstg-cancel-cloning-update").prop("disabled", true);
1134
+ // $link1.attr("href", $link1.attr("href") + '/' + response.directoryName);
1135
+ // $link1.append('/' + response.directoryName);
1136
+ // $link.attr("href", $link.attr("href") + '/' + response.directoryName);
1137
+ $link1.attr("href", response.url);
1138
+ $link.attr("href", response.url);
1139
+ cache.get("#wpstg-remove-clone").data("clone", that.data.cloneID);
1140
+
1141
+ // Finished
1142
+ that.isFinished = true;
1143
+
1144
+ finish();
1145
+ }
1146
+ );
1147
+ }
1148
+ });
1149
+
1150
+
1151
+ /**
1152
+ * Initiation
1153
+ * @type {Function}
1154
+ */
1155
+ that.init = (function () {
1156
+ loadOverview();
1157
+ elements();
1158
+ //startUpdate();
1159
+ stepButtons();
1160
+ tabs();
1161
+ //optimizer();
1162
+ });
1163
+
1164
+ /**
1165
+ * Ajax call
1166
+ * @type {ajax}
1167
+ */
1168
+ that.ajax = ajax;
1169
+ that.showError = showError;
1170
+ that.getLogs = getLogs;
1171
+ that.loadOverview = loadOverview;
1172
+
1173
+ return that;
1174
+ })(jQuery);
1175
+
1176
+ jQuery(document).ready(function () {
1177
+ WPStaging.init();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1178
  });
apps/Backend/views/_includes/messages/beta.php CHANGED
@@ -1,20 +1,20 @@
1
- <div class="wpstg_beta_notice error" style="box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);">
2
- <p>
3
- WP Staging is well tested and we did a lot to catch every possible error but
4
- we can not handle all possible combinations of server, plugins and themes. <br>
5
- <strong>BEFORE</strong> you create your first staging site it´s highly recommended
6
- <strong>to make a full backup of your website</strong> first!
7
- </p>
8
- <p>
9
- A good plugin for an entire WordPress backup is the free one
10
- <a href="https://wordpress.org/plugins/backwpup/" target="_blank">BackWPup</a>
11
- </p>
12
- <ul>
13
- <li>
14
- <a href="javascript:void(0);" class="wpstg_hide_beta" title="I understand" data-url="<?php echo admin_url("admin-ajax.php")?>" style="font-weight:bold;color:#00a0d2;">
15
- I understand! (Do not show this again)
16
- </a>
17
- </li>
18
- </ul>
19
- </div>
20
  <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-beta.js"?>"></script>
1
+ <div class="wpstg_beta_notice error" style="box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);">
2
+ <p>
3
+ WP Staging is well tested and we did a lot to catch every possible error but
4
+ we can not handle all possible combinations of server, plugins and themes. <br>
5
+ <strong>BEFORE</strong> you create your first staging site it´s highly recommended
6
+ <strong>to make a full backup of your website</strong> first!
7
+ </p>
8
+ <p>
9
+ A good plugin for an entire WordPress backup is the free one
10
+ <a href="https://wordpress.org/plugins/backwpup/" target="_blank">BackWPup</a>
11
+ </p>
12
+ <ul>
13
+ <li>
14
+ <a href="javascript:void(0);" class="wpstg_hide_beta" title="I understand" data-url="<?php echo admin_url("admin-ajax.php")?>" style="font-weight:bold;color:#00a0d2;">
15
+ I understand! (Do not show this again)
16
+ </a>
17
+ </li>
18
+ </ul>
19
+ </div>
20
  <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-beta.js"?>"></script>
apps/Backend/views/_includes/messages/rating.php CHANGED
@@ -1,31 +1,31 @@
1
- <div class="wpstg_fivestar updated" style="box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);">
2
- <p>
3
- Awesome, you've been using <strong>WP Staging </strong> for more than 1 week.
4
- May I ask you to give it a <strong>5-star</strong> rating on Wordpress?
5
- <br><br>
6
- P.S. Looking for a way to copy plugins and theme files from staging to live site? Try out <a href="https://wp-staging.com" target="_blank">WP Staging Pro</a> <br>
7
- </p>
8
-
9
- <p>
10
- <strong>Regards,<br>René Hermenau</strong>
11
- </p>
12
-
13
- <ul>
14
- <li>
15
- <a href="https://wordpress.org/support/plugin/wp-staging/reviews/?filter=5#new-post" class="thankyou" target="_new" title="Ok, you deserved it" style="font-weight:bold;">
16
- Ok, you deserved it
17
- </a>
18
- </li>
19
- <li>
20
- <a href="javascript:void(0);" class="wpstg_hide_rating" title="I already did" style="font-weight:bold;">
21
- I already did
22
- </a>
23
- </li>
24
- <li>
25
- <a href="javascript:void(0);" class="wpstg_hide_rating" title="No, not good enough" style="font-weight:bold;">
26
- No, not good enough
27
- </a>
28
- </li>
29
- </ul>
30
- </div>
31
  <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-rating.js"?>"></script>
1
+ <div class="wpstg_fivestar updated" style="box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);">
2
+ <p>
3
+ Awesome, you've been using <strong>WP Staging </strong> for more than 1 week.
4
+ May I ask you to give it a <strong>5-star</strong> rating on Wordpress?
5
+ <br><br>
6
+ P.S. Looking for a way to copy plugins and theme files from staging to live site? Try out <a href="https://wp-staging.com" target="_blank">WP Staging Pro</a> <br>
7
+ </p>
8
+
9
+ <p>
10
+ <strong>Regards,<br>René Hermenau</strong>
11
+ </p>
12
+
13
+ <ul>
14
+ <li>
15
+ <a href="https://wordpress.org/support/plugin/wp-staging/reviews/?filter=5#new-post" class="thankyou" target="_new" title="Ok, you deserved it" style="font-weight:bold;">
16
+ Ok, you deserved it
17
+ </a>
18
+ </li>
19
+ <li>
20
+ <a href="javascript:void(0);" class="wpstg_hide_rating" title="I already did" style="font-weight:bold;">
21
+ I already did
22
+ </a>
23
+ </li>
24
+ <li>
25
+ <a href="javascript:void(0);" class="wpstg_hide_rating" title="No, not good enough" style="font-weight:bold;">
26
+ No, not good enough
27
+ </a>
28
+ </li>
29
+ </ul>
30
+ </div>
31
  <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-rating.js"?>"></script>
apps/Backend/views/_includes/messages/wp-version-compatible-message.php CHANGED
@@ -1,13 +1,13 @@
1
- <div class="error">
2
- <p>
3
- <?php
4
- echo sprintf( __(
5
- '<strong>Watch out:</strong> This version of WP Staging has not been tested with your WordPress version %2$s.' .
6
- '<br/>As WP Staging is using crucial DB and file functions it\'s important that you are using a ' .
7
- 'WP Staging version <br> which has been verified to be working with your WordPress version. ' .
8
- 'You risk unexpected data lose if you do not so! ' .
9
- '<p><strong>Get the latest WP Staging plugin from <a href="%1$s" target="_blank">https://wp-staging.com</a>.</strong>', 'wpstg'), 'https://wp-staging.com', get_bloginfo( 'version' )
10
- );
11
- ?>
12
- </p>
13
  </div>
1
+ <div class="error">
2
+ <p>
3
+ <?php
4
+ echo sprintf( __(
5
+ '<strong>Watch out:</strong> This version of WP Staging has not been tested with your WordPress version %2$s.' .
6
+ '<br/>As WP Staging is using crucial DB and file functions it\'s important that you are using a ' .
7
+ 'WP Staging version <br> which has been verified to be working with your WordPress version. ' .
8
+ 'You risk unexpected data lose if you do not so! ' .
9
+ '<p><strong>Get the latest WP Staging plugin from <a href="%1$s" target="_blank">https://wp-staging.com</a>.</strong>', 'wpstg'), 'https://wp-staging.com', get_bloginfo( 'version' )
10
+ );
11
+ ?>
12
+ </p>
13
  </div>
apps/Backend/views/clone/ajax/scan.php CHANGED
@@ -1,129 +1,129 @@
1
- <label id="wpstg-clone-label" for="wpstg-new-clone">
2
- <?php echo __('Staging Site Name:', 'wpstg')?>
3
- <input type="text" id="wpstg-new-clone-id" value="<?php echo $options->current; ?>"<?php if (null !== $options->current) echo " disabled='disabled'"?>>
4
- </label>
5
-
6
- <span class="wpstg-error-msg" id="wpstg-clone-id-error" style="display:none;">
7
- <?php echo __(
8
- "<br>Probably not enough free disk space to create a staging site. ".
9
- "<br> You can continue but its likely that the copying process will fail.",
10
- "wpstg"
11
- )?>
12
- </span>
13
-
14
- <div class="wpstg-tabs-wrapper">
15
- <a href="#" class="wpstg-tab-header active" data-id="#wpstg-scanning-db">
16
- <span class="wpstg-tab-triangle">&#9658;</span>
17
- <?php echo __("DB Tables", "wpstg")?>
18
- </a>
19
-
20
- <div class="wpstg-tab-section" id="wpstg-scanning-db">
21
- <?php do_action("wpstg_scanning_db")?>
22
- <h4 style="margin:0">
23
- <?php
24
-
25
- echo __(
26
- "Uncheck the tables you do not want to copy. Usually you should select tables with prefix '{$scan->prefix}', only.",
27
- "wpstg"
28
- )?>
29
- </h4>
30
- <div style="margin-top:10px;margin-bottom:10px;">
31
- <a href="#" class="wpstg-button-unselect button"> None </a>
32
- <a href="#" class="wpstg-button-select button"> <?php _e(WPStaging\WPStaging::getTablePrefix(), 'wpstg'); ?> </a>
33
- </div>
34
- <?php
35
- //print_r( $options->excludedTables);
36
- foreach ($options->tables as $table):
37
- $attributes = in_array($table->name, $options->excludedTables) ? '' : "checked";
38
- $attributes .= in_array($table->name, $options->clonedTables) ? " disabled" : '';
39
- ?>
40
- <div class="wpstg-db-table">
41
- <label>
42
- <input class="wpstg-db-table-checkboxes" type="checkbox" name="<?php echo $table->name?>" <?php echo $attributes?>>
43
- <?php echo $table->name?>
44
- </label>
45
- <span class="wpstg-size-info">
46
- <?php echo $scan->formatSize($table->size)?>
47
- </span>
48
- </div>
49
- <?php endforeach ?>
50
- <div style="margin-top:10px;">
51
- <a href="#" class="wpstg-button-unselect button"> None </a>
52
- <a href="#" class="wpstg-button-select button"> <?php _e(WPStaging\WPStaging::getTablePrefix(), 'wpstg'); ?> </a>
53
- </div>
54
- </div>
55
-
56
- <a href="#" class="wpstg-tab-header" data-id="#wpstg-scanning-files">
57
- <span class="wpstg-tab-triangle">&#9658;</span>
58
- <?php echo __("Files", "wpstg")?>
59
- </a>
60
-
61
- <div class="wpstg-tab-section" id="wpstg-scanning-files">
62
- <h4 style="margin:0">
63
- <?php echo __("Uncheck the folders you do not want to copy. Click on them for expanding!", "wpstg")?>
64
- </h4>
65
-
66
- <?php echo $scan->directoryListing()?>
67
-
68
- <h4 style="margin:10px 0 10px 0">
69
- <?php echo __("Extra directories to copy", "wpstg")?>
70
- </h4>
71
-
72
- <textarea id="wpstg_extraDirectories" name="wpstg_extraDirectories" style="width:100%;height:250px;"></textarea>
73
- <p>
74
- <span>
75
- <?php
76
- echo __(
77
- "Enter one folder path per line.<br>".
78
- "Folders must start with absolute path: " . $options->root,
79
- "wpstg"
80
- )
81
- ?>
82
- </span>
83
- </p>
84
-
85
- <p>
86
- <span>
87
- <?php
88
- if (isset($options->clone)){
89
- echo __("All files will be copied to: ", "wpstg") . $options->root . $options->clone;
90
- }
91
- ?>
92
- </span>
93
- </p>
94
- </div>
95
-
96
- <a href="#" class="wpstg-tab-header" data-id="#wpstg-advanced-settings">
97
- <span class="wpstg-tab-triangle">&#9658;</span>
98
- <?php echo __("Advanced Options", "wpstg")?>
99
- </a>
100
-
101
- <div class="wpstg-tab-section" id="wpstg-advanced-settings">
102
- Coming Soon...
103
- </div>
104
-
105
- </div>
106
-
107
- <button type="button" class="wpstg-prev-step-link wpstg-link-btn button-primary">
108
- <?php _e("Back", "wpstg")?>
109
- </button>
110
-
111
- <?php
112
- if (null !== $options->current)
113
- {
114
- $label = __("Update Clone", "wpstg");
115
- $action = 'wpstg_update';
116
-
117
- echo '<button type="button" id="wpstg-start-updating" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="'.$action.'">'.$label.'</button>';
118
- }
119
- else
120
- {
121
- $label = __("Start Cloning", "wpstg");
122
- $action = 'wpstg_cloning';
123
-
124
- echo '<button type="button" id="wpstg-start-cloning" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="'.$action.'">'.$label.'</button>';
125
-
126
- }
127
- ?>
128
-
129
  <a href="#" id="wpstg-check-space"><?php _e('Check Disk Space', 'wpstg'); ?></a>
1
+ <label id="wpstg-clone-label" for="wpstg-new-clone">
2
+ <?php echo __('Staging Site Name:', 'wpstg')?>
3
+ <input type="text" id="wpstg-new-clone-id" value="<?php echo $options->current; ?>"<?php if (null !== $options->current) echo " disabled='disabled'"?>>
4
+ </label>
5
+
6
+ <span class="wpstg-error-msg" id="wpstg-clone-id-error" style="display:none;">
7
+ <?php echo __(
8
+ "<br>Probably not enough free disk space to create a staging site. ".
9
+ "<br> You can continue but its likely that the copying process will fail.",
10
+ "wpstg"
11
+ )?>
12
+ </span>
13
+
14
+ <div class="wpstg-tabs-wrapper">
15
+ <a href="#" class="wpstg-tab-header active" data-id="#wpstg-scanning-db">
16
+ <span class="wpstg-tab-triangle">&#9658;</span>
17
+ <?php echo __("DB Tables", "wpstg")?>
18
+ </a>
19
+
20
+ <div class="wpstg-tab-section" id="wpstg-scanning-db">
21
+ <?php do_action("wpstg_scanning_db")?>
22
+ <h4 style="margin:0">
23
+ <?php
24
+
25
+ echo __(
26
+ "Uncheck the tables you do not want to copy. Usually you should select tables with prefix '{$scan->prefix}', only.",
27
+ "wpstg"
28
+ )?>
29
+ </h4>
30
+ <div style="margin-top:10px;margin-bottom:10px;">
31
+ <a href="#" class="wpstg-button-unselect button"> None </a>
32
+ <a href="#" class="wpstg-button-select button"> <?php _e(WPStaging\WPStaging::getTablePrefix(), 'wpstg'); ?> </a>
33
+ </div>
34
+ <?php
35
+ //print_r( $options->excludedTables);
36
+ foreach ($options->tables as $table):
37
+ $attributes = in_array($table->name, $options->excludedTables) ? '' : "checked";
38
+ $attributes .= in_array($table->name, $options->clonedTables) ? " disabled" : '';
39
+ ?>
40
+ <div class="wpstg-db-table">
41
+ <label>
42
+ <input class="wpstg-db-table-checkboxes" type="checkbox" name="<?php echo $table->name?>" <?php echo $attributes?>>
43
+ <?php echo $table->name?>
44
+ </label>
45
+ <span class="wpstg-size-info">
46
+ <?php echo $scan->formatSize($table->size)?>
47
+ </span>
48
+ </div>
49
+ <?php endforeach ?>
50
+ <div style="margin-top:10px;">
51
+ <a href="#" class="wpstg-button-unselect button"> None </a>
52
+ <a href="#" class="wpstg-button-select button"> <?php _e(WPStaging\WPStaging::getTablePrefix(), 'wpstg'); ?> </a>
53
+ </div>
54
+ </div>
55
+
56
+ <a href="#" class="wpstg-tab-header" data-id="#wpstg-scanning-files">
57
+ <span class="wpstg-tab-triangle">&#9658;</span>
58
+ <?php echo __("Files", "wpstg")?>
59
+ </a>
60
+
61
+ <div class="wpstg-tab-section" id="wpstg-scanning-files">
62
+ <h4 style="margin:0">
63
+ <?php echo __("Uncheck the folders you do not want to copy. Click on them for expanding!", "wpstg")?>
64
+ </h4>
65
+
66
+ <?php echo $scan->directoryListing()?>
67
+
68
+ <h4 style="margin:10px 0 10px 0">
69
+ <?php echo __("Extra directories to copy", "wpstg")?>
70
+ </h4>
71
+
72
+ <textarea id="wpstg_extraDirectories" name="wpstg_extraDirectories" style="width:100%;height:250px;"></textarea>
73
+ <p>
74
+ <span>
75
+ <?php
76
+ echo __(
77
+ "Enter one folder path per line.<br>".
78
+ "Folders must start with absolute path: " . $options->root,
79
+ "wpstg"
80
+ )
81
+ ?>
82
+ </span>
83
+ </p>
84
+
85
+ <p>
86
+ <span>
87
+ <?php
88
+ if (isset($options->clone)){
89
+ echo __("All files will be copied to: ", "wpstg") . $options->root . $options->clone;
90
+ }
91
+ ?>
92
+ </span>
93
+ </p>
94
+ </div>
95
+
96
+ <a href="#" class="wpstg-tab-header" data-id="#wpstg-advanced-settings">
97
+ <span class="wpstg-tab-triangle">&#9658;</span>
98
+ <?php echo __("Advanced Options", "wpstg")?>
99
+ </a>
100
+
101
+ <div class="wpstg-tab-section" id="wpstg-advanced-settings">
102
+ Coming Soon...
103
+ </div>
104
+
105
+ </div>
106
+
107
+ <button type="button" class="wpstg-prev-step-link wpstg-link-btn button-primary">
108
+ <?php _e("Back", "wpstg")?>
109
+ </button>
110
+
111
+ <?php
112
+ if (null !== $options->current)
113
+ {
114
+ $label = __("Update Clone", "wpstg");
115
+ $action = 'wpstg_update';
116
+
117
+ echo '<button type="button" id="wpstg-start-updating" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="'.$action.'">'.$label.'</button>';
118
+ }
119
+ else
120
+ {
121
+ $label = __("Start Cloning", "wpstg");
122
+ $action = 'wpstg_cloning';
123
+
124
+ echo '<button type="button" id="wpstg-start-cloning" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="'.$action.'">'.$label.'</button>';
125
+
126
+ }
127
+ ?>
128
+
129
  <a href="#" id="wpstg-check-space"><?php _e('Check Disk Space', 'wpstg'); ?></a>
apps/Backend/views/clone/ajax/single-overview.php CHANGED
@@ -1,56 +1,56 @@
1
- <div id="wpstg-step-1">
2
- <button id="wpstg-new-clone" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="wpstg_scanning">
3
- <?php echo __("Create new staging site", "wpstg")?>
4
- </button>
5
- </div>
6
-
7
- <?php if (isset($availableClones) && !empty($availableClones)):?>
8
- <!-- Existing Clones -->
9
- <div id="wpstg-existing-clones">
10
- <h3>
11
- <?php _e("Your Staging Sites:", "wpstg")?>
12
- </h3>
13
- <?php //wp_die(var_dump($availableClones)); ?>
14
- <?php foreach ($availableClones as $name => $data):?>
15
- <div id="<?php echo $data["directoryName"]; ?>" class="wpstg-clone">
16
-
17
- <?php $urlLogin = $data["url"] . "/wp-login.php"?>
18
-
19
- <a href="<?php echo $urlLogin?>" class="wpstg-clone-title" target="_blank">
20
- <?php //echo $name?>
21
- <?php echo $data["directoryName"]; ?>
22
- </a>
23
-
24
- <?php echo apply_filters("wpstg_before_stage_buttons", $html = '', $name, $data)?>
25
-
26
- <a href="<?php echo $urlLogin?>" class="wpstg-open-clone wpstg-clone-action" target="_blank">
27
- <?php _e("Open", "wpstg"); ?>
28
- </a>
29
-
30
- <a href="#" class="wpstg-execute-clone wpstg-clone-action" data-clone="<?php echo $name?>">
31
- <?php _e("Update", "wpstg"); ?>
32
- </a>
33
-
34
- <a href="#" class="wpstg-remove-clone wpstg-clone-action" data-clone="<?php echo $name?>">
35
- <?php _e("Delete", "wpstg"); ?>
36
- </a>
37
-
38
- <?php echo apply_filters("wpstg_after_stage_buttons", $html = '', $name, $data)?>
39
- <div class="wpstg-staging-info">
40
- <?php
41
- $prefix = isset ($data['prefix']) ? __("DB prefix: <span class='wpstg-bold'>" . $data['prefix'], "wpstg") . '</span> ' : '&nbsp;&nbsp;&nbsp;';
42
- //$path = isset ($data['directoryName']) ? __("Subdir: <span class='wpstg-bold'>" . $data['directoryName'], "wpstg") . '</span>' : '';
43
- echo $prefix;
44
- ?>
45
- </div>
46
- </div>
47
- <?php endforeach?>
48
- </div>
49
- <!-- /Existing Clones -->
50
- <?php endif?>
51
-
52
- <!-- Remove Clone -->
53
- <div id="wpstg-removing-clone">
54
-
55
- </div>
56
  <!-- /Remove Clone -->
1
+ <div id="wpstg-step-1">
2
+ <button id="wpstg-new-clone" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="wpstg_scanning">
3
+ <?php echo __("Create new staging site", "wpstg")?>
4
+ </button>
5
+ </div>
6
+
7
+ <?php if (isset($availableClones) && !empty($availableClones)):?>
8
+ <!-- Existing Clones -->
9
+ <div id="wpstg-existing-clones">
10
+ <h3>
11
+ <?php _e("Your Staging Sites:", "wpstg")?>
12
+ </h3>
13
+ <?php //wp_die(var_dump($availableClones)); ?>
14
+ <?php foreach ($availableClones as $name => $data):?>
15
+ <div id="<?php echo $data["directoryName"]; ?>" class="wpstg-clone">
16
+
17
+ <?php $urlLogin = $data["url"] . "/wp-login.php"?>
18
+
19
+ <a href="<?php echo $urlLogin?>" class="wpstg-clone-title" target="_blank">
20
+ <?php //echo $name?>
21
+ <?php echo $data["directoryName"]; ?>
22
+ </a>
23
+
24
+ <?php echo apply_filters("wpstg_before_stage_buttons", $html = '', $name, $data)?>
25
+
26
+ <a href="<?php echo $urlLogin?>" class="wpstg-open-clone wpstg-clone-action" target="_blank">
27
+ <?php _e("Open", "wpstg"); ?>
28
+ </a>
29
+
30
+ <a href="#" class="wpstg-execute-clone wpstg-clone-action" data-clone="<?php echo $name?>">
31
+ <?php _e("Update", "wpstg"); ?>
32
+ </a>
33
+
34
+ <a href="#" class="wpstg-remove-clone wpstg-clone-action" data-clone="<?php echo $name?>">
35
+ <?php _e("Delete", "wpstg"); ?>
36
+ </a>
37
+
38
+ <?php echo apply_filters("wpstg_after_stage_buttons", $html = '', $name, $data)?>
39
+ <div class="wpstg-staging-info">
40
+ <?php
41
+ $prefix = isset ($data['prefix']) ? __("DB prefix: <span class='wpstg-bold'>" . $data['prefix'], "wpstg") . '</span> ' : '&nbsp;&nbsp;&nbsp;';
42
+ //$path = isset ($data['directoryName']) ? __("Subdir: <span class='wpstg-bold'>" . $data['directoryName'], "wpstg") . '</span>' : '';
43
+ echo $prefix;
44
+ ?>
45
+ </div>
46
+ </div>
47
+ <?php endforeach?>
48
+ </div>
49
+ <!-- /Existing Clones -->
50
+ <?php endif?>
51
+
52
+ <!-- Remove Clone -->
53
+ <div id="wpstg-removing-clone">
54
+
55
+ </div>
56
  <!-- /Remove Clone -->
apps/Backend/views/clone/ajax/start.php CHANGED
@@ -1,113 +1,113 @@
1
- <div class=successfullying-section">
2
- <?php echo __("Copy Database Tables", "wpstg")?>
3
- <div class="wpstg-progress-bar">
4
- <div class="wpstg-progress" id="wpstg-db-progress" style="width:0"></div>
5
- </div>
6
- </div>
7
-
8
- <div class="wpstg-cloning-section">
9
- <?php echo __("Prepare Directories", "wpstg")?>
10
- <div class="wpstg-progress-bar">
11
- <div class="wpstg-progress" id="wpstg-directories-progress" style="width:0"></div>
12
- </div>
13
- </div>
14
-
15
- <div class="wpstg-cloning-section">
16
- <?php echo __("Copy Files", "wpstg")?>
17
- <div class="wpstg-progress-bar">
18
- <div class="wpstg-progress" id="wpstg-files-progress" style="width:0"></div>
19
- </div>
20
- </div>
21
-
22
- <div class="wpstg-cloning-section">
23
- <?php echo __("Replace Data", "wpstg")?>
24
- <div class="wpstg-progress-bar">
25
- <div class="wpstg-progress" id="wpstg-links-progress" style="width:0"></div>
26
- </div>
27
- </div>
28
-
29
- <button type="button" id="wpstg-cancel-cloning" class="wpstg-link-btn button-primary">
30
- <?php echo __("Cancel", "wpstg")?>
31
- </button>
32
-
33
- <button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
34
- <?php _e('Display working log', 'wpstg')?>
35
- </button>
36
-
37
- <div>
38
- <span id="wpstg-cloning-result"></span>
39
- </div>
40
-
41
- <div id="wpstg-finished-result">
42
- <h3>Congratulations
43
- </h3>
44
- <?php
45
- //echo ABSPATH . '<br>';
46
- //echo get_home_path();
47
- $subDirectory = str_replace( get_home_path(), '', ABSPATH );
48
- $url = get_home_url() . str_replace('/', '', $subDirectory);
49
- echo sprintf( __( 'WP Staging successfully created a staging site in a sub-directory of your main site in:<br><strong><a href="%1$s" target="_blank" id="wpstg-clone-url-1">%1$s</a></strong>', 'wpstg' ), $url );
50
- ?>
51
- <br>
52
- <?php //echo __('Open and access the staging site: ', 'wpstg')?>
53
- <br>
54
- <a href="<?php echo $url; ?>" id="wpstg-clone-url" target="_blank" class="wpstg-link-btn button-primary">
55
- Open staging site <span style="font-size: 10px;">(login with your admin credentials)</span>
56
- </a>
57
- <!--<a href="" class="wpstg-link-btn button-primary" id="wpstg-remove-cloning">
58
- <?php //echo __("Remove", "wpstg")?>
59
- </a>//-->
60
- <a href="" class="wpstg-link-btn button-primary" id="wpstg-home-link">
61
- <?php echo __("Start again", "wpstg")?>
62
- </a>
63
- <div id="wpstg-success-notice">
64
- <h3 style="margin-top:0px;">
65
- <?php _e("Important Notes:", "wpstg")?>
66
- </h3>
67
- <ul>
68
- <li>
69
- <strong>1. Permalinks on your <span style="font-style:italic;">staging site</span> will be disabled for technical reasons! </strong>
70
- <br>
71
- Usually this is no problem for a staging website and you do not need to use permalinks!
72
- <br>
73
- <p>
74
- If you really want permalinks on your staging site you need to do several modifications to your .htaccess (Apache) or *.conf (Nginx).
75
- <br>
76
- WP Staging can not do this modification automatically.
77
- </p>
78
- <p>
79
- <strong>Read more:</strong>
80
- <a href="http://stackoverflow.com/questions/5564881/htaccess-to-rewrite-wordpress-subdirectory-with-permalinks-to-root" target="_blank">
81
- Changes .htaccess
82
- </a> |
83
- <a href="http://robido.com/nginx/nginx-wordpress-subdirectory-configuration-example/" target="_blank">
84
- Changes nginx conf
85
- </a>
86
- </p>
87
- </li>
88
- <li>
89
- <strong>2. Verify that you are REALLY working on your staging site and NOT on your production site if you are uncertain! </strong>
90
- <br>
91
- Your main and your staging site are both reachable under the same domain so
92
- <br>
93
- it´s easy to get confused.
94
- <p>
95
- To assist you we changed the name of the dashboard link to
96
- <strong style="font-style:italic;">
97
- "STAGING - <span class="wpstg-clone-name"><?php echo get_bloginfo("name")?></span>"
98
- </strong>.
99
- <br>
100
- You will notice this new name in the admin bar:
101
- <br><br>
102
- <img src="<?php echo $this->url . "/img/admin_dashboard.png" ?>">
103
- </p>
104
- </li>
105
- </ul>
106
- </div>
107
- </div>
108
-
109
- <div id="wpstg-error-wrapper">
110
- <div id="wpstg-error-details"></div>
111
- </div>
112
-
113
  <div id="wpstg-log-details"></div>
1
+ <div class=successfullying-section">
2
+ <?php echo __("Copy Database Tables", "wpstg")?>
3
+ <div class="wpstg-progress-bar">
4
+ <div class="wpstg-progress" id="wpstg-db-progress" style="width:0"></div>
5
+ </div>
6
+ </div>
7
+
8
+ <div class="wpstg-cloning-section">
9
+ <?php echo __("Prepare Directories", "wpstg")?>
10
+ <div class="wpstg-progress-bar">
11
+ <div class="wpstg-progress" id="wpstg-directories-progress" style="width:0"></div>
12
+ </div>
13
+ </div>
14
+
15
+ <div class="wpstg-cloning-section">
16
+ <?php echo __("Copy Files", "wpstg")?>
17
+ <div class="wpstg-progress-bar">
18
+ <div class="wpstg-progress" id="wpstg-files-progress" style="width:0"></div>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="wpstg-cloning-section">
23
+ <?php echo __("Replace Data", "wpstg")?>
24
+ <div class="wpstg-progress-bar">
25
+ <div class="wpstg-progress" id="wpstg-links-progress" style="width:0"></div>
26
+ </div>
27
+ </div>
28
+
29
+ <button type="button" id="wpstg-cancel-cloning" class="wpstg-link-btn button-primary">
30
+ <?php echo __("Cancel", "wpstg")?>
31
+ </button>
32
+
33
+ <button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
34
+ <?php _e('Display working log', 'wpstg')?>
35
+ </button>
36
+
37
+ <div>
38
+ <span id="wpstg-cloning-result"></span>
39
+ </div>
40
+
41
+ <div id="wpstg-finished-result">
42
+ <h3>Congratulations
43
+ </h3>
44
+ <?php
45
+ //echo ABSPATH . '<br>';
46
+ //echo get_home_path();
47
+ $subDirectory = str_replace( get_home_path(), '', ABSPATH );
48
+ $url = get_home_url() . str_replace('/', '', $subDirectory);
49
+ echo sprintf( __( 'WP Staging successfully created a staging site in a sub-directory of your main site in:<br><strong><a href="%1$s" target="_blank" id="wpstg-clone-url-1">%1$s</a></strong>', 'wpstg' ), $url );
50
+ ?>
51
+ <br>
52
+ <?php //echo __('Open and access the staging site: ', 'wpstg')?>
53
+ <br>
54
+ <a href="<?php echo $url; ?>" id="wpstg-clone-url" target="_blank" class="wpstg-link-btn button-primary">
55
+ Open staging site <span style="font-size: 10px;">(login with your admin credentials)</span>
56
+ </a>
57
+ <!--<a href="" class="wpstg-link-btn button-primary" id="wpstg-remove-cloning">
58
+ <?php //echo __("Remove", "wpstg")?>
59
+ </a>//-->
60
+ <a href="" class="wpstg-link-btn button-primary" id="wpstg-home-link">
61
+ <?php echo __("Start again", "wpstg")?>
62
+ </a>
63
+ <div id="wpstg-success-notice">
64
+ <h3 style="margin-top:0px;">
65
+ <?php _e("Important Notes:", "wpstg")?>
66
+ </h3>
67
+ <ul>
68
+ <li>
69
+ <strong>1. Permalinks on your <span style="font-style:italic;">staging site</span> will be disabled for technical reasons! </strong>
70
+ <br>
71
+ Usually this is no problem for a staging website and you do not need to use permalinks!
72
+ <br>
73
+ <p>
74
+ If you really want permalinks on your staging site you need to do several modifications to your .htaccess (Apache) or *.conf (Nginx).
75
+ <br>
76
+ WP Staging can not do this modification automatically.
77
+ </p>
78
+ <p>
79
+ <strong>Read more:</strong>
80
+ <a href="http://stackoverflow.com/questions/5564881/htaccess-to-rewrite-wordpress-subdirectory-with-permalinks-to-root" target="_blank">
81
+ Changes .htaccess
82
+ </a> |
83
+ <a href="http://robido.com/nginx/nginx-wordpress-subdirectory-configuration-example/" target="_blank">
84
+ Changes nginx conf
85
+ </a>
86
+ </p>
87
+ </li>
88
+ <li>
89
+ <strong>2. Verify that you are REALLY working on your staging site and NOT on your production site if you are uncertain! </strong>
90
+ <br>
91
+ Your main and your staging site are both reachable under the same domain so
92
+ <br>
93
+ it´s easy to get confused.
94
+ <p>
95
+ To assist you we changed the name of the dashboard link to
96
+ <strong style="font-style:italic;">
97
+ "STAGING - <span class="wpstg-clone-name"><?php echo get_bloginfo("name")?></span>"
98
+ </strong>.
99
+ <br>
100
+ You will notice this new name in the admin bar:
101
+ <br><br>
102
+ <img src="<?php echo $this->url . "/img/admin_dashboard.png" ?>">
103
+ </p>
104
+ </li>
105
+ </ul>
106
+ </div>
107
+ </div>
108
+
109
+ <div id="wpstg-error-wrapper">
110
+ <div id="wpstg-error-details"></div>
111
+ </div>
112
+
113
  <div id="wpstg-log-details"></div>
apps/Backend/views/clone/ajax/update.php CHANGED
@@ -1,46 +1,46 @@
1
- <div class=successfullying-section">
2
- <?php echo __("Copy Database Tables", "wpstg")?>
3
- <div class="wpstg-progress-bar">
4
- <div class="wpstg-progress" id="wpstg-db-progress" style="width:0"></div>
5
- </div>
6
- </div>
7
-
8
- <div class="wpstg-cloning-section">
9
- <?php echo __("Prepare Directories", "wpstg")?>
10
- <div class="wpstg-progress-bar">
11
- <div class="wpstg-progress" id="wpstg-directories-progress" style="width:0"></div>
12
- </div>
13
- </div>
14
-
15
- <div class="wpstg-cloning-section">
16
- <?php echo __("Copy Files", "wpstg")?>
17
- <div class="wpstg-progress-bar">
18
- <div class="wpstg-progress" id="wpstg-files-progress" style="width:0"></div>
19
- </div>
20
- </div>
21
-
22
- <div class="wpstg-cloning-section">
23
- <?php echo __("Replace Data", "wpstg")?>
24
- <div class="wpstg-progress-bar">
25
- <div class="wpstg-progress" id="wpstg-links-progress" style="width:0"></div>
26
- </div>
27
- </div>
28
-
29
- <button type="button" id="wpstg-cancel-cloning-update" class="wpstg-link-btn button-primary">
30
- <?php echo __("Cancel Update", "wpstg")?>
31
- </button>
32
-
33
- <button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
34
- <?php _e('Display working log', 'wpstg')?>
35
- </button>
36
-
37
- <div>
38
- <span id="wpstg-cloning-result"></span>
39
- </div>
40
-
41
-
42
- <div id="wpstg-error-wrapper">
43
- <div id="wpstg-error-details"></div>
44
- </div>
45
-
46
  <div id="wpstg-log-details"></div>
1
+ <div class=successfullying-section">
2
+ <?php echo __("Copy Database Tables", "wpstg")?>
3
+ <div class="wpstg-progress-bar">
4
+ <div class="wpstg-progress" id="wpstg-db-progress" style="width:0"></div>
5
+ </div>
6
+ </div>
7
+
8
+ <div class="wpstg-cloning-section">
9
+ <?php echo __("Prepare Directories", "wpstg")?>
10
+ <div class="wpstg-progress-bar">
11
+ <div class="wpstg-progress" id="wpstg-directories-progress" style="width:0"></div>
12
+ </div>
13
+ </div>
14
+
15
+ <div class="wpstg-cloning-section">
16
+ <?php echo __("Copy Files", "wpstg")?>
17
+ <div class="wpstg-progress-bar">
18
+ <div class="wpstg-progress" id="wpstg-files-progress" style="width:0"></div>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="wpstg-cloning-section">
23
+ <?php echo __("Replace Data", "wpstg")?>
24
+ <div class="wpstg-progress-bar">
25
+ <div class="wpstg-progress" id="wpstg-links-progress" style="width:0"></div>
26
+ </div>
27
+ </div>
28
+
29
+ <button type="button" id="wpstg-cancel-cloning-update" class="wpstg-link-btn button-primary">
30
+ <?php echo __("Cancel Update", "wpstg")?>
31
+ </button>
32
+
33
+ <button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
34
+ <?php _e('Display working log', 'wpstg')?>
35
+ </button>
36
+
37
+ <div>
38
+ <span id="wpstg-cloning-result"></span>
39
+ </div>
40
+
41
+
42
+ <div id="wpstg-error-wrapper">
43
+ <div id="wpstg-error-details"></div>
44
+ </div>
45
+
46
  <div id="wpstg-log-details"></div>
apps/Backend/views/settings/index.php CHANGED
@@ -99,7 +99,7 @@
99
  To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it
100
  until you get no more errors during copying process.
101
  <br>
102
- <strong> Default: 1000 </strong>
103
  </span>
104
  </div>
105
  </td>
99
  To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it
100
  until you get no more errors during copying process.
101
  <br>
102
+ <strong> Default: 5000 </strong>
103
  </span>
104
  </div>
105
  </td>
apps/Core/Iterators/RecursiveDirectoryIterator.php CHANGED
@@ -1,52 +1,52 @@
1
- <?php
2
-
3
- namespace WPStaging\Iterators;
4
-
5
- // No Direct Access
6
- if (!defined("WPINC")) {
7
- die;
8
- }
9
-
10
- class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator {
11
-
12
- protected $exclude = array();
13
-
14
- public function __construct( $path ) {
15
- parent::__construct( $path );
16
-
17
- // Skip current and parent directory
18
- $this->skipdots();
19
- }
20
-
21
- public function rewind() {
22
- parent::rewind();
23
-
24
- // Skip current and parent directory
25
- $this->skipdots();
26
- }
27
-
28
- public function next() {
29
- parent::next();
30
-
31
- // Skip current and parent directory
32
- $this->skipdots();
33
- }
34
-
35
- /**
36
- * Returns whether current entry is a directory and not '.' or '..'
37
- *
38
- * Explicitly set allow links flag, because RecursiveDirectoryIterator::FOLLOW_SYMLINKS
39
- * is not supported by <= PHP 5.3.0
40
- *
41
- * @return bool
42
- */
43
- public function hasChildren( $allow_links = true ) {
44
- return parent::hasChildren( $allow_links );
45
- }
46
-
47
- protected function skipdots() {
48
- while ( $this->isDot() ) {
49
- parent::next();
50
- }
51
- }
52
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Iterators;
4
+
5
+ // No Direct Access
6
+ if (!defined("WPINC")) {
7
+ die;
8
+ }
9
+
10
+ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator {
11
+
12
+ protected $exclude = array();
13
+
14
+ public function __construct( $path ) {
15
+ parent::__construct( $path );
16
+
17
+ // Skip current and parent directory
18
+ $this->skipdots();
19
+ }
20
+
21
+ public function rewind() {
22
+ parent::rewind();
23
+
24
+ // Skip current and parent directory
25
+ $this->skipdots();
26
+ }
27
+
28
+ public function next() {
29
+ parent::next();
30
+
31
+ // Skip current and parent directory
32
+ $this->skipdots();
33
+ }
34
+
35
+ /**
36
+ * Returns whether current entry is a directory and not '.' or '..'
37
+ *
38
+ * Explicitly set allow links flag, because RecursiveDirectoryIterator::FOLLOW_SYMLINKS
39
+ * is not supported by <= PHP 5.3.0
40
+ *
41
+ * @return bool
42
+ */
43
+ public function hasChildren( $allow_links = true ) {
44
+ return parent::hasChildren( $allow_links );
45
+ }
46
+
47
+ protected function skipdots() {
48
+ while ( $this->isDot() ) {
49
+ parent::next();
50
+ }
51
+ }
52
+ }
apps/Core/Iterators/RecursiveFilterExclude.php CHANGED
@@ -1,23 +1,23 @@
1
- <?php
2
-
3
- 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
- }
1
+ <?php
2
+
3
+ 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
+ }
apps/Core/Iterators/RecursiveFilterNewLine.php CHANGED
@@ -1,21 +1,21 @@
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
-
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/Cache.php CHANGED
@@ -1,212 +1,212 @@
1
- <?php
2
- namespace WPStaging\Utils;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
- use WPStaging\WPStaging;
11
- use WPStaging\Backend\Modules\Jobs;
12
-
13
- /**
14
- * Class Cache
15
- * @package WPStaging\Utils
16
- */
17
- class Cache
18
- {
19
- /**
20
- * Cache directory (full path)
21
- * @var string
22
- */
23
- private $cacheDir;
24
-
25
- /**
26
- * Cache file extension
27
- * @var string
28
- */
29
- private $cacheExtension = "cache";
30
-
31
- /**
32
- * Lifetime of cache files in seconds
33
- * @var int
34
- */
35
- private $lifetime = 2592000; // 30 days
36
-
37
- /**
38
- * Cache constructor.
39
- * @param null|int $lifetime
40
- * @param null|string $cacheDir
41
- * @param null|string $cacheExtension
42
- * @throws \Exception
43
- */
44
- public function __construct($lifetime = null, $cacheDir = null, $cacheExtension = null)
45
- {
46
- // Set lifetime
47
- $lifetime = (int) $lifetime;
48
- if ($lifetime > 0)
49
- {
50
- $this->lifetime = $lifetime;
51
- }
52
-
53
- // Set cache directory
54
- if (!empty($cacheDir) && is_dir($cacheDir))
55
- {
56
- $this->cacheDir = $cacheDir;
57
- }
58
- // Set default
59
- else
60
- {
61
-
62
- $this->cacheDir = \WPStaging\WPStaging::getContentDir();
63
- }
64
-
65
- // Set cache extension
66
- if (!empty($cacheExtension))
67
- {
68
- $this->cacheExtension = $cacheExtension;
69
- }
70
-
71
- // If cache directory doesn't exists, create it
72
- if (!is_dir($this->cacheDir) && !@mkdir($this->cacheDir, 0775, true))
73
- {
74
- throw new \Exception("Failed to create cache directory " . $this->cacheDir . '! Make sure folder permission is 755 and owner is correct. Should be www-data or similar.');
75
- }
76
- }
77
-
78
- /**
79
- * Get cache
80
- * @param string $cacheFileName
81
- * @param mixed $defaultValue
82
- * @param null|int $lifetime
83
- * @return mixed|null
84
- */
85
- public function get($cacheFileName, $defaultValue = null, $lifetime = null)
86
- {
87
- // Check if file is valid
88
- if (false === ($cacheFile = $this->isValid($cacheFileName, true, $lifetime)))
89
- {
90
- return $defaultValue;
91
- }
92
-
93
- return @unserialize(file_get_contents($cacheFile));
94
- }
95
-
96
- /**
97
- * Saves value to given cache file
98
- * @param string $cacheFileName
99
- * @param mixed $value
100
- * @return bool
101
- * @throws \Exception
102
- */
103
- public function save($cacheFileName, $value)
104
- {
105
- $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
106
-
107
- // Attempt to delete cache file if it exists
108
- if (is_file($cacheFile) && !@unlink($cacheFile))
109
- {
110
- $this->returnException("Can't delete existing cache file");
111
- throw new \Exception("Can't delete existing cache file");
112
- }
113
-
114
- // Save it to file
115
- return (file_put_contents($cacheFile, @serialize($value), LOCK_EX) !== false);
116
- //return (file_put_contents($cacheFile, @serialize($value)) !== false);
117
- }
118
-
119
- /**
120
- * Throw a errror message via json and stop further execution
121
- * @param string $message
122
- */
123
- protected function returnException($message = ''){
124
- wp_die( json_encode(array(
125
- 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
126
- 'status' => false,
127
- 'message' => $message,
128
- 'error' => true
129
- )));
130
- }
131
-
132
- /**
133
- * Checks if file is valid or not
134
- * @param $cacheFileName
135
- * @param bool $deleteFileIfInvalid
136
- * @param null|int $lifetime
137
- * @return string|bool
138
- * @throws \Exception
139
- */
140
- public function isValid($cacheFileName, $deleteFileIfInvalid = false, $lifetime = null)
141
- {
142
- // Lifetime
143
- $lifetime = (int) $lifetime;
144
- if (-1 > $lifetime || 0 == $lifetime)
145
- {
146
- $lifetime = $this->lifetime;
147
- }
148
-
149
- // Get full path of the given cache file
150
- $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
151
-
152
- // File doesn't exist
153
- if (!is_file($cacheFile))
154
- {
155
- return false;
156
- }
157
-
158
- // As long as file exists, don't check lifetime
159
- if (-1 == $lifetime)
160
- {
161
- return $cacheFile;
162
- }
163
-
164
- // Time is up, file is invalid
165
- if (time() - filemtime($cacheFile) >= $lifetime)
166
- {
167
-
168
- // Attempt to delete the file
169
- if ($deleteFileIfInvalid === true && !@unlink($cacheFile))
170
- {
171
- throw new \Exception("Attempting to delete invalid cache file has failed!");
172
- }
173
-
174
- // No need to delete the file, return
175
- return false;
176
- }
177
-
178
- return $cacheFile;
179
- }
180
-
181
- /**
182
- * Delete a cache file
183
- * @param string $cacheFileName
184
- * @return bool
185
- * @throws \Exception
186
- */
187
- public function delete($cacheFileName)
188
- {
189
- if (false !== ($cacheFile = $this->isValid($cacheFileName, true)) && false === @unlink($cacheFile))
190
- {
191
- throw new \Exception("Couldn't delete cache: {$cacheFileName}. Full Path: {$cacheFile}");
192
- }
193
-
194
- return true;
195
- }
196
-
197
- /**
198
- * @return string
199
- */
200
- public function getCacheDir()
201
- {
202
- return $this->cacheDir;
203
- }
204
-
205
- /**
206
- * @return string
207
- */
208
- public function getCacheExtension()
209
- {
210
- return $this->cacheExtension;
211
- }
212
  }
1
+ <?php
2
+ namespace WPStaging\Utils;
3
+
4
+ // No Direct Access
5
+ if (!defined("WPINC"))
6
+ {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\WPStaging;
11
+ use WPStaging\Backend\Modules\Jobs;
12
+
13
+ /**
14
+ * Class Cache
15
+ * @package WPStaging\Utils
16
+ */
17
+ class Cache
18
+ {
19
+ /**
20
+ * Cache directory (full path)
21
+ * @var string
22
+ */
23
+ private $cacheDir;
24
+
25
+ /**
26
+ * Cache file extension
27
+ * @var string
28
+ */
29
+ private $cacheExtension = "cache";
30
+
31
+ /**
32
+ * Lifetime of cache files in seconds
33
+ * @var int
34
+ */
35
+ private $lifetime = 2592000; // 30 days
36
+
37
+ /**
38
+ * Cache constructor.
39
+ * @param null|int $lifetime
40
+ * @param null|string $cacheDir
41
+ * @param null|string $cacheExtension
42
+ * @throws \Exception
43
+ */
44
+ public function __construct($lifetime = null, $cacheDir = null, $cacheExtension = null)
45
+ {
46
+ // Set lifetime
47
+ $lifetime = (int) $lifetime;
48
+ if ($lifetime > 0)
49
+ {
50
+ $this->lifetime = $lifetime;
51
+ }
52
+
53
+ // Set cache directory
54
+ if (!empty($cacheDir) && is_dir($cacheDir))
55
+ {
56
+ $this->cacheDir = $cacheDir;
57
+ }
58
+ // Set default
59
+ else
60
+ {
61
+
62
+ $this->cacheDir = \WPStaging\WPStaging::getContentDir();
63
+ }
64
+
65
+ // Set cache extension
66
+ if (!empty($cacheExtension))
67
+ {
68
+ $this->cacheExtension = $cacheExtension;
69
+ }
70
+
71
+ // If cache directory doesn't exists, create it
72
+ if (!is_dir($this->cacheDir) && !@mkdir($this->cacheDir, 0775, true))
73
+ {
74
+ throw new \Exception("Failed to create cache directory " . $this->cacheDir . '! Make sure folder permission is 755 and owner is correct. Should be www-data or similar.');
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Get cache
80
+ * @param string $cacheFileName
81
+ * @param mixed $defaultValue
82
+ * @param null|int $lifetime
83
+ * @return mixed|null
84
+ */
85
+ public function get($cacheFileName, $defaultValue = null, $lifetime = null)
86
+ {
87
+ // Check if file is valid
88
+ if (false === ($cacheFile = $this->isValid($cacheFileName, true, $lifetime)))
89
+ {
90
+ return $defaultValue;
91
+ }
92
+
93
+ return @unserialize(file_get_contents($cacheFile));
94
+ }
95
+
96
+ /**
97
+ * Saves value to given cache file
98
+ * @param string $cacheFileName
99
+ * @param mixed $value
100
+ * @return bool
101
+ * @throws \Exception
102
+ */
103
+ public function save($cacheFileName, $value)
104
+ {
105
+ $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
106
+
107
+ // Attempt to delete cache file if it exists
108
+ if (is_file($cacheFile) && !@unlink($cacheFile))
109
+ {
110
+ $this->returnException("Can't delete existing cache file");
111
+ throw new \Exception("Can't delete existing cache file");
112
+ }
113
+
114
+ // Save it to file
115
+ return (file_put_contents($cacheFile, @serialize($value), LOCK_EX) !== false);
116
+ //return (file_put_contents($cacheFile, @serialize($value)) !== false);
117
+ }
118
+
119
+ /**
120
+ * Throw a errror message via json and stop further execution
121
+ * @param string $message
122
+ */
123
+ protected function returnException($message = ''){
124
+ wp_die( json_encode(array(
125
+ 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
126
+ 'status' => false,
127
+ 'message' => $message,
128
+ 'error' => true
129
+ )));
130
+ }
131
+
132
+ /**
133
+ * Checks if file is valid or not
134
+ * @param $cacheFileName
135
+ * @param bool $deleteFileIfInvalid
136
+ * @param null|int $lifetime
137
+ * @return string|bool
138
+ * @throws \Exception
139
+ */
140
+ public function isValid($cacheFileName, $deleteFileIfInvalid = false, $lifetime = null)
141
+ {
142
+ // Lifetime
143
+ $lifetime = (int) $lifetime;
144
+ if (-1 > $lifetime || 0 == $lifetime)
145
+ {
146
+ $lifetime = $this->lifetime;
147
+ }
148
+
149
+ // Get full path of the given cache file
150
+ $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
151
+
152
+ // File doesn't exist
153
+ if (!is_file($cacheFile))
154
+ {
155
+ return false;
156
+ }
157
+
158
+ // As long as file exists, don't check lifetime
159
+ if (-1 == $lifetime)
160
+ {
161
+ return $cacheFile;
162
+ }
163
+
164
+ // Time is up, file is invalid
165
+ if (time() - filemtime($cacheFile) >= $lifetime)
166
+ {
167
+
168
+ // Attempt to delete the file
169
+ if ($deleteFileIfInvalid === true && !@unlink($cacheFile))
170
+ {
171
+ throw new \Exception("Attempting to delete invalid cache file has failed!");
172
+ }
173
+
174
+ // No need to delete the file, return
175
+ return false;
176
+ }
177
+
178
+ return $cacheFile;
179
+ }
180
+
181
+ /**
182
+ * Delete a cache file
183
+ * @param string $cacheFileName
184
+ * @return bool
185
+ * @throws \Exception
186
+ */
187
+ public function delete($cacheFileName)
188
+ {
189
+ if (false !== ($cacheFile = $this->isValid($cacheFileName, true)) && false === @unlink($cacheFile))
190
+ {
191
+ throw new \Exception("Couldn't delete cache: {$cacheFileName}. Full Path: {$cacheFile}");
192
+ }
193
+
194
+ return true;
195
+ }
196
+
197
+ /**
198
+ * @return string
199
+ */
200
+ public function getCacheDir()
201
+ {
202
+ return $this->cacheDir;
203
+ }
204
+
205
+ /**
206
+ * @return string
207
+ */
208
+ public function getCacheExtension()
209
+ {
210
+ return $this->cacheExtension;
211
+ }
212
  }
apps/Core/Utils/Strings.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Utils;
4
+
5
+ // No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
+ die;
8
+ }
9
+
10
+ class Strings{
11
+
12
+ /**
13
+ * Replace first occurance of certain string
14
+ * @param string $search
15
+ * @param string $replace
16
+ * @param string $subject
17
+ * @return string
18
+ */
19
+ public function str_replace_first( $search, $replace, $subject ) {
20
+ $pos = strpos( $subject, $search );
21
+ if( $pos !== false ) {
22
+ return substr_replace( $subject, $replace, $pos, strlen( $search ) );
23
+ }
24
+ return $subject;
25
+ }
26
+
27
+ }
apps/Core/WPStaging.php CHANGED
@@ -1,429 +1,429 @@
1
- <?php
2
-
3
- namespace WPStaging;
4
-
5
- // No Direct Access
6
- if( !defined( "WPINC" ) ) {
7
- die;
8
- }
9
-
10
- // Ensure to include autoloader class
11
- require_once __DIR__ . DIRECTORY_SEPARATOR . "Utils" . DIRECTORY_SEPARATOR . "Autoloader.php";
12
-
13
- use WPStaging\Backend\Administrator;
14
- use WPStaging\DTO\Settings;
15
- use WPStaging\Frontend\Frontend;
16
- use WPStaging\Utils\Autoloader;
17
- use WPStaging\Utils\Cache;
18
- use WPStaging\Utils\Loader;
19
- use WPStaging\Utils\Logger;
20
- use WPStaging\DI\InjectionAware;
21
- use WPStaging\Cron\Cron;
22
-
23
- /**
24
- * Class WPStaging
25
- * @package WPStaging
26
- */
27
- final class WPStaging {
28
-
29
- /**
30
- * Plugin version
31
- */
32
- const VERSION = "2.1.8";
33
-
34
- /**
35
- * Plugin name
36
- */
37
- const NAME = "WP Staging";
38
-
39
- /**
40
- * Plugin slug
41
- */
42
- const SLUG = "wp-staging";
43
-
44
- /**
45
- * Compatible WP Version
46
- */
47
- const WP_COMPATIBLE = "4.9.5";
48
-
49
- /**
50
- * Slug: Either wp-staging or wp-staging-pro
51
- * @var string
52
- */
53
- public $slug;
54
-
55
- /**
56
- * Absolute plugin path
57
- * @var string
58
- */
59
- public $pluginPath;
60
-
61
- /**
62
- * Services
63
- * @var array
64
- */
65
- private $services;
66
-
67
- /**
68
- * Singleton instance
69
- * @var WPStaging
70
- */
71
- private static $instance;
72
-
73
- /**
74
- * WPStaging constructor.
75
- */
76
- private function __construct() {
77
-
78
- $file = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR . self::SLUG . ".php";
79
-
80
- $this->registerMain();
81
- $this->registerNamespaces();
82
- $this->loadLanguages();
83
- $this->loadDependencies();
84
- $this->defineHooks();
85
- // Licensing stuff be loaded in wpstg core to make cron hook available from frontpage
86
- $this->initLicensing();
87
- }
88
-
89
- /**
90
- * Method to be executed upon activation of the plugin
91
- */
92
- public function onActivation() {
93
- $Activation = new \WPStaging\Backend\Activation\Activation();
94
- $Activation->install_dependancies();
95
- }
96
-
97
- public function registerMain() {
98
- // Slug of the plugin
99
- $this->slug = plugin_basename( dirname( dirname( dirname( __FILE__ ) ) ) );
100
-
101
- // absolute path to the main plugin dir
102
- $this->pluginPath = plugin_dir_path( dirname( dirname( __FILE__ ) ) );
103
-
104
- // URL to apps folder
105
- $this->url = plugin_dir_url( dirname( __FILE__ ) );
106
-
107
- // URL to backend public folder folder
108
- $this->backend_url = plugin_dir_url( dirname( __FILE__ ) ) . "Backend/public/";
109
-
110
- // URL to frontend public folder folder
111
- $this->frontend_url = plugin_dir_url( dirname( __FILE__ ) ) . "Frontend/public/";
112
- }
113
-
114
- /**
115
- * Define Hooks
116
- */
117
- public function defineHooks() {
118
- $loader = $this->get( "loader" );
119
- $loader->addAction( "admin_enqueue_scripts", $this, "enqueueElements", 100 );
120
- $loader->addAction( "wp_enqueue_scripts", $this, "enqueueElements", 100 );
121
- $this->addIntervals();
122
- }
123
-
124
- /**
125
- * Add new cron time event "weekly"
126
- */
127
- public function addIntervals(){
128
- $interval = new Cron();
129
- }
130
-
131
- /**
132
- * Scripts and Styles
133
- * @param string $hook
134
- */
135
- public function enqueueElements( $hook ) {
136
-
137
- // Load this css file on frontend and backend on all pages if current site is a staging site
138
- if( $this->isStagingSite() ) {
139
- wp_enqueue_style( "wpstg-admin-bar", $this->backend_url . "css/wpstg-admin-bar.css", $this->getVersion() );
140
- }
141
-
142
- $availablePages = array(
143
- "toplevel_page_wpstg_clone",
144
- "wp-staging_page_wpstg-settings",
145
- "wp-staging_page_wpstg-tools",
146
- "wp-staging_page_wpstg-license",
147
- "wp-staging_page_wpstg-welcome",
148
- );
149
-
150
- // Load these css and js files only on wp staging admin pages
151
- if( !in_array( $hook, $availablePages ) || !is_admin() ) {
152
- return;
153
- }
154
-
155
- // Load admin js files
156
- wp_enqueue_script(
157
- "wpstg-admin-script", $this->backend_url . "js/wpstg-admin.js", array("jquery"), $this->getVersion(), false
158
- );
159
-
160
- // Load admin css files
161
- wp_enqueue_style(
162
- "wpstg-admin", $this->backend_url . "css/wpstg-admin.css", $this->getVersion()
163
- );
164
-
165
- wp_localize_script( "wpstg-admin-script", "wpstg", array(
166
- "nonce" => wp_create_nonce( "wpstg_ajax_nonce" ),
167
- "mu_plugin_confirmation" => __(
168
- "If confirmed we will install an additional WordPress 'Must Use' plugin. "
169
- . "This plugin will allow us to control which plugins are loaded during "
170
- . "WP Staging specific operations. Do you wish to continue?", "wpstg"
171
- ),
172
- "plugin_compatibility_settings_problem" => __(
173
- "A problem occurred when trying to change the plugin compatibility setting.", "wpstg"
174
- ),
175
- "saved" => __( "Saved", "The settings were saved successfully", "wpstg" ),
176
- "status" => __( "Status", "Current request status", "wpstg" ),
177
- "response" => __( "Response", "The message the server responded with", "wpstg" ),
178
- "blacklist_problem" => __(
179
- "A problem occurred when trying to add plugins to backlist.", "wpstg"
180
- ),
181
- "cpuLoad" => $this->getCPULoadSetting(),
182
- "settings" => ( object ) array(), // TODO add settings?
183
- "tblprefix" => self::getTablePrefix()
184
- ) );
185
- }
186
-
187
- /**
188
- * Get table prefix of the current site
189
- * @return string
190
- */
191
- public static function getTablePrefix(){
192
- $wpDB = WPStaging::getInstance()->get("wpdb");
193
- return $wpDB->prefix;
194
- }
195
-
196
- /**
197
- * Caching and logging folder
198
- *
199
- * @return string
200
- */
201
- public static function getContentDir() {
202
- $wp_upload_dir = wp_upload_dir();
203
- $path = $wp_upload_dir['basedir'] . '/wp-staging';
204
- wp_mkdir_p( $path );
205
- return apply_filters( 'wpstg_get_upload_dir', $path . DIRECTORY_SEPARATOR );
206
- }
207
-
208
- /**
209
- * Register used namespaces
210
- */
211
- private function registerNamespaces() {
212
- $autoloader = new Autoloader();
213
- $this->set( "autoloader", $autoloader );
214
-
215
- // Autoloader
216
- $autoloader->registerNamespaces( array(
217
- "WPStaging" => array(
218
- $this->pluginPath . 'apps' . DIRECTORY_SEPARATOR,
219
- $this->pluginPath . 'apps' . DIRECTORY_SEPARATOR . 'Core' . DIRECTORY_SEPARATOR,
220
- )
221
- ) );
222
-
223
- // Register namespaces
224
- $autoloader->register();
225
- }
226
-
227
- /**
228
- * Get Instance
229
- * @return WPStaging
230
- */
231
- public static function getInstance() {
232
- if( null === static::$instance ) {
233
- static::$instance = new static();
234
- }
235
-
236
- return static::$instance;
237
- }
238
-
239
- /**
240
- * Prevent cloning
241
- * @return void
242
- */
243
- private function __clone() {
244
-
245
- }
246
-
247
- /**
248
- * Prevent unserialization
249
- * @return void
250
- */
251
- private function __wakeup() {
252
-
253
- }
254
-
255
- /**
256
- * Load Dependencies
257
- */
258
- private function loadDependencies() {
259
- // Set loader
260
- $this->set( "loader", new Loader() );
261
-
262
- // Set cache
263
- $this->set( "cache", new Cache() );
264
-
265
- // Set logger
266
- $this->set( "logger", new Logger() );
267
-
268
- // Set settings
269
- $this->set( "settings", new Settings() );
270
-
271
- // Set Administrator
272
- if( is_admin() ) {
273
- new Administrator( $this );
274
- } else {
275
- new Frontend( $this );
276
- }
277
- }
278
-
279
- /**
280
- * Execute Plugin
281
- */
282
- public function run() {
283
- $this->get( "loader" )->run();
284
- }
285
-
286
- /**
287
- * Set a variable to DI with given name
288
- * @param string $name
289
- * @param mixed $variable
290
- * @return $this
291
- */
292
- public function set( $name, $variable ) {
293
- // It is a function
294
- if( is_callable( $variable ) )
295
- $variable = $variable();
296
-
297
- // Add it to services
298
- $this->services[$name] = $variable;
299
-
300
- return $this;
301
- }
302
-
303
- /**
304
- * Get given name index from DI
305
- * @param string $name
306
- * @return mixed|null
307
- */
308
- public function get( $name ) {
309
- return (isset( $this->services[$name] )) ? $this->services[$name] : null;
310
- }
311
-
312
- /**
313
- * @return string
314
- */
315
- public function getVersion() {
316
- return self::VERSION;
317
- }
318
-
319
- /**
320
- * @return string
321
- */
322
- public function getName() {
323
- return self::NAME;
324
- }
325
-
326
- /**
327
- * @return string
328
- */
329
- public static function getSlug() {
330
- return plugin_basename( dirname( dirname( dirname( __FILE__ ) ) ) );
331
- }
332
-
333
- /**
334
- * Get path to main plugin file
335
- * @return string
336
- */
337
- public function getPath() {
338
- return dirname( dirname( __FILE__ ) );
339
- }
340
-
341
- /**
342
- * Get main plugin url
343
- * @return type
344
- */
345
- public function getUrl() {
346
- return plugin_dir_url( dirname( __FILE__ ) );
347
- }
348
-
349
- /**
350
- * @return array|mixed|object
351
- */
352
- public function getCPULoadSetting() {
353
- $options = $this->get( "settings" );
354
- $setting = $options->getCpuLoad();
355
-
356
- switch ( $setting ) {
357
- case "high":
358
- $cpuLoad = 0;
359
- break;
360
-
361
- case "medium":
362
- $cpuLoad = 1000;
363
- break;
364
-
365
- case "low":
366
- $cpuLoad = 3000;
367
- break;
368
-
369
- case "default":
370
- default:
371
- $cpuLoad = 1000;
372
- }
373
-
374
- return $cpuLoad;
375
- }
376
-
377
- /**
378
- * Load language file
379
- */
380
- public function loadLanguages() {
381
- $languagesDirectory = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR;
382
- $languagesDirectory.= "vars" . DIRECTORY_SEPARATOR . "languages" . DIRECTORY_SEPARATOR;
383
-
384
- // Set filter for plugins languages directory
385
- $languagesDirectory = apply_filters( "wpstg_languages_directory", $languagesDirectory );
386
-
387
- // Traditional WP plugin locale filter
388
- $locale = apply_filters( "plugin_locale", get_locale(), "wpstg" );
389
- $moFile = sprintf( '%1$s-%2$s.mo', "wpstg", $locale );
390
-
391
- // Setup paths to current locale file
392
- $moFileLocal = $languagesDirectory . $moFile;
393
- $moFileGlobal = WP_LANG_DIR . DIRECTORY_SEPARATOR . "wpstg" . DIRECTORY_SEPARATOR . $moFile;
394
-
395
- // Global file (/wp-content/languages/WPSTG)
396
- if( file_exists( $moFileGlobal ) ) {
397
- load_textdomain( "wpstg", $moFileGlobal );
398
- }
399
- // Local file (/wp-content/plugins/wp-staging/languages/)
400
- elseif( file_exists( $moFileLocal ) ) {
401
- load_textdomain( "wpstg", $moFileGlobal );
402
- }
403
- // Default file
404
- else {
405
- load_plugin_textdomain( "wpstg", false, $languagesDirectory );
406
- }
407
- }
408
-
409
- /**
410
- * Check if it is a staging site
411
- * @return bool
412
- */
413
- private function isStagingSite() {
414
- return ("true" === get_option( "wpstg_is_staging_site" ));
415
- }
416
-
417
- /**
418
- * Initialize licensing functions
419
- * @return boolean
420
- */
421
- public function initLicensing() {
422
- // Add licensing stuff if class exists
423
- if( class_exists( 'WPStaging\Backend\Pro\Licensing\Licensing' ) ) {
424
- $licensing = new Backend\Pro\Licensing\Licensing();
425
- }
426
- return false;
427
- }
428
-
429
- }
1
+ <?php
2
+
3
+ namespace WPStaging;
4
+
5
+ // No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
+ die;
8
+ }
9
+
10
+ // Ensure to include autoloader class
11
+ require_once __DIR__ . DIRECTORY_SEPARATOR . "Utils" . DIRECTORY_SEPARATOR . "Autoloader.php";
12
+
13
+ use WPStaging\Backend\Administrator;
14
+ use WPStaging\DTO\Settings;
15
+ use WPStaging\Frontend\Frontend;
16
+ use WPStaging\Utils\Autoloader;
17
+ use WPStaging\Utils\Cache;
18
+ use WPStaging\Utils\Loader;
19
+ use WPStaging\Utils\Logger;
20
+ use WPStaging\DI\InjectionAware;
21
+ use WPStaging\Cron\Cron;
22
+
23
+ /**
24
+ * Class WPStaging
25
+ * @package WPStaging
26
+ */
27
+ final class WPStaging {
28
+
29
+ /**
30
+ * Plugin version
31
+ */
32
+ const VERSION = "2.2.0";
33
+
34
+ /**
35
+ * Plugin name
36
+ */
37
+ const NAME = "WP Staging";
38
+
39
+ /**
40
+ * Plugin slug
41
+ */
42
+ const SLUG = "wp-staging";
43
+
44
+ /**
45
+ * Compatible WP Version
46
+ */
47
+ const WP_COMPATIBLE = "4.9.4";
48
+
49
+ /**
50
+ * Slug: Either wp-staging or wp-staging-pro
51
+ * @var string
52
+ */
53
+ public $slug;
54
+
55
+ /**
56
+ * Absolute plugin path
57
+ * @var string
58
+ */
59
+ public $pluginPath;
60
+
61
+ /**
62
+ * Services
63
+ * @var array
64
+ */
65
+ private $services;
66
+
67
+ /**
68
+ * Singleton instance
69
+ * @var WPStaging
70
+ */
71
+ private static $instance;
72
+
73
+ /**
74
+ * WPStaging constructor.
75
+ */
76
+ private function __construct() {
77
+
78
+ $file = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR . self::SLUG . ".php";
79
+
80
+ $this->registerMain();
81
+ $this->registerNamespaces();
82
+ $this->loadLanguages();
83
+ $this->loadDependencies();
84
+ $this->defineHooks();
85
+ // Licensing stuff be loaded in wpstg core to make cron hook available from frontpage
86
+ $this->initLicensing();
87
+ }
88
+
89
+ /**
90
+ * Method to be executed upon activation of the plugin
91
+ */
92
+ public function onActivation() {
93
+ $Activation = new \WPStaging\Backend\Activation\Activation();
94
+ $Activation->install_dependancies();
95
+ }
96
+
97
+ public function registerMain() {
98
+ // Slug of the plugin
99
+ $this->slug = plugin_basename( dirname( dirname( dirname( __FILE__ ) ) ) );
100
+
101
+ // absolute path to the main plugin dir
102
+ $this->pluginPath = plugin_dir_path( dirname( dirname( __FILE__ ) ) );
103
+
104
+ // URL to apps folder
105
+ $this->url = plugin_dir_url( dirname( __FILE__ ) );
106
+
107
+ // URL to backend public folder folder
108
+ $this->backend_url = plugin_dir_url( dirname( __FILE__ ) ) . "Backend/public/";
109
+
110
+ // URL to frontend public folder folder
111
+ $this->frontend_url = plugin_dir_url( dirname( __FILE__ ) ) . "Frontend/public/";
112
+ }
113
+
114
+ /**
115
+ * Define Hooks
116
+ */
117
+ public function defineHooks() {
118
+ $loader = $this->get( "loader" );
119
+ $loader->addAction( "admin_enqueue_scripts", $this, "enqueueElements", 100 );
120
+ $loader->addAction( "wp_enqueue_scripts", $this, "enqueueElements", 100 );
121
+ $this->addIntervals();
122
+ }
123
+
124
+ /**
125
+ * Add new cron time event "weekly"
126
+ */
127
+ public function addIntervals(){
128
+ $interval = new Cron();
129
+ }
130
+
131
+ /**
132
+ * Scripts and Styles
133
+ * @param string $hook
134
+ */
135
+ public function enqueueElements( $hook ) {
136
+
137
+ // Load this css file on frontend and backend on all pages if current site is a staging site
138
+ if( $this->isStagingSite() ) {
139
+ wp_enqueue_style( "wpstg-admin-bar", $this->backend_url . "css/wpstg-admin-bar.css", $this->getVersion() );
140
+ }
141
+
142
+ $availablePages = array(
143
+ "toplevel_page_wpstg_clone",
144
+ "wp-staging_page_wpstg-settings",
145
+ "wp-staging_page_wpstg-tools",
146
+ "wp-staging_page_wpstg-license",
147
+ "wp-staging_page_wpstg-welcome",
148
+ );
149
+
150
+ // Load these css and js files only on wp staging admin pages
151
+ if( !in_array( $hook, $availablePages ) || !is_admin() ) {
152
+ return;
153
+ }
154
+
155
+ // Load admin js files
156
+ wp_enqueue_script(
157
+ "wpstg-admin-script", $this->backend_url . "js/wpstg-admin.js", array("jquery"), $this->getVersion(), false
158
+ );
159
+
160
+ // Load admin css files
161
+ wp_enqueue_style(
162
+ "wpstg-admin", $this->backend_url . "css/wpstg-admin.css", $this->getVersion()
163
+ );
164
+
165
+ wp_localize_script( "wpstg-admin-script", "wpstg", array(
166
+ "nonce" => wp_create_nonce( "wpstg_ajax_nonce" ),
167
+ "mu_plugin_confirmation" => __(
168
+ "If confirmed we will install an additional WordPress 'Must Use' plugin. "
169
+ . "This plugin will allow us to control which plugins are loaded during "
170
+ . "WP Staging specific operations. Do you wish to continue?", "wpstg"
171
+ ),
172
+ "plugin_compatibility_settings_problem" => __(
173
+ "A problem occurred when trying to change the plugin compatibility setting.", "wpstg"
174
+ ),
175
+ "saved" => __( "Saved", "The settings were saved successfully", "wpstg" ),
176
+ "status" => __( "Status", "Current request status", "wpstg" ),
177
+ "response" => __( "Response", "The message the server responded with", "wpstg" ),
178
+ "blacklist_problem" => __(
179
+ "A problem occurred when trying to add plugins to backlist.", "wpstg"
180
+ ),
181
+ "cpuLoad" => $this->getCPULoadSetting(),
182
+ "settings" => ( object ) array(), // TODO add settings?
183
+ "tblprefix" => self::getTablePrefix()
184
+ ) );
185
+ }
186
+
187
+ /**
188
+ * Get table prefix of the current site
189
+ * @return string
190
+ */
191
+ public static function getTablePrefix(){
192
+ $wpDB = WPStaging::getInstance()->get("wpdb");
193
+ return $wpDB->prefix;
194
+ }
195
+
196
+ /**
197
+ * Caching and logging folder
198
+ *
199
+ * @return string
200
+ */
201
+ public static function getContentDir() {
202
+ $wp_upload_dir = wp_upload_dir();
203
+ $path = $wp_upload_dir['basedir'] . '/wp-staging';
204
+ wp_mkdir_p( $path );
205
+ return apply_filters( 'wpstg_get_upload_dir', $path . DIRECTORY_SEPARATOR );
206
+ }
207
+
208
+ /**
209
+ * Register used namespaces
210
+ */
211
+ private function registerNamespaces() {
212
+ $autoloader = new Autoloader();
213
+ $this->set( "autoloader", $autoloader );
214
+
215
+ // Autoloader
216
+ $autoloader->registerNamespaces( array(
217
+ "WPStaging" => array(
218
+ $this->pluginPath . 'apps' . DIRECTORY_SEPARATOR,
219
+ $this->pluginPath . 'apps' . DIRECTORY_SEPARATOR . 'Core' . DIRECTORY_SEPARATOR,
220
+ )
221
+ ) );
222
+
223
+ // Register namespaces
224
+ $autoloader->register();
225
+ }
226
+
227
+ /**
228
+ * Get Instance
229
+ * @return WPStaging
230
+ */
231
+ public static function getInstance() {
232
+ if( null === static::$instance ) {
233
+ static::$instance = new static();
234
+ }
235
+
236
+ return static::$instance;
237
+ }
238
+
239
+ /**
240
+ * Prevent cloning
241
+ * @return void
242
+ */
243
+ private function __clone() {
244
+
245
+ }
246
+
247
+ /**
248
+ * Prevent unserialization
249
+ * @return void
250
+ */
251
+ private function __wakeup() {
252
+
253
+ }
254
+
255
+ /**
256
+ * Load Dependencies
257
+ */
258
+ private function loadDependencies() {
259
+ // Set loader
260
+ $this->set( "loader", new Loader() );
261
+
262
+ // Set cache
263
+ $this->set( "cache", new Cache() );
264
+
265
+ // Set logger
266
+ $this->set( "logger", new Logger() );
267
+
268
+ // Set settings
269
+ $this->set( "settings", new Settings() );
270
+
271
+ // Set Administrator
272
+ if( is_admin() ) {
273
+ new Administrator( $this );
274
+ } else {
275
+ new Frontend( $this );
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Execute Plugin
281
+ */
282
+ public function run() {
283
+ $this->get( "loader" )->run();
284
+ }
285
+
286
+ /**
287
+ * Set a variable to DI with given name
288
+ * @param string $name
289
+ * @param mixed $variable
290
+ * @return $this
291
+ */
292
+ public function set( $name, $variable ) {
293
+ // It is a function
294
+ if( is_callable( $variable ) )
295
+ $variable = $variable();
296
+
297
+ // Add it to services
298
+ $this->services[$name] = $variable;
299
+
300
+ return $this;
301
+ }
302
+
303
+ /**
304
+ * Get given name index from DI
305
+ * @param string $name
306
+ * @return mixed|null
307
+ */
308
+ public function get( $name ) {
309
+ return (isset( $this->services[$name] )) ? $this->services[$name] : null;
310
+ }
311
+
312
+ /**
313
+ * @return string
314
+ */
315
+ public function getVersion() {
316
+ return self::VERSION;
317
+ }
318
+
319
+ /**
320
+ * @return string
321
+ */
322
+ public function getName() {
323
+ return self::NAME;
324
+ }
325
+
326
+ /**
327
+ * @return string
328
+ */
329
+ public static function getSlug() {
330
+ return plugin_basename( dirname( dirname( dirname( __FILE__ ) ) ) );
331
+ }
332
+
333
+ /**
334
+ * Get path to main plugin file
335
+ * @return string
336
+ */
337
+ public function getPath() {
338
+ return dirname( dirname( __FILE__ ) );
339
+ }
340
+
341
+ /**
342
+ * Get main plugin url
343
+ * @return type
344
+ */
345
+ public function getUrl() {
346
+ return plugin_dir_url( dirname( __FILE__ ) );
347
+ }
348
+
349
+ /**
350
+ * @return array|mixed|object
351
+ */
352
+ public function getCPULoadSetting() {
353
+ $options = $this->get( "settings" );
354
+ $setting = $options->getCpuLoad();
355
+
356
+ switch ( $setting ) {
357
+ case "high":
358
+ $cpuLoad = 0;
359
+ break;
360
+
361
+ case "medium":
362
+ $cpuLoad = 1000;
363
+ break;
364
+
365
+ case "low":
366
+ $cpuLoad = 3000;
367
+ break;
368
+
369
+ case "default":
370
+ default:
371
+ $cpuLoad = 1000;
372
+ }
373
+
374
+ return $cpuLoad;
375
+ }
376
+
377
+ /**
378
+ * Load language file
379
+ */
380
+ public function loadLanguages() {
381
+ $languagesDirectory = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR;
382
+ $languagesDirectory.= "vars" . DIRECTORY_SEPARATOR . "languages" . DIRECTORY_SEPARATOR;
383
+
384
+ // Set filter for plugins languages directory
385
+ $languagesDirectory = apply_filters( "wpstg_languages_directory", $languagesDirectory );
386
+
387
+ // Traditional WP plugin locale filter
388
+ $locale = apply_filters( "plugin_locale", get_locale(), "wpstg" );
389
+ $moFile = sprintf( '%1$s-%2$s.mo', "wpstg", $locale );
390
+
391
+ // Setup paths to current locale file
392
+ $moFileLocal = $languagesDirectory . $moFile;
393
+ $moFileGlobal = WP_LANG_DIR . DIRECTORY_SEPARATOR . "wpstg" . DIRECTORY_SEPARATOR . $moFile;
394
+
395
+ // Global file (/wp-content/languages/WPSTG)
396
+ if( file_exists( $moFileGlobal ) ) {
397
+ load_textdomain( "wpstg", $moFileGlobal );
398
+ }
399
+ // Local file (/wp-content/plugins/wp-staging/languages/)
400
+ elseif( file_exists( $moFileLocal ) ) {
401
+ load_textdomain( "wpstg", $moFileGlobal );
402
+ }
403
+ // Default file
404
+ else {
405
+ load_plugin_textdomain( "wpstg", false, $languagesDirectory );
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Check if it is a staging site
411
+ * @return bool
412
+ */
413
+ private function isStagingSite() {
414
+ return ("true" === get_option( "wpstg_is_staging_site" ));
415
+ }
416
+
417
+ /**
418
+ * Initialize licensing functions
419
+ * @return boolean
420
+ */
421
+ public function initLicensing() {
422
+ // Add licensing stuff if class exists
423
+ if( class_exists( 'WPStaging\Backend\Pro\Licensing\Licensing' ) ) {
424
+ $licensing = new Backend\Pro\Licensing\Licensing();
425
+ }
426
+ return false;
427
+ }
428
+
429
+ }
package-lock.json DELETED
@@ -1,3333 +0,0 @@
1
- {
2
- "name": "wp-staging",
3
- "version": "2.1.6",
4
- "lockfileVersion": 1,
5
- "requires": true,
6
- "dependencies": {
7
- "abbrev": {
8
- "version": "1.1.1",
9
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
10
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
11
- "dev": true
12
- },
13
- "ajv": {
14
- "version": "5.5.1",
15
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz",
16
- "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=",
17
- "dev": true,
18
- "optional": true,
19
- "requires": {
20
- "co": "4.6.0",
21
- "fast-deep-equal": "1.0.0",
22
- "fast-json-stable-stringify": "2.0.0",
23
- "json-schema-traverse": "0.3.1"
24
- }
25
- },
26
- "amdefine": {
27
- "version": "1.0.1",
28
- "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
29
- "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
30
- "dev": true
31
- },
32
- "ansi-regex": {
33
- "version": "0.2.1",
34
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
35
- "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
36
- "dev": true
37
- },
38
- "ansi-styles": {
39
- "version": "1.1.0",
40
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
41
- "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
42
- "dev": true
43
- },
44
- "aproba": {
45
- "version": "1.2.0",
46
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
47
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
48
- "dev": true
49
- },
50
- "archiver": {
51
- "version": "1.3.0",
52
- "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz",
53
- "integrity": "sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI=",
54
- "dev": true,
55
- "requires": {
56
- "archiver-utils": "1.3.0",
57
- "async": "2.6.0",
58
- "buffer-crc32": "0.2.13",
59
- "glob": "7.1.2",
60
- "lodash": "4.17.4",
61
- "readable-stream": "2.3.3",
62
- "tar-stream": "1.5.5",
63
- "walkdir": "0.0.11",
64
- "zip-stream": "1.2.0"
65
- },
66
- "dependencies": {
67
- "async": {
68
- "version": "2.6.0",
69
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
70
- "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
71
- "dev": true,
72
- "requires": {
73
- "lodash": "4.17.4"
74
- }
75
- },
76
- "glob": {
77
- "version": "7.1.2",
78
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
79
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
80
- "dev": true,
81
- "requires": {
82
- "fs.realpath": "1.0.0",
83
- "inflight": "1.0.6",
84
- "inherits": "2.0.3",
85
- "minimatch": "3.0.4",
86
- "once": "1.4.0",
87
- "path-is-absolute": "1.0.1"
88
- }
89
- },
90
- "lodash": {
91
- "version": "4.17.4",
92
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
93
- "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
94
- "dev": true
95
- },
96
- "minimatch": {
97
- "version": "3.0.4",
98
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
99
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
100
- "dev": true,
101
- "requires": {
102
- "brace-expansion": "1.1.8"
103
- }
104
- }
105
- }
106
- },
107
- "archiver-utils": {
108
- "version": "1.3.0",
109
- "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz",
110
- "integrity": "sha1-5QtMCccL89aA4y/xt5lOn52JUXQ=",
111
- "dev": true,
112
- "requires": {
113
- "glob": "7.1.2",
114
- "graceful-fs": "4.1.11",
115
- "lazystream": "1.0.0",
116
- "lodash": "4.17.4",
117
- "normalize-path": "2.1.1",
118
- "readable-stream": "2.3.3"
119
- },
120
- "dependencies": {
121
- "glob": {
122
- "version": "7.1.2",
123
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
124
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
125
- "dev": true,
126
- "requires": {
127
- "fs.realpath": "1.0.0",
128
- "inflight": "1.0.6",
129
- "inherits": "2.0.3",
130
- "minimatch": "3.0.4",
131
- "once": "1.4.0",
132
- "path-is-absolute": "1.0.1"
133
- }
134
- },
135
- "graceful-fs": {
136
- "version": "4.1.11",
137
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
138
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
139
- "dev": true
140
- },
141
- "lodash": {
142
- "version": "4.17.4",
143
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
144
- "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
145
- "dev": true
146
- },
147
- "minimatch": {
148
- "version": "3.0.4",
149
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
150
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
151
- "dev": true,
152
- "requires": {
153
- "brace-expansion": "1.1.8"
154
- }
155
- }
156
- }
157
- },
158
- "are-we-there-yet": {
159
- "version": "1.1.4",
160
- "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
161
- "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
162
- "dev": true,
163
- "requires": {
164
- "delegates": "1.0.0",
165
- "readable-stream": "2.3.3"
166
- }
167
- },
168
- "argparse": {
169
- "version": "1.0.9",
170
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
171
- "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
172
- "dev": true,
173
- "requires": {
174
- "sprintf-js": "1.0.3"
175
- }
176
- },
177
- "array-differ": {
178
- "version": "1.0.0",
179
- "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz",
180
- "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=",
181
- "dev": true
182
- },
183
- "array-find-index": {
184
- "version": "1.0.2",
185
- "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
186
- "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
187
- "dev": true
188
- },
189
- "array-union": {
190
- "version": "1.0.2",
191
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
192
- "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
193
- "dev": true,
194
- "requires": {
195
- "array-uniq": "1.0.3"
196
- }
197
- },
198
- "array-uniq": {
199
- "version": "1.0.3",
200
- "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
201
- "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
202
- "dev": true
203
- },
204
- "arrify": {
205
- "version": "1.0.1",
206
- "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
207
- "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
208
- "dev": true
209
- },
210
- "asn1": {
211
- "version": "0.2.3",
212
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
213
- "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
214
- "dev": true,
215
- "optional": true
216
- },
217
- "assert-plus": {
218
- "version": "1.0.0",
219
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
220
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
221
- "dev": true
222
- },
223
- "async": {
224
- "version": "1.5.2",
225
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
226
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
227
- "dev": true
228
- },
229
- "asynckit": {
230
- "version": "0.4.0",
231
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
232
- "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
233
- "dev": true,
234
- "optional": true
235
- },
236
- "aws-sign2": {
237
- "version": "0.7.0",
238
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
239
- "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
240
- "dev": true,
241
- "optional": true
242
- },
243
- "aws4": {
244
- "version": "1.6.0",
245
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
246
- "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
247
- "dev": true,
248
- "optional": true
249
- },
250
- "balanced-match": {
251
- "version": "1.0.0",
252
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
253
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
254
- "dev": true
255
- },
256
- "bcrypt-pbkdf": {
257
- "version": "1.0.1",
258
- "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
259
- "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
260
- "dev": true,
261
- "optional": true,
262
- "requires": {
263
- "tweetnacl": "0.14.5"
264
- }
265
- },
266
- "bl": {
267
- "version": "1.2.1",
268
- "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
269
- "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=",
270
- "dev": true,
271
- "requires": {
272
- "readable-stream": "2.3.3"
273
- }
274
- },
275
- "block-stream": {
276
- "version": "0.0.9",
277
- "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
278
- "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
279
- "dev": true,
280
- "optional": true,
281
- "requires": {
282
- "inherits": "2.0.3"
283
- }
284
- },
285
- "boom": {
286
- "version": "4.3.1",
287
- "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
288
- "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
289
- "dev": true,
290
- "optional": true,
291
- "requires": {
292
- "hoek": "4.2.0"
293
- }
294
- },
295
- "brace-expansion": {
296
- "version": "1.1.8",
297
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
298
- "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
299
- "dev": true,
300
- "requires": {
301
- "balanced-match": "1.0.0",
302
- "concat-map": "0.0.1"
303
- }
304
- },
305
- "browserify-zlib": {
306
- "version": "0.1.4",
307
- "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
308
- "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
309
- "dev": true,
310
- "requires": {
311
- "pako": "0.2.9"
312
- }
313
- },
314
- "buffer-crc32": {
315
- "version": "0.2.13",
316
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
317
- "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
318
- "dev": true
319
- },
320
- "builtin-modules": {
321
- "version": "1.1.1",
322
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
323
- "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
324
- "dev": true
325
- },
326
- "cache-swap": {
327
- "version": "0.0.6",
328
- "resolved": "https://registry.npmjs.org/cache-swap/-/cache-swap-0.0.6.tgz",
329
- "integrity": "sha1-F834NebDAf0VgJCwPRzDb0Z7FpU=",
330
- "dev": true,
331
- "requires": {
332
- "async": "0.2.10",
333
- "lodash": "1.1.1",
334
- "rimraf": "2.1.4"
335
- },
336
- "dependencies": {
337
- "async": {
338
- "version": "0.2.10",
339
- "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
340
- "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
341
- "dev": true
342
- },
343
- "lodash": {
344
- "version": "1.1.1",
345
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.1.1.tgz",
346
- "integrity": "sha1-QaKy6aAOZNbRmZ8UP/awdV9ruyQ=",
347
- "dev": true
348
- },
349
- "rimraf": {
350
- "version": "2.1.4",
351
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.1.4.tgz",
352
- "integrity": "sha1-Wm62Lu2gaPUe3lDymz5c0i89m7I=",
353
- "dev": true,
354
- "requires": {
355
- "graceful-fs": "1.2.3"
356
- }
357
- }
358
- }
359
- },
360
- "camelcase": {
361
- "version": "2.1.1",
362
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
363
- "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
364
- "dev": true
365
- },
366
- "camelcase-keys": {
367
- "version": "2.1.0",
368
- "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
369
- "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
370
- "dev": true,
371
- "requires": {
372
- "camelcase": "2.1.1",
373
- "map-obj": "1.0.1"
374
- }
375
- },
376
- "caseless": {
377
- "version": "0.12.0",
378
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
379
- "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
380
- "dev": true,
381
- "optional": true
382
- },
383
- "chalk": {
384
- "version": "0.5.1",
385
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
386
- "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
387
- "dev": true,
388
- "requires": {
389
- "ansi-styles": "1.1.0",
390
- "escape-string-regexp": "1.0.5",
391
- "has-ansi": "0.1.0",
392
- "strip-ansi": "0.3.0",
393
- "supports-color": "0.2.0"
394
- }
395
- },
396
- "chownr": {
397
- "version": "1.0.1",
398
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
399
- "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
400
- "dev": true,
401
- "optional": true
402
- },
403
- "clean-css": {
404
- "version": "3.4.28",
405
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz",
406
- "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=",
407
- "dev": true,
408
- "requires": {
409
- "commander": "2.8.1",
410
- "source-map": "0.4.4"
411
- }
412
- },
413
- "cli": {
414
- "version": "0.4.5",
415
- "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz",
416
- "integrity": "sha1-ePlIXNFhtWbppsctcXDEJw6B22E=",
417
- "dev": true,
418
- "requires": {
419
- "glob": "3.1.21"
420
- }
421
- },
422
- "co": {
423
- "version": "4.6.0",
424
- "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
425
- "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
426
- "dev": true,
427
- "optional": true
428
- },
429
- "code-point-at": {
430
- "version": "1.1.0",
431
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
432
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
433
- "dev": true
434
- },
435
- "coffee-script": {
436
- "version": "1.10.0",
437
- "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz",
438
- "integrity": "sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA=",
439
- "dev": true
440
- },
441
- "colors": {
442
- "version": "1.1.2",
443
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
444
- "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
445
- "dev": true
446
- },
447
- "combined-stream": {
448
- "version": "1.0.5",
449
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
450
- "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
451
- "dev": true,
452
- "requires": {
453
- "delayed-stream": "1.0.0"
454
- }
455
- },
456
- "commander": {
457
- "version": "2.8.1",
458
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
459
- "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
460
- "dev": true,
461
- "requires": {
462
- "graceful-readlink": "1.0.1"
463
- }
464
- },
465
- "compress-commons": {
466
- "version": "1.2.2",
467
- "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz",
468
- "integrity": "sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8=",
469
- "dev": true,
470
- "requires": {
471
- "buffer-crc32": "0.2.13",
472
- "crc32-stream": "2.0.0",
473
- "normalize-path": "2.1.1",
474
- "readable-stream": "2.3.3"
475
- }
476
- },
477
- "concat-map": {
478
- "version": "0.0.1",
479
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
480
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
481
- "dev": true
482
- },
483
- "concat-stream": {
484
- "version": "1.6.0",
485
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
486
- "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
487
- "dev": true,
488
- "requires": {
489
- "inherits": "2.0.3",
490
- "readable-stream": "2.3.3",
491
- "typedarray": "0.0.6"
492
- },
493
- "dependencies": {
494
- "isarray": {
495
- "version": "1.0.0",
496
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
497
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
498
- "dev": true
499
- },
500
- "readable-stream": {
501
- "version": "2.3.3",
502
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
503
- "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
504
- "dev": true,
505
- "requires": {
506
- "core-util-is": "1.0.2",
507
- "inherits": "2.0.3",
508
- "isarray": "1.0.0",
509
- "process-nextick-args": "1.0.7",
510
- "safe-buffer": "5.1.1",
511
- "string_decoder": "1.0.3",
512
- "util-deprecate": "1.0.2"
513
- }
514
- },
515
- "string_decoder": {
516
- "version": "1.0.3",
517
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
518
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
519
- "dev": true,
520
- "requires": {
521
- "safe-buffer": "5.1.1"
522
- }
523
- }
524
- }
525
- },
526
- "console-browserify": {
527
- "version": "0.1.6",
528
- "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz",
529
- "integrity": "sha1-0SijwLuINQ61YmxufHGm8P1ImDw=",
530
- "dev": true
531
- },
532
- "console-control-strings": {
533
- "version": "1.1.0",
534
- "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
535
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
536
- "dev": true
537
- },
538
- "core-util-is": {
539
- "version": "1.0.2",
540
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
541
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
542
- "dev": true
543
- },
544
- "crc": {
545
- "version": "3.5.0",
546
- "resolved": "https://registry.npmjs.org/crc/-/crc-3.5.0.tgz",
547
- "integrity": "sha1-mLi6fUiWZbo5efWbITgTdBAaGWQ=",
548
- "dev": true
549
- },
550
- "crc32-stream": {
551
- "version": "2.0.0",
552
- "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz",
553
- "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=",
554
- "dev": true,
555
- "requires": {
556
- "crc": "3.5.0",
557
- "readable-stream": "2.3.3"
558
- }
559
- },
560
- "cryptiles": {
561
- "version": "3.1.2",
562
- "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
563
- "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
564
- "dev": true,
565
- "optional": true,
566
- "requires": {
567
- "boom": "5.2.0"
568
- },
569
- "dependencies": {
570
- "boom": {
571
- "version": "5.2.0",
572
- "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
573
- "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
574
- "dev": true,
575
- "optional": true,
576
- "requires": {
577
- "hoek": "4.2.0"
578
- }
579
- }
580
- }
581
- },
582
- "cson": {
583
- "version": "3.0.2",
584
- "resolved": "https://registry.npmjs.org/cson/-/cson-3.0.2.tgz",
585
- "integrity": "sha1-g+6Qids8JUvsHpjkmNmqzxGtzFQ=",
586
- "dev": true,
587
- "requires": {
588
- "coffee-script": "1.12.7",
589
- "cson-parser": "1.3.5",
590
- "extract-opts": "3.3.1",
591
- "requirefresh": "2.1.0",
592
- "safefs": "4.1.0"
593
- },
594
- "dependencies": {
595
- "coffee-script": {
596
- "version": "1.12.7",
597
- "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
598
- "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==",
599
- "dev": true
600
- }
601
- }
602
- },
603
- "cson-parser": {
604
- "version": "1.3.5",
605
- "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz",
606
- "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=",
607
- "dev": true,
608
- "requires": {
609
- "coffee-script": "1.12.7"
610
- },
611
- "dependencies": {
612
- "coffee-script": {
613
- "version": "1.12.7",
614
- "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
615
- "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==",
616
- "dev": true
617
- }
618
- }
619
- },
620
- "currently-unhandled": {
621
- "version": "0.4.1",
622
- "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
623
- "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
624
- "dev": true,
625
- "requires": {
626
- "array-find-index": "1.0.2"
627
- }
628
- },
629
- "dashdash": {
630
- "version": "1.14.1",
631
- "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
632
- "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
633
- "dev": true,
634
- "optional": true,
635
- "requires": {
636
- "assert-plus": "1.0.0"
637
- }
638
- },
639
- "date-time": {
640
- "version": "1.1.0",
641
- "resolved": "https://registry.npmjs.org/date-time/-/date-time-1.1.0.tgz",
642
- "integrity": "sha1-GIdtC9pMGf5w3Tv0sDTygbEqQLY=",
643
- "dev": true,
644
- "requires": {
645
- "time-zone": "0.1.0"
646
- }
647
- },
648
- "dateformat": {
649
- "version": "1.0.12",
650
- "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
651
- "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
652
- "dev": true,
653
- "requires": {
654
- "get-stdin": "4.0.1",
655
- "meow": "3.7.0"
656
- }
657
- },
658
- "decamelize": {
659
- "version": "1.2.0",
660
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
661
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
662
- "dev": true
663
- },
664
- "deep-extend": {
665
- "version": "0.4.2",
666
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
667
- "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=",
668
- "dev": true,
669
- "optional": true
670
- },
671
- "delayed-stream": {
672
- "version": "1.0.0",
673
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
674
- "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
675
- "dev": true
676
- },
677
- "delegates": {
678
- "version": "1.0.0",
679
- "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
680
- "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
681
- "dev": true
682
- },
683
- "detect-libc": {
684
- "version": "0.2.0",
685
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-0.2.0.tgz",
686
- "integrity": "sha1-R/31ZzSKF+wl/L8LnkRjSKdvn7U=",
687
- "dev": true,
688
- "optional": true
689
- },
690
- "eachr": {
691
- "version": "3.2.0",
692
- "resolved": "https://registry.npmjs.org/eachr/-/eachr-3.2.0.tgz",
693
- "integrity": "sha1-LDXkPqCGUW95l8+At6pk1VpKRIQ=",
694
- "dev": true,
695
- "requires": {
696
- "editions": "1.3.3",
697
- "typechecker": "4.4.1"
698
- }
699
- },
700
- "ecc-jsbn": {
701
- "version": "0.1.1",
702
- "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
703
- "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
704
- "dev": true,
705
- "optional": true,
706
- "requires": {
707
- "jsbn": "0.1.1"
708
- }
709
- },
710
- "editions": {
711
- "version": "1.3.3",
712
- "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.3.tgz",
713
- "integrity": "sha1-CQcQG92iD6w8vjNMJ8vQaI3Jmls=",
714
- "dev": true
715
- },
716
- "end-of-stream": {
717
- "version": "1.4.0",
718
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz",
719
- "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=",
720
- "dev": true,
721
- "requires": {
722
- "once": "1.4.0"
723
- }
724
- },
725
- "error-ex": {
726
- "version": "1.3.1",
727
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
728
- "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
729
- "dev": true,
730
- "requires": {
731
- "is-arrayish": "0.2.1"
732
- }
733
- },
734
- "escape-string-regexp": {
735
- "version": "1.0.5",
736
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
737
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
738
- "dev": true
739
- },
740
- "esprima": {
741
- "version": "2.7.3",
742
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
743
- "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
744
- "dev": true
745
- },
746
- "eventemitter2": {
747
- "version": "0.4.14",
748
- "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
749
- "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
750
- "dev": true
751
- },
752
- "exit": {
753
- "version": "0.1.2",
754
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
755
- "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
756
- "dev": true
757
- },
758
- "expand-template": {
759
- "version": "1.1.0",
760
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.0.tgz",
761
- "integrity": "sha512-kkjwkMqj0h4w/sb32ERCDxCQkREMCAgS39DscDnSwDsbxnwwM1BTZySdC3Bn1lhY7vL08n9GoO/fVTynjDgRyQ==",
762
- "dev": true,
763
- "optional": true
764
- },
765
- "extend": {
766
- "version": "3.0.1",
767
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
768
- "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
769
- "dev": true,
770
- "optional": true
771
- },
772
- "extract-opts": {
773
- "version": "3.3.1",
774
- "resolved": "https://registry.npmjs.org/extract-opts/-/extract-opts-3.3.1.tgz",
775
- "integrity": "sha1-WrvtyYwNUgLjJ4cn+Rktfghsa+E=",
776
- "dev": true,
777
- "requires": {
778
- "eachr": "3.2.0",
779
- "editions": "1.3.3",
780
- "typechecker": "4.4.1"
781
- }
782
- },
783
- "extsprintf": {
784
- "version": "1.3.0",
785
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
786
- "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
787
- "dev": true
788
- },
789
- "fast-deep-equal": {
790
- "version": "1.0.0",
791
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
792
- "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
793
- "dev": true,
794
- "optional": true
795
- },
796
- "fast-json-stable-stringify": {
797
- "version": "2.0.0",
798
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
799
- "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
800
- "dev": true,
801
- "optional": true
802
- },
803
- "figures": {
804
- "version": "1.7.0",
805
- "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
806
- "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
807
- "dev": true,
808
- "requires": {
809
- "escape-string-regexp": "1.0.5",
810
- "object-assign": "4.1.1"
811
- }
812
- },
813
- "find-up": {
814
- "version": "1.1.2",
815
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
816
- "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
817
- "dev": true,
818
- "requires": {
819
- "path-exists": "2.1.0",
820
- "pinkie-promise": "2.0.1"
821
- }
822
- },
823
- "findup-sync": {
824
- "version": "0.3.0",
825
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
826
- "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
827
- "dev": true,
828
- "requires": {
829
- "glob": "5.0.15"
830
- },
831
- "dependencies": {
832
- "glob": {
833
- "version": "5.0.15",
834
- "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
835
- "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
836
- "dev": true,
837
- "requires": {
838
- "inflight": "1.0.6",
839
- "inherits": "2.0.3",
840
- "minimatch": "3.0.4",
841
- "once": "1.4.0",
842
- "path-is-absolute": "1.0.1"
843
- }
844
- },
845
- "minimatch": {
846
- "version": "3.0.4",
847
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
848
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
849
- "dev": true,
850
- "requires": {
851
- "brace-expansion": "1.1.8"
852
- }
853
- }
854
- }
855
- },
856
- "forever-agent": {
857
- "version": "0.6.1",
858
- "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
859
- "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
860
- "dev": true,
861
- "optional": true
862
- },
863
- "form-data": {
864
- "version": "2.3.1",
865
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
866
- "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
867
- "dev": true,
868
- "optional": true,
869
- "requires": {
870
- "asynckit": "0.4.0",
871
- "combined-stream": "1.0.5",
872
- "mime-types": "2.1.17"
873
- }
874
- },
875
- "fs.realpath": {
876
- "version": "1.0.0",
877
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
878
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
879
- "dev": true
880
- },
881
- "fstream": {
882
- "version": "1.0.11",
883
- "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
884
- "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
885
- "dev": true,
886
- "requires": {
887
- "graceful-fs": "4.1.11",
888
- "inherits": "2.0.3",
889
- "mkdirp": "0.5.1",
890
- "rimraf": "2.2.8"
891
- },
892
- "dependencies": {
893
- "graceful-fs": {
894
- "version": "4.1.11",
895
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
896
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
897
- "dev": true
898
- }
899
- }
900
- },
901
- "gauge": {
902
- "version": "2.7.4",
903
- "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
904
- "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
905
- "dev": true,
906
- "requires": {
907
- "aproba": "1.2.0",
908
- "console-control-strings": "1.1.0",
909
- "has-unicode": "2.0.1",
910
- "object-assign": "4.1.1",
911
- "signal-exit": "3.0.2",
912
- "string-width": "1.0.2",
913
- "strip-ansi": "3.0.1",
914
- "wide-align": "1.1.2"
915
- },
916
- "dependencies": {
917
- "ansi-regex": {
918
- "version": "2.1.1",
919
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
920
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
921
- "dev": true
922
- },
923
- "strip-ansi": {
924
- "version": "3.0.1",
925
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
926
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
927
- "dev": true,
928
- "requires": {
929
- "ansi-regex": "2.1.1"
930
- }
931
- }
932
- }
933
- },
934
- "get-stdin": {
935
- "version": "4.0.1",
936
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
937
- "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
938
- "dev": true
939
- },
940
- "getobject": {
941
- "version": "0.1.0",
942
- "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
943
- "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
944
- "dev": true
945
- },
946
- "getpass": {
947
- "version": "0.1.7",
948
- "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
949
- "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
950
- "dev": true,
951
- "optional": true,
952
- "requires": {
953
- "assert-plus": "1.0.0"
954
- }
955
- },
956
- "github-from-package": {
957
- "version": "0.0.0",
958
- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
959
- "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=",
960
- "dev": true,
961
- "optional": true
962
- },
963
- "glob": {
964
- "version": "3.1.21",
965
- "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
966
- "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=",
967
- "dev": true,
968
- "requires": {
969
- "graceful-fs": "1.2.3",
970
- "inherits": "1.0.2",
971
- "minimatch": "0.2.14"
972
- },
973
- "dependencies": {
974
- "inherits": {
975
- "version": "1.0.2",
976
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz",
977
- "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=",
978
- "dev": true
979
- }
980
- }
981
- },
982
- "graceful-fs": {
983
- "version": "1.2.3",
984
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
985
- "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=",
986
- "dev": true
987
- },
988
- "graceful-readlink": {
989
- "version": "1.0.1",
990
- "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
991
- "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
992
- "dev": true
993
- },
994
- "grunt": {
995
- "version": "1.0.1",
996
- "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.1.tgz",
997
- "integrity": "sha1-6HeHZOlEsY8yuw8QuQeEdcnftWs=",
998
- "dev": true,
999
- "requires": {
1000
- "coffee-script": "1.10.0",
1001
- "dateformat": "1.0.12",
1002
- "eventemitter2": "0.4.14",
1003
- "exit": "0.1.2",
1004
- "findup-sync": "0.3.0",
1005
- "glob": "7.0.6",
1006
- "grunt-cli": "1.2.0",
1007
- "grunt-known-options": "1.1.0",
1008
- "grunt-legacy-log": "1.0.0",
1009
- "grunt-legacy-util": "1.0.0",
1010
- "iconv-lite": "0.4.19",
1011
- "js-yaml": "3.5.5",
1012
- "minimatch": "3.0.4",
1013
- "nopt": "3.0.6",
1014
- "path-is-absolute": "1.0.1",
1015
- "rimraf": "2.2.8"
1016
- },
1017
- "dependencies": {
1018
- "glob": {
1019
- "version": "7.0.6",
1020
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
1021
- "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
1022
- "dev": true,
1023
- "requires": {
1024
- "fs.realpath": "1.0.0",
1025
- "inflight": "1.0.6",
1026
- "inherits": "2.0.3",
1027
- "minimatch": "3.0.4",
1028
- "once": "1.4.0",
1029
- "path-is-absolute": "1.0.1"
1030
- }
1031
- },
1032
- "grunt-cli": {
1033
- "version": "1.2.0",
1034
- "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
1035
- "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
1036
- "dev": true,
1037
- "requires": {
1038
- "findup-sync": "0.3.0",
1039
- "grunt-known-options": "1.1.0",
1040
- "nopt": "3.0.6",
1041
- "resolve": "1.1.7"
1042
- }
1043
- },
1044
- "minimatch": {
1045
- "version": "3.0.4",
1046
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
1047
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
1048
- "dev": true,
1049
- "requires": {
1050
- "brace-expansion": "1.1.8"
1051
- }
1052
- }
1053
- }
1054
- },
1055
- "grunt-contrib-clean": {
1056
- "version": "0.6.0",
1057
- "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz",
1058
- "integrity": "sha1-9TLbpLghJnTHwBPhRr2mY4uQSPY=",
1059
- "dev": true,
1060
- "requires": {
1061
- "rimraf": "2.2.8"
1062
- }
1063
- },
1064
- "grunt-contrib-compress": {
1065
- "version": "1.4.3",
1066
- "resolved": "https://registry.npmjs.org/grunt-contrib-compress/-/grunt-contrib-compress-1.4.3.tgz",
1067
- "integrity": "sha1-Ac7/ucY39S5wgfRjdQmD0KOw+nM=",
1068
- "dev": true,
1069
- "requires": {
1070
- "archiver": "1.3.0",
1071
- "chalk": "1.1.3",
1072
- "iltorb": "1.3.10",
1073
- "lodash": "4.17.4",
1074
- "pretty-bytes": "4.0.2",
1075
- "stream-buffers": "2.2.0"
1076
- },
1077
- "dependencies": {
1078
- "ansi-regex": {
1079
- "version": "2.1.1",
1080
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
1081
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
1082
- "dev": true
1083
- },
1084
- "ansi-styles": {
1085
- "version": "2.2.1",
1086
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
1087
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
1088
- "dev": true
1089
- },
1090
- "chalk": {
1091
- "version": "1.1.3",
1092
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1093
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1094
- "dev": true,
1095
- "requires": {
1096
- "ansi-styles": "2.2.1",
1097
- "escape-string-regexp": "1.0.5",
1098
- "has-ansi": "2.0.0",
1099
- "strip-ansi": "3.0.1",
1100
- "supports-color": "2.0.0"
1101
- }
1102
- },
1103
- "has-ansi": {
1104
- "version": "2.0.0",
1105
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
1106
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
1107
- "dev": true,
1108
- "requires": {
1109
- "ansi-regex": "2.1.1"
1110
- }
1111
- },
1112
- "lodash": {
1113
- "version": "4.17.4",
1114
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
1115
- "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
1116
- "dev": true
1117
- },
1118
- "pretty-bytes": {
1119
- "version": "4.0.2",
1120
- "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
1121
- "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=",
1122
- "dev": true
1123
- },
1124
- "strip-ansi": {
1125
- "version": "3.0.1",
1126
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1127
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1128
- "dev": true,
1129
- "requires": {
1130
- "ansi-regex": "2.1.1"
1131
- }
1132
- },
1133
- "supports-color": {
1134
- "version": "2.0.0",
1135
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
1136
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
1137
- "dev": true
1138
- }
1139
- }
1140
- },
1141
- "grunt-contrib-copy": {
1142
- "version": "0.7.0",
1143
- "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-0.7.0.tgz",
1144
- "integrity": "sha1-xt5I4N9zFEmu2w8InAldvCpVBQ8=",
1145
- "dev": true,
1146
- "requires": {
1147
- "chalk": "0.5.1"
1148
- }
1149
- },
1150
- "grunt-contrib-cssmin": {
1151
- "version": "0.12.3",
1152
- "resolved": "https://registry.npmjs.org/grunt-contrib-cssmin/-/grunt-contrib-cssmin-0.12.3.tgz",
1153
- "integrity": "sha1-QVdZYJb7dlb8RktMx7B0beHzkBQ=",
1154
- "dev": true,
1155
- "requires": {
1156
- "chalk": "1.1.3",
1157
- "clean-css": "3.4.28",
1158
- "maxmin": "1.1.0"
1159
- },
1160
- "dependencies": {
1161
- "ansi-regex": {
1162
- "version": "2.1.1",
1163
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
1164
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
1165
- "dev": true
1166
- },
1167
- "ansi-styles": {
1168
- "version": "2.2.1",
1169
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
1170
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
1171
- "dev": true
1172
- },
1173
- "chalk": {
1174
- "version": "1.1.3",
1175
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1176
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1177
- "dev": true,
1178
- "requires": {
1179
- "ansi-styles": "2.2.1",
1180
- "escape-string-regexp": "1.0.5",
1181
- "has-ansi": "2.0.0",
1182
- "strip-ansi": "3.0.1",
1183
- "supports-color": "2.0.0"
1184
- }
1185
- },
1186
- "has-ansi": {
1187
- "version": "2.0.0",
1188
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
1189
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
1190
- "dev": true,
1191
- "requires": {
1192
- "ansi-regex": "2.1.1"
1193
- }
1194
- },
1195
- "strip-ansi": {
1196
- "version": "3.0.1",
1197
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1198
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1199
- "dev": true,
1200
- "requires": {
1201
- "ansi-regex": "2.1.1"
1202
- }
1203
- },
1204
- "supports-color": {
1205
- "version": "2.0.0",
1206
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
1207
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
1208
- "dev": true
1209
- }
1210
- }
1211
- },
1212
- "grunt-contrib-jshint": {
1213
- "version": "0.6.5",
1214
- "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
1215
- "integrity": "sha1-OvtGdnRTZMxKGe7nk0wOBgCLVm4=",
1216
- "dev": true,
1217
- "requires": {
1218
- "jshint": "2.1.11"
1219
- }
1220
- },
1221
- "grunt-contrib-uglify": {
1222
- "version": "0.2.7",
1223
- "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-0.2.7.tgz",
1224
- "integrity": "sha1-5r2lHgxAoUWfbOrUI8Ze/XJaG/c=",
1225
- "dev": true,
1226
- "requires": {
1227
- "grunt-lib-contrib": "0.6.1",
1228
- "uglify-js": "2.4.24"
1229
- }
1230
- },
1231
- "grunt-known-options": {
1232
- "version": "1.1.0",
1233
- "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz",
1234
- "integrity": "sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk=",
1235
- "dev": true
1236
- },
1237
- "grunt-legacy-log": {
1238
- "version": "1.0.0",
1239
- "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-1.0.0.tgz",
1240
- "integrity": "sha1-+4bxgJhHvAfcR4Q/ns1srLYt8tU=",
1241
- "dev": true,
1242
- "requires": {
1243
- "colors": "1.1.2",
1244
- "grunt-legacy-log-utils": "1.0.0",
1245
- "hooker": "0.2.3",
1246
- "lodash": "3.10.1",
1247
- "underscore.string": "3.2.3"
1248
- }
1249
- },
1250
- "grunt-legacy-log-utils": {
1251
- "version": "1.0.0",
1252
- "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-1.0.0.tgz",
1253
- "integrity": "sha1-p7ji0Ps1taUPSvmG/BEnSevJbz0=",
1254
- "dev": true,
1255
- "requires": {
1256
- "chalk": "1.1.3",
1257
- "lodash": "4.3.0"
1258
- },
1259
- "dependencies": {
1260
- "ansi-regex": {
1261
- "version": "2.1.1",
1262
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
1263
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
1264
- "dev": true
1265
- },
1266
- "ansi-styles": {
1267
- "version": "2.2.1",
1268
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
1269
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
1270
- "dev": true
1271
- },
1272
- "chalk": {
1273
- "version": "1.1.3",
1274
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1275
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1276
- "dev": true,
1277
- "requires": {
1278
- "ansi-styles": "2.2.1",
1279
- "escape-string-regexp": "1.0.5",
1280
- "has-ansi": "2.0.0",
1281
- "strip-ansi": "3.0.1",
1282
- "supports-color": "2.0.0"
1283
- }
1284
- },
1285
- "has-ansi": {
1286
- "version": "2.0.0",
1287
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
1288
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
1289
- "dev": true,
1290
- "requires": {
1291
- "ansi-regex": "2.1.1"
1292
- }
1293
- },
1294
- "lodash": {
1295
- "version": "4.3.0",
1296
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz",
1297
- "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=",
1298
- "dev": true
1299
- },
1300
- "strip-ansi": {
1301
- "version": "3.0.1",
1302
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1303
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1304
- "dev": true,
1305
- "requires": {
1306
- "ansi-regex": "2.1.1"
1307
- }
1308
- },
1309
- "supports-color": {
1310
- "version": "2.0.0",
1311
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
1312
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
1313
- "dev": true
1314
- }
1315
- }
1316
- },
1317
- "grunt-legacy-util": {
1318
- "version": "1.0.0",
1319
- "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.0.0.tgz",
1320
- "integrity": "sha1-OGqnjcbtUJhsKxiVcmWxtIq7m4Y=",
1321
- "dev": true,
1322
- "requires": {
1323
- "async": "1.5.2",
1324
- "exit": "0.1.2",
1325
- "getobject": "0.1.0",
1326
- "hooker": "0.2.3",
1327
- "lodash": "4.3.0",
1328
- "underscore.string": "3.2.3",
1329
- "which": "1.2.14"
1330
- },
1331
- "dependencies": {
1332
- "lodash": {
1333
- "version": "4.3.0",
1334
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz",
1335
- "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=",
1336
- "dev": true
1337
- }
1338
- }
1339
- },
1340
- "grunt-lib-contrib": {
1341
- "version": "0.6.1",
1342
- "resolved": "https://registry.npmjs.org/grunt-lib-contrib/-/grunt-lib-contrib-0.6.1.tgz",
1343
- "integrity": "sha1-P1att9oG6BR5XuJBWw6+X7iQPrs=",
1344
- "dev": true,
1345
- "requires": {
1346
- "zlib-browserify": "0.0.1"
1347
- }
1348
- },
1349
- "grunt-phplint": {
1350
- "version": "0.0.8",
1351
- "resolved": "https://registry.npmjs.org/grunt-phplint/-/grunt-phplint-0.0.8.tgz",
1352
- "integrity": "sha1-wSeqKP930jnBSgHwUMx7cVnOkMA=",
1353
- "dev": true,
1354
- "requires": {
1355
- "cache-swap": "0.0.6",
1356
- "grunt": "0.4.5"
1357
- },
1358
- "dependencies": {
1359
- "argparse": {
1360
- "version": "0.1.16",
1361
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
1362
- "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=",
1363
- "dev": true,
1364
- "requires": {
1365
- "underscore": "1.7.0",
1366
- "underscore.string": "2.4.0"
1367
- },
1368
- "dependencies": {
1369
- "underscore.string": {
1370
- "version": "2.4.0",
1371
- "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz",
1372
- "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=",
1373
- "dev": true
1374
- }
1375
- }
1376
- },
1377
- "async": {
1378
- "version": "0.1.22",
1379
- "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz",
1380
- "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=",
1381
- "dev": true
1382
- },
1383
- "coffee-script": {
1384
- "version": "1.3.3",
1385
- "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz",
1386
- "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=",
1387
- "dev": true
1388
- },
1389
- "colors": {
1390
- "version": "0.6.2",
1391
- "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
1392
- "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
1393
- "dev": true
1394
- },
1395
- "dateformat": {
1396
- "version": "1.0.2-1.2.3",
1397
- "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz",
1398
- "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=",
1399
- "dev": true
1400
- },
1401
- "esprima": {
1402
- "version": "1.0.4",
1403
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz",
1404
- "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=",
1405
- "dev": true
1406
- },
1407
- "findup-sync": {
1408
- "version": "0.1.3",
1409
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
1410
- "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=",
1411
- "dev": true,
1412
- "requires": {
1413
- "glob": "3.2.11",
1414
- "lodash": "2.4.2"
1415
- },
1416
- "dependencies": {
1417
- "glob": {
1418
- "version": "3.2.11",
1419
- "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
1420
- "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
1421
- "dev": true,
1422
- "requires": {
1423
- "inherits": "2.0.3",
1424
- "minimatch": "0.3.0"
1425
- }
1426
- },
1427
- "lodash": {
1428
- "version": "2.4.2",
1429
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
1430
- "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
1431
- "dev": true
1432
- },
1433
- "minimatch": {
1434
- "version": "0.3.0",
1435
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
1436
- "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
1437
- "dev": true,
1438
- "requires": {
1439
- "lru-cache": "2.7.3",
1440
- "sigmund": "1.0.1"
1441
- }
1442
- }
1443
- }
1444
- },
1445
- "grunt": {
1446
- "version": "0.4.5",
1447
- "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
1448
- "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=",
1449
- "dev": true,
1450
- "requires": {
1451
- "async": "0.1.22",
1452
- "coffee-script": "1.3.3",
1453
- "colors": "0.6.2",
1454
- "dateformat": "1.0.2-1.2.3",
1455
- "eventemitter2": "0.4.14",
1456
- "exit": "0.1.2",
1457
- "findup-sync": "0.1.3",
1458
- "getobject": "0.1.0",
1459
- "glob": "3.1.21",
1460
- "grunt-legacy-log": "0.1.3",
1461
- "grunt-legacy-util": "0.2.0",
1462
- "hooker": "0.2.3",
1463
- "iconv-lite": "0.2.11",
1464
- "js-yaml": "2.0.5",
1465
- "lodash": "0.9.2",
1466
- "minimatch": "0.2.14",
1467
- "nopt": "1.0.10",
1468
- "rimraf": "2.2.8",
1469
- "underscore.string": "2.2.1",
1470
- "which": "1.0.9"
1471
- }
1472
- },
1473
- "grunt-legacy-log": {
1474
- "version": "0.1.3",
1475
- "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz",
1476
- "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=",
1477
- "dev": true,
1478
- "requires": {
1479
- "colors": "0.6.2",
1480
- "grunt-legacy-log-utils": "0.1.1",
1481
- "hooker": "0.2.3",
1482
- "lodash": "2.4.2",
1483
- "underscore.string": "2.3.3"
1484
- },
1485
- "dependencies": {
1486
- "lodash": {
1487
- "version": "2.4.2",
1488
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
1489
- "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
1490
- "dev": true
1491
- },
1492
- "underscore.string": {
1493
- "version": "2.3.3",
1494
- "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz",
1495
- "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=",
1496
- "dev": true
1497
- }
1498
- }
1499
- },
1500
- "grunt-legacy-log-utils": {
1501
- "version": "0.1.1",
1502
- "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz",
1503
- "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=",
1504
- "dev": true,
1505
- "requires": {
1506
- "colors": "0.6.2",
1507
- "lodash": "2.4.2",
1508
- "underscore.string": "2.3.3"
1509
- },
1510
- "dependencies": {
1511
- "lodash": {
1512
- "version": "2.4.2",
1513
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
1514
- "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
1515
- "dev": true
1516
- },
1517
- "underscore.string": {
1518
- "version": "2.3.3",
1519
- "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz",
1520
- "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=",
1521
- "dev": true
1522
- }
1523
- }
1524
- },
1525
- "grunt-legacy-util": {
1526
- "version": "0.2.0",
1527
- "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz",
1528
- "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=",
1529
- "dev": true,
1530
- "requires": {
1531
- "async": "0.1.22",
1532
- "exit": "0.1.2",
1533
- "getobject": "0.1.0",
1534
- "hooker": "0.2.3",
1535
- "lodash": "0.9.2",
1536
- "underscore.string": "2.2.1",
1537
- "which": "1.0.9"
1538
- }
1539
- },
1540
- "iconv-lite": {
1541
- "version": "0.2.11",
1542
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz",
1543
- "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=",
1544
- "dev": true
1545
- },
1546
- "js-yaml": {
1547
- "version": "2.0.5",
1548
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
1549
- "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=",
1550
- "dev": true,
1551
- "requires": {
1552
- "argparse": "0.1.16",
1553
- "esprima": "1.0.4"
1554
- }
1555
- },
1556
- "lodash": {
1557
- "version": "0.9.2",
1558
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz",
1559
- "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=",
1560
- "dev": true
1561
- },
1562
- "nopt": {
1563
- "version": "1.0.10",
1564
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
1565
- "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
1566
- "dev": true,
1567
- "requires": {
1568
- "abbrev": "1.1.1"
1569
- }
1570
- },
1571
- "underscore.string": {
1572
- "version": "2.2.1",
1573
- "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz",
1574
- "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=",
1575
- "dev": true
1576
- },
1577
- "which": {
1578
- "version": "1.0.9",
1579
- "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz",
1580
- "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=",
1581
- "dev": true
1582
- }
1583
- }
1584
- },
1585
- "grunt-string-replace": {
1586
- "version": "1.2.1",
1587
- "resolved": "https://registry.npmjs.org/grunt-string-replace/-/grunt-string-replace-1.2.1.tgz",
1588
- "integrity": "sha1-c9tiSuiyVNfB52LVGgPgQOEHIcY=",
1589
- "dev": true,
1590
- "requires": {
1591
- "async": "1.5.2",
1592
- "chalk": "1.1.3"
1593
- },
1594
- "dependencies": {
1595
- "ansi-regex": {
1596
- "version": "2.1.1",
1597
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
1598
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
1599
- "dev": true
1600
- },
1601
- "ansi-styles": {
1602
- "version": "2.2.1",
1603
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
1604
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
1605
- "dev": true
1606
- },
1607
- "async": {
1608
- "version": "1.5.2",
1609
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
1610
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
1611
- "dev": true
1612
- },
1613
- "chalk": {
1614
- "version": "1.1.3",
1615
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1616
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1617
- "dev": true,
1618
- "requires": {
1619
- "ansi-styles": "2.2.1",
1620
- "escape-string-regexp": "1.0.5",
1621
- "has-ansi": "2.0.0",
1622
- "strip-ansi": "3.0.1",
1623
- "supports-color": "2.0.0"
1624
- }
1625
- },
1626
- "has-ansi": {
1627
- "version": "2.0.0",
1628
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
1629
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
1630
- "dev": true,
1631
- "requires": {
1632
- "ansi-regex": "2.1.1"
1633
- }
1634
- },
1635
- "strip-ansi": {
1636
- "version": "3.0.1",
1637
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1638
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1639
- "dev": true,
1640
- "requires": {
1641
- "ansi-regex": "2.1.1"
1642
- }
1643
- },
1644
- "supports-color": {
1645
- "version": "2.0.0",
1646
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
1647
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
1648
- "dev": true
1649
- }
1650
- }
1651
- },
1652
- "grunt-version": {
1653
- "version": "1.2.1",
1654
- "resolved": "https://registry.npmjs.org/grunt-version/-/grunt-version-1.2.1.tgz",
1655
- "integrity": "sha512-FMRde7kCY1T2zCBAg4ZbLR9GbKDX60xCIZS89kCyk29LLQ/nbRSgXY+782HjT+HfC21kszcpWttfCGq1eERHPA==",
1656
- "dev": true,
1657
- "requires": {
1658
- "grunt": "1.0.1",
1659
- "semver": "4.3.6"
1660
- },
1661
- "dependencies": {
1662
- "semver": {
1663
- "version": "4.3.6",
1664
- "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
1665
- "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=",
1666
- "dev": true
1667
- }
1668
- }
1669
- },
1670
- "gzip-size": {
1671
- "version": "1.0.0",
1672
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz",
1673
- "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=",
1674
- "dev": true,
1675
- "requires": {
1676
- "browserify-zlib": "0.1.4",
1677
- "concat-stream": "1.6.0"
1678
- }
1679
- },
1680
- "har-schema": {
1681
- "version": "2.0.0",
1682
- "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
1683
- "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
1684
- "dev": true,
1685
- "optional": true
1686
- },
1687
- "har-validator": {
1688
- "version": "5.0.3",
1689
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
1690
- "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
1691
- "dev": true,
1692
- "optional": true,
1693
- "requires": {
1694
- "ajv": "5.5.1",
1695
- "har-schema": "2.0.0"
1696
- }
1697
- },
1698
- "has-ansi": {
1699
- "version": "0.1.0",
1700
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
1701
- "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
1702
- "dev": true,
1703
- "requires": {
1704
- "ansi-regex": "0.2.1"
1705
- }
1706
- },
1707
- "has-unicode": {
1708
- "version": "2.0.1",
1709
- "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
1710
- "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
1711
- "dev": true
1712
- },
1713
- "hawk": {
1714
- "version": "6.0.2",
1715
- "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
1716
- "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
1717
- "dev": true,
1718
- "optional": true,
1719
- "requires": {
1720
- "boom": "4.3.1",
1721
- "cryptiles": "3.1.2",
1722
- "hoek": "4.2.0",
1723
- "sntp": "2.1.0"
1724
- }
1725
- },
1726
- "hoek": {
1727
- "version": "4.2.0",
1728
- "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
1729
- "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==",
1730
- "dev": true
1731
- },
1732
- "hooker": {
1733
- "version": "0.2.3",
1734
- "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
1735
- "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
1736
- "dev": true
1737
- },
1738
- "hosted-git-info": {
1739
- "version": "2.5.0",
1740
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
1741
- "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==",
1742
- "dev": true
1743
- },
1744
- "http-signature": {
1745
- "version": "1.2.0",
1746
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
1747
- "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
1748
- "dev": true,
1749
- "optional": true,
1750
- "requires": {
1751
- "assert-plus": "1.0.0",
1752
- "jsprim": "1.4.1",
1753
- "sshpk": "1.13.1"
1754
- }
1755
- },
1756
- "iconv-lite": {
1757
- "version": "0.4.19",
1758
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
1759
- "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
1760
- "dev": true
1761
- },
1762
- "iltorb": {
1763
- "version": "1.3.10",
1764
- "resolved": "https://registry.npmjs.org/iltorb/-/iltorb-1.3.10.tgz",
1765
- "integrity": "sha512-nyB4+ru1u8CQqQ6w7YjasboKN3NQTN8GH/V/eEssNRKhW6UbdxdWhB9fJ5EEdjJfezKY0qPrcwLyIcgjL8hHxA==",
1766
- "dev": true,
1767
- "optional": true,
1768
- "requires": {
1769
- "detect-libc": "0.2.0",
1770
- "nan": "2.8.0",
1771
- "node-gyp": "3.6.2",
1772
- "prebuild-install": "2.4.1"
1773
- }
1774
- },
1775
- "indent-string": {
1776
- "version": "2.1.0",
1777
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
1778
- "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
1779
- "dev": true,
1780
- "requires": {
1781
- "repeating": "2.0.1"
1782
- }
1783
- },
1784
- "inflight": {
1785
- "version": "1.0.6",
1786
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
1787
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
1788
- "dev": true,
1789
- "requires": {
1790
- "once": "1.4.0",
1791
- "wrappy": "1.0.2"
1792
- }
1793
- },
1794
- "inherit": {
1795
- "version": "2.2.6",
1796
- "resolved": "https://registry.npmjs.org/inherit/-/inherit-2.2.6.tgz",
1797
- "integrity": "sha1-8WFLBshUToEo5CKchjR9tzrZeI0=",
1798
- "dev": true
1799
- },
1800
- "inherits": {
1801
- "version": "2.0.3",
1802
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
1803
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
1804
- "dev": true
1805
- },
1806
- "ini": {
1807
- "version": "1.3.5",
1808
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
1809
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
1810
- "dev": true,
1811
- "optional": true
1812
- },
1813
- "is-arrayish": {
1814
- "version": "0.2.1",
1815
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
1816
- "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
1817
- "dev": true
1818
- },
1819
- "is-builtin-module": {
1820
- "version": "1.0.0",
1821
- "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
1822
- "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
1823
- "dev": true,
1824
- "requires": {
1825
- "builtin-modules": "1.1.1"
1826
- }
1827
- },
1828
- "is-finite": {
1829
- "version": "1.0.2",
1830
- "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
1831
- "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
1832
- "dev": true,
1833
- "requires": {
1834
- "number-is-nan": "1.0.1"
1835
- }
1836
- },
1837
- "is-fullwidth-code-point": {
1838
- "version": "1.0.0",
1839
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
1840
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
1841
- "dev": true,
1842
- "requires": {
1843
- "number-is-nan": "1.0.1"
1844
- }
1845
- },
1846
- "is-typedarray": {
1847
- "version": "1.0.0",
1848
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
1849
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
1850
- "dev": true,
1851
- "optional": true
1852
- },
1853
- "is-utf8": {
1854
- "version": "0.2.1",
1855
- "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
1856
- "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
1857
- "dev": true
1858
- },
1859
- "isarray": {
1860
- "version": "1.0.0",
1861
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
1862
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
1863
- "dev": true
1864
- },
1865
- "isexe": {
1866
- "version": "2.0.0",
1867
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1868
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
1869
- "dev": true
1870
- },
1871
- "isstream": {
1872
- "version": "0.1.2",
1873
- "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
1874
- "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
1875
- "dev": true,
1876
- "optional": true
1877
- },
1878
- "jit-grunt": {
1879
- "version": "0.10.0",
1880
- "resolved": "https://registry.npmjs.org/jit-grunt/-/jit-grunt-0.10.0.tgz",
1881
- "integrity": "sha1-AIw6f+Hpa9DYTiYOofoXg0V/ecI=",
1882
- "dev": true
1883
- },
1884
- "js-yaml": {
1885
- "version": "3.5.5",
1886
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz",
1887
- "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=",
1888
- "dev": true,
1889
- "requires": {
1890
- "argparse": "1.0.9",
1891
- "esprima": "2.7.3"
1892
- }
1893
- },
1894
- "jsbn": {
1895
- "version": "0.1.1",
1896
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
1897
- "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
1898
- "dev": true,
1899
- "optional": true
1900
- },
1901
- "jshint": {
1902
- "version": "2.1.11",
1903
- "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
1904
- "integrity": "sha1-61EI/vm6Xd67gwmD9XLSQuSeP5Y=",
1905
- "dev": true,
1906
- "requires": {
1907
- "cli": "0.4.5",
1908
- "console-browserify": "0.1.6",
1909
- "minimatch": "0.2.14",
1910
- "shelljs": "0.1.4",
1911
- "underscore": "1.4.4"
1912
- },
1913
- "dependencies": {
1914
- "underscore": {
1915
- "version": "1.4.4",
1916
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
1917
- "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=",
1918
- "dev": true
1919
- }
1920
- }
1921
- },
1922
- "json-schema": {
1923
- "version": "0.2.3",
1924
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
1925
- "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
1926
- "dev": true,
1927
- "optional": true
1928
- },
1929
- "json-schema-traverse": {
1930
- "version": "0.3.1",
1931
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
1932
- "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
1933
- "dev": true,
1934
- "optional": true
1935
- },
1936
- "json-stringify-safe": {
1937
- "version": "5.0.1",
1938
- "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
1939
- "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
1940
- "dev": true,
1941
- "optional": true
1942
- },
1943
- "jsprim": {
1944
- "version": "1.4.1",
1945
- "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
1946
- "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
1947
- "dev": true,
1948
- "optional": true,
1949
- "requires": {
1950
- "assert-plus": "1.0.0",
1951
- "extsprintf": "1.3.0",
1952
- "json-schema": "0.2.3",
1953
- "verror": "1.10.0"
1954
- }
1955
- },
1956
- "lazystream": {
1957
- "version": "1.0.0",
1958
- "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
1959
- "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
1960
- "dev": true,
1961
- "requires": {
1962
- "readable-stream": "2.3.3"
1963
- }
1964
- },
1965
- "load-grunt-config": {
1966
- "version": "0.19.2",
1967
- "resolved": "https://registry.npmjs.org/load-grunt-config/-/load-grunt-config-0.19.2.tgz",
1968
- "integrity": "sha1-UgkNSiDG5j90p2SPJJsZ57f87CQ=",
1969
- "dev": true,
1970
- "requires": {
1971
- "cson": "3.0.2",
1972
- "glob": "5.0.15",
1973
- "jit-grunt": "0.10.0",
1974
- "js-yaml": "3.4.6",
1975
- "load-grunt-tasks": "3.3.0",
1976
- "lodash": "3.10.1"
1977
- },
1978
- "dependencies": {
1979
- "argparse": {
1980
- "version": "1.0.9",
1981
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
1982
- "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
1983
- "dev": true,
1984
- "requires": {
1985
- "sprintf-js": "1.0.3"
1986
- }
1987
- },
1988
- "esprima": {
1989
- "version": "2.7.3",
1990
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
1991
- "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
1992
- "dev": true
1993
- },
1994
- "glob": {
1995
- "version": "5.0.15",
1996
- "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
1997
- "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
1998
- "dev": true,
1999
- "requires": {
2000
- "inflight": "1.0.6",
2001
- "inherits": "2.0.3",
2002
- "minimatch": "3.0.4",
2003
- "once": "1.4.0",
2004
- "path-is-absolute": "1.0.1"
2005
- }
2006
- },
2007
- "js-yaml": {
2008
- "version": "3.4.6",
2009
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.4.6.tgz",
2010
- "integrity": "sha1-a+GyP2JJ9T0pM3D9TRqqY84bTrA=",
2011
- "dev": true,
2012
- "requires": {
2013
- "argparse": "1.0.9",
2014
- "esprima": "2.7.3",
2015
- "inherit": "2.2.6"
2016
- }
2017
- },
2018
- "load-grunt-tasks": {
2019
- "version": "3.3.0",
2020
- "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.3.0.tgz",
2021
- "integrity": "sha1-vliSkJRY2T3fdp60vGhRAggMYyE=",
2022
- "dev": true,
2023
- "requires": {
2024
- "arrify": "1.0.1",
2025
- "multimatch": "2.1.0",
2026
- "pkg-up": "1.0.0"
2027
- }
2028
- },
2029
- "lodash": {
2030
- "version": "3.10.1",
2031
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
2032
- "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
2033
- "dev": true
2034
- },
2035
- "minimatch": {
2036
- "version": "3.0.4",
2037
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
2038
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
2039
- "dev": true,
2040
- "requires": {
2041
- "brace-expansion": "1.1.8"
2042
- }
2043
- }
2044
- }
2045
- },
2046
- "load-grunt-tasks": {
2047
- "version": "3.1.0",
2048
- "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.1.0.tgz",
2049
- "integrity": "sha1-9Z56g0E+Loclkcd3f/3BijcJ2iA=",
2050
- "dev": true,
2051
- "requires": {
2052
- "findup-sync": "0.2.1",
2053
- "multimatch": "2.1.0"
2054
- },
2055
- "dependencies": {
2056
- "findup-sync": {
2057
- "version": "0.2.1",
2058
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.2.1.tgz",
2059
- "integrity": "sha1-4KkKRQB1xJRm7lE3MgV1FLgeh4w=",
2060
- "dev": true,
2061
- "requires": {
2062
- "glob": "4.3.5"
2063
- }
2064
- },
2065
- "glob": {
2066
- "version": "4.3.5",
2067
- "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.5.tgz",
2068
- "integrity": "sha1-gPuwjKVA8jiszl0R0em8QedRc9M=",
2069
- "dev": true,
2070
- "requires": {
2071
- "inflight": "1.0.6",
2072
- "inherits": "2.0.3",
2073
- "minimatch": "2.0.10",
2074
- "once": "1.4.0"
2075
- }
2076
- },
2077
- "minimatch": {
2078
- "version": "2.0.10",
2079
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz",
2080
- "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=",
2081
- "dev": true,
2082
- "requires": {
2083
- "brace-expansion": "1.1.8"
2084
- }
2085
- }
2086
- }
2087
- },
2088
- "load-json-file": {
2089
- "version": "1.1.0",
2090
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
2091
- "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
2092
- "dev": true,
2093
- "requires": {
2094
- "graceful-fs": "4.1.11",
2095
- "parse-json": "2.2.0",
2096
- "pify": "2.3.0",
2097
- "pinkie-promise": "2.0.1",
2098
- "strip-bom": "2.0.0"
2099
- },
2100
- "dependencies": {
2101
- "graceful-fs": {
2102
- "version": "4.1.11",
2103
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
2104
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
2105
- "dev": true
2106
- }
2107
- }
2108
- },
2109
- "lodash": {
2110
- "version": "3.10.1",
2111
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
2112
- "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
2113
- "dev": true
2114
- },
2115
- "loud-rejection": {
2116
- "version": "1.6.0",
2117
- "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
2118
- "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
2119
- "dev": true,
2120
- "requires": {
2121
- "currently-unhandled": "0.4.1",
2122
- "signal-exit": "3.0.2"
2123
- }
2124
- },
2125
- "lru-cache": {
2126
- "version": "2.7.3",
2127
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
2128
- "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
2129
- "dev": true
2130
- },
2131
- "map-obj": {
2132
- "version": "1.0.1",
2133
- "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
2134
- "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
2135
- "dev": true
2136
- },
2137
- "maxmin": {
2138
- "version": "1.1.0",
2139
- "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz",
2140
- "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=",
2141
- "dev": true,
2142
- "requires": {
2143
- "chalk": "1.1.3",
2144
- "figures": "1.7.0",
2145
- "gzip-size": "1.0.0",
2146
- "pretty-bytes": "1.0.4"
2147
- },
2148
- "dependencies": {
2149
- "ansi-regex": {
2150
- "version": "2.1.1",
2151
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
2152
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
2153
- "dev": true
2154
- },
2155
- "ansi-styles": {
2156
- "version": "2.2.1",
2157
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
2158
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
2159
- "dev": true
2160
- },
2161
- "chalk": {
2162
- "version": "1.1.3",
2163
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
2164
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
2165
- "dev": true,
2166
- "requires": {
2167
- "ansi-styles": "2.2.1",
2168
- "escape-string-regexp": "1.0.5",
2169
- "has-ansi": "2.0.0",
2170
- "strip-ansi": "3.0.1",
2171
- "supports-color": "2.0.0"
2172
- }
2173
- },
2174
- "has-ansi": {
2175
- "version": "2.0.0",
2176
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
2177
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
2178
- "dev": true,
2179
- "requires": {
2180
- "ansi-regex": "2.1.1"
2181
- }
2182
- },
2183
- "strip-ansi": {
2184
- "version": "3.0.1",
2185
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
2186
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
2187
- "dev": true,
2188
- "requires": {
2189
- "ansi-regex": "2.1.1"
2190
- }
2191
- },
2192
- "supports-color": {
2193
- "version": "2.0.0",
2194
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
2195
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
2196
- "dev": true
2197
- }
2198
- }
2199
- },
2200
- "meow": {
2201
- "version": "3.7.0",
2202
- "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
2203
- "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
2204
- "dev": true,
2205
- "requires": {
2206
- "camelcase-keys": "2.1.0",
2207
- "decamelize": "1.2.0",
2208
- "loud-rejection": "1.6.0",
2209
- "map-obj": "1.0.1",
2210
- "minimist": "1.2.0",
2211
- "normalize-package-data": "2.4.0",
2212
- "object-assign": "4.1.1",
2213
- "read-pkg-up": "1.0.1",
2214
- "redent": "1.0.0",
2215
- "trim-newlines": "1.0.0"
2216
- }
2217
- },
2218
- "mime-db": {
2219
- "version": "1.30.0",
2220
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
2221
- "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=",
2222
- "dev": true
2223
- },
2224
- "mime-types": {
2225
- "version": "2.1.17",
2226
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
2227
- "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
2228
- "dev": true,
2229
- "requires": {
2230
- "mime-db": "1.30.0"
2231
- }
2232
- },
2233
- "minimatch": {
2234
- "version": "0.2.14",
2235
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
2236
- "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=",
2237
- "dev": true,
2238
- "requires": {
2239
- "lru-cache": "2.7.3",
2240
- "sigmund": "1.0.1"
2241
- }
2242
- },
2243
- "minimist": {
2244
- "version": "1.2.0",
2245
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
2246
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
2247
- "dev": true
2248
- },
2249
- "mkdirp": {
2250
- "version": "0.5.1",
2251
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
2252
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
2253
- "dev": true,
2254
- "requires": {
2255
- "minimist": "0.0.8"
2256
- },
2257
- "dependencies": {
2258
- "minimist": {
2259
- "version": "0.0.8",
2260
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
2261
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
2262
- "dev": true
2263
- }
2264
- }
2265
- },
2266
- "multimatch": {
2267
- "version": "2.1.0",
2268
- "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz",
2269
- "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=",
2270
- "dev": true,
2271
- "requires": {
2272
- "array-differ": "1.0.0",
2273
- "array-union": "1.0.2",
2274
- "arrify": "1.0.1",
2275
- "minimatch": "3.0.4"
2276
- },
2277
- "dependencies": {
2278
- "minimatch": {
2279
- "version": "3.0.4",
2280
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
2281
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
2282
- "dev": true,
2283
- "requires": {
2284
- "brace-expansion": "1.1.8"
2285
- }
2286
- }
2287
- }
2288
- },
2289
- "nan": {
2290
- "version": "2.8.0",
2291
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
2292
- "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=",
2293
- "dev": true,
2294
- "optional": true
2295
- },
2296
- "node-abi": {
2297
- "version": "2.1.2",
2298
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.1.2.tgz",
2299
- "integrity": "sha512-hmUtb8m75RSi7N+zZLYqe75XDvZB+6LyTBPkj2DConvNgQet2e3BIqEwe1LLvqMrfyjabuT5ZOrTioLCH1HTdA==",
2300
- "dev": true,
2301
- "optional": true,
2302
- "requires": {
2303
- "semver": "5.4.1"
2304
- }
2305
- },
2306
- "node-gyp": {
2307
- "version": "3.6.2",
2308
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz",
2309
- "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=",
2310
- "dev": true,
2311
- "optional": true,
2312
- "requires": {
2313
- "fstream": "1.0.11",
2314
- "glob": "7.1.2",
2315
- "graceful-fs": "4.1.11",
2316
- "minimatch": "3.0.4",
2317
- "mkdirp": "0.5.1",
2318
- "nopt": "3.0.6",
2319
- "npmlog": "4.1.2",
2320
- "osenv": "0.1.4",
2321
- "request": "2.83.0",
2322
- "rimraf": "2.2.8",
2323
- "semver": "5.3.0",
2324
- "tar": "2.2.1",
2325
- "which": "1.2.14"
2326
- },
2327
- "dependencies": {
2328
- "glob": {
2329
- "version": "7.1.2",
2330
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
2331
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
2332
- "dev": true,
2333
- "optional": true,
2334
- "requires": {
2335
- "fs.realpath": "1.0.0",
2336
- "inflight": "1.0.6",
2337
- "inherits": "2.0.3",
2338
- "minimatch": "3.0.4",
2339
- "once": "1.4.0",
2340
- "path-is-absolute": "1.0.1"
2341
- }
2342
- },
2343
- "graceful-fs": {
2344
- "version": "4.1.11",
2345
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
2346
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
2347
- "dev": true,
2348
- "optional": true
2349
- },
2350
- "minimatch": {
2351
- "version": "3.0.4",
2352
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
2353
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
2354
- "dev": true,
2355
- "requires": {
2356
- "brace-expansion": "1.1.8"
2357
- }
2358
- },
2359
- "semver": {
2360
- "version": "5.3.0",
2361
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
2362
- "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
2363
- "dev": true,
2364
- "optional": true
2365
- }
2366
- }
2367
- },
2368
- "noop-logger": {
2369
- "version": "0.1.1",
2370
- "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
2371
- "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=",
2372
- "dev": true,
2373
- "optional": true
2374
- },
2375
- "nopt": {
2376
- "version": "3.0.6",
2377
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
2378
- "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
2379
- "dev": true,
2380
- "requires": {
2381
- "abbrev": "1.1.1"
2382
- }
2383
- },
2384
- "normalize-package-data": {
2385
- "version": "2.4.0",
2386
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
2387
- "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
2388
- "dev": true,
2389
- "requires": {
2390
- "hosted-git-info": "2.5.0",
2391
- "is-builtin-module": "1.0.0",
2392
- "semver": "5.4.1",
2393
- "validate-npm-package-license": "3.0.1"
2394
- }
2395
- },
2396
- "normalize-path": {
2397
- "version": "2.1.1",
2398
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
2399
- "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
2400
- "dev": true,
2401
- "requires": {
2402
- "remove-trailing-separator": "1.1.0"
2403
- }
2404
- },
2405
- "npmlog": {
2406
- "version": "4.1.2",
2407
- "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
2408
- "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
2409
- "dev": true,
2410
- "requires": {
2411
- "are-we-there-yet": "1.1.4",
2412
- "console-control-strings": "1.1.0",
2413
- "gauge": "2.7.4",
2414
- "set-blocking": "2.0.0"
2415
- }
2416
- },
2417
- "number-is-nan": {
2418
- "version": "1.0.1",
2419
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
2420
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
2421
- "dev": true
2422
- },
2423
- "oauth-sign": {
2424
- "version": "0.8.2",
2425
- "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
2426
- "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
2427
- "dev": true,
2428
- "optional": true
2429
- },
2430
- "object-assign": {
2431
- "version": "4.1.1",
2432
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
2433
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
2434
- "dev": true
2435
- },
2436
- "once": {
2437
- "version": "1.4.0",
2438
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
2439
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
2440
- "dev": true,
2441
- "requires": {
2442
- "wrappy": "1.0.2"
2443
- }
2444
- },
2445
- "os-homedir": {
2446
- "version": "1.0.2",
2447
- "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
2448
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
2449
- "dev": true
2450
- },
2451
- "os-tmpdir": {
2452
- "version": "1.0.2",
2453
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
2454
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
2455
- "dev": true,
2456
- "optional": true
2457
- },
2458
- "osenv": {
2459
- "version": "0.1.4",
2460
- "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz",
2461
- "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=",
2462
- "dev": true,
2463
- "optional": true,
2464
- "requires": {
2465
- "os-homedir": "1.0.2",
2466
- "os-tmpdir": "1.0.2"
2467
- }
2468
- },
2469
- "pako": {
2470
- "version": "0.2.9",
2471
- "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
2472
- "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
2473
- "dev": true
2474
- },
2475
- "parse-json": {
2476
- "version": "2.2.0",
2477
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
2478
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
2479
- "dev": true,
2480
- "requires": {
2481
- "error-ex": "1.3.1"
2482
- }
2483
- },
2484
- "parse-ms": {
2485
- "version": "1.0.1",
2486
- "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz",
2487
- "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=",
2488
- "dev": true
2489
- },
2490
- "path-exists": {
2491
- "version": "2.1.0",
2492
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
2493
- "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
2494
- "dev": true,
2495
- "requires": {
2496
- "pinkie-promise": "2.0.1"
2497
- }
2498
- },
2499
- "path-is-absolute": {
2500
- "version": "1.0.1",
2501
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
2502
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
2503
- "dev": true
2504
- },
2505
- "path-type": {
2506
- "version": "1.1.0",
2507
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
2508
- "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
2509
- "dev": true,
2510
- "requires": {
2511
- "graceful-fs": "4.1.11",
2512
- "pify": "2.3.0",
2513
- "pinkie-promise": "2.0.1"
2514
- },
2515
- "dependencies": {
2516
- "graceful-fs": {
2517
- "version": "4.1.11",
2518
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
2519
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
2520
- "dev": true
2521
- }
2522
- }
2523
- },
2524
- "performance-now": {
2525
- "version": "2.1.0",
2526
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
2527
- "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
2528
- "dev": true,
2529
- "optional": true
2530
- },
2531
- "pify": {
2532
- "version": "2.3.0",
2533
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
2534
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
2535
- "dev": true
2536
- },
2537
- "pinkie": {
2538
- "version": "2.0.4",
2539
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
2540
- "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
2541
- "dev": true
2542
- },
2543
- "pinkie-promise": {
2544
- "version": "2.0.1",
2545
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
2546
- "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
2547
- "dev": true,
2548
- "requires": {
2549
- "pinkie": "2.0.4"
2550
- }
2551
- },
2552
- "pkg-up": {
2553
- "version": "1.0.0",
2554
- "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz",
2555
- "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=",
2556
- "dev": true,
2557
- "requires": {
2558
- "find-up": "1.1.2"
2559
- }
2560
- },
2561
- "plur": {
2562
- "version": "1.0.0",
2563
- "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz",
2564
- "integrity": "sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY=",
2565
- "dev": true
2566
- },
2567
- "prebuild-install": {
2568
- "version": "2.4.1",
2569
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.4.1.tgz",
2570
- "integrity": "sha512-99TyEFYTTkBWANT+mwSptmLb9ZCLQ6qKIUE36fXSIOtShB0JNprL2hzBD8F1yIuT9btjFrFEwbRHXhqDi1HmRA==",
2571
- "dev": true,
2572
- "optional": true,
2573
- "requires": {
2574
- "expand-template": "1.1.0",
2575
- "github-from-package": "0.0.0",
2576
- "minimist": "1.2.0",
2577
- "mkdirp": "0.5.1",
2578
- "node-abi": "2.1.2",
2579
- "noop-logger": "0.1.1",
2580
- "npmlog": "4.1.2",
2581
- "os-homedir": "1.0.2",
2582
- "pump": "1.0.3",
2583
- "rc": "1.2.2",
2584
- "simple-get": "1.4.3",
2585
- "tar-fs": "1.16.0",
2586
- "tunnel-agent": "0.6.0",
2587
- "xtend": "4.0.1"
2588
- }
2589
- },
2590
- "pretty-bytes": {
2591
- "version": "1.0.4",
2592
- "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz",
2593
- "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=",
2594
- "dev": true,
2595
- "requires": {
2596
- "get-stdin": "4.0.1",
2597
- "meow": "3.7.0"
2598
- }
2599
- },
2600
- "pretty-ms": {
2601
- "version": "1.4.0",
2602
- "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-1.4.0.tgz",
2603
- "integrity": "sha1-aQ1VY3lXwMU/gIaoEK23R4Jamg8=",
2604
- "dev": true,
2605
- "requires": {
2606
- "get-stdin": "4.0.1",
2607
- "is-finite": "1.0.2",
2608
- "meow": "3.7.0",
2609
- "parse-ms": "1.0.1",
2610
- "plur": "1.0.0"
2611
- }
2612
- },
2613
- "process-nextick-args": {
2614
- "version": "1.0.7",
2615
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
2616
- "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
2617
- "dev": true
2618
- },
2619
- "pump": {
2620
- "version": "1.0.3",
2621
- "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz",
2622
- "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
2623
- "dev": true,
2624
- "requires": {
2625
- "end-of-stream": "1.4.0",
2626
- "once": "1.4.0"
2627
- }
2628
- },
2629
- "punycode": {
2630
- "version": "1.4.1",
2631
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
2632
- "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
2633
- "dev": true,
2634
- "optional": true
2635
- },
2636
- "qs": {
2637
- "version": "6.5.1",
2638
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
2639
- "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==",
2640
- "dev": true,
2641
- "optional": true
2642
- },
2643
- "rc": {
2644
- "version": "1.2.2",
2645
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz",
2646
- "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=",
2647
- "dev": true,
2648
- "optional": true,
2649
- "requires": {
2650
- "deep-extend": "0.4.2",
2651
- "ini": "1.3.5",
2652
- "minimist": "1.2.0",
2653
- "strip-json-comments": "2.0.1"
2654
- }
2655
- },
2656
- "read-pkg": {
2657
- "version": "1.1.0",
2658
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
2659
- "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
2660
- "dev": true,
2661
- "requires": {
2662
- "load-json-file": "1.1.0",
2663
- "normalize-package-data": "2.4.0",
2664
- "path-type": "1.1.0"
2665
- }
2666
- },
2667
- "read-pkg-up": {
2668
- "version": "1.0.1",
2669
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
2670
- "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
2671
- "dev": true,
2672
- "requires": {
2673
- "find-up": "1.1.2",
2674
- "read-pkg": "1.1.0"
2675
- }
2676
- },
2677
- "readable-stream": {
2678
- "version": "2.3.3",
2679
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
2680
- "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
2681
- "dev": true,
2682
- "requires": {
2683
- "core-util-is": "1.0.2",
2684
- "inherits": "2.0.3",
2685
- "isarray": "1.0.0",
2686
- "process-nextick-args": "1.0.7",
2687
- "safe-buffer": "5.1.1",
2688
- "string_decoder": "1.0.3",
2689
- "util-deprecate": "1.0.2"
2690
- }
2691
- },
2692
- "redent": {
2693
- "version": "1.0.0",
2694
- "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
2695
- "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
2696
- "dev": true,
2697
- "requires": {
2698
- "indent-string": "2.1.0",
2699
- "strip-indent": "1.0.1"
2700
- }
2701
- },
2702
- "remove-trailing-separator": {
2703
- "version": "1.1.0",
2704
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
2705
- "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
2706
- "dev": true
2707
- },
2708
- "repeating": {
2709
- "version": "2.0.1",
2710
- "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
2711
- "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
2712
- "dev": true,
2713
- "requires": {
2714
- "is-finite": "1.0.2"
2715
- }
2716
- },
2717
- "request": {
2718
- "version": "2.83.0",
2719
- "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
2720
- "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==",
2721
- "dev": true,
2722
- "optional": true,
2723
- "requires": {
2724
- "aws-sign2": "0.7.0",
2725
- "aws4": "1.6.0",
2726
- "caseless": "0.12.0",
2727
- "combined-stream": "1.0.5",
2728
- "extend": "3.0.1",
2729
- "forever-agent": "0.6.1",
2730
- "form-data": "2.3.1",
2731
- "har-validator": "5.0.3",
2732
- "hawk": "6.0.2",
2733
- "http-signature": "1.2.0",
2734
- "is-typedarray": "1.0.0",
2735
- "isstream": "0.1.2",
2736
- "json-stringify-safe": "5.0.1",
2737
- "mime-types": "2.1.17",
2738
- "oauth-sign": "0.8.2",
2739
- "performance-now": "2.1.0",
2740
- "qs": "6.5.1",
2741
- "safe-buffer": "5.1.1",
2742
- "stringstream": "0.0.5",
2743
- "tough-cookie": "2.3.3",
2744
- "tunnel-agent": "0.6.0",
2745
- "uuid": "3.1.0"
2746
- }
2747
- },
2748
- "requirefresh": {
2749
- "version": "2.1.0",
2750
- "resolved": "https://registry.npmjs.org/requirefresh/-/requirefresh-2.1.0.tgz",
2751
- "integrity": "sha1-dC3Mwg86lpGNZsbxWX3I/+vE9vU=",
2752
- "dev": true,
2753
- "requires": {
2754
- "editions": "1.3.3"
2755
- }
2756
- },
2757
- "resolve": {
2758
- "version": "1.1.7",
2759
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
2760
- "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
2761
- "dev": true
2762
- },
2763
- "rimraf": {
2764
- "version": "2.2.8",
2765
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
2766
- "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
2767
- "dev": true
2768
- },
2769
- "safe-buffer": {
2770
- "version": "5.1.1",
2771
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
2772
- "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
2773
- "dev": true
2774
- },
2775
- "safefs": {
2776
- "version": "4.1.0",
2777
- "resolved": "https://registry.npmjs.org/safefs/-/safefs-4.1.0.tgz",
2778
- "integrity": "sha1-+CrrS9165R9lPrIPZyizBYyNZEU=",
2779
- "dev": true,
2780
- "requires": {
2781
- "editions": "1.3.3",
2782
- "graceful-fs": "4.1.11"
2783
- },
2784
- "dependencies": {
2785
- "graceful-fs": {
2786
- "version": "4.1.11",
2787
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
2788
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
2789
- "dev": true
2790
- }
2791
- }
2792
- },
2793
- "semver": {
2794
- "version": "5.4.1",
2795
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
2796
- "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
2797
- "dev": true
2798
- },
2799
- "set-blocking": {
2800
- "version": "2.0.0",
2801
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
2802
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
2803
- "dev": true
2804
- },
2805
- "shelljs": {
2806
- "version": "0.1.4",
2807
- "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz",
2808
- "integrity": "sha1-37vnjVbDwBaNL7eeEOzR28sH7A4=",
2809
- "dev": true
2810
- },
2811
- "sigmund": {
2812
- "version": "1.0.1",
2813
- "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
2814
- "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
2815
- "dev": true
2816
- },
2817
- "signal-exit": {
2818
- "version": "3.0.2",
2819
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
2820
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
2821
- "dev": true
2822
- },
2823
- "simple-get": {
2824
- "version": "1.4.3",
2825
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-1.4.3.tgz",
2826
- "integrity": "sha1-6XVe2kB+ltpAxeUVjJ6jezO+y+s=",
2827
- "dev": true,
2828
- "optional": true,
2829
- "requires": {
2830
- "once": "1.4.0",
2831
- "unzip-response": "1.0.2",
2832
- "xtend": "4.0.1"
2833
- }
2834
- },
2835
- "sntp": {
2836
- "version": "2.1.0",
2837
- "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
2838
- "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
2839
- "dev": true,
2840
- "optional": true,
2841
- "requires": {
2842
- "hoek": "4.2.0"
2843
- }
2844
- },
2845
- "source-map": {
2846
- "version": "0.4.4",
2847
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
2848
- "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
2849
- "dev": true,
2850
- "requires": {
2851
- "amdefine": "1.0.1"
2852
- }
2853
- },
2854
- "spdx-correct": {
2855
- "version": "1.0.2",
2856
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
2857
- "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
2858
- "dev": true,
2859
- "requires": {
2860
- "spdx-license-ids": "1.2.2"
2861
- }
2862
- },
2863
- "spdx-expression-parse": {
2864
- "version": "1.0.4",
2865
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
2866
- "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=",
2867
- "dev": true
2868
- },
2869
- "spdx-license-ids": {
2870
- "version": "1.2.2",
2871
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
2872
- "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=",
2873
- "dev": true
2874
- },
2875
- "sprintf-js": {
2876
- "version": "1.0.3",
2877
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
2878
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
2879
- "dev": true
2880
- },
2881
- "sshpk": {
2882
- "version": "1.13.1",
2883
- "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
2884
- "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
2885
- "dev": true,
2886
- "optional": true,
2887
- "requires": {
2888
- "asn1": "0.2.3",
2889
- "assert-plus": "1.0.0",
2890
- "bcrypt-pbkdf": "1.0.1",
2891
- "dashdash": "1.14.1",
2892
- "ecc-jsbn": "0.1.1",
2893
- "getpass": "0.1.7",
2894
- "jsbn": "0.1.1",
2895
- "tweetnacl": "0.14.5"
2896
- }
2897
- },
2898
- "stream-buffers": {
2899
- "version": "2.2.0",
2900
- "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz",
2901
- "integrity": "sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=",
2902
- "dev": true
2903
- },
2904
- "string-width": {
2905
- "version": "1.0.2",
2906
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
2907
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
2908
- "dev": true,
2909
- "requires": {
2910
- "code-point-at": "1.1.0",
2911
- "is-fullwidth-code-point": "1.0.0",
2912
- "strip-ansi": "3.0.1"
2913
- },
2914
- "dependencies": {
2915
- "ansi-regex": {
2916
- "version": "2.1.1",
2917
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
2918
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
2919
- "dev": true
2920
- },
2921
- "strip-ansi": {
2922
- "version": "3.0.1",
2923
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
2924
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
2925
- "dev": true,
2926
- "requires": {
2927
- "ansi-regex": "2.1.1"
2928
- }
2929
- }
2930
- }
2931
- },
2932
- "string_decoder": {
2933
- "version": "1.0.3",
2934
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
2935
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
2936
- "dev": true,
2937
- "requires": {
2938
- "safe-buffer": "5.1.1"
2939
- }
2940
- },
2941
- "stringstream": {
2942
- "version": "0.0.5",
2943
- "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
2944
- "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
2945
- "dev": true,
2946
- "optional": true
2947
- },
2948
- "strip-ansi": {
2949
- "version": "0.3.0",
2950
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
2951
- "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
2952
- "dev": true,
2953
- "requires": {
2954
- "ansi-regex": "0.2.1"
2955
- }
2956
- },
2957
- "strip-bom": {
2958
- "version": "2.0.0",
2959
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
2960
- "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
2961
- "dev": true,
2962
- "requires": {
2963
- "is-utf8": "0.2.1"
2964
- }
2965
- },
2966
- "strip-indent": {
2967
- "version": "1.0.1",
2968
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
2969
- "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
2970
- "dev": true,
2971
- "requires": {
2972
- "get-stdin": "4.0.1"
2973
- }
2974
- },
2975
- "strip-json-comments": {
2976
- "version": "2.0.1",
2977
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
2978
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
2979
- "dev": true,
2980
- "optional": true
2981
- },
2982
- "supports-color": {
2983
- "version": "0.2.0",
2984
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
2985
- "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
2986
- "dev": true
2987
- },
2988
- "tar": {
2989
- "version": "2.2.1",
2990
- "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
2991
- "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
2992
- "dev": true,
2993
- "optional": true,
2994
- "requires": {
2995
- "block-stream": "0.0.9",
2996
- "fstream": "1.0.11",
2997
- "inherits": "2.0.3"
2998
- }
2999
- },
3000
- "tar-fs": {
3001
- "version": "1.16.0",
3002
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.0.tgz",
3003
- "integrity": "sha512-I9rb6v7mjWLtOfCau9eH5L7sLJyU2BnxtEZRQ5Mt+eRKmf1F0ohXmT/Jc3fr52kDvjJ/HV5MH3soQfPL5bQ0Yg==",
3004
- "dev": true,
3005
- "optional": true,
3006
- "requires": {
3007
- "chownr": "1.0.1",
3008
- "mkdirp": "0.5.1",
3009
- "pump": "1.0.3",
3010
- "tar-stream": "1.5.5"
3011
- }
3012
- },
3013
- "tar-stream": {
3014
- "version": "1.5.5",
3015
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz",
3016
- "integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==",
3017
- "dev": true,
3018
- "requires": {
3019
- "bl": "1.2.1",
3020
- "end-of-stream": "1.4.0",
3021
- "readable-stream": "2.3.3",
3022
- "xtend": "4.0.1"
3023
- }
3024
- },
3025
- "text-table": {
3026
- "version": "0.2.0",
3027
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
3028
- "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
3029
- "dev": true
3030
- },
3031
- "time-grunt": {
3032
- "version": "1.1.1",
3033
- "resolved": "https://registry.npmjs.org/time-grunt/-/time-grunt-1.1.1.tgz",
3034
- "integrity": "sha1-8YW6AAkdu+xmY6Y2cKqr+OcXlM8=",
3035
- "dev": true,
3036
- "requires": {
3037
- "chalk": "1.1.3",
3038
- "date-time": "1.1.0",
3039
- "figures": "1.7.0",
3040
- "hooker": "0.2.3",
3041
- "pretty-ms": "1.4.0",
3042
- "text-table": "0.2.0"
3043
- },
3044
- "dependencies": {
3045
- "ansi-regex": {
3046
- "version": "2.1.1",
3047
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
3048
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
3049
- "dev": true
3050
- },
3051
- "ansi-styles": {
3052
- "version": "2.2.1",
3053
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
3054
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
3055
- "dev": true
3056
- },
3057
- "chalk": {
3058
- "version": "1.1.3",
3059
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
3060
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
3061
- "dev": true,
3062
- "requires": {
3063
- "ansi-styles": "2.2.1",
3064
- "escape-string-regexp": "1.0.5",
3065
- "has-ansi": "2.0.0",
3066
- "strip-ansi": "3.0.1",
3067
- "supports-color": "2.0.0"
3068
- }
3069
- },
3070
- "has-ansi": {
3071
- "version": "2.0.0",
3072
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
3073
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
3074
- "dev": true,
3075
- "requires": {
3076
- "ansi-regex": "2.1.1"
3077
- }
3078
- },
3079
- "strip-ansi": {
3080
- "version": "3.0.1",
3081
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
3082
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
3083
- "dev": true,
3084
- "requires": {
3085
- "ansi-regex": "2.1.1"
3086
- }
3087
- },
3088
- "supports-color": {
3089
- "version": "2.0.0",
3090
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
3091
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
3092
- "dev": true
3093
- }
3094
- }
3095
- },
3096
- "time-zone": {
3097
- "version": "0.1.0",
3098
- "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-0.1.0.tgz",
3099
- "integrity": "sha1-Sncotqwo2w4Aj1FAQ/1VW9VXO0Y=",
3100
- "dev": true
3101
- },
3102
- "tough-cookie": {
3103
- "version": "2.3.3",
3104
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
3105
- "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
3106
- "dev": true,
3107
- "optional": true,
3108
- "requires": {
3109
- "punycode": "1.4.1"
3110
- }
3111
- },
3112
- "trim-newlines": {
3113
- "version": "1.0.0",
3114
- "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
3115
- "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
3116
- "dev": true
3117
- },
3118
- "tunnel-agent": {
3119
- "version": "0.6.0",
3120
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
3121
- "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
3122
- "dev": true,
3123
- "requires": {
3124
- "safe-buffer": "5.1.1"
3125
- }
3126
- },
3127
- "tweetnacl": {
3128
- "version": "0.14.5",
3129
- "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
3130
- "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
3131
- "dev": true,
3132
- "optional": true
3133
- },
3134
- "typechecker": {
3135
- "version": "4.4.1",
3136
- "resolved": "https://registry.npmjs.org/typechecker/-/typechecker-4.4.1.tgz",
3137
- "integrity": "sha1-+XuV9RsDhBchLWd9RaNz7nvO1+Y=",
3138
- "dev": true,
3139
- "requires": {
3140
- "editions": "1.3.3"
3141
- }
3142
- },
3143
- "typedarray": {
3144
- "version": "0.0.6",
3145
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
3146
- "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
3147
- "dev": true
3148
- },
3149
- "uglify-js": {
3150
- "version": "2.4.24",
3151
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz",
3152
- "integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=",
3153
- "dev": true,
3154
- "requires": {
3155
- "async": "0.2.10",
3156
- "source-map": "0.1.34",
3157
- "uglify-to-browserify": "1.0.2",
3158
- "yargs": "3.5.4"
3159
- },
3160
- "dependencies": {
3161
- "async": {
3162
- "version": "0.2.10",
3163
- "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
3164
- "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
3165
- "dev": true
3166
- },
3167
- "source-map": {
3168
- "version": "0.1.34",
3169
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz",
3170
- "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=",
3171
- "dev": true,
3172
- "requires": {
3173
- "amdefine": "1.0.1"
3174
- }
3175
- }
3176
- }
3177
- },
3178
- "uglify-to-browserify": {
3179
- "version": "1.0.2",
3180
- "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
3181
- "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
3182
- "dev": true
3183
- },
3184
- "underscore": {
3185
- "version": "1.7.0",
3186
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
3187
- "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=",
3188
- "dev": true
3189
- },
3190
- "underscore.string": {
3191
- "version": "3.2.3",
3192
- "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.3.tgz",
3193
- "integrity": "sha1-gGmSYzZl1eX8tNsfs6hi62jp5to=",
3194
- "dev": true
3195
- },
3196
- "unzip-response": {
3197
- "version": "1.0.2",
3198
- "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz",
3199
- "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=",
3200
- "dev": true,
3201
- "optional": true
3202
- },
3203
- "util-deprecate": {
3204
- "version": "1.0.2",
3205
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
3206
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
3207
- "dev": true
3208
- },
3209
- "uuid": {
3210
- "version": "3.1.0",
3211
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
3212
- "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==",
3213
- "dev": true,
3214
- "optional": true
3215
- },
3216
- "validate-npm-package-license": {
3217
- "version": "3.0.1",
3218
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
3219
- "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
3220
- "dev": true,
3221
- "requires": {
3222
- "spdx-correct": "1.0.2",
3223
- "spdx-expression-parse": "1.0.4"
3224
- }
3225
- },
3226
- "verror": {
3227
- "version": "1.10.0",
3228
- "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
3229
- "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
3230
- "dev": true,
3231
- "optional": true,
3232
- "requires": {
3233
- "assert-plus": "1.0.0",
3234
- "core-util-is": "1.0.2",
3235
- "extsprintf": "1.3.0"
3236
- }
3237
- },
3238
- "walkdir": {
3239
- "version": "0.0.11",
3240
- "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz",
3241
- "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=",
3242
- "dev": true
3243
- },
3244
- "which": {
3245
- "version": "1.2.14",
3246
- "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
3247
- "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
3248
- "dev": true,
3249
- "requires": {
3250
- "isexe": "2.0.0"
3251
- }
3252
- },
3253
- "wide-align": {
3254
- "version": "1.1.2",
3255
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
3256
- "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
3257
- "dev": true,
3258
- "requires": {
3259
- "string-width": "1.0.2"
3260
- }
3261
- },
3262
- "window-size": {
3263
- "version": "0.1.0",
3264
- "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
3265
- "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
3266
- "dev": true
3267
- },
3268
- "wordwrap": {
3269
- "version": "0.0.2",
3270
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
3271
- "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
3272
- "dev": true
3273
- },
3274
- "wrappy": {
3275
- "version": "1.0.2",
3276
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
3277
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
3278
- "dev": true
3279
- },
3280
- "xtend": {
3281
- "version": "4.0.1",
3282
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
3283
- "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
3284
- "dev": true
3285
- },
3286
- "yargs": {
3287
- "version": "3.5.4",
3288
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz",
3289
- "integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=",
3290
- "dev": true,
3291
- "requires": {
3292
- "camelcase": "1.2.1",
3293
- "decamelize": "1.2.0",
3294
- "window-size": "0.1.0",
3295
- "wordwrap": "0.0.2"
3296
- },
3297
- "dependencies": {
3298
- "camelcase": {
3299
- "version": "1.2.1",
3300
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
3301
- "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
3302
- "dev": true
3303
- }
3304
- }
3305
- },
3306
- "zip-stream": {
3307
- "version": "1.2.0",
3308
- "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz",
3309
- "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=",
3310
- "dev": true,
3311
- "requires": {
3312
- "archiver-utils": "1.3.0",
3313
- "compress-commons": "1.2.2",
3314
- "lodash": "4.17.4",
3315
- "readable-stream": "2.3.3"
3316
- },
3317
- "dependencies": {
3318
- "lodash": {
3319
- "version": "4.17.4",
3320
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
3321
- "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
3322
- "dev": true
3323
- }
3324
- }
3325
- },
3326
- "zlib-browserify": {
3327
- "version": "0.0.1",
3328
- "resolved": "https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz",
3329
- "integrity": "sha1-T6akXQDbwV8xikr6HZr8Aljhdsw=",
3330
- "dev": true
3331
- }
3332
- }
3333
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,301 +1,317 @@
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
5
- Contributors: ReneHermi, WP-Staging
6
- Donate link: https://wordpress.org/plugins/wp-staging
7
- License: GPLv2 or later
8
- License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
- Tags: 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.1.8
13
-
14
- A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
15
-
16
- == Description ==
17
-
18
- <strong>This cloning and staging plugin is well tested but work in progress. <br><br>
19
- If you find any issue, please open a [support ticket](https://wp-staging.com/support/ "support ticket").
20
- </strong>
21
- <br /><br />
22
- <strong>Note: </strong> For pushing plugins and theme files to live site, check out [https://wp-staging.com/](https://wp-staging.com/ "WP Staging Pro")
23
- <br /><br />
24
- <blockquote>
25
- <h4> WP Staging for WordPress Migration </h4>
26
- This duplicator plugin allows you to create an staging or development environment in seconds* <br /> <br />
27
- It creates a clone of your website into a subfolder of your main WordPress installation including an entire copy of your database.
28
- This sounds pretty simple and yes it is! All the hard time-consumptive database and file copying stuff including url replacements is done in the background.
29
- <br /> <br />
30
- I created this plugin because all other solutions are way too complex, overloaded with dozens of options or having server requirements which are not available on most shared hosting solutions.
31
- All these reasons prevent user from testing new plugins and updates first before installing them on their live website, so its time to release a plugin which has the potential to be merged into everyone´s wordpress workflow.
32
- <br /><br />
33
- <p><small><em>* Time of creation depends on size of your database and file size</em></small></p>
34
- </blockquote>
35
-
36
- WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!
37
-
38
- [youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
39
-
40
- = Main Features =
41
-
42
- * <strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!
43
- * <strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power
44
- * <strong>Safe: </strong> Access to staging site is granted for administrators only.
45
- <br /><br />
46
- <strong>More safe:</strong>
47
- <br>
48
- * Admin bar reflects that you are working on a staging site
49
- * Extensive logging if duplication and migration process fails.
50
-
51
- = What does not work or is not tested when running wordpress migration? =
52
-
53
- * Wordpress migration of wordpress multisites (not tested)
54
- * WordPress duplicating process on windows server (not tested but will probably work)
55
- Edit: Duplication on windows server seems to be working well: [Read more](https://wordpress.org/support/topic/wont-copy-files?replies=5 "Read more")
56
-
57
-
58
- <strong>Change your workflow of updating themes and plugins data:</strong>
59
-
60
- 1. Use WP Staging for migration of a production website to a clone site for staging purposes
61
- 2. Customize theme, configuration and plugins or install new plugins
62
- 3. Test everything on your staging site first
63
- 4. Everything running as expected? You are on the save side for migration of all these modifications to your production site!
64
-
65
-
66
- <h3> Why should i use a staging website? </h3>
67
-
68
- Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.
69
- When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.
70
- This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)
71
-
72
- Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a
73
- up-to-date copy of your website.
74
-
75
- Some people are also afraid of installing plugins updates because they follow the rule "never touch a running system" with having in mind that untested updates are increasing the risk of breaking their site.
76
- I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior.
77
-
78
- <strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
79
-
80
- <h3> Can´t i just use my local wordpress development copy for testing like xampp / lampp? </h3>
81
-
82
- 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
83
- of your local copy is working on your live website exactely as you would expect it.
84
- There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the
85
- the cpu performance can lead to unexpected results on your production website.
86
- There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform.
87
-
88
- This is were WP Staging steps in... Site cloning and staging site creation simplified!
89
-
90
- <h3>I just want to migrate the database from one installation to another</h3>
91
- If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.
92
- WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.
93
- Both tools are excellent cooperating eachother.
94
-
95
- <h3>What are the benefits compared to a plugin like Duplicator?</h3>
96
- At first, i love the [Duplicator plugin](https://wordpress.org/plugins/duplicator/ "Duplicator plugin"). Duplicator is a great tool for migrating from development site to production one or from production site to development one.
97
- The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.
98
- However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful.
99
-
100
- So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using
101
- the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!
102
-
103
- = I need you feedback =
104
- This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:
105
- Please open a [support request](https://wordpress.org/support/plugin/wp-staging/ "support request") and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines.
106
-
107
-
108
- = Important =
109
-
110
- Per default the staging site will have permalinks disabled because the staging site will be cloned into a subfolder and regular permalinks are not working
111
- without doing changes to your .htaccess or nginx.conf.
112
- In the majority of cases this is abolutely fine for a staging platform and you still will be able to test new plugins and do some theme changes on your staging platform.
113
- If you need the same permalink stucture on your staging platform as you have in your prodcution website you have to create a custom .htaccess for apache webserver
114
- or to adjust your nginx.conf.
115
-
116
-
117
- = How to install and setup? =
118
- Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.
119
- After installation goto the settings page 'Staging' and do your adjustments there.
120
-
121
-
122
- == Frequently Asked Questions ==
123
-
124
-
125
- == Official Site ==
126
- https://wp-staging.com
127
-
128
- == Installation ==
129
- 1. Download the file "wp-staging" , unzip and place it in your wp-content/plugins/wp-staging folder. You can alternatively upload and install it via the WordPress plugin backend.
130
- 2. Activate the plugin through the 'Plugins' menu in WordPress.
131
- 3. Start Plugins->Staging
132
-
133
- == Screenshots ==
134
-
135
- 1. Step 1. Create new WordPress staging site
136
- 2. Step 2. Scanning your website for files and database tables
137
- 3. Step 3. Wordpress Staging site creation in progress
138
- 4. Finish!
139
-
140
- == Changelog ==
141
-
142
- = 2.1.8 =
143
- * Fix: Increase the max memory consumption
144
-
145
- = 2.1.7 =
146
- * Tweak: Return more human readable error notices
147
- * Fix: Cloning process stops due to file permission issue
148
- * Fix: Exclude WP Super Cache from copying process because of bug in WP Super Cache, see https://github.com/Automattic/wp-super-cache/issues/505
149
-
150
- = 2.1.6 =
151
- * New: increased speed for cloning process by factor 5, using new method of file agregation
152
- * New: Skip files larger than 8MB
153
- * Fix: Additional checks to ensure that the root path is never deleted
154
- * New: Compatible up to WP 4.9.1
155
-
156
- = 2.1.5 =
157
- * Fix. Change link to support
158
- * Fix: Missing files in clone site if copy file limit is higher than 1
159
-
160
- = 2.1.4 =
161
- * Fix: Link to the staging site is missing a slash if WordPress is installed in subdir
162
- * Tweak: Allow file copy limit 1 to prevent copy timeouts
163
-
164
- = 2.1.3 =
165
- * New: Add more details to tools->system info log for better debugging
166
- * New: Add buttons to select all default wp tables with one click
167
- * New: Show used db table in list of staging sites
168
- * Fix: Delete staging site not possible if db prefix is same as one of the live site
169
- * Fix: Edit/Update clone function is duplicating tables.
170
- * Fix: Other staging site can be overwritten when Edit/Update clone function is executed
171
- * Fix: Several improvements to improve reliability and prevent timeouts and fatal errors during cloning
172
-
173
- = 2.1.2 =
174
- * Fix: Remove LOCK_EX parameter in file_put_contents(). LOCK_EX is not working on several systems which results in cloning process timeouts
175
- * Fix: Huge Performance improvement in copying process by removing duplicate file entries in the cache file. This also prevents weird timeout issues on some hosted websites
176
- * Fix: Error 500 when debug mode is activated
177
- * Fix: Limit maximum execution time to 30 seconds
178
- * Fix: Sanitize Clone Names and Keys to fix "clone not found" issue in upgrade routine
179
- * Fix: Do not clone the plugin wps-hide-login
180
- * Fix: Staging sites can not be deleted if they are very big
181
- * Fix: Link to staging site is undefined
182
- * Tweak: Better admin message for asking for a review
183
- * Tweak: Remove table wpstg_rmpermalinks_executed when plugin is uninstalled
184
- * New: New setting to specify the maximum amount of files copied within one ajax call to fix godaddy and bluehost ajax 404 errors. Default 10 per batch
185
-
186
-
187
- = 2.1.1 =
188
- * New: Add link to tutorial explaining the process of pushing modification to the live site
189
-
190
- = 2.1.0 =
191
- * New: Exclude unneccessary files from cloning process: .tmp, .log, .htaccess, .git, .gitignore, desktop.ini, .DS_Store, .svn
192
- * New: More details for debugging in Tools->System Info
193
- * Fix: Check if tables in staging site exists before attempting to modify them
194
- * Fix: WordPress in sub directories were not opening
195
- * Fix: Nonce check not working if nonce life time is filtered by another plugin WP Bug: https://core.trac.wordpress.org/ticket/41617#comment:1
196
- * Fix: Access to staging site not working, if WP_SITEURL and WP_HOME is defined in wp-config.php
197
- * Tweak: Exclude wp-content/cache folder from copying process
198
-
199
-
200
- = 2.0.9 =
201
- * Skip Version
202
-
203
- = 2.0.8 =
204
- * Fix: After update from wpstg 1.6.x to 2.x previous settings were not imported resulting in cancelation of cloning process. Still not fixed in 2.0.7
205
-
206
- = 2.0.7 =
207
- * Fix: After update from wpstg 1.6.x to 2.x previous settings were not imported resulting in cancelation of cloning process
208
-
209
-
210
- = 2.0.6 =
211
- * Fix: Cancel Cloning button not working
212
- * Fix: Limit max execution time to a maximum of 30sec to prevent high memory consumption and script timeouts
213
-
214
-
215
- = 2.0.5 =
216
- * New: Major version - Complete rewrite of the code base
217
- * New: Batch processing allows to clone even huge sites without any timeouts
218
- * New: Preparation for WP QUADS PRO with ability to copy file changes back to live site
219
- * New: Bypass (broken) third party plugins during wp staging related ajax requests to prevent processing errors. Use a mu plugin for this.
220
-
221
- = 1.1.6 =
222
- * New: Add download link to WP Staging Beta Version 2.0.1
223
-
224
- = 1.1.5 =
225
- * Fix: Admin notice is throwing a false positive write permission error
226
- * New: Move log folder to wp-content/uploads/wp-staging/logs
227
- * New: Tested up to WP 4.7.3
228
-
229
- = 1.1.4 =
230
- * Fix: Fatal error Unsupported operand types
231
-
232
- = 1.1.3 =
233
- * New: Tested up to wp 4.7.2
234
- * Fix: Arrows in drop down for folder selection are distorted
235
- * Tweak: Show working log as default to make debugging easier
236
-
237
- = 1.1.2 =
238
- * Fix: Settings are not deleted when plugin is removed
239
- * Fix: Staging site is available for non administrators
240
-
241
- = 1.1.1 =
242
- * Fix: Change rating url
243
-
244
- = 1.1.0 =
245
- * New: Tested up to WP 4.6
246
- * New: Create a poll and ask what feature is most required
247
-
248
- = 1.0.9 =
249
- * Fix: Undefined WPSTG() warning
250
- * Fix: Change compatibility version to wp 4.5.3
251
-
252
- = 1.0.8 =
253
- * Tested up to WP 4.5.2
254
-
255
- = 1.0.7 =
256
- * Fix: Activation hook is not fired and staging site is not working properly
257
- * Performance: Increase default query copy limit to 1000
258
-
259
- = 1.0.6 =
260
- * Fix: Uninstalling plugin throwing error
261
- * Fix: Error permission admin notice although permission issues are correct
262
-
263
-
264
- = 1.0.5 =
265
- * New: Tested up to WP 4.5
266
- * Fix: Download system log not working
267
- * Fix: Click on Optimizer "Select all | none | invert" links leads to jumping
268
- * Tweak: Make clear that unselecting a checkbox will exlude table or file from copy process
269
- * Tweak: Remove unnecessary text
270
- * Tweak: Change beta notice in dashboard. WP Staging is stable
271
- * Tweak: Change twitter handle to @wpstg
272
-
273
- = 1.0.3 =
274
- * Fix: Missing const MASHFS_VERSION
275
- * Fix: Remove error "table XY has been created, BUT inserting rows failed."
276
- * Fix: Not tested up to 4.4.2 message shown although it's tested up to WP 4.4.2
277
- * New: Disable either free or pro version and does not allow to have both version enabled at the same time
278
-
279
- = 1.0.2 =
280
- * Tweak: Change setting description of uninstall option
281
- * Tweak: Lower tags in readme.txt
282
-
283
- = 1.0.1 =
284
- * New: Orange colored admin bar on staging site for better visualization and comparision between production live site and staging site
285
- * Tweak: Remove contact link on multisite notification
286
-
287
- = 1.0.0 =
288
- * Fix: Do not follow symlinks during file copy process
289
- * Fix: css error
290
- * Fix: Show "not-compatible" notice only when blog version is higher than plugin tested version.
291
- * Fix: undefined var $size
292
- * Fix: Check if $path is null before writing to remaining_files.json
293
- * Fix: $db_helper undefined message
294
- * Fix: Skip non utf8 encoded files during copying process
295
-
296
- Complete changelog: [https://wp-staging.com/changelog.txt](https://wp-staging.com/changelog.txt)
297
-
298
- == Upgrade Notice ==
299
-
300
- = 2.1.7 =
301
- 2.1.7 * Update for WordPress 4.9.1. Preparations for Database Migration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
5
+ Contributors: ReneHermi, WP-Staging
6
+ Donate link: https://wordpress.org/plugins/wp-staging
7
+ License: GPLv2 or later
8
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
+ Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
+ Requires at least: 3.6+
11
+ Tested up to: 4.9
12
+ Stable tag: 2.2.0
13
+
14
+ A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
15
+
16
+ == Description ==
17
+
18
+ <strong>This cloning and staging plugin is well tested but work in progress. <br><br>
19
+ If you find any issue, please open a [support ticket](https://wp-staging.com/support/ "support ticket").
20
+ </strong>
21
+ <br /><br />
22
+ <strong>Note: </strong> For pushing & migrating plugins and theme files to live site, check out [https://wp-staging.com/](https://wp-staging.com/ "WP Staging Pro")
23
+ <br /><br />
24
+ <blockquote>
25
+ <h4> WP Staging for WordPress Migration </h4>
26
+ This duplicator plugin allows you to create an staging or development environment in seconds* <br /> <br />
27
+ It creates a clone of your website into a subfolder of your main WordPress installation including an entire copy of your database.
28
+ This sounds pretty simple and yes it is! All the hard time-consumptive database and file copying stuff including url replacements is done in the background.
29
+ <br /> <br />
30
+ I created this plugin because all other solutions are way too complex, overloaded with dozens of options or having server requirements which are not available on most shared hosting solutions.
31
+ All these reasons prevent user from testing new plugins and updates first before installing them on their live website, so its time to release a plugin which has the potential to be merged into everyone´s wordpress workflow.
32
+ <br /><br />
33
+ <p><small><em>* Time of creation depends on size of your database and file size</em></small></p>
34
+ </blockquote>
35
+
36
+ WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!
37
+ <p>Note: WordPress 5.0 will be shipped with a new visual editor called Gutenberg. Use WP Staging to check if Gutenberg editor is working as intended on your website and that all used plugins are compatible with that new editor.</p>
38
+
39
+
40
+ [youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
41
+
42
+ = Main Features =
43
+
44
+ * <strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!
45
+ * <strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power
46
+ * <strong>Safe: </strong> Access to staging site is granted for administrators only.
47
+ <br /><br />
48
+ <strong>More safe:</strong>
49
+ <br>
50
+ * Admin bar reflects that you are working on a staging site
51
+ * Extensive logging if duplication and migration process fails.
52
+
53
+ = What does not work or is not tested when running wordpress migration? =
54
+
55
+ * Wordpress migration of wordpress multisites (not tested)
56
+ * WordPress duplicating process on windows server (not tested but will probably work)
57
+ Edit: Duplication on windows server seems to be working well: [Read more](https://wordpress.org/support/topic/wont-copy-files?replies=5 "Read more")
58
+
59
+
60
+ <strong>Change your workflow of updating themes and plugins data:</strong>
61
+
62
+ 1. Use WP Staging for migration of a production website to a clone site for staging purposes
63
+ 2. Customize theme, configuration and plugins or install new plugins
64
+ 3. Test everything on your staging site first
65
+ 4. Everything running as expected? You are on the save side for migration of all these modifications to your production site!
66
+
67
+
68
+ <h3> Why should i use a staging website? </h3>
69
+
70
+ Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.
71
+ When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.
72
+ This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)
73
+
74
+ Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a
75
+ up-to-date copy of your website.
76
+
77
+ Some people are also afraid of installing plugins updates because they follow the rule "never touch a running system" with having in mind that untested updates are increasing the risk of breaking their site.
78
+ I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior.
79
+
80
+ <strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
81
+
82
+ <h3> Can´t i just use my local wordpress development copy for testing like xampp / lampp? </h3>
83
+
84
+ 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
85
+ of your local copy is working on your live website exactely as you would expect it.
86
+ There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the
87
+ the cpu performance can lead to unexpected results on your production website.
88
+ There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform.
89
+
90
+ This is were WP Staging steps in... Site cloning and staging site creation simplified!
91
+
92
+ <h3>I just want to migrate the database from one installation to another</h3>
93
+ If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.
94
+ WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.
95
+ Both tools are excellent cooperating eachother.
96
+
97
+ <h3>What are the benefits compared to a plugin like Duplicator?</h3>
98
+ At first, i love the [Duplicator plugin](https://wordpress.org/plugins/duplicator/ "Duplicator plugin"). Duplicator is a great tool for migrating from development site to production one or from production site to development one.
99
+ The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.
100
+ However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful.
101
+
102
+ So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using
103
+ the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!
104
+
105
+ = I need you feedback =
106
+ This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:
107
+ Please open a [support request](https://wordpress.org/support/plugin/wp-staging/ "support request") and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines.
108
+
109
+
110
+ = Important =
111
+
112
+ Per default the staging site will have permalinks disabled because the staging site will be cloned into a subfolder and regular permalinks are not working
113
+ without doing changes to your .htaccess or nginx.conf.
114
+ In the majority of cases this is abolutely fine for a staging platform and you still will be able to test new plugins and do some theme changes on your staging platform.
115
+ If you need the same permalink stucture on your staging platform as you have in your prodcution website you have to create a custom .htaccess for apache webserver
116
+ or to adjust your nginx.conf.
117
+
118
+
119
+ = How to install and setup? =
120
+ Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.
121
+ After installation goto the settings page 'Staging' and do your adjustments there.
122
+
123
+
124
+ == Frequently Asked Questions ==
125
+
126
+
127
+ == Official Site ==
128
+ https://wp-staging.com
129
+
130
+ == Installation ==
131
+ 1. Download the file "wp-staging" , unzip and place it in your wp-content/plugins/wp-staging folder. You can alternatively upload and install it via the WordPress plugin backend.
132
+ 2. Activate the plugin through the 'Plugins' menu in WordPress.
133
+ 3. Start Plugins->Staging
134
+
135
+ == Screenshots ==
136
+
137
+ 1. Step 1. Create new WordPress staging site
138
+ 2. Step 2. Scanning your website for files and database tables
139
+ 3. Step 3. Wordpress Staging site creation in progress
140
+ 4. Finish!
141
+
142
+ == Changelog ==
143
+
144
+ = 2.2.0 =
145
+ * Fix: Old staging site is not listed and pushing is not working properly if plugin is updated from wp staging version 1.6 and lower
146
+
147
+ = 2.1.9 =
148
+ * New: Performance improvement increase db query limit to 5000
149
+ * New: Detect automatically if WordPress is installed in sub folder
150
+ * Tweak: Tested up to WP 4.9.4
151
+ * Fix: Updating from an old version 1.1.6 < to latest version deletes the staging sites listing table
152
+ * Fix: Reduce memory size of the logging window to prevent browser timeouts
153
+ * Fix: Can not copy db table if table name contains the db prefix multiple times
154
+ * Fix: Some excluded folders are not ignored during copy process
155
+ * Fix: mod_security is causing script termination
156
+ * Fix: Skip directory listings for symlinks
157
+
158
+ = 2.1.8 =
159
+ * Fix: Increase the max memory consumption
160
+
161
+ = 2.1.7 =
162
+ * Tweak: Return more human readable error notices
163
+ * Fix: Cloning process stops due to file permission issue
164
+ * Fix: Exclude WP Super Cache from copying process because of bug in WP Super Cache, see https://github.com/Automattic/wp-super-cache/issues/505
165
+
166
+ = 2.1.6 =
167
+ * New: increased speed for cloning process by factor 5, using new method of file agregation
168
+ * New: Skip files larger than 8MB
169
+ * Fix: Additional checks to ensure that the root path is never deleted
170
+ * New: Compatible up to WP 4.9.1
171
+
172
+ = 2.1.5 =
173
+ * Fix. Change link to support
174
+ * Fix: Missing files in clone site if copy file limit is higher than 1
175
+
176
+ = 2.1.4 =
177
+ * Fix: Link to the staging site is missing a slash if WordPress is installed in subdir
178
+ * Tweak: Allow file copy limit 1 to prevent copy timeouts
179
+
180
+ = 2.1.3 =
181
+ * New: Add more details to tools->system info log for better debugging
182
+ * New: Add buttons to select all default wp tables with one click
183
+ * New: Show used db table in list of staging sites
184
+ * Fix: Delete staging site not possible if db prefix is same as one of the live site
185
+ * Fix: Edit/Update clone function is duplicating tables.
186
+ * Fix: Other staging site can be overwritten when Edit/Update clone function is executed
187
+ * Fix: Several improvements to improve reliability and prevent timeouts and fatal errors during cloning
188
+
189
+ = 2.1.2 =
190
+ * Fix: Remove LOCK_EX parameter in file_put_contents(). LOCK_EX is not working on several systems which results in cloning process timeouts
191
+ * Fix: Huge Performance improvement in copying process by removing duplicate file entries in the cache file. This also prevents weird timeout issues on some hosted websites
192
+ * Fix: Error 500 when debug mode is activated
193
+ * Fix: Limit maximum execution time to 30 seconds
194
+ * Fix: Sanitize Clone Names and Keys to fix "clone not found" issue in upgrade routine
195
+ * Fix: Do not clone the plugin wps-hide-login
196
+ * Fix: Staging sites can not be deleted if they are very big
197
+ * Fix: Link to staging site is undefined
198
+ * Tweak: Better admin message for asking for a review
199
+ * Tweak: Remove table wpstg_rmpermalinks_executed when plugin is uninstalled
200
+ * New: New setting to specify the maximum amount of files copied within one ajax call to fix godaddy and bluehost ajax 404 errors. Default 10 per batch
201
+
202
+
203
+ = 2.1.1 =
204
+ * New: Add link to tutorial explaining the process of pushing modification to the live site
205
+
206
+ = 2.1.0 =
207
+ * New: Exclude unneccessary files from cloning process: .tmp, .log, .htaccess, .git, .gitignore, desktop.ini, .DS_Store, .svn
208
+ * New: More details for debugging in Tools->System Info
209
+ * Fix: Check if tables in staging site exists before attempting to modify them
210
+ * Fix: WordPress in sub directories were not opening
211
+ * Fix: Nonce check not working if nonce life time is filtered by another plugin WP Bug: https://core.trac.wordpress.org/ticket/41617#comment:1
212
+ * Fix: Access to staging site not working, if WP_SITEURL and WP_HOME is defined in wp-config.php
213
+ * Tweak: Exclude wp-content/cache folder from copying process
214
+
215
+
216
+ = 2.0.9 =
217
+ * Skip Version
218
+
219
+ = 2.0.8 =
220
+ * Fix: After update from wpstg 1.6.x to 2.x previous settings were not imported resulting in cancelation of cloning process. Still not fixed in 2.0.7
221
+
222
+ = 2.0.7 =
223
+ * Fix: After update from wpstg 1.6.x to 2.x previous settings were not imported resulting in cancelation of cloning process
224
+
225
+
226
+ = 2.0.6 =
227
+ * Fix: Cancel Cloning button not working
228
+ * Fix: Limit max execution time to a maximum of 30sec to prevent high memory consumption and script timeouts
229
+
230
+
231
+ = 2.0.5 =
232
+ * New: Major version - Complete rewrite of the code base
233
+ * New: Batch processing allows to clone even huge sites without any timeouts
234
+ * New: Preparation for WP QUADS PRO with ability to copy file changes back to live site
235
+ * New: Bypass (broken) third party plugins during wp staging related ajax requests to prevent processing errors. Use a mu plugin for this.
236
+
237
+ = 1.1.6 =
238
+ * New: Add download link to WP Staging Beta Version 2.0.1
239
+
240
+ = 1.1.5 =
241
+ * Fix: Admin notice is throwing a false positive write permission error
242
+ * New: Move log folder to wp-content/uploads/wp-staging/logs
243
+ * New: Tested up to WP 4.7.3
244
+
245
+ = 1.1.4 =
246
+ * Fix: Fatal error Unsupported operand types
247
+
248
+ = 1.1.3 =
249
+ * New: Tested up to wp 4.7.2
250
+ * Fix: Arrows in drop down for folder selection are distorted
251
+ * Tweak: Show working log as default to make debugging easier
252
+
253
+ = 1.1.2 =
254
+ * Fix: Settings are not deleted when plugin is removed
255
+ * Fix: Staging site is available for non administrators
256
+
257
+ = 1.1.1 =
258
+ * Fix: Change rating url
259
+
260
+ = 1.1.0 =
261
+ * New: Tested up to WP 4.6
262
+ * New: Create a poll and ask what feature is most required
263
+
264
+ = 1.0.9 =
265
+ * Fix: Undefined WPSTG() warning
266
+ * Fix: Change compatibility version to wp 4.5.3
267
+
268
+ = 1.0.8 =
269
+ * Tested up to WP 4.5.2
270
+
271
+ = 1.0.7 =
272
+ * Fix: Activation hook is not fired and staging site is not working properly
273
+ * Performance: Increase default query copy limit to 1000
274
+
275
+ = 1.0.6 =
276
+ * Fix: Uninstalling plugin throwing error
277
+ * Fix: Error permission admin notice although permission issues are correct
278
+
279
+
280
+ = 1.0.5 =
281
+ * New: Tested up to WP 4.5
282
+ * Fix: Download system log not working
283
+ * Fix: Click on Optimizer "Select all | none | invert" links leads to jumping
284
+ * Tweak: Make clear that unselecting a checkbox will exlude table or file from copy process
285
+ * Tweak: Remove unnecessary text
286
+ * Tweak: Change beta notice in dashboard. WP Staging is stable
287
+ * Tweak: Change twitter handle to @wpstg
288
+
289
+ = 1.0.3 =
290
+ * Fix: Missing const MASHFS_VERSION
291
+ * Fix: Remove error "table XY has been created, BUT inserting rows failed."
292
+ * Fix: Not tested up to 4.4.2 message shown although it's tested up to WP 4.4.2
293
+ * New: Disable either free or pro version and does not allow to have both version enabled at the same time
294
+
295
+ = 1.0.2 =
296
+ * Tweak: Change setting description of uninstall option
297
+ * Tweak: Lower tags in readme.txt
298
+
299
+ = 1.0.1 =
300
+ * New: Orange colored admin bar on staging site for better visualization and comparision between production live site and staging site
301
+ * Tweak: Remove contact link on multisite notification
302
+
303
+ = 1.0.0 =
304
+ * Fix: Do not follow symlinks during file copy process
305
+ * Fix: css error
306
+ * Fix: Show "not-compatible" notice only when blog version is higher than plugin tested version.
307
+ * Fix: undefined var $size
308
+ * Fix: Check if $path is null before writing to remaining_files.json
309
+ * Fix: $db_helper undefined message
310
+ * Fix: Skip non utf8 encoded files during copying process
311
+
312
+ Complete changelog: [https://wp-staging.com/changelog.txt](https://wp-staging.com/changelog.txt)
313
+
314
+ == Upgrade Notice ==
315
+
316
+ = 2.1.7 =
317
+ 2.1.7 * Update for WordPress 4.9.1. Preparations for Database Migration
uninstall.php CHANGED
@@ -1,89 +1,89 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend;
4
-
5
- use WPStaging\Backend\Optimizer\Optimizer;
6
-
7
- /**
8
- * Uninstall WP-Staging
9
- *
10
- * @package WPSTG
11
- * @subpackage Uninstall
12
- * @copyright Copyright (c) 2015, René Hermenau
13
- * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
14
- * @since 0.9.0
15
- */
16
- // No direct access
17
- if (!defined('WP_UNINSTALL_PLUGIN')) {
18
- exit;
19
- }
20
-
21
- class uninstall {
22
-
23
- public function __construct() {
24
-
25
- // Plugin Folder Path
26
- if (!defined('WPSTG_PLUGIN_DIR')) {
27
- define('WPSTG_PLUGIN_DIR', plugin_dir_path(__FILE__));
28
- }
29
-
30
- /**
31
- * Path to main WP Staging class
32
- * Make sure to not redeclare class in case free version has been installed previosly
33
- */
34
- if (!class_exists('WPStaging\WPStaging')) {
35
- require_once plugin_dir_path(__FILE__) . "apps/Core/WPStaging.php";
36
- }
37
- $wpStaging = \WPStaging\WPStaging::getInstance();
38
-
39
- // Delete our must use plugin
40
- $this->deleteMuPlugin();
41
-
42
- $this->init();
43
- }
44
-
45
- private function init() {
46
-
47
- $options = json_decode(json_encode(get_option("wpstg_settings", array())));
48
-
49
- if (isset($options->unInstallOnDelete) && '1' === $options->unInstallOnDelete) {
50
- // Delete options
51
- delete_option("wpstg_version_upgraded_from");
52
- delete_option("wpstg_version");
53
- delete_option("wpstg_installDate");
54
- delete_option("wpstg_firsttime");
55
- delete_option("wpstg_is_staging_site");
56
- delete_option("wpstg_settings");
57
- delete_option("wpstg_rmpermalinks_executed");
58
-
59
- /* Do not delete these fields without actually deleting the staging site
60
- * @create a delete routine which deletes the staging sites first
61
- */
62
- //delete_option( "wpstg_existing_clones" );
63
- //delete_option( "wpstg_existing_clones_beta" );
64
- // Old wpstg 1.3 options for admin notices
65
- delete_option("wpstg_start_poll");
66
- delete_option("wpstg_hide_beta");
67
- delete_option("wpstg_RatingDiv");
68
-
69
- // New 2.x options for admin notices
70
- delete_option("wpstg_poll");
71
- delete_option("wpstg_rating");
72
- delete_option("wpstg_beta");
73
-
74
- // Delete events
75
- wp_clear_scheduled_hook('wpstg_weekly_event');
76
- }
77
- }
78
-
79
- /**
80
- * delete MuPlugin
81
- */
82
- private function deleteMuPlugin() {
83
- $optimizer = new Optimizer;
84
- $optimizer->unstallOptimizer();
85
- }
86
-
87
- }
88
-
89
- new uninstall();
1
+ <?php
2
+
3
+ namespace WPStaging\Backend;
4
+
5
+ use WPStaging\Backend\Optimizer\Optimizer;
6
+
7
+ /**
8
+ * Uninstall WP-Staging
9
+ *
10
+ * @package WPSTG
11
+ * @subpackage Uninstall
12
+ * @copyright Copyright (c) 2015, René Hermenau
13
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
14
+ * @since 0.9.0
15
+ */
16
+ // No direct access
17
+ if (!defined('WP_UNINSTALL_PLUGIN')) {
18
+ exit;
19
+ }
20
+
21
+ class uninstall {
22
+
23
+ public function __construct() {
24
+
25
+ // Plugin Folder Path
26
+ if (!defined('WPSTG_PLUGIN_DIR')) {
27
+ define('WPSTG_PLUGIN_DIR', plugin_dir_path(__FILE__));
28
+ }
29
+
30
+ /**
31
+ * Path to main WP Staging class
32
+ * Make sure to not redeclare class in case free version has been installed previosly
33
+ */
34
+ if (!class_exists('WPStaging\WPStaging')) {
35
+ require_once plugin_dir_path(__FILE__) . "apps/Core/WPStaging.php";
36
+ }
37
+ $wpStaging = \WPStaging\WPStaging::getInstance();
38
+
39
+ // Delete our must use plugin
40
+ $this->deleteMuPlugin();
41
+
42
+ $this->init();
43
+ }
44
+
45
+ private function init() {
46
+
47
+ $options = json_decode(json_encode(get_option("wpstg_settings", array())));
48
+
49
+ if (isset($options->unInstallOnDelete) && '1' === $options->unInstallOnDelete) {
50
+ // Delete options
51
+ delete_option("wpstg_version_upgraded_from");
52
+ delete_option("wpstg_version");
53
+ delete_option("wpstg_installDate");
54
+ delete_option("wpstg_firsttime");
55
+ delete_option("wpstg_is_staging_site");
56
+ delete_option("wpstg_settings");
57
+ delete_option("wpstg_rmpermalinks_executed");
58
+
59
+ /* Do not delete these fields without actually deleting the staging site
60
+ * @create a delete routine which deletes the staging sites first
61
+ */
62
+ //delete_option( "wpstg_existing_clones" );
63
+ //delete_option( "wpstg_existing_clones_beta" );
64
+ // Old wpstg 1.3 options for admin notices
65
+ delete_option("wpstg_start_poll");
66
+ delete_option("wpstg_hide_beta");
67
+ delete_option("wpstg_RatingDiv");
68
+
69
+ // New 2.x options for admin notices
70
+ delete_option("wpstg_poll");
71
+ delete_option("wpstg_rating");
72
+ delete_option("wpstg_beta");
73
+
74
+ // Delete events
75
+ wp_clear_scheduled_hook('wpstg_weekly_event');
76
+ }
77
+ }
78
+
79
+ /**
80
+ * delete MuPlugin
81
+ */
82
+ private function deleteMuPlugin() {
83
+ $optimizer = new Optimizer;
84
+ $optimizer->unstallOptimizer();
85
+ }
86
+
87
+ }
88
+
89
+ new uninstall();
wp-staging.php CHANGED
@@ -1,91 +1,91 @@
1
- <?php
2
-
3
- /**
4
- * Plugin Name: WP Staging
5
- * Plugin URI: https://wordpress.org/plugins/wp-staging
6
- * Description: Create a staging clone site for testing & developing
7
- * Author: WP-Staging
8
- * Author URI: https://wp-staging.com
9
- * Contributors: ReneHermi, ilgityildirim
10
- * Version: 2.1.8
11
- * Text Domain: wpstg
12
- * Domain Path: /languages/
13
-
14
- *
15
- * WP-Staging is free software: you can redistribute it and/or modify
16
- * it under the terms of the GNU General Public License as published by
17
- * the Free Software Foundation, either version 2 of the License, or
18
- * any later version.
19
- *
20
- * WP-Staging is distributed in the hope that it will be useful,
21
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
- * GNU General Public License for more details.
24
- *
25
- * You should have received a copy of the GNU General Public License
26
- * along with Staging. If not, see <http://www.gnu.org/licenses/>.
27
- *
28
- * @package WPSTG
29
- * @category Core
30
- * @author René Hermenau, Ilgıt Yıldırım
31
- */
32
-
33
- // No Direct Access
34
- if (!defined("WPINC"))
35
- {
36
- die;
37
- }
38
-
39
- // Plugin Folder Path
40
- if( !defined( 'WPSTG_PLUGIN_DIR' ) ) {
41
- define( 'WPSTG_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
42
- }
43
- // Plugin Folder URL
44
- if( !defined( 'WPSTG_PLUGIN_URL' ) ) {
45
- define( 'WPSTG_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
46
- }
47
-
48
-
49
- /**
50
- * Fix nonce check
51
- * https://core.trac.wordpress.org/ticket/41617#ticket
52
- * @param int $seconds
53
- * @return int
54
- */
55
- function wpstg_overwrite_nonce($seconds){
56
- return 86400;
57
- }
58
- add_filter('nonce_life', 'wpstg_overwrite_nonce', 99999);
59
-
60
- /**
61
- * Path to main WP Staging class
62
- * Make sure to not redeclare class in case free version has been installed previosly
63
- */
64
- if (!class_exists( 'WPStaging\WPStaging' )){
65
- require_once plugin_dir_path(__FILE__) . "apps/Core/WPStaging.php";
66
- }
67
-
68
- $wpStaging = \WPStaging\WPStaging::getInstance();
69
-
70
- /**
71
- * Load a few important WP globals into WPStaging class to make them available via dependancy injection
72
- */
73
-
74
- // Wordpress DB Object
75
- if (isset($wpdb))
76
- {
77
- $wpStaging->set("wpdb", $wpdb);
78
- }
79
-
80
- // WordPress Filter Object
81
- if (isset($wp_filter))
82
- {
83
- $wpStaging->set("wp_filter", function() use(&$wp_filter) {
84
- return $wp_filter;
85
- });
86
- }
87
-
88
- /**
89
- * Inititalize WPStaging
90
- */
91
- $wpStaging->run();
1
+ <?php
2
+
3
+ /**
4
+ * Plugin Name: WP Staging
5
+ * Plugin URI: https://wordpress.org/plugins/wp-staging
6
+ * Description: Create a staging clone site for testing & developing
7
+ * Author: WP-Staging
8
+ * Author URI: https://wp-staging.com
9
+ * Contributors: ReneHermi, ilgityildirim
10
+ * Version: 2.2.0
11
+ * Text Domain: wpstg
12
+ * Domain Path: /languages/
13
+
14
+ *
15
+ * WP-Staging is free software: you can redistribute it and/or modify
16
+ * it under the terms of the GNU General Public License as published by
17
+ * the Free Software Foundation, either version 2 of the License, or
18
+ * any later version.
19
+ *
20
+ * WP-Staging is distributed in the hope that it will be useful,
21
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ * GNU General Public License for more details.
24
+ *
25
+ * You should have received a copy of the GNU General Public License
26
+ * along with Staging. If not, see <http://www.gnu.org/licenses/>.
27
+ *
28
+ * @package WPSTG
29
+ * @category Core
30
+ * @author René Hermenau, Ilgıt Yıldırım
31
+ */
32
+
33
+ // No Direct Access
34
+ if (!defined("WPINC"))
35
+ {
36
+ die;
37
+ }
38
+
39
+ // Plugin Folder Path
40
+ if( !defined( 'WPSTG_PLUGIN_DIR' ) ) {
41
+ define( 'WPSTG_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
42
+ }
43
+ // Plugin Folder URL
44
+ if( !defined( 'WPSTG_PLUGIN_URL' ) ) {
45
+ define( 'WPSTG_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
46
+ }
47
+
48
+
49
+ /**
50
+ * Fix nonce check
51
+ * https://core.trac.wordpress.org/ticket/41617#ticket
52
+ * @param int $seconds
53
+ * @return int
54
+ */
55
+ function wpstg_overwrite_nonce($seconds){
56
+ return 86400;
57
+ }
58
+ add_filter('nonce_life', 'wpstg_overwrite_nonce', 99999);
59
+
60
+ /**
61
+ * Path to main WP Staging class
62
+ * Make sure to not redeclare class in case free version has been installed previosly
63
+ */
64
+ if (!class_exists( 'WPStaging\WPStaging' )){
65
+ require_once plugin_dir_path(__FILE__) . "apps/Core/WPStaging.php";
66
+ }
67
+
68
+ $wpStaging = \WPStaging\WPStaging::getInstance();
69
+
70
+ /**
71
+ * Load a few important WP globals into WPStaging class to make them available via dependancy injection
72
+ */
73
+
74
+ // Wordpress DB Object
75
+ if (isset($wpdb))
76
+ {
77
+ $wpStaging->set("wpdb", $wpdb);
78
+ }
79
+
80
+ // WordPress Filter Object
81
+ if (isset($wp_filter))
82
+ {
83
+ $wpStaging->set("wp_filter", function() use(&$wp_filter) {
84
+ return $wp_filter;
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Inititalize WPStaging
90
+ */
91
+ $wpStaging->run();