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

Version Description

  • Fix: Do not search & replace mail addresses
Download this release

Release Info

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

Code changes from version 2.3.1 to 2.3.2

apps/Backend/Modules/Jobs/Cloning.php CHANGED
@@ -1,275 +1,275 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
- use WPStaging\WPStaging;
6
-
7
- /**
8
- * Class Cloning
9
- * @package WPStaging\Backend\Modules\Jobs
10
- */
11
- class Cloning extends Job
12
- {
13
- /**
14
- * Initialize is called in \Job
15
- */
16
- public function initialize(){
17
- $this->db = WPStaging::getInstance()->get("wpdb");
18
- }
19
-
20
- /**
21
- * Save Chosen Cloning Settings
22
- * @return bool
23
- */
24
- public function save()
25
- {
26
- if (!isset($_POST) || !isset($_POST["cloneID"]))
27
- {
28
- return false;
29
- }
30
-
31
- // Generate Options
32
- // Clone
33
- $this->options->clone = $_POST["cloneID"];
34
- $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
35
- $this->options->cloneNumber = 1;
36
- $this->options->prefix = $this->setStagingPrefix();
37
- $this->options->includedDirectories = array();
38
- $this->options->excludedDirectories = array();
39
- $this->options->extraDirectories = array();
40
- $this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
41
- $this->options->currentStep = 0;
42
-
43
- // Job
44
- $this->options->job = new \stdClass();
45
-
46
- // Check if clone data already exists and use that one
47
- if (isset($this->options->existingClones[$this->options->clone]) )
48
- {
49
-
50
- $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]->number;
51
-
52
- $this->options->prefix = isset($this->options->existingClones[$this->options->clone]->prefix) ?
53
- $this->options->existingClones[$this->options->clone]->prefix :
54
- $this->setStagingPrefix();
55
-
56
- } // Clone does not exist but there are other clones in db
57
- // Get data and increment it
58
- elseif (!empty($this->options->existingClones))
59
- {
60
- $this->options->cloneNumber = count($this->options->existingClones)+1;
61
- $this->options->prefix = $this->setStagingPrefix();
62
- }
63
-
64
- // Included Tables
65
- if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"]))
66
- {
67
- $this->options->tables = $_POST["includedTables"];
68
- } else {
69
- $this->options->tables = array();
70
- }
71
- // Excluded Tables
72
- // if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
73
- // {
74
- // $this->options->excludedTables = $_POST["excludedTables"];
75
- // }
76
-
77
- // Excluded Directories
78
- if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
79
- {
80
- $this->options->excludedDirectories = $_POST["excludedDirectories"];
81
- }
82
-
83
-
84
- // Excluded Directories TOTAL
85
- // Do not copy these folders and plugins
86
- $excludedDirectories = array(
87
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
88
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
89
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wp-super-cache',
90
- ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'peters-login-redirect',
91
- );
92
-
93
- $this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
94
-
95
- // Included Directories
96
- if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
97
- {
98
- $this->options->includedDirectories = $_POST["includedDirectories"];
99
- }
100
-
101
- // Extra Directories
102
- if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
103
- {
104
- $this->options->extraDirectories = $_POST["extraDirectories"];
105
- }
106
-
107
- // Directories to Copy
108
- $this->options->directoriesToCopy = array_merge(
109
- $this->options->includedDirectories,
110
- $this->options->extraDirectories
111
- );
112
-
113
- array_unshift($this->options->directoriesToCopy, ABSPATH);
114
-
115
- // Delete files to copy listing
116
- $this->cache->delete("files_to_copy");
117
-
118
- return $this->saveOptions();
119
- }
120
-
121
- /**
122
- * Create a new staging prefix which does not already exists in database
123
- */
124
- private function setStagingPrefix() {
125
-
126
- // Get & find a new prefix that does not already exist in database.
127
- // Loop through up to 1000 different possible prefixes should be enough here;)
128
- for($i=0; $i <= 10000; $i++){
129
- $this->options->prefix = isset($this->options->existingClones) ?
130
- 'wpstg' . (count($this->options->existingClones)+$i) . '_' :
131
- 'wpstg' . $i . '_';
132
-
133
- $sql = "SHOW TABLE STATUS LIKE '{$this->options->prefix}%'";
134
- $tables = $this->db->get_results($sql);
135
-
136
- // Prefix does not exists. We can use it
137
- if (!$tables){
138
- //$this->returnException('new ' . $this->options->prefix);
139
- return $this->options->prefix;
140
- }
141
- }
142
- $this->returnException("Fatal Error: Can not create staging prefix. '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
143
- wp_die("Fatal Error: Can not create staging prefix. Prefix '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
144
- }
145
-
146
- /**
147
- * Check if potential new prefix of staging site would be identical with live site.
148
- * @return boolean
149
- */
150
- private function isPrefixIdentical(){
151
- $db = WPStaging::getInstance()->get("wpdb");
152
-
153
- $livePrefix = $db->prefix;
154
- $stagingPrefix = $this->options->prefix;
155
-
156
- if ($livePrefix == $stagingPrefix){
157
- return true;
158
- }
159
- return false;
160
- }
161
-
162
- /**
163
- * Start the cloning job
164
- */
165
- public function start()
166
- {
167
- if (null === $this->options->currentJob)
168
- {
169
- $this->log("Cloning job for {$this->options->clone} finished");
170
- return true;
171
- }
172
-
173
- $methodName = "job" . ucwords($this->options->currentJob);
174
-
175
- if (!method_exists($this, $methodName))
176
- {
177
- $this->log("Can't execute job; Job's method {$methodName} is not found");
178
- throw new JobNotFoundException($methodName);
179
- }
180
-
181
- // Call the job
182
- //$this->log("execute job: Job's method {$methodName}");
183
- return $this->{$methodName}();
184
- }
185
-
186
- /**
187
- * @param object $response
188
- * @param string $nextJob
189
- * @return object
190
- */
191
- private function handleJobResponse($response, $nextJob)
192
- {
193
- // Job is not done
194
- if (true !== $response->status)
195
- {
196
- return $response;
197
- }
198
-
199
- $this->options->currentJob = $nextJob;
200
- $this->options->currentStep = 0;
201
- $this->options->totalSteps = 0;
202
-
203
- // Save options
204
- $this->saveOptions();
205
-
206
- return $response;
207
- }
208
-
209
-
210
-
211
-
212
-
213
- /**
214
- * Clone Database
215
- * @return object
216
- */
217
- public function jobDatabase()
218
- {
219
- $database = new Database();
220
- return $this->handleJobResponse($database->start(), "SearchReplace");
221
- }
222
-
223
- /**
224
- * Search & Replace
225
- * @return object
226
- */
227
- public function jobSearchReplace()
228
- {
229
- $searchReplace = new SearchReplace();
230
- return $this->handleJobResponse($searchReplace->start(), "directories");
231
- }
232
-
233
- /**
234
- * Get All Files From Selected Directories Recursively Into a File
235
- * @return object
236
- */
237
- public function jobDirectories()
238
- {
239
- $directories = new Directories();
240
- return $this->handleJobResponse($directories->start(), "files");
241
- }
242
-
243
- /**
244
- * Copy Files
245
- * @return object
246
- */
247
- public function jobFiles()
248
- {
249
- $files = new Files();
250
- return $this->handleJobResponse($files->start(), "data");
251
- }
252
-
253
- /**
254
- * Replace Data
255
- * @return object
256
- */
257
- public function jobData()
258
- {
259
- $data = new Data();
260
- return $this->handleJobResponse($data->start(), "finish");
261
- }
262
-
263
-
264
- /**
265
- * Save Clone Data
266
- * @return object
267
- */
268
- public function jobFinish()
269
- {
270
- $finish = new Finish();
271
- return $this->handleJobResponse($finish->start(), '');
272
- }
273
-
274
-
275
}
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
5
+ use WPStaging\WPStaging;
6
+
7
+ /**
8
+ * Class Cloning
9
+ * @package WPStaging\Backend\Modules\Jobs
10
+ */
11
+ class Cloning extends Job
12
+ {
13
+ /**
14
+ * Initialize is called in \Job
15
+ */
16
+ public function initialize(){
17
+ $this->db = WPStaging::getInstance()->get("wpdb");
18
+ }
19
+
20
+ /**
21
+ * Save Chosen Cloning Settings
22
+ * @return bool
23
+ */
24
+ public function save()
25
+ {
26
+ if (!isset($_POST) || !isset($_POST["cloneID"]))
27
+ {
28
+ return false;
29
+ }
30
+
31
+ // Generate Options
32
+ // Clone
33
+ $this->options->clone = $_POST["cloneID"];
34
+ $this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
35
+ $this->options->cloneNumber = 1;
36
+ $this->options->prefix = $this->setStagingPrefix();
37
+ $this->options->includedDirectories = array();
38
+ $this->options->excludedDirectories = array();
39
+ $this->options->extraDirectories = array();
40
+ $this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
41
+ $this->options->currentStep = 0;
42
+
43
+ // Job
44
+ $this->options->job = new \stdClass();
45
+
46
+ // Check if clone data already exists and use that one
47
+ if (isset($this->options->existingClones[$this->options->clone]) )
48
+ {
49
+
50
+ $this->options->cloneNumber = $this->options->existingClones[$this->options->clone]->number;
51
+
52
+ $this->options->prefix = isset($this->options->existingClones[$this->options->clone]->prefix) ?
53
+ $this->options->existingClones[$this->options->clone]->prefix :
54
+ $this->setStagingPrefix();
55
+
56
+ } // Clone does not exist but there are other clones in db
57
+ // Get data and increment it
58
+ elseif (!empty($this->options->existingClones))
59
+ {
60
+ $this->options->cloneNumber = count($this->options->existingClones)+1;
61
+ $this->options->prefix = $this->setStagingPrefix();
62
+ }
63
+
64
+ // Included Tables
65
+ if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"]))
66
+ {
67
+ $this->options->tables = $_POST["includedTables"];
68
+ } else {
69
+ $this->options->tables = array();
70
+ }
71
+ // Excluded Tables
72
+ // if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
73
+ // {
74
+ // $this->options->excludedTables = $_POST["excludedTables"];
75
+ // }
76
+
77
+ // Excluded Directories
78
+ if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
79
+ {
80
+ $this->options->excludedDirectories = $_POST["excludedDirectories"];
81
+ }
82
+
83
+
84
+ // Excluded Directories TOTAL
85
+ // Do not copy these folders and plugins
86
+ $excludedDirectories = array(
87
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
88
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
89
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wp-super-cache',
90
+ ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'peters-login-redirect',
91
+ );
92
+
93
+ $this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
94
+
95
+ // Included Directories
96
+ if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
97
+ {
98
+ $this->options->includedDirectories = $_POST["includedDirectories"];
99
+ }
100
+
101
+ // Extra Directories
102
+ if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
103
+ {
104
+ $this->options->extraDirectories = $_POST["extraDirectories"];
105
+ }
106
+
107
+ // Directories to Copy
108
+ $this->options->directoriesToCopy = array_merge(
109
+ $this->options->includedDirectories,
110
+ $this->options->extraDirectories
111
+ );
112
+
113
+ array_unshift($this->options->directoriesToCopy, ABSPATH);
114
+
115
+ // Delete files to copy listing
116
+ $this->cache->delete("files_to_copy");
117
+
118
+ return $this->saveOptions();
119
+ }
120
+
121
+ /**
122
+ * Create a new staging prefix which does not already exists in database
123
+ */
124
+ private function setStagingPrefix() {
125
+
126
+ // Get & find a new prefix that does not already exist in database.
127
+ // Loop through up to 1000 different possible prefixes should be enough here;)
128
+ for($i=0; $i <= 10000; $i++){
129
+ $this->options->prefix = isset($this->options->existingClones) ?
130
+ 'wpstg' . (count($this->options->existingClones)+$i) . '_' :
131
+ 'wpstg' . $i . '_';
132
+
133
+ $sql = "SHOW TABLE STATUS LIKE '{$this->options->prefix}%'";
134
+ $tables = $this->db->get_results($sql);
135
+
136
+ // Prefix does not exists. We can use it
137
+ if (!$tables){
138
+ //$this->returnException('new ' . $this->options->prefix);
139
+ return $this->options->prefix;
140
+ }
141
+ }
142
+ $this->returnException("Fatal Error: Can not create staging prefix. '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
143
+ wp_die("Fatal Error: Can not create staging prefix. Prefix '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
144
+ }
145
+
146
+ /**
147
+ * Check if potential new prefix of staging site would be identical with live site.
148
+ * @return boolean
149
+ */
150
+ private function isPrefixIdentical(){
151
+ $db = WPStaging::getInstance()->get("wpdb");
152
+
153
+ $livePrefix = $db->prefix;
154
+ $stagingPrefix = $this->options->prefix;
155
+
156
+ if ($livePrefix == $stagingPrefix){
157
+ return true;
158
+ }
159
+ return false;
160
+ }
161
+
162
+ /**
163
+ * Start the cloning job
164
+ */
165
+ public function start()
166
+ {
167
+ if (null === $this->options->currentJob)
168
+ {
169
+ $this->log("Cloning job for {$this->options->clone} finished");
170
+ return true;
171
+ }
172
+
173
+ $methodName = "job" . ucwords($this->options->currentJob);
174
+
175
+ if (!method_exists($this, $methodName))
176
+ {
177
+ $this->log("Can't execute job; Job's method {$methodName} is not found");
178
+ throw new JobNotFoundException($methodName);
179
+ }
180
+
181
+ // Call the job
182
+ //$this->log("execute job: Job's method {$methodName}");
183
+ return $this->{$methodName}();
184
+ }
185
+
186
+ /**
187
+ * @param object $response
188
+ * @param string $nextJob
189
+ * @return object
190
+ */
191
+ private function handleJobResponse($response, $nextJob)
192
+ {
193
+ // Job is not done
194
+ if (true !== $response->status)
195
+ {
196
+ return $response;
197
+ }
198
+
199
+ $this->options->currentJob = $nextJob;
200
+ $this->options->currentStep = 0;
201
+ $this->options->totalSteps = 0;
202
+
203
+ // Save options
204
+ $this->saveOptions();
205
+
206
+ return $response;
207
+ }
208
+
209
+
210
+
211
+
212
+
213
+ /**
214
+ * Clone Database
215
+ * @return object
216
+ */
217
+ public function jobDatabase()
218
+ {
219
+ $database = new Database();
220
+ return $this->handleJobResponse($database->start(), "SearchReplace");
221
+ }
222
+
223
+ /**
224
+ * Search & Replace
225
+ * @return object
226
+ */
227
+ public function jobSearchReplace()
228
+ {
229
+ $searchReplace = new SearchReplace();
230
+ return $this->handleJobResponse($searchReplace->start(), "directories");
231
+ }
232
+
233
+ /**
234
+ * Get All Files From Selected Directories Recursively Into a File
235
+ * @return object
236
+ */
237
+ public function jobDirectories()
238
+ {
239
+ $directories = new Directories();
240
+ return $this->handleJobResponse($directories->start(), "files");
241
+ }
242
+
243
+ /**
244
+ * Copy Files
245
+ * @return object
246
+ */
247
+ public function jobFiles()
248
+ {
249
+ $files = new Files();
250
+ return $this->handleJobResponse($files->start(), "data");
251
+ }
252
+
253
+ /**
254
+ * Replace Data
255
+ * @return object
256
+ */
257
+ public function jobData()
258
+ {
259
+ $data = new Data();
260
+ return $this->handleJobResponse($data->start(), "finish");
261
+ }
262
+
263
+
264
+ /**
265
+ * Save Clone Data
266
+ * @return object
267
+ */
268
+ public function jobFinish()
269
+ {
270
+ $finish = new Finish();
271
+ return $this->handleJobResponse($finish->start(), '');
272
+ }
273
+
274
+
275
}
apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -1,727 +1,727 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs;
4
-
5
- // No Direct Access
6
- if( !defined( "WPINC" ) ) {
7
- die;
8
- }
9
-
10
- use WPStaging\Utils\Logger;
11
- use WPStaging\WPStaging;
12
- use WPStaging\Utils\Helper;
13
- use WPStaging\Utils\Strings;
14
-
15
- /**
16
- * Class Data
17
- * @package WPStaging\Backend\Modules\Jobs
18
- */
19
- class Data extends JobExecutable {
20
-
21
- /**
22
- * @var \wpdb
23
- */
24
- private $db;
25
-
26
- /**
27
- * @var string
28
- */
29
- private $prefix;
30
-
31
- /**
32
- *
33
- * @var string
34
- */
35
- private $homeUrl;
36
-
37
- /**
38
- * Tables e.g wpstg3_options
39
- * @var array
40
- */
41
- private $tables;
42
-
43
- /**
44
- * Initialize
45
- */
46
- public function initialize() {
47
- $this->db = WPStaging::getInstance()->get( "wpdb" );
48
-
49
- $this->prefix = $this->options->prefix;
50
-
51
- $this->getTables();
52
-
53
- $helper = new Helper();
54
-
55
- $this->homeUrl = $helper->get_home_url();
56
-
57
-
58
- // Fix current step
59
- if( 0 == $this->options->currentStep ) {
60
- $this->options->currentStep = 1;
61
- }
62
- }
63
-
64
- /**
65
- * Get a list of tables to copy
66
- */
67
- private function getTables() {
68
- $strings = new Strings();
69
- $this->tables = array();
70
- foreach ( $this->options->tables as $table ) {
71
- $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
72
- }
73
- }
74
-
75
- /**
76
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
77
- * @return void
78
- */
79
- protected function calculateTotalSteps() {
80
- $this->options->totalSteps = 12;
81
- }
82
-
83
- /**
84
- * Start Module
85
- * @return object
86
- */
87
- public function start() {
88
- // Execute steps
89
- $this->run();
90
-
91
- // Save option, progress
92
- $this->saveOptions();
93
-
94
- return ( object ) $this->response;
95
- }
96
-
97
- /**
98
- * Execute the Current Step
99
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
100
- * @return bool
101
- */
102
- protected function execute() {
103
- // Fatal error. Let this happen never and break here immediately
104
- if( $this->isRoot() ) {
105
- return false;
106
- }
107
-
108
- // Over limits threshold
109
- if( $this->isOverThreshold() ) {
110
- // Prepare response and save current progress
111
- $this->prepareResponse( false, false );
112
- $this->saveOptions();
113
- return false;
114
- }
115
-
116
- // No more steps, finished
117
- if( $this->isFinished() ) {
118
- $this->prepareResponse( true, false );
119
- return false;
120
- }
121
-
122
- // Execute step
123
- $stepMethodName = "step" . $this->options->currentStep;
124
- if( !$this->{$stepMethodName}() ) {
125
- $this->prepareResponse( false, false );
126
- return false;
127
- }
128
-
129
- // Prepare Response
130
- $this->prepareResponse();
131
-
132
- // Not finished
133
- return true;
134
- }
135
-
136
- /**
137
- * Checks Whether There is Any Job to Execute or Not
138
- * @return bool
139
- */
140
- protected function isFinished() {
141
- return (
142
- $this->options->currentStep > $this->options->totalSteps ||
143
- !method_exists( $this, "step" . $this->options->currentStep )
144
- );
145
- }
146
-
147
- /**
148
- * Check if current operation is done on the root folder or on the live DB
149
- * @return boolean
150
- */
151
- protected function isRoot() {
152
-
153
- // Prefix is the same as the one of live site
154
- $wpdb = WPStaging::getInstance()->get( "wpdb" );
155
- if( $wpdb->prefix === $this->prefix ) {
156
- return true;
157
- }
158
-
159
- // CloneName is empty
160
- $name = ( array ) $this->options->cloneDirectoryName;
161
- if( empty( $name ) ) {
162
- return true;
163
- }
164
-
165
- // Live Path === Staging path
166
- if( $this->homeUrl . $this->options->cloneDirectoryName === $this->homeUrl ) {
167
- return true;
168
- }
169
-
170
- return false;
171
- }
172
-
173
- /**
174
- * Check if table exists
175
- * @param string $table
176
- * @return boolean
177
- */
178
- protected function isTable( $table ) {
179
- if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
180
- $this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
181
- return false;
182
- }
183
- return true;
184
- }
185
-
186
- /**
187
- * Get the install sub directory if WP is installed in sub directory
188
- * @return string
189
- */
190
- protected function getSubDir() {
191
- $home = get_option( 'home' );
192
- $siteurl = get_option( 'siteurl' );
193
-
194
- if( empty( $home ) || empty( $siteurl ) ) {
195
- return '/';
196
- }
197
-
198
- $dir = str_replace( $home, '', $siteurl );
199
- return '/' . str_replace( '/', '', $dir ) . '/';
200
- }
201
-
202
- /**
203
- * Replace "siteurl" and "home"
204
- * @return bool
205
- */
206
- protected function step1() {
207
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
208
-
209
- // Skip - Table does not exist
210
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
211
- return true;
212
- }
213
- // Skip - Table is not selected or updated
214
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
215
- $this->log( "Preparing Data Step1: Skipping" );
216
- return true;
217
- }
218
-
219
- // Installed in sub-directory
220
- if( $this->isSubDir() ) {
221
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName );
222
- // Replace URLs
223
- $result = $this->db->query(
224
- $this->db->prepare(
225
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName
226
- )
227
- );
228
- } else {
229
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName );
230
- // Replace URLs
231
- $result = $this->db->query(
232
- $this->db->prepare(
233
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->homeUrl . '/' . $this->options->cloneDirectoryName
234
- )
235
- );
236
- }
237
-
238
-
239
- // All good
240
- if( $result ) {
241
- return true;
242
- }
243
-
244
- $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
245
- return false;
246
- }
247
-
248
- /**
249
- * Update "wpstg_is_staging_site"
250
- * @return bool
251
- */
252
- protected function step2() {
253
-
254
- $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
255
-
256
- // Skip - Table does not exist
257
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
258
- return true;
259
- }
260
- // Skip - Table is not selected or updated
261
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
262
- $this->log( "Preparing Data Step2: Skipping" );
263
- return true;
264
- }
265
-
266
- $result = $this->db->query(
267
- $this->db->prepare(
268
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
269
- )
270
- );
271
-
272
- // No errors but no option name such as wpstg_is_staging_site
273
- if( '' === $this->db->last_error && 0 == $result ) {
274
- $result = $this->db->query(
275
- $this->db->prepare(
276
- "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
277
- )
278
- );
279
- }
280
-
281
- // All good
282
- if( $result ) {
283
- return true;
284
- }
285
-
286
- $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
287
- return false;
288
- }
289
-
290
- /**
291
- * Update rewrite_rules
292
- * @return bool
293
- */
294
- protected function step3() {
295
-
296
- $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
297
-
298
- // Skip - Table does not exist
299
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
300
- return true;
301
- }
302
-
303
- // Skip - Table is not selected or updated
304
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
305
- $this->log( "Preparing Data Step3: Skipping" );
306
- return true;
307
- }
308
-
309
- $result = $this->db->query(
310
- $this->db->prepare(
311
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
312
- )
313
- );
314
-
315
- // All good
316
- if( $result ) {
317
- return true;
318
- }
319
-
320
- $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
321
- return true;
322
- }
323
-
324
- /**
325
- * Update Table Prefix in wp_usermeta and wp_options
326
- * @return bool
327
- */
328
- protected function step4() {
329
- $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
330
-
331
- // Skip - Table does not exist
332
- if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
333
- return true;
334
- }
335
-
336
- // Skip - Table is not selected or updated
337
- if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
338
- $this->log( "Preparing Data Step4: Skipping" );
339
- return true;
340
- }
341
-
342
- $update = $this->db->query(
343
- $this->db->prepare(
344
- "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
345
- )
346
- );
347
-
348
- if( !$update ) {
349
- $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
350
- $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
351
- return false;
352
- }
353
-
354
- // if( false === $this->isTable( $this->prefix . 'options' ) ) {
355
- // return true;
356
- // }
357
- // $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
358
- //
359
- // // Filter the rows below. Do not update them!
360
- // $filters = array(
361
- // 'wp_mail_smtp',
362
- // 'wp_mail_smtp_version',
363
- // 'wp_mail_smtp_debug',
364
- // );
365
- //
366
- // $filters = apply_filters('wpstg_filter_options_replace', $filters);
367
- //
368
- // $where = "";
369
- // foreach($filters as $filter){
370
- // $where .= " AND option_name <> '" . $filter . "'";
371
- // }
372
- //
373
- // $updateOptions = $this->db->query(
374
- // $this->db->prepare(
375
- // "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
376
- // )
377
- // );
378
- //
379
- // if( !$updateOptions ) {
380
- // $this->log( "Preparing Data Step4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
381
- // $this->returnException( "Data Crunching Step 4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
382
- // return false;
383
- // }
384
-
385
- return true;
386
- }
387
-
388
- /**
389
- * Update $table_prefix in wp-config.php
390
- * @return bool
391
- */
392
- protected function step5() {
393
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
394
-
395
- $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
396
- if( false === ($content = file_get_contents( $path )) ) {
397
- $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
398
- return false;
399
- }
400
-
401
- // Replace table prefix
402
- $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
403
-
404
- // Replace URLs
405
- $content = str_replace( $this->homeUrl, $this->homeUrl . '/' . $this->options->cloneDirectoryName, $content );
406
-
407
- if( false === @file_put_contents( $path, $content ) ) {
408
- $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
409
- return false;
410
- }
411
-
412
- return true;
413
- }
414
-
415
- /**
416
- * Reset index.php to original file
417
- * This is needed if live site is located in subfolder
418
- * Check first if main wordpress is used in subfolder and index.php in parent directory
419
- * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
420
- * @return bool
421
- */
422
- protected function step6() {
423
-
424
- if( !$this->isSubDir() ) {
425
- $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
426
- return true;
427
- }
428
-
429
- $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
430
-
431
- if( false === ($content = file_get_contents( $path )) ) {
432
- $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
433
- return false;
434
- }
435
-
436
-
437
- if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
438
- $this->log(
439
- "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
440
- );
441
- return false;
442
- }
443
- $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
444
-
445
- $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
446
-
447
- $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
448
- $replace.= " // Changed by WP-Staging";
449
-
450
-
451
-
452
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
453
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
454
- return false;
455
- }
456
-
457
- if( false === @file_put_contents( $path, $content ) ) {
458
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
459
- return false;
460
- }
461
- $this->Log( "Preparing Data: Finished Step 6 successfully" );
462
- return true;
463
- }
464
-
465
- /**
466
- * Update wpstg_rmpermalinks_executed
467
- * @return bool
468
- */
469
- protected function step7() {
470
-
471
- $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
472
-
473
- // Skip - Table does not exist
474
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
475
- return true;
476
- }
477
-
478
- // Skip - Table is not selected or updated
479
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
480
- $this->log( "Preparing Data Step7: Skipping" );
481
- return true;
482
- }
483
-
484
- $result = $this->db->query(
485
- $this->db->prepare(
486
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
487
- )
488
- );
489
-
490
- // All good
491
- if( $result ) {
492
- $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
493
- return true;
494
- }
495
-
496
- $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
497
- return true;
498
- }
499
-
500
- /**
501
- * Update permalink_structure
502
- * @return bool
503
- */
504
- protected function step8() {
505
-
506
- $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
507
-
508
- // Skip - Table does not exist
509
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
510
- return true;
511
- }
512
-
513
- // Skip - Table is not selected or updated
514
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
515
- $this->log( "Preparing Data Step8: Skipping" );
516
- return true;
517
- }
518
-
519
- $result = $this->db->query(
520
- $this->db->prepare(
521
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
522
- )
523
- );
524
-
525
- // All good
526
- if( $result ) {
527
- $this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
528
- return true;
529
- }
530
-
531
- $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
532
- return true;
533
- }
534
-
535
- /**
536
- * Update blog_public option to not allow staging site to be indexed by search engines
537
- * @return bool
538
- */
539
- protected function step9() {
540
-
541
- $this->log( "Preparing Data Step9: Set staging site to noindex" );
542
-
543
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
544
- return true;
545
- }
546
-
547
- // Skip - Table is not selected or updated
548
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
549
- $this->log( "Preparing Data Step9: Skipping" );
550
- return true;
551
- }
552
-
553
- $result = $this->db->query(
554
- $this->db->prepare(
555
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
556
- )
557
- );
558
-
559
- // All good
560
- if( $result ) {
561
- $this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
562
- return true;
563
- }
564
-
565
- $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
566
- return true;
567
- }
568
-
569
- /**
570
- * Update WP_HOME in wp-config.php
571
- * @return bool
572
- */
573
- protected function step10() {
574
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
575
-
576
- $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
577
-
578
- if( false === ($content = file_get_contents( $path )) ) {
579
- $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
580
- return false;
581
- }
582
-
583
-
584
- // Get WP_HOME from wp-config.php
585
- preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
586
-
587
- if( !empty( $matches[1] ) ) {
588
- $matches[1];
589
-
590
- $pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
591
-
592
- $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
593
- $replace.= " // Changed by WP-Staging";
594
-
595
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
596
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
597
- return false;
598
- }
599
- } else {
600
- $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
601
- }
602
-
603
- if( false === @file_put_contents( $path, $content ) ) {
604
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
605
- return false;
606
- }
607
- $this->Log( "Preparing Data: Finished Step 11 successfully" );
608
- return true;
609
- }
610
-
611
- /**
612
- * Update WP_SITEURL in wp-config.php
613
- * @return bool
614
- */
615
- protected function step11() {
616
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
617
-
618
- $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
619
-
620
- if( false === ($content = file_get_contents( $path )) ) {
621
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
622
- return false;
623
- }
624
-
625
-
626
- // Get WP_SITEURL from wp-config.php
627
- preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
628
-
629
- if( !empty( $matches[1] ) ) {
630
- $matches[1];
631
-
632
- $pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
633
-
634
- $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
635
- $replace.= " // Changed by WP-Staging";
636
-
637
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
638
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
639
- return false;
640
- }
641
- } else {
642
- $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
643
- }
644
-
645
-
646
- if( false === @file_put_contents( $path, $content ) ) {
647
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
648
- return false;
649
- }
650
- $this->Log( "Preparing Data: Finished Step 11 successfully" );
651
- return true;
652
- }
653
-
654
- /**
655
- * Update Table Prefix in wp_options
656
- * @return bool
657
- */
658
- protected function step12() {
659
- $this->log( "Preparing Data Step12: Updating db prefix in {$this->prefix}options. Error: {$this->db->last_error}" );
660
-
661
- // Skip - Table does not exist
662
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
663
- return true;
664
- }
665
-
666
- // Skip - Table is not selected or updated
667
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
668
- $this->log( "Preparing Data Step12: Skipping" );
669
- return true;
670
- }
671
-
672
-
673
- $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
674
-
675
- // Filter the rows below. Do not update them!
676
- $filters = array(
677
- 'wp_mail_smtp',
678
- 'wp_mail_smtp_version',
679
- 'wp_mail_smtp_debug',
680
- );
681
-
682
- $filters = apply_filters( 'wpstg_filter_options_replace', $filters );
683
-
684
- $where = "";
685
- foreach ( $filters as $filter ) {
686
- $where .= " AND option_name <> '" . $filter . "'";
687
- }
688
-
689
- $updateOptions = $this->db->query(
690
- $this->db->prepare(
691
- "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
692
- )
693
- );
694
-
695
- if( !$updateOptions ) {
696
- $this->log( "Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
697
- $this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
698
- return false;
699
- }
700
-
701
- return true;
702
- }
703
-
704
- /**
705
- * Return URL to staging site
706
- * @return string
707
- */
708
- protected function getStagingSiteUrl() {
709
- if( $this->isSubDir() ) {
710
- return rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName;
711
- }
712
-
713
- return rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName;
714
- }
715
-
716
- /**
717
- * Check if WP is installed in subdir
718
- * @return boolean
719
- */
720
- protected function isSubDir() {
721
- if( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
722
- return true;
723
- }
724
- return false;
725
- }
726
-
727
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs;
4
+
5
+ // No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\Utils\Logger;
11
+ use WPStaging\WPStaging;
12
+ use WPStaging\Utils\Helper;
13
+ use WPStaging\Utils\Strings;
14
+
15
+ /**
16
+ * Class Data
17
+ * @package WPStaging\Backend\Modules\Jobs
18
+ */
19
+ class Data extends JobExecutable {
20
+
21
+ /**
22
+ * @var \wpdb
23
+ */
24
+ private $db;
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ private $prefix;
30
+
31
+ /**
32
+ *
33
+ * @var string
34
+ */
35
+ private $homeUrl;
36
+
37
+ /**
38
+ * Tables e.g wpstg3_options
39
+ * @var array
40
+ */
41
+ private $tables;
42
+
43
+ /**
44
+ * Initialize
45
+ */
46
+ public function initialize() {
47
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
48
+
49
+ $this->prefix = $this->options->prefix;
50
+
51
+ $this->getTables();
52
+
53
+ $helper = new Helper();
54
+
55
+ $this->homeUrl = $helper->get_home_url();
56
+
57
+
58
+ // Fix current step
59
+ if( 0 == $this->options->currentStep ) {
60
+ $this->options->currentStep = 1;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get a list of tables to copy
66
+ */
67
+ private function getTables() {
68
+ $strings = new Strings();
69
+ $this->tables = array();
70
+ foreach ( $this->options->tables as $table ) {
71
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
77
+ * @return void
78
+ */
79
+ protected function calculateTotalSteps() {
80
+ $this->options->totalSteps = 12;
81
+ }
82
+
83
+ /**
84
+ * Start Module
85
+ * @return object
86
+ */
87
+ public function start() {
88
+ // Execute steps
89
+ $this->run();
90
+
91
+ // Save option, progress
92
+ $this->saveOptions();
93
+
94
+ return ( object ) $this->response;
95
+ }
96
+
97
+ /**
98
+ * Execute the Current Step
99
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
100
+ * @return bool
101
+ */
102
+ protected function execute() {
103
+ // Fatal error. Let this happen never and break here immediately
104
+ if( $this->isRoot() ) {
105
+ return false;
106
+ }
107
+
108
+ // Over limits threshold
109
+ if( $this->isOverThreshold() ) {
110
+ // Prepare response and save current progress
111
+ $this->prepareResponse( false, false );
112
+ $this->saveOptions();
113
+ return false;
114
+ }
115
+
116
+ // No more steps, finished
117
+ if( $this->isFinished() ) {
118
+ $this->prepareResponse( true, false );
119
+ return false;
120
+ }
121
+
122
+ // Execute step
123
+ $stepMethodName = "step" . $this->options->currentStep;
124
+ if( !$this->{$stepMethodName}() ) {
125
+ $this->prepareResponse( false, false );
126
+ return false;
127
+ }
128
+
129
+ // Prepare Response
130
+ $this->prepareResponse();
131
+
132
+ // Not finished
133
+ return true;
134
+ }
135
+
136
+ /**
137
+ * Checks Whether There is Any Job to Execute or Not
138
+ * @return bool
139
+ */
140
+ protected function isFinished() {
141
+ return (
142
+ $this->options->currentStep > $this->options->totalSteps ||
143
+ !method_exists( $this, "step" . $this->options->currentStep )
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Check if current operation is done on the root folder or on the live DB
149
+ * @return boolean
150
+ */
151
+ protected function isRoot() {
152
+
153
+ // Prefix is the same as the one of live site
154
+ $wpdb = WPStaging::getInstance()->get( "wpdb" );
155
+ if( $wpdb->prefix === $this->prefix ) {
156
+ return true;
157
+ }
158
+
159
+ // CloneName is empty
160
+ $name = ( array ) $this->options->cloneDirectoryName;
161
+ if( empty( $name ) ) {
162
+ return true;
163
+ }
164
+
165
+ // Live Path === Staging path
166
+ if( $this->homeUrl . $this->options->cloneDirectoryName === $this->homeUrl ) {
167
+ return true;
168
+ }
169
+
170
+ return false;
171
+ }
172
+
173
+ /**
174
+ * Check if table exists
175
+ * @param string $table
176
+ * @return boolean
177
+ */
178
+ protected function isTable( $table ) {
179
+ if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
180
+ $this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
181
+ return false;
182
+ }
183
+ return true;
184
+ }
185
+
186
+ /**
187
+ * Get the install sub directory if WP is installed in sub directory
188
+ * @return string
189
+ */
190
+ protected function getSubDir() {
191
+ $home = get_option( 'home' );
192
+ $siteurl = get_option( 'siteurl' );
193
+
194
+ if( empty( $home ) || empty( $siteurl ) ) {
195
+ return '/';
196
+ }
197
+
198
+ $dir = str_replace( $home, '', $siteurl );
199
+ return '/' . str_replace( '/', '', $dir ) . '/';
200
+ }
201
+
202
+ /**
203
+ * Replace "siteurl" and "home"
204
+ * @return bool
205
+ */
206
+ protected function step1() {
207
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
208
+
209
+ // Skip - Table does not exist
210
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
211
+ return true;
212
+ }
213
+ // Skip - Table is not selected or updated
214
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
215
+ $this->log( "Preparing Data Step1: Skipping" );
216
+ return true;
217
+ }
218
+
219
+ // Installed in sub-directory
220
+ if( $this->isSubDir() ) {
221
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName );
222
+ // Replace URLs
223
+ $result = $this->db->query(
224
+ $this->db->prepare(
225
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName
226
+ )
227
+ );
228
+ } else {
229
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName );
230
+ // Replace URLs
231
+ $result = $this->db->query(
232
+ $this->db->prepare(
233
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->homeUrl . '/' . $this->options->cloneDirectoryName
234
+ )
235
+ );
236
+ }
237
+
238
+
239
+ // All good
240
+ if( $result ) {
241
+ return true;
242
+ }
243
+
244
+ $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
245
+ return false;
246
+ }
247
+
248
+ /**
249
+ * Update "wpstg_is_staging_site"
250
+ * @return bool
251
+ */
252
+ protected function step2() {
253
+
254
+ $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
255
+
256
+ // Skip - Table does not exist
257
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
258
+ return true;
259
+ }
260
+ // Skip - Table is not selected or updated
261
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
262
+ $this->log( "Preparing Data Step2: Skipping" );
263
+ return true;
264
+ }
265
+
266
+ $result = $this->db->query(
267
+ $this->db->prepare(
268
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
269
+ )
270
+ );
271
+
272
+ // No errors but no option name such as wpstg_is_staging_site
273
+ if( '' === $this->db->last_error && 0 == $result ) {
274
+ $result = $this->db->query(
275
+ $this->db->prepare(
276
+ "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
277
+ )
278
+ );
279
+ }
280
+
281
+ // All good
282
+ if( $result ) {
283
+ return true;
284
+ }
285
+
286
+ $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
287
+ return false;
288
+ }
289
+
290
+ /**
291
+ * Update rewrite_rules
292
+ * @return bool
293
+ */
294
+ protected function step3() {
295
+
296
+ $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
297
+
298
+ // Skip - Table does not exist
299
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
300
+ return true;
301
+ }
302
+
303
+ // Skip - Table is not selected or updated
304
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
305
+ $this->log( "Preparing Data Step3: Skipping" );
306
+ return true;
307
+ }
308
+
309
+ $result = $this->db->query(
310
+ $this->db->prepare(
311
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
312
+ )
313
+ );
314
+
315
+ // All good
316
+ if( $result ) {
317
+ return true;
318
+ }
319
+
320
+ $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
321
+ return true;
322
+ }
323
+
324
+ /**
325
+ * Update Table Prefix in wp_usermeta and wp_options
326
+ * @return bool
327
+ */
328
+ protected function step4() {
329
+ $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
330
+
331
+ // Skip - Table does not exist
332
+ if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
333
+ return true;
334
+ }
335
+
336
+ // Skip - Table is not selected or updated
337
+ if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
338
+ $this->log( "Preparing Data Step4: Skipping" );
339
+ return true;
340
+ }
341
+
342
+ $update = $this->db->query(
343
+ $this->db->prepare(
344
+ "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
345
+ )
346
+ );
347
+
348
+ if( !$update ) {
349
+ $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
350
+ $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
351
+ return false;
352
+ }
353
+
354
+ // if( false === $this->isTable( $this->prefix . 'options' ) ) {
355
+ // return true;
356
+ // }
357
+ // $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
358
+ //
359
+ // // Filter the rows below. Do not update them!
360
+ // $filters = array(
361
+ // 'wp_mail_smtp',
362
+ // 'wp_mail_smtp_version',
363
+ // 'wp_mail_smtp_debug',
364
+ // );
365
+ //
366
+ // $filters = apply_filters('wpstg_filter_options_replace', $filters);
367
+ //
368
+ // $where = "";
369
+ // foreach($filters as $filter){
370
+ // $where .= " AND option_name <> '" . $filter . "'";
371
+ // }
372
+ //
373
+ // $updateOptions = $this->db->query(
374
+ // $this->db->prepare(
375
+ // "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
376
+ // )
377
+ // );
378
+ //
379
+ // if( !$updateOptions ) {
380
+ // $this->log( "Preparing Data Step4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
381
+ // $this->returnException( "Data Crunching Step 4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
382
+ // return false;
383
+ // }
384
+
385
+ return true;
386
+ }
387
+
388
+ /**
389
+ * Update $table_prefix in wp-config.php
390
+ * @return bool
391
+ */
392
+ protected function step5() {
393
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
394
+
395
+ $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
396
+ if( false === ($content = file_get_contents( $path )) ) {
397
+ $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
398
+ return false;
399
+ }
400
+
401
+ // Replace table prefix
402
+ $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
403
+
404
+ // Replace URLs
405
+ $content = str_replace( $this->homeUrl, $this->homeUrl . '/' . $this->options->cloneDirectoryName, $content );
406
+
407
+ if( false === @file_put_contents( $path, $content ) ) {
408
+ $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
409
+ return false;
410
+ }
411
+
412
+ return true;
413
+ }
414
+
415
+ /**
416
+ * Reset index.php to original file
417
+ * This is needed if live site is located in subfolder
418
+ * Check first if main wordpress is used in subfolder and index.php in parent directory
419
+ * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
420
+ * @return bool
421
+ */
422
+ protected function step6() {
423
+
424
+ if( !$this->isSubDir() ) {
425
+ $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
426
+ return true;
427
+ }
428
+
429
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
430
+
431
+ if( false === ($content = file_get_contents( $path )) ) {
432
+ $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
433
+ return false;
434
+ }
435
+
436
+
437
+ if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
438
+ $this->log(
439
+ "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
440
+ );
441
+ return false;
442
+ }
443
+ $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
444
+
445
+ $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
446
+
447
+ $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
448
+ $replace.= " // Changed by WP-Staging";
449
+
450
+
451
+
452
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
453
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
454
+ return false;
455
+ }
456
+
457
+ if( false === @file_put_contents( $path, $content ) ) {
458
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
459
+ return false;
460
+ }
461
+ $this->Log( "Preparing Data: Finished Step 6 successfully" );
462
+ return true;
463
+ }
464
+
465
+ /**
466
+ * Update wpstg_rmpermalinks_executed
467
+ * @return bool
468
+ */
469
+ protected function step7() {
470
+
471
+ $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
472
+
473
+ // Skip - Table does not exist
474
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
475
+ return true;
476
+ }
477
+
478
+ // Skip - Table is not selected or updated
479
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
480
+ $this->log( "Preparing Data Step7: Skipping" );
481
+ return true;
482
+ }
483
+
484
+ $result = $this->db->query(
485
+ $this->db->prepare(
486
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
487
+ )
488
+ );
489
+
490
+ // All good
491
+ if( $result ) {
492
+ $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
493
+ return true;
494
+ }
495
+
496
+ $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
497
+ return true;
498
+ }
499
+
500
+ /**
501
+ * Update permalink_structure
502
+ * @return bool
503
+ */
504
+ protected function step8() {
505
+
506
+ $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
507
+
508
+ // Skip - Table does not exist
509
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
510
+ return true;
511
+ }
512
+
513
+ // Skip - Table is not selected or updated
514
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
515
+ $this->log( "Preparing Data Step8: Skipping" );
516
+ return true;
517
+ }
518
+
519
+ $result = $this->db->query(
520
+ $this->db->prepare(
521
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
522
+ )
523
+ );
524
+
525
+ // All good
526
+ if( $result ) {
527
+ $this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
528
+ return true;
529
+ }
530
+
531
+ $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
532
+ return true;
533
+ }
534
+
535
+ /**
536
+ * Update blog_public option to not allow staging site to be indexed by search engines
537
+ * @return bool
538
+ */
539
+ protected function step9() {
540
+
541
+ $this->log( "Preparing Data Step9: Set staging site to noindex" );
542
+
543
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
544
+ return true;
545
+ }
546
+
547
+ // Skip - Table is not selected or updated
548
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
549
+ $this->log( "Preparing Data Step9: Skipping" );
550
+ return true;
551
+ }
552
+
553
+ $result = $this->db->query(
554
+ $this->db->prepare(
555
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
556
+ )
557
+ );
558
+
559
+ // All good
560
+ if( $result ) {
561
+ $this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
562
+ return true;
563
+ }
564
+
565
+ $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
566
+ return true;
567
+ }
568
+
569
+ /**
570
+ * Update WP_HOME in wp-config.php
571
+ * @return bool
572
+ */
573
+ protected function step10() {
574
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
575
+
576
+ $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
577
+
578
+ if( false === ($content = file_get_contents( $path )) ) {
579
+ $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
580
+ return false;
581
+ }
582
+
583
+
584
+ // Get WP_HOME from wp-config.php
585
+ preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
586
+
587
+ if( !empty( $matches[1] ) ) {
588
+ $matches[1];
589
+
590
+ $pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
591
+
592
+ $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
593
+ $replace.= " // Changed by WP-Staging";
594
+
595
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
596
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
597
+ return false;
598
+ }
599
+ } else {
600
+ $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
601
+ }
602
+
603
+ if( false === @file_put_contents( $path, $content ) ) {
604
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
605
+ return false;
606
+ }
607
+ $this->Log( "Preparing Data: Finished Step 11 successfully" );
608
+ return true;
609
+ }
610
+
611
+ /**
612
+ * Update WP_SITEURL in wp-config.php
613
+ * @return bool
614
+ */
615
+ protected function step11() {
616
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
617
+
618
+ $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
619
+
620
+ if( false === ($content = file_get_contents( $path )) ) {
621
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
622
+ return false;
623
+ }
624
+
625
+
626
+ // Get WP_SITEURL from wp-config.php
627
+ preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
628
+
629
+ if( !empty( $matches[1] ) ) {
630
+ $matches[1];
631
+
632
+ $pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
633
+
634
+ $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
635
+ $replace.= " // Changed by WP-Staging";
636
+
637
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
638
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
639
+ return false;
640
+ }
641
+ } else {
642
+ $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
643
+ }
644
+
645
+
646
+ if( false === @file_put_contents( $path, $content ) ) {
647
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
648
+ return false;
649
+ }
650
+ $this->Log( "Preparing Data: Finished Step 11 successfully" );
651
+ return true;
652
+ }
653
+
654
+ /**
655
+ * Update Table Prefix in wp_options
656
+ * @return bool
657
+ */
658
+ protected function step12() {
659
+ $this->log( "Preparing Data Step12: Updating db prefix in {$this->prefix}options. Error: {$this->db->last_error}" );
660
+
661
+ // Skip - Table does not exist
662
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
663
+ return true;
664
+ }
665
+
666
+ // Skip - Table is not selected or updated
667
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
668
+ $this->log( "Preparing Data Step12: Skipping" );
669
+ return true;
670
+ }
671
+
672
+
673
+ $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
674
+
675
+ // Filter the rows below. Do not update them!
676
+ $filters = array(
677
+ 'wp_mail_smtp',
678
+ 'wp_mail_smtp_version',
679
+ 'wp_mail_smtp_debug',
680
+ );
681
+
682
+ $filters = apply_filters( 'wpstg_filter_options_replace', $filters );
683
+
684
+ $where = "";
685
+ foreach ( $filters as $filter ) {
686
+ $where .= " AND option_name <> '" . $filter . "'";
687
+ }
688
+
689
+ $updateOptions = $this->db->query(
690
+ $this->db->prepare(
691
+ "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
692
+ )
693
+ );
694
+
695
+ if( !$updateOptions ) {
696
+ $this->log( "Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
697
+ $this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
698
+ return false;
699
+ }
700
+
701
+ return true;
702
+ }
703
+
704
+ /**
705
+ * Return URL to staging site
706
+ * @return string
707
+ */
708
+ protected function getStagingSiteUrl() {
709
+ if( $this->isSubDir() ) {
710
+ return rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName;
711
+ }
712
+
713
+ return rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName;
714
+ }
715
+
716
+ /**
717
+ * Check if WP is installed in subdir
718
+ * @return boolean
719
+ */
720
+ protected function isSubDir() {
721
+ if( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
722
+ return true;
723
+ }
724
+ return false;
725
+ }
726
+
727
+ }
apps/Backend/Modules/Jobs/Database.php CHANGED
@@ -1,282 +1,282 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs;
3
-
4
- // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
- }
9
-
10
- use WPStaging\WPStaging;
11
- use WPStaging\Utils\Strings;
12
-
13
- /**
14
- * Class Database
15
- * @package WPStaging\Backend\Modules\Jobs
16
- */
17
- class Database extends JobExecutable
18
- {
19
-
20
- /**
21
- * @var int
22
- */
23
- private $total = 0;
24
-
25
- /**
26
- * @var \WPDB
27
- */
28
- private $db;
29
-
30
- /**
31
- * Initialize
32
- */
33
- public function initialize()
34
- {
35
- // Variables
36
- $this->total = count($this->options->tables);
37
- $this->db = WPStaging::getInstance()->get("wpdb");
38
- $this->isFatalError();
39
-
40
- }
41
-
42
-
43
- /**
44
- * Return fatal error and stops here if subfolder already exists
45
- * and mainJob is not updating the clone
46
- * @return boolean
47
- */
48
- private function isFatalError(){
49
- $path = trailingslashit(get_home_path()) . $this->options->cloneDirectoryName;
50
- if (isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && is_dir($path)){
51
- $this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
52
- }
53
- return false;
54
- }
55
-
56
- /**
57
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
58
- * @return void
59
- */
60
- protected function calculateTotalSteps()
61
- {
62
- $this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
63
- }
64
-
65
- /**
66
- * Execute the Current Step
67
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
68
- * @return bool
69
- */
70
- protected function execute()
71
- {
72
- // Over limits threshold
73
- if ($this->isOverThreshold())
74
- {
75
- // Prepare response and save current progress
76
- $this->prepareResponse(false, false);
77
- $this->saveOptions();
78
- return false;
79
- }
80
-
81
- // No more steps, finished
82
- if ($this->options->currentStep > $this->total || !isset($this->options->tables[$this->options->currentStep]))
83
- {
84
- $this->prepareResponse(true, false);
85
- return false;
86
- }
87
-
88
- // Table is excluded
89
- // if (in_array($this->options->tables[$this->options->currentStep]->name, $this->options->excludedTables))
90
- // {
91
- // $this->prepareResponse();
92
- // return true;
93
- // }
94
-
95
- // Copy table
96
- //if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
97
- if (!$this->copyTable($this->options->tables[$this->options->currentStep]))
98
- {
99
- // Prepare Response
100
- $this->prepareResponse(false, false);
101
-
102
- // Not finished
103
- return true;
104
- }
105
-
106
- // Prepare Response
107
- $this->prepareResponse();
108
-
109
- // Not finished
110
- return true;
111
- }
112
-
113
- /**
114
- * Get new prefix for the staging site
115
- * @return string
116
- */
117
- private function getStagingPrefix(){
118
- $stagingPrefix = $this->options->prefix;
119
- // Make sure prefix of staging site is NEVER identical to prefix of live site!
120
- if ( $stagingPrefix == $this->db->prefix ){
121
- wp_die('Fatal error 7: The new database table prefix '. $stagingPrefix .' would be identical to the table prefix of the live site. Please open a support ticket to support@wp-staging.com');
122
- }
123
- return $stagingPrefix;
124
- }
125
-
126
- /**
127
- * No worries, SQL queries don't eat from PHP execution time!
128
- * @param string $tableName
129
- * @return bool
130
- */
131
- private function copyTable($tableName)
132
- {
133
-
134
- $strings = new Strings();
135
- $tableName = is_object($tableName) ? $tableName->name : $tableName;
136
- $newTableName = $this->getStagingPrefix() . $strings->str_replace_first($this->db->prefix, null, $tableName);
137
-
138
- // Drop table if necessary
139
- $this->dropTable($newTableName);
140
-
141
- // Save current job
142
- $this->setJob($newTableName);
143
-
144
- // Beginning of the job
145
- if (!$this->startJob($newTableName, $tableName))
146
- {
147
- return true;
148
- }
149
-
150
- // Copy data
151
- $this->copyData($newTableName, $tableName);
152
-
153
- // Finis the step
154
- return $this->finishStep();
155
- }
156
-
157
- /**
158
- * Copy data from old table to new table
159
- * @param string $new
160
- * @param string $old
161
- */
162
- private function copyData($new, $old)
163
- {
164
- $rows = $this->options->job->start+$this->settings->queryLimit;
165
- $this->log(
166
- "DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
167
- );
168
-
169
- $limitation = '';
170
-
171
- if (0 < (int) $this->settings->queryLimit)
172
- {
173
- $limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
174
- }
175
-
176
- $this->db->query(
177
- "INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
178
- );
179
-
180
- // Set new offset
181
- $this->options->job->start += $this->settings->queryLimit;
182
- }
183
-
184
- /**
185
- * Set the job
186
- * @param string $table
187
- */
188
- private function setJob($table)
189
- {
190
- if (isset($this->options->job->current))
191
- {
192
- return;
193
- }
194
-
195
- $this->options->job->current = $table;
196
- $this->options->job->start = 0;
197
- }
198
-
199
- /**
200
- * Start Job
201
- * @param string $new
202
- * @param string $old
203
- * @return bool
204
- */
205
- private function startJob($new, $old)
206
- {
207
- if (0 != $this->options->job->start)
208
- {
209
- return true;
210
- }
211
-
212
- $this->log("DB Copy: Creating table {$new}");
213
-
214
- $this->db->query("CREATE TABLE {$new} LIKE {$old}");
215
-
216
- $this->options->job->total = (int) $this->db->get_var("SELECT COUNT(1) FROM {$old}");
217
-
218
- if (0 == $this->options->job->total)
219
- {
220
- $this->finishStep();
221
- return false;
222
- }
223
-
224
- return true;
225
- }
226
-
227
- /**
228
- * Finish the step
229
- */
230
- private function finishStep()
231
- {
232
- // This job is not finished yet
233
- if ($this->options->job->total > $this->options->job->start)
234
- {
235
- return false;
236
- }
237
-
238
- // Add it to cloned tables listing
239
- $this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
240
-
241
- // Reset job
242
- $this->options->job = new \stdClass();
243
-
244
- return true;
245
- }
246
-
247
- /**
248
- * Drop table if necessary
249
- * @param string $new
250
- */
251
- private function dropTable($new)
252
- {
253
- $old = $this->db->get_var($this->db->prepare("SHOW TABLES LIKE %s", $new));
254
-
255
- if (!$this->shouldDropTable($new, $old))
256
- {
257
- return;
258
- }
259
-
260
- $this->log("DB Copy: {$new} already exists, dropping it first");
261
- $this->db->query("DROP TABLE {$new}");
262
- }
263
-
264
- /**
265
- * Check if table needs to be dropped
266
- * @param string $new
267
- * @param string $old
268
- * @return bool
269
- */
270
- private function shouldDropTable($new, $old)
271
- {
272
- return (
273
- $old === $new &&
274
- (
275
- !isset($this->options->job->current) ||
276
- !isset($this->options->job->start) ||
277
- 0 == $this->options->job->start
278
- )
279
- );
280
- }
281
-
282
}
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs;
3
+
4
+ // No Direct Access
5
+ if (!defined("WPINC"))
6
+ {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\WPStaging;
11
+ use WPStaging\Utils\Strings;
12
+
13
+ /**
14
+ * Class Database
15
+ * @package WPStaging\Backend\Modules\Jobs
16
+ */
17
+ class Database extends JobExecutable
18
+ {
19
+
20
+ /**
21
+ * @var int
22
+ */
23
+ private $total = 0;
24
+
25
+ /**
26
+ * @var \WPDB
27
+ */
28
+ private $db;
29
+
30
+ /**
31
+ * Initialize
32
+ */
33
+ public function initialize()
34
+ {
35
+ // Variables
36
+ $this->total = count($this->options->tables);
37
+ $this->db = WPStaging::getInstance()->get("wpdb");
38
+ $this->isFatalError();
39
+
40
+ }
41
+
42
+
43
+ /**
44
+ * Return fatal error and stops here if subfolder already exists
45
+ * and mainJob is not updating the clone
46
+ * @return boolean
47
+ */
48
+ private function isFatalError(){
49
+ $path = trailingslashit(get_home_path()) . $this->options->cloneDirectoryName;
50
+ if (isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && is_dir($path)){
51
+ $this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
52
+ }
53
+ return false;
54
+ }
55
+
56
+ /**
57
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
58
+ * @return void
59
+ */
60
+ protected function calculateTotalSteps()
61
+ {
62
+ $this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
63
+ }
64
+
65
+ /**
66
+ * Execute the Current Step
67
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
68
+ * @return bool
69
+ */
70
+ protected function execute()
71
+ {
72
+ // Over limits threshold
73
+ if ($this->isOverThreshold())
74
+ {
75
+ // Prepare response and save current progress
76
+ $this->prepareResponse(false, false);
77
+ $this->saveOptions();
78
+ return false;
79
+ }
80
+
81
+ // No more steps, finished
82
+ if ($this->options->currentStep > $this->total || !isset($this->options->tables[$this->options->currentStep]))
83
+ {
84
+ $this->prepareResponse(true, false);
85
+ return false;
86
+ }
87
+
88
+ // Table is excluded
89
+ // if (in_array($this->options->tables[$this->options->currentStep]->name, $this->options->excludedTables))
90
+ // {
91
+ // $this->prepareResponse();
92
+ // return true;
93
+ // }
94
+
95
+ // Copy table
96
+ //if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
97
+ if (!$this->copyTable($this->options->tables[$this->options->currentStep]))
98
+ {
99
+ // Prepare Response
100
+ $this->prepareResponse(false, false);
101
+
102
+ // Not finished
103
+ return true;
104
+ }
105
+
106
+ // Prepare Response
107
+ $this->prepareResponse();
108
+
109
+ // Not finished
110
+ return true;
111
+ }
112
+
113
+ /**
114
+ * Get new prefix for the staging site
115
+ * @return string
116
+ */
117
+ private function getStagingPrefix(){
118
+ $stagingPrefix = $this->options->prefix;
119
+ // Make sure prefix of staging site is NEVER identical to prefix of live site!
120
+ if ( $stagingPrefix == $this->db->prefix ){
121
+ wp_die('Fatal error 7: The new database table prefix '. $stagingPrefix .' would be identical to the table prefix of the live site. Please open a support ticket to support@wp-staging.com');
122
+ }
123
+ return $stagingPrefix;
124
+ }
125
+
126
+ /**
127
+ * No worries, SQL queries don't eat from PHP execution time!
128
+ * @param string $tableName
129
+ * @return bool
130
+ */
131
+ private function copyTable($tableName)
132
+ {
133
+
134
+ $strings = new Strings();
135
+ $tableName = is_object($tableName) ? $tableName->name : $tableName;
136
+ $newTableName = $this->getStagingPrefix() . $strings->str_replace_first($this->db->prefix, null, $tableName);
137
+
138
+ // Drop table if necessary
139
+ $this->dropTable($newTableName);
140
+
141
+ // Save current job
142
+ $this->setJob($newTableName);
143
+
144
+ // Beginning of the job
145
+ if (!$this->startJob($newTableName, $tableName))
146
+ {
147
+ return true;
148
+ }
149
+
150
+ // Copy data
151
+ $this->copyData($newTableName, $tableName);
152
+
153
+ // Finis the step
154
+ return $this->finishStep();
155
+ }
156
+
157
+ /**
158
+ * Copy data from old table to new table
159
+ * @param string $new
160
+ * @param string $old
161
+ */
162
+ private function copyData($new, $old)
163
+ {
164
+ $rows = $this->options->job->start+$this->settings->queryLimit;
165
+ $this->log(
166
+ "DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
167
+ );
168
+
169
+ $limitation = '';
170
+
171
+ if (0 < (int) $this->settings->queryLimit)
172
+ {
173
+ $limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
174
+ }
175
+
176
+ $this->db->query(
177
+ "INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
178
+ );
179
+
180
+ // Set new offset
181
+ $this->options->job->start += $this->settings->queryLimit;
182
+ }
183
+
184
+ /**
185
+ * Set the job
186
+ * @param string $table
187
+ */
188
+ private function setJob($table)
189
+ {
190
+ if (isset($this->options->job->current))
191
+ {
192
+ return;
193
+ }
194
+
195
+ $this->options->job->current = $table;
196
+ $this->options->job->start = 0;
197
+ }
198
+
199
+ /**
200
+ * Start Job
201
+ * @param string $new
202
+ * @param string $old
203
+ * @return bool
204
+ */
205
+ private function startJob($new, $old)
206
+ {
207
+ if (0 != $this->options->job->start)
208
+ {
209
+ return true;
210
+ }
211
+
212
+ $this->log("DB Copy: Creating table {$new}");
213
+
214
+ $this->db->query("CREATE TABLE {$new} LIKE {$old}");
215
+
216
+ $this->options->job->total = (int) $this->db->get_var("SELECT COUNT(1) FROM {$old}");
217
+
218
+ if (0 == $this->options->job->total)
219
+ {
220
+ $this->finishStep();
221
+ return false;
222
+ }
223
+
224
+ return true;
225
+ }
226
+
227
+ /**
228
+ * Finish the step
229
+ */
230
+ private function finishStep()
231
+ {
232
+ // This job is not finished yet
233
+ if ($this->options->job->total > $this->options->job->start)
234
+ {
235
+ return false;
236
+ }
237
+
238
+ // Add it to cloned tables listing
239
+ $this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
240
+
241
+ // Reset job
242
+ $this->options->job = new \stdClass();
243
+
244
+ return true;
245
+ }
246
+
247
+ /**
248
+ * Drop table if necessary
249
+ * @param string $new
250
+ */
251
+ private function dropTable($new)
252
+ {
253
+ $old = $this->db->get_var($this->db->prepare("SHOW TABLES LIKE %s", $new));
254
+
255
+ if (!$this->shouldDropTable($new, $old))
256
+ {
257
+ return;
258
+ }
259
+
260
+ $this->log("DB Copy: {$new} already exists, dropping it first");
261
+ $this->db->query("DROP TABLE {$new}");
262
+ }
263
+
264
+ /**
265
+ * Check if table needs to be dropped
266
+ * @param string $new
267
+ * @param string $old
268
+ * @return bool
269
+ */
270
+ private function shouldDropTable($new, $old)
271
+ {
272
+ return (
273
+ $old === $new &&
274
+ (
275
+ !isset($this->options->job->current) ||
276
+ !isset($this->options->job->start) ||
277
+ 0 == $this->options->job->start
278
+ )
279
+ );
280
+ }
281
+
282
}
apps/Backend/Modules/Jobs/Delete.php CHANGED
@@ -1,429 +1,429 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs;
4
-
5
- use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
6
- use WPStaging\Utils\Directories;
7
- use WPStaging\Utils\Logger;
8
- use WPStaging\WPStaging;
9
-
10
- /**
11
- * Class Delete
12
- * @package WPStaging\Backend\Modules\Jobs
13
- */
14
- class Delete extends Job {
15
-
16
- /**
17
- * @var false
18
- */
19
- private $clone = false;
20
-
21
- /**
22
- * @var null|object
23
- */
24
- private $tables = null;
25
-
26
- /**
27
- * @var object|null
28
- */
29
- private $job = null;
30
-
31
- /**
32
- * @var bool
33
- */
34
- private $forceDeleteDirectories = false;
35
-
36
- /**
37
- *
38
- * @var object
39
- */
40
- public $wpdb;
41
-
42
- public function __construct() {
43
- parent::__construct();
44
- $this->wpdb = WPStaging::getInstance()->get("wpdb");
45
- }
46
-
47
- /**
48
- * Sets Clone and Table Records
49
- * @param null|array $clone
50
- */
51
- public function setData($clone = null) {
52
- if (!is_array($clone)) {
53
- $this->getCloneRecords();
54
- } else {
55
- $this->clone = (object) $clone;
56
- $this->forceDeleteDirectories = true;
57
- }
58
-
59
- $this->getTableRecords();
60
- }
61
-
62
- /**
63
- * Get clone
64
- * @param null|string $name
65
- * @throws CloneNotFoundException
66
- */
67
- private function getCloneRecords($name = null) {
68
- if (null === $name && !isset($_POST["clone"])) {
69
- $this->log("Clone name is not set", Logger::TYPE_FATAL);
70
- throw new CloneNotFoundException();
71
- }
72
-
73
- if (null === $name) {
74
- $name = $_POST["clone"];
75
- }
76
-
77
- $clones = get_option("wpstg_existing_clones_beta", array());
78
-
79
- if (empty($clones) || !isset($clones[$name])) {
80
- $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
81
- throw new CloneNotFoundException();
82
- }
83
-
84
- $this->clone = $clones[$name];
85
- $this->clone["name"] = $name;
86
-
87
- $this->clone = (object) $this->clone;
88
-
89
- unset($clones);
90
- }
91
-
92
- /**
93
- * Get Tables
94
- */
95
- private function getTableRecords() {
96
- //$wpdb = WPStaging::getInstance()->get("wpdb");
97
- $this->wpdb = WPStaging::getInstance()->get("wpdb");
98
-
99
- $stagingPrefix = $this->getStagingPrefix();
100
-
101
- $tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$stagingPrefix}%'");
102
-
103
- $this->tables = array();
104
-
105
- foreach ($tables as $table) {
106
- $this->tables[] = array(
107
- "name" => $table->Name,
108
- "size" => $this->formatSize(($table->Data_length + $table->Index_length))
109
- );
110
- }
111
-
112
- $this->tables = json_decode(json_encode($this->tables));
113
- }
114
-
115
- /**
116
- * Check and return prefix of the staging site
117
- */
118
- public function getStagingPrefix() {
119
- // Prefix not defined! Happens if staging site has ben generated with older version of wpstg
120
- // Try to get staging prefix from wp-config.php of staging site
121
- if (empty($this->clone->prefix)) {
122
- // Throw error
123
- $path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
124
- if (false === ($content = @file_get_contents($path))) {
125
- $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
126
- // Create a random prefix which hopefully never exists.
127
- $this->clone->prefix = rand(7, 15) . '_';
128
- } else {
129
-
130
- // Get prefix from wp-config.php
131
- //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
132
- preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
133
- //wp_die(var_dump($matches));
134
-
135
- if (!empty($matches[1])) {
136
- $this->clone->prefix = $matches[1];
137
- } else {
138
- $this->log("Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
139
- // Create a random prefix which hopefully never exists.
140
- return $this->clone->prefix = rand(7, 15) . '_';
141
- }
142
- }
143
- }
144
-
145
- // Check if staging prefix is the same as the live prefix
146
- if ($this->wpdb->prefix == $this->clone->prefix) {
147
- $this->log("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
148
- wp_die("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
149
- }
150
-
151
- // Else
152
- return $this->clone->prefix;
153
- }
154
-
155
- /**
156
- * Format bytes into human readable form
157
- * @param int $bytes
158
- * @param int $precision
159
- * @return string
160
- */
161
- public function formatSize($bytes, $precision = 2) {
162
- if ((int) $bytes < 1) {
163
- return '';
164
- }
165
-
166
- $units = array('B', "KB", "MB", "GB", "TB");
167
-
168
- $bytes = (int) $bytes;
169
- $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
170
- $pow = pow(1000, $base - floor($base)); // Same rule for 1000
171
-
172
- return round($pow, $precision) . ' ' . $units[(int) floor($base)];
173
- }
174
-
175
- /**
176
- * @return false
177
- */
178
- public function getClone() {
179
- return $this->clone;
180
- }
181
-
182
- /**
183
- * @return null|object
184
- */
185
- public function getTables() {
186
- return $this->tables;
187
- }
188
-
189
- /**
190
- * Start Module
191
- * @param null|array $clone
192
- * @return bool
193
- */
194
- public function start($clone = null) {
195
- // Set data
196
- $this->setData($clone);
197
-
198
- // Get the job first
199
- $this->getJob();
200
-
201
- $method = "delete" . ucwords($this->job->current);
202
- return $this->{$method}();
203
- }
204
-
205
- /**
206
- * Get job data
207
- */
208
- private function getJob() {
209
- $this->job = $this->cache->get("delete_job_{$this->clone->name}");
210
-
211
-
212
- if (null !== $this->job) {
213
- return;
214
- }
215
-
216
- // Generate JOB
217
- $this->job = (object) array(
218
- "current" => "tables",
219
- "nextDirectoryToDelete" => $this->clone->path,
220
- "name" => $this->clone->name
221
- );
222
-
223
- $this->cache->save("delete_job_{$this->clone->name}", $this->job);
224
- }
225
-
226
- /**
227
- * @return bool
228
- */
229
- private function updateJob() {
230
- $this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
231
- return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
232
- }
233
-
234
- /**
235
- * @return array
236
- */
237
- private function getTablesToRemove() {
238
- $tables = $this->getTableNames();
239
-
240
- if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"])) {
241
- return $tables;
242
- }
243
-
244
- return array_diff($tables, $_POST["excludedTables"]);
245
- }
246
-
247
- /**
248
- * @return array
249
- */
250
- private function getTableNames() {
251
- return (!is_array($this->tables)) ? array() : array_map(function($value) {
252
- return ($value->name);
253
- }, $this->tables);
254
- }
255
-
256
- /**
257
- * Delete Tables
258
- */
259
- public function deleteTables() {
260
- if ($this->isOverThreshold()) {
261
- return;
262
- }
263
-
264
- //$wpdb = WPStaging::getInstance()->get("wpdb");
265
-
266
- foreach ($this->getTablesToRemove() as $table) {
267
- // PROTECTION: Never delete any table that beginns with wp prefix of live site
268
- if ($this->startsWith($table, $this->wpdb->prefix)) {
269
- $this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
270
- return false;
271
- } else {
272
- $this->wpdb->query("DROP TABLE {$table}");
273
- }
274
- }
275
-
276
- // Move on to the next
277
- $this->job->current = "directory";
278
- $this->updateJob();
279
- }
280
-
281
- /**
282
- * Check if a strings start with a specific string
283
- * @param string $haystack
284
- * @param string $needle
285
- * @return bool
286
- */
287
- protected function startsWith($haystack, $needle) {
288
- $length = strlen($needle);
289
- return (substr($haystack, 0, $length) === $needle);
290
- }
291
-
292
- /**
293
- * Delete complete directory including all files and subfolders
294
- *
295
- * @throws InvalidArgumentException
296
- */
297
- public function deleteDirectory() {
298
- if ($this->isFatalError()) {
299
- $this->returnException('Can not delete directory: ' . $this->clone->path . '. This seems to be the root directory. Please contact support@wp-staging.com');
300
- throw new \Exception('Can not delete directory: ' . $this->clone->path . ' This seems to be the root directory. Please contact support@wp-staging.com');
301
- }
302
- // Finished or path does not exist
303
- if (
304
- empty($this->clone->path) ||
305
- $this->clone->path == get_home_path() ||
306
- !is_dir($this->clone->path)) {
307
-
308
- $this->job->current = "finish";
309
- $this->updateJob();
310
- return $this->returnFinish();
311
- }
312
-
313
- $this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
314
-
315
- // Just to make sure the root dir is never deleted!
316
- if ($this->clone->path == get_home_path()) {
317
- $this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
318
- $this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
319
- }
320
-
321
- // Check if threshold is reached
322
- if ($this->isOverThreshold()) {
323
- //$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
324
- return;
325
- }
326
-
327
- $di = new \RecursiveDirectoryIterator($this->clone->path, \FilesystemIterator::SKIP_DOTS);
328
- $ri = new \RecursiveIteratorIterator($di, \RecursiveIteratorIterator::CHILD_FIRST);
329
- foreach ($ri as $file) {
330
- //$file->isDir() ? @rmdir($file) : unlink($file);
331
- $this->deleteFile($file);
332
- if ($this->isOverThreshold()) {
333
- //$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
334
- return;
335
- }
336
- }
337
-
338
- if (@rmdir($this->clone->path)) {
339
- return $this->returnFinish();
340
- }
341
- return;
342
- }
343
-
344
- /**
345
- * Delete file
346
- * @param object iterator $file
347
- */
348
- private function deleteFile($file) {
349
- if ($file->isDir()) {
350
- if (!@rmdir($file)) {
351
- $this->returnException('Permission Error: Can not delete folder ' . $file);
352
- }
353
- } else {
354
- if (!unlink($file)) {
355
- $this->returnException('Permission Error: Can not delete file ' . $file);
356
- }
357
- }
358
- }
359
-
360
- /**
361
- * @return bool
362
- */
363
- public function isDirectoryDeletingFinished() {
364
- return (
365
- (false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
366
- !is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
367
- );
368
- }
369
-
370
-
371
-
372
- /**
373
- *
374
- * @return boolean
375
- */
376
- public function isFatalError(){
377
- $homePath = rtrim(get_home_path(), "/");
378
- if (rtrim($this->clone->path,"/") == $homePath){
379
- return true;
380
- }
381
- return false;
382
- }
383
-
384
- /**
385
- * Finish / Update Existing Clones
386
- */
387
- public function deleteFinish() {
388
- $existingClones = get_option("wpstg_existing_clones_beta", array());
389
-
390
- // Check if clones still exist
391
- $this->log("Verifying existing clones...");
392
- foreach ($existingClones as $name => $clone) {
393
- if (!is_dir($clone["path"])) {
394
- unset($existingClones[$name]);
395
- }
396
- }
397
- $this->log("Existing clones verified!");
398
-
399
- if (false === update_option("wpstg_existing_clones_beta", $existingClones)) {
400
- $this->log("Failed to save {$this->options->clone}'s clone job data to database'");
401
- }
402
-
403
- // Delete cached file
404
- $this->cache->delete("delete_job_{$this->clone->name}");
405
- $this->cache->delete("delete_directories_{$this->clone->name}");
406
-
407
- //return true;
408
- $response = array('delete' => 'finished');
409
- wp_die(json_encode($response));
410
- }
411
-
412
- /**
413
- * Get json response
414
- * return json
415
- */
416
- private function returnFinish($message = '') {
417
-
418
- $this->deleteFinish();
419
-
420
- wp_die(json_encode(array(
421
- 'job' => 'delete',
422
- 'status' => true,
423
- 'message' => $message,
424
- 'error' => false,
425
- 'delete' => 'finished'
426
- )));
427
- }
428
-
429
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs;
4
+
5
+ use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
6
+ use WPStaging\Utils\Directories;
7
+ use WPStaging\Utils\Logger;
8
+ use WPStaging\WPStaging;
9
+
10
+ /**
11
+ * Class Delete
12
+ * @package WPStaging\Backend\Modules\Jobs
13
+ */
14
+ class Delete extends Job {
15
+
16
+ /**
17
+ * @var false
18
+ */
19
+ private $clone = false;
20
+
21
+ /**
22
+ * @var null|object
23
+ */
24
+ private $tables = null;
25
+
26
+ /**
27
+ * @var object|null
28
+ */
29
+ private $job = null;
30
+
31
+ /**
32
+ * @var bool
33
+ */
34
+ private $forceDeleteDirectories = false;
35
+
36
+ /**
37
+ *
38
+ * @var object
39
+ */
40
+ public $wpdb;
41
+
42
+ public function __construct() {
43
+ parent::__construct();
44
+ $this->wpdb = WPStaging::getInstance()->get("wpdb");
45
+ }
46
+
47
+ /**
48
+ * Sets Clone and Table Records
49
+ * @param null|array $clone
50
+ */
51
+ public function setData($clone = null) {
52
+ if (!is_array($clone)) {
53
+ $this->getCloneRecords();
54
+ } else {
55
+ $this->clone = (object) $clone;
56
+ $this->forceDeleteDirectories = true;
57
+ }
58
+
59
+ $this->getTableRecords();
60
+ }
61
+
62
+ /**
63
+ * Get clone
64
+ * @param null|string $name
65
+ * @throws CloneNotFoundException
66
+ */
67
+ private function getCloneRecords($name = null) {
68
+ if (null === $name && !isset($_POST["clone"])) {
69
+ $this->log("Clone name is not set", Logger::TYPE_FATAL);
70
+ throw new CloneNotFoundException();
71
+ }
72
+
73
+ if (null === $name) {
74
+ $name = $_POST["clone"];
75
+ }
76
+
77
+ $clones = get_option("wpstg_existing_clones_beta", array());
78
+
79
+ if (empty($clones) || !isset($clones[$name])) {
80
+ $this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
81
+ throw new CloneNotFoundException();
82
+ }
83
+
84
+ $this->clone = $clones[$name];
85
+ $this->clone["name"] = $name;
86
+
87
+ $this->clone = (object) $this->clone;
88
+
89
+ unset($clones);
90
+ }
91
+
92
+ /**
93
+ * Get Tables
94
+ */
95
+ private function getTableRecords() {
96
+ //$wpdb = WPStaging::getInstance()->get("wpdb");
97
+ $this->wpdb = WPStaging::getInstance()->get("wpdb");
98
+
99
+ $stagingPrefix = $this->getStagingPrefix();
100
+
101
+ $tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$stagingPrefix}%'");
102
+
103
+ $this->tables = array();
104
+
105
+ foreach ($tables as $table) {
106
+ $this->tables[] = array(
107
+ "name" => $table->Name,
108
+ "size" => $this->formatSize(($table->Data_length + $table->Index_length))
109
+ );
110
+ }
111
+
112
+ $this->tables = json_decode(json_encode($this->tables));
113
+ }
114
+
115
+ /**
116
+ * Check and return prefix of the staging site
117
+ */
118
+ public function getStagingPrefix() {
119
+ // Prefix not defined! Happens if staging site has ben generated with older version of wpstg
120
+ // Try to get staging prefix from wp-config.php of staging site
121
+ if (empty($this->clone->prefix)) {
122
+ // Throw error
123
+ $path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
124
+ if (false === ($content = @file_get_contents($path))) {
125
+ $this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
126
+ // Create a random prefix which hopefully never exists.
127
+ $this->clone->prefix = rand(7, 15) . '_';
128
+ } else {
129
+
130
+ // Get prefix from wp-config.php
131
+ //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
132
+ preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
133
+ //wp_die(var_dump($matches));
134
+
135
+ if (!empty($matches[1])) {
136
+ $this->clone->prefix = $matches[1];
137
+ } else {
138
+ $this->log("Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
139
+ // Create a random prefix which hopefully never exists.
140
+ return $this->clone->prefix = rand(7, 15) . '_';
141
+ }
142
+ }
143
+ }
144
+
145
+ // Check if staging prefix is the same as the live prefix
146
+ if ($this->wpdb->prefix == $this->clone->prefix) {
147
+ $this->log("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
148
+ wp_die("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
149
+ }
150
+
151
+ // Else
152
+ return $this->clone->prefix;
153
+ }
154
+
155
+ /**
156
+ * Format bytes into human readable form
157
+ * @param int $bytes
158
+ * @param int $precision
159
+ * @return string
160
+ */
161
+ public function formatSize($bytes, $precision = 2) {
162
+ if ((int) $bytes < 1) {
163
+ return '';
164
+ }
165
+
166
+ $units = array('B', "KB", "MB", "GB", "TB");
167
+
168
+ $bytes = (int) $bytes;
169
+ $base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
170
+ $pow = pow(1000, $base - floor($base)); // Same rule for 1000
171
+
172
+ return round($pow, $precision) . ' ' . $units[(int) floor($base)];
173
+ }
174
+
175
+ /**
176
+ * @return false
177
+ */
178
+ public function getClone() {
179
+ return $this->clone;
180
+ }
181
+