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

Version Description

  • New: Exclude unnecessary files from cloning process: .tmp, .log, .htaccess, .git, .gitignore, desktop.ini, .DS_Store, .svn
  • New: More details for debugging in Tools->System Info
  • Fix: Check if tables in staging site exists before attempting to modify them
  • Fix: WordPress in sub directories were not opening
  • 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
  • Fix: Access to staging site not working, if WP_SITEURL and WP_HOME is defined in wp-config.php
  • Tweak: Exclude wp-content/cache folder from copying process
Download this release

Release Info

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

Code changes from version 2.0.8 to 2.1.0

apps/Backend/Modules/Jobs/Cloning.php CHANGED
@@ -3,10 +3,6 @@ namespace WPStaging\Backend\Modules\Jobs;
3
 
4
  use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
 
6
- //ini_set('display_startup_errors', 1);
7
- //ini_set('display_errors', 1);
8
- //error_reporting(-1);
9
-
10
  /**
11
  * Class Cloning
12
  * @package WPStaging\Backend\Modules\Jobs
@@ -34,6 +30,7 @@ class Cloning extends Job
34
  $this->options->includedDirectories = array();
35
  $this->options->excludedDirectories = array();
36
  $this->options->extraDirectories = array();
 
37
 
38
  // Job
39
  $this->options->job = new \stdClass();
@@ -58,6 +55,15 @@ class Cloning extends Job
58
  {
59
  $this->options->excludedDirectories = $_POST["excludedDirectories"];
60
  }
 
 
 
 
 
 
 
 
 
61
 
62
  // Included Directories
63
  if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
@@ -78,9 +84,7 @@ class Cloning extends Job
78
  );
79
 
80
  array_unshift($this->options->directoriesToCopy, ABSPATH);
81
-
82
- //var_dump($this->options->directoriesToCopy);
83
-
84
  // Delete files to copy listing
85
  $this->cache->delete("files_to_copy");
86
 
3
 
4
  use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
 
 
 
 
 
6
  /**
7
  * Class Cloning
8
  * @package WPStaging\Backend\Modules\Jobs
30
  $this->options->includedDirectories = array();
31
  $this->options->excludedDirectories = array();
32
  $this->options->extraDirectories = array();
33
+ $this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
34
 
35
  // Job
36
  $this->options->job = new \stdClass();
55
  {
56
  $this->options->excludedDirectories = $_POST["excludedDirectories"];
57
  }
58
+
59
+ // Excluded Directories TOTAL
60
+ // Do not copy these folders and plugins
61
+ $excludedDirectories = array(
62
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
63
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login'
64
+ );
65
+
66
+ $this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
67
 
68
  // Included Directories
69
  if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
84
  );
85
 
86
  array_unshift($this->options->directoriesToCopy, ABSPATH);
87
+
 
 
88
  // Delete files to copy listing
89
  $this->cache->delete("files_to_copy");
90
 
apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -157,39 +157,74 @@ class Data extends JobExecutable
157
 
158
  return false;
159
  }
160
-
 
161
  /**
162
- * Replace "siteurl"
163
- * @return bool
 
164
  */
