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

Version Description

  • Fix: Remove LOCK_EX parameter in file_put_contents(). LOCK_EX is not working on several systems which results in cloning process timeouts
  • Fix: Huge Performance improvement in copying process by removing duplicate file entries in the cache file. This also prevents weird timeout issues on some hosted websites
  • Fix: Error 500 when debug mode is activated
  • Fix: Limit maximum execution time to 30 seconds
  • Fix: Sanitize Clone Names and Keys to fix "clone not found" issue in upgrade routine
  • Fix: Do not clone the plugin wps-hide-login
  • Fix: Staging sites can not be deleted if they are very big
  • Fix: Link to staging site is undefined
  • Tweak: Better admin message for asking for a review
  • Tweak: Remove table wpstg_rmpermalinks_executed when plugin is uninstalled
  • New: New setting to specify the maximum amount of files copied within one ajax call to fix godaddy and bluehost ajax 404 errors. Default 10 per batch
Download this release

Release Info

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

Code changes from version 2.1.1 to 2.1.2

apps/Backend/Modules/Jobs/Cloning.php CHANGED
@@ -1,190 +1,190 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
-
6
- /**
7
- * Class Cloning
8
- * @package WPStaging\Backend\Modules\Jobs
9
- */
10
- class Cloning extends Job
11
- {
12
-
13
- /**
14
- * Save Chosen Cloning Settings
15
- * @return bool
16
- */
17
- public function save()
18
- {
19
- if (!isset($_POST) || !isset($_POST["cloneID"]))
20
- {
21
- return false;
22
- }
23
-
24
- // Generate Options
25
-
26
- // Clone
27
- $this->options->clone = $_POST["cloneID"];
28
- $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
29
- $this->options->cloneNumber = 1;
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();
37
-
38
- if (isset($this->options->existingClones[$this->options->clone]))
39
- {
40
- $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]->number;
41
- }
42
- elseif (!empty($this->options->existingClones))
43
- {
44
- $this->options->cloneNumber = count($this->options->existingClones)+1;
45
- }
46
-
47
- // Excluded Tables
48
- if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
49
- {
50
- $this->options->excludedTables = $_POST["excludedTables"];
51
- }
52
-
53
- // Excluded Directories
54
- if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
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"]))
70
- {
71
- $this->options->includedDirectories = $_POST["includedDirectories"];
72
- }
73
-
74
- // Extra Directories
75
- if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
76
- {
77
- $this->options->extraDirectories = $_POST["extraDirectories"];
78
- }
79
-
80
- // Directories to Copy
81
- $this->options->directoriesToCopy = array_merge(
82
- $this->options->includedDirectories,
83
- $this->options->extraDirectories
84
- );
85
-
86
- array_unshift($this->options->directoriesToCopy, ABSPATH);
87
-
88
- // Delete files to copy listing
89
- $this->cache->delete("files_to_copy");
90
-
91
- return $this->saveOptions();
92
- }
93
-
94
- /**
95
- * Start the cloning job
96
- */
97
- public function start()
98
- {
99
- if (null === $this->options->currentJob)
100
- {
101
- $this->log("Cloning job for {$this->options->clone} finished");
102
- return true;
103
- }
104
-
105
- $methodName = "job" . ucwords($this->options->currentJob);
106
-
107
- if (!method_exists($this, $methodName))
108
- {
109
- $this->log("Can't execute job; Job's method {$methodName} is not found");
110
- throw new JobNotFoundException($methodName);
111
- }
112
-
113
- // Call the job
114
- //$this->log("execute job: Job's method {$methodName}");
115
- return $this->{$methodName}();
116
- }
117
-
118
- /**
119
- * @param object $response
120
- * @param string $nextJob
121
- * @return object
122
- */
123
- private function handleJobResponse($response, $nextJob)
124
- {
125
- // Job is not done
126
- if (true !== $response->status)
127
- {
128
- return $response;
129
- }
130
-
131
- $this->options->currentJob = $nextJob;
132
- $this->options->currentStep = 0;
133
- $this->options->totalSteps = 0;
134
-
135
- // Save options
136
- $this->saveOptions();
137
-
138
- return $response;
139
- }
140
-
141
- /**
142
- * Clone Database
143
- * @return object
144
- */
145
- public function jobDatabase()
146
- {
147
- $database = new Database();
148
- return $this->handleJobResponse($database->start(), "directories");
149
- }
150
-
151
- /**
152
- * Get All Files From Selected Directories Recursively Into a File
153
- * @return object
154
- */
155
- public function jobDirectories()
156
- {
157
- $directories = new Directories();
158
- return $this->handleJobResponse($directories->start(), "files");
159
- }
160
-
161
- /**
162
- * Copy Files
163
- * @return object
164
- */
165
- public function jobFiles()
166
- {
167
- $files = new Files();
168
- return $this->handleJobResponse($files->start(), "data");
169
- }
170
-
171
- /**
172
- * Replace Data
173
- * @return object
174
- */
175
- public function jobData()
176
- {
177
- $data = new Data();
178
- return $this->handleJobResponse($data->start(), "finish");
179
- }
180
-
181
- /**
182
- * Save Clone Data
183
- * @return object
184
- */
185
- public function jobFinish()
186
- {
187
- $finish = new Finish();
188
- return $this->handleJobResponse($finish->start(), '');
189
- }
190
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
+
6
+ /**
7
+ * Class Cloning
8
+ * @package WPStaging\Backend\Modules\Jobs
9
+ */
10
+ class Cloning extends Job
11
+ {
12
+
13
+ /**
14
+ * Save Chosen Cloning Settings
15
+ * @return bool
16
+ */
17
+ public function save()
18
+ {
19
+ if (!isset($_POST) || !isset($_POST["cloneID"]))
20
+ {
21
+ return false;
22
+ }
23
+
24
+ // Generate Options
25
+
26
+ // Clone
27
+ $this->options->clone = $_POST["cloneID"];
28
+ $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
29
+ $this->options->cloneNumber = 1;
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();
37
+
38
+ if (isset($this->options->existingClones[$this->options->clone]))
39
+ {
40
+ $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]->number;
41
+ }
42
+ elseif (!empty($this->options->existingClones))
43
+ {
44
+ $this->options->cloneNumber = count($this->options->existingClones)+1;
45
+ }
46
+
47
+ // Excluded Tables
48
+ if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
49
+ {
50
+ $this->options->excludedTables = $_POST["excludedTables"];
51
+ }
52
+
53
+ // Excluded Directories
54
+ if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
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"]))
70
+ {
71
+ $this->options->includedDirectories = $_POST["includedDirectories"];
72
+ }
73
+
74
+ // Extra Directories
75
+ if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
76
+ {
77
+ $this->options->extraDirectories = $_POST["extraDirectories"];
78
+ }
79
+
80
+ // Directories to Copy
81
+ $this->options->directoriesToCopy = array_merge(
82
+ $this->options->includedDirectories,
83
+ $this->options->extraDirectories
84
+ );
85
+
86
+ array_unshift($this->options->directoriesToCopy, ABSPATH);
87
+
88
+ // Delete files to copy listing
89
+ $this->cache->delete("files_to_copy");
90
+
91
+ return $this->saveOptions();
92
+ }
93
+
94
+ /**
95
+ * Start the cloning job
96
+ */
97
+ public function start()
98
+ {
99
+ if (null === $this->options->currentJob)
100
+ {
101
+ $this->log("Cloning job for {$this->options->clone} finished");
102
+ return true;
103
+ }
104
+
105
+ $methodName = "job" . ucwords($this->options->currentJob);
106
+
107
+ if (!method_exists($this, $methodName))
108
+ {
109
+ $this->log("Can't execute job; Job's method {$methodName} is not found");
110
+ throw new JobNotFoundException($methodName);
111
+ }
112
+
113
+ // Call the job
114
+ //$this->log("execute job: Job's method {$methodName}");
115
+ return $this->{$methodName}();
116
+ }
117
+
118
+ /**
119
+ * @param object $response
120
+ * @param string $nextJob
121
+ * @return object
122
+ */
123
+ private function handleJobResponse($response, $nextJob)
124
+ {
125
+ // Job is not done
126
+ if (true !== $response->status)
127
+ {
128
+ return $response;
129
+ }
130
+
131
+ $this->options->currentJob = $nextJob;
132
+ $this->options->currentStep = 0;
133
+ $this->options->totalSteps = 0;
134
+
135
+ // Save options
136
+ $this->saveOptions();
137
+
138
+ return $response;
139
+ }
140
+
141
+ /**
142
+ * Clone Database
143
+ * @return object
144
+ */
145
+ public function jobDatabase()
146
+ {
147
+ $database = new Database();
148
+ return $this->handleJobResponse($database->start(), "directories");
149
+ }
150
+
151
+ /**
152
+ * Get All Files From Selected Directories Recursively Into a File
153
+ * @return object
154
+ */
155
+ public function jobDirectories()
156
+ {
157
+ $directories = new Directories();
158
+ return $this->handleJobResponse($directories->start(), "files");
159
+ }
160
+
161
+ /**
162
+ * Copy Files
163
+ * @return object
164
+ */
165
+ public function jobFiles()
166
+ {
167
+ $files = new Files();
168
+ return $this->handleJobResponse($files->start(), "data");
169
+ }
170
+
171
+ /**
172
+ * Replace Data
173
+ * @return object
174
+ */
175
+ public function jobData()
176
+ {
177
+ $data = new Data();
178
+ return $this->handleJobResponse($data->start(), "finish");
179
+ }
180
+
181
+ /**
182
+ * Save Clone Data
183
+ * @return object
184
+ */
185
+ public function jobFinish()
186
+ {
187
+ $finish = new Finish();
188
+ return $this->handleJobResponse($finish->start(), '');
189
+ }
190
  }
apps/Backend/Modules/Jobs/Delete.php CHANGED
@@ -1,426 +1,488 @@
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 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
-
84
- $this->clone = (object) $this->clone;
85
-
86
- unset($clones);
87
- }
88
-
89
- /**
90
- * Get Tables
91
- */
92
- private function getTableRecords()
93
- {
94
- $wpdb = WPStaging::getInstance()->get("wpdb");
95
-
96
- $tables = $wpdb->get_results("SHOW TABLE STATUS LIKE 'wpstg{$this->clone->number}_%'");
97
-
98
- $this->tables = array();
99
-
100
- foreach ($tables as $table)
101
- {
102
- $this->tables[] = array(
103
- "name" => $table->Name,
104
- "size" => $this->formatSize(($table->Data_length + $table->Index_length))
105
- );
106
- }
107
-
108
- $this->tables = json_decode(json_encode($this->tables));
109
- }
110
-
111
- /**
112
- * Format bytes into human readable form
113
- * @param int $bytes
114
- * @param int $precision
115
- * @return string
116
- */
117
- public function formatSize($bytes, $precision = 2)
118
- {
119
- if ((int) $bytes < 1)
120
- {
121
- return '';
122
- }
123
-
124
- $units = array('B', "KB", "MB", "GB", "TB");
125
-
126
- $bytes = (int) $bytes;
127
- $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
128
- $pow = pow(1000, $base - floor($base)); // Same rule for 1000
129
-
130
- return round($pow, $precision) . ' ' . $units[(int) floor($base)];
131
- }
132
-
133
-
134
- /**
135
- * @return false
136
- */
137
- public function getClone()
138
- {
139
- return $this->clone;
140
- }
141
-
142
- /**
143
- * @return null|object
144
- */
145
- public function getTables()
146
- {
147
- return $this->tables;
148
- }
149
-
150
- /**
151
- * Start Module
152
- * @param null|array $clone
153
- * @return bool
154
- */
155
- public function start($clone = null)
156
- {
157
- // Set data
158
- $this->setData($clone);
159
-
160
- // Get the job first
161
- $this->getJob();
162
-
163
- $method = "delete" . ucwords($this->job->current);
164
- return $this->{$method}();
165
- }
166
-
167
- /**
168
- * Get job data
169
- */
170
- private function getJob() {
171
- $this->job = $this->cache->get( "delete_job_{$this->clone->name}" );
172
-
173
-
174
- if( null !== $this->job ) {
175
- return;
176
- }
177
-
178
- // Generate JOB
179
- $this->job = ( object ) array(
180
- "current" => "tables",
181
- "nextDirectoryToDelete" => $this->clone->path,
182
- "name" => $this->clone->name
183
- );
184
-
185
- $this->cache->save( "delete_job_{$this->clone->name}", $this->job );
186
- }
187
-
188
-
189
-
190
- /**
191
- * @return bool
192
- */
193
- private function updateJob()
194
- {
195
- $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
196
- return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
197
- }
198
-
199
- /**
200
- * @return array
201
- */
202
- private function getTablesToRemove()
203
- {
204
- $tables = $this->getTableNames();
205
-
206
- if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"]))
207
- {
208
- return $tables;
209
- }
210
-
211
- return array_diff($tables, $_POST["excludedTables"]);
212
- }
213
-
214
- /**
215
- * @return array
216
- */
217
- private function getTableNames()
218
- {
219
- return (!is_array($this->tables)) ? array() : array_map(function($value) {
220
- return ($value->name);
221
- }, $this->tables);
222
- }
223
-
224
- /**
225
- * Delete Tables
226
- */
227
- public function deleteTables()
228
- {
229
- if ($this->isOverThreshold())
230
- {
231
- return;
232
- }
233
-
234
- $wpdb = WPStaging::getInstance()->get("wpdb");
235
-
236
- foreach ($this->getTablesToRemove() as $table)
237
- {
238
- // PROTECTION: Never delete any table that beginns with wp prefix of live site
239
- if($this->startsWith($table, $wpdb->prefix)){
240
- $this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
241
- return false;
242
- } else{
243
- $wpdb->query("DROP TABLE {$table}");
244
- }
245
- }
246
-
247
- // Move on to the next
248
- $this->job->current = "directory";
249
- $this->updateJob();
250
- }
251
-
252
- /**
253
- * Check if a strings start with a specific string
254
- * @param string $haystack
255
- * @param string $needle
256
- * @return bool
257
- */
258
- protected function startsWith($haystack, $needle)
259
- {
260
- $length = strlen($needle);
261
- return (substr($haystack, 0, $length) === $needle);
262
- }
263
-
264
- /**
265
- * Delete a specific directory and all of its subfolders in a native way without using any external caching data
266
- *
267
- * @param array $dir
268
- * @param array $excluded_dirs
269
- * @return boolean false when its finished
270
- */
271
- function deleteDirectoryNative( $dir = '' ) {
272
-
273
- if( !file_exists( $dir ) ) {
274
- return $this->isFinished();
275
- }
276
-
277
- if( !is_dir( $dir ) || is_link( $dir ) ) {
278
- unlink( $dir );
279
- return $this->isFinished();
280
- }
281
- foreach ( scandir( $dir ) as $item ) {
282
- if( $item == '.' || $item == '..' ) {
283
- continue;
284
- }
285
- if( !$this->deleteDirectoryNative( $dir . "/" . $item, false ) ) {
286
- //chmod( $dir . "/" . $item, 0777 );
287
- //if( !$this->deleteDirectoryNative( $dir . "/" . $item, false ) ){
288
- //return false;
289
- //}
290
- }
291
- };
292
-
293
- rmdir( $dir );
294
- return $this->isFinished();
295
- }
296
-
297
-
298
-
299
- /**
300
- * Delete Directories
301
- */
302
- public function deleteDirectory()
303
- {
304
- // No deleting directories or root of this clone is deleted
305
- if ($this->isDirectoryDeletingFinished())
306
- {
307
- $this->job->current = "finish";
308
- $this->updateJob();
309
- return;
310
- }
311
-
312
- $this->processDirectory($this->job->nextDirectoryToDelete);
313
-
314
- return;
315
- }
316
-
317
- /**
318
- * @return bool
319
- */
320
- public function isDirectoryDeletingFinished()
321
- {
322
- return (
323
- !is_dir($this->clone->path) ||
324
- (false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
325
- ABSPATH === $this->job->nextDirectoryToDelete
326
- );
327
- }
328
-
329
- /**
330
- * Delete contents of the directory if there are no directories in it and then delete itself
331
- * @param string $path
332
- * @return mixed
333
- */
334
- private function processDirectory($path)
335
- {
336
- // We hit the limit, stop
337
- if ($this->shouldStop($path))
338
- {
339
- $this->updateJob();
340
- return false;
341
- }
342
-
343
- $this->totalRecursion++;
344
-
345
- $contents = new \DirectoryIterator($path);
346
-
347
- foreach ($contents as $content)
348
- {
349
- // Skip dots
350
- if ($content->isDot())
351
- {
352
- continue;
353
- }
354
-
355
- // Get into the directory
356
- if (!$content->isLink() && $content->isDir())
357
- {
358
- return $this->processDirectory($content->getRealPath());
359
- }
360
-
361
- // Delete file
362
- if ($content->isFile())
363
- {
364
- @unlink($content->getRealPath());
365
- }
366
- }
367
-
368
- // Delete directory
369
- $this->job->lastDeletedDirectory = realpath($path . "/..");
370
- @rmdir($path);
371
- $this->updateJob();
372
- $this->processDirectory($this->job->nextDirectoryToDelete);
373
- }
374
-
375
- /**
376
- * @param string $path
377
- * @return bool
378
- */
379
- private function shouldStop($path)
380
- {
381
- // Just to make sure the root dir is never deleted!
382
- if ($path === get_home_path()){
383
- $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
384
- return true;
385
- }
386
-
387
- // Check if threshold is reached and is valid dir
388
- return (
389
- $this->isOverThreshold() ||
390
- !is_dir($path) ||
391
- $this->isDirectoryDeletingFinished()
392
- );
393
- }
394
-
395
- /**
396
- * Finish / Update Existing Clones
397
- */
398
- public function deleteFinish()
399
- {
400
- $existingClones = get_option("wpstg_existing_clones_beta", array());
401
-
402
- // Check if clones still exist
403
- $this->log("Verifying existing clones...");
404
- foreach ($existingClones as $name => $clone)
405
- {
406
- if (!is_dir($clone["path"]))
407
- {
408
- unset($existingClones[$name]);
409
- }
410
- }
411
- $this->log("Existing clones verified!");
412
-
413
- if (false === update_option("wpstg_existing_clones_beta", $existingClones))
414
- {
415
- $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
416
- }
417
-
418
- // Delete cached file
419
- $this->cache->delete("delete_job_{$this->clone->name}");
420
- $this->cache->delete("delete_directories_{$this->clone->name}");
421
-
422
- //return true;
423
- $response = array('delete' => 'finished');
424
- wp_die(json_encode($response));
425
- }
426
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs;
4
+
5
+ use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
6
+ use WPStaging\Utils\Directories;
7
+ use WPStaging\Utils\Logger;
8
+ use WPStaging\WPStaging;
9
+
10
+ /**
11
+ * Class Delete
12
+ * @package WPStaging\Backend\Modules\Jobs
13
+ */
14
+ class Delete extends Job {
15
+
16
+ /**
17
+ * @var false
18
+ */
19
+ private $clone = false;
20
+
21
+ /**
22
+ * @var null|object
23
+ */
24
+ private $tables = null;
25
+
26
+ /**
27
+ * @var object|null
28
+ */
29
+ private $job = null;
30
+
31
+ /**
32
+ * @var bool
33
+ */
34
+ private $forceDeleteDirectories = false;
35
+
36
+ /**
37
+ *
38
+ * @var object
39
+ */
40
+ public $wpdb;
41
+
42
+ public function __construct() {
43
+ parent::__construct();
44
+ $this->wpdb = WPStaging::getInstance()->get("wpdb");
45
+ }
46
+
47
+ /**
48
+ * Sets Clone and Table Records
49
+ * @param null|array $clone
50
+ */
51
+ public function setData($clone = null) {
52
+ if (!is_array($clone)) {
53
+ $this->getCloneRecords();
54
+ } else {
55
+ $this->clone = (object) $clone;
56
+ $this->forceDeleteDirectories = true;
57
+ }
58
+
59
+ $this->getTableRecords();
60
+ }
61
+
62
+ /**
63
+ * Get clone
64
+ * @param null|string $name
65
+ * @throws CloneNotFoundException
66
+ */
67
+ private function getCloneRecords($name = null) {
68
+ if (null === $name && !isset($_POST["clone"])) {
69
+ $this->log("Clone name is not set", Logger::TYPE_FATAL);
70
+ throw new CloneNotFoundException();
71
+ }
72
+
73
+ if (null === $name) {
74
+ $name = $_POST["clone"];
75
+ }
76
+
77
+ $clones = get_option("wpstg_existing_clones_beta", array());
78
+
79
+ if (empty($clones) || !isset($clones[$name])) {
80
+ $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
81
+ throw new CloneNotFoundException();
82
+ }
83
+
84
+ $this->clone = $clones[$name];
85
+ $this->clone["name"] = $name;
86
+
87
+ $this->clone = (object) $this->clone;
88
+
89
+ unset($clones);
90
+ }
91
+
92
+ /**
93
+ * Get Tables
94
+ */
95
+ private function getTableRecords() {
96
+ //$wpdb = WPStaging::getInstance()->get("wpdb");
97
+ $this->wpdb = WPStaging::getInstance()->get("wpdb");
98
+
99
+
100
+ $stagingPrefix = $this->getStagingPrefix();
101
+
102
+ $tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$stagingPrefix}%'");
103
+
104
+ $this->tables = array();
105
+
106
+ foreach ($tables as $table) {
107
+ $this->tables[] = array(
108
+ "name" => $table->Name,
109
+ "size" => $this->formatSize(($table->Data_length + $table->Index_length))
110
+ );
111
+ }
112
+
113
+ $this->tables = json_decode(json_encode($this->tables));
114
+ }
115
+
116
+ /**
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 hopefully 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
+ //wp_die(var_dump($matches));
136
+
137
+ if (!empty($matches[1])) {
138
+ $this->clone->prefix = $matches[1];
139
+ } else {
140
+ $this->log("Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
141
+ // Create a random prefix which hopefully never exists.
142
+ return $this->clone->prefix = rand(7, 15) . '_';
143
+ }
144
+ }
145
+ }
146
+
147
+ // Check if staging prefix is the same as the live prefix
148
+ if ($this->wpdb->prefix == $this->clone->prefix) {
149
+ $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");
150
+ 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");
151
+ }
152
+
153
+ // Else
154
+ return $this->clone->prefix;
155
+ }
156
+
157
+ /**
158
+ * Format bytes into human readable form
159
+ * @param int $bytes
160
+ * @param int $precision
161
+ * @return string
162
+ */
163
+ public function formatSize($bytes, $precision = 2) {
164
+ if ((int) $bytes < 1) {
165
+ return '';
166
+ }
167
+
168
+ $units = array('B', "KB", "MB", "GB", "TB");
169
+
170
+ $bytes = (int) $bytes;
171
+ $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
172
+ $pow = pow(1000, $base - floor($base)); // Same rule for 1000
173
+
174
+ return round($pow, $precision) . ' ' . $units[(int) floor($base)];
175
+ }
176
+
177
+ /**
178
+ * @return false
179
+ */
180
+ public function getClone() {
181
+ return $this->clone;
182
+ }
183
+
184
+ /**
185
+ * @return null|object
186
+ */
187
+ public function getTables() {
188
+ return $this->tables;
189
+ }
190
+
191
+ /**
192
+ * Start Module
193
+ * @param null|array $clone
194
+ * @return bool
195
+ */
196
+ public function start($clone = null) {
197
+ // Set data
198
+ $this->setData($clone);
199
+
200
+ // Get the job first
201
+ $this->getJob();
202
+
203
+ $method = "delete" . ucwords($this->job->current);
204
+ return $this->{$method}();
205
+ }
206
+
207
+ /**
208
+ * Get job data
209
+ */
210
+ private function getJob() {
211
+ $this->job = $this->cache->get("delete_job_{$this->clone->name}");
212
+
213
+
214
+ if (null !== $this->job) {
215
+ return;
216
+ }
217
+
218
+ // Generate JOB
219
+ $this->job = (object) array(
220
+ "current" => "tables",
221
+ "nextDirectoryToDelete" => $this->clone->path,
222
+ "name" => $this->clone->name
223
+ );
224
+
225
+ $this->cache->save("delete_job_{$this->clone->name}", $this->job);
226
+ }
227
+
228
+ /**
229
+ * @return bool
230
+ */
231
+ private function updateJob() {
232
+ $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
233
+ return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
234
+ }
235
+
236
+ /**
237
+ * @return array
238
+ */
239
+ private function getTablesToRemove() {
240
+ $tables = $this->getTableNames();
241
+
242
+ if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"])) {
243
+ return $tables;
244
+ }
245
+
246
+ return array_diff($tables, $_POST["excludedTables"]);
247
+ }
248
+
249
+ /**
250
+ * @return array
251
+ */
252
+ private function getTableNames() {
253
+ return (!is_array($this->tables)) ? array() : array_map(function($value) {
254
+ return ($value->name);
255
+ }, $this->tables);
256
+ }
257
+
258
+ /**
259
+ * Delete Tables
260
+ */
261
+ public function deleteTables() {
262
+ if ($this->isOverThreshold()) {
263
+ return;
264
+ }
265
+
266
+ //$wpdb = WPStaging::getInstance()->get("wpdb");
267
+
268
+ foreach ($this->getTablesToRemove() as $table) {
269
+ // PROTECTION: Never delete any table that beginns with wp prefix of live site
270
+ if ($this->startsWith($table, $this->wpdb->prefix)) {
271
+ $this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
272
+ return false;
273
+ } else {
274
+ $this->wpdb->query("DROP TABLE {$table}");
275
+ }
276
+ }
277
+
278
+ // Move on to the next
279
+ $this->job->current = "directory";
280
+ $this->updateJob();
281
+ }
282
+
283
+ /**
284
+ * Check if a strings start with a specific string
285
+ * @param string $haystack
286
+ * @param string $needle
287
+ * @return bool
288
+ */
289
+ protected function startsWith($haystack, $needle) {
290
+ $length = strlen($needle);
291
+ return (substr($haystack, 0, $length) === $needle);
292
+ }
293
+
294
+ /**
295
+ * Delete complete directory including all files and subfolders
296
+ *
297
+ * @throws InvalidArgumentException
298
+ */
299
+ public function deleteDirectory() {
300
+
301
+ // Finished or path does not exist
302
+ if (!is_dir($this->clone->path)) {
303
+ $this->job->current = "finish";
304
+ $this->updateJob();
305
+ return $this->returnFinish();
306
+ }
307
+
308
+ $this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
309
+
310
+ // Just to make sure the root dir is never deleted!
311
+ if ($this->clone->path === get_home_path()) {
312
+ $this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
313
+ $this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
314
+ }
315
+
316
+ // Check if threshold is reached
317
+ if ($this->isOverThreshold()) {
318
+ //$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
319
+ return;
320
+ }
321
+
322
+ $di = new \RecursiveDirectoryIterator($this->clone->path, \FilesystemIterator::SKIP_DOTS);
323
+ $ri = new \RecursiveIteratorIterator($di, \RecursiveIteratorIterator::CHILD_FIRST);
324
+ foreach ($ri as $file) {
325
+ $file->isDir() ? @rmdir($file) : unlink($file);
326
+ if ($this->isOverThreshold()) {
327
+ //$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
328
+ return;
329
+ }
330
+ }
331
+ //rmdir($this->clone->path);
332
+ // if (is_dir($this->clone->path)) {
333
+ // return $this->returnException('Unable to delete all files in folder' . $this->clone->path . '<br>Please try to delete the folder manually via FTP. Than run the delete function again.');
334
+ // } else {
335
+ // return $this->returnFinish();
336
+ // }
337
+ if (@rmdir($this->clone->path)){
338
+ return $this->returnFinish();
339
+ }
340
+ return;
341
+
342
+
343
+
344
+ }
345
+
346
+ /**
347
+ * Delete Directories
348
+ */
349
+ public function deleteDirectory_old() {
350
+ // No deleting directories or root of this clone is deleted
351
+ if ($this->isDirectoryDeletingFinished()) {
352
+ $this->job->current = "finish";
353
+ $this->updateJob();
354
+ return;
355
+ }
356
+
357
+ $this->processDirectory($this->job->nextDirectoryToDelete);
358
+
359
+ return;
360
+ }
361
+
362
+ /**
363
+ * @return bool
364
+ */
365
+ public function isDirectoryDeletingFinished() {
366
+ return (
367
+ (false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
368
+ !is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
369
+ );
370
+ }
371
+
372
+ /**
373
+ * Delete contents of the directory if there are no directories in it and then delete itself
374
+ * @param string $path
375
+ * @return mixed
376
+ */
377
+ private function processDirectory($path) {
378
+ // We hit the limit, stop
379
+ if ($this->shouldStop($path)) {
380
+ $this->updateJob();
381
+ return false;
382
+ }
383
+
384
+ $this->totalRecursion++;
385
+
386
+ $contents = new \DirectoryIterator($path);
387
+
388
+ foreach ($contents as $content => $value) {
389
+
390
+ // Skip dots
391
+ if ($content->isDot())
392
+
393
+
394
+ // Get into the directory
395
+ if (!$content->isLink() && $content->isDir()) {
396
+ return $this->processDirectory($content->getRealPath());
397
+ }
398
+
399
+ // Delete file
400
+ if ($content->isFile()) {
401
+ @unlink($content->getRealPath());
402
+ }
403
+ }
404
+
405
+ // Delete directory
406
+ $this->job->lastDeletedDirectory = realpath($path . "/..");
407
+ @rmdir($path);
408
+ $this->updateJob();
409
+ $this->processDirectory($this->job->nextDirectoryToDelete);
410
+ }
411
+
412
+ /**
413
+ * @param string $path
414
+ * @return bool
415
+ */
416
+ private function shouldStop($path) {
417
+ // Just to make sure the root dir is never deleted!
418
+ if ($path === get_home_path()) {
419
+ $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
420
+ return true;
421
+ }
422
+
423
+ // Check if threshold is reached and is valid dir
424
+ return (
425
+ $this->isOverThreshold() ||
426
+ !is_dir($path) ||
427
+ $this->isDirectoryDeletingFinished()
428
+ );
429
+ }
430
+
431
+ /**
432
+ * Finish / Update Existing Clones
433
+ */
434
+ public function deleteFinish() {
435
+ $existingClones = get_option("wpstg_existing_clones_beta", array());
436
+
437
+ // Check if clones still exist
438
+ $this->log("Verifying existing clones...");
439
+ foreach ($existingClones as $name => $clone) {
440
+ if (!is_dir($clone["path"])) {
441
+ unset($existingClones[$name]);
442
+ }
443
+ }
444
+ $this->log("Existing clones verified!");
445
+
446
+ if (false === update_option("wpstg_existing_clones_beta", $existingClones)) {
447
+ $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
448
+ }
449
+
450
+ // Delete cached file
451
+ $this->cache->delete("delete_job_{$this->clone->name}");
452
+ $this->cache->delete("delete_directories_{$this->clone->name}");
453
+
454
+ //return true;
455
+ $response = array('delete' => 'finished');
456
+ wp_die(json_encode($response));
457
+ }
458
+
459
+ /**
460
+ * Get json response
461
+ * return json
462
+ */
463
+ // private function returnException($message = ''){
464
+ // wp_die( json_encode(array(
465
+ // 'job' => 'delete',
466
+ // 'status' => false,
467
+ // 'message' => $message,
468
+ // 'error' => true
469
+ // )));
470
+ // }
471
+ /**
472
+ * Get json response
473
+ * return json
474
+ */
475
+ private function returnFinish($message = '') {
476
+
477
+ $this->deleteFinish();
478
+
479
+ wp_die(json_encode(array(
480
+ 'job' => 'delete',
481
+ 'status' => true,
482
+ 'message' => $message,
483
+ 'error' => false,
484
+ 'delete' => 'finished'
485
+ )));
486
+ }
487
+
488
+ }
apps/Backend/Modules/Jobs/Directories.php CHANGED
@@ -2,16 +2,14 @@
2
 
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
- //ini_set('display_startup_errors', 1);
6
- //ini_set('display_errors', 1);
7
- //error_reporting(-1);
8
-
9
  // No Direct Access
