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

Version Description

  • New: increased speed for cloning process by factor 5, using new method of file agregation
  • New: Skip files larger than 8MB
  • Fix: Additional checks to ensure that the root path is never deleted
  • New: Compatible up to WP 4.9.1
Download this release

Release Info

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

Code changes from version 2.1.5 to 2.1.6

apps/Backend/Modules/Jobs/Cancel.php CHANGED
@@ -1,60 +1,74 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- /**
5
- * Class Cancel
6
- * @package WPStaging\Backend\Modules\Jobs
7
- */
8
- class Cancel extends Job
9
- {
10
- /**
11
- * @return bool
12
- */
13
- public function check()
14
- {
15
- return (
16
- isset($this->options) &&
17
- isset($this->options->clone) &&
18
- isset($this->options->cloneNumber) &&
19
- isset($this->options->cloneDirectoryName) &&
20
- isset($_POST["clone"]) &&
21
- $_POST["clone"] === $this->options->clone
22
- );
23
- }
24
-
25
- /**
26
- * @return array
27
- */
28
- protected function createCloneData()
29
- {
30
- $clone = array();
31
-
32
- if (!$this->check())
33
- {
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
-
41
- return $clone;
42
- }
43
-
44
- /**
45
- * Start Module
46
- * @return bool
47
- */
48
- public function start()
49
- {
50
- $cloneData = $this->createCloneData();
51
-
52
- if (empty($cloneData))
53
- {
54
- return true;
55
- }
56
-
57
- $delete = new Delete();
58
- return $delete->start($cloneData);
59
- }
60
- }
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/Delete.php CHANGED
@@ -96,7 +96,6 @@ class Delete extends Job {
96
//$wpdb = WPStaging::getInstance()->get("wpdb");
97
$this->wpdb = WPStaging::getInstance()->get("wpdb");
98
99
-
100
$stagingPrefix = $this->getStagingPrefix();
101
102
$tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$stagingPrefix}%'");
@@ -117,52 +116,8 @@ class Delete extends Job {
117
* Check and return prefix of the staging site
118
*/
119
public function getStagingPrefix() {
120
- // prefix not defined! Happens if staging site has ben generated with older version of wpstg
121
- // Try to get staging prefix from wp-config.php of staging site
122
- //wp_die($this->clone->directoryName);
123
- if (empty($this->clone->prefix)) {
124
- // Throw error
125
- $path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
126
- if (false === ($content = @file_get_contents($path))) {
127
- $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
128
- // Create a random prefix which highly like never exists
129
- $this->clone->prefix = rand(7, 15) . '_';
130
- } else {
131
-
132
- // Get prefix from wp-config.php
133
- //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
134
- preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
135
-
136
- if (!empty($matches[1])) {
137
- $this->clone->prefix = $matches[1];
138
- } else {
139
- $this->log("Can not find Prefix. '{$matches[1]}'.");
140
- // Create a random prefix which highly like never exists
141
- return $this->clone->prefix = rand(7, 15) . '_';
142
- }
143
- }
144
- }
145
-
146
- // Check if staging prefix is the same as the live prefix
147
- if ($this->wpdb->prefix == $this->clone->prefix) {
148
- // Create a random prefix which highly like never exists
149
- return $this->clone->prefix = rand(7, 15) . '_';
150
- $this->log("Can not use prefix. '{$this->clone->prefix}', is used for the live site. Creating a new random prefix");
151
- //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");
152
- }
153
-
154
- // Else
155
- return $this->clone->prefix;
156
- }
157
-
158
- /**
159
- * Check if we can delete the staging site tables without affecting live ones
160
- * @return bool
161
- */
162
- public function isInvalidStagingPrefix() {
163
- // prefix not defined! Happens if staging site has ben generated with older version of wpstg
164
// Try to get staging prefix from wp-config.php of staging site
165
- //wp_die($this->clone->directoryName);
166
if (empty($this->clone->prefix)) {
167
// Throw error
168
$path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
@@ -189,11 +144,12 @@ class Delete extends Job {
189
190
// Check if staging prefix is the same as the live prefix
191
if ($this->wpdb->prefix == $this->clone->prefix) {
192
- return true;
193
}
194
195
// Else
196
- return false;
197
}
198
199
/**
@@ -257,23 +213,12 @@ class Delete extends Job {
257
return;
258
}
259
260
- // Skip deletion of tables and generate JOB to delete directories
261
- if ($this->isInvalidStagingPrefix()) {
262
- $this->log('Invalid staging prefix. Skip deleting tables');
263
- // Generate JOB and delete tables
264
- $this->job = (object) array(
265
- "current" => "directory",
266
- "nextDirectoryToDelete" => $this->clone->path,
267
- "name" => $this->clone->name
268
- );
269
- } else {
270
- // Generate JOB and delete tables
271
- $this->job = (object) array(
272
- "current" => "tables",
273
- "nextDirectoryToDelete" => $this->clone->path,
274
- "name" => $this->clone->name
275
- );
276
- }
277
278
$this->cache->save("delete_job_{$this->clone->name}", $this->job);
279
}
@@ -316,11 +261,12 @@ class Delete extends Job {
316
return;
317
}
318
319
320
foreach ($this->getTablesToRemove() as $table) {
321
// PROTECTION: Never delete any table that beginns with wp prefix of live site
322
if ($this->startsWith($table, $this->wpdb->prefix)) {
323
- $this->log("Fatal Error: Trying to delete table {$table} of main WP installation! Contact support[at]wp-staging.com", Logger::TYPE_CRITICAL);
324
return false;
325
} else {
326
$this->wpdb->query("DROP TABLE {$table}");
@@ -349,9 +295,16 @@ class Delete extends Job {
349
* @throws InvalidArgumentException
350
*/
351
public function deleteDirectory() {
352
-
353
// Finished or path does not exist
354
- if (!is_dir($this->clone->path)) {
355
$this->job->current = "finish";
356
$this->updateJob();
357
return $this->returnFinish();
@@ -360,7 +313,7 @@ class Delete extends Job {
360
$this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
361
362
// Just to make sure the root dir is never deleted!
363
- if ($this->clone->path === get_home_path()) {
364
$this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
365
$this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
366
}
@@ -374,7 +327,8 @@ class Delete extends Job {
374
$di = new \RecursiveDirectoryIterator($this->clone->path, \FilesystemIterator::SKIP_DOTS);
375
$ri = new \RecursiveIteratorIterator($di, \RecursiveIteratorIterator::CHILD_FIRST);
376
foreach ($ri as $file) {
377
- $file->isDir() ? @rmdir($file) : unlink($file);
378
if ($this->isOverThreshold()) {
379
//$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
380
return;
@@ -387,7 +341,21 @@ class Delete extends Job {
387
return;
388
}
389
390
-
391
392
/**
393
* @return bool
@@ -404,58 +372,69 @@ class Delete extends Job {
404
* @param string $path
405
* @return mixed
406
*/
407
- private function processDirectory($path) {
408
- // We hit the limit, stop
409
- if ($this->shouldStop($path)) {
410
- $this->updateJob();
411
- return false;
412
- }
413
-
414
- $this->totalRecursion++;
415
-
416
- $contents = new \DirectoryIterator($path);
417
-
418
- foreach ($contents as $content => $value) {
419
-
420
- // Skip dots
421
- if ($content->isDot())
422
-
423
-
424
- // Get into the directory
425
- if (!$content->isLink() && $content->isDir()) {
426
- return $this->processDirectory($content->getRealPath());
427
- }
428
-
429
- // Delete file
430
- if ($content->isFile()) {
431
- @unlink($content->getRealPath());
432
- }
433
- }
434
-
435
- // Delete directory
436
- $this->job->lastDeletedDirectory = realpath($path . "/..");
437
- @rmdir($path);
438
- $this->updateJob();
439
- $this->processDirectory($this->job->nextDirectoryToDelete);
440
- }
441
442
/**
443
* @param string $path
444
* @return bool
445
*/
446
- private function shouldStop($path) {
447
- // Just to make sure the root dir is never deleted!
448
- if ($path === get_home_path()) {
449
- $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
450
return true;
451
}
452
-
453
- // Check if threshold is reached and is valid dir
454
- return (
455
- $this->isOverThreshold() ||
456
- !is_dir($path) ||
457
- $this->isDirectoryDeletingFinished()
458
- );
459
}
460
461
/**
@@ -486,7 +465,18 @@ class Delete extends Job {
486
wp_die(json_encode($response));
487
}
488
489
-
490
/**
491
* Get json response
492
* return json
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}%'");
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";
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
/**
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
}
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}");
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();
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
}
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;
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
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
/**
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
apps/Backend/Modules/Jobs/Directories.php CHANGED
@@ -1,393 +1,444 @@
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
-
13
-
14
- /**
15
- * Class Files
16
- * @package WPStaging\Backend\Modules\Directories
17
- */
18
- class Directories extends JobExecutable {
19
-
20
- /**
21
- * @var array
22
- */
23
- private $files = array();
24
-
25
- /**
26
- * @var int
27
- */
28
- private $total = 0;
29
-
30
- /**
31
- * Initialize
32
- */
33
- public function initialize() {
34
- $this->total = count( $this->options->directoriesToCopy );
35
- $this->getFiles();
36
- }
37
-
38
- /**
39
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
40
- * @return void
41
- */
42
- protected function calculateTotalSteps() {
43
- $this->options->totalSteps = $this->total;
44
- }
45
-
46
- /**
47
- * Get Root Files
48
- */
49
- protected function getRootFiles() {
50
- if( 1 < $this->options->totalFiles ) {
51
- return;
52
- }
53
-
54
- $this->getFilesFromDirectory( ABSPATH );
55
- }
56
-
57
- /**
58
- * Start Module
59
- * @return object
60
- */
61
- public function start() {
62
- // Root files
63
- $this->getRootFiles();
64
-
65
- // Execute steps
66
- $this->run();
67
-
68
- // Save option, progress
69
- $this->saveProgress();
70
-
71
- return ( object ) $this->response;
72
- }
73
-
74
- /**
75
- * Execute the Current Step
76
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
77
- * @return bool
78
- */
79
- protected function execute() {
80
- // No job left to execute
81
- if( $this->isFinished() ) {
82
- $this->prepareResponse( true, false );
83
- return false;
84
- }
85
-
86
- // Get current directory
87
- $directory = $this->options->directoriesToCopy[$this->options->currentStep];
88
-
89
- // Get files recursively
90
- if( !$this->getFilesFromSubDirectories( $directory ) ) {
91
- $this->prepareResponse( false, false );
92
- return false;
93
- }
94
-
95
- // Add directory to scanned directories listing
96
- $this->options->scannedDirectories[] = $directory;
97
-
98
- // Prepare response
99
- $this->prepareResponse();
100
-
101
- // Not finished
102
- return true;
103
- }
104
-
105
- /**
106
- * Checks Whether There is Any Job to Execute or Not
107
- * @return bool
108
- */
109
- private function isFinished() {
110
- return (
111
- $this->options->currentStep > $this->total ||
112
- empty( $this->options->directoriesToCopy ) ||
113
- !isset( $this->options->directoriesToCopy[$this->options->currentStep] )
114
- );
115
- }
116
-
117
- /**
118
- * @param $path
119
- * @return bool
120
- */
121
- protected function getFilesFromSubDirectories( $path ) {
122
- $this->totalRecursion++;
123
-
124
- if( $this->isOverThreshold() ) {
125
- //$this->saveProgress();
126
- return false;
127
- }
128
-
129
- $this->log( "Scanning {$path} for its sub-directories and files" );
130
-
131
- $directories = new \DirectoryIterator( $path );
132
-
133
- foreach ( $directories as $directory ) {
134
-
135
-
136
- // Not a valid directory
137
- if( false === ($path = $this->getPath( $directory )) ) {
138
- continue;
139
- }
140
-
141
- // Excluded directory
142
- if( $this->isDirectoryExcluded( $directory->getRealPath() ) ) {
143
- continue;
144
- }
145
-
146
- // This directory is already scanned
147
- if( in_array( $path, $this->options->scannedDirectories ) ) {
148
- continue;
149
- }
150
-
151
- // Save all files
152
- $dir = ABSPATH . $path . DIRECTORY_SEPARATOR;
153
- $this->getFilesFromDirectory( $dir );
154
-
155
- // Add scanned directory listing
156
- $this->options->scannedDirectories[] = $dir;
157
-
158
- if( $this->isOverThreshold() ) {
159
- //$this->saveProgress();
160
- return false;
161
- }
162
- }
163
-
164
- $this->saveOptions();
165
-
166
- // Not finished
167
- return true;
168
- }
169
-
170
- /**
171
- * Get files from directory
172
- * @param $directory
173
- * @return bool
174
- */
175
- protected function getFilesFromDirectory( $directory ) {
176
- $this->totalRecursion++;
177
-
178
- // Get only files
179
- $files = array_diff( scandir( $directory ), array('.', "..") );
180
-
181
- foreach ( $files as $file ) {
182
- $fullPath = $directory . $file;
183
-
184
- // Conditions:
185
- // - Must be valid file
186
- // - Is readable file
187
- // - Not collected already
188
- // - File not excluded by another rule or condition
189
-
190
- if( is_file( $fullPath ) && is_readable( $fullPath ) && !in_array( $fullPath, $this->files ) && !$this->isExcluded($file) ) {
191
- $this->options->totalFiles++;
192
- $this->files[] = $fullPath;
193
- continue;
194
- }
195
- // It's a valid file but not readable
196
- if( is_file( $fullPath ) && !is_readable( $fullPath ) ) {
197
- $this->debugLog('File {$fullPath} is not readable', Logger::TYPE_DEBUG );
198
- continue;
199
- }
200
-
201
- // Iterate and loop through if it's a directory and if it's not excluded
202
- if( is_dir( $fullPath ) && !in_array( $fullPath, $this->options->directoriesToCopy ) && !$this->isDirectoryExcluded( $fullPath ) ) {
203
- $this->options->directoriesToCopy[] = $fullPath;
204
-
205
- //return $this->getFilesFromSubDirectories( $fullPath );
206
- //continue;
207
- $this->getFilesFromSubDirectories( $fullPath );
208
- continue;
209
- }
210
-
211
- // if( !is_file( $fullPath ) || in_array( $fullPath, $this->files ) ) {
212
- // continue;
213
- // }
214
-
215
- // $this->options->totalFiles++;
216
- //
217
- // $this->files[] = $fullPath;
218
-
219
- /**
220
- * Test and measure if its faster to copy at the same time while the array with folders is generated
221
- */
222
- //$this->copy($fullPath);
223
-
224
- }
225
- }
226
-
227
- /**
228
- * Get Path from $directory
229
- * @param \SplFileInfo $directory
230
- * @return string|false
231
- */
232
- protected function getPath( $directory ) {
233
-
234
- /*
235
- * Do not follow root path like src/web/..
236
- * This must be done before \SplFileInfo->isDir() is used!
237
- * Prevents open base dir restriction fatal errors
238
- */
239
- if (strpos( $directory->getRealPath(), ABSPATH ) !== 0 ) {
240
- return false;
241
- }
242
-
243
- $path = str_replace( ABSPATH, null, $directory->getRealPath() );
244
-
245
- // Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
246
- if( !$directory->isDir() || strlen( $path ) < 1 ) {
247
- return false;
248
- }
249
-
250
- return $path;
251
- }
252
-
253
- /**
254
- * Check if directory is excluded from copying
255
- * @param string $directory
256
- * @return bool
257
- */
258
- protected function isDirectoryExcluded( $directory ) {
259
- foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
260
- if( strpos( $directory, $excludedDirectory ) === 0 && !$this->isExtraDirectory( $directory ) ) {
261
- return true;
262
- }
263
- }
264
-
265
- return false;
266
- }
267
-
268
- /**
269
- * Check if directory is an extra directory and should be copied
270
- * @param string $directory
271
- * @return boolean
272
- */
273
- protected function isExtraDirectory( $directory ) {
274
- foreach ( $this->options->extraDirectories as $extraDirectory ) {
275
- if( strpos( $directory, $extraDirectory ) === 0 ) {
276
- return true;
277
- }
278
- }
279
-
280
- return false;
281
- }
282
-
283
- /**
284
- * Save files
285
- * @return bool
286
- */
287
- protected function saveProgress() {
288
- $this->saveOptions();
289
-
290
- $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
291
- $files = implode( PHP_EOL, $this->files );
292
-
293
- if( strlen( $files ) > 0 ) {
294
- //$files .= PHP_EOL;
295
- }
296
-
297
- return (false !== @file_put_contents( $fileName, $files ));
298
- }
299
-
300
- /**
301
- * Get files
302
- * @return void
303
- */
304
- protected function getFiles() {
305
- $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
306
-
307
- if( false === ($this->files = @file_get_contents( $fileName )) ) {
308
- $this->files = array();
309
- return;
310
- }
311
-
312
- $this->files = explode( PHP_EOL, $this->files );
313
- }
314
-
315
- /**
316
- * Copy File using PHP (Only for testing)
317
- * @param string $file
318
- * @param string $destination
319
- * @return bool
320
- *
321
- * @deprecated since version 2.0.2
322
- */
323
- protected function copy($file)
324
- {
325
-
326
- if( $this->isOverThreshold() ) {
327
- return false;
328
- }
329
-
330
- // Failed to get destination
331
- if (false === ($destination = $this->getDestination($file)))
332
- {
333
- //$this->log("Can't get the destination of {$file}");
334
- //return false;
335
- }
336
-
337
- // Attempt to copy
338
- if (!@copy($file, $destination))
339
- {
340
- //$this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
341
- //return false;
342
- }
343
-
344
- //$this->log("Copy {$file} -> {$destination}", Logger::TYPE_INFO);
345
-
346
- // Not finished
347
- return true;
348
- }
349
-
350
-
351
- /**
352
- * (only for testing)
353
- * Gets destination file and checks if the directory exists, if it does not attempts to create it.
354
- * If creating destination directory fails, it returns false, gives destination full path otherwise
355
- * @param string $file
356
- * @return bool|string
357
- *
358
- * @deprecated
359
- */
360
- private function getDestination($file)
361
- {
362
- $destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
363
- $relativePath = str_replace(ABSPATH, null, $file);
364
- $destinationPath = $destination . $relativePath;
365
- $destinationDirectory = dirname($destinationPath);
366
-
367
- if (!is_dir($destinationDirectory) && !@mkdir($destinationDirectory, 0775, true))
368
- {
369
- $this->log("Destination directory doesn't exist; {$destinationDirectory}", Logger::TYPE_ERROR);
370
- //return false;
371
- }
372
-
373
- return $destinationPath;
374
- }
375
-
376
- /**
377
- * Check if filename is excluded for cloning process
378
- *
379
- * @param string $file filename including ending
380
- * @return boolean
381
- */
382
- private function isExcluded( $file ) {
383
- $excluded = false;
384
- foreach ( $this->options->excludedFiles as $excludedFile ) {
385
- if (stripos(strrev($file), strrev($excludedFile)) === 0) {
386
- $excluded = true;
387
- break;
388
- }
389
- }
390
- return $excluded;
391
- }
392
-
393
- }
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 content directory
79
+ $iterator = new \DirectoryIterator(ABSPATH);
80
+
81
+ // Write path line
82
+ foreach ($iterator as $item) {
83
+ if ($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
+ throw new \Exception('Out of disk space.');
94
+ } catch (\Exception $e) {
95
+ // Skip bad file permissions
96
+ }
97
+
98
+ $this->close($files);
99
+ return true;
100
+
101
+ }
102
+
103
+ /**
104
+ * Step 2
105
+ * Get WP Content Files
106
+ */
107
+ public function getWpContentFiles() {
108
+
109
+ // if (1 < $this->options->totalFiles) {
110
+ // return;
111
+ // }
112
+
113
+ // open file handle
114
+ $files = $this->open($this->filename, 'a');
115
+
116
+ $excludeWpContent = array(
117
+ 'HUGE-FOLDER',
118
+ 'cache',
119
+ 'wps-hide-login',
120
+ 'node_modules',
121
+ 'nbproject'
122
+ );
123
+
124
+ try {
125
+
126
+ // Iterate over content directory
127
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(WP_CONTENT_DIR);
128
+
129
+ // Exclude new line file names
130
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
131
+
132
+ // Exclude uploads, plugins or themes
133
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $excludeWpContent));
134
+ // Recursively iterate over content directory
135
+ $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
136
+
137
+ // Write path line
138
+ foreach ($iterator as $item) {
139
+ if ($item->isFile()) {
140
+ if ($this->write($files, 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
141
+ $this->options->totalFiles++;
142
+
143
+ // Add current file size
144
+ $this->options->totalFileSize += $iterator->getSize();
145
+ }
146
+ }
147
+ }
148
+ } catch (\Exception $e) {
149
+ throw new \Exception('Out of disk space.');
150
+ } catch (\Exception $e) {
151
+ // Skip bad file permissions
152
+ }
153
+
154
+ // close the file handler
155
+ $this->close($files);
156
+ return true;
157
+ }
158
+
159
+ /**
160
+ * Step 3
161
+ * @return boolean
162
+ * @throws \Exception
163
+ */
164
+ public function getWpIncludesFiles() {
165
+
166
+ // open file handle and attach data to end of file
167
+ $files = $this->open($this->filename, 'a');
168
+
169
+ try {
170
+
171
+ // Iterate over wp-admin directory
172
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR);
173
+
174
+ // Exclude new line file names
175
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
176
+
177
+ // Exclude uploads, plugins or themes
178
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
179
+ // Recursively iterate over wp-includes directory
180
+ $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
181
+
182
+ // Write files
183
+ foreach ($iterator as $item) {
184
+ if ($item->isFile()) {
185
+ if ($this->write($files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
186
+ $this->options->totalFiles++;
187
+
188
+ // Add current file size
189
+ $this->options->totalFileSize += $iterator->getSize();
190
+ }
191
+ }
192
+ }
193
+ } catch (\Exception $e) {
194
+ throw new \Exception('Out of disk space.');
195
+ } catch (\Exception $e) {
196
+ // Skip bad file permissions
197
+ }
198
+
199
+ // close the file handler
200
+ $this->close($files);
201
+ return true;
202
+ }
203
+
204
+ /**
205
+ * Step 4
206
+ * @return boolean
207
+ * @throws \Exception
208
+ */
209
+ public function getWpAdminFiles() {
210
+
211
+ // open file handle and attach data to end of file
212
+ $files = $this->open($this->filename, 'a');
213
+
214
+ try {
215
+
216
+ // Iterate over wp-admin directory
217
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR);
218
+
219
+ // Exclude new line file names
220
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
221
+
222
+ // Exclude uploads, plugins or themes
223
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
224
+ // Recursively iterate over content directory
225
+ $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
226
+
227
+ // Write path line
228
+ foreach ($iterator as $item) {
229
+ if ($item->isFile()) {
230
+ if ($this->write($files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
231
+ $this->options->totalFiles++;
232
+ // Add current file size
233
+ $this->options->totalFileSize += $iterator->getSize();
234
+ }
235
+ }
236
+ }
237
+ } catch (\Exception $e) {
238
+ throw new \Exception('Out of disk space.');
239
+ } catch (\Exception $e) {
240
+ // Skip bad file permissions
241
+ }
242
+
243
+ // close the file handler
244
+ $this->close($files);
245
+ return true;
246
+ }
247
+
248
+ /**
249
+ * Closes a file handle
250
+ *
251
+ * @param resource $handle File handle to close
252
+ * @return boolean
253
+ */
254
+ public function close($handle) {
255
+ return @fclose($handle);
256
+ }
257
+
258
+ /**
259
+ * Opens a file in specified mode
260
+ *
261
+ * @param string $file Path to the file to open
262
+ * @param string $mode Mode in which to open the file
263
+ * @return resource
264
+ * @throws Exception
265
+ */
266
+ public function open($file, $mode) {
267
+
268
+ $file_handle = @fopen($file, $mode);
269
+ if (false === $file_handle) {
270
+ throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wpstg'), $file, $mode));
271
+ }
272
+
273
+ return $file_handle;
274
+ }
275
+
276
+ /**
277
+ * Write contents to a file
278
+ *
279
+ * @param resource $handle File handle to write to
280
+ * @param string $content Contents to write to the file
281
+ * @return integer
282
+ * @throws Exception
283
+ * @throws Exception
284
+ */
285
+ public function write($handle, $content) {
286
+ $write_result = @fwrite($handle, $content);
287
+ if (false === $write_result) {
288
+ if (( $meta = \stream_get_meta_data($handle))) {
289
+ throw new \Exception(sprintf(__('Unable to write to: %s', 'wpstg'), $meta['uri']));
290
+ }
291
+ } elseif (strlen($content) !== $write_result) {
292
+ throw new \Exception(__('Out of disk space.', 'wpstg'));
293
+ }
294
+
295
+ return $write_result;
296
+ }
297
+
298
+ /**
299
+ * Execute the Current Step
300
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
301
+ * @return bool
302
+ */
303
+ protected function execute() {
304
+
305
+ // No job left to execute
306
+ if ($this->isFinished()) {
307
+ $this->prepareResponse(true, false);
308
+ return false;
309
+ }
310
+
311
+
312
+ if ($this->options->currentStep == 0) {
313
+ $this->getWpRootFiles();
314
+ $this->prepareResponse(false, true);
315
+ return false;
316
+ }
317
+
318
+ if ($this->options->currentStep == 1) {
319
+ $this->getWpContentFiles();
320
+ $this->prepareResponse(false, true);
321
+ return false;
322
+ }
323
+
324
+ if ($this->options->currentStep == 2) {
325
+ $this->getWpIncludesFiles();
326
+ $this->prepareResponse(false, true);
327
+ return false;
328
+ }
329
+
330
+ if ($this->options->currentStep == 3) {
331
+ $this->getWpAdminFiles();
332
+ $this->prepareResponse(false, true);
333
+ return false;
334
+ }
335
+
336
+
337
+ // Prepare response
338
+ $this->prepareResponse(false, true);
339
+ // Not finished
340
+ return true;
341
+ }
342
+
343
+ /**
344
+ * Checks Whether There is Any Job to Execute or Not
345
+ * @return bool
346
+ */
347
+ protected function isFinished() {
348
+ return (
349
+ $this->options->currentStep > $this->total ||
350
+ $this->options->currentStep == 4
351
+ );
352
+ }
353
+
354
+ /**
355
+ * Get Path from $directory
356
+ * @param \SplFileInfo $directory
357
+ * @return string|false
358
+ */
359
+ // protected function getPath($directory) {
360
+ //
361
+ // /*
362
+ // * Do not follow root path like src/web/..
363
+ // * This must be done before \SplFileInfo->isDir() is used!
364
+ // * Prevents open base dir restriction fatal errors
365
+ // */
366
+ // if (strpos($directory->getRealPath(), ABSPATH) !== 0) {
367
+ // return false;
368
+ // }
369
+ //
370
+ // $path = str_replace(ABSPATH, null, $directory->getRealPath());
371
+ //
372
+ // // Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
373
+ // if (!$directory->isDir() || strlen($path) < 1) {
374
+ // return false;
375
+ // }
376
+ //
377
+ // return $path;
378
+ // }
379
+
380
+ /**
381
+ * Check if directory is excluded from copying
382
+ * @param string $directory
383
+ * @return bool
384
+ */
385
+ // protected function isDirectoryExcluded($directory) {
386
+ // foreach ($this->options->excludedDirectories as $excludedDirectory) {
387
+ // if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
388
+ // return true;
389
+ // }
390
+ // }
391
+ //
392
+ // return false;
393
+ // }
394
+
395
+ /**
396
+ * Check if directory is an extra directory and should be copied
397
+ * @param string $directory
398
+ * @return boolean
399
+ */
400
+ // protected function isExtraDirectory($directory) {
401
+ // foreach ($this->options->extraDirectories as $extraDirectory) {
402
+ // if (strpos($directory, $extraDirectory) === 0) {
403
+ // return true;
404
+ // }
405
+ // }
406
+ //
407
+ // return false;
408
+ // }
409
+
410
+ /**
411
+ * Save files
412
+ * @return bool
413
+ */
414
+ protected function saveProgress() {
415
+ return $this->saveOptions();
416
+
417
+ // $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
418
+ // $files = implode(PHP_EOL, $this->files);
419
+ //
420
+ // if (strlen($files) > 0) {
421
+ // //$files .= PHP_EOL;
422
+ // }
423
+ //
424
+ // return (false !== @file_put_contents($fileName, $files));
425
+ }
426
+
427
+ /**
428
+ * Get files
429
+ * @return void
430
+ */
431
+ protected function getFiles() {
432
+ $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
433
+
434
+ if (false === ($this->files = @file_get_contents($fileName))) {
435
+ $this->files = array();
436
+ return;
437
+ }
438
+
439
+ $this->files = explode(PHP_EOL, $this->files);
440
+ }
441
+
442
+
443
+
444
+ }
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -23,9 +23,8 @@ class Files extends JobExecutable {
23
/**
24
* @var int
25
*/
26
- //private $maxFilesPerRun = 500;
27
private $maxFilesPerRun;
28
-
29
/**
30
* File Object
31
*/
@@ -40,8 +39,7 @@ class Files extends JobExecutable {
40
* Initialization
41
*/
42
public function initialize() {
43
- //$this->getVerifyFiles();
44
-
45
$this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
46
47
$filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
@@ -57,8 +55,6 @@ class Files extends JobExecutable {
57
58
$this->settings->batchSize = $this->settings->batchSize * 1000000;
59
$this->maxFilesPerRun = $this->settings->fileLimit;
60
- //$this->maxFilesPerRun = 1;
61
- //$this->options->copiedFiles = 0;
62
}
63
64
/**
@@ -67,7 +63,6 @@ class Files extends JobExecutable {
67
*/
68
protected function calculateTotalSteps() {
69
$this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
70
- //$this->options->totalSteps = $this->options->totalFiles;
71
}
72
73
/**
@@ -112,22 +107,15 @@ class Files extends JobExecutable {
112
// Go to last copied line and than to next one
113
//if ($this->options->copiedFiles != 0) {
114
if (isset($this->options->copiedFiles) && $this->options->copiedFiles != 0) {
115
- $this->file->seek($this->options->copiedFiles-1);
116
}
117
118
$this->file->setFlags(\SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
119
120
- // End of file
121
- // if ($this->file->eof())
122
- // {
123
- // // Increment copied files when end of file is reached
124
- // $this->options->copiedFiles++;
125
- // return;
126
- // }
127
// Loop x files at a time
128
for ($i = 0; $i < $this->maxFilesPerRun; $i++) {
129
-
130
-
131
// Increment copied files
132
// Do this anytime to make sure to not stuck in the same step / files
133
$this->options->copiedFiles++;
@@ -136,19 +124,16 @@ class Files extends JobExecutable {
136
if ($this->file->eof()) {
137
break;
138
}
139
-
140
141
$file = $this->file->fgets();
142
$this->debugLog('copy file ' . $file, Logger::TYPE_DEBUG);
143
$this->copyFile($file);
144
- //$this->verifyFiles[] = $file;
145
-
146
}
147
148
//$totalFiles = $this->maxFilesPerRun + $this->options->copiedFiles;
149
$totalFiles = $this->options->copiedFiles;
150
$this->log("Total {$totalFiles} files processed");
151
- //$this->saveCopiedFiles();
152
153
return true;
154
}
@@ -169,7 +154,30 @@ class Files extends JobExecutable {
169
* @return bool
170
*/
171
private function copyFile($file) {
172
- $file = trim($file);
173
174
// Invalid file, skipping it as if succeeded
175
if (!is_file($file) || !is_readable($file)) {
@@ -183,8 +191,22 @@ class Files extends JobExecutable {
183
return false;
184
}
185
186
// Good old PHP
187
- return $this->copy($file, $destination);
188
}
189
190
/**
@@ -212,24 +234,25 @@ class Files extends JobExecutable {
212
* @param string $destination
213
* @return bool
214
*/
215
- private function copy($file, $destination) {
216
- // Get file size
217
- $fileSize = filesize($file);
218
-
219
- // File is over batch size
220
- if ($fileSize >= $this->settings->batchSize) {
221
- $this->log("Trying to copy big file {$file} -> {$destination}", Logger::TYPE_INFO);
222
- return $this->copyBig($file, $destination, $this->settings->batchSize);
223
- }
224
-
225
- // Attempt to copy
226
- if (!@copy($file, $destination)) {
227
- $this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
228
- return false;
229
- }
230
-
231
- return true;
232
- }
233
234
/**
235
* Copy bigger files than $this->settings->batchSize
@@ -291,31 +314,52 @@ class Files extends JobExecutable {
291
fclose($dest);
292
return true;
293
}
294
-
295
/**
296
- * Save verified Files
297
* @return bool
298
*/
299
- // private function saveCopiedFiles(){
300
- // $fileName = $this->cache->getCacheDir() . "files_to_verify." . $this->cache->getCacheExtension();
301
- // $files = implode( PHP_EOL, $this->verifyFiles );
302
- //
303
- // return (false !== @file_put_contents( $fileName, $files ));
304
- // }
305
-
306
/**
307
- * Get files
308
- * @return void
309
*/
310
- // protected function getVerifyFiles() {
311
- // $fileName = $this->cache->getCacheDir() . "files_to_verify." . $this->cache->getCacheExtension();
312
- //
313
- // if( false === ($this->verifyFiles = @file_get_contents( $fileName )) ) {
314
- // $this->verifyFiles = array();
315
- // return;
316
- // }
317
- //
318
- // $this->verifyFiles = explode( PHP_EOL, $this->verifyFiles );
319
- // }
320
321
}
23
/**
24
* @var int
25
*/
26
private $maxFilesPerRun;
27
+
28
/**
29
* File Object
30
*/
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();
55
56
$this->settings->batchSize = $this->settings->batchSize * 1000000;
57
$this->maxFilesPerRun = $this->settings->fileLimit;
58
}
59
60
/**
63
*/
64
protected function calculateTotalSteps() {
65
$this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
66
}
67
68
/**
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++;
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
}
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)) {
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
/**
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
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
}
apps/Backend/Modules/Jobs/Job.php CHANGED
@@ -380,19 +380,19 @@ abstract class Job implements JobInterface
380
* @deprecated since version 2.0.0
381
382
*/
383
- protected function resetTime()
384
- {
385
- // Attempt to reset timeout
386
- if (!@set_time_limit($this->maxExecutionTime))
387
- {
388
- return false;
389
- }
390
-
391
- // Increase execution limit
392
- $this->executionLimit = $this->executionLimit * 2;
393
-
394
- return true;
395
- }
396
397
/**
398
* Reset time limit and memory
@@ -400,22 +400,22 @@ abstract class Job implements JobInterface
400
*
401
* @deprecated since version 2.0.0
402
*/
403
- protected function reset()
404
- {
405
- // Attempt to reset time
406
- if (!$this->resetTime())
407
- {
408
- return false;
409
- }
410
-
411
- // Attempt to reset memory
412
- if (!$this->resetMemory())
413
- {
414
- return false;
415
- }
416
-
417
- return true;
418
- }
419
420
/**
421
* Checks if calls are over recursion limit
@@ -475,7 +475,7 @@ abstract class Job implements JobInterface
475
*/
476
protected function returnException($message = ''){
477
wp_die( json_encode(array(
478
- 'job' => $this->options->currentJob,
479
'status' => false,
480
'message' => $message,
481
'error' => true
380
* @deprecated since version 2.0.0
381
382
*/
383
+ // protected function resetTime()
384
+ // {
385
+ // // Attempt to reset timeout
386
+ // if (!@set_time_limit($this->maxExecutionTime))
387
+ // {
388
+ // return false;
389
+ // }
390
+ //
391
+ // // Increase execution limit
392
+ // $this->executionLimit = $this->executionLimit * 2;
393
+ //
394
+ // return true;
395
+ // }
396
397
/**
398
* Reset time limit and memory
400
*
401
* @deprecated since version 2.0.0
402
*/
403
+ // protected function reset()
404
+ // {
405
+ // // Attempt to reset time
406
+ // if (!$this->resetTime())
407
+ // {
408
+ // return false;
409
+ // }
410
+ //
411
+ // // Attempt to reset memory
412
+ // if (!$this->resetMemory())
413
+ // {
414
+ // return false;
415
+ // }
416
+ //
417
+ // return true;
418
+ // }
419
420
/**
421
* Checks if calls are over recursion limit
475
*/
476
protected function returnException($message = ''){
477
wp_die( json_encode(array(
478
+ 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
479
'status' => false,
480
'message' => $message,
481
'error' => true
apps/Backend/Modules/Jobs/Scan.php CHANGED
@@ -69,8 +69,10 @@ class Scan extends Job
69
70
// Files
71
$this->options->totalFiles = 0;
72
$this->options->copiedFiles = 0;
73
74
// Directories
75
$this->options->includedDirectories = array();
76
$this->options->excludedDirectories = 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();
apps/Backend/public/css/wpstg-admin.css CHANGED
@@ -337,9 +337,10 @@ color:#777777;
337
338
#wpstg-error-wrapper,
339
#wpstg-error-details {
340
- display: none;
341
- margin-top: 10px;
342
font-size: 13px;
343
}
344
345
#wpstg-show-error-details {
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 {
apps/Core/Iterators/RecursiveDirectoryIterator.php ADDED
@@ -0,0 +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
+ }
apps/Core/Iterators/RecursiveFilterExclude.php ADDED
@@ -0,0 +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
+ }
apps/Core/Iterators/RecursiveFilterNewLine.php ADDED
@@ -0,0 +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
+
apps/Core/Utils/Cache.php CHANGED
@@ -1,197 +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
-
12
- /**
13
- * Class Cache
14
- * @package WPStaging\Utils
15
- */
16
- class Cache
17
- {
18
- /**
19
- * Cache directory (full path)
20
- * @var string
21
- */
22
- private $cacheDir;
23
-
24
- /**
25
- * Cache file extension
26
- * @var string
27
- */
28
- private $cacheExtension = "cache";
29
-
30
- /**
31
- * Lifetime of cache files in seconds
32
- * @var int
33
- */
34
- private $lifetime = 2592000; // 30 days
35
-
36
- /**
37
- * Cache constructor.
38
- * @param null|int $lifetime
39
- * @param null|string $cacheDir
40
- * @param null|string $cacheExtension
41
- * @throws \Exception
42
- */
43
- public function __construct($lifetime = null, $cacheDir = null, $cacheExtension = null)
44
- {
45
- // Set lifetime
46
- $lifetime = (int) $lifetime;
47
- if ($lifetime > 0)
48
- {
49
- $this->lifetime = $lifetime;
50
- }
51
-
52
- // Set cache directory
53
- if (!empty($cacheDir) && is_dir($cacheDir))
54
- {
55
- $this->cacheDir = $cacheDir;
56
- }
57
- // Set default
58
- else
59
- {
60
-
61
- $this->cacheDir = \WPStaging\WPStaging::getContentDir();
62
- }
63
-
64
- // Set cache extension
65
- if (!empty($cacheExtension))
66
- {
67
- $this->cacheExtension = $cacheExtension;
68
- }
69
-
70
- // If cache directory doesn't exists, create it
71
- if (!is_dir($this->cacheDir) && !@mkdir($this->cacheDir, 0775, true))
72
- {
73
- 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.');
74
- }
75
- }
76
-
77
- /**
78
- * Get cache
79
- * @param string $cacheFileName
80
- * @param mixed $defaultValue
81
- * @param null|int $lifetime
82
- * @return mixed|null
83
- */
84
- public function get($cacheFileName, $defaultValue = null, $lifetime = null)
85
- {
86
- // Check if file is valid
87
- if (false === ($cacheFile = $this->isValid($cacheFileName, true, $lifetime)))
88
- {
89
- return $defaultValue;
90
- }
91
-
92
- return @unserialize(file_get_contents($cacheFile));
93
- }
94
-
95
- /**
96
- * Saves value to given cache file
97
- * @param string $cacheFileName
98
- * @param mixed $value
99
- * @return bool
100
- * @throws \Exception
101
- */
102
- public function save($cacheFileName, $value)
103
- {
104
- $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
105
-
106
- // Attempt to delete cache file if it exists
107
- if (is_file($cacheFile) && !@unlink($cacheFile))
108
- {
109
- throw new \Exception("Can't delete existing cache file");
110
- }
111
-
112
- // Save it to file
113
- return (file_put_contents($cacheFile, @serialize($value), LOCK_EX) !== false);
114
- //return (file_put_contents($cacheFile, @serialize($value)) !== false);
115
- }
116
-
117
- /**
118
- * Checks if file is valid or not
119
- * @param $cacheFileName
120
- * @param bool $deleteFileIfInvalid
121
- * @param null|int $lifetime
122
- * @return string|bool
123
- * @throws \Exception
124
- */
125
- public function isValid($cacheFileName, $deleteFileIfInvalid = false, $lifetime = null)
126
- {
127
- // Lifetime
128
- $lifetime = (int) $lifetime;
129
- if (-1 > $lifetime || 0 == $lifetime)
130
- {
131
- $lifetime = $this->lifetime;
132
- }
133
-
134
- // Get full path of the given cache file
135
- $cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
136
-
137
- // File doesn't exist
138
- if (!is_file($cacheFile))
139
- {
140
- return false;
141
- }
142
-
143
- // As long as file exists, don't check lifetime
144
- if (-1 == $lifetime)
145
- {
146
- return $cacheFile;
147
- }
148
-
149
- // Time is up, file is invalid
150
- if (time() - filemtime($cacheFile) >= $lifetime)
151
- {
152
-
153
- // Attempt to delete the file
154
- if ($deleteFileIfInvalid === true && !@unlink($cacheFile))
155
- {
156
- throw new \Exception("Attempting to delete invalid cache file has failed!");
157
- }
158
-
159
- // No need to delete the file, return
160
- return false;
161
- }
162
-
163
- return $cacheFile;
164
- }
165
-
166
- /**
167
- * Delete a cache file
168
- * @param string $cacheFileName
169
- * @return bool
170
- * @throws \Exception
171
- */
172
- public function delete($cacheFileName)
173
- {
174
- if (false !== ($cacheFile = $this->isValid($cacheFileName, true)) && false === @unlink($cacheFile))
175
- {
176
- throw new \Exception("Couldn't delete cache: {$cacheFileName}. Full Path: {$cacheFile}");
177
- }
178
-
179
- return true;
180
- }
181
-
182
- /**
183
- * @return string
184
- */
185
- public function getCacheDir()
186
- {
187
- return $this->cacheDir;
188
- }
189
-
190
- /**
191
- * @return string
192
- */
193
- public function getCacheExtension()
194
- {
195
- return $this->cacheExtension;
196
- }
197
}
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/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
/**
30
* Plugin version
31
*/
32
- const VERSION = "2.1.5";
33
34
/**
35
* Plugin name
@@ -44,7 +44,7 @@ final class WPStaging {
44
/**
45
* Compatible WP Version
46
*/
47
- const WP_COMPATIBLE = "4.9.1";
48
49
/**
50
* Slug: Either wp-staging or wp-staging-pro
29
/**
30
* Plugin version
31
*/
32
+ const VERSION = "2.1.6";
33
34
/**
35
* Plugin name
44
/**
45
* Compatible WP Version
46
*/
47
+ const WP_COMPATIBLE = "4.9.0";
48
49
/**
50
* Slug: Either wp-staging or wp-staging-pro
package-lock.json ADDED
@@ -0,0 +1,3333 @@