165
- protected function step1()
166
- {
167
- $this->log( "Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
168
-
169
- $result = $this->db->query(
170
- $this->db->prepare(
171
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'",
172
- get_home_url() . '/' . $this->options->cloneDirectoryName
173
- )
174
- );
175
-
176
- // All good
177
- if ($result)
178
- {
179
- return true;
180
- }
181
-
182
- $this->log("Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
183
- return false;
184
  }
185
 
186
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  * Update "wpstg_is_staging_site"
188
  * @return bool
189
  */
190
  protected function step2()
191
  {
 
192
  $this->log( "Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
 
 
 
 
193
 
194
  $result = $this->db->query(
195
  $this->db->prepare(
@@ -225,8 +260,13 @@ class Data extends JobExecutable
225
  */
226
  protected function step3()
227
  {
 
228
  $this->log("Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}");
229
 
 
 
 
 
230
  $result = $this->db->query(
231
  $this->db->prepare(
232
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'",
@@ -250,6 +290,10 @@ class Data extends JobExecutable
250
  */
251
  protected function step4() {
252
  $this->log( "Updating {$this->prefix}usermeta db prefix {$this->db->last_error}" );
 
 
 
 
253
 
254
  $resultOptions = $this->db->query(
255
  $this->db->prepare(
@@ -297,7 +341,7 @@ class Data extends JobExecutable
297
  $content = str_replace('$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content);
298
 
299
  // Replace URLs
300
- $content = str_replace(get_home_url(), get_home_url() . $this->options->cloneDirectoryName, $content);
301
 
302
  if (false === @file_put_contents($path, $content))
303
  {
157
 
158
  return false;
159
  }
160
+
161
+
162
  /**
163
+ * Check if table exists
164
+ * @param string $table
165
+ * @return boolean
166
  */
167
+ protected function isTable($table){
168
+ if($this->db->get_var("SHOW TABLES LIKE '{$table}'") != $table ){
169
+ $this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
170
+ return false;
171
+ }
172
+ return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
174
 
175
  /**
176
+ * Replace "siteurl"
177
+ * @return bool
178
+ */
179
+ protected function step1() {
180
+ $this->log( "Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
181
+
182
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
183
+ return true;
184
+ }
185
+
186
+ // Installed in sub-directory
187
+ if( isset( $this->settings->wpSubDirectory ) && "1" === $this->settings->wpSubDirectory ) {
188
+ $subDirectory = str_replace( get_home_path(), '', ABSPATH );
189
+ $this->log( "Updating siteurl and homeurl to " . get_home_url() . '/' . $subDirectory . $this->options->cloneDirectoryName );
190
+ // Replace URLs
191
+ $result = $this->db->query(
192
+ $this->db->prepare(
193
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", get_home_url() . '/' . $subDirectory . $this->options->cloneDirectoryName
194
+ )
195
+ );
196
+ } else {
197
+ $this->log( "Updating siteurl and homeurl to " . get_home_url() . '/' . $this->options->cloneDirectoryName );
198
+ // Replace URLs
199
+ $result = $this->db->query(
200
+ $this->db->prepare(
201
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", get_home_url() . '/' . $this->options->cloneDirectoryName
202
+ )
203
+ );
204
+ }
205
+
206
+
207
+ // All good
208
+ if( $result ) {
209
+ return true;
210
+ }
211
+
212
+ $this->log( "Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
213
+ return false;
214
+ }
215
+
216
+ /**
217
  * Update "wpstg_is_staging_site"
218
  * @return bool
219
  */
220
  protected function step2()
221
  {
222
+
223
  $this->log( "Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
224
+
225
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
226
+ return true;
227
+ }
228
 
229
  $result = $this->db->query(
230
  $this->db->prepare(
260
  */
261
  protected function step3()
262
  {
263
+
264
  $this->log("Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}");
265
 
266
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
267
+ return true;
268
+ }
269
+
270
  $result = $this->db->query(
271
  $this->db->prepare(
272
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'",
290
  */
291
  protected function step4() {
292
  $this->log( "Updating {$this->prefix}usermeta db prefix {$this->db->last_error}" );
293
+
294
+ if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
295
+ return true;
296
+ }
297
 
298
  $resultOptions = $this->db->query(
299
  $this->db->prepare(
341
  $content = str_replace('$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content);
342
 
343
  // Replace URLs
344
+ $content = str_replace(get_home_url(), get_home_url() . '/' . $this->options->cloneDirectoryName, $content);
345
 
346
  if (false === @file_put_contents($path, $content))
347
  {
apps/Backend/Modules/Jobs/Delete_deprecated.php DELETED
@@ -1,397 +0,0 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
5
- //use WPStaging\Utils\Directories;
6
- use WPStaging\Utils\Logger;
7
- use WPStaging\WPStaging;
8
-
9
- /**
10
- * Class Delete
11
- * @package WPStaging\Backend\Modules\Jobs
12
- */
13
- class Delete_deprecated extends Job
14
- {
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
- * Sets Clone and Table Records
38
- * @param null|array $clone
39
- */
40
- public function setData($clone = null)
41
- {
42
- if (!is_array($clone))
43
- {
44
- $this->getCloneRecords();
45
- }
46
- else
47
- {
48
- $this->clone = (object) $clone;
49
- $this->forceDeleteDirectories = true;
50
- }
51
-
52
- $this->getTableRecords();
53
- }
54
-
55
- /**
56
- * Get clone
57
- * @param null|string $name
58
- * @throws CloneNotFoundException
59
- */
60
- private function getCloneRecords($name = null)
61
- {
62
- if (null === $name && !isset($_POST["clone"]))
63
- {
64
- $this->log("Clone name is not set", Logger::TYPE_FATAL);
65
- throw new CloneNotFoundException();
66
- }
67
-
68
- if (null === $name)
69
- {
70
- $name = $_POST["clone"];
71
- }
72
-
73
- $clones = get_option("wpstg_existing_clones_beta", array());
74
-
75
- if (empty($clones) || !isset($clones[$name]))
76
- {
77
- $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
78
- throw new CloneNotFoundException();
79
- }
80
-
81
- $this->clone = $clones[$name];
82
- $this->clone["name"] = $name;
83
- // $this->clone["size"] = null;
84
- //
85
- // if (isset($this->settings->checkDirectorySize) || '1' === $this->settings->checkDirectorySize)
86
- // {
87
- // $directories = new Directories();
88
- // $this->clone["size"] = $this->formatSize($directories->size($this->clone));
89
- // unset($directories);
90
- // }
91
-
92
- $this->clone = (object) $this->clone;
93
-
94
- unset($clones);
95
- }
96
-
97
- /**
98
- * Get Tables
99
- */
100
- private function getTableRecords()
101
- {
102
- $wpdb = WPStaging::getInstance()->get("wpdb");
103
-
104
- // if ($this->clone['version']){
105
- //
106
- // }
107
-
108
- $tables = $wpdb->get_results("SHOW TABLE STATUS LIKE 'wpstg{$this->clone->number}_%'");
109
-
110
- $this->tables = array();
111
-
112
- foreach ($tables as $table)
113
- {
114
- $this->tables[] = array(
115
- "name" => $table->Name,
116
- "size" => $this->formatSize(($table->Data_length + $table->Index_length))
117
- );
118
- }
119
-
120
- $this->tables = json_decode(json_encode($this->tables));
121
- }
122
-
123
- /**
124
- * Format bytes into human readable form
125
- * @param int $bytes
126
- * @param int $precision
127
- * @return string
128
- */
129
- public function formatSize($bytes, $precision = 2)
130
- {
131
- if ((int) $bytes < 1)
132
- {
133
- return '';
134
- }
135
-
136
- $units = array('B', "KB", "MB", "GB", "TB");
137
-
138
- $bytes = (int) $bytes;
139
- $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
140
- $pow = pow(1000, $base - floor($base)); // Same rule for 1000
141
-
142
- return round($pow, $precision) . ' ' . $units[(int) floor($base)];
143
- }
144
-
145
- /**
146
- * @return false
147
- */
148
- public function getClone()
149
- {
150
- return $this->clone;
151
- }
152
-
153
- /**
154
- * @return null|object
155
- */
156
- public function getTables()
157
- {
158
- return $this->tables;
159
- }
160
-
161
- /**
162
- * Start Module
163
- * @param null|array $clone
164
- * @return bool
165
- */
166
- public function start($clone = null)
167
- {
168
- // Set data
169
- $this->setData($clone);
170
-
171
- // Get the job first
172
- $this->getJob();
173
-
174
- $method = "delete" . ucwords($this->job->current);
175
- return $this->{$method}();
176
- }
177
-
178
- /**
179
- * Get job data
180
- */
181
- private function getJob()
182
- {
183
- $this->job = $this->cache->get("delete_job_{$this->clone->name}");
184
-
185
- if (null !== $this->job)
186
- {
187
- return;
188
- }
189
-
190
- // Generate JOB
191
- $this->job = (object) array(
192
- "current" => "tables",
193
- "nextDirectoryToDelete" => $this->clone->path
194
- );
195
-
196
- $this->cache->save("delete_job_{$this->clone->name}", $this->job);
197
- }
198
-
199
- /**
200
- * @return bool
201
- */
202
- private function updateJob()
203
- {
204
- $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
205
- return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
206
- }
207
-
208
- /**
209
- * @return array
210
- */
211
- private function getTablesToRemove()
212
- {
213
- $tables = $this->getTableNames();
214
-
215
- if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"]))
216
- {
217
- return $tables;
218
- }
219
-
220
- return array_diff($tables, $_POST["excludedTables"]);
221
- }
222
-
223
- /**
224
- * @return array
225
- */
226
- private function getTableNames()
227
- {
228
- return (!is_array($this->tables)) ? array() : array_map(function($value) {
229
- return ($value->name);
230
- }, $this->tables);
231
- }
232
-
233
- /**
234
- * Delete Tables
235
- */
236
- public function deleteTables()
237
- {
238
- if ($this->isOverThreshold())
239
- {
240
- return;
241
- }
242
-
243
- $wpdb = WPStaging::getInstance()->get("wpdb");
244
-
245
- foreach ($this->getTablesToRemove() as $table)
246
- {
247
- // PROTECTION: Never delete any table that beginns with wp prefix of live site
248
- if($this->startsWith($table, $wpdb->prefix)){
249
- $this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
250
- return false;
251
- } else{
252
- $wpdb->query("DROP TABLE {$table}");
253
- }
254
- }
255
-
256
- // Move on to the next
257
- $this->job->current = "directory";
258
- $this->updateJob();
259
- }
260
-
261
- /**
262
- * Check if a strings start with a specific string
263
- * @param string $haystack
264
- * @param string $needle
265
- * @return bool
266
- */
267
- protected function startsWith($haystack, $needle)
268
- {
269
- $length = strlen($needle);
270
- return (substr($haystack, 0, $length) === $needle);
271
- }
272
-
273
- /**
274
- * Delete Directories
275
- */
276
- public function deleteDirectory()
277
- {
278
- // No deleting directories or root of this clone is deleted
279
- if ($this->isDirectoryDeletingFinished())
280
- {
281
- $this->job->current = "finish";
282
- $this->updateJob();
283
- return;
284
- }
285
-
286
- $this->processDirectory($this->job->nextDirectoryToDelete);
287
-
288
- return;
289
- }
290
-
291
- /**
292
- * @return bool
293
- */
294
- public function isDirectoryDeletingFinished()
295
- {
296
- return (
297
- (false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
298
- !is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
299
- );
300
- }
301
-
302
- /**
303
- * Delete contents of the directory if there are no directories in it and then delete itself
304
- * @param string $path
305
- * @return mixed
306
- */
307
- private function processDirectory($path)
308
- {
309
- // We hit the limit, stop
310
- if ($this->shouldStop($path))
311
- {
312
- $this->updateJob();
313
- return false;
314
- }
315
-
316
- $this->totalRecursion++;
317
-
318
- $contents = new \DirectoryIterator($path);
319
-
320
- foreach ($contents as $content)
321
- {
322
- // Skip dots
323
- if ($content->isDot())
324
- {
325
- continue;
326
- }
327
-
328
- // Get into the directory
329
- if (!$content->isLink() && $content->isDir())
330
- {
331
- return $this->processDirectory($content->getRealPath());
332
- }
333
-
334
- // Delete file
335
- if ($content->isFile())
336
- {
337
- @unlink($content->getRealPath());
338
- }
339
- }
340
-
341
- // Delete directory
342
- $this->job->lastDeletedDirectory = realpath($path . "/..");
343
- @rmdir($path);
344
- $this->updateJob();
345
- $this->processDirectory($this->job->nextDirectoryToDelete);
346
- }
347
-
348
- /**
349
- * @param string $path
350
- * @return bool
351
- */
352
- private function shouldStop($path)
353
- {
354
- // Just to make sure the root dir is never deleted!
355
- if ($path === get_home_path()){
356
- $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
357
- return true;
358
- }
359
-
360
- // Check if threshold is reached and is valid dir
361
- return (
362
- $this->isOverThreshold() ||
363
- !is_dir($path) ||
364
- $this->isDirectoryDeletingFinished()
365
- );
366
- }
367
-
368
- /**
369
- * Finish / Update Existing Clones
370
- */
371
- public function deleteFinish()
372
- {
373
- $existingClones = get_option("wpstg_existing_clones_beta", array());
374
-
375
- // Check if clones still exist
376
- $this->log("Verifying existing clones...");
377
- foreach ($existingClones as $name => $clone)
378
- {
379
- if (!is_dir($clone["path"]))
380
- {
381
- unset($existingClones[$name]);
382
- }
383
- }
384
- $this->log("Existing clones verified!");
385
-
386
- if (false === update_option("wpstg_existing_clones_beta", $existingClones))
387
- {
388
- $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
389
- }
390
-
391
- // Delete cached file
392
- $this->cache->delete("delete_job_{$this->clone->name}");
393
- $this->cache->delete("delete_directories_{$this->clone->name}");
394
-
395
- return true;
396
- }
397
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apps/Backend/Modules/Jobs/Delete_new.php DELETED
@@ -1,382 +0,0 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
5
- //use WPStaging\Utils\Directories;
6
- use WPStaging\Utils\Logger;
7
- use WPStaging\WPStaging;
8
-
9
- /**
10
- * Class Delete
11
- * @package WPStaging\Backend\Modules\Jobs
12
- */
13
- class Delete_new extends Job
14
- {
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
- * Sets Clone and Table Records
38
- * @param null|array $clone
39
- */
40
- public function setData($clone = null)
41
- {
42
- if (!is_array($clone))
43
- {
44
- $this->getCloneRecords();
45
- }
46
- else
47
- {
48
- $this->clone = (object) $clone;
49
- $this->forceDeleteDirectories = true;
50
- }
51
-
52
- $this->getTableRecords();
53
- }
54
-
55
- /**
56
- * Get clone
57
- * @param null|string $name
58
- * @throws CloneNotFoundException
59
- */
60
- private function getCloneRecords($name = null)
61
- {
62
- if (null === $name && !isset($_POST["clone"]))
63
- {
64
- $this->log("Clone name is not set", Logger::TYPE_FATAL);
65
- throw new CloneNotFoundException();
66
- }
67
-
68
- if (null === $name)
69
- {
70
- $name = $_POST["clone"];
71
- }
72
-
73
- $clones = get_option("wpstg_existing_clones_beta", array());
74
-
75
- if (empty($clones) || !isset($clones[$name]))
76
- {
77
- $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
78
- throw new CloneNotFoundException();
79
- }
80
-
81
- $this->clone = $clones[$name];
82
- $this->clone["name"] = $name;
83
- $this->clone = (object) $this->clone;
84
-
85
- unset($clones);
86
- }
87
-
88
- /**
89
- * Get Tables
90
- */
91
- private function getTableRecords()
92
- {
93
- $wpdb = WPStaging::getInstance()->get("wpdb");
94
-
95
- $tables = $wpdb->get_results("SHOW TABLE STATUS LIKE 'wpstg{$this->clone->number}_%'");
96
-
97
- $this->tables = array();
98
-
99
- foreach ($tables as $table)
100
- {
101
- $this->tables[] = array(
102
- "name" => $table->Name,
103
- "size" => $this->formatSize(($table->Data_length + $table->Index_length))
104
- );
105
- }
106
-
107
- $this->tables = json_decode(json_encode($this->tables));
108
- }
109
-
110
- /**
111
- * @return false
112
- */
113
- public function getClone()
114
- {
115
- return $this->clone;
116
- }
117
-
118
- /**
119
- * @return null|object
120
- */
121
- public function getTables()
122
- {
123
- return $this->tables;
124
- }
125
-
126
- /**
127
- * Start Module
128
- * @param null|array $clone
129
- * @return bool
130
- */
131
- public function start($clone = null)
132
- {
133
- // Set data
134
- $this->setData($clone);
135
-
136
- // Get the job first
137
- $this->getJob();
138
-
139
- $method = "delete" . ucwords($this->job->current);
140
- return $this->{$method}();
141
- }
142
-
143
-
144
-
145
- /**
146
- * Get job data
147
- */
148
- private function getJob()
149
- {
150
- $this->job = $this->cache->get("delete_job_{$this->clone->name}");
151
-
152
- if (null !== $this->job)
153
- {
154
- return;
155
- }
156
-
157
- // Generate JOB
158
- $this->job = (object) array(
159
- "current" => "tables",
160
- "nextDirectoryToDelete" => $this->clone->path
161
- );
162
-
163
- $this->cache->save("delete_job_{$this->clone->name}", $this->job);
164
- }
165
-
166
- /**
167
- * Delete Directories
168
- */
169
- // public function deleteDirectory()
170
- // {
171
- // // No deleting directories or root of this clone is deleted
172
- // if ($this->isDirectoryDeletingFinished())
173
- // {
174
- // $this->job->current = "finish";
175
- // $this->updateJob();
176
- // return;
177
- // }
178
- //
179
- // $this->processDirectory($this->job->nextDirectoryToDelete);
180
- //
181
- // return;
182
- // }
183
-
184
- public function deleteDirectory(){
185
- // Just to make sure the root dir is never deleted!
186
- if ($this->clone->path === get_home_path()){
187
- $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
188
- return false;
189
- }
190
-
191
- $files = glob($this->clone->path . '/*');
192
- foreach ($files as $file) {
193
- is_dir($file) ? $this->deleteDirectory($file) : unlink($file);
194
- }
195
- rmdir($path);
196
- $this->processDirectory($this->job->nextDirectoryToDelete);
197
- return;
198
- }
199
-
200
- /**
201
- * @return bool
202
- */
203
- private function updateJob()
204
- {
205
- $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
206
- return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
207
- }
208
-
209
- /**
210
- * @return array
211
- */
212
- private function getTablesToRemove()
213
- {
214
- $tables = $this->getTableNames();
215
-
216
- if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"]))
217
- {
218
- return $tables;
219
- }
220
-
221
- return array_diff($tables, $_POST["excludedTables"]);
222
- }
223
-
224
- /**
225
- * @return array
226
- */
227
- private function getTableNames()
228
- {
229
- return (!is_array($this->tables)) ? array() : array_map(function($value) {
230
- return ($value->name);
231
- }, $this->tables);
232
- }
233
-
234
- /**
235
- * Delete Tables
236
- */
237
- public function deleteTables()
238
- {
239
- if ($this->isOverThreshold())
240
- {
241
- return;
242
- }
243
-
244
- $wpdb = WPStaging::getInstance()->get("wpdb");
245
-
246
- foreach ($this->getTablesToRemove() as $table)
247
- {
248
- // PROTECTION: Never delete any table that beginns with wp prefix of live site
249
- if($this->startsWith($table, $wpdb->prefix)){
250
- $this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
251
- return false;
252
- } else{
253
- $wpdb->query("DROP TABLE {$table}");
254
- }
255
- }
256
-
257
- // Move on to the next
258
- $this->job->current = "directory";
259
- $this->updateJob();
260
- }
261
-
262
- /**
263
- * Check if a strings start with a specific string
264
- * @param string $haystack
265
- * @param string $needle
266
- * @return bool
267
- */
268
- protected function startsWith($haystack, $needle)
269
- {
270
- $length = strlen($needle);
271
- return (substr($haystack, 0, $length) === $needle);
272
- }
273
-
274
-
275
-
276
- /**
277
- * @return bool
278
- */
279
- public function isDirectoryDeletingFinished()
280
- {
281
- return (
282
- (false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
283
- !is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
284
- );
285
- }
286
-
287
- /**
288
- * Delete contents of the directory if there are no directories in it and then delete itself
289
- * @param string $path
290
- * @return mixed
291
- */
292
- private function processDirectory($path)
293
- {
294
- // We hit the limit, stop
295
- if ($this->shouldStop($path))
296
- {
297
- $this->updateJob();
298
- return false;
299
- }
300
-
301
- $this->totalRecursion++;
302
-
303
- $contents = new \DirectoryIterator($path);
304
-
305
- foreach ($contents as $content)
306
- {
307
- // Skip dots
308
- if ($content->isDot())
309
- {
310
- continue;
311
- }
312
-
313
- // Get into the directory
314
- if (!$content->isLink() && $content->isDir())
315
- {
316
- return $this->processDirectory($content->getRealPath());
317
- }
318
-
319
- // Delete file
320
- if ($content->isFile())
321
- {
322
- @unlink($content->getRealPath());
323
- }
324
- }
325
-
326
- // Delete directory
327
- $this->job->lastDeletedDirectory = realpath($path . "/..");
328
- @rmdir($path);
329
- $this->updateJob();
330
- $this->processDirectory($this->job->nextDirectoryToDelete);
331
- }
332
-
333
- /**
334
- * @param string $path
335
- * @return bool
336
- */
337
- private function shouldStop($path)
338
- {
339
- // Just to make sure the root dir is never deleted!
340
- if ($path === get_home_path()){
341
- $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
342
- return true;
343
- }
344
-
345
- // Check if threshold is reached and is valid dir
346
- return (
347
- $this->isOverThreshold() ||
348
- !is_dir($path) ||
349
- $this->isDirectoryDeletingFinished()
350
- );
351
- }
352
-
353
- /**
354
- * Finish / Update Existing Clones
355
- */
356
- public function deleteFinish()
357
- {
358
- $existingClones = get_option("wpstg_existing_clones_beta", array());
359
-
360
- // Check if clones still exist
361
- $this->log("Verifying existing clones...");
362
- foreach ($existingClones as $name => $clone)
363
- {
364
- if (!is_dir($clone["path"]))
365
- {
366
- unset($existingClones[$name]);
367
- }
368
- }
369
- $this->log("Existing clones verified!");
370
-
371
- if (false === update_option("wpstg_existing_clones_beta", $existingClones))
372
- {
373
- $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
374
- }
375
-
376
- // Delete cached file
377
- $this->cache->delete("delete_job_{$this->clone->name}");
378
- $this->cache->delete("delete_directories_{$this->clone->name}");
379
-
380
- return true;
381
- }
382
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apps/Backend/Modules/Jobs/Delete_old.php DELETED
@@ -1,367 +0,0 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
5
- use WPStaging\Utils\Directories;
6
- use WPStaging\Utils\Logger;
7
- use WPStaging\WPStaging;
8
-
9
- /**
10
- * Class Delete
11
- * @package WPStaging\Backend\Modules\Jobs
12
- */
13
- class Delete_old extends Job
14
- {
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
- * Sets Clone and Table Records
38
- * @param null|array $clone
39
- */
40
- public function setData($clone = null)
41
- {
42
- if (!is_array($clone))
43
- {
44
- $this->getCloneRecords();
45
- }
46
- else
47
- {
48
- $this->clone = (object) $clone;
49
- $this->forceDeleteDirectories = true;
50
- }
51
-
52
- $this->getTableRecords();
53
- }
54
-
55
- /**
56
- * Get clone
57
- * @param null|string $name
58
- * @throws CloneNotFoundException
59
- */
60
- private function getCloneRecords($name = null)
61
- {
62
- if (null === $name && !isset($_POST["clone"]))
63
- {
64
- $this->log("Clone name is not set", Logger::TYPE_FATAL);
65
- throw new CloneNotFoundException();
66
- }
67
-
68
- if (null === $name)
69
- {
70
- $name = $_POST["clone"];
71
- }
72
-
73
- $clones = get_option("wpstg_existing_clones_beta", array());
74
-
75
- if (empty($clones) || !isset($clones[$name]))
76
- {
77
- $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
78
- throw new CloneNotFoundException();
79
- }
80
-
81
- $this->clone = $clones[$name];
82
- $this->clone["name"] = $name;
83
- $this->clone["size"] = null;
84
-
85
- if (isset($this->settings->checkDirectorySize) || '1' === $this->settings->checkDirectorySize)
86
- {
87
- $directories = new Directories();
88
- $this->clone["size"] = $this->formatSize($directories->size($this->clone));
89
- unset($directories);
90
- }
91
-
92
- $this->clone = (object) $this->clone;
93
-
94
- unset($clones);
95
- }
96
-
97
- /**
98
- * Get Tables
99
- */
100
- private function getTableRecords()
101
- {
102
- $wpdb = WPStaging::getInstance()->get("wpdb");
103
- $tables = $wpdb->get_results("SHOW TABLE STATUS LIKE 'wpstg{$this->clone->number}_%'");
104
-
105
- $this->tables = array();
106
-
107
- foreach ($tables as $table)
108
- {
109
- $this->tables[] = array(
110
- "name" => $table->Name,
111
- "size" => $this->formatSize(($table->Data_length + $table->Index_length))
112
- );
113
- }
114
-
115
- $this->tables = json_decode(json_encode($this->tables));
116
- }
117
-
118
- /**
119
- * Format bytes into human readable form
120
- * @param int $bytes
121
- * @param int $precision
122
- * @return string
123
- */
124
- public function formatSize($bytes, $precision = 2)
125
- {
126
- if ((int) $bytes < 1)
127
- {
128
- return '';
129
- }
130
-
131
- $units = array('B', "KB", "MB", "GB", "TB");
132
-
133
- $bytes = (int) $bytes;
134
- $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
135
- $pow = pow(1000, $base - floor($base)); // Same rule for 1000
136
-
137
- return round($pow, $precision) . ' ' . $units[(int) floor($base)];
138
- }
139
-
140
- /**
141
- * @return false
142
- */
143
- public function getClone()
144
- {
145
- return $this->clone;
146
- }
147
-
148
- /**
149
- * @return null|object
150
- */
151
- public function getTables()
152
- {
153
- return $this->tables;
154
- }
155
-
156
- /**
157
- * Start Module
158
- * @param null|array $clone
159
- * @return bool
160
- */
161
- public function start($clone = null)
162
- {
163
- // Set data
164
- $this->setData($clone);
165
-
166
- // Get the job first
167
- $this->getJob();
168
-
169
- $method = "delete" . ucwords($this->job->current);
170
- return $this->{$method}();
171
- }
172
-
173
- /**
174
- * Get job data
175
- */
176
- private function getJob()
177
- {
178
- $this->job = $this->cache->get("delete_job_{$this->clone->name}");
179
-
180
- if (null !== $this->job)
181
- {
182
- return;
183
- }
184
-
185
- // Generate JOB
186
- $this->job = (object) array(
187
- "current" => "tables",
188
- "nextDirectoryToDelete" => $this->clone->path
189
- );
190
-
191
- $this->cache->save("delete_job_{$this->clone->name}", $this->job);
192
- }
193
-
194
- /**
195
- * @return bool
196
- */
197
- private function updateJob()
198
- {
199
- $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
200
- return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
201
- }
202
-
203
- /**
204
- * @return array
205
- */
206
- private function getTablesToRemove()
207
- {
208
- $tables = $this->getTableNames();
209
-
210
- if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"]))
211
- {
212
- return $tables;
213
- }
214
-
215
- return array_diff($tables, $_POST["excludedTables"]);
216
- }
217
-
218
- /**
219
- * @return array
220
- */
221
- private function getTableNames()
222
- {
223
- return (!is_array($this->tables)) ? array() : array_map(function($value) {
224
- return ($value->name);
225
- }, $this->tables);
226
- }
227
-
228
- /**
229
- * Delete Tables
230
- */
231
- public function deleteTables()
232
- {
233
- if ($this->isOverThreshold())
234
- {
235
- return;
236
- }
237
-
238
- $wpdb = WPStaging::getInstance()->get("wpdb");
239
-
240
- foreach ($this->getTablesToRemove() as $table)
241
- {
242
- $wpdb->query("DROP TABLE {$table}");
243
- }
244
-
245
- // Move on to the next
246
- $this->job->current = "directory";
247
- $this->updateJob();
248
- }
249
-
250
- /**
251
- * Delete Directories
252
- */
253
- public function deleteDirectory()
254
- {
255
- // No deleting directories or root of this clone is deleted
256
- if ($this->isDirectoryDeletingFinished())
257
- {
258
- $this->job->current = "finish";
259
- $this->updateJob();
260
- return;
261
- }
262
-
263
- $this->processDirectory($this->job->nextDirectoryToDelete);
264
-
265
- return;
266
- }
267
-
268
- /**
269
- * @return bool
270
- */
271
- public function isDirectoryDeletingFinished()
272
- {
273
- return (
274
- (false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
275
- !is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
276
- );
277
- }
278
-
279
- /**
280
- * Delete contents of the directory if there are no directories in it and then delete itself
281
- * @param string $path
282
- * @return mixed
283
- */
284
- private function processDirectory($path)
285
- {
286
- // We hit the limit, stop
287
- if ($this->shouldStop($path))
288
- {
289
- $this->updateJob();
290
- return false;
291
- }
292
-
293
- $this->totalRecursion++;
294
-
295
- $contents = new \DirectoryIterator($path);
296
-
297
- foreach ($contents as $content)
298
- {
299
- // Skip dots
300
- if ($content->isDot())
301
- {
302
- continue;
303
- }
304
-
305
- // Get into the directory
306
- if (!$content->isLink() && $content->isDir())
307
- {
308
- return $this->processDirectory($content->getRealPath());
309
- }
310
-
311
- // Delete file
312
- if ($content->isFile())
313
- {
314
- @unlink($content->getRealPath());
315
- }
316
- }
317
-
318
- // Delete directory
319
- $this->job->lastDeletedDirectory = realpath($path . "/..");
320
- @rmdir($path);
321
- $this->updateJob();
322
- $this->processDirectory($this->job->nextDirectoryToDelete);
323
- }
324
-
325
- /**
326
- * @param string $path
327
- * @return bool
328
- */
329
- private function shouldStop($path)
330
- {
331
- return (
332
- $this->isOverThreshold() ||
333
- !is_dir($path) ||
334
- $this->isDirectoryDeletingFinished()
335
- );
336
- }
337
-
338
- /**
339
- * Finish / Update Existing Clones
340
- */
341
- public function deleteFinish()
342
- {
343
- $existingClones = get_option("wpstg_existing_clones_beta", array());
344
-
345
- // Check if clones still exist
346
- $this->log("Verifying existing clones...");
347
- foreach ($existingClones as $name => $clone)
348
- {
349
- if (!is_dir($clone["path"]))
350
- {
351
- unset($existingClones[$name]);
352
- }
353
- }
354
- $this->log("Existing clones verified!");
355
-
356
- if (false === update_option("wpstg_existing_clones_beta", $existingClones))
357
- {
358
- $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
359
- }
360
-
361
- // Delete cached file
362
- $this->cache->delete("delete_job_{$this->clone->name}");
363
- $this->cache->delete("delete_directories_{$this->clone->name}");
364
-
365
- return true;
366
- }
367
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apps/Backend/Modules/Jobs/Directories.php CHANGED
@@ -125,7 +125,6 @@ class Directories extends JobExecutable {
125
 
126
  if( $this->isOverThreshold() ) {
127
  //$this->saveProgress();
128
-
129
  return false;
130
  }
131
 
@@ -172,26 +171,41 @@ class Directories extends JobExecutable {
172
  protected function getFilesFromDirectory( $directory ) {
173
  $this->totalRecursion++;
174
 
175
- // Save all files
176
  $files = array_diff( scandir( $directory ), array('.', "..") );
177
 
178
  foreach ( $files as $file ) {
179
  $fullPath = $directory . $file;
 
 
 
 
 
 
 
 
 
 
 
 
180
 
 
181
  if( is_dir( $fullPath ) && !in_array( $fullPath, $this->options->directoriesToCopy ) && !$this->isDirectoryExcluded( $fullPath ) ) {
182
  $this->options->directoriesToCopy[] = $fullPath;
183
 
184
- return $this->getFilesFromSubDirectories( $fullPath );
185
  //continue;
186
- }
187
-
188
- if( !is_file( $fullPath ) || in_array( $fullPath, $this->files ) ) {
189
  continue;
190
  }
191
 
192
- $this->options->totalFiles++;
 
 
193
 
194
- $this->files[] = $fullPath;
 
 
195
 
196
  /**
197
  * Test and measure if its faster to copy at the same time while the array with folders is generated
@@ -349,5 +363,22 @@ class Directories extends JobExecutable {
349
 
350
  return $destinationPath;
351
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
 
353
  }
125
 
126
  if( $this->isOverThreshold() ) {
127
  //$this->saveProgress();
 
128
  return false;
129
  }
130
 
171
  protected function getFilesFromDirectory( $directory ) {
172
  $this->totalRecursion++;
173
 
174
+ // Get only files
175
  $files = array_diff( scandir( $directory ), array('.', "..") );
176
 
177
  foreach ( $files as $file ) {
178
  $fullPath = $directory . $file;
179
+
180
+ // It's a readable valid file and not excluded for copying
181
+ if( is_file( $fullPath ) && !$this->isExcluded($file) ) {
182
+ $this->options->totalFiles++;
183
+ $this->files[] = $fullPath;
184
+ continue;
185
+ }
186
+ // It's a valid file but not readable
187
+ if( is_file( $fullPath ) && !is_readable( $fullPath ) ) {
188
+ $this->debugLog('File {$fullPath} is not readable', Logger::TYPE_DEBUG );
189
+ continue;
190
+ }
191
 
192
+ // Iterate and loop through if it's a directory and if it's not excluded
193
  if( is_dir( $fullPath ) && !in_array( $fullPath, $this->options->directoriesToCopy ) && !$this->isDirectoryExcluded( $fullPath ) ) {
194
  $this->options->directoriesToCopy[] = $fullPath;
195
 
196
+ //return $this->getFilesFromSubDirectories( $fullPath );
197
  //continue;
198
+ $this->getFilesFromSubDirectories( $fullPath );
 
 
199
  continue;
200
  }
201
 
202
+ // if( !is_file( $fullPath ) || in_array( $fullPath, $this->files ) ) {
203
+ // continue;
204
+ // }
205
 
206
+ // $this->options->totalFiles++;
207
+ //
208
+ // $this->files[] = $fullPath;
209
 
210
  /**
211
  * Test and measure if its faster to copy at the same time while the array with folders is generated
363
 
364
  return $destinationPath;
365
  }
366
+
367
+ /**
368
+ * Check if filename is excluded for cloning process
369
+ *
370
+ * @param string $file filename including ending
371
+ * @return boolean
372
+ */
373
+ private function isExcluded( $file ) {
374
+ $excluded = false;
375
+ foreach ( $this->options->excludedFiles as $excludedFile ) {
376
+ if (stripos(strrev($file), strrev($excludedFile)) === 0) {
377
+ $excluded = true;
378
+ break;
379
+ }
380
+ }
381
+ return $excluded;
382
+ }
383
 
384
  }
apps/Backend/Modules/SystemInfo.php CHANGED
@@ -22,6 +22,7 @@ class SystemInfo extends InjectionAware
22
  * @var bool
23
  */
24
  private $isMultiSite;
 
25
 
26
  /**
27
  * Initialize class
@@ -48,6 +49,8 @@ class SystemInfo extends InjectionAware
48
  {
49
  $output = "### Begin System Info ###" . PHP_EOL . PHP_EOL;
50
 
 
 
51
  $output .= $this->site();
52
 
53
  $output .= $this->browser();
@@ -68,6 +71,8 @@ class SystemInfo extends InjectionAware
68
 
69
  return $output;
70
  }
 
 
71
 
72
  /**
73
  * @param string $string
@@ -86,7 +91,7 @@ class SystemInfo extends InjectionAware
86
  */
87
  public function info($title, $value)
88
  {
89
- return str_pad($title, 26, ' ', STR_PAD_RIGHT) . $value . PHP_EOL;
90
  }
91
 
92
  /**
@@ -121,8 +126,55 @@ class SystemInfo extends InjectionAware
121
 
122
  return apply_filters("wpstg_sysinfo_after_site_info", $output);
123
  }
124
-
125
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  * Browser Information
127
  * @return string
128
  */
22
  * @var bool
23
  */
24
  private $isMultiSite;
25
+
26
 
27
  /**
28
  * Initialize class
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();
71
 
72
  return $output;
73
  }
74
+
75
+
76
 
77
  /**
78
  * @param string $string
91
  */
92
  public function info($title, $value)
93
  {
94
+ return str_pad($title, 56, ' ', STR_PAD_RIGHT) . $value . PHP_EOL;
95
  }
96
 
97
  /**
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( "Batch Size:", isset( $settings->batchSize ) ? $settings->batchSize : 'undefined' );
147
+ $output .= $this->info( "CPU Load:", isset( $settings->cpuLoad ) ? $settings->cpuLoad : 'undefined' );
148
+ $output .= $this->info( "WP in Subdir:", isset( $settings->wpSubDirectory ) ? $settings->wpSubDirectory : 'false' );
149
+
150
+ $output .= PHP_EOL . PHP_EOL . "-- Available Sites Version < 1.6.x" . PHP_EOL . PHP_EOL;
151
+
152
+ $i = 1;
153
+ foreach ( $clones as $key => $value ) {
154
+ $output .= $this->info( "Site name & subfolder :", $value );
155
+ }
156
+ $output .= PHP_EOL . PHP_EOL . "-- Available Sites Version > 2.0.x" . PHP_EOL . PHP_EOL;
157
+
158
+ foreach ( $clonesBeta as $key => $clone ) {
159
+ $output .= $this->info( "Number:", isset( $clone['number'] ) ? $clone['number'] : 'undefined' );
160
+ $output .= $this->info( "directoryName:", isset( $clone['directoryName'] ) ? $clone['directoryName'] : 'undefined' );
161
+ $output .= $this->info( "Path:", isset( $clone['path'] ) ? $clone['path'] : 'undefined' );
162
+ $output .= $this->info( "URL:", isset( $clone['url'] ) ? $clone['url'] : 'undefined' );
163
+ $output .= $this->info( "Version:", isset( $clone['version'] ) ? $clone['version'] : 'undefined' ) . PHP_EOL . PHP_EOL;
164
+ }
165
+
166
+ //$output .= PHP_EOL . PHP_EOL;
167
+
168
+ $output .= $this->info( "Plugin Version:", get_option('wpstg_version', 'undefined') );
169
+ $output .= $this->info( "Install Date:", get_option('wpstg_installDate', 'undefined') );
170
+ $output .= $this->info( "Upgraded from:", get_option('wpstg_version_upgraded_from', 'undefined') );
171
+ $output .= $this->info( "Is Staging Site:", get_option('wpstg_is_staging_site', 'undefined') ) . PHP_EOL . PHP_EOL;
172
+
173
+
174
+ return apply_filters( "wpstg_sysinfo_after_wpstaging_info", $output );
175
+ }
176
+
177
+ /**
178
  * Browser Information
179
  * @return string
180
  */
apps/Backend/public/css/wpstg-admin.css CHANGED
@@ -642,7 +642,7 @@ color:#777777;
642
  }
643
 
644
  .wpstg-sysinfo {
645
- width:400px;
646
  height: 500px;
647
  }
648
 
642
  }
643
 
644
  .wpstg-sysinfo {
645
+ width:700px;
646
  height: 500px;
647
  }
648
 
apps/Backend/public/js/wpstg-admin.js CHANGED
@@ -322,7 +322,7 @@ var WPStaging = (function ($)
322
 
323
  showError(
324
  "Fatal Unknown Error. This should not happen." +
325
- "Please again. If this does not help, " +
326
  "<a href='https://wordpress.org/support/plugin/wp-staging' target='_blank'>Open a ticket</a> " +
327
  "in the WP Staging support forum."
328
  );
322
 
323
  showError(
324
  "Fatal Unknown Error. This should not happen." +
325
+ "Plese try again. If this does not help, " +
326
  "<a href='https://wordpress.org/support/plugin/wp-staging' target='_blank'>Open a ticket</a> " +
327
  "in the WP Staging support forum."
328
  );
apps/Backend/views/clone/ajax/start.php CHANGED
@@ -42,12 +42,18 @@
42
  <h3>Congratulations
43
  </h3>
44
  <?php
45
- 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' ), get_home_url() );
 
 
46
  ?>
47
  <br>
48
  <?php //echo __('Open and access the staging site: ', 'wpstg')?>
49
  <br>
50
- <a href="<?php echo get_home_url()?>" id="wpstg-clone-url" target="_blank" class="wpstg-link-btn button-primary">
 
 
 
 
51
  Open staging site <span style="font-size: 10px;">(login with your admin credentials)</span>
52
  </a>
53
  <!--<a href="" class="wpstg-link-btn button-primary" id="wpstg-remove-cloning">
42
  <h3>Congratulations
43
  </h3>
44
  <?php
45
+ $subDirectory = str_replace( get_home_path(), '', ABSPATH );
46
+ $url = get_home_url() . '/' . str_replace('/', '', $subDirectory);
47
+ 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 );
48
  ?>
49
  <br>
50
  <?php //echo __('Open and access the staging site: ', 'wpstg')?>
51
  <br>
52
+ <?php
53
+ $subDirectory = str_replace( get_home_path(), '', ABSPATH );
54
+ $url = get_home_url() . $subDirectory;
55
+ ?>
56
+ <a href="<?php echo $url; ?>" id="wpstg-clone-url" target="_blank" class="wpstg-link-btn button-primary">
57
  Open staging site <span style="font-size: 10px;">(login with your admin credentials)</span>
58
  </a>
59
  <!--<a href="" class="wpstg-link-btn button-primary" id="wpstg-remove-cloning">
apps/Core/DTO/Settings.php CHANGED
@@ -65,14 +65,15 @@ class Settings
65
  /**
66
  * Settings constructor.
67
  */
68
- public function __construct()
69
- {
70
- $this->_raw = get_option("wpstg_settings", array());
71
 
72
- $this->hydrate($this->_raw);
73
- }
 
 
74
 
75
- /**
76
  * @param array $settings
77
  * @return $this
78
  */
@@ -94,22 +95,19 @@ class Settings
94
  /**
95
  * @return bool
96
  */
97
- public function save()
98
- {
99
- $data = array();
100
-
101
- foreach (get_object_vars($this) as $key => $value)
102
- {
103
- if (0 == strpos($key, '_'))
104
- {
105
- continue;
106
- }
107
-
108
- $data[$key] = $value;
109
- }
110
-
111
- return update_option("wpstg_settings", $data);
112
- }
113
 
114
  /**
115
  * @return array
65
  /**
66
  * Settings constructor.
67
  */
68
+ public function __construct() {
69
+ $this->_raw = get_option( "wpstg_settings", array() );
 
70
 
71
+ if( !empty( $this->_raw ) ) {
72
+ $this->hydrate( $this->_raw );
73
+ }
74
+ }
75
 
76
+ /**
77
  * @param array $settings
78
  * @return $this
79
  */
95
  /**
96
  * @return bool
97
  */
98
+ // public function save() {
99
+ // $data = array();
100
+ //
101
+ // foreach ( get_object_vars( $this ) as $key => $value ) {
102
+ // if( 0 == strpos( $key, '_' ) ) {
103
+ // continue;
104
+ // }
105
+ //
106
+ // $data[$key] = $value;
107
+ // }
108
+ //
109
+ // return update_option( "wpstg_settings", $data );
110
+ // }
 
 
 
111
 
112
  /**
113
  * @return array
apps/Core/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
  /**
30
  * Plugin version
31
  */
32
- const VERSION = "2.0.8";
33
 
34
  /**
35
  * Plugin name
@@ -44,7 +44,7 @@ final class WPStaging {
44
  /**
45
  * Compatible WP Version
46
  */
47
- const WP_COMPATIBLE = "4.8";
48
 
49
  /**
50
  * Slug: Either wp-staging or wp-staging-pro
29
  /**
30
  * Plugin version
31
  */
32
+ const VERSION = "2.1.0";
33
 
34
  /**
35
  * Plugin name
44
  /**
45
  * Compatible WP Version
46
  */
47
+ const WP_COMPATIBLE = "4.8.4";
48
 
49
  /**
50
  * Slug: Either wp-staging or wp-staging-pro
readme.txt CHANGED
@@ -9,7 +9,7 @@ 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.8
12
- Stable tag: 2.0.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
 
@@ -141,6 +141,19 @@ https://wp-staging.com
141
 
142
 
143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  = 2.0.8 =
145
  * 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
146
 
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.8
12
+ Stable tag: 2.1.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
 
141
 
142
 
143
 
144
+ = 2.1.0 =
145
+ * New: Exclude unnecessary files from cloning process: .tmp, .log, .htaccess, .git, .gitignore, desktop.ini, .DS_Store, .svn
146
+ * New: More details for debugging in Tools->System Info
147
+ * Fix: Check if tables in staging site exists before attempting to modify them
148
+ * Fix: WordPress in sub directories were not opening
149
+ * 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
150
+ * Fix: Access to staging site not working, if WP_SITEURL and WP_HOME is defined in wp-config.php
151
+ * Tweak: Exclude wp-content/cache folder from copying process
152
+
153
+
154
+ = 2.0.9 =
155
+ * Skip Version
156
+
157
  = 2.0.8 =
158
  * 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
159
 
wp-staging.php CHANGED
@@ -6,7 +6,7 @@
6
  * Description: Create a staging clone site for testing & developing
7
  * Author: WP-Staging, René Hermenau, Ilgıt Yıldırım
8
  * Author URI: https://wordpress.org/plugins/wp-staging
9
- * Version: 2.0.8
10
  * Text Domain: wpstg
11
  * Domain Path: /languages/
12
 
@@ -44,6 +44,18 @@ if( !defined( 'WPSTG_PLUGIN_URL' ) ) {
44
  define( 'WPSTG_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
45
  }
46
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  /**
48
  * Path to main WP Staging class
49
  * Make sure to not redeclare class in case free version has been installed previosly
6
  * Description: Create a staging clone site for testing & developing
7
  * Author: WP-Staging, René Hermenau, Ilgıt Yıldırım
8
  * Author URI: https://wordpress.org/plugins/wp-staging
9
+ * Version: 2.1.0
10
  * Text Domain: wpstg
11
  * Domain Path: /languages/
12
 
44
  define( 'WPSTG_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
45
  }
46
 
47
+
48
+ /**
49
+ * Fix nonce check
50
+ * https://core.trac.wordpress.org/ticket/41617#ticket
51
+ * @param int $seconds
52
+ * @return int
53
+ */
54
+ function wpstg_overwrite_nonce($seconds){
55
+ return 86400;
56
+ }
57
+ add_filter('nonce_life', 'wpstg_overwrite_nonce', 99999);
58
+
59
  /**
60
  * Path to main WP Staging class
61
  * Make sure to not redeclare class in case free version has been installed previosly