10
  if( !defined( "WPINC" ) ) {
11
  die;
12
  }
13
 
14
  use WPStaging\WPStaging;
 
 
15
 
16
  /**
17
  * Class Files
@@ -156,6 +154,11 @@ class Directories extends JobExecutable {
156
 
157
  // Add scanned directory listing
158
  $this->options->scannedDirectories[] = $dir;
 
 
 
 
 
159
  }
160
 
161
  $this->saveOptions();
@@ -165,6 +168,7 @@ class Directories extends JobExecutable {
165
  }
166
 
167
  /**
 
168
  * @param $directory
169
  * @return bool
170
  */
@@ -177,8 +181,13 @@ class Directories extends JobExecutable {
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;
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
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();
168
  }
169
 
170
  /**
171
+ * Get files from directory
172
  * @param $directory
173
  * @return bool
174
  */
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;
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -1,287 +1,288 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- // No Direct Access
5
- use WPStaging\Utils\Logger;
6
-
7
- if (!defined("WPINC"))
8
- {
9
- die;
10
- }
11
-
12
- /**
13
- * Class Files
14
- * @package WPStaging\Backend\Modules\Jobs
15
- */
16
- class Files extends JobExecutable
17
- {
18
-
19
- /**
20
- * @var \SplFileObject
21
- */
22
- private $file;
23
-
24
- /**
25
- * @var int
26
- */
27
- private $maxFilesPerRun = 500;
28
-
29
- /**
30
- * @var string
31
- */
32
- private $destination;
33
-
34
- /**
35
- * Initialization
36
- */
37
- public function initialize()
38
- {
39
- $this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
40
-
41
- $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
42
-
43
- if (is_file($filePath))
44
- {
45
- $this->file = new \SplFileObject($filePath, 'r');
46
- }
47
-
48
- // Informational logs
49
- if (0 == $this->options->currentStep)
50
- {
51
- $this->log("Copying files...");
52
- }
53
-
54
- $this->settings->batchSize = $this->settings->batchSize * 1000000;
55
- }
56
-
57
- /**
58
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
59
- * @return void
60
- */
61
- protected function calculateTotalSteps()
62
- {
63
- $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
64
- }
65
-
66
- /**
67
- * Execute the Current Step
68
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
69
- * @return bool
70
- */
71
- protected function execute()
72
- {
73
- // Finished
74
- if ($this->isFinished())
75
- {
76
- $this->log("Copying files finished");
77
- $this->prepareResponse(true, false);
78
- return false;
79
- }
80
-
81
- // Get files and copy'em
82
- if (!$this->getFilesAndCopy())
83
- {
84
- $this->prepareResponse(false, false);
85
- return false;
86
- }
87
-
88
- // Prepare and return response
89
- $this->prepareResponse();
90
-
91
- // Not finished
92
- return true;
93
- }
94
-
95
- /**
96
- * Get files and copy
97
- * @return bool
98
- */
99
- private function getFilesAndCopy()
100
- {
101
- // Over limits threshold
102
- if ($this->isOverThreshold())
103
- {
104
- // Prepare response and save current progress
105
- $this->prepareResponse(false, false);
106
- $this->saveOptions();
107
- return false;
108
- }
109
-
110
- // Skip processed ones
111
- if ($this->options->copiedFiles != 0)
112
- {
113
- $this->file->seek($this->options->copiedFiles);
114
- }
115
-
116
- $this->file->setFlags(\SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
117
-
118
- // One thousand files at a time
119
- for ($i = 0; $i <= $this->maxFilesPerRun; $i++)
120
- {
121
- // End of file
122
- if ($this->file->eof())
123
- {
124
- break;
125
- }
126
-
127
- $this->copyFile($this->file->fgets());
128
- }
129
-
130
- $totalFiles = $this->maxFilesPerRun + $this->options->copiedFiles;
131
- $this->log("Total {$totalFiles} files processed");
132
-
133
- return true;
134
- }
135
-
136
- /**
137
- * Checks Whether There is Any Job to Execute or Not
138
- * @return bool
139
- */
140
- private function isFinished()
141
- {
142
- return (
143
- $this->options->currentStep > $this->options->totalSteps ||
144
- $this->options->copiedFiles >= $this->options->totalFiles
145
- );
146
- }
147
-
148
- /**
149
- * @param string $file
150
- * @return bool
151
- */
152
- private function copyFile($file)
153
- {
154
- $file = trim($file);
155
-
156
- // Increment copied files whatever the result is
157
- // This way we don't get stuck in the same step / files
158
- $this->options->copiedFiles++;
159
-
160
- // Invalid file, skipping it as if succeeded
161
- if (!is_file($file) || !is_readable($file))
162
- {
163
- $this->log("Can't read file or file doesn't exist {$file}");
164
- return true;
165
- }
166
-
167
- // Failed to get destination
168
- if (false === ($destination = $this->getDestination($file)))
169
- {
170
- $this->log("Can't get the destination of {$file}");
171
- return false;
172
- }
173
-
174
- // Good old PHP
175
- return $this->copy($file, $destination);
176
- }
177
-
178
- /**
179
- * Gets destination file and checks if the directory exists, if it does not attempts to create it.
180
- * If creating destination directory fails, it returns false, gives destination full path otherwise
181
- * @param string $file
182
- * @return bool|string
183
- */
184
- private function getDestination($file)
185
- {
186
- $relativePath = str_replace(ABSPATH, null, $file);
187
- $destinationPath = $this->destination . $relativePath;
188
- $destinationDirectory = dirname($destinationPath);
189
-
190
- if (!is_dir($destinationDirectory) && !@mkdir($destinationDirectory, 0775, true))
191
- {
192
- $this->log("Destination directory doesn't exist; {$destinationDirectory}", Logger::TYPE_ERROR);
193
- return false;
194
- }
195
-
196
- return $destinationPath;
197
- }
198
-
199
- /**
200
- * Copy File using PHP
201
- * @param string $file
202
- * @param string $destination
203
- * @return bool
204
- */
205
- private function copy($file, $destination)
206
- {
207
- // Get file size
208
- $fileSize = filesize($file);
209
-
210
- // File is over batch size
211
- if ($fileSize >= $this->settings->batchSize)
212
- {
213
- $this->log("Trying to copy big file {$file} -> {$destination}", Logger::TYPE_INFO);
214
- return $this->copyBig($file, $destination, $this->settings->batchSize);
215
- }
216
-
217
- // Attempt to copy
218
- if (!@copy($file, $destination))
219
- {
220
- $this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
221
- return false;
222
- }
223
-
224
- return true;
225
- }
226
-
227
- /**
228
- * Copy bigger files than $this->settings->batchSize
229
- * @param string $file
230
- * @param string $destination
231
- * @return bool
232
- *
233
- * @deprecated since version 2.0.0 (Supported only in php 5.5.11 and later)
234
- */
235
- // private function copyBig($file, $destination)
236
- // {
237
- // $bytes = 0;
238
- // $fileInput = new \SplFileObject($file, "rb");
239
- // $fileOutput = new \SplFileObject($destination, 'w');
240
- //
241
- // $this->log("Copying big file; {$file} -> {$destination}");
242
- //
243
- // while (!$fileInput->eof())
244
- // {
245
- // $bytes += $fileOutput->fwrite($fileInput->fread($this->settings->batchSize));
246
- // }
247
- //
248
- // $fileInput = null;
249
- // $fileOutput= null;
250
- //
251
- // return ($bytes > 0);
252
- // }
253
-
254
- /**
255
- * Copy bigger files than $this->settings->batchSize
256
- * @param string $src
257
- * @param string $dst
258
- * @param int $buffersize
259
- * @return boolean
260
- */
261
- private function copyBig($src, $dst, $buffersize) {
262
- $src = fopen($src, 'r');
263
- $dest = fopen($dst, 'w');
264
-
265
- // Try first method:
266
- while (! feof($src)){
267
- if (false === fwrite($dest, fread($src, $buffersize))){
268
- $error = true;
269
- }
270
- }
271
- // Try second method if first one failed
272
- if (isset($error) && ($error === true)){
273
- while(!feof($src)){
274
- if (false === stream_copy_to_stream($src, $dest, 1024 )) {
275
- $this->log("Can not copy big file; {$src} -> {$dest}");
276
- fclose($src);
277
- fclose($dest);
278
- return false;
279
- }
280
- }
281
- }
282
- // Close any open handler
283
- fclose($src);
284
- fclose($dest);
285
- return true;
286
- }
 
287
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ // No Direct Access
5
+ use WPStaging\Utils\Logger;
6
+
7
+ if (!defined("WPINC"))
8
+ {
9
+ die;
10
+ }
11
+
12
+ /**
13
+ * Class Files
14
+ * @package WPStaging\Backend\Modules\Jobs
15
+ */
16
+ class Files extends JobExecutable
17
+ {
18
+
19
+ /**
20
+ * @var \SplFileObject
21
+ */
22
+ private $file;
23
+
24
+ /**
25
+ * @var int
26
+ */
27
+ private $maxFilesPerRun;
28
+
29
+ /**
30
+ * @var string
31
+ */
32
+ private $destination;
33
+
34
+ /**
35
+ * Initialization
36
+ */
37
+ public function initialize()
38
+ {
39
+ $this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
40
+
41
+ $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
42
+
43
+ if (is_file($filePath))
44
+ {
45
+ $this->file = new \SplFileObject($filePath, 'r');
46
+ }
47
+
48
+ // Informational logs
49
+ if (0 == $this->options->currentStep)
50
+ {
51
+ $this->log("Copying files...");
52
+ }
53
+
54
+ $this->settings->batchSize = $this->settings->batchSize * 1000000;
55
+ $this->maxFilesPerRun = $this->settings->fileLimit;
56
+ }
57
+
58
+ /**
59
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
60
+ * @return void
61
+ */
62
+ protected function calculateTotalSteps()
63
+ {
64
+ $this->options->totalSteps = ceil($this->options->totalFiles / $this->maxFilesPerRun);
65
+ }
66
+
67
+ /**
68
+ * Execute the Current Step
69
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
70
+ * @return bool
71
+ */
72
+ protected function execute()
73
+ {
74
+ // Finished
75
+ if ($this->isFinished())
76
+ {
77
+ $this->log("Copying files finished");
78
+ $this->prepareResponse(true, false);
79
+ return false;
80
+ }
81
+
82
+ // Get files and copy'em
83
+ if (!$this->getFilesAndCopy())
84
+ {
85
+ $this->prepareResponse(false, false);
86
+ return false;
87
+ }
88
+
89
+ // Prepare and return response
90
+ $this->prepareResponse();
91
+
92
+ // Not finished
93
+ return true;
94
+ }
95
+
96
+ /**
97
+ * Get files and copy
98
+ * @return bool
99
+ */
100
+ private function getFilesAndCopy()
101
+ {
102
+ // Over limits threshold
103
+ if ($this->isOverThreshold())
104
+ {
105
+ // Prepare response and save current progress
106
+ $this->prepareResponse(false, false);
107
+ $this->saveOptions();
108
+ return false;
109
+ }
110
+
111
+ // Skip processed ones
112
+ if ($this->options->copiedFiles != 0)
113
+ {
114
+ $this->file->seek($this->options->copiedFiles);
115
+ }
116
+
117
+ $this->file->setFlags(\SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
118
+
119
+ // One thousand files at a time
120
+ for ($i = 0; $i <= $this->maxFilesPerRun; $i++)
121
+ {
122
+ // End of file
123
+ if ($this->file->eof())
124
+ {
125
+ break;
126
+ }
127
+
128
+ $this->copyFile($this->file->fgets());
129
+ }
130
+
131
+ $totalFiles = $this->maxFilesPerRun + $this->options->copiedFiles;
132
+ $this->log("Total {$totalFiles} files processed");
133
+
134
+ return true;
135
+ }
136
+
137
+ /**
138
+ * Checks Whether There is Any Job to Execute or Not
139
+ * @return bool
140
+ */
141
+ private function isFinished()
142
+ {
143
+ return (
144
+ $this->options->currentStep > $this->options->totalSteps ||
145
+ $this->options->copiedFiles >= $this->options->totalFiles
146
+ );
147
+ }
148
+
149
+ /**
150
+ * @param string $file
151
+ * @return bool
152
+ */
153
+ private function copyFile($file)
154
+ {
155
+ $file = trim($file);
156
+
157
+ // Increment copied files whatever the result is
158
+ // This way we don't get stuck in the same step / files
159
+ $this->options->copiedFiles++;
160
+
161
+ // Invalid file, skipping it as if succeeded
162
+ if (!is_file($file) || !is_readable($file))
163
+ {
164
+ $this->log("Can't read file or file doesn't exist {$file}");
165
+ return true;
166
+ }
167
+
168
+ // Failed to get destination
169
+ if (false === ($destination = $this->getDestination($file)))
170
+ {
171
+ $this->log("Can't get the destination of {$file}");
172
+ return false;
173
+ }
174
+
175
+ // Good old PHP
176
+ return $this->copy($file, $destination);
177
+ }
178
+
179
+ /**
180
+ * Gets destination file and checks if the directory exists, if it does not attempts to create it.
181
+ * If creating destination directory fails, it returns false, gives destination full path otherwise
182
+ * @param string $file
183
+ * @return bool|string
184
+ */
185
+ private function getDestination($file)
186
+ {
187
+ $relativePath = str_replace(ABSPATH, null, $file);
188
+ $destinationPath = $this->destination . $relativePath;
189
+ $destinationDirectory = dirname($destinationPath);
190
+
191
+ if (!is_dir($destinationDirectory) && !@mkdir($destinationDirectory, 0775, true))
192
+ {
193
+ $this->log("Destination directory doesn't exist; {$destinationDirectory}", Logger::TYPE_ERROR);
194
+ return false;
195
+ }
196
+
197
+ return $destinationPath;
198
+ }
199
+
200
+ /**
201
+ * Copy File using PHP
202
+ * @param string $file
203
+ * @param string $destination
204
+ * @return bool
205
+ */
206
+ private function copy($file, $destination)
207
+ {
208
+ // Get file size
209
+ $fileSize = filesize($file);
210
+
211
+ // File is over batch size
212
+ if ($fileSize >= $this->settings->batchSize)
213
+ {
214
+ $this->log("Trying to copy big file {$file} -> {$destination}", Logger::TYPE_INFO);
215
+ return $this->copyBig($file, $destination, $this->settings->batchSize);
216
+ }
217
+
218
+ // Attempt to copy
219
+ if (!@copy($file, $destination))
220
+ {
221
+ $this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
222
+ return false;
223
+ }
224
+
225
+ return true;
226
+ }
227
+
228
+ /**
229
+ * Copy bigger files than $this->settings->batchSize
230
+ * @param string $file
231
+ * @param string $destination
232
+ * @return bool
233
+ *
234
+ * @deprecated since version 2.0.0 (Supported only in php 5.5.11 and later)
235
+ */
236
+ // private function copyBig($file, $destination)
237
+ // {
238
+ // $bytes = 0;
239
+ // $fileInput = new \SplFileObject($file, "rb");
240
+ // $fileOutput = new \SplFileObject($destination, 'w');
241
+ //
242
+ // $this->log("Copying big file; {$file} -> {$destination}");
243
+ //
244
+ // while (!$fileInput->eof())
245
+ // {
246
+ // $bytes += $fileOutput->fwrite($fileInput->fread($this->settings->batchSize));
247
+ // }
248
+ //
249
+ // $fileInput = null;
250
+ // $fileOutput= null;
251
+ //
252
+ // return ($bytes > 0);
253
+ // }
254
+
255
+ /**
256
+ * Copy bigger files than $this->settings->batchSize
257
+ * @param string $src
258
+ * @param string $dst
259
+ * @param int $buffersize
260
+ * @return boolean
261
+ */
262
+ private function copyBig($src, $dst, $buffersize) {
263
+ $src = fopen($src, 'r');
264
+ $dest = fopen($dst, 'w');
265
+
266
+ // Try first method:
267
+ while (! feof($src)){
268
+ if (false === fwrite($dest, fread($src, $buffersize))){
269
+ $error = true;
270
+ }
271
+ }
272
+ // Try second method if first one failed
273
+ if (isset($error) && ($error === true)){
274
+ while(!feof($src)){
275
+ if (false === stream_copy_to_stream($src, $dest, 1024 )) {
276
+ $this->log("Can not copy big file; {$src} -> {$dest}");
277
+ fclose($src);
278
+ fclose($dest);
279
+ return false;
280
+ }
281
+ }
282
+ }
283
+ // Close any open handler
284
+ fclose($src);
285
+ fclose($dest);
286
+ return true;
287
+ }
288
  }
apps/Backend/Modules/Jobs/Finish.php CHANGED
@@ -1,236 +1,262 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\WPStaging;
5
-
6
- //error_reporting( E_ALL );
7
-
8
- /**
9
- * Class Finish
10
- * @package WPStaging\Backend\Modules\Jobs
11
- */
12
- class Finish extends Job
13
- {
14
-
15
- /**
16
- * Start Module
17
- * @return object
18
- */
19
- public function start()
20
- {
21
- // Delete Cache Files
22
- $this->deleteCacheFiles();
23
-
24
- // Prepare .htaccess file for staging site
25
- //$this->protectDirectoriesAndFiles();
26
-
27
- // Prepare clone records & save scanned directories for delete job later
28
- $this->prepareCloneDataRecords();
29
-
30
- return (object) $this->options->existingClones[$this->options->clone];
31
- }
32
-
33
- /**
34
- * Delete Cache Files
35
- */
36
- protected function deleteCacheFiles()
37
- {
38
- $this->log("Deleting clone job's cache files...");
39
-
40
- // Clean cache files
41
- $this->cache->delete("clone_options");
42
- $this->cache->delete("files_to_copy");
43
-
44
- $this->log("Clone job's cache files have been deleted!");
45
- }
46
-
47
- /**
48
- * Get Upload Directory
49
- * @return string
50
- */
51
- private function getUploadDirectory()
52
- {
53
- $wpUploadDirectory = wp_get_upload_dir();
54
- $uploadDirectory = $wpUploadDirectory["basedir"] . DIRECTORY_SEPARATOR . WPStaging::SLUG;
55
-
56
- // Failed to create upload directory
57
- if (!is_dir($uploadDirectory) && !wp_mkdir_p($uploadDirectory))
58
- {
59
- $this->log("Upload directory ({$uploadDirectory}) doesn't exist and failed to create!");
60
- }
61
-
62
- $uploadDirectory = apply_filters("wpstg_get_upload_dir", $uploadDirectory);
63
-
64
- return rtrim($uploadDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
65
- }
66
-
67
- /**
68
- * Get .htaccess rules
69
- * @return string
70
- */
71
- private function getHtaccessRules()
72
- {
73
- // Prevent directory browsing and direct access to all files
74
- $rules = "<Files \"*\">\n";
75
- $rules .= "<IfModule mod_access.c>\n";
76
- $rules .= "Deny from all\n";
77
- $rules .= "</IfModule>\n";
78
- $rules .= "<IfModule !mod_access_compat>\n";
79
- $rules .= "<IfModule mod_authz_host.c>\n";
80
- $rules .= "Deny from all\n";
81
- $rules .= "</IfModule>\n";
82
- $rules .= "</IfModule>\n";
83
- $rules .= "<IfModule mod_access_compat>\n";
84
- $rules .= "Deny from all\n";
85
- $rules .= "</IfModule>\n";
86
- $rules .= "</Files>\n";
87
-
88
- return apply_filters("wpstg_protected_directory_htaccess_rules", $rules);
89
- }
90
-
91
- /**
92
- * Update .htaccess file and its rules
93
- * @param string $file
94
- * @param string $contents
95
- * @return bool
96
- */
97
- private function updateHTAccess($file, $contents)
98
- {
99
- return (
100
- (!$contents || $this->getHtaccessRules() !== $contents) &&
101
- false === @file_put_contents($file, $this->getHtaccessRules())
102
- );
103
- }
104
-
105
- /**
106
- * Save HTAccess file
107
- */
108
- private function saveHTAccess()
109
- {
110
- $uploadDir = $this->getUploadDirectory();
111
- $htAccessFile = $uploadDir . ".htaccess";
112
-
113
- // .htaccess exists
114
- if (file_exists($htAccessFile))
115
- {
116
- $contents = @file_get_contents($htAccessFile);
117
-
118
- // Rules doesn't match, update .htaccess rules
119
- if (false === $this->updateHTAccess($htAccessFile, $contents))
120
- {
121
- $this->log("Failed to update {$htAccessFile}");
122
- }
123
- }
124
- // .htaccess doesn't exists and
125
- else if (wp_is_writable($uploadDir) && false === @file_put_contents($htAccessFile, $this->getHtaccessRules()))
126
- {
127
- $this->log("Failed to create {$htAccessFile}");
128
- }
129
- }
130
-
131
- /**
132
- * Save blank index file
133
- * @return bool
134
- */
135
- private function saveBlankIndex()
136
- {
137
- $uploadDir = $this->getUploadDirectory();
138
- $indexFile = $uploadDir . "index.php";
139
-
140
- if (file_exists($indexFile))
141
- {
142
- return true;
143
- }
144
-
145
- $contents = "<?php" . PHP_EOL . "// WP-Staging protection file";
146
-
147
- if (!wp_is_writable($uploadDir) || false === @file_put_contents($indexFile, $contents))
148
- {
149
- $this->log("{$uploadDir} is not writable or couldn't generate {$indexFile}");
150
- return false;
151
- }
152
-
153
- return true;
154
- }
155
-
156
- /**
157
- * Prepare protect directories and files
158
- * @param bool $force
159
- */
160
- // protected function protectDirectoriesAndFiles($force = false)
161
- // {
162
- // // Don't execute
163
- // if (true !== get_transient("wpstg_check_protection_files") && false === $force)
164
- // {
165
- // return;
166
- // }
167
- //
168
- // // Save .htaccess file
169
- // $this->saveHTAccess();
170
- //
171
- // // Save blank index.php file
172
- // $this->saveBlankIndex();
173
- //
174
- // // TODO put blank index to upload directories?? Why??
175
- //
176
- // // Check files once a day
177
- // set_transient("wpstg_check_protection_files", true, DAY_IN_SECONDS); // 24 hours in seconds
178
- //
179
- //
180
- // }
181
-
182
- /**
183
- * Prepare clone records
184
- * @return bool
185
- */
186
- protected function prepareCloneDataRecords()
187
- {
188
- // Check if clones still exist
189
- $this->log("Verifying existing clones...");
190
-
191
-
192
- // Clone data already exists
193
- if (isset($this->options->existingClones[$this->options->clone]))
194
- {
195
- $this->log("Clone data already exists, no need to update, the job finished");
196
- return true;
197
- }
198
-
199
- // Save new clone data
200
- $this->log("{$this->options->clone}'s clone job's data is not in database, generating data");
201
-
202
-
203
-
204
- $this->options->existingClones[$this->options->clone] = array(
205
- "directoryName" => $this->options->cloneDirectoryName,
206
- "path" => ABSPATH . $this->options->cloneDirectoryName,
207
- "url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
208
- "number" => $this->options->cloneNumber,
209
- "version" => \WPStaging\WPStaging::VERSION,
210
- "status" => false
211
- );
212
-
213
- if (false === update_option("wpstg_existing_clones_beta", $this->options->existingClones))
214
- {
215
- $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
216
- return false;
217
- }
218
-
219
- // Save scanned directories for a delete job
220
- $this->saveScannedDirectories();
221
-
222
- return true;
223
- }
224
-
225
- /**
226
- * Save Scanned Directories for Delete Job Later
227
- */
228
- protected function saveScannedDirectories()
229
- {
230
- // Save scanned directories for delete job
231
- $this->cache->save("delete_directories_" . $this->options->clone, $this->options->scannedDirectories);
232
-
233
- $this->log("Successfully saved {$this->options->clone}'s clone job data to database'");
234
- $this->log("Cloning job has finished!");
235
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ use WPStaging\WPStaging;
5
+
6
+ //error_reporting( E_ALL );
7
+
8
+ /**
9
+ * Class Finish
10
+ * @package WPStaging\Backend\Modules\Jobs
11
+ */
12
+ class Finish extends Job
13
+ {
14
+ /**
15
+ * Clone Key
16
+ * @var string
17
+ */
18
+ private $clone = '';
19
+
20
+ /**
21
+ * Start Module
22
+ * @return object
23
+ */
24
+ public function start()
25
+ {
26
+ // sanitize the clone name before saving
27
+ $this->clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
28
+
29
+ // Delete Cache Files
30
+ $this->deleteCacheFiles();
31
+
32
+ // Prepare clone records & save scanned directories for delete job later
33
+ $this->prepareCloneDataRecords();
34
+
35
+
36
+ $return = array(
37
+ "directoryName" => $this->options->cloneDirectoryName,
38
+ "path" => ABSPATH . $this->options->cloneDirectoryName,
39
+ "url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
40
+ "number" => $this->options->cloneNumber,
41
+ "version" => \WPStaging\WPStaging::VERSION,
42
+ "status" => false,
43
+ "prefix" => $this->options->prefix,
44
+ "last_msg" => $this->logger->getLastLogMsg(),
45
+ "job" => $this->options->currentJob
46
+ );
47
+
48
+
49
+ //return (object) $this->options->existingClones[$this->options->clone];
50
+ //return (object) $this->options->existingClones[$this->clone];
51
+ return (object) $return;
52
+ }
53
+
54
+ /**
55
+ * Delete Cache Files
56
+ */
57
+ protected function deleteCacheFiles()
58
+ {
59
+ $this->log("Finish: Deleting clone job's cache files...");
60
+
61
+ // Clean cache files
62
+ $this->cache->delete("clone_options");
63
+ $this->cache->delete("files_to_copy");
64
+
65
+ $this->log("Finish: Clone job's cache files have been deleted!");
66
+ }
67
+
68
+ /**
69
+ * Prepare clone records
70
+ * @return bool
71
+ */
72
+ protected function prepareCloneDataRecords()
73
+ {
74
+ // Check if clones still exist
75
+ $this->log("Finish: Verifying existing clones...");
76
+
77
+ // Clone data already exists
78
+ if (isset($this->options->existingClones[$this->options->clone]))
79
+ {
80
+ $this->log("Finish: Clone data already exists, no need to update, the job finished");
81
+ return true;
82
+ }
83
+
84
+ // Save new clone data
85
+ $this->log("Finish: {$this->options->clone}'s clone job's data is not in database, generating data");
86
+
87
+ // sanitize the clone name before saving
88
+ //$clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
89
+
90
+ $this->options->existingClones[$this->clone] = array(
91
+ "directoryName" => $this->options->cloneDirectoryName,
92
+ "path" => ABSPATH . $this->options->cloneDirectoryName,
93
+ "url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
94
+ "number" => $this->options->cloneNumber,
95
+ "version" => \WPStaging\WPStaging::VERSION,
96
+ "status" => false,
97
+ "prefix" => $this->options->prefix,
98
+ );
99
+
100
+ if (false === update_option("wpstg_existing_clones_beta", $this->options->existingClones))
101
+ {
102
+ $this->log("Finish: Failed to save {$this->options->clone}'s clone job data to database'");
103
+ return false;
104
+ }
105
+
106
+ // Save scanned directories for a delete job
107
+ //$this->saveScannedDirectories();
108
+
109
+ return true;
110
+ }
111
+
112
+ /**
113
+ * Save Scanned Directories for Delete Job Later
114
+ */
115
+ // protected function saveScannedDirectories()
116
+ // {
117
+ // // Save scanned directories for delete job
118
+ // $this->cache->save("delete_directories_" . $this->options->clone, $this->options->scannedDirectories);
119
+ //
120
+ // $this->log("Successfully saved {$this->options->clone}'s clone job data to database'");
121
+ // $this->log("Cloning job has finished!");
122
+ // }
123
+
124
+ /**
125
+ * Get Upload Directory
126
+ * @return string
127
+ */
128
+ // private function getUploadDirectory()
129
+ // {
130
+ // $wpUploadDirectory = wp_get_upload_dir();
131
+ // $uploadDirectory = $wpUploadDirectory["basedir"] . DIRECTORY_SEPARATOR . WPStaging::SLUG;
132
+ //
133
+ // // Failed to create upload directory
134
+ // if (!is_dir($uploadDirectory) && !wp_mkdir_p($uploadDirectory))
135
+ // {
136
+ // $this->log("Upload directory ({$uploadDirectory}) doesn't exist and failed to create!");
137
+ // }
138
+ //
139
+ // $uploadDirectory = apply_filters("wpstg_get_upload_dir", $uploadDirectory);
140
+ //
141
+ // return rtrim($uploadDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
142
+ // }
143
+
144
+
145
+
146
+ /**
147
+ * Get .htaccess rules
148
+ * @return string
149
+ */
150
+ // private function getHtaccessRules()
151
+ // {
152
+ // // Prevent directory browsing and direct access to all files
153
+ // $rules = "<Files \"*\">\n";
154
+ // $rules .= "<IfModule mod_access.c>\n";
155
+ // $rules .= "Deny from all\n";
156
+ // $rules .= "</IfModule>\n";
157
+ // $rules .= "<IfModule !mod_access_compat>\n";
158
+ // $rules .= "<IfModule mod_authz_host.c>\n";
159
+ // $rules .= "Deny from all\n";
160
+ // $rules .= "</IfModule>\n";
161
+ // $rules .= "</IfModule>\n";
162
+ // $rules .= "<IfModule mod_access_compat>\n";
163
+ // $rules .= "Deny from all\n";
164
+ // $rules .= "</IfModule>\n";
165
+ // $rules .= "</Files>\n";
166
+ //
167
+ // return apply_filters("wpstg_protected_directory_htaccess_rules", $rules);
168
+ // }
169
+
170
+ /**
171
+ * Update .htaccess file and its rules
172
+ * @param string $file
173
+ * @param string $contents
174
+ * @return bool
175
+ */
176
+ // private function updateHTAccess($file, $contents)
177
+ // {
178
+ // return (
179
+ // (!$contents || $this->getHtaccessRules() !== $contents) &&
180
+ // false === @file_put_contents($file, $this->getHtaccessRules())
181
+ // );
182
+ // }
183
+
184
+ /**
185
+ * Save HTAccess file
186
+ */
187
+ // private function saveHTAccess()
188
+ // {
189
+ // $uploadDir = $this->getUploadDirectory();
190
+ // $htAccessFile = $uploadDir . ".htaccess";
191
+ //
192
+ // // .htaccess exists
193
+ // if (file_exists($htAccessFile))
194
+ // {
195
+ // $contents = @file_get_contents($htAccessFile);
196
+ //
197
+ // // Rules doesn't match, update .htaccess rules
198
+ // if (false === $this->updateHTAccess($htAccessFile, $contents))
199
+ // {
200
+ // $this->log("Failed to update {$htAccessFile}");
201
+ // }
202
+ // }
203
+ // // .htaccess doesn't exists and
204
+ // else if (wp_is_writable($uploadDir) && false === @file_put_contents($htAccessFile, $this->getHtaccessRules()))
205
+ // {
206
+ // $this->log("Failed to create {$htAccessFile}");
207
+ // }
208
+ // }
209
+
210
+ /**
211
+ * Save blank index file
212
+ * @return bool
213
+ */
214
+ // private function saveBlankIndex()
215
+ // {
216
+ // $uploadDir = $this->getUploadDirectory();
217
+ // $indexFile = $uploadDir . "index.php";
218
+ //
219
+ // if (file_exists($indexFile))
220
+ // {
221
+ // return true;
222
+ // }
223
+ //
224
+ // $contents = "<?php" . PHP_EOL . "// WP-Staging protection file";
225
+ //
226
+ // if (!wp_is_writable($uploadDir) || false === @file_put_contents($indexFile, $contents))
227
+ // {
228
+ // $this->log("{$uploadDir} is not writable or couldn't generate {$indexFile}");
229
+ // return false;
230
+ // }
231
+ //
232
+ // return true;
233
+ // }
234
+
235
+ /**
236
+ * Prepare protect directories and files
237
+ * @param bool $force
238
+ */
239
+ // protected function protectDirectoriesAndFiles($force = false)
240
+ // {
241
+ // // Don't execute
242
+ // if (true !== get_transient("wpstg_check_protection_files") && false === $force)
243
+ // {
244
+ // return;
245
+ // }
246
+ //
247
+ // // Save .htaccess file
248
+ // $this->saveHTAccess();
249
+ //
250
+ // // Save blank index.php file
251
+ // $this->saveBlankIndex();
252
+ //
253
+ // // TODO put blank index to upload directories?? Why??
254
+ //
255
+ // // Check files once a day
256
+ // set_transient("wpstg_check_protection_files", true, DAY_IN_SECONDS); // 24 hours in seconds
257
+ //
258
+ //
259
+ // }
260
+
261
+
262
  }
apps/Backend/Modules/Jobs/Job.php CHANGED
@@ -1,473 +1,474 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
- use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
11
- use WPStaging\Utils\Logger;
12
- use WPStaging\WPStaging;
13
- use WPStaging\Utils\Cache;
14
-
15
- /**
16
- * Class Job
17
- * @package WPStaging\Backend\Modules\Jobs
18
- */
19
- abstract class Job implements JobInterface
20
- {
21
-
22
- const EXECUTION_TIME_RATIO = 0.8;
23
-
24
- const MAX_MEMORY_RATIO = 0.8;
25
-
26
- /**
27
- * @var Cache
28
- */
29
- protected $cache;
30
-
31
- /**
32
- * @var Logger
33
- */
34
- protected $logger;
35
-
36
- /**
37
- * @var bool
38
- */
39
- protected $hasLoggedFileNameSet = false;
40
-
41
- /**
42
- * @var object
43
- */
44
- protected $options;
45
-
46
- /**
47
- * @var object
48
- */
49
- protected $settings;
50
-
51
- /**
52
- * System total maximum memory consumption
53
- * @var int
54
- */
55
- protected $maxMemoryLimit;
56
-
57
- /**
58
- * Script maximum memory consumption
59
- * @var int
60
- */
61
- protected $memoryLimit;
62
-
63
- /**
64
- * @var int
65
- */
66
- protected $maxExecutionTime;
67
-
68
-
69
- /**
70
- * @var int
71
- */
72
- protected $executionLimit;
73
-
74
- /**
75
- * @var int
76
- */
77
- protected $totalRecursion;
78
-
79
- /**
80
- * @var int
81
- */
82
- protected $maxRecursionLimit;
83
-
84
- /**
85
- * @var int
86
- */
87
- protected $start;
88
-
89
- /**
90
- * Job constructor.
91
- */
92
- public function __construct()
93
- {
94
- // Get max limits
95
- $this->start = $this->time();
96
- $this->maxMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
97
- $this->maxExecutionTime = (int) ini_get("max_execution_time");
98
- //$this->maxExecutionTime = (int) 30;
99
-
100
- if ($this->maxExecutionTime > 30)
101
- {
102
- $this->maxExecutionTime = 30;
103
- }
104
-
105
- if ($this->maxExecutionTime < 1)
106
- {
107
- $this->maxExecutionTime = 30;
108
- }
109
-
110
-
111
- // Services
112
- $this->cache = new Cache(-1, \WPStaging\WPStaging::getContentDir());
113
- $this->logger = WPStaging::getInstance()->get("logger");
114
-
115
- // Settings and Options
116
- $this->options = $this->cache->get("clone_options");
117
- //$this->settings = json_decode(json_encode(get_option("wpstg_settings", array())));
118
- $this->settings = (object)get_option("wpstg_settings", array());
119
-
120
-
121
-
122
- // check default options
123
- if (!$this->settings)
124
- {
125
- $this->options = new \stdClass();
126
- }
127
-
128
- if (isset($this->options->existingClones) && is_object($this->options->existingClones))
129
- {
130
- $this->options->existingClones = json_decode(json_encode($this->options->existingClones), true);
131
- }
132
-
133
- if (!isset($this->settings) || !isset($this->settings->queryLimit) || !isset($this->settings->batchSize) || !isset($this->settings->cpuLoad))
134
- {
135
- $this->settings = new \stdClass();
136
- $this->setDefaultSettings();
137
- }
138
-
139
- // Set limits accordingly to CPU LIMITS
140
- $this->setLimits();
141
-
142
- $this->maxRecursionLimit = (int) ini_get("xdebug.max_nesting_level");
143
-
144
- /*
145
- * This is needed to make sure that maxRecursionLimit = -1
146
- * if xdebug is not used in production env.
147
- * For using xdebug, maxRecursionLimit must be larger
148
- * otherwise xdebug is throwing an error 500 while debugging
149
- */
150
- if ($this->maxRecursionLimit < 1)
151
- {
152
- $this->maxRecursionLimit = -1;
153
- }
154
- else
155
- {
156
- $this->maxRecursionLimit = $this->maxRecursionLimit - 50; // just to make sure
157
- }
158
-
159
- if (method_exists($this, "initialize"))
160
- {
161
- $this->initialize();
162
- }
163
- }
164
-
165
- /**
166
- * Job destructor
167
- */
168
- public function __destruct()
169
- {
170
- // Commit logs
171
- $this->logger->commit();
172
- }
173
-
174
- /**
175
- * Set default settings
176
- */
177
- protected function setDefaultSettings(){
178
- $this->settings->queryLimit = "1000";
179
- $this->settings->batchSize = "2";
180
- $this->settings->cpuLoad = 'medium';
181
- update_option('wpstg_settings', $this->settings);
182
- }
183
-
184
- /**
185
- * Set limits accordingly to
186
- */
187
- protected function setLimits()
188
- {
189
-
190
- if (!isset($this->settings->cpuLoad))
191
- {
192
- $this->settings->cpuLoad = "medium";
193
- }
194
-
195
- $memoryLimit= self::MAX_MEMORY_RATIO;
196
- $timeLimit = self::EXECUTION_TIME_RATIO;
197
-
198
- switch($this->settings->cpuLoad)
199
- {
200
- case "medium":
201
- //$memoryLimit= $memoryLimit / 2; // 0.4
202
- $timeLimit = $timeLimit / 2;
203
- break;
204
- case "low":
205
- //$memoryLimit= $memoryLimit / 4; // 0.2
206
- $timeLimit = $timeLimit / 4;
207
- break;
208
-
209
- case "fast": // 0.8
210
- default:
211
- break;
212
- }
213
-
214
- $this->memoryLimit = $this->maxMemoryLimit * $memoryLimit;
215
- $this->executionLimit = $this->maxExecutionTime * $timeLimit;
216
- }
217
-
218
- /**
219
- * Save options
220
- * @param null|array|object $options
221
- * @return bool
222
- */
223
- protected function saveOptions($options = null)
224
- {
225
- // Get default options
226
- if (null === $options)
227
- {
228
- $options = $this->options;
229
- }
230
-
231
- // Ensure that it is an object
232
- $options = json_decode(json_encode($options));
233
-
234
- return $this->cache->save("clone_options", $options);
235
- }
236
-
237
- /**
238
- * @return object
239
- */
240
- public function getOptions()
241
- {
242
- return $this->options;
243
- }
244
-
245
- /**
246
- * @param string $memory
247
- * @return int
248
- */
249
- protected function getMemoryInBytes($memory)
250
- {
251
- // Handle unlimited ones
252
- if (1 > (int) $memory)
253
- {
254
- return (int) $memory;
255
- }
256
-
257
- $bytes = (int) $memory; // grab only the number
258
- $size = trim(str_replace($bytes, null, strtolower($memory))); // strip away number and lower-case it
259
-
260
- // Actual calculation
261
- switch($size)
262
- {
263
- case 'k':
264
- $bytes *= 1024;
265
- break;
266
- case 'm':
267
- $bytes *= (1024 * 1024);
268
- break;
269
- case 'g':
270
- $bytes *= (1024 * 1024 * 1024);
271
- break;
272
- }
273
-
274
- return $bytes;
275
- }
276
-
277
- /**
278
- * Format bytes into ini_set favorable form
279
- * @param int $bytes
280
- * @return string
281
- */
282
- protected function formatBytes($bytes)
283
- {
284
- if ((int) $bytes < 1)
285
- {
286
- return '';
287
- }
288
-
289
- $units = array('B', 'K', 'M', 'G'); // G since PHP 5.1.x so we are good!
290
-
291
- $bytes = (int) $bytes;
292
- $base = log($bytes) / log(1000);
293
- $pow = pow(1000, $base - floor($base));
294
-
295
- return round($pow, 0) . $units[(int) floor($base)];
296
- }
297
-
298
- /**
299
- * Get current time in seconds
300
- * @return float
301
- */
302
- protected function time()
303
- {
304
- $time = microtime();
305
- $time = explode(' ', $time);
306
- $time = $time[1] + $time[0];
307
- return $time;
308
- }
309
-
310
- /**
311
- * @return bool
312
- */
313
- protected function isOverThreshold()
314
- {
315
- // Check if the memory is over threshold
316
- $usedMemory = (int) @memory_get_usage(true);
317
-
318
- $this->debugLog('Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Max Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script Memory Limit: ' . $this->formatBytes( $this->memoryLimit), Logger::TYPE_DEBUG );
319
-
320
- if ($usedMemory >= $this->memoryLimit)
321
- {
322
- $this->log('Used Memory: ' . $this->formatBytes($usedMemory) . ' Memory Limit: ' . $this->formatBytes($this->maxMemoryLimit) . ' Max Script memory limit: ' . $this->formatBytes( $this->memoryLimit ) );
323
- $this->resetMemory();
324
- return true;
325
- }
326
-
327
- if ($this->isRecursionLimit())
328
- {
329
- //$this->log('RESET RECURSION');
330
- return true;
331
- }
332
-
333
- // Check if execution time is over threshold
334
- ///$time = round($this->start + $this->time(), 4);
335
- $time = round($this->time() - $this->start, 4);
336
- $this->debugLog( 'Execution time: ' . $time . ' Execution Limit' . $this->executionLimit );
337
- if ($time >= $this->executionLimit)
338
- {
339
- //$this->log('RESET TIME');
340
- return true;
341
- }
342
-
343
- return false;
344
- }
345
-
346
- /**
347
- * Attempt to reset memory
348
- * @return bool
349
- *
350
- */
351
- protected function resetMemory()
352
- {
353
- $newMemoryLimit = $this->maxMemoryLimit * 2;
354
-
355
- // Failed to set
356
- if (false === ini_set("memory_limit", $this->formatBytes($newMemoryLimit)))
357
- {
358
- $this->log('Can not free some memory', Logger::TYPE_CRITICAL);
359
- return false;
360
- }
361
-
362
- // Double checking
363
- $newMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
364
- if ($newMemoryLimit <= $this->maxMemoryLimit)
365
- {
366
- return false;
367
- }
368
-
369
- // Set the new Maximum memory limit
370
- $this->maxMemoryLimit = $newMemoryLimit;
371
-
372
- // Calculate threshold limit
373
- $this->memoryLimit = $newMemoryLimit * self::MAX_MEMORY_RATIO;
374
-
375
- return true;
376
- }
377
-
378
- /**
379
- * Attempt to reset time
380
- * @return bool
381
- *
382
- * @deprecated since version 2.0.0
383
-
384
- */
385
- protected function resetTime()
386
- {
387
- // Attempt to reset timeout
388
- if (!@set_time_limit($this->maxExecutionTime))
389
- {
390
- return false;
391
- }
392
-
393
- // Increase execution limit
394
- $this->executionLimit = $this->executionLimit * 2;
395
-
396
- return true;
397
- }
398
-
399
- /**
400
- * Reset time limit and memory
401
- * @return bool
402
- *
403
- * @deprecated since version 2.0.0
404
- */
405
- protected function reset()
406
- {
407
- // Attempt to reset time
408
- if (!$this->resetTime())
409
- {
410
- return false;
411
- }
412
-
413
- // Attempt to reset memory
414
- if (!$this->resetMemory())
415
- {
416
- return false;
417
- }
418
-
419
- return true;
420
- }
421
-
422
- /**
423
- * Checks if calls are over recursion limit
424
- * @return bool
425
- */
426
- protected function isRecursionLimit()
427
- {
428
- return ($this->maxRecursionLimit > 0 && $this->totalRecursion >= $this->maxRecursionLimit);
429
- }
430
-
431
- /**
432
- * @param string $msg
433
- * @param string $type
434
- */
435
- protected function log($msg, $type = Logger::TYPE_INFO)
436
- {
437
-
438
- if (!isset($this->options->clone)){
439
- $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
440
- }
441
-
442
- if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
443
- {
444
- $this->logger->setFileName($this->options->clone);
445
- $this->hasLoggedFileNameSet = true;
446
- }
447
-
448
- $this->logger->add($msg, $type);
449
- }
450
- /**
451
- * @param string $msg
452
- * @param string $type
453
- */
454
- protected function debugLog($msg, $type = Logger::TYPE_INFO)
455
- {
456
-
457
- if (!isset($this->options->clone)){
458
- $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
459
- }
460
-
461
- if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
462
- {
463
- $this->logger->setFileName($this->options->clone);
464
- $this->hasLoggedFileNameSet = true;
465
- }
466
-
467
-
468
- if (isset($this->settings->debugMode)){
469
- $this->logger->add($msg, $type);
470
- }
471
-
472
- }
 
473
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ // No Direct Access
5
+ if (!defined("WPINC"))
6
+ {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
11
+ use WPStaging\Utils\Logger;
12
+ use WPStaging\WPStaging;
13
+ use WPStaging\Utils\Cache;
14
+
15
+ /**
16
+ * Class Job
17
+ * @package WPStaging\Backend\Modules\Jobs
18
+ */
19
+ abstract class Job implements JobInterface
20
+ {
21
+
22
+ const EXECUTION_TIME_RATIO = 0.8;
23
+
24
+ const MAX_MEMORY_RATIO = 0.8;
25
+
26
+ /**
27
+ * @var Cache
28
+ */
29
+ protected $cache;
30
+
31
+ /**
32
+ * @var Logger
33
+ */
34
+ protected $logger;
35
+
36
+ /**
37
+ * @var bool
38
+ */
39
+ protected $hasLoggedFileNameSet = false;
40
+
41
+ /**
42
+ * @var object
43
+ */
44
+ protected $options;
45
+
46
+ /**
47
+ * @var object
48
+ */
49
+ protected $settings;
50
+
51
+ /**
52
+ * System total maximum memory consumption
53
+ * @var int
54
+ */
55
+ protected $maxMemoryLimit;
56
+
57
+ /**
58
+ * Script maximum memory consumption
59
+ * @var int
60
+ */
61
+ protected $memoryLimit;
62
+
63
+ /**
64
+ * @var int
65
+ */
66
+ protected $maxExecutionTime;
67
+
68
+
69
+ /**
70
+ * @var int
71
+ */
72
+ protected $executionLimit;
73
+
74
+ /**
75
+ * @var int
76
+ */
77
+ protected $totalRecursion;
78
+
79
+ /**
80
+ * @var int
81
+ */
82
+ protected $maxRecursionLimit;
83
+
84
+ /**
85
+ * @var int
86
+ */
87
+ protected $start;
88
+
89
+ /**
90
+ * Job constructor.
91
+ */
92
+ public function __construct()
93
+ {
94
+ // Get max limits
95
+ $this->start = $this->time();
96
+ $this->maxMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
97
+ //$this->maxExecutionTime = (int) ini_get("max_execution_time");
98
+ $this->maxExecutionTime = (int) 30;
99
+
100
+ // if ($this->maxExecutionTime > 30)
101
+ // {
102
+ // $this->maxExecutionTime = 30;
103
+ // }
104
+ //
105
+ // if ($this->maxExecutionTime < 1)
106
+ // {
107
+ // $this->maxExecutionTime = 30;
108
+ // }
109
+
110
+
111
+ // Services
112
+ $this->cache = new Cache(-1, \WPStaging\WPStaging::getContentDir());
113
+ $this->logger = WPStaging::getInstance()->get("logger");
114
+
115
+ // Settings and Options
116
+ $this->options = $this->cache->get("clone_options");
117
+ //$this->settings = json_decode(json_encode(get_option("wpstg_settings", array())));
118
+ $this->settings = (object)get_option("wpstg_settings", array());
119
+
120
+
121
+
122
+ // check default options
123
+ if (!$this->settings)
124
+ {
125
+ $this->options = new \stdClass();
126
+ }
127
+
128
+ if (isset($this->options->existingClones) && is_object($this->options->existingClones))
129
+ {
130
+ $this->options->existingClones = json_decode(json_encode($this->options->existingClones), true);
131
+ }
132
+
133
+ if (!isset($this->settings) || !isset($this->settings->queryLimit) || !isset($this->settings->batchSize) || !isset($this->settings->cpuLoad))
134
+ {
135
+ $this->settings = new \stdClass();
136
+ $this->setDefaultSettings();
137
+ }
138
+
139
+ // Set limits accordingly to CPU LIMITS
140
+ $this->setLimits();
141
+
142
+ $this->maxRecursionLimit = (int) ini_get("xdebug.max_nesting_level");
143
+
144
+ /*
145
+ * This is needed to make sure that maxRecursionLimit = -1
146
+ * if xdebug is not used in production env.
147
+ * For using xdebug, maxRecursionLimit must be larger
148
+ * otherwise xdebug is throwing an error 500 while debugging
149
+ */
150
+ if ($this->maxRecursionLimit < 1)
151
+ {
152
+ $this->maxRecursionLimit = -1;
153
+ }
154
+ else
155
+ {
156
+ $this->maxRecursionLimit = $this->maxRecursionLimit - 50; // just to make sure
157
+ }
158
+
159
+ if (method_exists($this, "initialize"))
160
+ {
161
+ $this->initialize();
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Job destructor
167
+ */
168
+ public function __destruct()
169
+ {
170
+ // Commit logs
171
+ $this->logger->commit();
172
+ }
173
+
174
+ /**
175
+ * Set default settings
176
+ */
177
+ protected function setDefaultSettings(){
178
+ $this->settings->queryLimit = "1000";
179
+ $this->settings->fileCopyLimit = "10";
180
+ $this->settings->batchSize = "2";
181
+ $this->settings->cpuLoad = 'medium';
182
+ update_option('wpstg_settings', $this->settings);
183
+ }
184
+
185
+ /**
186
+ * Set limits accordingly to
187
+ */
188
+ protected function setLimits()
189
+ {
190
+
191
+ if (!isset($this->settings->cpuLoad))
192
+ {
193
+ $this->settings->cpuLoad = "medium";
194
+ }
195
+
196
+ $memoryLimit= self::MAX_MEMORY_RATIO;
197
+ $timeLimit = self::EXECUTION_TIME_RATIO;
198
+
199
+ switch($this->settings->cpuLoad)
200
+ {
201
+ case "medium":
202
+ //$memoryLimit= $memoryLimit / 2; // 0.4
203
+ $timeLimit = $timeLimit / 2;
204
+ break;
205
+ case "low":
206
+ //$memoryLimit= $memoryLimit / 4; // 0.2
207
+ $timeLimit = $timeLimit / 4;
208
+ break;
209
+
210
+ case "fast": // 0.8
211
+ default:
212
+ break;
213
+ }
214
+
215
+ $this->memoryLimit = $this->maxMemoryLimit * $memoryLimit;
216
+ $this->executionLimit = $this->maxExecutionTime * $timeLimit;
217
+ }
218
+
219
+ /**
220
+ * Save options
221
+ * @param null|array|object $options
222
+ * @return bool
223
+ */
224
+ protected function saveOptions($options = null)
225
+ {
226
+ // Get default options
227
+ if (null === $options)
228
+ {
229
+ $options = $this->options;
230
+ }
231
+
232
+ // Ensure that it is an object
233
+ $options = json_decode(json_encode($options));
234
+
235
+ return $this->cache->save("clone_options", $options);
236
+ }
237
+
238
+ /**
239
+ * @return object
240
+ */
241
+ public function getOptions()
242
+ {
243
+ return $this->options;
244
+ }
245
+
246
+ /**
247
+ * @param string $memory
248
+ * @return int
249
+ */
250
+ protected function getMemoryInBytes($memory)
251
+ {
252
+ // Handle unlimited ones
253
+ if (1 > (int) $memory)
254
+ {
255
+ return (int) $memory;
256
+ }
257
+
258
+ $bytes = (int) $memory; // grab only the number
259
+ $size = trim(str_replace($bytes, null, strtolower($memory))); // strip away number and lower-case it
260
+
261
+ // Actual calculation
262
+ switch($size)
263
+ {
264
+ case 'k':
265
+ $bytes *= 1024;
266
+ break;
267
+ case 'm':
268
+ $bytes *= (1024 * 1024);
269
+ break;
270
+ case 'g':
271
+ $bytes *= (1024 * 1024 * 1024);
272
+ break;
273
+ }
274
+
275
+ return $bytes;
276
+ }
277
+
278
+ /**
279
+ * Format bytes into ini_set favorable form
280
+ * @param int $bytes
281
+ * @return string
282
+ */
283
+ protected function formatBytes($bytes)
284
+ {
285
+ if ((int) $bytes < 1)
286
+ {
287
+ return '';
288
+ }
289
+
290
+ $units = array('B', 'K', 'M', 'G'); // G since PHP 5.1.x so we are good!
291
+
292
+ $bytes = (int) $bytes;
293
+ $base = log($bytes) / log(1000);
294
+ $pow = pow(1000, $base - floor($base));
295
+
296
+ return round($pow, 0) . $units[(int) floor($base)];
297
+ }
298
+
299
+ /**
300
+ * Get current time in seconds
301
+ * @return float
302
+ */
303
+ protected function time()
304
+ {
305
+ $time = microtime();
306
+ $time = explode(' ', $time);
307
+ $time = $time[1] + $time[0];
308
+ return $time;
309
+ }
310
+
311
+ /**
312
+ * @return bool
313
+ */
314
+ protected function isOverThreshold()
315
+ {
316
+ // Check if the memory is over threshold
317
+ $usedMemory = (int) @memory_get_usage(true);
318
+
319
+ $this->debugLog('Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Max Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script Memory Limit: ' . $this->formatBytes( $this->memoryLimit), Logger::TYPE_DEBUG );
320
+
321
+ if ($usedMemory >= $this->memoryLimit)
322
+ {
323
+ $this->log('Used Memory: ' . $this->formatBytes($usedMemory) . ' Memory Limit: ' . $this->formatBytes($this->maxMemoryLimit) . ' Max Script memory limit: ' . $this->formatBytes( $this->memoryLimit ) );
324
+ $this->resetMemory();
325
+ return true;
326
+ }
327
+
328
+ if ($this->isRecursionLimit())
329
+ {
330
+ //$this->log('RESET RECURSION');
331
+ return true;
332
+ }
333
+
334
+ // Check if execution time is over threshold
335
+ ///$time = round($this->start + $this->time(), 4);
336
+ $time = round($this->time() - $this->start, 4);
337
+ $this->debugLog( 'Execution time: ' . $time . ' Execution Limit' . $this->executionLimit );
338
+ if ($time >= $this->executionLimit)
339
+ {
340
+ //$this->log('RESET TIME');
341
+ return true;
342
+ }
343
+
344
+ return false;
345
+ }
346
+
347
+ /**
348
+ * Attempt to reset memory
349
+ * @return bool
350
+ *
351
+ */
352
+ protected function resetMemory()
353
+ {
354
+ $newMemoryLimit = $this->maxMemoryLimit * 2;
355
+
356
+ // Failed to set
357
+ if (false === ini_set("memory_limit", $this->formatBytes($newMemoryLimit)))
358
+ {
359
+ $this->log('Can not free some memory', Logger::TYPE_CRITICAL);
360
+ return false;
361
+ }
362
+
363
+ // Double checking
364
+ $newMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
365
+ if ($newMemoryLimit <= $this->maxMemoryLimit)
366
+ {
367
+ return false;
368
+ }
369
+
370
+ // Set the new Maximum memory limit
371
+ $this->maxMemoryLimit = $newMemoryLimit;
372
+
373
+ // Calculate threshold limit
374
+ $this->memoryLimit = $newMemoryLimit * self::MAX_MEMORY_RATIO;
375
+
376
+ return true;
377
+ }
378
+
379
+ /**
380
+ * Attempt to reset time
381
+ * @return bool
382
+ *
383
+ * @deprecated since version 2.0.0
384
+
385
+ */
386
+ protected function resetTime()
387
+ {
388
+ // Attempt to reset timeout
389
+ if (!@set_time_limit($this->maxExecutionTime))
390
+ {
391
+ return false;
392
+ }
393
+
394
+ // Increase execution limit
395
+ $this->executionLimit = $this->executionLimit * 2;
396
+
397
+ return true;
398
+ }
399
+
400
+ /**
401
+ * Reset time limit and memory
402
+ * @return bool
403
+ *
404
+ * @deprecated since version 2.0.0
405
+ */
406
+ protected function reset()
407
+ {
408
+ // Attempt to reset time
409
+ if (!$this->resetTime())
410
+ {
411
+ return false;
412
+ }
413
+
414
+ // Attempt to reset memory
415
+ if (!$this->resetMemory())
416
+ {
417
+ return false;
418
+ }
419
+
420
+ return true;
421
+ }
422
+
423
+ /**
424
+ * Checks if calls are over recursion limit
425
+ * @return bool
426
+ */
427
+ protected function isRecursionLimit()
428
+ {
429
+ return ($this->maxRecursionLimit > 0 && $this->totalRecursion >= $this->maxRecursionLimit);
430
+ }
431
+
432
+ /**
433
+ * @param string $msg
434
+ * @param string $type
435
+ */
436
+ protected function log($msg, $type = Logger::TYPE_INFO)
437
+ {
438
+
439
+ if (!isset($this->options->clone)){
440
+ $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
441
+ }
442
+
443
+ if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
444
+ {
445
+ $this->logger->setFileName($this->options->clone);
446
+ $this->hasLoggedFileNameSet = true;
447
+ }
448
+
449
+ $this->logger->add($msg, $type);
450
+ }
451
+ /**
452
+ * @param string $msg
453
+ * @param string $type
454
+ */
455
+ protected function debugLog($msg, $type = Logger::TYPE_INFO)
456
+ {
457
+
458
+ if (!isset($this->options->clone)){
459
+ $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
460
+ }
461
+
462
+ if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
463
+ {
464
+ $this->logger->setFileName($this->options->clone);
465
+ $this->hasLoggedFileNameSet = true;
466
+ }
467
+
468
+
469
+ if (isset($this->settings->debugMode)){
470
+ $this->logger->add($msg, $type);
471
+ }
472
+
473
+ }
474
  }
apps/Backend/Modules/SystemInfo.php CHANGED
@@ -143,6 +143,7 @@ class SystemInfo extends InjectionAware
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' );
143
 
144
  $output = "-- WP Staging Settings" . PHP_EOL . PHP_EOL;
145
  $output .= $this->info( "Query Limit:", isset( $settings->queryLimit ) ? $settings->queryLimit : 'undefined' );
146
+ $output .= $this->info( "File Copy Limit:", isset( $settings->fileLimit ) ? $settings->fileLimit : 'undefined' );
147
  $output .= $this->info( "Batch Size:", isset( $settings->batchSize ) ? $settings->batchSize : 'undefined' );
148
  $output .= $this->info( "CPU Load:", isset( $settings->cpuLoad ) ? $settings->cpuLoad : 'undefined' );
149
  $output .= $this->info( "WP in Subdir:", isset( $settings->wpSubDirectory ) ? $settings->wpSubDirectory : 'false' );
apps/Backend/Modules/Views/Forms/Settings.php CHANGED
@@ -4,6 +4,7 @@ namespace WPStaging\Backend\Modules\Views\Forms;
4
  use WPStaging\Forms\Elements\Check;
5
  use WPStaging\Forms\Elements\Numerical;
6
  use WPStaging\Forms\Elements\Select;
 
7
  use WPStaging\Forms\Form;
8
  use WPStaging\Backend\Modules\Views\Tabs\Tabs;
9
 
@@ -65,6 +66,24 @@ class Settings
65
  ->setDefault(isset($settings->queryLimit) ? $settings->queryLimit : 1000)
66
  );
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  // File Copy Batch Size
69
  $element = new Numerical(
70
  "wpstg_settings[batchSize]",
@@ -96,6 +115,7 @@ class Settings
96
  ->setDefault(isset($settings->cpuLoad) ? $settings->cpuLoad : "fast")
97
  );
98
 
 
99
  // Optimizer
100
  $element = new Check(
101
  "wpstg_settings[optimizer]",
4
  use WPStaging\Forms\Elements\Check;
5
  use WPStaging\Forms\Elements\Numerical;
6
  use WPStaging\Forms\Elements\Select;
7
+ use WPStaging\Forms\Elements\SelectMultiple;
8
  use WPStaging\Forms\Form;
9
  use WPStaging\Backend\Modules\Views\Tabs\Tabs;
10
 
66
  ->setDefault(isset($settings->queryLimit) ? $settings->queryLimit : 1000)
67
  );
68
 
69
+ $options = array('250' => '250' ,'500' => '500', '1000' => '1000');
70
+ // DB Copy Query Limit
71
+ $element = new Select(
72
+ "wpstg_settings[fileLimit]",
73
+ $options,
74
+ array(
75
+ "class" => "medium-text",
76
+ "step" => 1,
77
+ "max" => 999999,
78
+ "min" => 0
79
+ )
80
+ );
81
+
82
+ $this->form["general"]->add(
83
+ $element->setLabel("File Copy Limit")->setDefault(isset($settings->fileLimit) ? $settings->fileLimit : 500)
84
+ );
85
+
86
+
87
  // File Copy Batch Size
88
  $element = new Numerical(
89
  "wpstg_settings[batchSize]",
115
  ->setDefault(isset($settings->cpuLoad) ? $settings->cpuLoad : "fast")
116
  );
117
 
118
+
119
  // Optimizer
120
  $element = new Check(
121
  "wpstg_settings[optimizer]",
apps/Backend/Notices/Notices.php CHANGED
@@ -1,147 +1,155 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Notices;
4
-
5
- /*
6
- * Admin Notices | Warnings | Messages
7
- */
8
-
9
- // No Direct Access
10
- if( !defined( "WPINC" ) ) {
11
- die;
12
- }
13
-
14
- use WPStaging\WPStaging;
15
-
16
- /**
17
- * Class Notices
18
- * @package WPStaging\Backend\Notices
19
- */
20
- class Notices {
21
-
22
- /**
23
- * @var string
24
- */
25
- private $path;
26
-
27
- /**
28
- * @var string
29
- */
30
- private $url;
31
-
32
- public function __construct( $path, $url ) {
33
- $this->path = $path;
34
- $this->url = $url;
35
- }
36
-
37
- /**
38
- * Check whether the page is admin page or not
39
- * @return bool
40
- */
41
- private function isAdminPage() {
42
- $currentPage = (isset( $_GET["page"] )) ? $_GET["page"] : null;
43
-
44
- $availablePages = array(
45
- "wpstg-settings", "wpstg-addons", "wpstg-tools", "wpstg-clone", "wpstg_clone"
46
- );
47
-
48
- if( !is_admin() || !did_action( "wp_loaded" ) || !in_array( $currentPage, $availablePages, true ) ) {
49
- return false;
50
- }
51
-
52
- return true;
53
- }
54
-
55
- /**
56
- * Check if notice should be shown after certain days of installation
57
- * @param int $days default 10
58
- * @return bool
59
- */
60
- private function canShow( $option, $days = 10 ) {
61
-
62
- if( empty( $option ) ) {
63
- return false;
64
- }
65
-
66
- $installDate = new \DateTime( get_option( "wpstg_installDate" ) );
67
- $now = new \DateTime( "now" );
68
-
69
- // Get days difference
70
- $difference = $now->diff( $installDate )->days;
71
-
72
- return ($days <= $difference && "no" !== get_option( $option ));
73
-
74
- return false;
75
- }
76
-
77
- public function messages() {
78
-
79
- $this->plugin_deactivated_notice();
80
-
81
- // Display messages to only admins, only on admin panel
82
- if( !current_user_can( "update_plugins" ) || !$this->isAdminPage() ) {
83
- return;
84
- }
85
-
86
- $viewsNoticesPath = "{$this->path}views/_includes/messages/";
87
-
88
- $varsDirectory = \WPStaging\WPStaging::getContentDir();
89
-
90
-
91
- // Poll do not show any longer
92
- /*if( $this->canShow( "wpstg_poll", 7 ) ) {
93
- require_once "{$viewsNoticesPath}poll.php";
94
- }*/
95
-
96
- // Cache directory in uploads is not writable
97
- if( !wp_is_writable( $varsDirectory ) ) {
98
- require_once "{$viewsNoticesPath}/uploads-cache-directory-permission-problem.php";
99
- }
100
- // Staging directory is not writable
101
- if( !wp_is_writable( get_home_path() ) ) {
102
- require_once "{$viewsNoticesPath}/staging-directory-permission-problem.php";
103
- }
104
-
105
- // Version Control
106
- if( version_compare( WPStaging::WP_COMPATIBLE, get_bloginfo( "version" ), "<" ) ) {
107
- require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
108
- }
109
-
110
- // Beta
111
- if( false === get_option( "wpstg_beta" ) || "no" !== get_option( "wpstg_beta" ) ) {
112
- require_once "{$viewsNoticesPath}beta.php";
113
- }
114
-
115
- // WP Staging Pro and Free can not be activated both
116
- if( false !== ( $deactivatedNoticeID = get_transient( "wp_staging_deactivated_notice_id" ) ) ) {
117
- require_once "{$viewsNoticesPath}transient.php";
118
- delete_transient( "wp_staging_deactivated_notice_id" );
119
- }
120
-
121
- if( $this->canShow( "wpstg_rating", 7 ) ) {
122
- require_once "{$viewsNoticesPath}rating.php";
123
- }
124
-
125
- }
126
-
127
- /**
128
- * Show a message when pro or free plugin becomes deactivated
129
- *
130
- * @return void
131
- */
132
- private function plugin_deactivated_notice() {
133
- if( false !== ( $deactivated_notice_id = get_transient( 'wp_staging_deactivated_notice_id' ) ) ) {
134
- if( '1' === $deactivated_notice_id ) {
135
- $message = __( "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging.", 'wpstg' );
136
- } else {
137
- $message = __( "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging Pro.", 'wpstg' );
138
- }
139
- ?>
140
- <div class="updated notice is-dismissible" style="border-left: 4px solid #ffba00;">
141
- <p><?php echo esc_html( $message ); ?></p>
142
- </div> <?php
143
- delete_transient( 'wp_staging_deactivated_notice_id' );
144
- }
145
- }
146
-
147
- }
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Notices;
4
+
5
+ /*
6
+ * Admin Notices | Warnings | Messages
7
+ */
8
+
9
+ // No Direct Access
10
+ if( !defined( "WPINC" ) ) {
11
+ die;
12
+ }
13
+
14
+ use WPStaging\WPStaging;
15
+
16
+ /**
17
+ * Class Notices
18
+ * @package WPStaging\Backend\Notices
19
+ */
20
+ class Notices {
21
+
22
+ /**
23
+ * @var string
24
+ */
25
+ private $path;
26
+
27
+ /**
28
+ * @var string
29
+ */
30
+ private $url;
31
+
32
+ public function __construct( $path, $url ) {
33
+ $this->path = $path;
34
+ $this->url = $url;
35
+ }
36
+
37
+ /**
38
+ * Check whether the page is an WP QUADS admin settings page or not
39
+ * @return bool
40
+ */
41
+ private function isAdminPage() {
42
+ $currentPage = (isset( $_GET["page"] )) ? $_GET["page"] : null;
43
+
44
+ $availablePages = array(
45
+ "wpstg-settings", "wpstg-addons", "wpstg-tools", "wpstg-clone", "wpstg_clone"
46
+ );
47
+
48
+ //if( !is_admin() || !did_action( "wp_loaded" ) || !in_array( $currentPage, $availablePages, true ) ) {
49
+ if( !is_admin() || !in_array( $currentPage, $availablePages, true ) ) {
50
+ return false;
51
+ }
52
+
53
+ return true;
54
+ }
55
+
56
+ /**
57
+ * Check if notice should be shown after certain days of installation
58
+ * @param int $days default 10
59
+ * @return bool
60
+ */
61
+ private function canShow( $option, $days = 10 ) {
62
+
63
+ if( empty( $option ) ) {
64
+ return false;
65
+ }
66
+
67
+ $installDate = new \DateTime( get_option( "wpstg_installDate" ) );
68
+ $now = new \DateTime( "now" );
69
+
70
+ // Get days difference
71
+ $difference = $now->diff( $installDate )->days;
72
+
73
+ return ($days <= $difference && "no" !== get_option( $option ));
74
+
75
+ return false;
76
+ }
77
+
78
+ public function messages() {
79
+
80
+ $this->plugin_deactivated_notice();
81
+
82
+ // Do not display notices to user_roles lower than 'update_plugins'
83
+ if( !current_user_can( 'update_plugins' ) ) {
84
+ return;
85
+ }
86
+
87
+ $viewsNoticesPath = "{$this->path}views/_includes/messages/";
88
+
89
+ // Show rating review message on all admin pages
90
+ if( $this->canShow( "wpstg_rating", 7 ) ) {
91
+ require_once "{$viewsNoticesPath}rating.php";
92
+ }
93
+
94
+
95
+ // Display messages below on wp quads admin page only
96
+ if( !$this->isAdminPage() ) {
97
+ return;
98
+ }
99
+
100
+
101
+ $varsDirectory = \WPStaging\WPStaging::getContentDir();
102
+
103
+
104
+ // Poll do not show any longer
105
+ /* if( $this->canShow( "wpstg_poll", 7 ) ) {
106
+ require_once "{$viewsNoticesPath}poll.php";
107
+ } */
108
+
109
+ // Cache directory in uploads is not writable
110
+ if( !wp_is_writable( $varsDirectory ) ) {
111
+ require_once "{$viewsNoticesPath}/uploads-cache-directory-permission-problem.php";
112
+ }
113
+ // Staging directory is not writable
114
+ if( !wp_is_writable( get_home_path() ) ) {
115
+ require_once "{$viewsNoticesPath}/staging-directory-permission-problem.php";
116
+ }
117
+
118
+ // Version Control
119
+ if( version_compare( WPStaging::WP_COMPATIBLE, get_bloginfo( "version" ), "<" ) ) {
120
+ require_once "{$viewsNoticesPath}wp-version-compatible-message.php";
121
+ }
122
+
123
+ // Beta
124
+ if( false === get_option( "wpstg_beta" ) || "no" !== get_option( "wpstg_beta" ) ) {
125
+ require_once "{$viewsNoticesPath}beta.php";
126
+ }
127
+
128
+ // WP Staging Pro and Free can not be activated both
129
+ if( false !== ( $deactivatedNoticeID = get_transient( "wp_staging_deactivated_notice_id" ) ) ) {
130
+ require_once "{$viewsNoticesPath}transient.php";
131
+ delete_transient( "wp_staging_deactivated_notice_id" );
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Show a message when pro or free plugin becomes deactivated
137
+ *
138
+ * @return void
139
+ */
140
+ private function plugin_deactivated_notice() {
141
+ if( false !== ( $deactivated_notice_id = get_transient( 'wp_staging_deactivated_notice_id' ) ) ) {
142
+ if( '1' === $deactivated_notice_id ) {
143
+ $message = __( "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging.", 'wpstg' );
144
+ } else {
145
+ $message = __( "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging Pro.", 'wpstg' );
146
+ }
147
+ ?>
148
+ <div class="updated notice is-dismissible" style="border-left: 4px solid #ffba00;">
149
+ <p><?php echo esc_html( $message ); ?></p>
150
+ </div> <?php
151
+ delete_transient( 'wp_staging_deactivated_notice_id' );
152
+ }
153
+ }
154
+
155
+ }
apps/Backend/Upgrade/Upgrade.php CHANGED
@@ -1,214 +1,229 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Upgrade;
4
-
5
- use WPStaging\WPStaging;
6
- use WPStaging\Utils\Logger;
7
-
8
- /**
9
- * Upgrade Class
10
- * This must be loaded on every page init to ensure all settings are
11
- * adjusted correctly and to run any upgrade process if necessary.
12
- */
13
-
14
- // No Direct Access
15
- if( !defined( "WPINC" ) ) {
16
- die;
17
- }
18
-
19
- class Upgrade {
20
-
21
- /**
22
- * Previous Version number
23
- * @var string
24
- */
25
- private $previousVersion;
26
-
27
- /**
28
- * Clone data
29
- * @var obj
30
- */
31
- private $clones;
32
-
33
- /**
34
- * Clone data
35
- * @var obj
36
- */
37
- private $clonesBeta;
38
-
39
- /**
40
- * Cron data
41
- * @var obj
42
- */
43
- private $cron;
44
-
45
- /**
46
- * Logger
47
- * @var obj
48
- */
49
- private $logger;
50
-
51
- public function __construct() {
52
-
53
- // add wpstg_weekly_event to cron events
54
- $this->cron = new \WPStaging\Cron\Cron;
55
-
56
- // Previous version
57
- $this->previousVersion = preg_replace( '/[^0-9.].*/', '', get_option( 'wpstg_version' ) );
58
-
59
- // Options earlier than version 2.0.0
60
- $this->clones = get_option( "wpstg_existing_clones", array() );
61
-
62
- // Current options
63
- $this->clonesBeta = get_option( "wpstg_existing_clones_beta", array() );
64
-
65
- // Logger
66
- $this->logger = new Logger;
67
- }
68
-
69
- public function doUpgrade() {
70
- $this->upgrade2_0_3();
71
- //$this->upgrade2_0_4();
72
- $this->setVersion();
73
- }
74
-
75
- /**
76
- * Upgrade method 2.0.3
77
- */
78
- public function upgrade2_0_3() {
79
- // Previous version lower than 2.0.2 or new install
80
- if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.0.2', '<' ) ) {
81
- $this->upgradeOptions();
82
- $this->upgradeClonesBeta();
83
- $this->upgradeNotices();
84
- }
85
- }
86
-
87
- /**
88
- * Upgrade method 2.0.4
89
- */
90
- // public function upgrade2_0_4() {
91
- // if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.0.4', '<' ) ) {
92
- //
93
- // // Register cron job.
94
- // $this->cron->schedule_event();
95
- //
96
- // // Install Optimizer
97
- // $optimizer = new Optimizer();
98
- // $optimizer->installOptimizer();
99
- // }
100
- // }
101
-
102
- /**
103
- * Upgrade routine for new install
104
- */
105
- private function upgradeOptions() {
106
- // Write some default vars
107
- add_option( 'wpstg_installDate', date( 'Y-m-d h:i:s' ) );
108
- }
109
-
110
- /**
111
- * Write new version number into db
112
- * return bool
113
- */
114
- private function setVersion() {
115
- // Check if version number in DB is lower than version number in current plugin
116
- if( version_compare( $this->previousVersion, \WPStaging\WPStaging::VERSION, '<' ) ) {
117
- // Update Version number
118
- update_option( 'wpstg_version', preg_replace( '/[^0-9.].*/', '', \WPStaging\WPStaging::VERSION ) );
119
- // Update "upgraded from" version number
120
- update_option( 'wpstg_version_upgraded_from', preg_replace( '/[^0-9.].*/', '', $this->previousVersion ) );
121
-
122
- return true;
123
- }
124
- return false;
125
- }
126
-
127
- /**
128
- * Create a new db option for beta version 2.0.2
129
- * @return bool
130
- */
131
- private function upgradeClonesBeta() {
132
-
133
- // Copy old data to new option
134
- //update_option( 'wpstg_existing_clones_beta', $this->clones );
135
-
136
- $new = array();
137
-
138
- if( empty( $this->clones ) ) {
139
- return false;
140
- }
141
-
142
-
143
- foreach ( $this->clones as $key => &$value ) {
144
-
145
- // Skip the rest of the loop if data is already compatible to wpstg 2.0.2
146
- if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
147
- continue;
148
- }
149
-
150
- $new[$value]['directoryName'] = $value;
151
- $new[$value]['path'] = get_home_path() . $value;
152
- $new[$value]['url'] = get_home_url() . "/" . $value;
153
- $new[$value]['number'] = $key + 1;
154
- $new[$value]['version'] = $this->previousVersion;
155
- }
156
- unset( $value );
157
-
158
- if( empty( $new ) || false === update_option( 'wpstg_existing_clones_beta', $new ) ) {
159
- $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
160
- }
161
- }
162
-
163
- /**
164
- * Convert clone data from wpstg 1.x to wpstg 2.x
165
- * Only use this later when wpstg 2.x is ready for production
166
- */
167
- private function upgradeClones() {
168
-
169
- $new = array();
170
-
171
- if( empty( $this->clones ) ) {
172
- return false;
173
- }
174
-
175
- foreach ( $this->clones as $key => &$value ) {
176
-
177
- // Skip the rest of the loop if data is already compatible to wpstg 2.0.1
178
- if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
179
- continue;
180
- }
181
- $new[$value]['directoryName'] = $value;
182
- $new[$value]['path'] = get_home_path() . $value;
183
- $new[$value]['url'] = get_home_url() . "/" . $value;
184
- $new[$value]['number'] = $key + 1;
185
- $new[$value]['version'] = $this->previousVersion;
186
- }
187
- unset( $value );
188
-
189
- if( empty( $new ) || false === update_option( 'wpstg_existing_clones', $new ) ) {
190
- $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
191
- }
192
- }
193
-
194
- /**
195
- * Upgrade Notices db options from wpstg 1.3 -> 2.0.1
196
- * Fix some logical db options
197
- */
198
- private function upgradeNotices() {
199
- $poll = get_option( "wpstg_start_poll", false );
200
- $beta = get_option( "wpstg_hide_beta", false );
201
- $rating = get_option( "wpstg_RatingDiv", false );
202
-
203
- if( $poll && $poll === "no" ) {
204
- update_option( 'wpstg_poll', 'no' );
205
- }
206
- if( $beta && $beta === "yes" ) {
207
- update_option( 'wpstg_beta', 'no' );
208
- }
209
- if( $rating && $rating === 'yes' ) {
210
- update_option( 'wpstg_rating', 'no' );
211
- }
212
- }
213
-
214
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Upgrade;
4
+
5
+ use WPStaging\WPStaging;
6
+ use WPStaging\Utils\Logger;
7
+
8
+ /**
9
+ * Upgrade Class
10
+ * This must be loaded on every page init to ensure all settings are
11
+ * adjusted correctly and to run any upgrade process if necessary.
12
+ */
13
+
14
+ // No Direct Access
15
+ if( !defined( "WPINC" ) ) {
16
+ die;
17
+ }
18
+
19
+ class Upgrade {
20
+
21
+ /**
22
+ * Previous Version number
23
+ * @var string
24
+ */
25
+ private $previousVersion;
26
+
27
+ /**
28
+ * Clone data
29
+ * @var obj
30
+ */
31
+ private $clones;
32
+
33
+ /**
34
+ * Clone data
35
+ * @var obj
36
+ */
37
+ private $clonesBeta;
38
+
39
+ /**
40
+ * Cron data
41
+ * @var obj
42
+ */
43
+ private $cron;
44
+
45
+ /**
46
+ * Logger
47
+ * @var obj
48
+ */
49
+ private $logger;
50
+
51
+ public function __construct() {
52
+
53
+ // add wpstg_weekly_event to cron events
54
+ $this->cron = new \WPStaging\Cron\Cron;
55
+
56
+ // Previous version
57
+ $this->previousVersion = preg_replace( '/[^0-9.].*/', '', get_option( 'wpstg_version' ) );
58
+
59
+ // Options earlier than version 2.0.0
60
+ $this->clones = get_option( "wpstg_existing_clones", array() );
61
+
62
+ // Current options
63
+ $this->clonesBeta = get_option( "wpstg_existing_clones_beta", array() );
64
+
65
+ // Logger
66
+ $this->logger = new Logger;
67
+ }
68
+
69
+ public function doUpgrade() {
70
+ $this->upgrade2_0_3();
71
+ //$this->upgrade2_0_4();
72
+ $this->upgrade2_1_2();
73
+ $this->setVersion();
74
+ }
75
+
76
+ /**
77
+ * Upgrade method 2.0.3
78
+ */
79
+ public function upgrade2_0_3() {
80
+ // Previous version lower than 2.0.2 or new install
81
+ if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.0.2', '<' ) ) {
82
+ $this->upgradeOptions();
83
+ $this->upgradeClonesBeta();
84
+ $this->upgradeNotices();
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Upgrade method 2.0.4
90
+ */
91
+ // public function upgrade2_0_4() {
92
+ // if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.0.4', '<' ) ) {
93
+ //
94
+ // // Register cron job.
95
+ // $this->cron->schedule_event();
96
+ //
97
+ // // Install Optimizer
98
+ // $optimizer = new Optimizer();
99
+ // $optimizer->installOptimizer();
100
+ // }
101
+ // }
102
+
103
+ /**
104
+ * Upgrade method 2.1.2
105
+ * Sanitize the clone key value.
106
+ */
107
+ private function upgrade2_1_2(){
108
+ if( false === $this->previousVersion || version_compare( $this->previousVersion, '2.1.7', '<' ) ) {
109
+ foreach ( $this->clonesBeta as $key => $value){
110
+ unset($this->clonesBeta[$key]);
111
+ $this->clonesBeta[preg_replace("#\W+#", '-', strtolower($key))] = $value;
112
+ }
113
+ update_option('wpstg_existing_clones_beta', $this->clonesBeta);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Upgrade routine for new install
119
+ */
120
+ private function upgradeOptions() {
121
+ // Write some default vars
122
+ add_option( 'wpstg_installDate', date( 'Y-m-d h:i:s' ) );
123
+ }
124
+
125
+ /**
126
+ * Write new version number into db
127
+ * return bool
128
+ */
129
+ private function setVersion() {
130
+ // Check if version number in DB is lower than version number in current plugin
131
+ if( version_compare( $this->previousVersion, \WPStaging\WPStaging::VERSION, '<' ) ) {
132
+ // Update Version number
133
+ update_option( 'wpstg_version', preg_replace( '/[^0-9.].*/', '', \WPStaging\WPStaging::VERSION ) );
134
+ // Update "upgraded from" version number
135
+ update_option( 'wpstg_version_upgraded_from', preg_replace( '/[^0-9.].*/', '', $this->previousVersion ) );
136
+
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+
142
+ /**
143
+ * Create a new db option for beta version 2.0.2
144
+ * @return bool
145
+ */
146
+ private function upgradeClonesBeta() {
147
+
148
+ // Copy old data to new option
149
+ //update_option( 'wpstg_existing_clones_beta', $this->clones );
150
+
151
+ $new = array();
152
+
153
+ if( empty( $this->clones ) ) {
154
+ return false;
155
+ }
156
+
157
+
158
+ foreach ( $this->clones as $key => &$value ) {
159
+
160
+ // Skip the rest of the loop if data is already compatible to wpstg 2.0.2
161
+ if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
162
+ continue;
163
+ }
164
+
165
+ $new[$value]['directoryName'] = $value;
166
+ $new[$value]['path'] = get_home_path() . $value;
167
+ $new[$value]['url'] = get_home_url() . "/" . $value;
168
+ $new[$value]['number'] = $key + 1;
169
+ $new[$value]['version'] = $this->previousVersion;
170
+ }
171
+ unset( $value );
172
+
173
+ if( empty( $new ) || false === update_option( 'wpstg_existing_clones_beta', $new ) ) {
174
+ $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Convert clone data from wpstg 1.x to wpstg 2.x
180
+ * Only use this later when wpstg 2.x is ready for production
181
+ */
182
+ private function upgradeClones() {
183
+
184
+ $new = array();
185
+
186
+ if( empty( $this->clones ) ) {
187
+ return false;
188
+ }
189
+
190
+ foreach ( $this->clones as $key => &$value ) {
191
+
192
+ // Skip the rest of the loop if data is already compatible to wpstg 2.0.1
193
+ if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
194
+ continue;
195
+ }
196
+ $new[$value]['directoryName'] = $value;
197
+ $new[$value]['path'] = get_home_path() . $value;
198
+ $new[$value]['url'] = get_home_url() . "/" . $value;
199
+ $new[$value]['number'] = $key + 1;
200
+ $new[$value]['version'] = $this->previousVersion;
201
+ }
202
+ unset( $value );
203
+
204
+ if( empty( $new ) || false === update_option( 'wpstg_existing_clones', $new ) ) {
205
+ $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Upgrade Notices db options from wpstg 1.3 -> 2.0.1
211
+ * Fix some logical db options
212
+ */
213
+ private function upgradeNotices() {
214
+ $poll = get_option( "wpstg_start_poll", false );
215
+ $beta = get_option( "wpstg_hide_beta", false );
216
+ $rating = get_option( "wpstg_RatingDiv", false );
217
+
218
+ if( $poll && $poll === "no" ) {
219
+ update_option( 'wpstg_poll', 'no' );
220
+ }
221
+ if( $beta && $beta === "yes" ) {
222
+ update_option( 'wpstg_beta', 'no' );
223
+ }
224
+ if( $rating && $rating === 'yes' ) {
225
+ update_option( 'wpstg_rating', 'no' );
226
+ }
227
+ }
228
+
229
+ }
apps/Backend/public/js/wpstg-admin-rating.js CHANGED
@@ -1,24 +1,32 @@
1
- jQuery(document).ready(function ($) {
2
- $(".wpstg_hide_rating").click(function (e) {
3
- e.preventDefault();
4
-
5
- WPStaging.ajax(
6
- {action: "wpstg_hide_rating"},
7
- function (response)
8
- {
9
- if (true === response)
10
- {
11
- $(".wpstg_fivestar").slideUp("fast");
12
- return true;
13
- } else {
14
- alert(
15
- "Unexpected message received. This might mean the data was not saved " +
16
- "and you might see this message again"
17
- );
18
- }
19
- }
20
- );
21
-
22
-
23
- })
 
 
 
 
 
 
 
 
24
  });
1
+ jQuery(document).ready(function ($) {
2
+ $(".wpstg_hide_rating").click(function (e) {
3
+ e.preventDefault();
4
+
5
+ $.ajax({
6
+ url: ajaxurl,
7
+ type: "POST",
8
+ data: {action: "wpstg_hide_rating"},
9
+ error: function (xhr, textStatus, errorThrown) {
10
+ console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
11
+ console.log(textStatus);
12
+
13
+
14
+ alert(
15
+ "Unknown error"
16
+ );
17
+ },
18
+ success: function (data) {
19
+ $(".wpstg_fivestar").slideUp("fast");
20
+ return true;
21
+ },
22
+ statusCode: {
23
+ 404: function () {
24
+ alert("Something went wrong; can't find ajax request URL!");
25
+ },
26
+ 500: function () {
27
+ alert("Something went wrong; internal server error while processing the request!");
28
+ }
29
+ }
30
+ });
31
+ });
32
  });
apps/Backend/public/js/wpstg-admin.js CHANGED
@@ -321,10 +321,9 @@ var WPStaging = (function ($)
321
  }
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
  );
329
  },
330
  success: function (data) {
321
  }
322
 
323
  showError(
324
+ "Fatal Unknown Error. Go to WP Staging > Settings and lower 'File Copy Limit'" +
325
+ "Than try again. If this does not help, " +
326
+ "<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
 
327
  );
328
  },
329
  success: function (data) {
apps/Backend/views/_includes/messages/rating.php CHANGED
@@ -1,31 +1,31 @@
1
- <div class="wpstg_fivestar updated" style="box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);">
2
- <p>
3
- Awesome, you've been using <strong>WP Staging </strong> for more than 1 week.
4
- May I ask you to give it a <strong>5-star</strong> rating on Wordpress?
5
- <br><br>
6
- P.S. Looking for a way to copy plugins and theme files from staging to live site? Try out <a href="https://wp-staging.com" target="_blank">WP Staging Pro</a> <br>
7
- </p>
8
-
9
- <p>
10
- <strong>Regards,<br>René Hermenau</strong>
11
- </p>
12
-
13
- <ul>
14
- <li>
15
- <a href="https://wordpress.org/support/plugin/wp-staging/reviews/?filter=5#new-post" class="thankyou" target="_new" title="Ok, you deserved it" style="font-weight:bold;">
16
- Ok, you deserved it
17
- </a>
18
- </li>
19
- <li>
20
- <a href="javascript:void(0);" class="wpstg_hide_rating" title="I already did" style="font-weight:bold;">
21
- I already did
22
- </a>
23
- </li>
24
- <li>
25
- <a href="javascript:void(0);" class="wpstg_hide_rating" title="No, not good enough" style="font-weight:bold;">
26
- No, not good enough
27
- </a>
28
- </li>
29
- </ul>
30
- </div>
31
  <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-rating.js"?>"></script>
1
+ <div class="wpstg_fivestar updated" style="box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);">
2
+ <p>
3
+ Awesome, you've been using <strong>WP Staging </strong> for more than 1 week.
4
+ May I ask you to give it a <strong>5-star</strong> rating on Wordpress?
5
+ <br><br>
6
+ P.S. Looking for a way to copy plugins and theme files from staging to live site? Try out <a href="https://wp-staging.com" target="_blank">WP Staging Pro</a> <br>
7
+ </p>
8
+
9
+ <p>
10
+ <strong>Regards,<br>René Hermenau</strong>
11
+ </p>
12
+
13
+ <ul>
14
+ <li>
15
+ <a href="https://wordpress.org/support/plugin/wp-staging/reviews/?filter=5#new-post" class="thankyou" target="_new" title="Ok, you deserved it" style="font-weight:bold;">
16
+ Ok, you deserved it
17
+ </a>
18
+ </li>
19
+ <li>
20
+ <a href="javascript:void(0);" class="wpstg_hide_rating" title="I already did" style="font-weight:bold;">
21
+ I already did
22
+ </a>
23
+ </li>
24
+ <li>
25
+ <a href="javascript:void(0);" class="wpstg_hide_rating" title="No, not good enough" style="font-weight:bold;">
26
+ No, not good enough
27
+ </a>
28
+ </li>
29
+ </ul>
30
+ </div>
31
  <script type="text/javascript" src="<?php echo $this->url . "js/wpstg-admin-rating.js"?>"></script>
apps/Backend/views/clone/ajax/start.php CHANGED
@@ -1,115 +1,113 @@
1
- <div class=successfullying-section">
2
- <?php echo __("Copy Database Tables", "wpstg")?>
3
- <div class="wpstg-progress-bar">
4
- <div class="wpstg-progress" id="wpstg-db-progress" style="width:0"></div>
5
- </div>
6
- </div>
7
-
8
- <div class="wpstg-cloning-section">
9
- <?php echo __("Prepare Directories", "wpstg")?>
10
- <div class="wpstg-progress-bar">
11
- <div class="wpstg-progress" id="wpstg-directories-progress" style="width:0"></div>
12
- </div>
13
- </div>
14
-
15
- <div class="wpstg-cloning-section">
16
- <?php echo __("Copy Files", "wpstg")?>
17
- <div class="wpstg-progress-bar">
18
- <div class="wpstg-progress" id="wpstg-files-progress" style="width:0"></div>
19
- </div>
20
- </div>
21
-
22
- <div class="wpstg-cloning-section">
23
- <?php echo __("Replace Data", "wpstg")?>
24
- <div class="wpstg-progress-bar">
25
- <div class="wpstg-progress" id="wpstg-links-progress" style="width:0"></div>
26
- </div>
27
- </div>
28
-
29
- <button type="button" id="wpstg-cancel-cloning" class="wpstg-link-btn button-primary">
30
- <?php echo __("Cancel", "wpstg")?>
31
- </button>
32
-
33
- <button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
34
- <?php _e('Display working log', 'wpstg')?>
35
- </button>
36
-
37
- <div>
38
- <span id="wpstg-cloning-result"></span>
39
- </div>
40
-
41
- <div id="wpstg-finished-result">
42
- <h3>Congratulations
43
- </h3>
44
- <?php
45
- $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">
60
- <?php //echo __("Remove", "wpstg")?>
61
- </a>//-->
62
- <a href="" class="wpstg-link-btn button-primary" id="wpstg-home-link">
63
- <?php echo __("Start again", "wpstg")?>
64
- </a>
65
- <div id="wpstg-success-notice">
66
- <h3 style="margin-top:0px;">
67
- <?php _e("Important Notes:", "wpstg")?>
68
- </h3>
69
- <ul>
70
- <li>
71
- <strong>1. Permalinks on your <span style="font-style:italic;">staging site</span> will be disabled for technical reasons! </strong>
72
- <br>
73
- Usually this is no problem for a staging website and you do not need to use permalinks!
74
- <br>
75
- <p>
76
- If you really want permalinks on your staging site you need to do several modifications to your .htaccess (Apache) or *.conf (Nginx).
77
- <br>
78
- WP Staging can not do this modification automatically.
79
- </p>
80
- <p>
81
- <strong>Read more:</strong>
82
- <a href="http://stackoverflow.com/questions/5564881/htaccess-to-rewrite-wordpress-subdirectory-with-permalinks-to-root" target="_blank">
83
- Changes .htaccess
84
- </a> |
85
- <a href="http://robido.com/nginx/nginx-wordpress-subdirectory-configuration-example/" target="_blank">
86
- Changes nginx conf
87
- </a>
88
- </p>
89
- </li>
90
- <li>
91
- <strong>2. Verify that you are REALLY working on your staging site and NOT on your production site if you are uncertain! </strong>
92
- <br>
93
- Your main and your staging site are both reachable under the same domain so
94
- <br>
95
- it´s easy to get confused.
96
- <p>
97
- To assist you we changed the name of the dashboard link to
98
- <strong style="font-style:italic;">
99
- "STAGING - <span class="wpstg-clone-name"><?php echo get_bloginfo("name")?></span>"
100
- </strong>.
101
- <br>
102
- You will notice this new name in the admin bar:
103
- <br><br>
104
- <img src="<?php echo $this->url . "/img/admin_dashboard.png" ?>">
105
- </p>
106
- </li>
107
- </ul>
108
- </div>
109
- </div>
110
-
111
- <div id="wpstg-error-wrapper">
112
- <div id="wpstg-error-details"></div>
113
- </div>
114
-
115
  <div id="wpstg-log-details"></div>
1
+ <div class=successfullying-section">
2
+ <?php echo __("Copy Database Tables", "wpstg")?>
3
+ <div class="wpstg-progress-bar">
4
+ <div class="wpstg-progress" id="wpstg-db-progress" style="width:0"></div>
5
+ </div>
6
+ </div>
7
+
8
+ <div class="wpstg-cloning-section">
9
+ <?php echo __("Prepare Directories", "wpstg")?>
10
+ <div class="wpstg-progress-bar">
11
+ <div class="wpstg-progress" id="wpstg-directories-progress" style="width:0"></div>
12
+ </div>
13
+ </div>
14
+
15
+ <div class="wpstg-cloning-section">
16
+ <?php echo __("Copy Files", "wpstg")?>
17
+ <div class="wpstg-progress-bar">
18
+ <div class="wpstg-progress" id="wpstg-files-progress" style="width:0"></div>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="wpstg-cloning-section">
23
+ <?php echo __("Replace Data", "wpstg")?>
24
+ <div class="wpstg-progress-bar">
25
+ <div class="wpstg-progress" id="wpstg-links-progress" style="width:0"></div>
26
+ </div>
27
+ </div>
28
+
29
+ <button type="button" id="wpstg-cancel-cloning" class="wpstg-link-btn button-primary">
30
+ <?php echo __("Cancel", "wpstg")?>
31
+ </button>
32
+
33
+ <button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
34
+ <?php _e('Display working log', 'wpstg')?>
35
+ </button>
36
+
37
+ <div>
38
+ <span id="wpstg-cloning-result"></span>
39
+ </div>
40
+
41
+ <div id="wpstg-finished-result">
42
+ <h3>Congratulations
43
+ </h3>
44
+ <?php
45
+ //echo ABSPATH . '<br>';
46
+ //echo get_home_path();
47
+ $subDirectory = str_replace( get_home_path(), '', ABSPATH );
48
+ $url = get_home_url() . str_replace('/', '', $subDirectory);
49
+ echo sprintf( __( 'WP Staging successfully created a staging site in a sub-directory of your main site in:<br><strong><a href="%1$s" target="_blank" id="wpstg-clone-url-1">%1$s</a></strong>', 'wpstg' ), $url );
50
+ ?>
51
+ <br>
52
+ <?php //echo __('Open and access the staging site: ', 'wpstg')?>
53
+ <br>
54
+ <a href="<?php echo $url; ?>" id="wpstg-clone-url" target="_blank" class="wpstg-link-btn button-primary">
55
+ Open staging site <span style="font-size: 10px;">(login with your admin credentials)</span>
56
+ </a>
57
+ <!--<a href="" class="wpstg-link-btn button-primary" id="wpstg-remove-cloning">
58
+ <?php //echo __("Remove", "wpstg")?>
59
+ </a>//-->
60
+ <a href="" class="wpstg-link-btn button-primary" id="wpstg-home-link">
61
+ <?php echo __("Start again", "wpstg")?>
62
+ </a>
63
+ <div id="wpstg-success-notice">
64
+ <h3 style="margin-top:0px;">
65
+ <?php _e("Important Notes:", "wpstg")?>
66
+ </h3>
67
+ <ul>
68
+ <li>
69
+ <strong>1. Permalinks on your <span style="font-style:italic;">staging site</span> will be disabled for technical reasons! </strong>
70
+ <br>
71
+ Usually this is no problem for a staging website and you do not need to use permalinks!
72
+ <br>
73
+ <p>
74
+ If you really want permalinks on your staging site you need to do several modifications to your .htaccess (Apache) or *.conf (Nginx).
75
+ <br>
76
+ WP Staging can not do this modification automatically.
77
+ </p>
78
+ <p>
79
+ <strong>Read more:</strong>
80
+ <a href="http://stackoverflow.com/questions/5564881/htaccess-to-rewrite-wordpress-subdirectory-with-permalinks-to-root" target="_blank">
81
+ Changes .htaccess
82
+ </a> |
83
+ <a href="http://robido.com/nginx/nginx-wordpress-subdirectory-configuration-example/" target="_blank">
84
+ Changes nginx conf
85
+ </a>
86
+ </p>
87
+ </li>
88
+ <li>
89
+ <strong>2. Verify that you are REALLY working on your staging site and NOT on your production site if you are uncertain! </strong>
90
+ <br>
91
+ Your main and your staging site are both reachable under the same domain so
92
+ <br>
93
+ it´s easy to get confused.
94
+ <p>
95
+ To assist you we changed the name of the dashboard link to
96
+ <strong style="font-style:italic;">
97
+ "STAGING - <span class="wpstg-clone-name"><?php echo get_bloginfo("name")?></span>"
98
+ </strong>.
99
+ <br>
100
+ You will notice this new name in the admin bar:
101
+ <br><br>
102
+ <img src="<?php echo $this->url . "/img/admin_dashboard.png" ?>">
103
+ </p>
104
+ </li>
105
+ </ul>
106
+ </div>
107
+ </div>
108
+
109
+ <div id="wpstg-error-wrapper">
110
+ <div id="wpstg-error-details"></div>
111
+ </div>
112
+
 
 
113
  <div id="wpstg-log-details"></div>
apps/Backend/views/settings/index.php CHANGED
@@ -96,10 +96,10 @@
96
  <span class="description">
97
  Number of DB rows, that will be copied within one ajax request.
98
  The higher the value the faster the database copy process.
99
- To find out the highest possible values try a high value like 1.000 or more and decrease it
100
- until you get no more errors during copy process.
101
  <br>
102
- <strong> Default: 100 </strong>
103
  </span>
104
  </div>
105
  </td>
@@ -108,6 +108,27 @@
108
  </td>
109
  </tr>
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  <tr class="row">
112
  <td class="row th">
113
  <div class="col-title">
96
  <span class="description">
97
  Number of DB rows, that will be copied within one ajax request.
98
  The higher the value the faster the database copy process.
99
+ To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it
100
+ until you get no more errors during copying process.
101
  <br>
102
+ <strong> Default: 1000 </strong>
103
  </span>
104
  </div>
105
  </td>
108
  </td>
109
  </tr>
110
 
111
+ <tr class="row">
112
+ <td class="row th">
113
+ <div class="col-title">
114
+ <?php
115
+ echo $form->label("wpstg_settings[fileLimit]")
116
+ ?>
117
+ <span class="description">
118
+ Number of files to copy that will be copied within one ajax request.
119
+ The higher the value the faster the file copy process.
120
+ To find out the highest possible values try a high value like 500 or more. If you get timeout issues, lower it
121
+ until you get no more errors during copying process.
122
+ <br>
123
+ <strong> Default: 500 </strong>
124
+ </span>
125
+ </div>
126
+ </td>
127
+ <td>
128
+ <?php echo $form->render("wpstg_settings[fileLimit]")?>
129
+ </td>
130
+ </tr>
131
+
132
  <tr class="row">
133
  <td class="row th">
134
  <div class="col-title">
apps/Core/DTO/Settings.php CHANGED
@@ -1,12 +1,13 @@
1
  <?php
 
2
  namespace WPStaging\DTO;
3
 
4
  /**
5
  * Class Settings
6
  * @package WPStaging\DTO
7
  */
8
- class Settings
9
- {
10
  /**
11
  * @var array
12
  */
@@ -20,6 +21,11 @@ class Settings
20
  /**
21
  * @var int
22
  */
 
 
 
 
 
23
  protected $batchSize;
24
 
25
  /**
@@ -68,7 +74,7 @@ class Settings
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
  }
@@ -77,14 +83,11 @@ class Settings
77
  * @param array $settings
78
  * @return $this
79
  */
80
- public function hydrate($settings = array())
81
- {
82
  $this->_raw = $settings;
83
 
84
- foreach ($settings as $key => $value)
85
- {
86
- if (property_exists($this, $key))
87
- {
88
  $this->{$key} = $value;
89
  }
90
  }
@@ -112,168 +115,161 @@ class Settings
112
  /**
113
  * @return array
114
  */
115
- public function getRaw()
116
- {
117
  return $this->_raw;
118
  }
119
 
120
  /**
121
  * @return int
122
  */
123
- public function getQueryLimit()
124
- {
125
- return (int) $this->queryLimit;
126
  }
127
 
128
  /**
129
  * @param int $queryLimit
130
  */
131
- public function setQueryLimit($queryLimit)
132
- {
133
  $this->queryLimit = $queryLimit;
134
  }
135
 
136
  /**
137
  * @return int
138
  */
139
- public function getBatchSize()
140
- {
141
- return (int) $this->batchSize;
142
  }
143
 
144
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  * @param int $batchSize
146
  */
147
- public function setBatchSize($batchSize)
148
- {
149
  $this->batchSize = $batchSize;
150
  }
151
 
152
  /**
153
  * @return string
154
  */
155
- public function getCpuLoad()
156
- {
157
  return $this->cpuLoad;
158
  }
159
 
160
  /**
161
  * @param string $cpuLoad
162
  */
163
- public function setCpuLoad($cpuLoad)
164
- {
165
  $this->cpuLoad = $cpuLoad;
166
  }
167
 
168
  /**
169
  * @return bool
170
  */
171
- public function isUnInstallOnDelete()
172
- {
173
  return ('1' === $this->unInstallOnDelete);
174
  }
175
 
176
  /**
177
  * @param bool $unInstallOnDelete
178
  */
179
- public function setUnInstallOnDelete($unInstallOnDelete)
180
- {
181
  $this->unInstallOnDelete = $unInstallOnDelete;
182
  }
183
 
184
  /**
185
  * @return bool
186
  */
187
- public function isOptimizer()
188
- {
189
  return ('1' === $this->optimizer);
190
  }
191
 
192
  /**
193
  * @param bool $optimizer
194
  */
195
- public function setOptimizer($optimizer)
196
- {
197
  $this->optimizer = $optimizer;
198
  }
199
 
200
  /**
201
  * @return bool
202
  */
203
- public function isDisableAdminLogin()
204
- {
205
  return ('1' === $this->disableAdminLogin);
206
  }
207
 
208
  /**
209
  * @param bool $disableAdminLogin
210
  */
211
- public function setDisableAdminLogin($disableAdminLogin)
212
- {
213
  $this->disableAdminLogin = $disableAdminLogin;
214
  }
215
 
216
  /**
217
  * @return bool
218
  */
219
- public function isWpSubDirectory()
220
- {
221
  return ('1' === $this->wpSubDirectory);
222
  }
223
 
224
  /**
225
  * @param bool $wpSubDirectory
226
  */
227
- public function setWpSubDirectory($wpSubDirectory)
228
- {
229
  $this->wpSubDirectory = $wpSubDirectory;
230
  }
231
 
232
  /**
233
  * @return bool
234
  */
235
- public function isCheckDirectorySize()
236
- {
237
  return ('1' === $this->checkDirectorySize);
238
  }
239
 
240
  /**
241
  * @param bool $checkDirectorySize
242
  */
243
- public function setCheckDirectorySize($checkDirectorySize)
244
- {
245
  $this->checkDirectorySize = $checkDirectorySize;
246
  }
247
 
248
  /**
249
  * @return bool
250
  */
251
- public function isDebugMode()
252
- {
253
  return ('1' === $this->debugMode);
254
  }
255
 
256
  /**
257
  * @param bool $debugMode
258
  */
259
- public function setDebugMode($debugMode)
260
- {
261
  $this->debugMode = $debugMode;
262
  }
263
 
264
  /**
265
  * @return array
266
  */
267
- public function getBlackListedPlugins()
268
- {
269
  return $this->blackListedPlugins;
270
  }
271
 
272
  /**
273
  * @param array $blackListedPlugins
274
  */
275
- public function setBlackListedPlugins($blackListedPlugins)
276
- {
277
  $this->blackListedPlugins = $blackListedPlugins;
278
  }
279
  }
1
  <?php
2
+
3
  namespace WPStaging\DTO;
4
 
5
  /**
6
  * Class Settings
7
  * @package WPStaging\DTO
8
  */
9
+ class Settings {
10
+
11
  /**
12
  * @var array
13
  */
21
  /**
22
  * @var int
23
  */
24
+ protected $fileLimit;
25
+
26
+ /**
27
+ * @var int
28
+ */
29
  protected $batchSize;
30
 
31
  /**
74
  public function __construct() {
75
  $this->_raw = get_option( "wpstg_settings", array() );
76
 
77
+ if (!empty($this->_raw)){
78
  $this->hydrate( $this->_raw );
79
  }
80
  }
83
  * @param array $settings
84
  * @return $this
85
  */
86
+ public function hydrate( $settings = array() ) {
 
87
  $this->_raw = $settings;
88
 
89
+ foreach ( $settings as $key => $value ) {
90
+ if( property_exists( $this, $key ) ) {
 
 
91
  $this->{$key} = $value;
92
  }
93
  }
115
  /**
116
  * @return array
117
  */
118
+ public function getRaw() {
 
119
  return $this->_raw;
120
  }
121
 
122
  /**
123
  * @return int
124
  */
125
+ public function getQueryLimit() {
126
+ return ( int ) $this->queryLimit;
 
127
  }
128
 
129
  /**
130
  * @param int $queryLimit
131
  */
132
+ public function setQueryLimit( $queryLimit ) {
 
133
  $this->queryLimit = $queryLimit;
134
  }
135
 
136
  /**
137
  * @return int
138
  */
139
+ public function getFileLimit() {
140
+ return ( int ) $this->fileLimit;
 
141
  }
142
 
143
  /**
144
+ * @param int $fileCopyLimit
145
+ */
146
+ public function setFileLimit( $fileLimit ) {
147
+ $this->fileLimit = $fileLimit;
148
+ }
149
+
150
+ /**
151
+ * @return int
152
+ */
153
+ public function getBatchSize() {
154
+ return ( int ) $this->batchSize;
155
+ }
156
+
157
+ /**
158
  * @param int $batchSize
159
  */
160
+ public function setBatchSize( $batchSize ) {
 
161
  $this->batchSize = $batchSize;
162
  }
163
 
164
  /**
165
  * @return string
166
  */
167
+ public function getCpuLoad() {
 
168
  return $this->cpuLoad;
169
  }
170
 
171
  /**
172
  * @param string $cpuLoad
173
  */
174
+ public function setCpuLoad( $cpuLoad ) {
 
175
  $this->cpuLoad = $cpuLoad;
176
  }
177
 
178
  /**
179
  * @return bool
180
  */
181
+ public function isUnInstallOnDelete() {
 
182
  return ('1' === $this->unInstallOnDelete);
183
  }
184
 
185
  /**
186
  * @param bool $unInstallOnDelete
187
  */
188
+ public function setUnInstallOnDelete( $unInstallOnDelete ) {
 
189
  $this->unInstallOnDelete = $unInstallOnDelete;
190
  }
191
 
192
  /**
193
  * @return bool
194
  */
195
+ public function isOptimizer() {
 
196
  return ('1' === $this->optimizer);
197
  }
198
 
199
  /**
200
  * @param bool $optimizer
201
  */
202
+ public function setOptimizer( $optimizer ) {
 
203
  $this->optimizer = $optimizer;
204
  }
205
 
206
  /**
207
  * @return bool
208
  */
209
+ public function isDisableAdminLogin() {
 
210
  return ('1' === $this->disableAdminLogin);
211
  }
212
 
213
  /**
214
  * @param bool $disableAdminLogin
215
  */
216
+ public function setDisableAdminLogin( $disableAdminLogin ) {
 
217
  $this->disableAdminLogin = $disableAdminLogin;
218
  }
219
 
220
  /**
221
  * @return bool
222
  */
223
+ public function isWpSubDirectory() {
 
224
  return ('1' === $this->wpSubDirectory);
225
  }
226
 
227
  /**
228
  * @param bool $wpSubDirectory
229
  */
230
+ public function setWpSubDirectory( $wpSubDirectory ) {
 
231
  $this->wpSubDirectory = $wpSubDirectory;
232
  }
233
 
234
  /**
235
  * @return bool
236
  */
237
+ public function isCheckDirectorySize() {
 
238
  return ('1' === $this->checkDirectorySize);
239
  }
240
 
241
  /**
242
  * @param bool $checkDirectorySize
243
  */
244
+ public function setCheckDirectorySize( $checkDirectorySize ) {
 
245
  $this->checkDirectorySize = $checkDirectorySize;
246
  }
247
 
248
  /**
249
  * @return bool
250
  */
251
+ public function isDebugMode() {
 
252
  return ('1' === $this->debugMode);
253
  }
254
 
255
  /**
256
  * @param bool $debugMode
257
  */
258
+ public function setDebugMode( $debugMode ) {
 
259
  $this->debugMode = $debugMode;
260
  }
261
 
262
  /**
263
  * @return array
264
  */
265
+ public function getBlackListedPlugins() {
 
266
  return $this->blackListedPlugins;
267
  }
268
 
269
  /**
270
  * @param array $blackListedPlugins
271
  */
272
+ public function setBlackListedPlugins( $blackListedPlugins ) {
 
273
  $this->blackListedPlugins = $blackListedPlugins;
274
  }
275
  }
apps/Core/Utils/Cache.php CHANGED
@@ -111,6 +111,7 @@ class Cache
111
 
112
  // Save it to file
113
  return (file_put_contents($cacheFile, @serialize($value), LOCK_EX) !== false);
 
114
  }
115
 
116
  /**
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
  /**
apps/Core/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
  /**
30
  * Plugin version
31
  */
32
- const VERSION = "2.1.1";
33
 
34
  /**
35
  * Plugin name
29
  /**
30
  * Plugin version
31
  */
32
+ const VERSION = "2.1.2";
33
 
34
  /**
35
  * Plugin name
readme.txt CHANGED
@@ -1,258 +1,270 @@
1
- === WP Staging - DB & File Duplicator & Migration ===
2
-
3
- Author URL: https://wordpress.org/plugins/wp-staging
4
- Plugin URL: https://wordpress.org/plugins/wp-staging
5
- Contributors: ReneHermi, WP-Staging
6
- Donate link: https://wordpress.org/plugins/wp-staging
7
- License: GPLv2 or later
8
- License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
- Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
- Requires at least: 3.6+
11
- Tested up to: 4.8
12
- Stable tag: 2.1.1
13
-
14
- A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
15
-
16
- == Description ==
17
-
18
- <strong>This cloning and staging plugin is well tested but work in progress. <br><br>
19
- If you find any issue, please open a [support ticket](https://wp-staging.com/support/ "support ticket").
20
- </strong>
21
- <br /><br />
22
- <strong>Note: </strong> For pushing plugins and theme files to live site, check out [https://wp-staging.com/](https://wp-staging.com/ "WP Staging Pro")
23
- <br /><br />
24
- <blockquote>
25
- <h4> WP Staging for WordPress Migration </h4>
26
- This duplicator plugin allows you to create an staging or development environment in seconds* <br /> <br />
27
- It creates a clone of your website into a subfolder of your main WordPress installation including an entire copy of your database.
28
- This sounds pretty simple and yes it is! All the hard time-consumptive database and file copying stuff including url replacements is done in the background.
29
- <br /> <br />
30
- I created this plugin because all other solutions are way too complex, overloaded with dozens of options or having server requirements which are not available on most shared hosting solutions.
31
- All these reasons prevent user from testing new plugins and updates first before installing them on their live website, so its time to release a plugin which has the potential to be merged into everyone´s wordpress workflow.
32
- <br /><br />
33
- <p><small><em>* Time of creation depends on size of your database and file size</em></small></p>
34
- </blockquote>
35
-
36
- WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!
37
-
38
- [youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
39
-
40
- = Main Features =
41
-
42
- * <strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!
43
- * <strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power
44
- * <strong>Safe: </strong> Access to staging site is granted for administrators only.
45
- <br /><br />
46
- <strong>More safe:</strong>
47
- <br>
48
- * Admin bar reflects that you are working on a staging site
49
- * Extensive logging if duplication and migration process fails.
50
-
51
- = What does not work or is not tested when running wordpress migration? =
52
-
53
- * Wordpress migration of wordpress multisites (not tested)
54
- * WordPress duplicating process on windows server (not tested but will probably work)
55
- Edit: Duplication on windows server seems to be working well: [Read more](https://wordpress.org/support/topic/wont-copy-files?replies=5 "Read more")
56
-
57
-
58
- <strong>Change your workflow of updating themes and plugins data:</strong>
59
-
60
- 1. Use WP Staging for migration of a production website to a clone site for staging purposes
61
- 2. Customize theme, configuration and plugins or install new plugins
62
- 3. Test everything on your staging site first
63
- 4. Everything running as expected? You are on the save side for migration of all these modifications to your production site!
64
-
65
-
66
- <h3> Why should i use a staging website? </h3>
67
-
68
- Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.
69
- When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.
70
- This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)
71
-
72
- Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a
73
- up-to-date copy of your website.
74
-
75
- Some people are also afraid of installing plugins updates because they follow the rule "never touch a running system" with having in mind that untested updates are increasing the risk of breaking their site.
76
- I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior.
77
-
78
- <strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
79
-
80
- <h3> Can´t i just use my local wordpress development copy for testing like xampp / lampp? </h3>
81
-
82
- Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect
83
- of your local copy is working on your live website exactely as you would expect it.
84
- There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the
85
- the cpu performance can lead to unexpected results on your production website.
86
- There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform.
87
-
88
- This is were WP Staging steps in... Site cloning and staging site creation simplified!
89
-
90
- <h3>I just want to migrate the database from one installation to another</h3>
91
- If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.
92
- WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.
93
- Both tools are excellent cooperating eachother.
94
-
95
- <h3>What are the benefits compared to a plugin like Duplicator?</h3>
96
- At first, i love the [Duplicator plugin](https://wordpress.org/plugins/duplicator/ "Duplicator plugin"). Duplicator is a great tool for migrating from development site to production one or from production site to development one.
97
- The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.
98
- However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful.
99
-
100
- So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using
101
- the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!
102
-
103
- = I need you feedback =
104
- This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:
105
- Please open a [support request](https://wordpress.org/support/plugin/wp-staging/ "support request") and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines.
106
-
107
-
108
- = Important =
109
-
110
- Per default the staging site will have permalinks disabled because the staging site will be cloned into a subfolder and regular permalinks are not working
111
- without doing changes to your .htaccess or nginx.conf.
112
- In the majority of cases this is abolutely fine for a staging platform and you still will be able to test new plugins and do some theme changes on your staging platform.
113
- If you need the same permalink stucture on your staging platform as you have in your prodcution website you have to create a custom .htaccess for apache webserver
114
- or to adjust your nginx.conf.
115
-
116
-
117
- = How to install and setup? =
118
- Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.
119
- After installation goto the settings page 'Staging' and do your adjustments there.
120
-
121
-
122
- == Frequently Asked Questions ==
123
-
124
-
125
- == Official Site ==
126
- https://wp-staging.com
127
-
128
- == Installation ==
129
- 1. Download the file "wp-staging" , unzip and place it in your wp-content/plugins/wp-staging folder. You can alternatively upload and install it via the WordPress plugin backend.
130
- 2. Activate the plugin through the 'Plugins' menu in WordPress.
131
- 3. Start Plugins->Staging
132
-
133
- == Screenshots ==
134
-
135
- 1. Step 1. Create new WordPress staging site
136
- 2. Step 2. Scanning your website for files and database tables
137
- 3. Step 3. Wordpress Staging site creation in progress
138
- 4. Finish!
139
-
140
- == Changelog ==
141
-
142
-
143
-
144
- = 2.1.1 =
145
- * New: Add link to tutorial explaining the process of pushing modification to the live site
146
-
147
- = 2.1.0 =
148
- * New: Exclude unneccessary files from cloning process: .tmp, .log, .htaccess, .git, .gitignore, desktop.ini, .DS_Store, .svn
149
- * New: More details for debugging in Tools->System Info
150
- * Fix: Check if tables in staging site exists before attempting to modify them
151
- * Fix: WordPress in sub directories were not opening
152
- * 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
153
- * Fix: Access to staging site not working, if WP_SITEURL and WP_HOME is defined in wp-config.php
154
- * Tweak: Exclude wp-content/cache folder from copying process
155
-
156
-
157
- = 2.0.9 =
158
- * Skip Version
159
-
160
- = 2.0.8 =
161
- * 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
162
-
163
- = 2.0.7 =
164
- * Fix: After update from wpstg 1.6.x to 2.x previous settings were not imported resulting in cancelation of cloning process
165
-
166
-
167
- = 2.0.6 =
168
- * Fix: Cancel Cloning button not working
169
- * Fix: Limit max execution time to a maximum of 30sec to prevent high memory consumption and script timeouts
170
-
171
-
172
- = 2.0.5 =
173
- * New: Major version - Complete rewrite of the code base
174
- * New: Batch processing allows to clone even huge sites without any timeouts
175
- * New: Preparation for WP QUADS PRO with ability to copy file changes back to live site
176
- * New: Bypass (broken) third party plugins during wp staging related ajax requests to prevent processing errors. Use a mu plugin for this.
177
-
178
- = 1.1.6 =
179
- * New: Add download link to WP Staging Beta Version 2.0.1
180
-
181
- = 1.1.5 =
182
- * Fix: Admin notice is throwing a false positive write permission error
183
- * New: Move log folder to wp-content/uploads/wp-staging/logs
184
- * New: Tested up to WP 4.7.3
185
-
186
- = 1.1.4 =
187
- * Fix: Fatal error Unsupported operand types
188
-
189
- = 1.1.3 =
190
- * New: Tested up to wp 4.7.2
191
- * Fix: Arrows in drop down for folder selection are distorted
192
- * Tweak: Show working log as default to make debugging easier
193
-
194
- = 1.1.2 =
195
- * Fix: Settings are not deleted when plugin is removed
196
- * Fix: Staging site is available for non administrators
197
-
198
- = 1.1.1 =
199
- * Fix: Change rating url
200
-
201
- = 1.1.0 =
202
- * New: Tested up to WP 4.6
203
- * New: Create a poll and ask what feature is most required
204
-
205
- = 1.0.9 =
206
- * Fix: Undefined WPSTG() warning
207
- * Fix: Change compatibility version to wp 4.5.3
208
-
209
- = 1.0.8 =
210
- * Tested up to WP 4.5.2
211
-
212
- = 1.0.7 =
213
- * Fix: Activation hook is not fired and staging site is not working properly
214
- * Performance: Increase default query copy limit to 1000
215
-
216
- = 1.0.6 =
217
- * Fix: Uninstalling plugin throwing error
218
- * Fix: Error permission admin notice although permission issues are correct
219
-
220
-
221
- = 1.0.5 =
222
- * New: Tested up to WP 4.5
223
- * Fix: Download system log not working
224
- * Fix: Click on Optimizer "Select all | none | invert" links leads to jumping
225
- * Tweak: Make clear that unselecting a checkbox will exlude table or file from copy process
226
- * Tweak: Remove unnecessary text
227
- * Tweak: Change beta notice in dashboard. WP Staging is stable
228
- * Tweak: Change twitter handle to @wpstg
229
-
230
- = 1.0.3 =
231
- * Fix: Missing const MASHFS_VERSION
232
- * Fix: Remove error "table XY has been created, BUT inserting rows failed."
233
- * Fix: Not tested up to 4.4.2 message shown although it's tested up to WP 4.4.2
234
- * New: Disable either free or pro version and does not allow to have both version enabled at the same time
235
-
236
- = 1.0.2 =
237
- * Tweak: Change setting description of uninstall option
238
- * Tweak: Lower tags in readme.txt
239
-
240
- = 1.0.1 =
241
- * New: Orange colored admin bar on staging site for better visualization and comparision between production live site and staging site
242
- * Tweak: Remove contact link on multisite notification
243
-
244
- = 1.0.0 =
245
- * Fix: Do not follow symlinks during file copy process
246
- * Fix: css error
247
- * Fix: Show "not-compatible" notice only when blog version is higher than plugin tested version.
248
- * Fix: undefined var $size
249
- * Fix: Check if $path is null before writing to remaining_files.json
250
- * Fix: $db_helper undefined message
251
- * Fix: Skip non utf8 encoded files during copying process
252
-
253
- Complete changelog: [https://wp-staging.com/changelog.txt](https://wp-staging.com/changelog.txt)
254
-
255
- == Upgrade Notice ==
256
-
257
- = 2.0.2 =
258
- 2.0.2 * New: Batch processing allows to clone even huge sites without any timeouts
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP Staging - DB & File Duplicator & Migration ===
2
+
3
+ Author URL: https://wordpress.org/plugins/wp-staging
4
+ Plugin URL: https://wordpress.org/plugins/wp-staging
5
+ Contributors: ReneHermi, WP-Staging
6
+ Donate link: https://wordpress.org/plugins/wp-staging
7
+ License: GPLv2 or later
8
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
+ Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
+ Requires at least: 3.6+
11
+ Tested up to: 4.8
12
+ Stable tag: 2.1.2
13
+
14
+ A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
15
+
16
+ == Description ==
17
+
18
+ <strong>This cloning and staging plugin is well tested but work in progress. <br><br>
19
+ If you find any issue, please open a [support ticket](https://wp-staging.com/support/ "support ticket").
20
+ </strong>
21
+ <br /><br />
22
+ <strong>Note: </strong> For pushing plugins and theme files to live site, check out [https://wp-staging.com/](https://wp-staging.com/ "WP Staging Pro")
23
+ <br /><br />
24
+ <blockquote>
25
+ <h4> WP Staging for WordPress Migration </h4>
26
+ This duplicator plugin allows you to create an staging or development environment in seconds* <br /> <br />
27
+ It creates a clone of your website into a subfolder of your main WordPress installation including an entire copy of your database.
28
+ This sounds pretty simple and yes it is! All the hard time-consumptive database and file copying stuff including url replacements is done in the background.
29
+ <br /> <br />
30
+ I created this plugin because all other solutions are way too complex, overloaded with dozens of options or having server requirements which are not available on most shared hosting solutions.
31
+ All these reasons prevent user from testing new plugins and updates first before installing them on their live website, so its time to release a plugin which has the potential to be merged into everyone´s wordpress workflow.
32
+ <br /><br />
33
+ <p><small><em>* Time of creation depends on size of your database and file size</em></small></p>
34
+ </blockquote>
35
+
36
+ WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!
37
+
38
+ [youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
39
+
40
+ = Main Features =
41
+
42
+ * <strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!
43
+ * <strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power
44
+ * <strong>Safe: </strong> Access to staging site is granted for administrators only.
45
+ <br /><br />
46
+ <strong>More safe:</strong>
47
+ <br>
48
+ * Admin bar reflects that you are working on a staging site
49
+ * Extensive logging if duplication and migration process fails.
50
+
51
+ = What does not work or is not tested when running wordpress migration? =
52
+
53
+ * Wordpress migration of wordpress multisites (not tested)
54
+ * WordPress duplicating process on windows server (not tested but will probably work)
55
+ Edit: Duplication on windows server seems to be working well: [Read more](https://wordpress.org/support/topic/wont-copy-files?replies=5 "Read more")
56
+
57
+
58
+ <strong>Change your workflow of updating themes and plugins data:</strong>
59
+
60
+ 1. Use WP Staging for migration of a production website to a clone site for staging purposes
61
+ 2. Customize theme, configuration and plugins or install new plugins
62
+ 3. Test everything on your staging site first
63
+ 4. Everything running as expected? You are on the save side for migration of all these modifications to your production site!
64
+
65
+
66
+ <h3> Why should i use a staging website? </h3>
67
+
68
+ Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.
69
+ When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.
70
+ This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)
71
+
72
+ Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a
73
+ up-to-date copy of your website.
74
+
75
+ Some people are also afraid of installing plugins updates because they follow the rule "never touch a running system" with having in mind that untested updates are increasing the risk of breaking their site.
76
+ I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior.
77
+
78
+ <strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
79
+
80
+ <h3> Can´t i just use my local wordpress development copy for testing like xampp / lampp? </h3>
81
+
82
+ Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect
83
+ of your local copy is working on your live website exactely as you would expect it.
84
+ There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the
85
+ the cpu performance can lead to unexpected results on your production website.
86
+ There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform.
87
+
88
+ This is were WP Staging steps in... Site cloning and staging site creation simplified!
89
+
90
+ <h3>I just want to migrate the database from one installation to another</h3>
91
+ If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.
92
+ WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.
93
+ Both tools are excellent cooperating eachother.
94
+
95
+ <h3>What are the benefits compared to a plugin like Duplicator?</h3>
96
+ At first, i love the [Duplicator plugin](https://wordpress.org/plugins/duplicator/ "Duplicator plugin"). Duplicator is a great tool for migrating from development site to production one or from production site to development one.
97
+ The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.
98
+ However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful.
99
+
100
+ So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using
101
+ the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!
102
+
103
+ = I need you feedback =
104
+ This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:
105
+ Please open a [support request](https://wordpress.org/support/plugin/wp-staging/ "support request") and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines.
106
+
107
+
108
+ = Important =
109
+
110
+ Per default the staging site will have permalinks disabled because the staging site will be cloned into a subfolder and regular permalinks are not working
111
+ without doing changes to your .htaccess or nginx.conf.
112
+ In the majority of cases this is abolutely fine for a staging platform and you still will be able to test new plugins and do some theme changes on your staging platform.
113
+ If you need the same permalink stucture on your staging platform as you have in your prodcution website you have to create a custom .htaccess for apache webserver
114
+ or to adjust your nginx.conf.
115
+
116
+
117
+ = How to install and setup? =
118
+ Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.
119
+ After installation goto the settings page 'Staging' and do your adjustments there.
120
+
121
+
122
+ == Frequently Asked Questions ==
123
+
124
+
125
+ == Official Site ==
126
+ https://wp-staging.com
127
+
128
+ == Installation ==
129
+ 1. Download the file "wp-staging" , unzip and place it in your wp-content/plugins/wp-staging folder. You can alternatively upload and install it via the WordPress plugin backend.
130
+ 2. Activate the plugin through the 'Plugins' menu in WordPress.
131
+ 3. Start Plugins->Staging
132
+
133
+ == Screenshots ==
134
+
135
+ 1. Step 1. Create new WordPress staging site
136
+ 2. Step 2. Scanning your website for files and database tables
137
+ 3. Step 3. Wordpress Staging site creation in progress
138
+ 4. Finish!
139
+
140
+ == Changelog ==
141
+
142
+ = 2.1.2 =
143
+ * Fix: Remove LOCK_EX parameter in file_put_contents(). LOCK_EX is not working on several systems which results in cloning process timeouts
144
+ * Fix: Huge Performance improvement in copying process by removing duplicate file entries in the cache file. This also prevents weird timeout issues on some hosted websites
145
+ * Fix: Error 500 when debug mode is activated
146
+ * Fix: Limit maximum execution time to 30 seconds
147
+ * Fix: Sanitize Clone Names and Keys to fix "clone not found" issue in upgrade routine
148
+ * Fix: Do not clone the plugin wps-hide-login
149
+ * Fix: Staging sites can not be deleted if they are very big
150
+ * Fix: Link to staging site is undefined
151
+ * Tweak: Better admin message for asking for a review
152
+ * Tweak: Remove table wpstg_rmpermalinks_executed when plugin is uninstalled
153
+ * New: New setting to specify the maximum amount of files copied within one ajax call to fix godaddy and bluehost ajax 404 errors. Default 10 per batch
154
+
155
+
156
+ = 2.1.1 =
157
+ * New: Add link to tutorial explaining the process of pushing modification to the live site
158
+
159
+ = 2.1.0 =
160
+ * New: Exclude unneccessary files from cloning process: .tmp, .log, .htaccess, .git, .gitignore, desktop.ini, .DS_Store, .svn
161
+ * New: More details for debugging in Tools->System Info
162
+ * Fix: Check if tables in staging site exists before attempting to modify them
163
+ * Fix: WordPress in sub directories were not opening
164
+ * 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
165
+ * Fix: Access to staging site not working, if WP_SITEURL and WP_HOME is defined in wp-config.php
166
+ * Tweak: Exclude wp-content/cache folder from copying process
167
+
168
+
169
+ = 2.0.9 =
170
+ * Skip Version
171
+
172
+ = 2.0.8 =
173
+ * 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
174
+
175
+ = 2.0.7 =
176
+ * Fix: After update from wpstg 1.6.x to 2.x previous settings were not imported resulting in cancelation of cloning process
177
+
178
+
179
+ = 2.0.6 =
180
+ * Fix: Cancel Cloning button not working
181
+ * Fix: Limit max execution time to a maximum of 30sec to prevent high memory consumption and script timeouts
182
+
183
+
184
+ = 2.0.5 =
185
+ * New: Major version - Complete rewrite of the code base
186
+ * New: Batch processing allows to clone even huge sites without any timeouts
187
+ * New: Preparation for WP QUADS PRO with ability to copy file changes back to live site
188
+ * New: Bypass (broken) third party plugins during wp staging related ajax requests to prevent processing errors. Use a mu plugin for this.
189
+
190
+ = 1.1.6 =
191
+ * New: Add download link to WP Staging Beta Version 2.0.1
192
+
193
+ = 1.1.5 =
194
+ * Fix: Admin notice is throwing a false positive write permission error
195
+ * New: Move log folder to wp-content/uploads/wp-staging/logs
196
+ * New: Tested up to WP 4.7.3
197
+
198
+ = 1.1.4 =
199
+ * Fix: Fatal error Unsupported operand types
200
+
201
+ = 1.1.3 =
202
+ * New: Tested up to wp 4.7.2
203
+ * Fix: Arrows in drop down for folder selection are distorted
204
+ * Tweak: Show working log as default to make debugging easier
205
+
206
+ = 1.1.2 =
207
+ * Fix: Settings are not deleted when plugin is removed
208
+ * Fix: Staging site is available for non administrators
209
+
210
+ = 1.1.1 =
211
+ * Fix: Change rating url
212
+
213
+ = 1.1.0 =
214
+ * New: Tested up to WP 4.6
215
+ * New: Create a poll and ask what feature is most required
216
+
217
+ = 1.0.9 =
218
+ * Fix: Undefined WPSTG() warning
219
+ * Fix: Change compatibility version to wp 4.5.3
220
+
221
+ = 1.0.8 =
222
+ * Tested up to WP 4.5.2
223
+
224
+ = 1.0.7 =
225
+ * Fix: Activation hook is not fired and staging site is not working properly
226
+ * Performance: Increase default query copy limit to 1000
227
+
228
+ = 1.0.6 =
229
+ * Fix: Uninstalling plugin throwing error
230
+ * Fix: Error permission admin notice although permission issues are correct
231
+
232
+
233
+ = 1.0.5 =
234
+ * New: Tested up to WP 4.5
235
+ * Fix: Download system log not working
236
+ * Fix: Click on Optimizer "Select all | none | invert" links leads to jumping
237
+ * Tweak: Make clear that unselecting a checkbox will exlude table or file from copy process
238
+ * Tweak: Remove unnecessary text
239
+ * Tweak: Change beta notice in dashboard. WP Staging is stable
240
+ * Tweak: Change twitter handle to @wpstg
241
+
242
+ = 1.0.3 =
243
+ * Fix: Missing const MASHFS_VERSION
244
+ * Fix: Remove error "table XY has been created, BUT inserting rows failed."
245
+ * Fix: Not tested up to 4.4.2 message shown although it's tested up to WP 4.4.2
246
+ * New: Disable either free or pro version and does not allow to have both version enabled at the same time
247
+
248
+ = 1.0.2 =
249
+ * Tweak: Change setting description of uninstall option
250
+ * Tweak: Lower tags in readme.txt
251
+
252
+ = 1.0.1 =
253
+ * New: Orange colored admin bar on staging site for better visualization and comparision between production live site and staging site
254
+ * Tweak: Remove contact link on multisite notification
255
+
256
+ = 1.0.0 =
257
+ * Fix: Do not follow symlinks during file copy process
258
+ * Fix: css error
259
+ * Fix: Show "not-compatible" notice only when blog version is higher than plugin tested version.
260
+ * Fix: undefined var $size
261
+ * Fix: Check if $path is null before writing to remaining_files.json
262
+ * Fix: $db_helper undefined message
263
+ * Fix: Skip non utf8 encoded files during copying process
264
+
265
+ Complete changelog: [https://wp-staging.com/changelog.txt](https://wp-staging.com/changelog.txt)
266
+
267
+ == Upgrade Notice ==
268
+
269
+ = 2.1.2 =
270
+ 2.1.2 * Lot of modifications to increase speed and reliability for crewating staging sites
uninstall.php CHANGED
@@ -14,75 +14,75 @@ use WPStaging\Backend\Optimizer\Optimizer;
14
  * @since 0.9.0
15
  */
16
  // No direct access
17
- if( !defined( 'WP_UNINSTALL_PLUGIN' ) ) {
18
  exit;
19
  }
20
 
21
  class uninstall {
22
 
23
- public function __construct() {
24
-
25
- // Plugin Folder Path
26
- if( !defined( 'WPSTG_PLUGIN_DIR' ) ) {
27
- define( 'WPSTG_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
28
- }
29
-
30
- /**
31
- * Path to main WP Staging class
32
- * Make sure to not redeclare class in case free version has been installed previosly
33
- */
34
- if( !class_exists( 'WPStaging\WPStaging' ) ) {
35
- require_once plugin_dir_path( __FILE__ ) . "apps/Core/WPStaging.php";
36
- }
37
- $wpStaging = \WPStaging\WPStaging::getInstance();
38
-
39
- // Delete our must use plugin
40
- $this->deleteMuPlugin();
41
-
42
- $this->init();
43
- }
44
-
45
- private function init() {
46
- $options = json_decode( json_encode( get_option( "wpstg_settings", array() ) ) );
47
-
48
- if( isset( $options->unInstallOnDelete ) && '1' === $options->unInstallOnDelete ) {
49
- // Delete options
50
- delete_option( "wpstg_version_upgraded_from" );
51
- delete_option( "wpstg_version" );
52
- delete_option( "wpstg_installDate" );
53
- delete_option( "wpstg_firsttime" );
54
- delete_option( "wpstg_is_staging_site" );
55
- delete_option( "wpstg_settings" );
56
-
57
- /* Do not delete these fields without actually deleting the staging site
58
- * @create a delete routine which deletes the staging sites first
59
- */
60
- //delete_option( "wpstg_existing_clones" );
61
- //delete_option( "wpstg_existing_clones_beta" );
62
-
63
- // Old wpstg 1.3 options for admin notices
64
- delete_option( "wpstg_start_poll" );
65
- delete_option( "wpstg_hide_beta" );
66
- delete_option( "wpstg_RatingDiv" );
67
-
68
- // New 2.x options for admin notices
69
- delete_option( "wpstg_poll" );
70
- delete_option( "wpstg_rating" );
71
- delete_option( "wpstg_beta" );
72
-
73
- // Delete events
74
- wp_clear_scheduled_hook( 'wpstg_weekly_event' );
75
-
76
- }
77
- }
78
-
79
- /**
80
- * delete MuPlugin
81
- */
82
- private function deleteMuPlugin() {
83
- $optimizer = new Optimizer;
84
- $optimizer->unstallOptimizer();
85
- }
86
 
87
  }
88
 
14
  * @since 0.9.0
15
  */
16
  // No direct access
17
+ if (!defined('WP_UNINSTALL_PLUGIN')) {
18
  exit;
19
  }
20
 
21
  class uninstall {
22
 
23
+ public function __construct() {
24
+
25
+ // Plugin Folder Path
26
+ if (!defined('WPSTG_PLUGIN_DIR')) {
27
+ define('WPSTG_PLUGIN_DIR', plugin_dir_path(__FILE__));
28
+ }
29
+
30
+ /**
31
+ * Path to main WP Staging class
32
+ * Make sure to not redeclare class in case free version has been installed previosly
33
+ */
34
+ if (!class_exists('WPStaging\WPStaging')) {
35
+ require_once plugin_dir_path(__FILE__) . "apps/Core/WPStaging.php";
36
+ }
37
+ $wpStaging = \WPStaging\WPStaging::getInstance();
38
+
39
+ // Delete our must use plugin
40
+ $this->deleteMuPlugin();
41
+
42
+ $this->init();
43
+ }
44
+
45
+ private function init() {
46
+
47
+ $options = json_decode(json_encode(get_option("wpstg_settings", array())));
48
+
49
+ if (isset($options->unInstallOnDelete) && '1' === $options->unInstallOnDelete) {
50
+ // Delete options
51
+ delete_option("wpstg_version_upgraded_from");
52
+ delete_option("wpstg_version");
53
+ delete_option("wpstg_installDate");
54
+ delete_option("wpstg_firsttime");
55
+ delete_option("wpstg_is_staging_site");
56
+ delete_option("wpstg_settings");
57
+ delete_option("wpstg_rmpermalinks_executed");
58
+
59
+ /* Do not delete these fields without actually deleting the staging site
60
+ * @create a delete routine which deletes the staging sites first
61
+ */
62
+ //delete_option( "wpstg_existing_clones" );
63
+ //delete_option( "wpstg_existing_clones_beta" );
64
+ // Old wpstg 1.3 options for admin notices
65
+ delete_option("wpstg_start_poll");
66
+ delete_option("wpstg_hide_beta");
67
+ delete_option("wpstg_RatingDiv");
68
+
69
+ // New 2.x options for admin notices
70
+ delete_option("wpstg_poll");
71
+ delete_option("wpstg_rating");
72
+ delete_option("wpstg_beta");
73
+
74
+ // Delete events
75
+ wp_clear_scheduled_hook('wpstg_weekly_event');
76
+ }
77
+ }
78
+
79
+ /**
80
+ * delete MuPlugin
81
+ */
82
+ private function deleteMuPlugin() {
83
+ $optimizer = new Optimizer;
84
+ $optimizer->unstallOptimizer();
85
+ }
86
 
87
  }
88
 
wp-staging.php CHANGED
@@ -7,7 +7,7 @@
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
- * Version: 2.1.1
11
  * Text Domain: wpstg
12
  * Domain Path: /languages/
13
 
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
+ * Version: 2.1.2
11
  * Text Domain: wpstg
12
  * Domain Path: /languages/
13