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

Version Description

  • Fix: Error 500 on some systems when files are collected and plugin iterates through the directories
Download this release

Release Info

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

Code changes from version 2.4.0 to 2.4.2

apps/Backend/Modules/Jobs/Database.php CHANGED
@@ -1,9 +1,9 @@
1
<?php
2
namespace WPStaging\Backend\Modules\Jobs;
3
4
// No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
die;
8
}
9
@@ -14,8 +14,7 @@ use WPStaging\Utils\Strings;
14
* Class Database
15
* @package WPStaging\Backend\Modules\Jobs
16
*/
17
- class Database extends JobExecutable
18
- {
19
20
/**
21
* @var int
@@ -30,36 +29,45 @@ class Database extends JobExecutable
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
/**
@@ -67,21 +75,18 @@ class Database extends JobExecutable
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
@@ -91,13 +96,11 @@ class Database extends JobExecutable
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;
@@ -114,12 +117,13 @@ class Database extends JobExecutable
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
@@ -128,27 +132,25 @@ class Database extends JobExecutable
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();
@@ -159,22 +161,20 @@ class Database extends JobExecutable
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
@@ -185,10 +185,8 @@ class Database extends JobExecutable
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
@@ -202,21 +200,18 @@ class Database extends JobExecutable
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
}
@@ -227,19 +222,17 @@ class Database extends JobExecutable
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
}
@@ -248,17 +241,15 @@ class Database extends JobExecutable
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
/**
@@ -267,16 +258,15 @@ class Database extends JobExecutable
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
+
3
namespace WPStaging\Backend\Modules\Jobs;
4
5
// No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
die;
8
}
9
14
* Class Database
15
* @package WPStaging\Backend\Modules\Jobs
16
*/
17
+ class Database extends JobExecutable {
18
19
/**
20
* @var int
29
/**
30
* Initialize
31
*/
32
+ public function initialize() {
33
// Variables
34
+ $this->isExternalDatabase();
35
+ $this->total = count( $this->options->tables );
36
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
37
$this->isFatalError();
38
+ }
39
+
40
+ /**
41
+ * Check if external database is used
42
+ * @return boolean
43
+ */
44
+ private function isExternalDatabase() {
45
+ if( !empty( $this->options->databaseUser ) ) {
46
+ $this->returnException( __("This staging site is located in another database and needs to be edited with <a href='https://wp-staging.com' target='_blank'>WP Staging Pro</a>","wp-staging") );
47
48
+ }
49
+ return false;
50
+ }
51
+
52
+ /**
53
+ * Return fatal error and stops here if subfolder already exists
54
+ * and mainJob is not updating the clone
55
+ * @return boolean
56
+ */
57
+ private function isFatalError() {
58
+ $path = trailingslashit( get_home_path() ) . $this->options->cloneDirectoryName;
59
+ if( isset( $this->options->mainJob ) && $this->options->mainJob !== 'updating' && is_dir( $path ) ) {
60
+ $this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
61
+ }
62
+ return false;
63
}
64
65
/**
66
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
67
* @return void
68
*/
69
+ protected function calculateTotalSteps() {
70
+ $this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
71
}
72
73
/**
75
* Returns false when over threshold limits are hit or when the job is done, true otherwise
76
* @return bool
77
*/
78
+ protected function execute() {
79
// Over limits threshold
80
+ if( $this->isOverThreshold() ) {
81
// Prepare response and save current progress
82
+ $this->prepareResponse( false, false );
83
$this->saveOptions();
84
return false;
85
}
86
87
// No more steps, finished
88
+ if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
89
+ $this->prepareResponse( true, false );
90
return false;
91
}
92
96
// $this->prepareResponse();
97
// return true;
98
// }
99
// Copy table
100
//if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
101
+ if( !$this->copyTable( $this->options->tables[$this->options->currentStep] ) ) {
102
// Prepare Response
103
+ $this->prepareResponse( false, false );
104
105
// Not finished
106
return true;
117
* Get new prefix for the staging site
118
* @return string
119
*/
120
+ private function getStagingPrefix() {
121
$stagingPrefix = $this->options->prefix;
122
// Make sure prefix of staging site is NEVER identical to prefix of live site!
123
+ if( $stagingPrefix == $this->db->prefix ) {
124
+ //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');
125
+ $this->returnException( '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' );
126
+ }
127
return $stagingPrefix;
128
}
129
132
* @param string $tableName
133
* @return bool
134
*/
135
+ private function copyTable( $tableName ) {
136
137
+ $strings = new Strings();
138
+ $tableName = is_object( $tableName ) ? $tableName->name : $tableName;
139
+ $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->prefix, null, $tableName );
140
141
// Drop table if necessary
142
+ $this->dropTable( $newTableName );
143
144
// Save current job
145
+ $this->setJob( $newTableName );
146
147
// Beginning of the job
148
+ if( !$this->startJob( $newTableName, $tableName ) ) {
149
return true;
150
}
151
152
// Copy data
153
+ $this->copyData( $newTableName, $tableName );
154
155
// Finis the step
156
return $this->finishStep();
161
* @param string $new
162
* @param string $old
163
*/
164
+ private function copyData( $new, $old ) {
165
+ $rows = $this->options->job->start + $this->settings->queryLimit;
166
$this->log(
167
+ "DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
168
);
169
170
$limitation = '';
171
172
+ if( 0 < ( int ) $this->settings->queryLimit ) {
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
185
* Set the job
186
* @param string $table
187
*/
188
+ private function setJob( $table ) {
189
+ if( isset( $this->options->job->current ) ) {
190
return;
191
}
192
200
* @param string $old
201
* @return bool
202
*/
203
+ private function startJob( $new, $old ) {
204
+ if( 0 != $this->options->job->start ) {
205
return true;
206
}
207
208
+ $this->log( "DB Copy: Creating table {$new}" );
209
210
+ $this->db->query( "CREATE TABLE {$new} LIKE {$old}" );
211
212
+ $this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
213
214
+ if( 0 == $this->options->job->total ) {
215
$this->finishStep();
216
return false;
217
}
222
/**
223
* Finish the step
224
*/
225
+ private function finishStep() {
226
// This job is not finished yet
227
+ if( $this->options->job->total > $this->options->job->start ) {
228
return false;
229
}
230
231
// Add it to cloned tables listing
232
+ $this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
233
234
// Reset job
235
+ $this->options->job = new \stdClass();
236
237
return true;
238
}
241
* Drop table if necessary
242
* @param string $new
243
*/
244
+ private function dropTable( $new ) {
245
+ $old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
246
247
+ if( !$this->shouldDropTable( $new, $old ) ) {
248
return;
249
}
250
251
+ $this->log( "DB Copy: {$new} already exists, dropping it first" );
252
+ $this->db->query( "DROP TABLE {$new}" );
253
}
254
255
/**
258
* @param string $old
259
* @return bool
260
*/
261
+ private function shouldDropTable( $new, $old ) {
262
return (
263
+ $old === $new &&
264
+ (
265
+ !isset( $this->options->job->current ) ||
266
+ !isset( $this->options->job->start ) ||
267
0 == $this->options->job->start
268
+ )
269
+ );
270
}
271
272
+ }
apps/Backend/Modules/Jobs/Directories.php CHANGED
@@ -4,14 +4,14 @@ namespace WPStaging\Backend\Modules\Jobs;
4
5
// No Direct Access
6
if( !defined( "WPINC" ) ) {
7
- die;
8
}
9
10
use WPStaging\WPStaging;
11
use WPStaging\Utils\Logger;
12
use WPStaging\Utils\Strings;
13
use WPStaging\Iterators\RecursiveDirectoryIterator;
14
- use WPStaging\Iterators\RecursiveFilterNewLine;
15
use WPStaging\Iterators\RecursiveFilterExclude;
16
17
/**
@@ -20,277 +20,272 @@ use WPStaging\Iterators\RecursiveFilterExclude;
20
*/
21
class Directories extends JobExecutable {
22
23
- /**
24
- * @var array
25
- */
26
- private $files = array();
27
-
28
- /**
29
- * Total steps to do
30
- * @var int
31
- */
32
- private $total = 4;
33
-
34
- /**
35
- * path to the cache file
36
- * @var string
37
- */
38
- private $filename;
39
-
40
- /**
41
- * Initialize
42
- */
43
- public function initialize() {
44
- $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
45
- }
46
-
47
- /**
48
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
49
- * @return void
50
- */
51
- protected function calculateTotalSteps() {
52
-
53
- $this->options->totalSteps = $this->total + count( $this->options->extraDirectories );
54
- }
55
-
56
- /**
57
- * Start Module
58
- * @return object
59
- */
60
- public function start() {
61
-
62
- // Execute steps
63
- $this->run();
64
-
65
- // Save option, progress
66
- $this->saveProgress();
67
-
68
- return ( object ) $this->response;
69
- }
70
-
71
- /**
72
- * Step 0
73
- * Get WP Root files
74
- * Does not collect any sub folders
75
- */
76
- private function getWpRootFiles() {
77
-
78
- // open file handle
79
- $files = $this->open( $this->filename, 'a' );
80
-
81
-
82
- try {
83
-
84
- // Iterate over wp root directory
85
- $iterator = new \DirectoryIterator( \WPStaging\WPStaging::getWPpath() );
86
-
87
- $this->log( "Scanning / for files" );
88
-
89
- // Write path line
90
- foreach ( $iterator as $item ) {
91
- if( !$item->isDot() && $item->isFile() ) {
92
- if( $this->write( $files, $iterator->getFilename() . PHP_EOL ) ) {
93
- $this->options->totalFiles++;
94
-
95
- // Too much cpu time
96
- //$this->options->totalFileSize += $iterator->getSize();
97
- }
98
}
99
- }
100
- } catch ( \Exception $e ) {
101
- $this->returnException( 'Error: ' . $e->getMessage() );
102
- } catch ( \Exception $e ) {
103
- // Skip bad file permissions
104
- }
105
-
106
- $this->close( $files );
107
- return true;
108
- }
109
-
110
- /**
111
- * Step 2
112
- * Get WP Content Files
113
- */
114
- private function getWpContentFiles() {
115
-
116
- // Skip it
117
- if( $this->isDirectoryExcluded( WP_CONTENT_DIR ) ) {
118
$this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR );
119
- return true;
120
- }
121
- // open file handle
122
- $files = $this->open( $this->filename, 'a' );
123
-
124
- $excludeWpContent = array(
125
- 'cache',
126
- 'wps-hide-login',
127
'node_modules'
128
- );
129
130
- try {
131
132
- // Iterate over content directory
133
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
134
135
- // Exclude new line file names
136
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
137
138
- // Exclude uploads, plugins or themes
139
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_excl_folders', $excludeWpContent ) );
140
- // Recursively iterate over content directory
141
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
142
143
- $this->log( "Scanning /wp-content for its sub-directories and files" );
144
145
- // Write path line
146
- foreach ( $iterator as $item ) {
147
- if( $item->isFile() ) {
148
- if( $this->write( $files, 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
149
- $this->options->totalFiles++;
150
151
- // Too much cpu time
152
- //$this->options->totalFileSize += $iterator->getSize();
153
- }
154
}
155
- }
156
- } catch ( \Exception $e ) {
157
- //$this->returnException('Out of disk space.');
158
- throw new \Exception( 'Error: ' . $e->getMessage() );
159
- } catch ( \Exception $e ) {
160
- // Skip bad file permissions
161
- }
162
-
163
- // close the file handler
164
- $this->close( $files );
165
- return true;
166
- }
167
-
168
- /**
169
- * Step 2
170
- * @return boolean
171
- * @throws \Exception
172
- */
173
- private function getWpIncludesFiles() {
174
-
175
- // Skip it
176
- if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR ) ) {
177
- $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
178
- return true;
179
- }
180
181
- // open file handle and attach data to end of file
182
- $files = $this->open( $this->filename, 'a' );
183
184
- try {
185
186
- // Iterate over wp-admin directory
187
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
188
189
- // Exclude new line file names
190
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
191
192
- // Recursively iterate over wp-includes directory
193
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
194
195
- $this->log( "Scanning /wp-includes for its sub-directories and files" );
196
197
- // Write files
198
- foreach ( $iterator as $item ) {
199
- if( $item->isFile() ) {
200
- if( $this->write( $files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
201
- $this->options->totalFiles++;
202
203
- // Too much cpu time
204
- //$this->options->totalFileSize += $iterator->getSize();
205
- }
206
}
207
- }
208
- } catch ( \Exception $e ) {
209
- //$this->returnException('Out of disk space.');
210
- throw new \Exception( 'Error: ' . $e->getMessage() );
211
- } catch ( \Exception $e ) {
212
- // Skip bad file permissions
213
- }
214
-
215
- // close the file handler
216
- $this->close( $files );
217
- return true;
218
- }
219
-
220
- /**
221
- * Step 3
222
- * @return boolean
223
- * @throws \Exception
224
- */
225
- private function getWpAdminFiles() {
226
-
227
- // Skip it
228
- if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR ) ) {
229
- $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
230
- return true;
231
- }
232
233
- // open file handle and attach data to end of file
234
- $files = $this->open( $this->filename, 'a' );
235
236
- try {
237
238
- // Iterate over wp-admin directory
239
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
240
241
- // Exclude new line file names
242
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
243
244
- // Recursively iterate over content directory
245
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
246
247
- $this->log( "Scanning /wp-admin for its sub-directories and files" );
248
249
- // Write path line
250
- foreach ( $iterator as $item ) {
251
- if( $item->isFile() ) {
252
- if( $this->write( $files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
253
- $this->options->totalFiles++;
254
- // Too much cpu time
255
- //$this->options->totalFileSize += $iterator->getSize();
256
- }
257
}
258
- }
259
- } catch ( \Exception $e ) {
260
- $this->returnException( 'Error: ' . $e->getMessage() );
261
- //throw new \Exception('Error: ' . $e->getMessage());
262
- } catch ( \Exception $e ) {
263
- // Skip bad file permissions
264
- }
265
-
266
- // close the file handler
267
- $this->close( $files );
268
- return true;
269
- }
270
-
271
- /**
272
- * Step 4 - x
273
- * Get extra folders of the wp root level
274
- * Does not collect wp-includes, wp-admin and wp-content folder
275
- */
276
- private function getExtraFiles( $folder ) {
277
278
if( !is_dir( $folder ) ) {
279
return true;
280
}
281
282
- // open file handle and attach data to end of file
283
- $files = $this->open( $this->filename, 'a' );
284
-
285
- try {
286
287
- // Iterate over extra directory
288
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
289
290
- // Exclude new line file names
291
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
292
293
- // Exclude wp core folders
294
// $exclude = array('wp-includes',
295
// 'wp-admin',
296
// 'wp-content');
@@ -299,208 +294,203 @@ class Directories extends JobExecutable {
299
// foreach ($this->options->excludedDirectories as $key => $value){
300
// $excludeMore[] = $this->getLastElemAfterString('/', $value);
301
// }
302
- //$exclude = array_merge($exclude, $excludeMore);
303
-
304
- $exclude = array();
305
-
306
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
307
- // Recursively iterate over content directory
308
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
309
-
310
- $strings = new Strings();
311
- $this->log( "Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files" );
312
-
313
- // Write path line
314
- foreach ( $iterator as $item ) {
315
- if( $item->isFile() ) {
316
- //if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
317
- if( $this->write( $files, str_replace( \WPStaging\WPStaging::getWPpath(), '', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
318
- $this->options->totalFiles++;
319
- // Too much cpu time
320
- //$this->options->totalFileSize += $iterator->getSize();
321
- }
322
}
323
- }
324
- } catch ( \Exception $e ) {
325
- $this->returnException( 'Error: ' . $e->getMessage() );
326
- } catch ( \Exception $e ) {
327
- // Skip bad file permissions
328
- }
329
-
330
- // close the file handler
331
- $this->close( $files );
332
- return true;
333
- }
334
-
335
- /**
336
- * Closes a file handle
337
- *
338
- * @param resource $handle File handle to close
339
- * @return boolean
340
- */
341
- public function close( $handle ) {
342
- return @fclose( $handle );
343
- }
344
-
345
- /**
346
- * Opens a file in specified mode
347
- *
348
- * @param string $file Path to the file to open
349
- * @param string $mode Mode in which to open the file
350
- * @return resource
351
- * @throws Exception
352
- */
353
- public function open( $file, $mode ) {
354
-
355
- $file_handle = @fopen( $file, $mode );
356
- if( false === $file_handle ) {
357
- $this->returnException( sprintf( __( 'Unable to open %s with mode %s', 'wp-staging' ), $file, $mode ) );
358
- //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wp-staging'), $file, $mode));
359
- }
360
-
361
- return $file_handle;
362
- }
363
-
364
- /**
365
- * Write contents to a file
366
- *
367
- * @param resource $handle File handle to write to
368
- * @param string $content Contents to write to the file
369
- * @return integer
370
- * @throws Exception
371
- * @throws Exception
372
- */
373
- public function write( $handle, $content ) {
374
- $write_result = @fwrite( $handle, $content );
375
- if( false === $write_result ) {
376
- if( ( $meta = \stream_get_meta_data( $handle ) ) ) {
377
- //$this->returnException(sprintf(__('Unable to write to: %s', 'wp-staging'), $meta['uri']));
378
- throw new \Exception( sprintf( __( 'Unable to write to: %s', 'wp-staging' ), $meta['uri'] ) );
379
- }
380
- } elseif( strlen( $content ) !== $write_result ) {
381
- //$this->returnException(__('Out of disk space.', 'wp-staging'));
382
- throw new \Exception( __( 'Out of disk space.', 'wp-staging' ) );
383
- }
384
-
385
- return $write_result;
386
- }
387
-
388
- /**
389
- * Execute the Current Step
390
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
391
- * @return bool
392
- */
393
- protected function execute() {
394
-
395
- // No job left to execute
396
- if( $this->isFinished() ) {
397
- $this->prepareResponse( true, false );
398
- return false;
399
- }
400
-
401
-
402
- if( $this->options->currentStep == 0 ) {
403
- $this->getWpRootFiles();
404
- $this->prepareResponse( false, true );
405
- return false;
406
- }
407
-
408
- if( $this->options->currentStep == 1 ) {
409
- $this->getWpContentFiles();
410
- $this->prepareResponse( false, true );
411
- return false;
412
- }
413
-
414
- if( $this->options->currentStep == 2 ) {
415
- $this->getWpIncludesFiles();
416
- $this->prepareResponse( false, true );
417
- return false;
418
- }
419
-
420
- if( $this->options->currentStep == 3 ) {
421
- $this->getWpAdminFiles();
422
- $this->prepareResponse( false, true );
423
- return false;
424
- }
425
-
426
- if( isset( $this->options->extraDirectories[$this->options->currentStep - $this->total] ) ) {
427
- $this->getExtraFiles( $this->options->extraDirectories[$this->options->currentStep - $this->total] );
428
- $this->prepareResponse( false, true );
429
- return false;
430
- }
431
-
432
-
433
- // Prepare response
434
- $this->prepareResponse( false, true );
435
- // Not finished
436
- return true;
437
- }
438
-
439
- /**
440
- * Checks Whether There is Any Job to Execute or Not
441
- * @return bool
442
- */
443
- protected function isFinished() {
444
- if( $this->options->currentStep >= $this->options->totalSteps ) {
445
- return true;
446
- }
447
-
448
- // return (
449
- // //$this->options->currentStep > $this->total ||
450
- // $this->options->currentStep >= $this->options->totalSteps
451
- // );
452
- }
453
-
454
- /**
455
- * Save files
456
- * @return bool
457
- */
458
- protected function saveProgress() {
459
- return $this->saveOptions();
460
- }
461
-
462
- /**
463
- * Get files
464
- * @return void
465
- */
466
- protected function getFiles() {
467
- $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
468
-
469
- if( false === ($this->files = @file_get_contents( $fileName )) ) {
470
- $this->files = array();
471
- return;
472
- }
473
-
474
- $this->files = explode( PHP_EOL, $this->files );
475
- }
476
-
477
- /**
478
- * Replace forward slash with current directory separator
479
- *
480
- * @param string $path Path
481
- *
482
- * @return string
483
- */
484
- private function sanitizeDirectorySeparator( $path ) {
485
- $string = str_replace( "/", "\\", $path );
486
- return str_replace( '\\\\', '\\', $string );
487
- }
488
-
489
- /**
490
- * Check if directory is excluded
491
- * @param string $directory
492
- * @return bool
493
- */
494
- protected function isDirectoryExcluded( $directory ) {
495
- $directory = $this->sanitizeDirectorySeparator( $directory );
496
- foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
497
- $excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
498
- if( strpos( trailingslashit( $directory ), trailingslashit( $excludedDirectory ) ) === 0 ) {
499
return true;
500
- }
501
- }
502
503
- return false;
504
- }
505
506
}
4
5
// No Direct Access
6
if( !defined( "WPINC" ) ) {
7
+ die;
8
}
9
10
use WPStaging\WPStaging;
11
use WPStaging\Utils\Logger;
12
use WPStaging\Utils\Strings;
13
use WPStaging\Iterators\RecursiveDirectoryIterator;
14
+ //use WPStaging\Iterators\RecursiveFilterNewLine;
15
use WPStaging\Iterators\RecursiveFilterExclude;
16
17
/**
20
*/
21
class Directories extends JobExecutable {
22
23
+ /**
24
+ * @var array
25
+ */
26
+ private $files = array();
27
+
28
+ /**
29
+ * Total steps to do
30
+ * @var int
31
+ */
32
+ private $total = 4;
33
+
34
+ /**
35
+ * path to the cache file
36
+ * @var string
37
+ */
38
+ private $filename;
39
+
40
+ /**
41
+ * Initialize
42
+ */
43
+ public function initialize() {
44
+ $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
45
+ }
46
+
47
+ /**
48
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
49
+ * @return void
50
+ */
51
+ protected function calculateTotalSteps() {
52
+
53
+ $this->options->totalSteps = $this->total + count( $this->options->extraDirectories );
54
+ }
55
+
56
+ /**
57
+ * Start Module
58
+ * @return object
59
+ */
60
+ public function start() {
61
+
62
+ // Execute steps
63
+ $this->run();
64
+
65
+ // Save option, progress
66
+ $this->saveProgress();
67
+
68
+ return ( object ) $this->response;
69
+ }
70
+
71
+ /**
72
+ * Step 0
73
+ * Get WP Root files
74
+ * Does not collect any sub folders
75
+ */
76
+ private function getWpRootFiles() {
77
+
78
+ // open file handle
79
+ $files = $this->open( $this->filename, 'a' );
80
+
81
+
82
+ try {
83
+
84
+ // Iterate over wp root directory
85
+ $iterator = new \DirectoryIterator( \WPStaging\WPStaging::getWPpath() );
86
+
87
+ $this->log( "Scanning / for files" );
88
+
89
+ // Write path line
90
+ foreach ( $iterator as $item ) {
91
+ if( !$item->isDot() && $item->isFile() ) {
92
+ if( $this->write( $files, $iterator->getFilename() . PHP_EOL ) ) {
93
+ $this->options->totalFiles++;
94
+
95
+ // Too much cpu time
96
+ //$this->options->totalFileSize += $iterator->getSize();
97
+ }
98
+ }
99
}
100
+ } catch ( \Exception $e ) {
101
+ $this->returnException( 'Error: ' . $e->getMessage() );
102
+ } catch ( \Exception $e ) {
103
+ // Skip bad file permissions
104
+ }
105
+
106
+ $this->close( $files );
107
+ return true;
108
+ }
109
+
110
+ /**
111
+ * Step 2
112
+ * Get WP Content Files
113
+ */
114
+ private function getWpContentFiles() {
115
+
116
+ // Skip it
117
+ if( $this->isDirectoryExcluded( WP_CONTENT_DIR ) ) {
118
$this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR );
119
+ return true;
120
+ }
121
+ // open file handle
122
+ $files = $this->open( $this->filename, 'a' );
123
+
124
+ $excludeWpContent = array(
125
+ 'cache',
126
+ 'wps-hide-login',
127
'node_modules'
128
+ );
129
130
+ try {
131
132
+ // Iterate over content directory
133
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
134
135
+ // Exclude new line file names
136
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterNewline( $iterator );
137
+ // Exclude uploads, plugins or themes
138
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_excl_folders', $excludeWpContent ) );
139
140
+ // Recursively iterate over content directory
141
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
142
143
+ $this->log( "Scanning /wp-content for its sub-directories and files" );
144
145
+ // Write path line
146
+ foreach ( $iterator as $item ) {
147
+ if( $item->isFile() ) {
148
+ if( $this->write( $files, 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
149
+ $this->options->totalFiles++;
150
151
+ // Too much cpu time
152
+ //$this->options->totalFileSize += $iterator->getSize();
153
+ }
154
+ }
155
}
156
+ } catch ( \Exception $e ) {
157
+ $this->returnException( 'Error: ' . $e->getMessage() );
158
+ //throw new \Exception( 'Error: ' . $e->getMessage() );
159
+ }
160
+
161
+ // close the file handler
162
+ $this->close( $files );
163
+ return true;
164
+ }
165
+
166
+ /**
167
+ * Step 2
168
+ * @return boolean
169
+ * @throws \Exception
170
+ */
171
+ private function getWpIncludesFiles() {
172
173
+ // Skip it
174
+ if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR ) ) {
175
+ $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
176
+ return true;
177
+ }
178
179
+ // open file handle and attach data to end of file
180
+ $files = $this->open( $this->filename, 'a' );
181
182
+ try {
183
184
+ // Iterate over wp-admin directory
185
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
186
187
+ // Exclude new line file names
188
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
189
+ // Recursively iterate over wp-includes directory
190
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
191
192
+ $this->log( "Scanning /wp-includes for its sub-directories and files" );
193
194
+ // Write files
195
+ foreach ( $iterator as $item ) {
196
+ if( $item->isFile() ) {
197
+ if( $this->write( $files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
198
+ $this->options->totalFiles++;
199
200
+ // Too much cpu time
201
+ //$this->options->totalFileSize += $iterator->getSize();
202
+ }
203
+ }
204
}
205
+ } catch ( \Exception $e ) {
206
+ //$this->returnException('Out of disk space.');
207
+ throw new \Exception( 'Error: ' . $e->getMessage() );
208
+ } catch ( \Exception $e ) {
209
+ // Skip bad file permissions
210
+ }
211
+
212
+ // close the file handler
213
+ $this->close( $files );
214
+ return true;
215
+ }
216
+
217
+ /**
218
+ * Step 3
219
+ * @return boolean
220
+ * @throws \Exception
221
+ */
222
+ private function getWpAdminFiles() {
223
224
+ // Skip it
225
+ if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR ) ) {
226
+ $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
227
+ return true;
228
+ }
229
230
+ // open file handle and attach data to end of file
231
+ $files = $this->open( $this->filename, 'a' );
232
233
+ try {
234
235
+ // Iterate over wp-admin directory
236
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
237
238
+ // Exclude new line file names
239
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
240
+ // Recursively iterate over content directory
241
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
242
243
+ $this->log( "Scanning /wp-admin for its sub-directories and files" );
244
245
+ // Write path line
246
+ foreach ( $iterator as $item ) {
247
+ if( $item->isFile() ) {
248
+ if( $this->write( $files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
249
+ $this->options->totalFiles++;
250
+ // Too much cpu time
251
+ //$this->options->totalFileSize += $iterator->getSize();
252
+ }
253
+ }
254
}
255
+ } catch ( \Exception $e ) {
256
+ $this->returnException( 'Error: ' . $e->getMessage() );
257
+ //throw new \Exception('Error: ' . $e->getMessage());
258
+ } catch ( \Exception $e ) {
259
+ // Skip bad file permissions
260
+ }
261
+
262
+ // close the file handler
263
+ $this->close( $files );
264
+ return true;
265
+ }
266
+
267
+ /**
268
+ * Step 4 - x
269
+ * Get extra folders of the wp root level
270
+ * Does not collect wp-includes, wp-admin and wp-content folder
271
+ */
272
+ private function getExtraFiles( $folder ) {
273
274
if( !is_dir( $folder ) ) {
275
return true;
276
}
277
278
+ // open file handle and attach data to end of file
279
+ $files = $this->open( $this->filename, 'a' );
280
281
+ try {
282
283
+ // Iterate over extra directory
284
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
285
286
+ // Exclude new line file names
287
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
288
+ // Exclude wp core folders
289
// $exclude = array('wp-includes',
290
// 'wp-admin',
291
// 'wp-content');
294
// foreach ($this->options->excludedDirectories as $key => $value){
295
// $excludeMore[] = $this->getLastElemAfterString('/', $value);
296
// }
297
+ //$exclude = array_merge($exclude, $excludeMore);
298
+
299
+ $exclude = array();
300
+
301
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
302
+ // Recursively iterate over content directory
303
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
304
+
305
+ $strings = new Strings();
306
+ $this->log( "Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files" );
307
+
308
+ // Write path line
309
+ foreach ( $iterator as $item ) {
310
+ if( $item->isFile() ) {
311
+ //if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
312
+ if( $this->write( $files, str_replace( \WPStaging\WPStaging::getWPpath(), '', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
313
+ $this->options->totalFiles++;
314
+ // Too much cpu time
315
+ //$this->options->totalFileSize += $iterator->getSize();
316
+ }
317
+ }
318
}
319
+ } catch ( \Exception $e ) {
320
+ $this->returnException( 'Error: ' . $e->getMessage() );
321
+ } catch ( \Exception $e ) {
322
+ // Skip bad file permissions
323
+ }
324
+
325
+ // close the file handler
326
+ $this->close( $files );
327
+ return true;
328
+ }
329
+
330
+ /**
331
+ * Closes a file handle
332
+ *
333
+ * @param resource $handle File handle to close
334
+ * @return boolean
335
+ */
336
+ public function close( $handle ) {
337
+ return @fclose( $handle );
338
+ }
339
+
340
+ /**
341
+ * Opens a file in specified mode
342
+ *
343
+ * @param string $file Path to the file to open
344
+ * @param string $mode Mode in which to open the file
345
+ * @return resource
346
+ * @throws Exception
347
+ */
348
+ public function open( $file, $mode ) {
349
+
350
+ $file_handle = @fopen( $file, $mode );
351
+ if( false === $file_handle ) {
352
+ $this->returnException( sprintf( __( 'Unable to open %s with mode %s', 'wp-staging' ), $file, $mode ) );
353
+ //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wp-staging'), $file, $mode));
354
+ }
355
+
356
+ return $file_handle;
357
+ }
358
+
359
+ /**
360
+ * Write contents to a file
361
+ *
362
+ * @param resource $handle File handle to write to
363
+ * @param string $content Contents to write to the file
364
+ * @return integer
365
+ * @throws Exception
366
+ * @throws Exception
367
+ */
368
+ public function write( $handle, $content ) {
369
+ $write_result = @fwrite( $handle, $content );
370
+ if( false === $write_result ) {
371
+ if( ( $meta = \stream_get_meta_data( $handle ) ) ) {
372
+ //$this->returnException(sprintf(__('Unable to write to: %s', 'wp-staging'), $meta['uri']));
373
+ throw new \Exception( sprintf( __( 'Unable to write to: %s', 'wp-staging' ), $meta['uri'] ) );
374
+ }
375
+ } elseif( strlen( $content ) !== $write_result ) {
376
+ //$this->returnException(__('Out of disk space.', 'wp-staging'));
377
+ throw new \Exception( __( 'Out of disk space.', 'wp-staging' ) );
378
+ }
379
+
380
+ return $write_result;
381
+ }
382
+
383
+ /**
384
+ * Execute the Current Step
385
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
386
+ * @return bool
387
+ */
388
+ protected function execute() {
389
+
390
+ // No job left to execute
391
+ if( $this->isFinished() ) {
392
+ $this->prepareResponse( true, false );
393
+ return false;
394
+ }
395
+
396
+
397
+ if( $this->options->currentStep == 0 ) {
398
+ $this->getWpRootFiles();
399
+ $this->prepareResponse( false, true );
400
+ return false;
401
+ }
402
+
403
+ if( $this->options->currentStep == 1 ) {
404
+ $this->getWpContentFiles();
405
+ $this->prepareResponse( false, true );
406
+ return false;
407
+ }
408
+
409
+ if( $this->options->currentStep == 2 ) {
410
+ $this->getWpIncludesFiles();
411
+ $this->prepareResponse( false, true );
412
+ return false;
413
+ }
414
+
415
+ if( $this->options->currentStep == 3 ) {
416
+ $this->getWpAdminFiles();
417
+ $this->prepareResponse( false, true );
418
+ return false;
419
+ }
420
+
421
+ if( isset( $this->options->extraDirectories[$this->options->currentStep - $this->total] ) ) {
422
+ $this->getExtraFiles( $this->options->extraDirectories[$this->options->currentStep - $this->total] );
423
+ $this->prepareResponse( false, true );
424
+ return false;
425
+ }
426
+
427
+
428
+ // Prepare response
429
+ $this->prepareResponse( false, true );
430
+ // Not finished
431
+ return true;
432
+ }
433
+
434
+ /**
435
+ * Checks Whether There is Any Job to Execute or Not
436
+ * @return bool
437
+ */
438
+ protected function isFinished() {
439
+ if( $this->options->currentStep >= $this->options->totalSteps ) {
440
return true;
441
+ }
442
+ }
443
+
444
+ /**
445
+ * Save files
446
+ * @return bool
447
+ */
448
+ protected function saveProgress() {
449
+ return $this->saveOptions();
450
+ }
451
+
452
+ /**
453
+ * Get files
454
+ * @return void
455
+ */
456
+ protected function getFiles() {
457
+ $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
458
+
459
+ if( false === ($this->files = @file_get_contents( $fileName )) ) {
460
+ $this->files = array();
461
+ return;
462
+ }
463
+
464
+ $this->files = explode( PHP_EOL, $this->files );
465
+ }
466
+
467
+ /**
468
+ * Replace forward slash with current directory separator
469
+ *
470
+ * @param string $path Path
471
+ *
472
+ * @return string
473
+ */
474
+ private function sanitizeDirectorySeparator( $path ) {
475
+ $string = str_replace( "/", "\\", $path );
476
+ return str_replace( '\\\\', '\\', $string );
477
+ }
478
+
479
+ /**
480
+ * Check if directory is excluded
481
+ * @param string $directory
482
+ * @return bool
483
+ */
484
+ protected function isDirectoryExcluded( $directory ) {
485
+ $directory = $this->sanitizeDirectorySeparator( $directory );
486
+ foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
487
+ $excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
488
+ if( strpos( trailingslashit( $directory ), trailingslashit( $excludedDirectory ) ) === 0 ) {
489
+ return true;
490
+ }
491
+ }
492
493
+ return false;
494
+ }
495
496
}
apps/Backend/Modules/Jobs/Multisite/Data.php CHANGED
@@ -1,1009 +1,1009 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs\Multisite;
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\Multisite;
14
- use WPStaging\Utils\Strings;
15
- use WPStaging\Backend\Modules\Jobs\JobExecutable;
16
-
17
- /**
18
- * Class Data
19
- * @package WPStaging\Backend\Modules\Jobs
20
- */
21
- class Data extends JobExecutable {
22
-
23
- /**
24
- * @var \wpdb
25
- */
26
- private $db;
27
-
28
- /**
29
- * @var string
30
- */
31
- private $prefix;
32
-
33
- /**
34
- * Tables e.g wpstg3_options
35
- * @var array
36
- */
37
- private $tables;
38
-
39
- /**
40
- * Initialize
41
- */
42
- public function initialize() {
43
- $this->db = WPStaging::getInstance()->get( "wpdb" );
44
-
45
- $this->prefix = $this->options->prefix;
46
-
47
- $this->getTables();
48
-
49
- // Fix current step
50
- if( 0 == $this->options->currentStep ) {
51
- $this->options->currentStep = 0;
52
- }
53
- }
54
-
55
- /**
56
- * Get a list of tables to copy
57
- */
58
- private function getTables() {
59
- $strings = new Strings();
60
- $this->tables = array();
61
- foreach ( $this->options->tables as $table ) {
62
- $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
63
- }
64
- // Add extra global tables from main multisite (wpstgx_users and wpstgx_usermeta)
65
- $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, 'users' );
66
- $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, 'usermeta' );
67
- }
68
-
69
- /**
70
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
71
- * @return void
72
- */
73
- protected function calculateTotalSteps() {
74
- $this->options->totalSteps = 18;
75
- }
76
-
77
- /**
78
- * Start Module
79
- * @return object
80
- */
81
- public function start() {
82
- // Execute steps
83
- $this->run();
84
-
85
- // Save option, progress
86
- $this->saveOptions();
87
-
88
- return ( object ) $this->response;
89
- }
90
-
91
- /**
92
- * Execute the Current Step
93
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
94
- * @return bool
95
- */
96
- protected function execute() {
97
- // Fatal error. Let this happen never and break here immediately
98
- if( $this->isRoot() ) {
99
- return false;
100
- }
101
-
102
- // Over limits threshold
103
- if( $this->isOverThreshold() ) {
104
- // Prepare response and save current progress
105
- $this->prepareResponse( false, false );
106
- $this->saveOptions();
107
- return false;
108
- }
109
-
110
- // No more steps, finished
111
- if( $this->isFinished() ) {
112
- $this->prepareResponse( true, false );
113
- return false;
114
- }
115
-
116
- // Execute step
117
- $stepMethodName = "step" . $this->options->currentStep;
118
- if( !$this->{$stepMethodName}() ) {
119
- $this->prepareResponse( false, false );
120
- return false;
121
- }
122
-
123
- // Prepare Response
124
- $this->prepareResponse();
125
-
126
- // Not finished
127
- return true;
128
- }
129
-
130
- /**
131
- * Checks Whether There is Any Job to Execute or Not
132
- * @return bool
133
- */
134
- protected function isFinished() {
135
- return (
136
- $this->options->currentStep > $this->options->totalSteps ||
137
- !method_exists( $this, "step" . $this->options->currentStep )
138
- );
139
- }
140
-
141
- /**
142
- * Check if current operation is done on the root folder or on the live DB
143
- * @return boolean
144
- */
145
- protected function isRoot() {
146
-
147
- // Prefix is the same as the one of live site
148
- //$wpdb = WPStaging::getInstance()->get( "wpdb" );
149
- if( $this->db->prefix === $this->prefix ) {
150
- return true;
151
- }
152
-
153
- // CloneName is empty
154
- $name = ( array ) $this->options->cloneDirectoryName;
155
- if( empty( $name ) ) {
156
- return true;
157
- }
158
-
159
- // Live domain === Staging domain
160
- if( $this->multisiteHomeDomain . $this->options->cloneDirectoryName === $this->multisiteHomeDomain ) {
161
- return true;
162
- }
163
-
164
- return false;
165
- }
166
-
167
- /**
168
- * Check if table exists
169
- * @param string $table
170
- * @return boolean
171
- */
172
- protected function isTable( $table ) {
173
- if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
174
- $this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
175
- return false;
176
- }
177
- return true;
178
- }
179
-
180
- /**
181
- * Return absolute destination path
182
- * @return string
183
- */
184
- // private function getAbsDestination() {
185
- // if( empty( $this->options->cloneDir ) ) {
186
- // return \WPStaging\WPStaging::getWPpath();
187
- // }
188
- // return trailingslashit( $this->options->cloneDir );
189
- // }
190
-
191
- /**
192
- * Copy wp-config.php if it is located outside of root one level up
193
- * @todo Needs some more testing before it will be released
194
- * @return boolean
195
- */
196
- protected function step0() {
197
- $this->log( "Preparing Data Step0: Check if wp-config.php is located in root path", Logger::TYPE_INFO );
198
-
199
- $dir = trailingslashit( dirname( ABSPATH ) );
200
-
201
- $source = $dir . 'wp-config.php';
202
-
203
- $destination = $this->options->destinationDir . 'wp-config.php';
204
-
205
-
206
- // Do not do anything
207
- if( (!is_file( $source ) && !is_link( $source )) || is_file( $destination ) ) {
208
- $this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
209
- return true;
210
- }
211
-
212
- // Copy target of a symbolic link
213
- if( is_link( $source ) ) {
214
- $this->log( "Preparing Data Step0: Symbolic link found...", Logger::TYPE_INFO );
215
- if( !@copy( readlink( $source ), $destination ) ) {
216
- $errors = error_get_last();
217
- $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
218
- return true;
219
- }
220
- }
221
-
222
- // Copy file wp-config.php
223
- if( !@copy( $source, $destination ) ) {
224
- $errors = error_get_last();
225
- $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
226
- return true;
227
- }
228
- $this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
229
- return true;
230
- }
231
-
232
- /**
233
- * Replace "siteurl" and "home"
234
- * @return bool
235
- */
236
- protected function step1() {
237
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
238
-
239
- // Skip - Table does not exist
240
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
241
- return true;
242
- }
243
- // Skip - Table is not selected or updated
244
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
245
- $this->log( "Preparing Data Step1: Skipping" );
246
- return true;
247
- }
248
-
249
- // Installed in sub-directory
250
- // if( $this->isSubDir() ) {
251
- // $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName );
252
- // // Replace URLs
253
- // $result = $this->db->query(
254
- // $this->db->prepare(
255
- // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName
256
- // )
257
- // );
258
- // } else
259
- // {
260
- // $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName );
261
- // // Replace URLs
262
- // $result = $this->db->query(
263
- // $this->db->prepare(
264
- // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName
265
- // )
266
- // );
267
- // }
268
-
269
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . $this->getStagingSiteUrl() );
270
- // Replace URLs
271
- $result = $this->db->query(
272
- $this->db->prepare(
273
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->getStagingSiteUrl()
274
- )
275
- );
276
-
277
-
278
-
279
- // All good
280
- if( $result ) {
281
- return true;
282
- }
283
-
284
- $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
285
- return false;
286
- }
287
-
288
- /**
289
- * Update "wpstg_is_staging_site"
290
- * @return bool
291
- */
292
- protected function step2() {
293
-
294
- $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
295
-
296
- // Skip - Table does not exist
297
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
298
- return true;
299
- }
300
- // Skip - Table is not selected or updated
301
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
302
- $this->log( "Preparing Data Step2: Skipping" );
303
- return true;
304
- }
305
-
306
- $result = $this->db->query(
307
- $this->db->prepare(
308
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
309
- )
310
- );
311
-
312
- // No errors but no option name such as wpstg_is_staging_site
313
- if( '' === $this->db->last_error && 0 == $result ) {
314
- $result = $this->db->query(
315
- $this->db->prepare(
316
- "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
317
- )
318
- );
319
- }
320
-
321
- // All good
322
- if( $result ) {
323
- return true;
324
- }
325
-
326
- $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
327
- return false;
328
- }
329
-
330
- /**
331
- * Update rewrite_rules
332
- * @return bool
333
- */
334
- protected function step3() {
335
-
336
- $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
337
-
338
- // Skip - Table does not exist
339
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
340
- return true;
341
- }
342
-
343
- // Skip - Table is not selected or updated
344
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
345
- $this->log( "Preparing Data Step3: Skipping" );
346
- return true;
347
- }
348
-
349
- $result = $this->db->query(
350
- $this->db->prepare(
351
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
352
- )
353
- );
354
-
355
- // All good
356
- if( $result ) {
357
- return true;
358
- }
359
-
360
- $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
361
- return true;
362
- }
363
-
364
- /**
365
- * Update Table Prefix in wp_usermeta and wp_options
366
- * @return bool
367
- */
368
- protected function step4() {
369
- $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. {$this->db->last_error}" );
370
-
371
- // Skip - Table does not exist
372
- if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
373
- return true;
374
- }
375
-
376
- // Skip - Table is not selected or updated
377
- if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
378
- $this->log( "Preparing Data Step4: Skipping" );
379
- return true;
380
- }
381
-
382
- $update = $this->db->query(
383
- $this->db->prepare(
384
- "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 . "_%"
385
- )
386
- );
387
-
388
- if( !$update ) {
389
- $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
390
- $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
391
- return false;
392
- }
393
- return true;
394
- }
395
-
396
- /**
397
- * Update $table_prefix in wp-config.php
398
- * @return bool
399
- */
400
- protected function step5() {
401
- $path = $this->options->destinationDir . "wp-config.php";
402
-
403
- $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
404
- if( false === ($content = file_get_contents( $path )) ) {
405
- $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
406
- return false;
407
- }
408
-
409
- // Replace table prefix
410
- $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
411
-
412
- // Replace URLs
413
- $content = str_replace( $this->multisiteHomeDomain, $this->getStagingSiteUrl(), $content );
414
-
415
- if( false === @file_put_contents( $path, $content ) ) {
416
- $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
417
- return false;
418
- }
419
-
420
- return true;
421
- }
422
-
423
- /**
424
- * Reset index.php to original file
425
- * This is needed if live site is located in subfolder
426
- * Check first if main wordpress is used in subfolder and index.php in parent directory
427
- * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
428
- * @return bool
429
- */
430
- protected function step6() {
431
-
432
- if( !$this->isSubDir() ) {
433
- $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
434
- return true;
435
- }
436
-
437
- $path = $this->options->destinationDir . "index.php";
438
-
439
- if( false === ($content = file_get_contents( $path )) ) {
440
- $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
441
- return false;
442
- }
443
-
444
-
445
- if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
446
- $this->log(
447
- "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
448
- );
449
- return false;
450
- }
451
- $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
452
-
453
- $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
454
-
455
- $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
456
- $replace.= " // Changed by WP-Staging";
457
-
458
-
459
-
460
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
461
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
462
- return false;
463
- }
464
-
465
- if( false === @file_put_contents( $path, $content ) ) {
466
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
467
- return false;
468
- }
469
- $this->Log( "Preparing Data: Finished Step 6 successfully" );
470
- return true;
471
- }
472
-
473
- /**
474
- * Update wpstg_rmpermalinks_executed
475
- * @return bool
476
- */
477
- protected function step7() {
478
-
479
- $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
480
-
481
- // Skip - Table does not exist
482
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
483
- return true;
484
- }
485
-
486
- // Skip - Table is not selected or updated
487
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
488
- $this->log( "Preparing Data Step7: Skipping" );
489
- return true;
490
- }
491
-
492
- $result = $this->db->query(
493
- $this->db->prepare(
494
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
495
- )
496
- );
497
-
498
- // All good
499
- if( $result ) {
500
- $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
501
- return true;
502
- }
503
-
504
- $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
505
- return true;
506
- }
507
-
508
- /**
509
- * Update permalink_structure
510
- * @return bool
511
- */
512
- protected function step8() {
513
-
514
- $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
515
-
516
- // Skip - Table does not exist
517
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
518
- return true;
519
- }
520
-
521
- // Skip - Table is not selected or updated
522
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
523
- $this->log( "Preparing Data Step8: Skipping" );
524
- return true;
525
- }
526
-
527
- $result = $this->db->query(
528
- $this->db->prepare(
529
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
530
- )
531
- );
532
-
533
- // All good
534
- if( $result ) {
535
- $this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
536
- return true;
537
- }
538
-
539
- $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
540
- return true;
541
- }
542
-
543
- /**
544
- * Update blog_public option to not allow staging site to be indexed by search engines
545
- * @return bool
546
- */
547
- protected function step9() {
548
-
549
- $this->log( "Preparing Data Step9: Set staging site to noindex" );
550
-
551
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
552
- return true;
553
- }
554
-
555
- // Skip - Table is not selected or updated
556
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
557
- $this->log( "Preparing Data Step9: Skipping" );
558
- return true;
559
- }
560
-
561
- $result = $this->db->query(
562
- $this->db->prepare(
563
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
564
- )
565
- );
566
-
567
- // All good
568
- if( $result ) {
569
- $this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
570
- return true;
571
- }
572
-
573
- $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
574
- return true;
575
- }
576
-
577
- /**
578
- * Update WP_HOME in wp-config.php
579
- * @return bool
580
- */
581
- protected function step10() {
582
- $path = $this->options->destinationDir . "wp-config.php";
583
-
584
- $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
585
-
586
- if( false === ($content = file_get_contents( $path )) ) {
587
- $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
588
- return false;
589
- }
590
-
591
-
592
- // Get WP_HOME from wp-config.php
593
- preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
594
-
595
- if( !empty( $matches[1] ) ) {
596
- $matches[1];
597
-
598
- $pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
599
-
600
- $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
601
- $replace.= " // Changed by WP-Staging";
602
-
603
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
604
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
605
- return false;
606
- }
607
- } else {
608
- $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
609
- }
610
-
611
- if( false === @file_put_contents( $path, $content ) ) {
612
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
613
- return false;
614
- }
615
- $this->Log( "Preparing Data: Finished Step 11 successfully" );
616
- return true;
617
- }
618
-
619
- /**
620
- * Update WP_SITEURL in wp-config.php
621
- * @return bool
622
- */
623
- protected function step11() {
624
- $path = $this->options->destinationDir . "wp-config.php";
625
-
626
- $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
627
-
628
- if( false === ($content = file_get_contents( $path )) ) {
629
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
630
- return false;
631
- }
632
-
633
-
634
- // Get WP_SITEURL from wp-config.php
635
- preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
636
-
637
- if( !empty( $matches[1] ) ) {
638
- $matches[1];
639
-
640
- $pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
641
-
642
- $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
643
- $replace.= " // Changed by WP-Staging";
644
-
645
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
646
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
647
- return false;
648
- }
649
- } else {
650
- $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
651
- }
652
-
653
-
654
- if( false === @file_put_contents( $path, $content ) ) {
655
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
656
- return false;
657
- }
658
- $this->Log( "Preparing Data: Finished Step 11 successfully" );
659
- return true;
660
- }
661
-
662
- /**
663
- * Update WP_ALLOW_MULTISITE constant in wp-config.php
664
- * @return bool
665
- */
666
- protected function step12() {
667
- $path = $this->options->destinationDir . "wp-config.php";
668
-
669
- $this->log( "Preparing Data Step12: Updating WP_ALLOW_MULTISITE in wp-config.php to false" );
670
-
671
- if( false === ($content = file_get_contents( $path )) ) {
672
- $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
673
- return false;
674
- }
675
-
676
-
677
- // Get WP_SITEURL from wp-config.php
678
- preg_match( "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
679
-
680
- if( !empty( $matches[1] ) ) {
681
- $matches[1];
682
-
683
- $pattern = "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/";
684
-
685
- $replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
686
- $replace.= " // Changed by WP-Staging";
687
-
688
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
689
- $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE", Logger::TYPE_ERROR );
690
- return false;
691
- }
692
- } else {
693
- $this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
694
- }
695
-
696
-
697
- if( false === @file_put_contents( $path, $content ) ) {
698
- $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
699
- return false;
700
- }
701
- $this->Log( "Preparing Data: Finished Step 12 successfully" );
702
- return true;
703
- }
704
-
705
- /**
706
- * Update MULTISITE constant in wp-config.php
707
- * @return bool
708
- */
709
- protected function step13() {
710
- $path = $this->options->destinationDir . "wp-config.php";
711
-
712
- $this->log( "Preparing Data Step13: Updating MULTISITE in wp-config.php to false" );
713
-
714
- if( false === ($content = file_get_contents( $path )) ) {
715
- $this->log( "Preparing Data Step13: Failed to update MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
716
- return false;
717
- }
718
-
719
-
720
- // Get WP_SITEURL from wp-config.php
721
- preg_match( "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
722
-
723
- if( !empty( $matches[1] ) ) {
724
- $matches[1];
725
-
726
- $pattern = "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/";
727
-
728
- $replace = "define('MULTISITE',false); // " . $matches[1];
729
- $replace.= " // Changed by WP-Staging";
730
-
731
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
732
- $this->log( "Preparing Data Step13: Failed to update MULTISITE", Logger::TYPE_ERROR );
733
- return false;
734
- }
735
- } else {
736
- $this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
737
- }
738
-
739
-
740
- if( false === @file_put_contents( $path, $content ) ) {
741
- $this->log( "Preparing Data Step13: Failed to update MULTISITE. Can't save contents", Logger::TYPE_ERROR );
742
- return false;
743
- }
744
- $this->Log( "Preparing Data: Finished Step 13 successfully" );
745
- return true;
746
- }
747
-
748
- /**
749
- * Get active_sitewide_plugins from wp_sitemeta and active_plugins from subsite
750
- * Merge both arrays and copy them to the staging site into active_plugins
751
- */
752
- protected function step14() {
753
-
754
-
755
- $this->log( "Data Crunching Step 14: Updating active_plugins" );
756
-
757
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
758
- $this->log( 'Data Crunching Step 14: Fatal Error ' . $this->prefix . 'options does not exist' );
759
- $this->returnException( 'Data Crunching Step 14: Fatal Error ' . $this->prefix . 'options does not exist' );
760
- return false;
761
- }
762
-
763
- // Skip - Table is not selected or updated
764
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
765
- $this->log( "Preparing Data Step14: Skipping" );
766
- return true;
767
- }
768
-
769
- // Get active_plugins value from sub site options table
770
- $active_plugins = $this->db->get_var( "SELECT option_value FROM {$this->db->prefix}options WHERE option_name = 'active_plugins' " );
771
-
772
- if( !$active_plugins ) {
773
- $this->log( "Data Crunching Step 14: Option active_plugins are empty " );
774
- $active_plugins = array();
775
- }
776
- // Get active_sitewide_plugins value from main multisite wp_sitemeta table
777
- $active_sitewide_plugins = $this->db->get_var( "SELECT meta_value FROM {$this->db->base_prefix}sitemeta WHERE meta_key = 'active_sitewide_plugins' " );
778
-
779
- if( !$active_sitewide_plugins ) {
780
- $this->log( "Data Crunching Step 14: Options {$this->db->base_prefix}active_sitewide_plugins is empty " );
781
- $active_sitewide_plugins = array();
782
- }
783
-
784
- $active_sitewide_plugins = unserialize( $active_sitewide_plugins );
785
- $active_plugins = unserialize( $active_plugins );
786
-
787
- $all_plugins = array_merge( $active_plugins, array_keys( $active_sitewide_plugins ) );
788
-
789
- sort( $all_plugins );
790
-
791
-
792
- // Update active_plugins
793
- $update = $this->db->query(
794
- "UPDATE {$this->prefix}options SET option_value = '" . serialize( $all_plugins ) . "' WHERE option_name = 'active_plugins'"
795
- );
796
-
797
- if( false === $update ) {
798
- $this->log( "Data Crunching Step 14: Can not update option active_plugins in {$this->prefix}options", Logger::TYPE_WARNING );
799
- return false;
800
- }
801
-
802
- $this->log( "Data Crunching Step 14: Successfull!" );
803
- return true;
804
- }
805
-
806
- /**
807
- * Update Table Prefix in wp_options
808
- * @return bool
809
- */
810
- protected function step15() {
811
- $this->log( "Preparing Data Step15: Updating db prefix in {$this->prefix}options." );
812
-
813
- // Skip - Table does not exist
814
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
815
- return true;
816
- }
817
-
818
- // Skip - Table is not selected or updated
819
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
820
- $this->log( "Preparing Data Step4: Skipping" );
821
- return true;
822
- }
823
-
824
-
825
- $this->log( "Updating db option_names in {$this->prefix}options. " );
826
-
827
- // Filter the rows below. Do not update them!
828
- $filters = array(
829
- 'wp_mail_smtp',
830
- 'wp_mail_smtp_version',
831
- 'wp_mail_smtp_debug',
832
- );
833
-
834
- $filters = apply_filters( 'wpstg_data_excl_rows', $filters );
835
-
836
- $where = "";
837
- foreach ( $filters as $filter ) {
838
- $where .= " AND option_name <> '" . $filter . "'";
839
- }
840
-
841
- $updateOptions = $this->db->query(
842
- $this->db->prepare(
843
- "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 . "_%"
844
- )
845
- );
846
-
847
- if( !$updateOptions ) {
848
- $this->log( "Preparing Data Step15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_WARNING );
849
- //$this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
850
- return true;
851
- }
852
-
853
-
854
- return true;
855
- }
856
-
857
- /**
858
- * Change upload_path in wp_options (if it is defined)
859
- * @return bool
860
- */
861
- protected function step16() {
862
- $this->log( "Preparing Data Step16: Updating upload_path {$this->prefix}options." );
863
-
864
- // Skip - Table does not exist
865
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
866
- return true;
867
- }
868
-
869
- $newUploadPath = $this->getNewUploadPath();
870
-
871
- if( false === $newUploadPath ) {
872
- $this->log( "Preparing Data Step16: Skipping" );
873
- return true;
874
- }
875
-
876
- // Skip - Table is not selected or updated
877
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
878
- $this->log( "Preparing Data Step16: Skipping" );
879
- return true;
880
- }
881
-
882
- $error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
883
-
884
- $this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
885
-
886
- $updateOptions = $this->db->query(
887
- $this->db->prepare(
888
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
889
- )
890
- );
891
-
892
- if( !$updateOptions ) {
893
- $this->log( "Preparing Data Step16: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
894
- return true;
895
- }
896
- $this->Log( "Preparing Data: Finished Step 16 successfully" );
897
- return true;
898
- }
899
-
900
- /**
901
- * Update WP_CACHE in wp-config.php
902
- * @return bool
903
- */
904
- protected function step17() {
905
- $path = $this->options->destinationDir . "wp-config.php";
906
-
907
- $this->log( "Preparing Data Step17: Set WP_CACHE in wp-config.php to false" );
908
-
909
- if( false === ($content = file_get_contents( $path )) ) {
910
- $this->log( "Preparing Data Step17: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
911
- return false;
912
- }
913
-
914
-
915
- // Get WP_CACHE from wp-config.php
916
- preg_match( "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/", $content, $matches );
917
-
918
- if( !empty( $matches[1] ) ) {
919
- $matches[1];
920
-
921
- $pattern = "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/";
922
-
923
- $replace = "define('WP_CACHE',false); // " . $matches[1];
924
- $replace.= " // Changed by WP-Staging";
925
-
926
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
927
- $this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
928
- return false;
929
- }
930
- } else {
931
- $this->log( "Preparing Data Step17: WP_CACHE not defined in wp-config.php. Skipping this step." );
932
- }
933
-
934
- if( false === @file_put_contents( $path, $content ) ) {
935
- $this->log( "Preparing Data Step17: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
936
- return false;
937
- }
938
- $this->Log( "Preparing Data: Finished Step 17 successfully" );
939
- return true;
940
- }
941
-
942
- /**
943
- * Get Upload Path to staging site
944
- * @return boolean|string
945
- */
946
- protected function getNewUploadPath() {
947
- $uploadPath = get_option( 'upload_path' );
948
-
949
- if( !$uploadPath ) {
950
- return false;
951
- }
952
-
953
- $customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
954
-
955
- $newUploadPath = \WPStaging\WPStaging::getWPpath() . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR . $customSlug;
956
-
957
- return $newUploadPath;
958
- }
959
-
960
- /**
961
- * Return URL to staging site
962
- * @return string
963
- */
964
- protected function getStagingSiteUrl() {
965
-
966
- if( !empty( $this->options->cloneHostname ) ) {
967
- return $this->options->cloneHostname;
968
- }
969
-
970
- if( $this->isSubDir() ) {
971
- return trailingslashit( $this->multisiteHomeDomain ) . trailingslashit($this->getSubDir()) . $this->options->cloneDirectoryName;
972
- }
973
-
974
- return trailingslashit( $this->multisiteHomeDomain ) . $this->options->cloneDirectoryName;
975
- }
976
-
977
- /**
978
- * Check if WP is installed in subdir
979
- * @return boolean
980
- */
981
- protected function isSubDir() {
982
- // Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
983
- // This is happening much more often than you would expect
984
- $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
985
- $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
986
-
987
- if( $home !== $siteurl ) {
988
- return true;
989
- }
990
- return false;
991
- }
992
-
993
- /**
994
- * Get the install sub directory if WP is installed in sub directory
995
- * @return string
996
- */
997
- protected function getSubDir() {
998
- $home = get_option( 'home' );
999
- $siteurl = get_option( 'siteurl' );
1000
-
1001
- if( empty( $home ) || empty( $siteurl ) ) {
1002
- return '';
1003
- }
1004
-
1005
- $dir = str_replace( $home, '', $siteurl );
1006
- return str_replace( '/', '', $dir );
1007
- }
1008
-
1009
- }
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs\Multisite;
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\Multisite;
14
+ use WPStaging\Utils\Strings;
15
+ use WPStaging\Backend\Modules\Jobs\JobExecutable;
16
+
17
+ /**
18
+ * Class Data
19
+ * @package WPStaging\Backend\Modules\Jobs
20
+ */
21
+ class Data extends JobExecutable {
22
+
23
+ /**
24
+ * @var \wpdb
25
+ */
26
+ private $db;
27
+
28
+ /**
29
+ * @var string
30
+ */
31
+ private $prefix;
32
+
33
+ /**
34
+ * Tables e.g wpstg3_options
35
+ * @var array
36
+ */
37
+ private $tables;
38
+
39
+ /**
40
+ * Initialize
41
+ */
42
+ public function initialize() {
43
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
44
+
45
+ $this->prefix = $this->options->prefix;
46
+
47
+ $this->getTables();
48
+
49
+ // Fix current step
50
+ if( 0 == $this->options->currentStep ) {
51
+ $this->options->currentStep = 0;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Get a list of tables to copy
57
+ */
58
+ private function getTables() {
59
+ $strings = new Strings();
60
+ $this->tables = array();
61
+ foreach ( $this->options->tables as $table ) {
62
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
63
+ }
64
+ // Add extra global tables from main multisite (wpstgx_users and wpstgx_usermeta)
65
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, 'users' );
66
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, 'usermeta' );
67
+ }
68
+
69
+ /**
70
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
71
+ * @return void
72
+ */
73
+ protected function calculateTotalSteps() {
74
+ $this->options->totalSteps = 18;
75
+ }
76
+
77
+ /**
78
+ * Start Module
79
+ * @return object
80
+ */
81
+ public function start() {
82
+ // Execute steps
83
+ $this->run();
84
+
85
+ // Save option, progress
86
+ $this->saveOptions();
87
+
88
+ return ( object ) $this->response;
89
+ }
90
+
91
+ /**
92
+ * Execute the Current Step
93
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
94
+ * @return bool
95
+ */
96
+ protected function execute() {
97
+ // Fatal error. Let this happen never and break here immediately
98
+ if( $this->isRoot() ) {
99
+ return false;
100
+ }
101
+
102
+ // Over limits threshold
103
+ if( $this->isOverThreshold() ) {
104
+ // Prepare response and save current progress
105
+ $this->prepareResponse( false, false );
106
+ $this->saveOptions();
107
+ return false;
108
+ }
109
+
110
+ // No more steps, finished
111
+ if( $this->isFinished() ) {
112
+ $this->prepareResponse( true, false );
113
+ return false;
114
+ }
115
+
116
+ // Execute step
117
+ $stepMethodName = "step" . $this->options->currentStep;
118
+ if( !$this->{$stepMethodName}() ) {
119
+ $this->prepareResponse( false, false );
120
+ return false;
121
+ }
122
+
123
+ // Prepare Response
124
+ $this->prepareResponse();
125
+
126
+ // Not finished
127
+ return true;
128
+ }
129
+
130
+ /**
131
+ * Checks Whether There is Any Job to Execute or Not
132
+ * @return bool
133
+ */
134
+ protected function isFinished() {
135
+ return (
136
+ $this->options->currentStep > $this->options->totalSteps ||
137
+ !method_exists( $this, "step" . $this->options->currentStep )
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Check if current operation is done on the root folder or on the live DB
143
+ * @return boolean
144
+ */
145
+ protected function isRoot() {
146
+
147
+ // Prefix is the same as the one of live site
148
+ //$wpdb = WPStaging::getInstance()->get( "wpdb" );
149
+ if( $this->db->prefix === $this->prefix ) {
150
+ return true;
151
+ }
152
+
153
+ // CloneName is empty
154
+ $name = ( array ) $this->options->cloneDirectoryName;
155
+ if( empty( $name ) ) {
156
+ return true;
157
+ }
158
+
159
+ // Live domain === Staging domain
160
+ if( $this->multisiteHomeDomain . $this->options->cloneDirectoryName === $this->multisiteHomeDomain ) {
161
+ return true;
162
+ }
163
+
164
+ return false;
165
+ }
166
+
167
+ /**
168
+ * Check if table exists
169
+ * @param string $table
170
+ * @return boolean
171
+ */
172
+ protected function isTable( $table ) {
173
+ if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
174
+ $this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
175
+ return false;
176
+ }
177
+ return true;
178
+ }
179
+
180
+ /**
181
+ * Return absolute destination path
182
+ * @return string
183
+ */
184
+ // private function getAbsDestination() {
185
+ // if( empty( $this->options->cloneDir ) ) {
186
+ // return \WPStaging\WPStaging::getWPpath();
187
+ // }
188
+ // return trailingslashit( $this->options->cloneDir );
189
+ // }
190
+
191
+ /**
192
+ * Copy wp-config.php if it is located outside of root one level up
193
+ * @todo Needs some more testing before it will be released
194
+ * @return boolean
195
+ */
196
+ protected function step0() {
197
+ $this->log( "Preparing Data Step0: Check if wp-config.php is located in root path", Logger::TYPE_INFO );
198
+
199
+ $dir = trailingslashit( dirname( ABSPATH ) );
200
+
201
+ $source = $dir . 'wp-config.php';
202
+
203
+ $destination = $this->options->destinationDir . 'wp-config.php';
204
+
205
+
206
+ // Do not do anything
207
+ if( (!is_file( $source ) && !is_link( $source )) || is_file( $destination ) ) {
208
+ $this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
209
+ return true;
210
+ }
211
+
212
+ // Copy target of a symbolic link
213
+ if( is_link( $source ) ) {
214
+ $this->log( "Preparing Data Step0: Symbolic link found...", Logger::TYPE_INFO );
215
+ if( !@copy( readlink( $source ), $destination ) ) {
216
+ $errors = error_get_last();
217
+ $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
218
+ return true;
219
+ }
220
+ }
221
+
222
+ // Copy file wp-config.php
223
+ if( !@copy( $source, $destination ) ) {
224
+ $errors = error_get_last();
225
+ $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
226
+ return true;
227
+ }
228
+ $this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
229
+ return true;
230
+ }
231
+
232
+ /**
233
+ * Replace "siteurl" and "home"
234
+ * @return bool
235
+ */
236
+ protected function step1() {
237
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
238
+
239
+ // Skip - Table does not exist
240
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
241
+ return true;
242
+ }
243
+ // Skip - Table is not selected or updated
244
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
245
+ $this->log( "Preparing Data Step1: Skipping" );
246
+ return true;
247
+ }
248
+
249
+ // Installed in sub-directory
250
+ // if( $this->isSubDir() ) {
251
+ // $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName );
252
+ // // Replace URLs
253
+ // $result = $this->db->query(
254
+ // $this->db->prepare(
255
+ // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName
256
+ // )
257
+ // );
258
+ // } else
259
+ // {
260
+ // $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName );
261
+ // // Replace URLs
262
+ // $result = $this->db->query(
263
+ // $this->db->prepare(
264
+ // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName
265
+ // )
266
+ // );
267
+ // }
268
+
269
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . $this->getStagingSiteUrl() );
270
+ // Replace URLs
271
+ $result = $this->db->query(
272
+ $this->db->prepare(
273
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->getStagingSiteUrl()
274
+ )
275
+ );
276
+
277
+
278
+
279
+ // All good
280
+ if( $result ) {
281
+ return true;
282
+ }
283
+
284
+ $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
285
+ return false;
286
+ }
287
+
288
+ /**
289
+ * Update "wpstg_is_staging_site"
290
+ * @return bool
291
+ */
292
+ protected function step2() {
293
+
294
+ $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
295
+
296
+ // Skip - Table does not exist
297
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
298
+ return true;
299
+ }
300
+ // Skip - Table is not selected or updated
301
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
302
+ $this->log( "Preparing Data Step2: Skipping" );
303
+ return true;
304
+ }
305
+
306
+ $result = $this->db->query(
307
+ $this->db->prepare(
308
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
309
+ )
310
+ );
311
+
312
+ // No errors but no option name such as wpstg_is_staging_site
313
+ if( '' === $this->db->last_error && 0 == $result ) {
314
+ $result = $this->db->query(
315
+ $this->db->prepare(
316
+ "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
317
+ )
318
+ );
319
+ }
320
+
321
+ // All good
322
+ if( $result ) {
323
+ return true;
324
+ }
325
+
326
+ $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
327
+ return false;
328
+ }
329
+
330
+ /**
331
+ * Update rewrite_rules
332
+ * @return bool
333
+ */
334
+ protected function step3() {
335
+
336
+ $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
337
+
338
+ // Skip - Table does not exist
339
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
340
+ return true;
341
+ }
342
+
343
+ // Skip - Table is not selected or updated
344
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
345
+ $this->log( "Preparing Data Step3: Skipping" );
346
+ return true;
347
+ }
348
+
349
+ $result = $this->db->query(
350
+ $this->db->prepare(
351
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
352
+ )
353
+ );
354
+
355
+ // All good
356
+ if( $result ) {
357
+ return true;
358
+ }
359
+
360
+ $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
361
+ return true;
362
+ }
363
+
364
+ /**
365
+ * Update Table Prefix in wp_usermeta and wp_options
366
+ * @return bool
367
+ */
368
+ protected function step4() {
369
+ $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. {$this->db->last_error}" );
370
+
371
+ // Skip - Table does not exist
372
+ if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
373
+ return true;
374
+ }
375
+
376
+ // Skip - Table is not selected or updated
377
+ if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
378
+ $this->log( "Preparing Data Step4: Skipping" );
379
+ return true;
380
+ }
381
+
382
+ $update = $this->db->query(
383
+ $this->db->prepare(
384
+ "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 . "_%"
385
+ )
386
+ );
387
+
388
+ if( !$update ) {
389
+ $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
390
+ $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
391
+ return false;
392
+ }
393
+ return true;
394
+ }
395
+
396
+ /**
397
+ * Update $table_prefix in wp-config.php
398
+ * @return bool
399
+ */
400
+ protected function step5() {
401
+ $path = $this->options->destinationDir . "wp-config.php";
402
+
403
+ $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
404
+ if( false === ($content = file_get_contents( $path )) ) {
405
+ $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
406
+ return false;
407
+ }
408
+
409
+ // Replace table prefix
410
+ $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
411
+
412
+ // Replace URLs
413
+ $content = str_replace( $this->multisiteHomeDomain, $this->getStagingSiteUrl(), $content );
414
+
415
+ if( false === @file_put_contents( $path, $content ) ) {
416
+ $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
417
+ return false;
418
+ }
419
+
420
+ return true;
421
+ }
422
+
423
+ /**
424
+ * Reset index.php to original file
425
+ * This is needed if live site is located in subfolder
426
+ * Check first if main wordpress is used in subfolder and index.php in parent directory
427
+ * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
428
+ * @return bool
429
+ */
430
+ protected function step6() {
431
+
432
+ if( !$this->isSubDir() ) {
433
+ $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
434
+ return true;
435
+ }
436
+
437
+ $path = $this->options->destinationDir . "index.php";
438
+
439
+ if( false === ($content = file_get_contents( $path )) ) {
440
+ $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
441
+ return false;
442
+ }
443
+
444
+
445
+ if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
446
+ $this->log(
447
+ "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
448
+ );
449
+ return false;
450
+ }
451
+ $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
452
+
453
+ $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
454
+
455
+ $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
456
+ $replace.= " // Changed by WP-Staging";
457
+
458
+
459
+
460
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
461
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
462
+ return false;
463
+ }
464
+
465
+ if( false === @file_put_contents( $path, $content ) ) {
466
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
467
+ return false;
468
+ }
469
+ $this->Log( "Preparing Data: Finished Step 6 successfully" );
470
+ return true;
471
+ }
472
+
473
+ /**
474
+ * Update wpstg_rmpermalinks_executed
475
+ * @return bool
476
+ */
477
+ protected function step7() {
478
+
479
+ $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
480
+
481
+ // Skip - Table does not exist
482
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
483
+ return true;
484
+ }
485
+
486
+ // Skip - Table is not selected or updated
487
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
488
+ $this->log( "Preparing Data Step7: Skipping" );
489
+ return true;
490
+ }
491
+
492
+ $result = $this->db->query(
493
+ $this->db->prepare(
494
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
495
+ )
496
+ );
497
+
498
+ // All good
499
+ if( $result ) {
500
+ $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
501
+ return true;
502
+ }
503
+
504
+ $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
505
+ return true;
506
+ }
507
+
508
+ /**
509
+ * Update permalink_structure
510
+ * @return bool
511
+ */
512
+ protected function step8() {
513
+
514
+ $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
515
+
516
+ // Skip - Table does not exist
517
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
518
+ return true;
519
+ }
520
+
521
+ // Skip - Table is not selected or updated
522
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
523
+ $this->log( "Preparing Data Step8: Skipping" );
524
+ return true;
525
+ }
526
+
527
+ $result = $this->db->query(
528