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

Version Description

  • Tweak: Lowering default performance settings for more reliability during cloning
  • New: Set WP_CACHE to false in wp-config.php after cloning to prevent log in issues to staging site
  • New: Compatibility mode to skip certain tables from third party plugins from beeing searched & replaced
  • Fix: Do not clone db.php, object-cache.php and advanced-cache.php
  • Fix: Show error message if ajax requests fail for any reason
Download this release

Release Info

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

Code changes from version 2.3.7 to 2.3.8

apps/Backend/Modules/Jobs/Cloning.php CHANGED
@@ -47,6 +47,11 @@ class Cloning extends Job {
47
  '.log',
48
  'web.config'
49
  );
 
 
 
 
 
50
  $this->options->currentStep = 0;
51
 
52
 
47
  '.log',
48
  'web.config'
49
  );
50
+ $this->options->excludedFilesFullPath = array(
51
+ 'wp-content' . DIRECTORY_SEPARATOR . 'db.php',
52
+ 'wp-content' . DIRECTORY_SEPARATOR . 'object-cache.php',
53
+ 'wp-content' . DIRECTORY_SEPARATOR . 'advanced-cache.php'
54
+ );
55
  $this->options->currentStep = 0;
56
 
57
 
apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -77,7 +77,7 @@ class Data extends JobExecutable {
77
  * @return void
78
  */
79
  protected function calculateTotalSteps() {
80
- $this->options->totalSteps = 13;
81
  }
82
 
83
  /**
@@ -759,6 +759,48 @@ class Data extends JobExecutable {
759
  return true;
760
  }
761
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
762
  protected function getNewUploadPath() {
763
  $uploadPath = get_option( 'upload_path' );
764
 
77
  * @return void
78
  */
79
  protected function calculateTotalSteps() {
80
+ $this->options->totalSteps = 14;
81
  }
82
 
83
  /**
759
  return true;
760
  }
761
 
762
+ /**
763
+ * Update WP_CACHE in wp-config.php
764
+ * @return bool
765
+ */
766
+ protected function step14() {
767
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
768
+
769
+ $this->log( "Preparing Data Step14: Set WP_CACHE in wp-config.php to false" );
770
+
771
+ if( false === ($content = file_get_contents( $path )) ) {
772
+ $this->log( "Preparing Data Step14: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
773
+ return false;
774
+ }
775
+
776
+
777
+ // Get WP_CACHE from wp-config.php
778
+ preg_match( "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/", $content, $matches );
779
+
780
+ if( !empty( $matches[1] ) ) {
781
+ $matches[1];
782
+
783
+ $pattern = "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/";
784
+
785
+ $replace = "define('WP_CACHE',false); // " . $matches[1];
786
+ $replace.= " // Changed by WP-Staging";
787
+
788
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
789
+ $this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
790
+ return false;
791
+ }
792
+ } else {
793
+ $this->log( "Preparing Data Step14: WP_CACHE not defined in wp-config.php. Skipping this step." );
794
+ }
795
+
796
+ if( false === @file_put_contents( $path, $content ) ) {
797
+ $this->log( "Preparing Data Step14: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
798
+ return false;
799
+ }
800
+ $this->Log( "Preparing Data: Finished Step 14 successfully" );
801
+ return true;
802
+ }
803
+
804
  protected function getNewUploadPath() {
805
  $uploadPath = get_option( 'upload_path' );
806
 
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -161,16 +161,22 @@ class Files extends JobExecutable {
161
 
162
  // Directory is excluded
163
  if( $this->isDirectoryExcluded( $directory ) ) {
164
- $this->debugLog( "Skipping directory by rule: {$file}", Logger::TYPE_INFO );
165
  return false;
166
  }
167
 
168
  // File is excluded
169
  if( $this->isFileExcluded( $file ) ) {
170
- $this->debugLog( "Skipping file by rule: {$file}", Logger::TYPE_INFO );
171
  return false;
172
  }
173
-
 
 
 
 
 
 
174
  // File is over maximum allowed file size (8MB)
175
  if( $fileSize >= $this->settings->maxFileSize * 1000000 ) {
176
  $this->log( "Skipping big file: {$file}", Logger::TYPE_INFO );
@@ -224,7 +230,7 @@ class Files extends JobExecutable {
224
  $destinationPath = $this->destination . $relativePath;
225
  $destinationDirectory = dirname( $destinationPath );
226
 
227
- if( !is_dir( $destinationDirectory ) && !@mkdir( $destinationDirectory, 0775, true ) ) {
228
  $this->log( "Files: Can not create directory {$destinationDirectory}", Logger::TYPE_ERROR );
229
  return false;
230
  }
@@ -273,22 +279,36 @@ class Files extends JobExecutable {
273
  * @return boolean
274
  */
275
  private function isFileExcluded( $file ) {
276
- $excluded = false;
277
- foreach ( $this->options->excludedFiles as $excludedFile ) {
278
- if( stripos( strrev( $file ), strrev( $excludedFile ) ) === 0 ) {
279
- $excluded = true;
280
- break;
281
- }
282
- }
283
 
 
 
 
284
  // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
285
  // because if the updating process fails, the staging site would not be accessable any longer
286
  if( isset( $this->options->mainJob ) && $this->options->mainJob == "updating" && stripos( strrev( $file ), strrev( "wp-config.php" ) ) === 0 ) {
287
- $excluded = true;
 
288
  }
289
 
290
 
291
- return $excluded;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  }
293
 
294
  /**
@@ -337,7 +357,7 @@ class Files extends JobExecutable {
337
  $directory = $this->sanitizeDirectorySeparator( $directory );
338
 
339
  foreach ( $this->options->extraDirectories as $extraDirectory ) {
340
- if( strpos( $directory, $this->sanitizeDirectorySeparator($extraDirectory) ) === 0 ) {
341
  return true;
342
  }
343
  }
161
 
162
  // Directory is excluded
163
  if( $this->isDirectoryExcluded( $directory ) ) {
164
+ $this->debugLog( "Directory Excluded: {$file}", Logger::TYPE_INFO );
165
  return false;
166
  }
167
 
168
  // File is excluded
169
  if( $this->isFileExcluded( $file ) ) {
170
+ $this->debugLog( "File excluded: {$file}", Logger::TYPE_INFO );
171
  return false;
172
  }
173
+
174
+ // Path + File is excluded
175
+ if( $this->isFileExcludedFullPath( $file ) ) {
176
+ $this->debugLog( "File Excluded Full Path: {$file}", Logger::TYPE_INFO );
177
+ return false;
178
+ }
179
+
180
  // File is over maximum allowed file size (8MB)
181
  if( $fileSize >= $this->settings->maxFileSize * 1000000 ) {
182
  $this->log( "Skipping big file: {$file}", Logger::TYPE_INFO );
230
  $destinationPath = $this->destination . $relativePath;
231
  $destinationDirectory = dirname( $destinationPath );
232
 
233
+ if( !is_dir( $destinationDirectory ) && !@mkdir( $destinationDirectory, 0755, true ) ) {
234
  $this->log( "Files: Can not create directory {$destinationDirectory}", Logger::TYPE_ERROR );
235
  return false;
236
  }
279
  * @return boolean
280
  */
281
  private function isFileExcluded( $file ) {
 
 
 
 
 
 
 
282
 
283
+ if( in_array( basename( $file ), $this->options->excludedFiles ) ) {
284
+ return true;
285
+ }
286
  // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
287
  // because if the updating process fails, the staging site would not be accessable any longer
288
  if( isset( $this->options->mainJob ) && $this->options->mainJob == "updating" && stripos( strrev( $file ), strrev( "wp-config.php" ) ) === 0 ) {
289
+ return true;
290
+
291
  }
292
 
293
 
294
+ return false;
295
+ }
296
+
297
+ /**
298
+ * Check if certain file is excluded from copying process
299
+ *
300
+ * @param string $file filename including ending + (part) path e.g wp-content/db.php
301
+ * @return boolean
302
+ */
303
+ private function isFileExcludedFullPath( $file ) {
304
+ // If path + file exists
305
+ foreach ($this->options->excludedFilesFullPath as $excludedFile){
306
+ if( false !== strpos( $file, $excludedFile )){
307
+ return true;
308
+ }
309
+ }
310
+
311
+ return false;
312
  }
313
 
314
  /**
357
  $directory = $this->sanitizeDirectorySeparator( $directory );
358
 
359
  foreach ( $this->options->extraDirectories as $extraDirectory ) {
360
+ if( strpos( $directory, $this->sanitizeDirectorySeparator( $extraDirectory ) ) === 0 ) {
361
  return true;
362
  }
363
  }
apps/Backend/Modules/Jobs/Job.php CHANGED
@@ -12,6 +12,8 @@ use WPStaging\Utils\Logger;
12
  use WPStaging\WPStaging;
13
  use WPStaging\Utils\Cache;
14
  use WPStaging\Utils\Multisite;
 
 
15
 
16
  /**
17
  * Class Job
@@ -87,15 +89,21 @@ abstract class Job implements JobInterface {
87
 
88
  /**
89
  * Multisite home domain without scheme
90
- * @var type
91
  */
92
  protected $multisiteDomainWithoutScheme;
93
 
94
  /**
95
  * Multisite home domain without scheme
96
- * @var type
97
  */
98
  protected $multisiteHomeDomain;
 
 
 
 
 
 
99
 
100
  /**
101
  * @var int
@@ -109,12 +117,12 @@ abstract class Job implements JobInterface {
109
  // Get max limits
110
  $this->start = $this->time();
111
  $this->maxMemoryLimit = $this->getMemoryInBytes( @ini_get( "memory_limit" ) );
 
112
 
113
  $multisite = new Multisite;
114
  $this->multisiteHomeUrlWithoutScheme = $multisite->getHomeUrlWithoutScheme();
115
  $this->multisiteHomeDomain = $multisite->getHomeDomain();
116
  $this->multisiteDomainWithoutScheme = $multisite->getHomeDomainWithoutScheme();
117
- //$this->multisiteUrl = $multisite->getHomeDomain();
118
 
119
  //$this->maxExecutionTime = (int) ini_get("max_execution_time");
120
  $this->maxExecutionTime = ( int ) 30;
12
  use WPStaging\WPStaging;
13
  use WPStaging\Utils\Cache;
14
  use WPStaging\Utils\Multisite;
15
+ use WPStaging\thirdParty\thirdPartyCompatibility;
16
+
17
 
18
  /**
19
  * Class Job
89
 
90
  /**
91
  * Multisite home domain without scheme
92
+ * @var string
93
  */
94
  protected $multisiteDomainWithoutScheme;
95
 
96
  /**
97
  * Multisite home domain without scheme
98
+ * @var string
99
  */
100
  protected $multisiteHomeDomain;
101
+
102
+ /**
103
+ *
104
+ * @var object
105
+ */
106
+ protected $thirdParty;
107
 
108
  /**
109
  * @var int
117
  // Get max limits
118
  $this->start = $this->time();
119
  $this->maxMemoryLimit = $this->getMemoryInBytes( @ini_get( "memory_limit" ) );
120
+ $this->thirdParty = new thirdPartyCompatibility($this);
121
 
122
  $multisite = new Multisite;
123
  $this->multisiteHomeUrlWithoutScheme = $multisite->getHomeUrlWithoutScheme();
124
  $this->multisiteHomeDomain = $multisite->getHomeDomain();
125
  $this->multisiteDomainWithoutScheme = $multisite->getHomeDomainWithoutScheme();
 
126
 
127
  //$this->maxExecutionTime = (int) ini_get("max_execution_time");
128
  $this->maxExecutionTime = ( int ) 30;
apps/Backend/Modules/Jobs/Multisite/Data.php CHANGED
@@ -1,938 +1,980 @@
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 = 1;
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 = 16;
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( $wpdb->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 Path === Staging path
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
- * Get the install sub directory if WP is installed in sub directory
182
- * @return string
183
- */
184
- protected function getSubDir() {
185
- return '/';
186
-
187
- // $home = get_option( 'home' );
188
- // $siteurl = get_option( 'siteurl' );
189
- //
190
- // if( empty( $home ) || empty( $siteurl ) ) {
191
- // return '/';
192
- // }
193
- //
194
- // $dir = str_replace( $home, '', $siteurl );
195
- // return '/' . str_replace( '/', '', $dir ) . '/';
196
- }
197
-
198
- /**
199
- * Copy wp-config.php if it is located outside of root one level up
200
- * @todo Needs some more testing before it will be released
201
- * @return boolean
202
- */
203
- // protected function step0(){
204
- // $this->log( "Preparing Data Step0: Check if wp-config.php is located in root path", Logger::TYPE_INFO );
205
- //
206
- // $dir = trailingslashit( dirname( ABSPATH ) );
207
- //
208
- // $source = $dir . 'wp-config.php';
209
- //
210
- // $destination = trailingslashit(ABSPATH) . $this->clone->cloneDirectoryName . DIRECTORY_SEPARATOR . 'wp-config.php';
211
- //
212
- //
213
- // // Do not do anything
214
- // if( (!is_file( $source ) && !is_link($source)) || is_file($destination) ) {
215
- // $this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
216
- // return true;
217
- // }
218
- //
219
- // // Copy target of a symbolic link
220
- // if (is_link($source)){
221
- // $this->log( "Preparing Data Step0: Symbolic link found...", Logger::TYPE_INFO );
222
- // if (!@copy(readlink($source), $destination)) {
223
- // $this->log("Preparing Data Step0: Failed to copy wp-config.php {$source} -> {$destination}", Logger::TYPE_ERROR);
224
- // return true;
225
- // }
226
- // }
227
- //
228
- // // regular copy of wp-config.php
229
- // if (!@copy($source, $destination)) {
230
- // $this->log("Preparing Data Step0: Failed to copy wp-config.php {$source} -> {$destination}", Logger::TYPE_ERROR);
231
- // return true;
232
- // }
233
- // $this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
234
- // return true;
235
- //
236
- // }
237
-
238
- /**
239
- * Replace "siteurl" and "home"
240
- * @return bool
241
- */
242
- protected function step1() {
243
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
244
-
245
- // Skip - Table does not exist
246
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
247
- return true;
248
- }
249
- // Skip - Table is not selected or updated
250
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
251
- $this->log( "Preparing Data Step1: Skipping" );
252
- return true;
253
- }
254
-
255
- // Installed in sub-directory
256
- if( $this->isSubDir() ) {
257
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName );
258
- // Replace URLs
259
- $result = $this->db->query(
260
- $this->db->prepare(
261
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->multisiteHomeDomain, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName
262
- )
263
- );
264
- } else {
265
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName );
266
- // Replace URLs
267
- $result = $this->db->query(
268
- $this->db->prepare(
269
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName
270
- )
271
- );
272
- }
273
-
274
-
275
- // All good
276
- if( $result ) {
277
- return true;
278
- }
279
-
280
- $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
281
- return false;
282
- }
283
-
284
- /**
285
- * Update "wpstg_is_staging_site"
286
- * @return bool
287
- */
288
- protected function step2() {
289
-
290
- $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
291
-
292
- // Skip - Table does not exist
293
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
294
- return true;
295
- }
296
- // Skip - Table is not selected or updated
297
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
298
- $this->log( "Preparing Data Step2: Skipping" );
299
- return true;
300
- }
301
-
302
- $result = $this->db->query(
303
- $this->db->prepare(
304
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
305
- )
306
- );
307
-
308
- // No errors but no option name such as wpstg_is_staging_site
309
- if( '' === $this->db->last_error && 0 == $result ) {
310
- $result = $this->db->query(
311
- $this->db->prepare(
312
- "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
313
- )
314
- );
315
- }
316
-
317
- // All good
318
- if( $result ) {
319
- return true;
320
- }
321
-
322
- $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
323
- return false;
324
- }
325
-
326
- /**
327
- * Update rewrite_rules
328
- * @return bool
329
- */
330
- protected function step3() {
331
-
332
- $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
333
-
334
- // Skip - Table does not exist
335
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
336
- return true;
337
- }
338
-
339
- // Skip - Table is not selected or updated
340
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
341
- $this->log( "Preparing Data Step3: Skipping" );
342
- return true;
343
- }
344
-
345
- $result = $this->db->query(
346
- $this->db->prepare(
347
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
348
- )
349
- );
350
-
351
- // All good
352
- if( $result ) {
353
- return true;
354
- }
355
-
356
- $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
357
- return true;
358
- }
359
-
360
- /**
361
- * Update Table Prefix in wp_usermeta and wp_options
362
- * @return bool
363
- */
364
- protected function step4() {
365
- $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. {$this->db->last_error}" );
366
-
367
- // Skip - Table does not exist
368
- if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
369
- return true;
370
- }
371
-
372
- // Skip - Table is not selected or updated
373
- if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
374
- $this->log( "Preparing Data Step4: Skipping" );
375
- return true;
376
- }
377
-
378
- $update = $this->db->query(
379
- $this->db->prepare(
380
- "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 . "_%"
381
- )
382
- );
383
-
384
- if( !$update ) {
385
- $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
386
- $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
387
- return false;
388
- }
389
- return true;
390
- }
391
-
392
- /**
393
- * Update $table_prefix in wp-config.php
394
- * @return bool
395
- */
396
- protected function step5() {
397
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
398
-
399
- $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
400
- if( false === ($content = file_get_contents( $path )) ) {
401
- $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
402
- return false;
403
- }
404
-
405
- // Replace table prefix
406
- $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
407
-
408
- // Replace URLs
409
- $content = str_replace( $this->multisiteHomeDomain, $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName, $content );
410
-
411
- if( false === @file_put_contents( $path, $content ) ) {
412
- $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
413
- return false;
414
- }
415
-
416
- return true;
417
- }
418
-
419
- /**
420
- * Reset index.php to original file
421
- * This is needed if live site is located in subfolder
422
- * Check first if main wordpress is used in subfolder and index.php in parent directory
423
- * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
424
- * @return bool
425
- */
426
- protected function step6() {
427
-
428
- if( !$this->isSubDir() ) {
429
- $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
430
- return true;
431
- }
432
-
433
- $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
434
-
435
- if( false === ($content = file_get_contents( $path )) ) {
436
- $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
437
- return false;
438
- }
439
-
440
-
441
- if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
442
- $this->log(
443
- "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
444
- );
445
- return false;
446
- }
447
- $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
448
-
449
- $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
450
-
451
- $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
452
- $replace.= " // Changed by WP-Staging";
453
-
454
-
455
-
456
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
457
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
458
- return false;
459
- }
460
-
461
- if( false === @file_put_contents( $path, $content ) ) {
462
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
463
- return false;
464
- }
465
- $this->Log( "Preparing Data: Finished Step 6 successfully" );
466
- return true;
467
- }
468
-
469
- /**
470
- * Update wpstg_rmpermalinks_executed
471
- * @return bool
472
- */
473
- protected function step7() {
474
-
475
- $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
476
-
477
- // Skip - Table does not exist
478
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
479
- return true;
480
- }
481
-
482
- // Skip - Table is not selected or updated
483
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
484
- $this->log( "Preparing Data Step7: Skipping" );
485
- return true;
486
- }
487
-
488
- $result = $this->db->query(
489
- $this->db->prepare(
490
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
491
- )
492
- );
493
-
494
- // All good
495
- if( $result ) {
496
- $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
497
- return true;
498
- }
499
-
500
- $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
501
- return true;
502
- }
503
-
504
- /**
505
- * Update permalink_structure
506
- * @return bool
507
- */
508
- protected function step8() {
509
-
510
- $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
511
-
512
- // Skip - Table does not exist
513
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
514
- return true;
515
- }
516
-
517
- // Skip - Table is not selected or updated
518
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
519
- $this->log( "Preparing Data Step8: Skipping" );
520
- return true;
521
- }
522
-
523
- $result = $this->db->query(
524
- $this->db->prepare(
525
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
526
- )
527
- );
528
-
529
- // All good
530
- if( $result ) {
531
- $this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
532
- return true;
533
- }
534
-
535
- $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
536
- return true;
537
- }
538
-
539
- /**
540
- * Update blog_public option to not allow staging site to be indexed by search engines
541
- * @return bool
542
- */
543
- protected function step9() {
544
-
545
- $this->log( "Preparing Data Step9: Set staging site to noindex" );
546
-
547
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
548
- return true;
549
- }
550
-
551
- // Skip - Table is not selected or updated
552
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
553
- $this->log( "Preparing Data Step9: Skipping" );
554
- return true;
555
- }
556
-
557
- $result = $this->db->query(
558
- $this->db->prepare(
559
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
560
- )
561
- );
562
-
563
- // All good
564
- if( $result ) {
565
- $this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
566
- return true;
567
- }
568
-
569
- $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
570
- return true;
571
- }
572
-
573
- /**
574
- * Update WP_HOME in wp-config.php
575
- * @return bool
576
- */
577
- protected function step10() {
578
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
579
-
580
- $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
581
-
582
- if( false === ($content = file_get_contents( $path )) ) {
583
- $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
584
- return false;
585
- }
586
-
587
-
588
- // Get WP_HOME from wp-config.php
589
- preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
590
-
591
- if( !empty( $matches[1] ) ) {
592
- $matches[1];
593
-
594
- $pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
595
-
596
- $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
597
- $replace.= " // Changed by WP-Staging";
598
-
599
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
600
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
601
- return false;
602
- }
603
- } else {
604
- $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
605
- }
606
-
607
- if( false === @file_put_contents( $path, $content ) ) {
608
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
609
- return false;
610
- }
611
- $this->Log( "Preparing Data: Finished Step 11 successfully" );
612
- return true;
613
- }
614
-
615
- /**
616
- * Update WP_SITEURL in wp-config.php
617
- * @return bool
618
- */
619
- protected function step11() {
620
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
621
-
622
- $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
623
-
624
- if( false === ($content = file_get_contents( $path )) ) {
625
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
626
- return false;
627
- }
628
-
629
-
630
- // Get WP_SITEURL from wp-config.php
631
- preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
632
-
633
- if( !empty( $matches[1] ) ) {
634
- $matches[1];
635
-
636
- $pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
637
-
638
- $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
639
- $replace.= " // Changed by WP-Staging";
640
-
641
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
642
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
643
- return false;
644
- }
645
- } else {
646
- $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
647
- }
648
-
649
-
650
- if( false === @file_put_contents( $path, $content ) ) {
651
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
652
- return false;
653
- }
654
- $this->Log( "Preparing Data: Finished Step 11 successfully" );
655
- return true;
656
- }
657
-
658
- /**
659
- * Update WP_ALLOW_MULTISITE constant in wp-config.php
660
- * @return bool
661
- */
662
- protected function step12() {
663
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
664
-
665
- $this->log( "Preparing Data Step12: Updating WP_ALLOW_MULTISITE in wp-config.php to false" );
666
-
667
- if( false === ($content = file_get_contents( $path )) ) {
668
- $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
669
- return false;
670
- }
671
-
672
-
673
- // Get WP_SITEURL from wp-config.php
674
- preg_match( "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
675
-
676
- if( !empty( $matches[1] ) ) {
677
- $matches[1];
678
-
679
- $pattern = "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/";
680
-
681
- $replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
682
- $replace.= " // Changed by WP-Staging";
683
-
684
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
685
- $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE", Logger::TYPE_ERROR );
686
- return false;
687
- }
688
- } else {
689
- $this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
690
- }
691
-
692
-
693
- if( false === @file_put_contents( $path, $content ) ) {
694
- $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
695
- return false;
696
- }
697
- $this->Log( "Preparing Data: Finished Step 12 successfully" );
698
- return true;
699
- }
700
-
701
- /**
702
- * Update MULTISITE constant in wp-config.php
703
- * @return bool
704
- */
705
- protected function step13() {
706
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
707
-
708
- $this->log( "Preparing Data Step13: Updating MULTISITE in wp-config.php to false" );
709
-
710
- if( false === ($content = file_get_contents( $path )) ) {
711
- $this->log( "Preparing Data Step13: Failed to update MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
712
- return false;
713
- }
714
-
715
-
716
- // Get WP_SITEURL from wp-config.php
717
- preg_match( "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
718
-
719
- if( !empty( $matches[1] ) ) {
720
- $matches[1];
721
-
722
- $pattern = "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/";
723
-
724
- $replace = "define('MULTISITE',false); // " . $matches[1];
725
- $replace.= " // Changed by WP-Staging";
726
-
727
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
728
- $this->log( "Preparing Data Step13: Failed to update MULTISITE", Logger::TYPE_ERROR );
729
- return false;
730
- }
731
- } else {
732
- $this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
733
- }
734
-
735
-
736
- if( false === @file_put_contents( $path, $content ) ) {
737
- $this->log( "Preparing Data Step13: Failed to update MULTISITE. Can't save contents", Logger::TYPE_ERROR );
738
- return false;
739
- }
740
- $this->Log( "Preparing Data: Finished Step 13 successfully" );
741
- return true;
742
- }
743
-
744
- /**
745
- * Get active_sitewide_plugins from wp_sitemeta and active_plugins from subsite
746
- * Merge both arrays and copy them to the staging site into active_plugins
747
- */
748
- protected function step14() {
749
-
750
-
751
- $this->log( "Data Crunching Step 14: Updating active_plugins" );
752
-
753
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
754
- $this->log( 'Data Crunching Step 14: Fatal Error ' . $this->prefix . 'options does not exist' );
755
- $this->returnException( 'Data Crunching Step 8: Fatal Error ' . $this->prefix . 'options does not exist' );
756
- return false;
757
- }
758
-
759
- // Skip - Table is not selected or updated
760
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
761
- $this->log( "Preparing Data Step14: Skipping" );
762
- return true;
763
- }
764
-
765
- // Get active_plugins value from sub site options table
766
- $active_plugins = $this->db->get_var( "SELECT option_value FROM {$this->db->prefix}options WHERE option_name = 'active_plugins' " );
767
-
768
- if( !$active_plugins ) {
769
- $this->log( "Data Crunching Step 14: Option active_plugins are empty " );
770
- $active_plugins = array();
771
- }
772
- // Get active_sitewide_plugins value from main multisite wp_sitemeta table
773
- $active_sitewide_plugins = $this->db->get_var( "SELECT meta_value FROM {$this->db->base_prefix}sitemeta WHERE meta_key = 'active_sitewide_plugins' " );
774
-
775
- if( !$active_sitewide_plugins ) {
776
- $this->log( "Data Crunching Step 14: Options {$this->db->base_prefix}active_sitewide_plugins is empty " );
777
- $active_sitewide_plugins = array();
778
- }
779
-
780
- $active_sitewide_plugins = unserialize( $active_sitewide_plugins );
781
- $active_plugins = unserialize( $active_plugins );
782
-
783
- $all_plugins = array_merge( $active_plugins, array_keys( $active_sitewide_plugins ) );
784
-
785
- sort( $all_plugins );
786
-
787
-
788
- // Update active_plugins
789
- $update = $this->db->query(
790
- "UPDATE {$this->prefix}options SET option_value = '" . serialize( $all_plugins ) . "' WHERE option_name = 'active_plugins'"
791
- );
792
-
793
- if( false === $update ) {
794
- $this->log( "Data Crunching Step 14: Can not update option active_plugins in {$this->prefix}options", Logger::TYPE_WARNING );
795
- //$this->returnException( "Data Crunching Step 8: Can not update row wpstg_version in {$this->tmpPrefix}options - db error: " . $this->db->last_error );
796
- return false;
797
- }
798
-
799
- $this->log( "Data Crunching Step 14: Successfull!" );
800
- return true;
801
- }
802
-
803
- /**
804
- * Update Table Prefix in wp_options
805
- * @return bool
806
- */
807
- protected function step15() {
808
- $this->log( "Preparing Data Step15: Updating db prefix in {$this->prefix}options." );
809
-
810
- // Skip - Table does not exist
811
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
812
- return true;
813
- }
814
-
815
- // Skip - Table is not selected or updated
816
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
817
- $this->log( "Preparing Data Step4: Skipping" );
818
- return true;
819
- }
820
-
821
-
822
- $this->log( "Updating db option_names in {$this->prefix}options. " );
823
-
824
- // Filter the rows below. Do not update them!
825
- $filters = array(
826
- 'wp_mail_smtp',
827
- 'wp_mail_smtp_version',
828
- 'wp_mail_smtp_debug',
829
- );
830
-
831
- $filters = apply_filters( 'wpstg_data_excl_rows', $filters );
832
-
833
- $where = "";
834
- foreach ( $filters as $filter ) {
835
- $where .= " AND option_name <> '" . $filter . "'";
836
- }
837
-
838
- $updateOptions = $this->db->query(
839
- $this->db->prepare(
840
- "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 . "_%"
841
- )
842
- );
843
-
844
- if( !$updateOptions ) {
845
- $this->log( "Preparing Data Step15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
846
- $this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
847
- return false;
848
- }
849
-
850
- return true;
851
- }
852
-
853
- /**
854
- * Change upload_path in wp_options (if it is defined)
855
- * @return bool
856
- */
857
- protected function step16() {
858
- $this->log( "Preparing Data Step16: Updating upload_path {$this->prefix}options." );
859
-
860
- // Skip - Table does not exist
861
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
862
- return true;
863
- }
864
-
865
- $newUploadPath = $this->getNewUploadPath();
866
-
867
- if( false === $newUploadPath ) {
868
- $this->log( "Preparing Data Step16: Skipping" );
869
- return true;
870
- }
871
-
872
- // Skip - Table is not selected or updated
873
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
874
- $this->log( "Preparing Data Step16: Skipping" );
875
- return true;
876
- }
877
-
878
- $error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
879
-
880
- $this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
881
-
882
- $updateOptions = $this->db->query(
883
- $this->db->prepare(
884
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
885
- )
886
- );
887
-
888
- if( !$updateOptions ) {
889
- $this->log( "Preparing Data Step16: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
890
- return true;
891
- }
892
- $this->Log( "Preparing Data: Finished Step 16 successfully" );
893
- return true;
894
- }
895
-
896
- protected function getNewUploadPath() {
897
- $uploadPath = get_option( 'upload_path' );
898
-
899
- if( !$uploadPath ) {
900
- return false;
901
- }
902
-
903
- $customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
904
-
905
- $newUploadPath = \WPStaging\WPStaging::getWPpath() . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR . $customSlug;
906
-
907
- return $newUploadPath;
908
- }
909
-
910
- /**
911
- * Return URL to staging site
912
- * @return string
913
- */
914
- protected function getStagingSiteUrl() {
915
- if( $this->isSubDir() ) {
916
- return rtrim( $this->multisiteHomeDomain, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName;
917
- }
918
-
919
- return rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName;
920
- }
921
-
922
- /**
923
- * Check if WP is installed in subdir
924
- * @return boolean
925
- */
926
- protected function isSubDir() {
927
- // Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
928
- // This is happening much more often than you would expect
929
- $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
930
- $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
931
-
932
- if( $home !== $siteurl ) {
933
- return true;
934
- }
935
- return false;
936
- }
937
-
938
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 = 1;
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 = 17;
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( $wpdb->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 Path === Staging path
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
+ * Get the install sub directory if WP is installed in sub directory
182
+ * @return string
183
+ */
184
+ protected function getSubDir() {
185
+ return '/';
186
+
187
+ // $home = get_option( 'home' );
188
+ // $siteurl = get_option( 'siteurl' );
189
+ //
190
+ // if( empty( $home ) || empty( $siteurl ) ) {
191
+ // return '/';
192
+ // }
193
+ //
194
+ // $dir = str_replace( $home, '', $siteurl );
195
+ // return '/' . str_replace( '/', '', $dir ) . '/';
196
+ }
197
+
198
+ /**
199
+ * Copy wp-config.php if it is located outside of root one level up
200
+ * @todo Needs some more testing before it will be released
201
+ * @return boolean
202
+ */
203
+ // protected function step0(){
204
+ // $this->log( "Preparing Data Step0: Check if wp-config.php is located in root path", Logger::TYPE_INFO );
205
+ //
206
+ // $dir = trailingslashit( dirname( ABSPATH ) );
207
+ //
208
+ // $source = $dir . 'wp-config.php';
209
+ //
210
+ // $destination = trailingslashit(ABSPATH) . $this->clone->cloneDirectoryName . DIRECTORY_SEPARATOR . 'wp-config.php';
211
+ //
212
+ //
213
+ // // Do not do anything
214
+ // if( (!is_file( $source ) && !is_link($source)) || is_file($destination) ) {
215
+ // $this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
216
+ // return true;
217
+ // }
218
+ //
219
+ // // Copy target of a symbolic link
220
+ // if (is_link($source)){
221
+ // $this->log( "Preparing Data Step0: Symbolic link found...", Logger::TYPE_INFO );
222
+ // if (!@copy(readlink($source), $destination)) {
223
+ // $this->log("Preparing Data Step0: Failed to copy wp-config.php {$source} -> {$destination}", Logger::TYPE_ERROR);
224
+ // return true;
225
+ // }
226
+ // }
227
+ //
228
+ // // regular copy of wp-config.php
229
+ // if (!@copy($source, $destination)) {
230
+ // $this->log("Preparing Data Step0: Failed to copy wp-config.php {$source} -> {$destination}", Logger::TYPE_ERROR);
231
+ // return true;
232
+ // }
233
+ // $this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
234
+ // return true;
235
+ //
236
+ // }
237
+
238
+ /**
239
+ * Replace "siteurl" and "home"
240
+ * @return bool
241
+ */
242
+ protected function step1() {
243
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
244
+
245
+ // Skip - Table does not exist
246
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
247
+ return true;
248
+ }
249
+ // Skip - Table is not selected or updated
250
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
251
+ $this->log( "Preparing Data Step1: Skipping" );
252
+ return true;
253
+ }
254
+
255
+ // Installed in sub-directory
256
+ if( $this->isSubDir() ) {
257
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName );
258
+ // Replace URLs
259
+ $result = $this->db->query(
260
+ $this->db->prepare(
261
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->multisiteHomeDomain, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName
262
+ )
263
+ );
264
+ } else {
265
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName );
266
+ // Replace URLs
267
+ $result = $this->db->query(
268
+ $this->db->prepare(
269
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName
270
+ )
271
+ );
272
+ }
273
+
274
+
275
+ // All good
276
+ if( $result ) {
277
+ return true;
278
+ }
279
+
280
+ $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
281
+ return false;
282
+ }
283
+
284
+ /**
285
+ * Update "wpstg_is_staging_site"
286
+ * @return bool
287
+ */
288
+ protected function step2() {
289
+
290
+ $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
291
+
292
+ // Skip - Table does not exist
293
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
294
+ return true;
295
+ }
296
+ // Skip - Table is not selected or updated
297
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
298
+ $this->log( "Preparing Data Step2: Skipping" );
299
+ return true;
300
+ }
301
+
302
+ $result = $this->db->query(
303
+ $this->db->prepare(
304
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
305
+ )
306
+ );
307
+
308
+ // No errors but no option name such as wpstg_is_staging_site
309
+ if( '' === $this->db->last_error && 0 == $result ) {
310
+ $result = $this->db->query(
311
+ $this->db->prepare(
312
+ "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
313
+ )
314
+ );
315
+ }
316
+
317
+ // All good
318
+ if( $result ) {
319
+ return true;
320
+ }
321
+
322
+ $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
323
+ return false;
324
+ }
325
+
326
+ /**
327
+ * Update rewrite_rules
328
+ * @return bool
329
+ */
330
+ protected function step3() {
331
+
332
+ $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
333
+
334
+ // Skip - Table does not exist
335
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
336
+ return true;
337
+ }
338
+
339
+ // Skip - Table is not selected or updated
340
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
341
+ $this->log( "Preparing Data Step3: Skipping" );
342
+ return true;
343
+ }
344
+
345
+ $result = $this->db->query(
346
+ $this->db->prepare(
347
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
348
+ )
349
+ );
350
+
351
+ // All good
352
+ if( $result ) {
353
+ return true;
354
+ }
355
+
356
+ $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
357
+ return true;
358
+ }
359
+
360
+ /**
361
+ * Update Table Prefix in wp_usermeta and wp_options
362
+ * @return bool
363
+ */
364
+ protected function step4() {
365
+ $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. {$this->db->last_error}" );
366
+
367
+ // Skip - Table does not exist
368
+ if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
369
+ return true;
370
+ }
371
+
372
+ // Skip - Table is not selected or updated
373
+ if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
374
+ $this->log( "Preparing Data Step4: Skipping" );
375
+ return true;
376
+ }
377
+
378
+ $update = $this->db->query(
379
+ $this->db->prepare(
380
+ "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 . "_%"
381
+ )
382
+ );
383
+
384
+ if( !$update ) {
385
+ $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
386
+ $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
387
+ return false;
388
+ }
389
+ return true;
390
+ }
391
+
392
+ /**
393
+ * Update $table_prefix in wp-config.php
394
+ * @return bool
395
+ */
396
+ protected function step5() {
397
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
398
+
399
+ $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
400
+ if( false === ($content = file_get_contents( $path )) ) {
401
+ $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
402
+ return false;
403
+ }
404
+
405
+ // Replace table prefix
406
+ $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
407
+
408
+ // Replace URLs
409
+ $content = str_replace( $this->multisiteHomeDomain, $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName, $content );
410
+
411
+ if( false === @file_put_contents( $path, $content ) ) {
412
+ $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
413
+ return false;
414
+ }
415
+
416
+ return true;
417
+ }
418
+
419
+ /**
420
+ * Reset index.php to original file
421
+ * This is needed if live site is located in subfolder
422
+ * Check first if main wordpress is used in subfolder and index.php in parent directory
423
+ * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
424
+ * @return bool
425
+ */
426
+ protected function step6() {
427
+
428
+ if( !$this->isSubDir() ) {
429
+ $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
430
+ return true;
431
+ }
432
+
433
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
434
+
435
+ if( false === ($content = file_get_contents( $path )) ) {
436
+ $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
437
+ return false;
438
+ }
439
+
440
+
441
+ if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
442
+ $this->log(
443
+ "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
444
+ );
445
+ return false;
446
+ }
447
+ $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
448
+
449
+ $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
450
+
451
+ $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
452
+ $replace.= " // Changed by WP-Staging";
453
+
454
+
455
+
456
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
457
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
458
+ return false;
459
+ }
460
+
461
+ if( false === @file_put_contents( $path, $content ) ) {
462
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
463
+ return false;
464
+ }
465
+ $this->Log( "Preparing Data: Finished Step 6 successfully" );
466
+ return true;
467
+ }
468
+
469
+ /**
470
+ * Update wpstg_rmpermalinks_executed
471
+ * @return bool
472
+ */
473
+ protected function step7() {
474
+
475
+ $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
476
+
477
+ // Skip - Table does not exist
478
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
479
+ return true;
480
+ }
481
+
482
+ // Skip - Table is not selected or updated
483
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
484
+ $this->log( "Preparing Data Step7: Skipping" );
485
+ return true;
486
+ }
487
+
488
+ $result = $this->db->query(
489
+ $this->db->prepare(
490
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
491
+ )
492
+ );
493
+
494
+ // All good
495
+ if( $result ) {
496
+ $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
497
+ return true;
498
+ }
499
+
500
+ $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
501
+ return true;
502
+ }
503
+
504
+ /**
505
+ * Update permalink_structure
506
+ * @return bool
507
+ */
508
+ protected function step8() {
509
+
510
+ $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
511
+
512
+ // Skip - Table does not exist
513
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
514
+ return true;
515
+ }
516
+
517
+ // Skip - Table is not selected or updated
518
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
519
+ $this->log( "Preparing Data Step8: Skipping" );
520
+ return true;
521
+ }
522
+
523
+ $result = $this->db->query(
524
+ $this->db->prepare(
525
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
526
+ )
527
+ );
528
+
529
+ // All good
530
+ if( $result ) {
531
+ $this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
532
+ return true;
533
+ }
534
+
535
+ $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
536
+ return true;
537
+ }
538
+
539
+ /**
540
+ * Update blog_public option to not allow staging site to be indexed by search engines
541
+ * @return bool
542
+ */
543
+ protected function step9() {
544
+
545
+ $this->log( "Preparing Data Step9: Set staging site to noindex" );
546
+
547
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
548
+ return true;
549
+ }
550
+
551
+ // Skip - Table is not selected or updated
552
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
553
+ $this->log( "Preparing Data Step9: Skipping" );
554
+ return true;
555
+ }
556
+
557
+ $result = $this->db->query(
558
+ $this->db->prepare(
559
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
560
+ )
561
+ );
562
+
563
+ // All good
564
+ if( $result ) {
565
+ $this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
566
+ return true;
567
+ }
568
+
569
+ $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
570
+ return true;
571
+ }
572
+
573
+ /**
574
+ * Update WP_HOME in wp-config.php
575
+ * @return bool
576
+ */
577
+ protected function step10() {
578
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
579
+
580
+ $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
581
+
582
+ if( false === ($content = file_get_contents( $path )) ) {
583
+ $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
584
+ return false;
585
+ }
586
+
587
+
588
+ // Get WP_HOME from wp-config.php
589
+ preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
590
+
591
+ if( !empty( $matches[1] ) ) {
592
+ $matches[1];
593
+
594
+ $pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
595
+
596
+ $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
597
+ $replace.= " // Changed by WP-Staging";
598
+
599
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
600
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
601
+ return false;
602
+ }
603
+ } else {
604
+ $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
605
+ }
606
+
607
+ if( false === @file_put_contents( $path, $content ) ) {
608
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
609
+ return false;
610
+ }
611
+ $this->Log( "Preparing Data: Finished Step 11 successfully" );
612
+ return true;
613
+ }
614
+
615
+ /**
616
+ * Update WP_SITEURL in wp-config.php
617
+ * @return bool
618
+ */
619
+ protected function step11() {
620
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
621
+
622
+ $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
623
+
624
+ if( false === ($content = file_get_contents( $path )) ) {
625
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
626
+ return false;
627
+ }
628
+
629
+
630
+ // Get WP_SITEURL from wp-config.php
631
+ preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
632
+
633
+ if( !empty( $matches[1] ) ) {
634
+ $matches[1];
635
+
636
+ $pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
637
+
638
+ $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
639
+ $replace.= " // Changed by WP-Staging";
640
+
641
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
642
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
643
+ return false;
644
+ }
645
+ } else {
646
+ $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
647
+ }
648
+
649
+
650
+ if( false === @file_put_contents( $path, $content ) ) {
651
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
652
+ return false;
653
+ }
654
+ $this->Log( "Preparing Data: Finished Step 11 successfully" );
655
+ return true;
656
+ }
657
+
658
+ /**
659
+ * Update WP_ALLOW_MULTISITE constant in wp-config.php
660
+ * @return bool
661
+ */
662
+ protected function step12() {
663
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
664
+
665
+ $this->log( "Preparing Data Step12: Updating WP_ALLOW_MULTISITE in wp-config.php to false" );
666
+
667
+ if( false === ($content = file_get_contents( $path )) ) {
668
+ $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
669
+ return false;
670
+ }
671
+
672
+
673
+ // Get WP_SITEURL from wp-config.php
674
+ preg_match( "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
675
+
676
+ if( !empty( $matches[1] ) ) {
677
+ $matches[1];
678
+
679
+ $pattern = "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/";
680
+
681
+ $replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
682
+ $replace.= " // Changed by WP-Staging";
683
+
684
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
685
+ $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE", Logger::TYPE_ERROR );
686
+ return false;
687
+ }
688
+ } else {
689
+ $this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
690
+ }
691
+
692
+
693
+ if( false === @file_put_contents( $path, $content ) ) {
694
+ $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
695
+ return false;
696
+ }
697
+ $this->Log( "Preparing Data: Finished Step 12 successfully" );
698
+ return true;
699
+ }
700
+
701
+ /**
702
+ * Update MULTISITE constant in wp-config.php
703
+ * @return bool
704
+ */
705
+ protected function step13() {
706
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
707
+
708
+ $this->log( "Preparing Data Step13: Updating MULTISITE in wp-config.php to false" );
709
+
710
+ if( false === ($content = file_get_contents( $path )) ) {
711
+ $this->log( "Preparing Data Step13: Failed to update MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
712
+ return false;
713
+ }
714
+
715
+
716
+ // Get WP_SITEURL from wp-config.php
717
+ preg_match( "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
718
+
719
+ if( !empty( $matches[1] ) ) {
720
+ $matches[1];
721
+
722
+ $pattern = "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/";
723
+
724
+ $replace = "define('MULTISITE',false); // " . $matches[1];
725
+ $replace.= " // Changed by WP-Staging";
726
+
727
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
728
+ $this->log( "Preparing Data Step13: Failed to update MULTISITE", Logger::TYPE_ERROR );
729
+ return false;
730
+ }
731
+ } else {
732
+ $this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
733
+ }
734
+
735
+
736
+ if( false === @file_put_contents( $path, $content ) ) {
737
+ $this->log( "Preparing Data Step13: Failed to update MULTISITE. Can't save contents", Logger::TYPE_ERROR );
738
+ return false;
739
+ }
740
+ $this->Log( "Preparing Data: Finished Step 13 successfully" );
741
+ return true;
742
+ }
743
+
744
+ /**
745
+ * Get active_sitewide_plugins from wp_sitemeta and active_plugins from subsite
746
+ * Merge both arrays and copy them to the staging site into active_plugins
747
+ */
748
+ protected function step14() {
749
+
750
+
751
+ $this->log( "Data Crunching Step 14: Updating active_plugins" );
752
+
753
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
754
+ $this->log( 'Data Crunching Step 14: Fatal Error ' . $this->prefix . 'options does not exist' );
755
+ $this->returnException( 'Data Crunching Step 8: Fatal Error ' . $this->prefix . 'options does not exist' );
756
+ return false;
757
+ }
758
+
759
+ // Skip - Table is not selected or updated
760
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
761
+ $this->log( "Preparing Data Step14: Skipping" );
762
+ return true;
763
+ }
764
+
765
+ // Get active_plugins value from sub site options table
766
+ $active_plugins = $this->db->get_var( "SELECT option_value FROM {$this->db->prefix}options WHERE option_name = 'active_plugins' " );
767
+
768
+ if( !$active_plugins ) {
769
+ $this->log( "Data Crunching Step 14: Option active_plugins are empty " );
770
+ $active_plugins = array();
771
+ }
772
+ // Get active_sitewide_plugins value from main multisite wp_sitemeta table
773
+ $active_sitewide_plugins = $this->db->get_var( "SELECT meta_value FROM {$this->db->base_prefix}sitemeta WHERE meta_key = 'active_sitewide_plugins' " );
774
+
775
+ if( !$active_sitewide_plugins ) {
776
+ $this->log( "Data Crunching Step 14: Options {$this->db->base_prefix}active_sitewide_plugins is empty " );
777
+ $active_sitewide_plugins = array();
778
+ }
779
+
780
+ $active_sitewide_plugins = unserialize( $active_sitewide_plugins );
781
+ $active_plugins = unserialize( $active_plugins );
782
+
783
+ $all_plugins = array_merge( $active_plugins, array_keys( $active_sitewide_plugins ) );
784
+
785
+ sort( $all_plugins );
786
+
787
+
788
+ // Update active_plugins
789
+ $update = $this->db->query(
790
+ "UPDATE {$this->prefix}options SET option_value = '" . serialize( $all_plugins ) . "' WHERE option_name = 'active_plugins'"
791
+ );
792
+
793
+ if( false === $update ) {
794
+ $this->log( "Data Crunching Step 14: Can not update option active_plugins in {$this->prefix}options", Logger::TYPE_WARNING );
795
+ //$this->returnException( "Data Crunching Step 8: Can not update row wpstg_version in {$this->tmpPrefix}options - db error: " . $this->db->last_error );
796
+ return false;
797
+ }
798
+
799
+ $this->log( "Data Crunching Step 14: Successfull!" );
800
+ return true;
801
+ }
802
+
803
+ /**
804
+ * Update Table Prefix in wp_options
805
+ * @return bool
806
+ */
807
+ protected function step15() {
808
+ $this->log( "Preparing Data Step15: Updating db prefix in {$this->prefix}options." );
809
+
810
+ // Skip - Table does not exist
811
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
812
+ return true;
813
+ }
814
+
815
+ // Skip - Table is not selected or updated
816
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
817
+ $this->log( "Preparing Data Step4: Skipping" );
818
+ return true;
819
+ }
820
+
821
+
822
+ $this->log( "Updating db option_names in {$this->prefix}options. " );
823
+
824
+ // Filter the rows below. Do not update them!
825
+ $filters = array(
826
+ 'wp_mail_smtp',
827
+ 'wp_mail_smtp_version',
828
+ 'wp_mail_smtp_debug',
829
+ );
830
+
831
+ $filters = apply_filters( 'wpstg_data_excl_rows', $filters );
832
+
833
+ $where = "";
834
+ foreach ( $filters as $filter ) {
835
+ $where .= " AND option_name <> '" . $filter . "'";
836
+ }
837
+
838
+ $updateOptions = $this->db->query(
839
+ $this->db->prepare(
840
+ "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 . "_%"
841
+ )
842
+ );
843
+
844
+ if( !$updateOptions ) {
845
+ $this->log( "Preparing Data Step15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
846
+ $this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
847
+ return false;
848
+ }
849
+
850
+ return true;
851
+ }
852
+
853
+ /**
854
+ * Change upload_path in wp_options (if it is defined)
855
+ * @return bool
856
+ */
857
+ protected function step16() {
858
+ $this->log( "Preparing Data Step16: Updating upload_path {$this->prefix}options." );
859
+
860
+ // Skip - Table does not exist
861
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
862
+ return true;
863
+ }
864
+
865
+ $newUploadPath = $this->getNewUploadPath();
866
+
867
+ if( false === $newUploadPath ) {
868
+ $this->log( "Preparing Data Step16: Skipping" );
869
+ return true;
870
+ }
871
+
872
+ // Skip - Table is not selected or updated
873
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
874
+ $this->log( "Preparing Data Step16: Skipping" );
875
+ return true;
876
+ }
877
+
878
+ $error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
879
+
880
+ $this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
881
+
882
+ $updateOptions = $this->db->query(
883
+ $this->db->prepare(
884
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
885
+ )
886
+ );
887
+
888
+ if( !$updateOptions ) {
889
+ $this->log( "Preparing Data Step16: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
890
+ return true;
891
+ }
892
+ $this->Log( "Preparing Data: Finished Step 16 successfully" );
893
+ return true;
894
+ }
895
+
896
+ /**
897
+ * Update WP_CACHE in wp-config.php
898
+ * @return bool
899
+ */
900
+ protected function step17() {
901
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
902
+
903
+ $this->log( "Preparing Data Step17: Set WP_CACHE in wp-config.php to false");
904
+
905
+ if( false === ($content = file_get_contents( $path )) ) {
906
+ $this->log( "Preparing Data Step17: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
907
+ return false;
908
+ }
909
+
910
+
911
+ // Get WP_CACHE from wp-config.php
912
+ preg_match( "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/", $content, $matches );
913
+
914
+ if( !empty( $matches[1] ) ) {
915
+ $matches[1];
916
+
917
+ $pattern = "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/";
918
+
919
+ $replace = "define('WP_CACHE',false); // " . $matches[1];
920
+ $replace.= " // Changed by WP-Staging";
921
+
922
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
923
+ $this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
924
+ return false;
925
+ }
926
+ } else {
927
+ $this->log( "Preparing Data Step17: WP_CACHE not defined in wp-config.php. Skipping this step." );
928
+ }
929
+
930
+ if( false === @file_put_contents( $path, $content ) ) {
931
+ $this->log( "Preparing Data Step17: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
932
+ return false;
933
+ }
934
+ $this->Log( "Preparing Data: Finished Step 17 successfully" );
935
+ return true;
936
+ }
937
+
938
+ protected function getNewUploadPath() {
939
+ $uploadPath = get_option( 'upload_path' );
940
+
941
+ if( !$uploadPath ) {
942
+ return false;
943
+ }
944
+
945
+ $customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
946
+
947
+ $newUploadPath = \WPStaging\WPStaging::getWPpath() . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR . $customSlug;
948
+
949
+ return $newUploadPath;
950
+ }
951
+
952
+ /**
953
+ * Return URL to staging site
954
+ * @return string
955
+ */
956
+ protected function getStagingSiteUrl() {
957
+ if( $this->isSubDir() ) {
958
+ return rtrim( $this->multisiteHomeDomain, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName;
959
+ }
960
+
961
+ return rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName;
962
+ }
963
+
964
+ /**
965
+ * Check if WP is installed in subdir
966
+ * @return boolean
967
+ */
968
+ protected function isSubDir() {
969
+ // Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
970
+ // This is happening much more often than you would expect
971
+ $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
972
+ $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
973
+
974
+ if( $home !== $siteurl ) {
975
+ return true;
976
+ }
977
+ return false;
978
+ }
979
+
980
+ }
apps/Backend/Modules/Jobs/Multisite/Database.php CHANGED
@@ -1,315 +1,315 @@
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\WPStaging;
11
- use WPStaging\Utils\Strings;
12
- use WPStaging\Backend\Modules\Jobs\JobExecutable;
13
-
14
- /**
15
- * Class Database
16
- * @package WPStaging\Backend\Modules\Jobs
17
- */
18
- class Database extends JobExecutable {
19
-
20
- /**
21
- * @var int
22
- */
23
- private $total = 0;
24
-
25
- /**
26
- * @var \WPDB
27
- */
28
- private $db;
29
-
30
- /**
31
- * Initialize
32
- */
33
- public function initialize() {
34
- // Add 2 to total table count because we need to copy two more tables from the main multisite installation wp_users and wp_usermeta
35
- $this->total = count( $this->options->tables ) + 2;
36
- $this->db = WPStaging::getInstance()->get( "wpdb" );
37
- $this->isFatalError();
38
-
39
- }
40
-
41
-
42
- /**
43
- * Return fatal error and stops here if subfolder already exists
44
- * and mainJob is not updating the clone
45
- * @return boolean
46
- */
47
- private function isFatalError(){
48
- $path = trailingslashit(get_home_path()) . $this->options->cloneDirectoryName;
49
- if (isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && is_dir($path)){
50
- $this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
51
- }
52
- return false;
53
- }
54
-
55
- /**
56
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
57
- * @return void
58
- */
59
- protected function calculateTotalSteps() {
60
- $this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
61
- }
62
-
63
- /**
64
- * Execute the Current Step
65
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
66
- * @return bool
67
- */
68
- protected function execute() {
69
-
70
-
71
- // Over limits threshold
72
- if( $this->isOverThreshold() ) {
73
- // Prepare response and save current progress
74
- $this->prepareResponse( false, false );
75
- $this->saveOptions();
76
- return false;
77
- }
78
-
79
- // No more steps, finished
80
- //if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
81
- if( $this->options->currentStep > $this->total ) {
82
- $this->prepareResponse( true, false );
83
- return false;
84
- }
85
-
86
- // Copy table
87
- //if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
88
- if( isset($this->options->tables[$this->options->currentStep]) && !$this->copyTable( $this->options->tables[$this->options->currentStep] ) ) {
89
- // Prepare Response
90
- $this->prepareResponse( false, false );
91
-
92
- // Not finished
93
- return true;
94
- }
95
-
96
- $this->copyWpUsers();
97
-
98
- $this->copyWpUsermeta();
99
-
100
- // Prepare Response
101
- $this->prepareResponse();
102
-
103
- // Not finished
104
- return true;
105
- }
106
-
107
- /**
108
- * Get new prefix for the staging site
109
- * @return string
110
- */
111
- private function getStagingPrefix() {
112
- $stagingPrefix = $this->options->prefix;
113
- // Make sure prefix of staging site is NEVER identical to prefix of live site!
114
- if( $stagingPrefix == $this->db->prefix ) {
115
- 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 at support@wp-staging.com' );
116
- }
117
- return $stagingPrefix;
118
- }
119
-
120
- /**
121
- * No worries, SQL queries don't eat from PHP execution time!
122
- * @param string $tableName
123
- * @return bool
124
- */
125
- private function copyTable( $tableName ) {
126
-
127
- $strings = new Strings();
128
- $tableName = is_object( $tableName ) ? $tableName->name : $tableName;
129
- $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->prefix, null, $tableName );
130
-
131
- // Drop table if necessary
132
- $this->dropTable( $newTableName );
133
-
134
- // Save current job
135
- $this->setJob( $newTableName );
136
-
137
- // Beginning of the job
138
- if( !$this->startJob( $newTableName, $tableName ) ) {
139
- return true;
140
- }
141
-
142
- // Copy data
143
- $this->copyData( $newTableName, $tableName );
144
-
145
- // Finish the step
146
- return $this->finishStep();
147
- }
148
-
149
- /**
150
- * Copy multisite global user table wp_users to wpstgX_users
151
- * @return bool
152
- */
153
- private function copyWpUsers() {
154
- $strings = new Strings();
155
- $tableName = $this->db->base_prefix . 'users';
156
- $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->base_prefix, null, $tableName );
157
-
158
- // Drop table if necessary
159
- $this->dropTable( $newTableName );
160
-
161
- // Save current job
162
- $this->setJob( $newTableName );
163
-
164
- // Beginning of the job
165
- if( !$this->startJob( $newTableName, $tableName ) ) {
166
- return true;
167
- }
168
-
169
- // Copy data
170
- $this->copyData( $newTableName, $tableName );
171
-
172
- // Finish the step
173
- return $this->finishStep();
174
- }
175
-
176
- /**
177
- * Copy multisite global user table wp_usermeta to wpstgX_users
178
- * @return bool
179
- */
180
- private function copyWpUsermeta() {
181
- $strings = new Strings();
182
- $tableName = $this->db->base_prefix . 'usermeta';
183
- $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->base_prefix, null, $tableName );
184
-
185
- // Drop table if necessary
186
- $this->dropTable( $newTableName );
187
-
188
- // Save current job
189
- $this->setJob( $newTableName );
190
-
191
- // Beginning of the job
192
- if( !$this->startJob( $newTableName, $tableName ) ) {
193
- return true;
194
- }
195
- // Copy data
196
- $this->copyData( $newTableName, $tableName );
197
-
198
- // Finish the step
199
- return $this->finishStep();
200
- }
201
-
202
- /**
203
- * Copy data from old table to new table
204
- * @param string $new
205
- * @param string $old
206
- */
207
- private function copyData( $new, $old ) {
208
- $rows = $this->options->job->start + $this->settings->queryLimit;
209
- $this->log(
210
- "DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
211
- );
212
-
213
- $limitation = '';
214
-
215
- if( 0 < ( int ) $this->settings->queryLimit ) {
216
- $limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
217
- }
218
-
219
- $this->db->query(
220
- "INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
221
- );
222
-
223
- // Set new offset
224
- $this->options->job->start += $this->settings->queryLimit;
225
- }
226
-
227
- /**
228
- * Set the job
229
- * @param string $table
230
- */
231
- private function setJob( $table ) {
232
- if( isset( $this->options->job->current ) ) {
233
- return;
234
- }
235
-
236
- $this->options->job->current = $table;
237
- $this->options->job->start = 0;
238
- }
239
-
240
- /**
241
- * Start Job
242
- * @param string $new
243
- * @param string $old
244
- * @return bool
245
- */
246
- private function startJob( $new, $old ) {
247
- if( 0 != $this->options->job->start ) {
248
- return true;
249
- }
250
-
251
- $this->log( "DB Copy: Creating table {$new}" );
252
-
253
- $this->db->query( "CREATE TABLE {$new} LIKE {$old}" );
254
-
255
- $this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
256
-
257
- if( 0 == $this->options->job->total ) {
258
- $this->finishStep();
259
- return false;
260
- }
261
-
262
- return true;
263
- }
264
-
265
- /**
266
- * Finish the step
267
- */
268
- private function finishStep() {
269
- // This job is not finished yet
270
- if( $this->options->job->total > $this->options->job->start ) {
271
- return false;
272
- }
273
-
274
- // Add it to cloned tables listing
275
- $this->options->clonedTables[] = isset($this->options->tables[$this->options->currentStep]) ? $this->options->tables[$this->options->currentStep] : false;
276
-
277
- // Reset job
278
- $this->options->job = new \stdClass();
279
-
280
- return true;
281
- }
282
-
283
- /**
284
- * Drop table if necessary
285
- * @param string $new
286
- */
287
- private function dropTable( $new ) {
288
- $old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
289
-
290
- if( !$this->shouldDropTable( $new, $old ) ) {
291
- return;
292
- }
293
-
294
- $this->log( "DB Copy: {$new} already exists, dropping it first" );
295
- $this->db->query( "DROP TABLE {$new}" );
296
- }
297
-
298
- /**
299
- * Check if table needs to be dropped
300
- * @param string $new
301
- * @param string $old
302
- * @return bool
303
- */
304
- private function shouldDropTable( $new, $old ) {
305
- return (
306
- $old === $new &&
307
- (
308
- !isset( $this->options->job->current ) ||
309
- !isset( $this->options->job->start ) ||
310
- 0 == $this->options->job->start
311
- )
312
- );
313
- }
314
-
315
- }
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\WPStaging;
11
+ use WPStaging\Utils\Strings;
12
+ use WPStaging\Backend\Modules\Jobs\JobExecutable;
13
+
14
+ /**
15
+ * Class Database
16
+ * @package WPStaging\Backend\Modules\Jobs
17
+ */
18
+ class Database extends JobExecutable {
19
+
20
+ /**
21
+ * @var int
22
+ */
23
+ private $total = 0;
24
+
25
+ /**
26
+ * @var \WPDB
27
+ */
28
+ private $db;
29
+
30
+ /**
31
+ * Initialize
32
+ */
33
+ public function initialize() {
34
+ // Add 2 to total table count because we need to copy two more tables from the main multisite installation wp_users and wp_usermeta
35
+ $this->total = count( $this->options->tables ) + 2;
36
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
37
+ $this->isFatalError();
38
+
39
+ }
40
+
41
+
42
+ /**
43
+ * Return fatal error and stops here if subfolder already exists
44
+ * and mainJob is not updating the clone
45
+ * @return boolean
46
+ */
47
+ private function isFatalError(){
48
+ $path = trailingslashit(get_home_path()) . $this->options->cloneDirectoryName;
49
+ if (isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && is_dir($path)){
50
+ $this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
51
+ }
52
+ return false;
53
+ }
54
+
55
+ /**
56
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
57
+ * @return void
58
+ */
59
+ protected function calculateTotalSteps() {
60
+ $this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
61
+ }
62
+
63
+ /**
64
+ * Execute the Current Step
65
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
66
+ * @return bool
67
+ */
68
+ protected function execute() {
69
+
70
+
71
+ // Over limits threshold
72
+ if( $this->isOverThreshold() ) {
73
+ // Prepare response and save current progress
74
+ $this->prepareResponse( false, false );
75
+ $this->saveOptions();
76
+ return false;
77
+ }
78
+
79
+ // No more steps, finished
80
+ //if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
81
+ if( $this->options->currentStep > $this->total ) {
82
+ $this->prepareResponse( true, false );
83
+ return false;
84
+ }
85
+
86
+ // Copy table
87
+ //if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
88
+ if( isset($this->options->tables[$this->options->currentStep]) && !$this->copyTable( $this->options->tables[$this->options->currentStep] ) ) {
89
+ // Prepare Response
90
+ $this->prepareResponse( false, false );
91
+
92
+ // Not finished
93
+ return true;
94
+ }
95
+
96
+ $this->copyWpUsers();
97
+
98
+ $this->copyWpUsermeta();
99
+
100
+ // Prepare Response
101
+ $this->prepareResponse();
102
+
103
+ // Not finished
104
+ return true;
105
+ }
106
+
107
+ /**
108
+ * Get new prefix for the staging site
109
+ * @return string
110
+ */
111
+ private function getStagingPrefix() {
112
+ $stagingPrefix = $this->options->prefix;
113
+ // Make sure prefix of staging site is NEVER identical to prefix of live site!
114
+ if( $stagingPrefix == $this->db->prefix ) {
115
+ 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 at support@wp-staging.com' );
116
+ }
117
+ return $stagingPrefix;
118
+ }
119
+
120
+ /**
121
+ * No worries, SQL queries don't eat from PHP execution time!
122
+ * @param string $tableName
123
+ * @return bool
124
+ */
125
+ private function copyTable( $tableName ) {
126
+
127
+ $strings = new Strings();
128
+ $tableName = is_object( $tableName ) ? $tableName->name : $tableName;
129
+ $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->prefix, null, $tableName );
130
+
131
+ // Drop table if necessary
132
+ $this->dropTable( $newTableName );
133
+
134
+ // Save current job
135
+ $this->setJob( $newTableName );
136
+
137
+ // Beginning of the job
138
+ if( !$this->startJob( $newTableName, $tableName ) ) {
139
+ return true;
140
+ }
141
+
142
+ // Copy data
143
+ $this->copyData( $newTableName, $tableName );
144
+
145
+ // Finish the step
146
+ return $this->finishStep();
147
+ }
148
+
149
+ /**
150
+ * Copy multisite global user table wp_users to wpstgX_users
151
+ * @return bool
152
+ */
153
+ private function copyWpUsers() {
154
+ $strings = new Strings();
155
+ $tableName = $this->db->base_prefix . 'users';
156
+ $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->base_prefix, null, $tableName );
157
+
158
+ // Drop table if necessary
159
+ $this->dropTable( $newTableName );
160
+
161
+ // Save current job
162
+ $this->setJob( $newTableName );
163
+
164
+ // Beginning of the job
165
+ if( !$this->startJob( $newTableName, $tableName ) ) {
166
+ return true;
167
+ }
168
+
169
+ // Copy data
170
+ $this->copyData( $newTableName, $tableName );
171
+
172
+ // Finish the step
173
+ return $this->finishStep();
174
+ }
175
+
176
+ /**
177
+ * Copy multisite global user table wp_usermeta to wpstgX_users
178
+ * @return bool
179
+ */
180
+ private function copyWpUsermeta() {
181
+ $strings = new Strings();
182
+ $tableName = $this->db->base_prefix . 'usermeta';
183
+ $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->base_prefix, null, $tableName );
184
+
185
+ // Drop table if necessary
186
+ $this->dropTable( $newTableName );
187
+
188
+ // Save current job
189
+ $this->setJob( $newTableName );
190
+
191
+ // Beginning of the job
192
+ if( !$this->startJob( $newTableName, $tableName ) ) {
193
+ return true;
194
+ }
195
+ // Copy data
196
+ $this->copyData( $newTableName, $tableName );
197
+
198
+ // Finish the step
199
+ return $this->finishStep();
200
+ }
201
+
202
+ /**
203
+ * Copy data from old table to new table
204
+ * @param string $new
205
+ * @param string $old
206
+ */
207
+ private function copyData( $new, $old ) {
208
+ $rows = $this->options->job->start + $this->settings->queryLimit;
209
+ $this->log(
210
+ "DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
211
+ );
212
+
213
+ $limitation = '';
214
+
215
+ if( 0 < ( int ) $this->settings->queryLimit ) {
216
+ $limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
217
+ }
218
+
219
+ $this->db->query(
220
+ "INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
221
+ );
222
+
223
+ // Set new offset
224
+ $this->options->job->start += $this->settings->queryLimit;
225
+ }
226
+
227
+ /**
228
+ * Set the job
229
+ * @param string $table
230
+ */
231
+ private function setJob( $table ) {
232
+ if( isset( $this->options->job->current ) ) {
233
+ return;
234
+ }
235
+
236
+ $this->options->job->current = $table;
237
+ $this->options->job->start = 0;
238
+ }
239
+
240
+ /**
241
+ * Start Job
242
+ * @param string $new
243
+ * @param string $old
244
+ * @return bool
245
+ */
246
+ private function startJob( $new, $old ) {
247
+ if( 0 != $this->options->job->start ) {
248
+ return true;
249
+ }
250
+
251
+ $this->log( "DB Copy: Creating table {$new}" );
252
+
253
+ $this->db->query( "CREATE TABLE {$new} LIKE {$old}" );
254
+
255
+ $this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
256
+
257
+ if( 0 == $this->options->job->total ) {
258
+ $this->finishStep();
259
+ return false;
260
+ }
261
+
262
+ return true;
263
+ }
264
+
265
+ /**
266
+ * Finish the step
267
+ */
268
+ private function finishStep() {
269
+ // This job is not finished yet
270
+ if( $this->options->job->total > $this->options->job->start ) {
271
+ return false;
272
+ }
273
+
274
+ // Add it to cloned tables listing
275
+ $this->options->clonedTables[] = isset($this->options->tables[$this->options->currentStep]) ? $this->options->tables[$this->options->currentStep] : false;
276
+
277
+ // Reset job
278
+ $this->options->job = new \stdClass();
279
+
280
+ return true;
281
+ }
282
+
283
+ /**
284
+ * Drop table if necessary
285
+ * @param string $new
286
+ */
287
+ private function dropTable( $new ) {
288
+ $old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
289
+
290
+ if( !$this->shouldDropTable( $new, $old ) ) {
291
+ return;
292
+ }
293
+
294
+ $this->log( "DB Copy: {$new} already exists, dropping it first" );
295
+ $this->db->query( "DROP TABLE {$new}" );
296
+ }
297
+
298
+ /**
299
+ * Check if table needs to be dropped
300
+ * @param string $new
301
+ * @param string $old
302
+ * @return bool
303
+ */
304
+ private function shouldDropTable( $new, $old ) {
305
+ return (
306
+ $old === $new &&
307
+ (
308
+ !isset( $this->options->job->current ) ||
309
+ !isset( $this->options->job->start ) ||
310
+ 0 == $this->options->job->start
311
+ )
312
+ );
313
+ }
314
+
315
+ }
apps/Backend/Modules/Jobs/Multisite/Directories.php CHANGED
@@ -1,655 +1,655 @@
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\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
- use WPStaging\Backend\Modules\Jobs\JobExecutable;
17
-
18
- /**
19
- * Class Files
20
- * @package WPStaging\Backend\Modules\Directories
21
- */
22
- class Directories extends JobExecutable {
23
-
24
- /**
25
- * @var array
26
- */
27
- private $files = array();
28
-
29
- /**
30
- * Total steps to do
31
- * @var int
32
- */
33
- private $total = 5;
34
-
35
- /**
36
- * path to the cache file
37
- * @var string
38
- */
39
- private $filename;
40
-
41
- /**
42
- * Initialize
43
- */
44
- public function initialize() {
45
- $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
46
- }
47
-
48
- /**
49
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
50
- * @return void
51
- */
52
- protected function calculateTotalSteps() {
53
-
54
- $this->options->totalSteps = $this->total + count( $this->options->extraDirectories );
55
- }
56
-
57
- /**
58
- * Start Module
59
- * @return object
60
- */
61
- public function start() {
62
-
63
- // Execute steps
64
- $this->run();
65
-
66
- // Save option, progress
67
- $this->saveProgress();
68
-
69
- return ( object ) $this->response;
70
- }
71
-
72
- /**
73
- * Step 0
74
- * Get WP Root files
75
- * Does not collect any sub folders
76
- */
77
- private function getWpRootFiles() {
78
-
79
- // open file handle
80
- $files = $this->open( $this->filename, 'a' );
81
-
82
-
83
- try {
84
-
85
- // Iterate over wp root directory
86
- $iterator = new \DirectoryIterator( \WPStaging\WPStaging::getWPpath() );
87
-
88
- $this->log( "Scanning / for files" );
89
-
90
- // Write path line
91
- foreach ( $iterator as $item ) {
92
- if( !$item->isDot() && $item->isFile() ) {
93
- if( $this->write( $files, $iterator->getFilename() . PHP_EOL ) ) {
94
- $this->options->totalFiles++;
95
-
96
- // Add current file size
97
- $this->options->totalFileSize += $iterator->getSize();
98
- }
99
- }
100
- }
101
- } catch ( \Exception $e ) {
102
- $this->returnException( 'Error: ' . $e->getMessage() );
103
- } catch ( \Exception $e ) {
104
- // Skip bad file permissions
105
- }
106
-
107
- $this->close( $files );
108
- return true;
109
- }
110
-
111
- /**
112
- * Step 2
113
- * Get WP Content Files without multisite folder wp-content/uploads/sites
114
- */
115
- private function getWpContentFiles() {
116
-
117
- // Skip it
118
- if( $this->isDirectoryExcluded( WP_CONTENT_DIR ) ) {
119
- $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR );
120
- return true;
121
- }
122
- // open file handle
123
- $files = $this->open( $this->filename, 'a' );
124
-
125
- /**
126
- * Excluded folders relative to the folder to iterate
127
- */
128
- $excludePaths = array(
129
- 'cache',
130
- 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
131
- 'uploads' . DIRECTORY_SEPARATOR . 'sites'
132
- );
133
-
134
- /**
135
- * Get user excluded folders
136
- */
137
- $directory = array();
138
- foreach ( $this->options->excludedDirectories as $dir ) {
139
- if( strpos( $dir, WP_CONTENT_DIR ) !== false ) {
140
- $directory[] = ltrim( str_replace( WP_CONTENT_DIR, '', $dir ), '/' );
141
- }
142
- }
143
-
144
- $excludePaths = array_merge( $excludePaths, $directory );
145
-
146
- // $excludeFolders = array(
147
- // 'cache',
148
- // 'node_modules',
149
- // 'nbproject',
150
- // 'wps-hide-login'
151
- // );
152
-
153
- try {
154
-
155
- // Iterate over content directory
156
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
157
-
158
- // Exclude new line file names
159
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
160
-
161
- // Exclude sites, uploads, plugins or themes
162
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_mu_excl_folders', $excludePaths ) );
163
- // Recursively iterate over content directory
164
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
165
-
166
- $this->log( "Scanning /wp-content for its sub-directories and files" );
167
-
168
- // Write path line
169
- foreach ( $iterator as $item ) {
170
- if( $item->isFile() ) {
171
- $file = 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL;
172
- if( $this->write( $files, $file ) ) {
173
- $this->options->totalFiles++;
174
-
175
- // Add current file size
176
- $this->options->totalFileSize += $iterator->getSize();
177
- }
178
- }
179
- }
180
- } catch ( \Exception $e ) {
181
- $this->returnException( 'Error: ' . $e->getMessage() );
182
- //throw new \Exception( 'Error: ' . $e->getMessage() );
183
- } catch ( \Exception $e ) {
184
- // Skip bad file permissions
185
- }
186
-
187
- // close the file handler
188
- $this->close( $files );
189
- return true;
190
- }
191
-
192
- /**
193
- * Step 2
194
- * @return boolean
195
- * @throws \Exception
196
- */
197
- private function getWpIncludesFiles() {
198
-
199
- // Skip it
200
- if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR ) ) {
201
- $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
202
- return true;
203
- }
204
-
205
- // open file handle and attach data to end of file
206
- $files = $this->open( $this->filename, 'a' );
207
-
208
- try {
209
-
210
- // Iterate over wp-admin directory
211
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
212
-
213
- // Exclude new line file names
214
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
215
-
216
- // Recursively iterate over wp-includes directory
217
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
218
-
219
- $this->log( "Scanning /wp-includes for its sub-directories and files" );
220
-
221
- // Write files
222
- foreach ( $iterator as $item ) {
223
- if( $item->isFile() ) {
224
- if( $this->write( $files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
225
- $this->options->totalFiles++;
226
-
227
- // Add current file size
228
- $this->options->totalFileSize += $iterator->getSize();
229
- }
230
- }
231
- }
232
- } catch ( \Exception $e ) {
233
- //$this->returnException('Out of disk space.');
234
- throw new \Exception( 'Error: ' . $e->getMessage() );
235
- } catch ( \Exception $e ) {
236
- // Skip bad file permissions
237
- }
238
-
239
- // close the file handler
240
- $this->close( $files );
241
- return true;
242
- }
243
-
244
- /**
245
- * Step 3
246
- * @return boolean
247
- * @throws \Exception
248
- */
249
- private function getWpAdminFiles() {
250
-
251
- // Skip it
252
- if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR ) ) {
253
- $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
254
- return true;
255
- }
256
-
257
- // open file handle and attach data to end of file
258
- $files = $this->open( $this->filename, 'a' );
259
-
260
- try {
261
-
262
- // Iterate over wp-admin directory
263
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
264
-
265
- // Exclude new line file names
266
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
267
-
268
- // Recursively iterate over content directory
269
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
270
-
271
- $this->log( "Scanning /wp-admin for its sub-directories and files" );
272
-
273
- // Write path line
274
- foreach ( $iterator as $item ) {
275
- if( $item->isFile() ) {
276
- if( $this->write( $files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
277
- $this->options->totalFiles++;
278
- // Add current file size
279
- $this->options->totalFileSize += $iterator->getSize();
280
- }
281
- }
282
- }
283
- } catch ( \Exception $e ) {
284
- $this->returnException( 'Error: ' . $e->getMessage() );
285
- //throw new \Exception('Error: ' . $e->getMessage());
286
- } catch ( \Exception $e ) {
287
- // Skip bad file permissions
288
- }
289
-
290
- // close the file handler
291
- $this->close( $files );
292
- return true;
293
- }
294
-
295
- /**
296
- * Step 4
297
- * Get WP Content Uploads Files multisite folder wp-content/uploads/sites or wp-content/blogs.dir/ID/files
298
- */
299
- private function getWpContentUploadsSites() {
300
-
301
- // Skip if main site is cloned
302
- if( is_main_site() ) {
303
- return true;
304
- }
305
-
306
- $blogId = get_current_blog_id();
307
-
308
- // Absolute path to uploads folder
309
- //$path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . $blogId . DIRECTORY_SEPARATOR;
310
- $path = $this->getAbsUploadPath();
311
-
312
- // Skip it
313
- if( !is_dir( $path ) ) {
314
- return true;
315
- }
316
-
317
- // Skip it
318
- if( $this->isDirectoryExcluded( $path ) ) {
319
- return true;
320
- }
321
-
322
-
323
- // open file handle
324
- $files = $this->open( $this->filename, 'a' );
325
-
326
- /**
327
- * Excluded folders relative to the folder to iterate
328
- */
329
- $excludePaths = array(
330
- 'cache',
331
- 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
332
- 'uploads' . DIRECTORY_SEPARATOR . 'sites'
333
- );
334
-
335
- /**
336
- * Get user excluded folders
337
- */
338
- $directory = array();
339
- foreach ( $this->options->excludedDirectories as $dir ) {
340
- if( strpos( $dir, $path ) !== false ) {
341
- $directory[] = ltrim( str_replace( $path, '', $dir ), '/' );
342
- }
343
- }
344
-
345
- $excludePaths = array_merge( $excludePaths, $directory );
346
-
347
- try {
348
-
349
- // Iterate over content directory
350
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $path );
351
-
352
- // Exclude new line file names
353
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
354
-
355
- // Exclude sites, uploads, plugins or themes
356
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $excludePaths );
357
-
358
- // Recursively iterate over content directory
359
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
360
- $this->log( "Scanning /wp-content/uploads/sites/{$blogId} for its sub-directories and files" );
361
-
362
- // Write path line
363
- foreach ( $iterator as $item ) {
364
- if( $item->isFile() ) {
365
- //$file = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . $blogId . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL;
366
- $file = $this->getRelUploadPath() . $iterator->getSubPathName() . PHP_EOL;
367
- if( $this->write( $files, $file ) ) {
368
- $this->options->totalFiles++;
369
-
370
- // Add current file size
371
- $this->options->totalFileSize += $iterator->getSize();
372
- }
373
- }
374
- }
375
- } catch ( \Exception $e ) {
376
- $this->returnException( 'Error: ' . $e->getMessage() );
377
- //throw new \Exception( 'Error: ' . $e->getMessage() );
378
- } catch ( \Exception $e ) {
379
- // Skip bad file permissions
380
- }
381
-
382
- // close the file handler
383
- $this->close( $files );
384
- return true;
385
- }
386
-
387
- /**
388
- * Get absolute path to the upload folder e.g. /srv/www/wp-content/blogs.dir/ID/files or /srv/www/wp-content/uploads/sites/ID/
389
- * @return type
390
- */
391
- private function getAbsUploadPath() {
392
- // Check first which method is used
393
- $uploads = wp_upload_dir();
394
- $basedir = $uploads['basedir'];
395
-
396
- return trailingslashit($basedir);
397
-
398
- // if( false === strpos( $basedir, 'blogs.dir' ) ) {
399
- // // Since WP 3.5
400
- // return 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id();
401
- // } else {
402
- // // old blog structure
403
- // return 'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files';
404
- // }
405
- }
406
- /**
407
- * Get relative path to the upload folder like wp-content/uploads or wp-content/blogs.dir/2/files
408
- * @return string
409
- */
410
- private function getRelUploadPath() {
411
- $uploads = wp_upload_dir();
412
- $basedir = $uploads['basedir'];
413
-
414
- return trailingslashit(str_replace(\WPStaging\WPStaging::getWPpath(), '', $basedir));
415
- }
416
-
417
- /**
418
- * Step 5 - x
419
- * Get extra folders of the wp root level
420
- * Does not collect wp-includes, wp-admin and wp-content folder
421
- */
422
- private function getExtraFiles( $folder ) {
423
-
424
-
425
- // open file handle and attach data to end of file
426
- $files = $this->open( $this->filename, 'a' );
427
-
428
- try {
429
-
430
- // Iterate over extra directory
431
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
432
-
433
- // Exclude new line file names
434
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
435
-
436
- // Exclude wp core folders
437
- // $exclude = array('wp-includes',
438
- // 'wp-admin',
439
- // 'wp-content');
440
- //
441
- // $excludeMore = array();
442
- // foreach ($this->options->excludedDirectories as $key => $value){
443
- // $excludeMore[] = $this->getLastElemAfterString('/', $value);
444
- // }
445
- //$exclude = array_merge($exclude, $excludeMore);
446
-
447
- $exclude = array();
448
-
449
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
450
- // Recursively iterate over content directory
451
- $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
452
-
453
- $strings = new Strings();
454
- $this->log( "Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files" );
455
-
456
- // Write path line
457
- foreach ( $iterator as $item ) {
458
- if( $item->isFile() ) {
459
- //if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
460
- if( $this->write( $files, str_replace( \WPStaging\WPStaging::getWPpath(), '', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
461
- $this->options->totalFiles++;
462
- // Add current file size
463
- $this->options->totalFileSize += $iterator->getSize();
464
- }
465
- }
466
- }
467
- } catch ( \Exception $e ) {
468
- $this->returnException( 'Error: ' . $e->getMessage() );
469
- } catch ( \Exception $e ) {
470
- // Skip bad file permissions
471
- }
472
-
473
- // close the file handler
474
- $this->close( $files );
475
- return true;
476
- }
477
-
478
- /**
479
- * Closes a file handle
480
- *
481
- * @param resource $handle File handle to close
482
- * @return boolean
483
- */
484
- public function close( $handle ) {
485
- return @fclose( $handle );
486
- }
487
-
488
- /**
489
- * Opens a file in specified mode
490
- *
491
- * @param string $file Path to the file to open
492
- * @param string $mode Mode in which to open the file
493
- * @return resource
494
- * @throws Exception
495
- */
496
- public function open( $file, $mode ) {
497
-
498
- $file_handle = @fopen( $file, $mode );
499
- if( false === $file_handle ) {
500
- $this->returnException( sprintf( __( 'Unable to open %s with mode %s', 'wp-staging' ), $file, $mode ) );
501
- //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wp-staging'), $file, $mode));
502
- }
503
-
504
- return $file_handle;
505
- }
506
-
507
- /**
508
- * Write contents to a file
509
- *
510
- * @param resource $handle File handle to write to
511
- * @param string $content Contents to write to the file
512
- * @return integer
513
- * @throws Exception
514
- * @throws Exception
515
- */
516
- public function write( $handle, $content ) {
517
- $write_result = @fwrite( $handle, $content );
518
- if( false === $write_result ) {
519
- if( ( $meta = \stream_get_meta_data( $handle ) ) ) {
520
- //$this->returnException(sprintf(__('Unable to write to: %s', 'wp-staging'), $meta['uri']));
521
- throw new \Exception( sprintf( __( 'Unable to write to: %s', 'wp-staging' ), $meta['uri'] ) );
522
- }
523
- } elseif( strlen( $content ) !== $write_result ) {
524
- //$this->returnException(__('Out of disk space.', 'wp-staging'));
525
- throw new \Exception( __( 'Out of disk space.', 'wp-staging' ) );
526
- }
527
-
528
- return $write_result;
529
- }
530
-
531
- /**
532
- * Execute the Current Step
533
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
534
- * @return bool
535
- */
536
- protected function execute() {
537
-
538
- // No job left to execute
539
- if( $this->isFinished() ) {
540
- $this->prepareResponse( true, false );
541
- return false;
542
- }
543
-
544
-
545
- if( $this->options->currentStep == 0 ) {
546
- $this->getWpRootFiles();
547
- $this->prepareResponse( false, true );
548
- return false;
549
- }
550
-
551
- if( $this->options->currentStep == 1 ) {
552
- $this->getWpContentFiles();
553
- $this->prepareResponse( false, true );
554
- return false;
555
- }
556
-
557
- if( $this->options->currentStep == 2 ) {
558
- $this->getWpIncludesFiles();
559
- $this->prepareResponse( false, true );
560
- return false;
561
- }
562
-
563
- if( $this->options->currentStep == 3 ) {
564
- $this->getWpAdminFiles();
565
- $this->prepareResponse( false, true );
566
- return false;
567
- }
568
-
569
- if( $this->options->currentStep == 4 ) {
570
- $this->getWpContentUploadsSites();
571
- $this->prepareResponse( false, true );
572
- return false;
573
- }
574
-
575
- if( isset( $this->options->extraDirectories[$this->options->currentStep - $this->total] ) ) {
576
- $this->getExtraFiles( $this->options->extraDirectories[$this->options->currentStep - $this->total] );
577
- $this->prepareResponse( false, true );
578
- return false;
579
- }
580
-
581
-
582
- // Prepare response
583
- $this->prepareResponse( false, true );
584
- // Not finished
585
- return true;
586
- }
587
-
588
- /**
589
- * Checks Whether There is Any Job to Execute or Not
590
- * @return bool
591
- */
592
- protected function isFinished() {
593
- if( $this->options->currentStep >= $this->options->totalSteps ) {
594
- return true;
595
- }
596
-
597
- // return (
598
- // //$this->options->currentStep > $this->total ||
599
- // $this->options->currentStep >= $this->options->totalSteps
600
- // );
601
- }
602
-
603
- /**
604
- * Save files
605
- * @return bool
606
- */
607
- protected function saveProgress() {
608
- return $this->saveOptions();
609
- }
610
-
611
- /**
612
- * Get files
613
- * @return void
614
- */
615
- protected function getFiles() {
616
- $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
617
-
618
- if( false === ($this->files = @file_get_contents( $fileName )) ) {
619
- $this->files = array();
620
- return;
621
- }
622
-
623
- $this->files = explode( PHP_EOL, $this->files );
624
- }
625
-
626
- /**
627
- * Replace forward slash with current directory separator
628
- *
629
- * @param string $path Path
630
- *
631
- * @return string
632
- */
633
- private function sanitizeDirectorySeparator( $path ) {
634
- $string = str_replace( "/", "\\", $path );
635
- return str_replace( '\\\\', '\\', $string );
636
- }
637
-
638
- /**
639
- * Check if directory is excluded
640
- * @param string $directory
641
- * @return bool
642
- */
643
- protected function isDirectoryExcluded( $directory ) {
644
- $directory = $this->sanitizeDirectorySeparator( $directory );
645
- foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
646
- $excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
647
- if( strpos( trailingslashit( $directory ), trailingslashit( $excludedDirectory ) ) === 0 ) {
648
- return true;
649
- }
650
- }
651
-
652
- return false;
653
- }
654
-
655
- }
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\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
+ use WPStaging\Backend\Modules\Jobs\JobExecutable;
17
+
18
+ /**
19
+ * Class Files
20
+ * @package WPStaging\Backend\Modules\Directories
21
+ */
22
+ class Directories extends JobExecutable {
23
+
24
+ /**
25
+ * @var array
26
+ */
27
+ private $files = array();
28
+
29
+ /**
30
+ * Total steps to do
31
+ * @var int
32
+ */
33
+ private $total = 5;
34
+
35
+ /**
36
+ * path to the cache file
37
+ * @var string
38
+ */
39
+ private $filename;
40
+
41
+ /**
42
+ * Initialize
43
+ */
44
+ public function initialize() {
45
+ $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
46
+ }
47
+
48
+ /**
49
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
50
+ * @return void
51
+ */
52
+ protected function calculateTotalSteps() {
53
+
54
+ $this->options->totalSteps = $this->total + count( $this->options->extraDirectories );
55
+ }
56
+
57
+ /**
58
+ * Start Module
59
+ * @return object
60
+ */
61
+ public function start() {
62
+
63
+ // Execute steps
64
+ $this->run();
65
+
66
+ // Save option, progress
67
+ $this->saveProgress();
68
+
69
+ return ( object ) $this->response;
70
+ }
71
+
72
+ /**
73
+ * Step 0
74
+ * Get WP Root files
75
+ * Does not collect any sub folders
76
+ */
77
+ private function getWpRootFiles() {
78
+
79
+ // open file handle
80
+ $files = $this->open( $this->filename, 'a' );
81
+
82
+
83
+ try {
84
+
85
+ // Iterate over wp root directory
86
+ $iterator = new \DirectoryIterator( \WPStaging\WPStaging::getWPpath() );
87
+
88
+ $this->log( "Scanning / for files" );
89
+
90
+ // Write path line
91
+ foreach ( $iterator as $item ) {
92
+ if( !$item->isDot() && $item->isFile() ) {
93
+ if( $this->write( $files, $iterator->getFilename() . PHP_EOL ) ) {
94
+ $this->options->totalFiles++;
95
+
96
+ // Add current file size
97
+ $this->options->totalFileSize += $iterator->getSize();
98
+ }
99
+ }
100
+ }
101
+ } catch ( \Exception $e ) {
102
+ $this->returnException( 'Error: ' . $e->getMessage() );
103
+ } catch ( \Exception $e ) {
104
+ // Skip bad file permissions
105
+ }
106
+
107
+ $this->close( $files );
108
+ return true;
109
+ }
110
+
111
+ /**
112
+ * Step 2
113
+ * Get WP Content Files without multisite folder wp-content/uploads/sites
114
+ */
115
+ private function getWpContentFiles() {
116
+
117
+ // Skip it
118
+ if( $this->isDirectoryExcluded( WP_CONTENT_DIR ) ) {
119
+ $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-content' . DIRECTORY_SEPARATOR );
120
+ return true;
121
+ }
122
+ // open file handle
123
+ $files = $this->open( $this->filename, 'a' );
124
+
125
+ /**
126
+ * Excluded folders relative to the folder to iterate
127
+ */
128
+ $excludePaths = array(
129
+ 'cache',
130
+ 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
131
+ 'uploads' . DIRECTORY_SEPARATOR . 'sites'
132
+ );
133
+
134
+ /**
135
+ * Get user excluded folders
136
+ */
137
+ $directory = array();
138
+ foreach ( $this->options->excludedDirectories as $dir ) {
139
+ if( strpos( $dir, WP_CONTENT_DIR ) !== false ) {
140
+ $directory[] = ltrim( str_replace( WP_CONTENT_DIR, '', $dir ), '/' );
141
+ }
142
+ }
143
+
144
+ $excludePaths = array_merge( $excludePaths, $directory );
145
+
146
+ // $excludeFolders = array(
147
+ // 'cache',
148
+ // 'node_modules',
149
+ // 'nbproject',
150
+ // 'wps-hide-login'
151
+ // );
152
+
153
+ try {
154
+
155
+ // Iterate over content directory
156
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
157
+
158
+ // Exclude new line file names
159
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
160
+
161
+ // Exclude sites, uploads, plugins or themes
162
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_mu_excl_folders', $excludePaths ) );
163
+ // Recursively iterate over content directory
164
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
165
+
166
+ $this->log( "Scanning /wp-content for its sub-directories and files" );
167
+
168
+ // Write path line
169
+ foreach ( $iterator as $item ) {
170
+ if( $item->isFile() ) {
171
+ $file = 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL;
172
+ if( $this->write( $files, $file ) ) {
173
+ $this->options->totalFiles++;
174
+
175
+ // Add current file size
176
+ $this->options->totalFileSize += $iterator->getSize();
177
+ }
178
+ }
179
+ }
180
+ } catch ( \Exception $e ) {
181
+ $this->returnException( 'Error: ' . $e->getMessage() );
182
+ //throw new \Exception( 'Error: ' . $e->getMessage() );
183
+ } catch ( \Exception $e ) {
184
+ // Skip bad file permissions
185
+ }
186
+
187
+ // close the file handler
188
+ $this->close( $files );
189
+ return true;
190
+ }
191
+
192
+ /**
193
+ * Step 2
194
+ * @return boolean
195
+ * @throws \Exception
196
+ */
197
+ private function getWpIncludesFiles() {
198
+
199
+ // Skip it
200
+ if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR ) ) {
201
+ $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
202
+ return true;
203
+ }
204
+
205
+ // open file handle and attach data to end of file
206
+ $files = $this->open( $this->filename, 'a' );
207
+
208
+ try {
209
+
210
+ // Iterate over wp-admin directory
211
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
212
+
213
+ // Exclude new line file names
214
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
215
+
216
+ // Recursively iterate over wp-includes directory
217
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
218
+
219
+ $this->log( "Scanning /wp-includes for its sub-directories and files" );
220
+
221
+ // Write files
222
+ foreach ( $iterator as $item ) {
223
+ if( $item->isFile() ) {
224
+ if( $this->write( $files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
225
+ $this->options->totalFiles++;
226
+
227
+ // Add current file size
228
+ $this->options->totalFileSize += $iterator->getSize();
229
+ }
230
+ }
231
+ }
232
+ } catch ( \Exception $e ) {
233
+ //$this->returnException('Out of disk space.');
234
+ throw new \Exception( 'Error: ' . $e->getMessage() );
235
+ } catch ( \Exception $e ) {
236
+ // Skip bad file permissions
237
+ }
238
+
239
+ // close the file handler
240
+ $this->close( $files );
241
+ return true;
242
+ }
243
+
244
+ /**
245
+ * Step 3
246
+ * @return boolean
247
+ * @throws \Exception
248
+ */
249
+ private function getWpAdminFiles() {
250
+
251
+ // Skip it
252
+ if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR ) ) {
253
+ $this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
254
+ return true;
255
+ }
256
+
257
+ // open file handle and attach data to end of file
258
+ $files = $this->open( $this->filename, 'a' );
259
+
260
+ try {
261
+
262
+ // Iterate over wp-admin directory
263
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
264
+
265
+ // Exclude new line file names
266
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
267
+
268
+ // Recursively iterate over content directory
269
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
270
+
271
+ $this->log( "Scanning /wp-admin for its sub-directories and files" );
272
+
273
+ // Write path line
274
+ foreach ( $iterator as $item ) {
275
+ if( $item->isFile() ) {
276
+ if( $this->write( $files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
277
+ $this->options->totalFiles++;
278
+ // Add current file size
279
+ $this->options->totalFileSize += $iterator->getSize();
280
+ }
281
+ }
282
+ }
283
+ } catch ( \Exception $e ) {
284
+ $this->returnException( 'Error: ' . $e->getMessage() );
285
+ //throw new \Exception('Error: ' . $e->getMessage());
286
+ } catch ( \Exception $e ) {
287
+ // Skip bad file permissions
288
+ }
289
+
290
+ // close the file handler
291
+ $this->close( $files );
292
+ return true;
293
+ }
294
+
295
+ /**
296
+ * Step 4
297
+ * Get WP Content Uploads Files multisite folder wp-content/uploads/sites or wp-content/blogs.dir/ID/files
298
+ */
299
+ private function getWpContentUploadsSites() {
300
+
301
+ // Skip if main site is cloned
302
+ if( is_main_site() ) {
303
+ return true;
304
+ }
305
+
306
+ $blogId = get_current_blog_id();
307
+
308
+ // Absolute path to uploads folder
309
+ //$path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . $blogId . DIRECTORY_SEPARATOR;
310
+ $path = $this->getAbsUploadPath();
311
+
312
+ // Skip it
313
+ if( !is_dir( $path ) ) {
314
+ return true;
315
+ }
316
+
317
+ // Skip it
318
+ if( $this->isDirectoryExcluded( $path ) ) {
319
+ return true;
320
+ }
321
+
322
+
323
+ // open file handle
324
+ $files = $this->open( $this->filename, 'a' );
325
+
326
+ /**
327
+ * Excluded folders relative to the folder to iterate
328
+ */
329
+ $excludePaths = array(
330
+ 'cache',
331
+ 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
332
+ 'uploads' . DIRECTORY_SEPARATOR . 'sites'
333
+ );
334
+
335
+ /**
336
+ * Get user excluded folders
337
+ */
338
+ $directory = array();
339
+ foreach ( $this->options->excludedDirectories as $dir ) {
340
+ if( strpos( $dir, $path ) !== false ) {
341
+ $directory[] = ltrim( str_replace( $path, '', $dir ), '/' );
342
+ }
343
+ }
344
+
345
+ $excludePaths = array_merge( $excludePaths, $directory );
346
+
347
+ try {
348
+
349
+ // Iterate over content directory
350
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $path );
351
+
352
+ // Exclude new line file names
353
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
354
+
355
+ // Exclude sites, uploads, plugins or themes
356
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $excludePaths );
357
+
358
+ // Recursively iterate over content directory
359
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
360
+ $this->log( "Scanning /wp-content/uploads/sites/{$blogId} for its sub-directories and files" );
361
+
362
+ // Write path line
363
+ foreach ( $iterator as $item ) {
364
+ if( $item->isFile() ) {
365
+ //$file = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . $blogId . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL;
366
+ $file = $this->getRelUploadPath() . $iterator->getSubPathName() . PHP_EOL;
367
+ if( $this->write( $files, $file ) ) {
368
+ $this->options->totalFiles++;
369
+
370
+ // Add current file size
371
+ $this->options->totalFileSize += $iterator->getSize();
372
+ }
373
+ }
374
+ }
375
+ } catch ( \Exception $e ) {
376
+ $this->returnException( 'Error: ' . $e->getMessage() );
377
+ //throw new \Exception( 'Error: ' . $e->getMessage() );
378
+ } catch ( \Exception $e ) {
379
+ // Skip bad file permissions
380
+ }
381
+
382
+ // close the file handler
383
+ $this->close( $files );
384
+ return true;
385
+ }
386
+
387
+ /**
388
+ * Get absolute path to the upload folder e.g. /srv/www/wp-content/blogs.dir/ID/files or /srv/www/wp-content/uploads/sites/ID/
389
+ * @return type
390
+ */
391
+ private function getAbsUploadPath() {
392
+ // Check first which method is used
393
+ $uploads = wp_upload_dir();
394
+ $basedir = $uploads['basedir'];
395
+
396
+ return trailingslashit($basedir);
397
+
398
+ // if( false === strpos( $basedir, 'blogs.dir' ) ) {
399
+ // // Since WP 3.5
400
+ // return 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id();
401
+ // } else {
402
+ // // old blog structure
403
+ // return 'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files';
404
+ // }
405
+ }
406
+ /**
407
+ * Get relative path to the upload folder like wp-content/uploads or wp-content/blogs.dir/2/files
408
+ * @return string
409
+ */
410
+ private function getRelUploadPath() {
411
+ $uploads = wp_upload_dir();
412
+ $basedir = $uploads['basedir'];
413
+
414
+ return trailingslashit(str_replace(\WPStaging\WPStaging::getWPpath(), '', $basedir));
415
+ }
416
+
417
+ /**
418
+ * Step 5 - x
419
+ * Get extra folders of the wp root level
420
+ * Does not collect wp-includes, wp-admin and wp-content folder
421
+ */
422
+ private function getExtraFiles( $folder ) {
423
+
424
+
425
+ // open file handle and attach data to end of file
426
+ $files = $this->open( $this->filename, 'a' );
427
+
428
+ try {
429
+
430
+ // Iterate over extra directory
431
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
432
+
433
+ // Exclude new line file names
434
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
435
+
436
+ // Exclude wp core folders
437
+ // $exclude = array('wp-includes',
438
+ // 'wp-admin',
439
+ // 'wp-content');
440
+ //
441
+ // $excludeMore = array();
442
+ // foreach ($this->options->excludedDirectories as $key => $value){
443
+ // $excludeMore[] = $this->getLastElemAfterString('/', $value);
444
+ // }
445
+ //$exclude = array_merge($exclude, $excludeMore);
446
+
447
+ $exclude = array();
448
+
449
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
450
+ // Recursively iterate over content directory
451
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
452
+
453
+ $strings = new Strings();
454
+ $this->log( "Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files" );
455
+
456
+ // Write path line
457
+ foreach ( $iterator as $item ) {
458
+ if( $item->isFile() ) {
459
+ //if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
460
+ if( $this->write( $files, str_replace( \WPStaging\WPStaging::getWPpath(), '', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
461
+ $this->options->totalFiles++;
462
+ // Add current file size
463
+ $this->options->totalFileSize += $iterator->getSize();
464
+ }
465
+ }
466
+ }
467
+ } catch ( \Exception $e ) {
468
+ $this->returnException( 'Error: ' . $e->getMessage() );
469
+ } catch ( \Exception $e ) {
470
+ // Skip bad file permissions
471
+ }
472
+
473
+ // close the file handler
474
+ $this->close( $files );
475
+ return true;
476
+ }
477
+
478
+ /**
479
+ * Closes a file handle
480
+ *
481
+ * @param resource $handle File handle to close
482
+ * @return boolean
483
+ */
484
+ public function close( $handle ) {
485
+ return @fclose( $handle );
486
+ }
487
+
488
+ /**
489
+ * Opens a file in specified mode
490
+ *
491
+ * @param string $file Path to the file to open
492
+ * @param string $mode Mode in which to open the file
493
+ * @return resource
494
+ * @throws Exception
495
+ */
496
+ public function open( $file, $mode ) {
497
+
498
+ $file_handle = @fopen( $file, $mode );
499
+ if( false === $file_handle ) {
500
+ $this->returnException( sprintf( __( 'Unable to open %s with mode %s', 'wp-staging' ), $file, $mode ) );
501
+ //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wp-staging'), $file, $mode));
502
+ }
503
+
504
+ return $file_handle;
505
+ }
506
+
507
+ /**
508
+ * Write contents to a file
509
+ *
510
+ * @param resource $handle File handle to write to
511
+ * @param string $content Contents to write to the file
512
+ * @return integer
513
+ * @throws Exception
514
+ * @throws Exception
515
+ */
516
+ public function write( $handle, $content ) {
517
+ $write_result = @fwrite( $handle, $content );
518
+ if( false === $write_result ) {
519
+ if( ( $meta = \stream_get_meta_data( $handle ) ) ) {
520
+ //$this->returnException(sprintf(__('Unable to write to: %s', 'wp-staging'), $meta['uri']));
521
+ throw new \Exception( sprintf( __( 'Unable to write to: %s', 'wp-staging' ), $meta['uri'] ) );
522
+ }
523
+ } elseif( strlen( $content ) !== $write_result ) {
524
+ //$this->returnException(__('Out of disk space.', 'wp-staging'));
525
+ throw new \Exception( __( 'Out of disk space.', 'wp-staging' ) );
526
+ }
527
+
528
+ return $write_result;
529
+ }
530
+
531
+ /**
532
+ * Execute the Current Step
533
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
534
+ * @return bool
535
+ */
536
+ protected function execute() {
537
+
538
+ // No job left to execute
539
+ if( $this->isFinished() ) {
540
+ $this->prepareResponse( true, false );
541
+ return false;
542
+ }
543
+
544
+
545
+ if( $this->options->currentStep == 0 ) {
546
+ $this->getWpRootFiles();
547
+ $this->prepareResponse( false, true );
548
+ return false;
549
+ }
550
+
551
+ if( $this->options->currentStep == 1 ) {
552
+ $this->getWpContentFiles();
553
+ $this->prepareResponse( false, true );
554
+ return false;
555
+ }
556
+
557
+ if( $this->options->currentStep == 2 ) {
558
+ $this->getWpIncludesFiles();
559
+ $this->prepareResponse( false, true );
560
+ return false;
561
+ }
562
+
563
+ if( $this->options->currentStep == 3 ) {
564
+ $this->getWpAdminFiles();
565
+ $this->prepareResponse( false, true );
566
+ return false;
567
+ }
568
+
569
+ if( $this->options->currentStep == 4 ) {
570
+ $this->getWpContentUploadsSites();
571
+ $this->prepareResponse( false, true );
572
+ return false;
573
+ }
574
+
575
+ if( isset( $this->options->extraDirectories[$this->options->currentStep - $this->total] ) ) {
576
+ $this->getExtraFiles( $this->options->extraDirectories[$this->options->currentStep - $this->total] );
577
+ $this->prepareResponse( false, true );
578
+ return false;
579
+ }
580
+
581
+
582
+ // Prepare response
583
+ $this->prepareResponse( false, true );
584
+ // Not finished
585
+ return true;
586
+ }
587
+
588
+ /**
589
+ * Checks Whether There is Any Job to Execute or Not
590
+ * @return bool
591
+ */
592
+ protected function isFinished() {
593
+ if( $this->options->currentStep >= $this->options->totalSteps ) {
594
+ return true;
595
+ }
596
+
597
+ // return (
598
+ // //$this->options->currentStep > $this->total ||
599
+ // $this->options->currentStep >= $this->options->totalSteps
600
+ // );
601
+ }
602
+
603
+ /**
604
+ * Save files
605
+ * @return bool
606
+ */
607
+ protected function saveProgress() {
608
+ return $this->saveOptions();
609
+ }
610
+
611
+ /**
612
+ * Get files
613
+ * @return void
614
+ */
615
+ protected function getFiles() {
616
+ $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
617
+
618
+ if( false === ($this->files = @file_get_contents( $fileName )) ) {
619
+ $this->files = array();
620
+ return;
621
+ }
622
+
623
+ $this->files = explode( PHP_EOL, $this->files );
624
+ }
625
+
626
+ /**
627
+ * Replace forward slash with current directory separator
628
+ *
629
+ * @param string $path Path
630
+ *
631
+ * @return string
632
+ */
633
+ private function sanitizeDirectorySeparator( $path ) {
634
+ $string = str_replace( "/", "\\", $path );
635
+ return str_replace( '\\\\', '\\', $string );
636
+ }
637
+
638
+ /**
639
+ * Check if directory is excluded
640
+ * @param string $directory
641
+ * @return bool
642
+ */
643
+ protected function isDirectoryExcluded( $directory ) {
644
+ $directory = $this->sanitizeDirectorySeparator( $directory );
645
+ foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
646
+ $excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
647
+ if( strpos( trailingslashit( $directory ), trailingslashit( $excludedDirectory ) ) === 0 ) {
648
+ return true;
649
+ }
650
+ }
651
+
652
+ return false;
653
+ }
654
+
655
+ }
apps/Backend/Modules/Jobs/Multisite/Files.php CHANGED
@@ -1,365 +1,384 @@
1
- <?php
2
-
3
- namespace WPStaging\Backend\Modules\Jobs\Multisite;
4
-
5
- use WPStaging\Backend\Modules\Jobs\JobExecutable;
6
- // No Direct Access
7
- use WPStaging\Utils\Logger;
8
-
9
- if( !defined( "WPINC" ) ) {
10
- die;
11
- }
12
-
13
- /**
14
- * Class Files
15
- * @package WPStaging\Backend\Modules\Jobs
16
- */
17
- class Files extends JobExecutable {
18
-
19
- /**
20
- * @var \SplFileObject
21
- */
22
- private $file;
23
-
24
- /**
25
- * @var int
26
- */
27
- private $maxFilesPerRun;
28
-
29
- /**
30
- * @var string
31
- */
32
- private $destination;
33
-
34
- /**
35
- * Initialization
36
- */
37
- public function initialize() {
38
-
39
- $this->destination = \WPStaging\WPStaging::getWPpath() . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
40
-
41
- $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
42
-
43
- if( is_file( $filePath ) ) {
44
- $this->file = new \SplFileObject( $filePath, 'r' );
45
- }
46
-
47
- // Informational logs
48
- if( 0 == $this->options->currentStep ) {
49
- $this->log( "Copying files..." );
50
- }
51
-
52
- $this->settings->batchSize = $this->settings->batchSize * 1000000;
53
- $this->maxFilesPerRun = $this->settings->fileLimit;
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
- $this->options->totalSteps = ceil( $this->options->totalFiles / $this->maxFilesPerRun );
62
- }
63
-
64
- /**
65
- * Execute the Current Step
66
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
67
- * @return bool
68
- */
69
- protected function execute() {
70
- // Finished
71
- if( $this->isFinished() ) {
72
- $this->log( "Copying files finished" );
73
- $this->prepareResponse( true, false );
74
- return false;
75
- }
76
-
77
- // Get files and copy'em
78
- if( !$this->getFilesAndCopy() ) {
79
- $this->prepareResponse( false, false );
80
- return false;
81
- }
82
-
83
- // Prepare and return response
84
- $this->prepareResponse();
85
-
86
- // Not finished
87
- return true;
88
- }
89
-
90
- /**
91
- * Get files and copy
92
- * @return bool
93
- */
94
- private function getFilesAndCopy() {
95
- // Over limits threshold
96
- if( $this->isOverThreshold() ) {
97
- // Prepare response and save current progress
98
- $this->prepareResponse( false, false );
99
- $this->saveOptions();
100
- return false;
101
- }
102
-
103
- // Go to last copied line and than to next one
104
- //if ($this->options->copiedFiles != 0) {
105
- if( isset( $this->options->copiedFiles ) && $this->options->copiedFiles != 0 ) {
106
- $this->file->seek( $this->options->copiedFiles - 1 );
107
- }
108
-
109
- $this->file->setFlags( \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );
110
-
111
-
112
- // Loop x files at a time
113
- for ( $i = 0; $i < $this->maxFilesPerRun; $i++ ) {
114
-
115
- // Increment copied files
116
- // Do this anytime to make sure to not stuck in the same step / files
117
- $this->options->copiedFiles++;
118
-
119
- // End of file
120
- if( $this->file->eof() ) {
121
- break;
122
- }
123
-
124
-
125
- $file = $this->file->fgets();
126
-
127
- $this->copyFile( $file );
128
- }
129
-
130
- $totalFiles = $this->options->copiedFiles;
131
- // Log this only every 50 entries to keep the log small and to not block the rendering browser
132
- if( $this->options->copiedFiles % 50 == 0 ) {
133
- $this->log( "Total {$totalFiles} files processed" );
134
- }
135
-
136
- return true;
137
- }
138
-
139
- /**
140
- * Checks Whether There is Any Job to Execute or Not
141
- * @return bool
142
- */
143
- private function isFinished() {
144
- return (
145
- $this->options->currentStep > $this->options->totalSteps ||
146
- $this->options->copiedFiles >= $this->options->totalFiles
147
- );
148
- }
149
-
150
- /**
151
- * @param string $file
152
- * @return bool
153
- */
154
- private function copyFile( $file ) {
155
- $file = trim( \WPStaging\WPStaging::getWPpath() . $file );
156
-
157
- $directory = dirname( $file );
158
-
159
- // Get file size
160
- $fileSize = filesize( $file );
161
-
162
- // Directory is excluded
163
- if( $this->isDirectoryExcluded( $directory ) ) {
164
- $this->debugLog( "Skipping directory by rule: {$file}", Logger::TYPE_INFO );
165
- return false;
166
- }
167
-
168
- // File is excluded
169
- if( $this->isFileExcluded( $file ) ) {
170
- $this->log( "Skipping file by rule: {$file}", Logger::TYPE_INFO );
171
- return false;
172
- }
173
-
174
- // File is over maximum allowed file size (8MB)
175
- if( $fileSize >= $this->settings->maxFileSize * 1000000 ) {
176
- $this->log( "Skipping big file: {$file}", Logger::TYPE_INFO );
177
- return false;
178
- }
179
-
180
- // Invalid file, skipping it as if succeeded
181
- if( !is_file( $file ) || !is_readable( $file ) ) {
182
- $this->log( "Can't read file or file doesn't exist {$file}" );
183
- return true;
184
- }
185
-
186
- // Failed to get destination
187
- if( false === ($destination = $this->getDestination( $file )) ) {
188
- $this->log( "Can't get the destination of {$file}" );
189
- return false;
190
- }
191
-
192
- // File is over batch size
193
- if( $fileSize >= $this->settings->batchSize ) {
194
- $this->log( "Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO );
195
- return $this->copyBig( $file, $destination, $this->settings->batchSize );
196
- }
197
-
198
- // Attempt to copy
199
- if( !@copy( $file, $destination ) ) {
200
- $errors = error_get_last();
201
- $this->log( "Files: Failed to copy file to destination. Error: {$errors['message']} {$file} -> {$destination}", Logger::TYPE_ERROR );
202
- return false;
203
- }
204
-
205
- return true;
206
-
207
- // Good old PHP
208
- //return $this->copy($file, $destination);
209
- }
210
-
211
- /**
212
- * Gets destination file and checks if the directory exists, if it does not attempts to create it.
213
- * If creating destination directory fails, it returns false, gives destination full path otherwise
214
- * @param string $file
215
- * @return bool|string
216
- */
217
- private function getDestination( $file ) {
218
- $file = $this->getMultisiteUploadFolder( $file );
219
- $relativePath = str_replace( \WPStaging\WPStaging::getWPpath(), null, $file );
220
- $destinationPath = $this->destination . $relativePath;
221
- $destinationDirectory = dirname( $destinationPath );
222
-
223
- if( !is_dir( $destinationDirectory ) && !@mkdir( $destinationDirectory, 0775, true ) ) {
224
- $this->log( "Files: Can not create directory {$destinationDirectory}", Logger::TYPE_ERROR );
225
- return false;
226
- }
227
-
228
- return $destinationPath;
229
- }
230
-
231
- /**
232
- * Replace relative path of file if its located in multisite upload folder
233
- * wp-content/uploads/sites/SITEID or old wordpress structure wp-content/blogs.dir/SITEID/files
234
- * @return boolean
235
- */
236
- private function getMultisiteUploadFolder( $file ) {
237
- // Check first which method is used
238
- $uploads = wp_upload_dir();
239
- $basedir = $uploads['basedir'];
240
-
241
- if( false === strpos( $basedir, 'blogs.dir' ) ) {
242
- // Since WP 3.5
243
- $search = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id();
244
- $replace = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
245
- $uploadsFolder = str_replace( $search, $replace, $file );
246
- } else {
247
- // old blog structure
248
- $search = 'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files';
249
- $replace = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
250
- $uploadsFolder = str_replace( $search, $replace, $file );
251
- }
252
-
253
- return $uploadsFolder;
254
- }
255
-
256
- /**
257
- * Copy bigger files than $this->settings->batchSize
258
- * @param string $src
259
- * @param string $dst
260
- * @param int $buffersize
261
- * @return boolean
262
- */
263
- private function copyBig( $src, $dst, $buffersize ) {
264
- $src = fopen( $src, 'r' );
265
- $dest = fopen( $dst, 'w' );
266
-
267
- // Try first method:
268
- while ( !feof( $src ) ) {
269
- if( false === fwrite( $dest, fread( $src, $buffersize ) ) ) {
270
- $error = true;
271
- }
272
- }
273
- // Try second method if first one failed
274
- if( isset( $error ) && ($error === true) ) {
275
- while ( !feof( $src ) ) {
276
- if( false === stream_copy_to_stream( $src, $dest, 1024 ) ) {
277
- $this->log( "Can not copy big file; {$src} -> {$dest}" );
278
- fclose( $src );
279
- fclose( $dest );
280
- return false;
281
- }
282
- }
283
- }
284
- // Close any open handler
285
- fclose( $src );
286
- fclose( $dest );
287
- return true;
288
- }
289
-
290
- /**
291
- * Check if file is excluded from copying process
292
- *
293
- * @param string $file filename including ending
294
- * @return boolean
295
- */
296
- private function isFileExcluded( $file ) {
297
- $excluded = false;
298
- foreach ( $this->options->excludedFiles as $excludedFile ) {
299
- if( stripos( strrev( $file ), strrev( $excludedFile ) ) === 0 ) {
300
- $excluded = true;
301
- break;
302
- }
303
- }
304
-
305
- // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
306
- // because if the updating process fails, the staging site is not accessable any longer
307
- if( isset( $this->options->mainJob ) && $this->options->mainJob == "updating" && stripos( strrev( $file ), strrev( "wp-config.php" ) ) === 0 ) {
308
- $excluded = true;
309
- }
310
-
311
-
312
- return $excluded;
313
- }
314
-
315
- /**
316
- * Replace forward slash with current directory separator
317
- * Windows Compatibility Fix
318
- * @param string $path Path
319
- *
320
- * @return string
321
- */
322
- private function sanitizeDirectorySeparator( $path ) {
323
- $string = str_replace( "/", "\\", $path );
324
- return str_replace( '\\\\', '\\', $string );
325
- }
326
-
327
- /**
328
- * Check if directory is excluded from copying
329
- * @param string $directory
330
- * @return bool
331
- */
332
- private function isDirectoryExcluded( $directory ) {
333
- // Make sure that wp-staging-pro directory / plugin is never excluded
334
- if( false !== strpos( $directory, 'wp-staging' ) || false !== strpos( $directory, 'wp-staging-pro' ) ) {
335
- return false;
336
- }
337
-
338
- $directory = $this->sanitizeDirectorySeparator( $directory );
339
-
340
- foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
341
- $excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
342
- if( strpos( $directory, $excludedDirectory ) === 0 && !$this->isExtraDirectory( $directory ) ) {
343
- return true;
344
- }
345
- }
346
-
347
- return false;
348
- }
349
-
350
- /**
351
- * Check if directory is an extra directory and should be copied
352
- * @param string $directory
353
- * @return boolean
354
- */
355
- private function isExtraDirectory( $directory ) {
356
- foreach ( $this->options->extraDirectories as $extraDirectory ) {
357
- if( strpos( $directory, $extraDirectory ) === 0 ) {
358
- return true;
359
- }
360
- }
361
-
362
- return false;
363
- }
364
-
365
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs\Multisite;
4
+
5
+ use WPStaging\Backend\Modules\Jobs\JobExecutable;
6
+ // No Direct Access
7
+ use WPStaging\Utils\Logger;
8
+
9
+ if( !defined( "WPINC" ) ) {
10
+ die;
11
+ }
12
+
13
+ /**
14
+ * Class Files
15
+ * @package WPStaging\Backend\Modules\Jobs
16
+ */
17
+ class Files extends JobExecutable {
18
+
19
+ /**
20
+ * @var \SplFileObject
21
+ */
22
+ private $file;
23
+
24
+ /**
25
+ * @var int
26
+ */
27
+ private $maxFilesPerRun;
28
+
29
+ /**
30
+ * @var string
31
+ */
32
+ private $destination;
33
+
34
+ /**
35
+ * Initialization
36
+ */
37
+ public function initialize() {
38
+
39
+ $this->destination = \WPStaging\WPStaging::getWPpath() . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
40
+
41
+ $filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
42
+
43
+ if( is_file( $filePath ) ) {
44
+ $this->file = new \SplFileObject( $filePath, 'r' );
45
+ }
46
+
47
+ // Informational logs
48
+ if( 0 == $this->options->currentStep ) {
49
+ $this->log( "Copying files..." );
50
+ }
51
+
52
+ $this->settings->batchSize = $this->settings->batchSize * 1000000;
53
+ $this->maxFilesPerRun = $this->settings->fileLimit;
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
+ $this->options->totalSteps = ceil( $this->options->totalFiles / $this->maxFilesPerRun );
62
+ }
63
+
64
+ /**
65
+ * Execute the Current Step
66
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
67
+ * @return bool
68
+ */
69
+ protected function execute() {
70
+ // Finished
71
+ if( $this->isFinished() ) {
72
+ $this->log( "Copying files finished" );
73
+ $this->prepareResponse( true, false );
74
+ return false;
75
+ }
76
+
77
+ // Get files and copy'em
78
+ if( !$this->getFilesAndCopy() ) {
79
+ $this->prepareResponse( false, false );
80
+ return false;
81
+ }
82
+
83
+ // Prepare and return response
84
+ $this->prepareResponse();
85
+
86
+ // Not finished
87
+ return true;
88
+ }
89
+
90
+ /**
91
+ * Get files and copy
92
+ * @return bool
93
+ */
94
+ private function getFilesAndCopy() {
95
+ // Over limits threshold
96
+ if( $this->isOverThreshold() ) {
97
+ // Prepare response and save current progress
98
+ $this->prepareResponse( false, false );
99
+ $this->saveOptions();
100
+ return false;
101
+ }
102
+
103
+ // Go to last copied line and than to next one
104
+ //if ($this->options->copiedFiles != 0) {
105
+ if( isset( $this->options->copiedFiles ) && $this->options->copiedFiles != 0 ) {
106
+ $this->file->seek( $this->options->copiedFiles - 1 );
107
+ }
108
+
109
+ $this->file->setFlags( \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );
110
+
111
+
112
+ // Loop x files at a time
113
+ for ( $i = 0; $i < $this->maxFilesPerRun; $i++ ) {
114
+
115
+ // Increment copied files
116
+ // Do this anytime to make sure to not stuck in the same step / files
117
+ $this->options->copiedFiles++;
118
+
119
+ // End of file
120
+ if( $this->file->eof() ) {
121
+ break;
122
+ }
123
+
124
+
125
+ $file = $this->file->fgets();
126
+
127
+ $this->copyFile( $file );
128
+ }
129
+
130
+ $totalFiles = $this->options->copiedFiles;
131
+ // Log this only every 50 entries to keep the log small and to not block the rendering browser
132
+ if( $this->options->copiedFiles % 50 == 0 ) {
133
+ $this->log( "Total {$totalFiles} files processed" );
134
+ }
135
+
136
+ return true;
137
+ }
138
+
139
+ /**
140
+ * Checks Whether There is Any Job to Execute or Not
141
+ * @return bool
142
+ */
143
+ private function isFinished() {
144
+ return (
145
+ $this->options->currentStep > $this->options->totalSteps ||
146
+ $this->options->copiedFiles >= $this->options->totalFiles
147
+ );
148
+ }
149
+
150
+ /**
151
+ * @param string $file
152
+ * @return bool
153
+ */
154
+ private function copyFile( $file ) {
155
+ $file = trim( \WPStaging\WPStaging::getWPpath() . $file );
156
+
157
+ $directory = dirname( $file );
158
+
159
+ // Get file size
160
+ $fileSize = filesize( $file );
161
+
162
+ // Directory is excluded
163
+ if( $this->isDirectoryExcluded( $directory ) ) {
164
+ $this->debugLog( "Skipping directory by rule: {$file}", Logger::TYPE_INFO );
165
+ return false;
166
+ }
167
+
168
+ // File is excluded
169
+ if( $this->isFileExcluded( $file ) ) {
170
+ $this->debugLog( "File Excluded: {$file}", Logger::TYPE_INFO );
171
+ return false;
172
+ }
173
+
174
+ // Path + File is excluded
175
+ if( $this->isFileExcludedFullPath( $file ) ) {
176
+ $this->debugLog( "File Excluded Full Path: {$file}", Logger::TYPE_INFO );
177
+ return false;
178
+ }
179
+
180
+ // File is over maximum allowed file size (8MB)
181
+ if( $fileSize >= $this->settings->maxFileSize * 1000000 ) {
182
+ $this->log( "Skipping big file: {$file}", Logger::TYPE_INFO );
183
+ return false;
184
+ }
185
+
186
+ // Invalid file, skipping it as if succeeded
187
+ if( !is_file( $file ) || !is_readable( $file ) ) {
188
+ $this->log( "Can't read file or file doesn't exist {$file}" );
189
+ return true;
190
+ }
191
+
192
+ // Failed to get destination
193
+ if( false === ($destination = $this->getDestination( $file )) ) {
194
+ $this->log( "Can't get the destination of {$file}" );
195
+ return false;
196
+ }
197
+
198
+ // File is over batch size
199
+ if( $fileSize >= $this->settings->batchSize ) {
200
+ $this->log( "Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO );
201
+ return $this->copyBig( $file, $destination, $this->settings->batchSize );
202
+ }
203
+
204
+ // Attempt to copy
205
+ if( !@copy( $file, $destination ) ) {
206
+ $errors = error_get_last();
207
+ $this->log( "Files: Failed to copy file to destination. Error: {$errors['message']} {$file} -> {$destination}", Logger::TYPE_ERROR );
208
+ return false;
209
+ }
210
+
211
+ return true;
212
+
213
+ // Good old PHP
214
+ //return $this->copy($file, $destination);
215
+ }
216
+
217
+ /**
218
+ * Gets destination file and checks if the directory exists, if it does not attempts to create it.
219
+ * If creating destination directory fails, it returns false, gives destination full path otherwise
220
+ * @param string $file
221
+ * @return bool|string
222
+ */
223
+ private function getDestination( $file ) {
224
+ $file = $this->getMultisiteUploadFolder( $file );
225
+ $relativePath = str_replace( \WPStaging\WPStaging::getWPpath(), null, $file );
226
+ $destinationPath = $this->destination . $relativePath;
227
+ $destinationDirectory = dirname( $destinationPath );
228
+
229
+ if( !is_dir( $destinationDirectory ) && !@mkdir( $destinationDirectory, 0755, true ) ) {
230
+ $this->log( "Files: Can not create directory {$destinationDirectory}", Logger::TYPE_ERROR );
231
+ return false;
232
+ }
233
+
234
+ return $destinationPath;
235
+ }
236
+
237
+ /**
238
+ * Replace relative path of file if its located in multisite upload folder
239
+ * wp-content/uploads/sites/SITEID or old wordpress structure wp-content/blogs.dir/SITEID/files
240
+ * @return boolean
241
+ */
242
+ private function getMultisiteUploadFolder( $file ) {
243
+ // Check first which method is used
244
+ $uploads = wp_upload_dir();
245
+ $basedir = $uploads['basedir'];
246
+
247
+ if( false === strpos( $basedir, 'blogs.dir' ) ) {
248
+ // Since WP 3.5
249
+ $search = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id();
250
+ $replace = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
251
+ $uploadsFolder = str_replace( $search, $replace, $file );
252
+ } else {
253
+ // old blog structure
254
+ $search = 'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files';
255
+ $replace = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
256
+ $uploadsFolder = str_replace( $search, $replace, $file );
257
+ }
258
+
259
+ return $uploadsFolder;
260
+ }
261
+
262
+ /**
263
+ * Copy bigger files than $this->settings->batchSize
264
+ * @param string $src
265
+ * @param string $dst
266
+ * @param int $buffersize
267
+ * @return boolean
268
+ */
269
+ private function copyBig( $src, $dst, $buffersize ) {
270
+ $src = fopen( $src, 'r' );
271
+ $dest = fopen( $dst, 'w' );
272
+
273
+ // Try first method:
274
+ while ( !feof( $src ) ) {
275
+ if( false === fwrite( $dest, fread( $src, $buffersize ) ) ) {
276
+ $error = true;
277
+ }
278
+ }
279
+ // Try second method if first one failed
280
+ if( isset( $error ) && ($error === true) ) {
281
+ while ( !feof( $src ) ) {
282
+ if( false === stream_copy_to_stream( $src, $dest, 1024 ) ) {
283
+ $this->log( "Can not copy big file; {$src} -> {$dest}" );
284
+ fclose( $src );
285
+ fclose( $dest );
286
+ return false;
287
+ }
288
+ }
289
+ }
290
+ // Close any open handler
291
+ fclose( $src );
292
+ fclose( $dest );
293
+ return true;
294
+ }
295
+
296
+ /**
297
+ * Check if file is excluded from copying process
298
+ *
299
+ * @param string $file filename including ending
300
+ * @return boolean
301
+ */
302
+ private function isFileExcluded( $file ) {
303
+
304
+ if( in_array( basename( $file ), $this->options->excludedFiles ) ) {
305
+ return true;
306
+ }
307
+ // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
308
+ // because if the updating process fails, the staging site is not accessable any longer
309
+ if( isset( $this->options->mainJob ) && $this->options->mainJob == "updating" && stripos( strrev( $file ), strrev( "wp-config.php" ) ) === 0 ) {
310
+ return true;
311
+ }
312
+
313
+
314
+ return false;
315
+ }
316
+
317
+ /**
318
+ * Check if certain file is excluded from copying process
319
+ *
320
+ * @param string $file filename including ending + (part) path e.g wp-content/db.php
321
+ * @return boolean
322
+ */
323
+ private function isFileExcludedFullPath( $file ) {
324
+ // If path + file exists
325
+ foreach ( $this->options->excludedFilesFullPath as $excludedFile ) {
326
+ if( false !== strpos( $file, $excludedFile ) ) {
327
+ return true;
328
+ }
329
+ }
330
+
331
+ return false;
332
+ }
333
+
334
+ /**
335
+ * Replace forward slash with current directory separator
336
+ * Windows Compatibility Fix
337
+ * @param string $path Path
338
+ *
339
+ * @return string
340
+ */
341
+ private function sanitizeDirectorySeparator( $path ) {
342
+ $string = str_replace( "/", "\\", $path );
343
+ return str_replace( '\\\\', '\\', $string );
344
+ }
345
+
346
+ /**
347
+ * Check if directory is excluded from copying
348
+ * @param string $directory
349
+ * @return bool
350
+ */
351
+ private function isDirectoryExcluded( $directory ) {
352
+ // Make sure that wp-staging-pro directory / plugin is never excluded
353
+ if( false !== strpos( $directory, 'wp-staging' ) || false !== strpos( $directory, 'wp-staging-pro' ) ) {
354
+ return false;
355
+ }
356
+
357
+ $directory = $this->sanitizeDirectorySeparator( $directory );
358
+
359
+ foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
360
+ $excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
361
+ if( strpos( $directory, $excludedDirectory ) === 0 && !$this->isExtraDirectory( $directory ) ) {
362
+ return true;
363
+ }
364
+ }
365
+
366
+ return false;
367
+ }
368
+
369
+ /**
370
+ * Check if directory is an extra directory and should be copied
371
+ * @param string $directory
372
+ * @return boolean
373
+ */
374
+ private function isExtraDirectory( $directory ) {
375
+ foreach ( $this->options->extraDirectories as $extraDirectory ) {
376
+ if( strpos( $directory, $extraDirectory ) === 0 ) {
377
+ return true;
378
+ }
379
+ }
380
+
381
+ return false;
382
+ }
383
+
384
+ }
apps/Backend/Modules/Jobs/Multisite/Finish.php CHANGED
@@ -1,116 +1,116 @@
1
- <?php
2
- namespace WPStaging\Backend\Modules\Jobs\Multisite;
3
-
4
- use WPStaging\WPStaging;
5
- use WPStaging\Backend\Modules\Jobs\Job;
6
- use WPStaging\Utils\Multisite;
7
-
8
- //error_reporting( E_ALL );
9
-
10
- /**
11
- * Class Finish
12
- * @package WPStaging\Backend\Modules\Jobs
13
- */
14
- class Finish extends Job
15
- {
16
- /**
17
- * Clone Key
18
- * @var string
19
- */
20
- private $clone = '';
21
-
22
-
23
-
24
- /**
25
- * Start Module
26
- * @return object
27
- */
28
- public function start()
29
- {
30
- // sanitize the clone name before saving
31
- $this->clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
32
-
33
- // Delete Cache Files
34
- $this->deleteCacheFiles();
35
-
36
- // Prepare clone records & save scanned directories for delete job later
37
- $this->prepareCloneDataRecords();
38
-
39
- $multisite = new Multisite;
40
-
41
- $return = array(
42
- "directoryName" => $this->options->cloneDirectoryName,
43
- "path" => ABSPATH . $this->options->cloneDirectoryName,
44
- "url" => $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName,
45
- //"url" => $this->multisiteUrl . '/' . $this->options->cloneDirectoryName,
46
- "number" => $this->options->cloneNumber,
47
- "version" => \WPStaging\WPStaging::VERSION,
48
- "status" => 'finished',
49
- "prefix" => $this->options->prefix,
50
- "last_msg" => $this->logger->getLastLogMsg(),
51
- "job" => $this->options->currentJob,
52
- "percentage" => 100
53
-
54
- );
55
-
56
- //$this->flush();
57
-
58
- return (object) $return;
59
- }
60
-
61
- /**
62
- * Delete Cache Files
63
- */
64
- protected function deleteCacheFiles()
65
- {
66
- $this->log("Finish: Deleting clone job's cache files...");
67
-
68
- // Clean cache files
69
- $this->cache->delete("clone_options");
70
- $this->cache->delete("files_to_copy");
71
-
72
- $this->log("Finish: Clone job's cache files have been deleted!");
73
- }
74
-
75
- /**
76
- * Prepare clone records
77
- * @return bool
78
- */
79
- protected function prepareCloneDataRecords()
80
- {
81
- // Check if clones still exist
82
- $this->log("Finish: Verifying existing clones...");
83
-
84
- // Clone data already exists
85
- if (isset($this->options->existingClones[$this->options->clone]))
86
- {
87
- $this->log("Finish: Clone data already exists, no need to update, the job finished");
88
- return true;
89
- }
90
-
91
- // Save new clone data
92
- $this->log("Finish: {$this->options->clone}'s clone job's data is not in database, generating data");
93
-
94
- // sanitize the clone name before saving
95
- //$clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
96
-
97
- $this->options->existingClones[$this->clone] = array(
98
- "directoryName" => $this->options->cloneDirectoryName,
99
- "path" => ABSPATH . $this->options->cloneDirectoryName,
100
- //"url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
101
- "url" => $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName,
102
- "number" => $this->options->cloneNumber,
103
- "version" => \WPStaging\WPStaging::VERSION,
104
- "status" => false,
105
- "prefix" => $this->options->prefix,
106
- );
107
-
108
- if (false === update_option("wpstg_existing_clones_beta", $this->options->existingClones))
109
- {
110
- $this->log("Finish: Failed to save {$this->options->clone}'s clone job data to database'");
111
- return false;
112
- }
113
-
114
- return true;
115
- }
116
  }
1
+ <?php
2
+ namespace WPStaging\Backend\Modules\Jobs\Multisite;
3
+
4
+ use WPStaging\WPStaging;
5
+ use WPStaging\Backend\Modules\Jobs\Job;
6
+ use WPStaging\Utils\Multisite;
7
+
8
+ //error_reporting( E_ALL );
9
+
10
+ /**
11
+ * Class Finish
12
+ * @package WPStaging\Backend\Modules\Jobs
13
+ */
14
+ class Finish extends Job
15
+ {
16
+ /**
17
+ * Clone Key
18
+ * @var string
19
+ */
20
+ private $clone = '';
21
+
22
+
23
+
24
+ /**
25
+ * Start Module
26
+ * @return object
27
+ */
28
+ public function start()
29
+ {
30
+ // sanitize the clone name before saving
31
+ $this->clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
32
+
33
+ // Delete Cache Files
34
+ $this->deleteCacheFiles();
35
+
36
+ // Prepare clone records & save scanned directories for delete job later
37
+ $this->prepareCloneDataRecords();
38
+
39
+ $multisite = new Multisite;
40
+
41
+ $return = array(
42
+ "directoryName" => $this->options->cloneDirectoryName,
43
+ "path" => ABSPATH . $this->options->cloneDirectoryName,
44
+ "url" => $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName,
45
+ //"url" => $this->multisiteUrl . '/' . $this->options->cloneDirectoryName,
46
+ "number" => $this->options->cloneNumber,
47
+ "version" => \WPStaging\WPStaging::VERSION,
48
+ "status" => 'finished',
49
+ "prefix" => $this->options->prefix,
50
+ "last_msg" => $this->logger->getLastLogMsg(),
51
+ "job" => $this->options->currentJob,
52
+ "percentage" => 100
53
+
54
+ );
55
+
56
+ //$this->flush();
57
+
58
+ return (object) $return;
59
+ }
60
+
61
+ /**
62
+ * Delete Cache Files
63
+ */
64
+ protected function deleteCacheFiles()
65
+ {
66
+ $this->log("Finish: Deleting clone job's cache files...");
67
+
68
+ // Clean cache files
69
+ $this->cache->delete("clone_options");
70
+ $this->cache->delete("files_to_copy");
71
+
72
+ $this->log("Finish: Clone job's cache files have been deleted!");
73
+ }
74
+
75
+ /**
76
+ * Prepare clone records
77
+ * @return bool
78
+ */
79
+ protected function prepareCloneDataRecords()
80
+ {
81
+ // Check if clones still exist
82
+ $this->log("Finish: Verifying existing clones...");
83
+
84
+ // Clone data already exists
85
+ if (isset($this->options->existingClones[$this->options->clone]))
86
+ {
87
+ $this->log("Finish: Clone data already exists, no need to update, the job finished");
88
+ return true;
89
+ }
90
+
91
+ // Save new clone data
92
+ $this->log("Finish: {$this->options->clone}'s clone job's data is not in database, generating data");
93
+
94
+ // sanitize the clone name before saving
95
+ //$clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
96
+
97
+ $this->options->existingClones[$this->clone] = array(
98
+ "directoryName" => $this->options->cloneDirectoryName,
99
+ "path" => ABSPATH . $this->options->cloneDirectoryName,
100
+ //"url" => get_site_url() . '/' . $this->options->cloneDirectoryName,
101
+ "url" => $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName,
102
+ "number" => $this->options->cloneNumber,
103
+ "version" => \WPStaging\WPStaging::VERSION,
104
+ "status" => false,
105
+ "prefix" => $this->options->prefix,
106
+ );
107
+
108
+ if (false === update_option("wpstg_existing_clones_beta", $this->options->existingClones))
109
+ {
110
+ $this->log("Finish: Failed to save {$this->options->clone}'s clone job data to database'");
111
+ return false;
112
+ }
113
+
114
+ return true;
115
+ }
116
  }
apps/Backend/Modules/Jobs/Multisite/SearchReplace.php CHANGED
@@ -1,772 +1,776 @@
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\WPStaging;
11
- use WPStaging\Utils\Strings;
12
- use WPStaging\Utils\Helper;
13
- use WPStaging\Utils\Multisite;
14
- use WPStaging\Backend\Modules\Jobs\JobExecutable;
15
-
16
- /**
17
- * Class Database
18
- * @package WPStaging\Backend\Modules\Jobs
19
- */
20
- class SearchReplace extends JobExecutable {
21
-
22
- /**
23
- * @var int
24
- */
25
- private $total = 0;
26
-
27
- /**
28
- * @var \WPDB
29
- */
30
- public $db;
31
-
32
- /**
33
- * The prefix of the new database tables which are used for the live site after updating tables
34
- * @var string
35
- */
36
- public $tmpPrefix;
37
-
38
- /**
39
- * Initialize
40
- */
41
- public function initialize() {
42
- $this->total = count( $this->options->tables );
43
- $this->db = WPStaging::getInstance()->get( "wpdb" );
44
- $this->tmpPrefix = $this->options->prefix;
45
- }
46
-
47
- public function start() {
48
- // Skip job. Nothing to do
49
- if( $this->options->totalSteps === 0 ) {
50
- $this->prepareResponse( true, false );
51
- }
52
-
53
- $this->run();
54
-
55
- // Save option, progress
56
- $this->saveOptions();
57
-
58
- return ( object ) $this->response;
59
- }
60
-
61
- /**
62
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
63
- * @return void
64
- */
65
- protected function calculateTotalSteps() {
66
- $this->options->totalSteps = $this->total;
67
- }
68
-
69
- /**
70
- * Execute the Current Step
71
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
72
- * @return bool
73
- */
74
- protected function execute() {
75
- // Over limits threshold
76
- if( $this->isOverThreshold() ) {
77
- // Prepare response and save current progress
78
- $this->prepareResponse( false, false );
79
- $this->saveOptions();
80
- return false;
81
- }
82
-
83
- // No more steps, finished
84
- if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
85
- $this->prepareResponse( true, false );
86
- return false;
87
- }
88
-
89
- // Table is excluded
90
- if( in_array( $this->options->tables[$this->options->currentStep], $this->options->excludedTables ) ) {
91
- $this->prepareResponse();
92
- return true;
93
- }
94
-
95
- // Search & Replace
96
- if( !$this->stopExecution() && !$this->updateTable( $this->options->tables[$this->options->currentStep] ) ) {
97
- // Prepare Response
98
- $this->prepareResponse( false, false );
99
-
100
- // Not finished
101
- return true;
102
- }
103
-
104
-
105
- // Prepare Response
106
- $this->prepareResponse();
107
-
108
- // Not finished
109
- return true;
110
- }
111
-
112
- /**
113
- * Stop Execution immediately
114
- * return mixed bool | json
115
- */
116
- private function stopExecution() {
117
- if( $this->db->prefix == $this->tmpPrefix ) {
118
- $this->returnException( 'Fatal Error 9: Prefix ' . $this->db->prefix . ' is used for the live site hence it can not be used for the staging site as well. Please ask support@wp-staging.com how to resolve this.' );
119
- }
120
- return false;
121
- }
122
-
123
- /**
124
- * Copy Tables
125
- * @param string $tableName
126
- * @return bool
127
- */
128
- private function updateTable( $tableName ) {
129
- $strings = new Strings();
130
- $table = $strings->str_replace_first( $this->db->prefix, '', $tableName );
131
- $newTableName = $this->tmpPrefix . $table;
132
-
133
- // Save current job
134
- $this->setJob( $newTableName );
135
-
136
- // Beginning of the job
137
- if( !$this->startJob( $newTableName, $tableName ) ) {
138
- return true;
139
- }
140
- // Copy data
141
- $this->startReplace( $newTableName );
142
-
143
- // Finis the step
144
- return $this->finishStep();
145
- }
146
-
147
- /**
148
- * Start search replace job
149
- * @param string $new
150
- * @param string $old
151
- */
152
- private function startReplace( $new ) {
153
- $rows = $this->options->job->start + $this->settings->querySRLimit;
154
- $this->log(
155
- "DB Processing: Table {$new} {$this->options->job->start} to {$rows} records"
156
- );
157
-
158
- // Search & Replace
159
- $this->searchReplace( $new, $rows, array() );
160
-
161
- // Set new offset
162
- $this->options->job->start += $this->settings->querySRLimit;
163
- }
164
-
165
- /**
166
- * Returns the number of pages in a table.
167
- * @access public
168
- * @return int
169
- */
170
- private function get_pages_in_table( $table ) {
171
- $table = esc_sql( $table );
172
- $rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
173
- $pages = ceil( $rows / $this->settings->querySRLimit );
174
- return absint( $pages );
175
- }
176
-
177
- /**
178
- * Gets the columns in a table.
179
- * @access public
180
- * @param string $table The table to check.
181
- * @return array
182
- */
183
- private function get_columns( $table ) {
184
- $primary_key = null;
185
- $columns = array();
186
- $fields = $this->db->get_results( 'DESCRIBE ' . $table );
187
- if( is_array( $fields ) ) {
188
- foreach ( $fields as $column ) {
189
- $columns[] = $column->Field;
190
- if( $column->Key == 'PRI' ) {
191
- $primary_key = $column->Field;
192
- }
193
- }
194
- }
195
- return array($primary_key, $columns);
196
- }
197
-
198
- /**
199
- * Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
200
- *
201
- * Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
202
- * and to be compatible with batch processing.
203
- *
204
- * @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
205
- *
206
- * @access public
207
- * @param string $table The table to run the replacement on.
208
- * @param int $page The page/block to begin the query on.
209
- * @param array $args An associative array containing arguements for this run.
210
- * @return array
211
- */
212
- private function searchReplace( $table, $page, $args ) {
213
-
214
-
215
- // Load up the default settings for this chunk.
216
- $table = esc_sql( $table );
217
- $current_page = $this->options->job->start + $this->settings->querySRLimit;
218
- $pages = $this->get_pages_in_table( $table );
219
- //$done = false;
220
-
221
-
222
- if( $this->isSubDir() ) {
223
- // Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
224
- $args['search_for'] = array(
225
- rtrim( $this->multisiteHomeUrlWithoutScheme, "/" ) . $this->getSubDir(),
226
- ABSPATH,
227
- str_replace( '/', '\/', rtrim( $this->multisiteHomeUrlWithoutScheme, '/' ) ) . str_replace( '/', '\/', $this->getSubDir() ), // // Used by revslider and several visual editors
228
- $this->getImagePathLive()
229
- );
230
-
231
-
232
- $args['replace_with'] = array(
233
- rtrim( $this->multisiteDomainWithoutScheme, "/" ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName,
234
- rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName,
235
- str_replace( '/', '\/', rtrim( $this->multisiteDomainWithoutScheme, "/" ) ) . str_replace( '/', '\/', $this->getSubDir() ) . '\/' . $this->options->cloneDirectoryName, // Used by revslider and several visual editors
236
- $this->getImagePathStaging()
237
- );
238
- } else {
239
- $args['search_for'] = array(
240
- rtrim( $this->multisiteHomeUrlWithoutScheme, '/' ),
241
- ABSPATH,
242
- str_replace( '/', '\/', rtrim( $this->multisiteHomeUrlWithoutScheme, '/' ) ),
243
- $this->getImagePathLive()
244
- );
245
- $args['replace_with'] = array(
246
- rtrim( $this->multisiteDomainWithoutScheme, '/' ) . '/' . $this->options->cloneDirectoryName,
247
- rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName,
248
- str_replace( '/', '\/', rtrim( $this->multisiteDomainWithoutScheme, '/' ) ) . '\/' . $this->options->cloneDirectoryName,
249
- $this->getImagePathStaging()
250
- );
251
- }
252
-
253
- //$this->log( 'Search: ' . $this->multisiteHomeUrlWithoutScheme . ' Replace: ' . rtrim( $this->multisiteDomainWithoutScheme, '/' ) . '/' . $this->options->cloneDirectoryName );
254
-
255
-
256
- $args['replace_guids'] = 'off';
257
- $args['dry_run'] = 'off';
258
- $args['case_insensitive'] = false;
259
- $args['replace_guids'] = 'off';
260
- $args['replace_mails'] = 'off';
261
-
262
- // Allow filtering of search & replace parameters
263
- $args = apply_filters( 'wpstg_clone_searchreplace_params', $args );
264
-
265
- // Get a list of columns in this table.
266
- list( $primary_key, $columns ) = $this->get_columns( $table );
267
-
268
- // Bail out early if there isn't a primary key.
269
- // We commented this to search & replace through tables which have no primary keys like wp_revslider_slides
270
- // @todo test this carefully. If it causes (performance) issues we need to activate it again!
271
- // @since 2.4.4
272
- // if( null === $primary_key ) {
273
- // return false;
274
- // }
275
-
276
- $current_row = 0;
277
- $start = $this->options->job->start;
278
- $end = $this->settings->querySRLimit;
279
-
280
- // Grab the content of the table.
281
- $data = $this->db->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
282
-
283
- // Filter certain rows option_name in wpstg_options
284
- $filter = array(
285
- 'Admin_custome_login_Slidshow',
286
- 'Admin_custome_login_Social',
287
- 'Admin_custome_login_logo',
288
- 'Admin_custome_login_text',
289
- 'Admin_custome_login_login',
290
- 'Admin_custome_login_top',
291
- 'Admin_custome_login_dashboard',
292
- 'Admin_custome_login_Version',
293
- 'upload_path',
294
- );
295
-
296
- apply_filters( 'wpstg_clone_searchreplace_excl_rows', $filter );
297
-
298
- // Loop through the data.
299
- foreach ( $data as $row ) {
300
- $current_row++;
301
- $update_sql = array();
302
- $where_sql = array();
303
- $upd = false;
304
-
305
- // Skip rows below
306
- if( isset( $row['option_name'] ) && in_array( $row['option_name'], $filter ) ) {
307
- continue;
308
- }
309
-
310
- // Skip rows with transients (They can store huge data and we need to save memory)
311
- if( isset( $row['option_name'] ) && strpos( $row['option_name'], '_transient' ) === 0 ) {
312
- continue;
313
- }
314
-
315
-
316
- foreach ( $columns as $column ) {
317
-
318
- $dataRow = $row[$column];
319
-
320
- if( $column == $primary_key ) {
321
- $where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
322
- continue;
323
- }
324
-
325
- // Skip GUIDs by default.
326
- if( 'on' !== $args['replace_guids'] && 'guid' == $column ) {
327
- continue;
328
- }
329
-
330
-
331
- // Skip mail addresses
332
- if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->multisiteDomainWithoutScheme ) ) {
333
- continue;
334
- }
335
-
336
- // Check options table
337
- if( $this->options->prefix . 'options' === $table ) {
338
-
339
- // Skip certain options
340
- if( isset( $should_skip ) && true === $should_skip ) {
341
- $should_skip = false;
342
- continue;
343
- }
344
-
345
- // Skip this row
346
- if( 'wpstg_existing_clones_beta' === $dataRow ||
347
- 'wpstg_existing_clones' === $dataRow ||
348
- 'wpstg_settings' === $dataRow ||
349
- 'wpstg_license_status' === $dataRow ||
350
- 'siteurl' === $dataRow ||
351
- 'home' === $dataRow
352
- ) {
353
- $should_skip = true;
354
- }
355
- }
356
-
357
- // Check the path delimiter for / or \/ and remove one of those which prevents from resulting in wrong syntax like domain.com/staging\/.
358
- // 1. local.wordpress.test -> local.wordpress.test/staging
359
- // 2. local.wordpress.test\/ -> local.wordpress.test\/staging\/
360
- $tmp = $args;
361
- if( false === strpos( $dataRow, $tmp['search_for'][0] ) ) {
362
- array_shift( $tmp['search_for'] ); // rtrim( $this->homeUrl, '/' ),
363
- array_shift( $tmp['replace_with'] ); // rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
364
- } else {
365
- unset( $tmp['search_for'][1] );
366
- unset( $tmp['replace_with'][1] );
367
- // recount array
368
- $tmp['search_for'] = array_values( $tmp['search_for'] );
369
- $tmp['replace_with'] = array_values( $tmp['replace_with'] );
370
- }
371
-
372
- // Run a search replace on the data row and respect the serialisation.
373
- $i = 0;
374
- foreach ( $tmp['search_for'] as $replace ) {
375
- //$this->log( "Search for: {$tmp['search_for'][$i]} Replace with {$tmp['replace_with'][$i]}", \WPStaging\Utils\Logger::TYPE_ERROR );
376
- $dataRow = $this->recursive_unserialize_replace( $tmp['search_for'][$i], $tmp['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
377
- $i++;
378
- }
379
- unset( $replace );
380
- unset( $i );
381
- unset( $tmp );
382
-
383
- // Something was changed
384
- if( $row[$column] != $dataRow ) {
385
- $update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
386
- $upd = true;
387
- }
388
- }
389
-
390
- // Determine what to do with updates.
391
- if( $args['dry_run'] === 'on' ) {
392
- // Don't do anything if a dry run
393
- } elseif( $upd && !empty( $where_sql ) ) {
394
- // If there are changes to make, run the query.
395
- $sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
396
- $result = $this->db->query( $sql );
397
-
398
- if( !$result ) {
399
- $this->log( "Error updating row {$current_row} SQL: {$sql}", \WPStaging\Utils\Logger::TYPE_ERROR );
400
- }
401
- }
402
- } // end row loop
403
- unset( $row );
404
- unset( $update_sql );
405
- unset( $where_sql );
406
- unset( $sql );
407
-
408
-
409
- // DB Flush
410
- $this->db->flush();
411
- return true;
412
- }
413
-
414
- /**
415
- * Get path to multisite image folder e.g. wp-content/blogs.dir/ID/files or wp-content/uploads/sites/ID
416
- * @return string
417
- */
418
- private function getImagePathLive() {
419
- // Check first which structure is used
420
- $uploads = wp_upload_dir();
421
- $basedir = $uploads['basedir'];
422
- $blogId = get_current_blog_id();
423
-
424
- if( false === strpos( $basedir, 'blogs.dir' ) ) {
425
- // Since WP 3.5
426
- $path = $blogId > 1 ?
427
- 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR :
428
- 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
429
- } else {
430
- // old blog structure
431
- $path = $blogId > 1 ?
432
- 'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR :
433
- 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
434
- }
435
- return $path;
436
- }
437
-
438
- /**
439
- * Get path to staging site image path wp-content/uploads
440
- * @return string
441
- */
442
- private function getImagePathStaging() {
443
- return 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
444
- }
445
-
446
- /**
447
- * Adapted from interconnect/it's search/replace script.
448
- *
449
- * @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
450
- *
451
- * Take a serialised array and unserialise it replacing elements as needed and
452
- * unserialising any subordinate arrays and performing the replace on those too.
453
- *
454
- * @access private
455
- * @param string $from String we're looking to replace.
456
- * @param string $to What we want it to be replaced with
457
- * @param array $data Used to pass any subordinate arrays back to in.
458
- * @param boolean $serialized Does the array passed via $data need serialising.
459
- * @param sting|boolean $case_insensitive Set to 'on' if we should ignore case, false otherwise.
460
- *
461
- * @return string|array The original array with all elements replaced as needed.
462
- */
463
- private function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialized = false, $case_insensitive = false ) {
464
- try {
465
- // Some unserialized data cannot be re-serialized eg. SimpleXMLElements
466
- if( is_serialized( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
467
- $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
468
- } elseif( is_array( $data ) ) {
469
- $tmp = array();
470
- foreach ( $data as $key => $value ) {
471
- $tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
472
- }
473
-
474
- $data = $tmp;
475
- unset( $tmp );
476
- } elseif( is_object( $data ) ) {
477
- $tmp = $data;
478
- $props = get_object_vars( $data );
479
- foreach ( $props as $key => $value ) {
480
- if( $key === '' || ord( $key[0] ) === 0 ) {
481
- continue;
482
- }
483
- $tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
484
- }
485
-
486
- $data = $tmp;
487
- unset( $tmp );
488
- } else {
489
- if( is_string( $data ) ) {
490
- if( !empty( $from ) && !empty( $to ) ) {
491
- $data = $this->str_replace( $from, $to, $data, $case_insensitive );
492
- }
493
- }
494
- }
495
-
496
- if( $serialized ) {
497
- return serialize( $data );
498
- }
499
- } catch ( Exception $error ) {
500
-
501
- }
502
-
503
- return $data;
504
- }
505
-
506
- // private function recursive_unserialize_replace_( $from = '', $to = '', $data = '', $serialised = false, $case_insensitive = false ) {
507
- // try {
508
- //
509
- // // Check first if its an object and repair it if necessary
510
- // //$data = $this->fixObject($data);
511
- //
512
- // if( is_string( $data ) && !is_serialized_string( $data ) && ( $unserialized = $this->unserialize( $data ) ) !== false ) {
513
- // $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
514
- // } elseif( is_array( $data ) ) {
515
- // $_tmp = array();
516
- // foreach ( $data as $key => $value ) {
517
- // $_tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
518
- // }
519
- //
520
- // $data = $_tmp;
521
- // unset( $_tmp );
522
- // }
523
- //
524
- // // Submitted by Tina Matter
525
- // elseif( $this->isValidObject( $data ) ) {
526
- // $_tmp = $data; // new $data_class( );
527
- // $props = get_object_vars( $data );
528
- // foreach ( $props as $key => $value ) {
529
- // if( $key === '' || ord( $key[0] ) === 0 ) {
530
- // continue;
531
- // }
532
- // $_tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
533
- // }
534
- //
535
- // $data = $_tmp;
536
- // unset( $_tmp );
537
- // } elseif( is_serialized_string( $data ) ) {
538
- // if( false !== ($data = $this->unserialize( $data )) ) {
539
- // $data = $this->str_replace( $from, $to, $data, $case_insensitive );
540
- // $data = serialize( $data );
541
- // }
542
- // } else {
543
- // if( is_string( $data ) ) {
544
- // $data = $this->str_replace( $from, $to, $data, $case_insensitive );
545
- // }
546
- // }
547
- //
548
- // if( $serialised ) {
549
- // return serialize( $data );
550
- // }
551
- // } catch ( Exception $error ) {
552
- //
553
- // }
554
- //
555
- // return $data;
556
- // }
557
-
558
-
559
-
560
- /**
561
- * Check if the object is a valid one and not __PHP_Incomplete_Class_Name
562
- * Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
563
- * @return boolean
564
- */
565
- // private function isValidObject( $data ) {
566
- // if( !is_object( $data ) || gettype( $data ) != 'object' ) {
567
- // return false;
568
- // }
569
- //
570
- // $invalid_class_props = get_object_vars( $data );
571
- //
572
- // if( !isset( $invalid_class_props['__PHP_Incomplete_Class_Name'] ) ) {
573
- // // Assume it must be an valid object
574
- // return true;
575
- // }
576
- //
577
- // $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
578
- //
579
- // if( !empty( $invalid_object_class ) ) {
580
- // return false;
581
- // }
582
- //
583
- // // Assume it must be an valid object
584
- // return true;
585
- // }
586
-
587
- /**
588
- * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
589
- * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
590
- * @access public
591
- * @param string $input The string to escape.
592
- * @return string
593
- */
594
- private function mysql_escape_mimic( $input ) {
595
- if( is_array( $input ) ) {
596
- return array_map( __METHOD__, $input );
597
- }
598
- if( !empty( $input ) && is_string( $input ) ) {
599
- return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
600
- }
601
-
602
- return $input;
603
- }
604
-
605
- /**
606
- * Return unserialized object or array
607
- *
608
- * @param string $serialized_string Serialized string.
609
- * @param string $method The name of the caller method.
610
- *
611
- * @return mixed, false on failure
612
- */
613
- private static function unserialize( $serialized_string ) {
614
- if( !is_serialized( $serialized_string ) ) {
615
- return false;
616
- }
617
-
618
- $serialized_string = trim( $serialized_string );
619
- $unserialized_string = @unserialize( $serialized_string );
620
-
621
- return $unserialized_string;
622
- }
623
-
624
- /**
625
- * Wrapper for str_replace
626
- *
627
- * @param string $from
628
- * @param string $to
629
- * @param string $data
630
- * @param string|bool $case_insensitive
631
- *
632
- * @return string
633
- */
634
- private function str_replace( $from, $to, $data, $case_insensitive = false ) {
635
-
636
- // Add filter
637
- $excludes = apply_filters( 'wpstg_clone_searchreplace_excl', array() );
638
-
639
- // Build pattern
640
- $regexExclude = '';
641
- foreach ( $excludes as $exclude ) {
642
- $regexExclude .= $exclude . '(*SKIP)(FAIL)|';
643
- }
644
-
645
- if( 'on' === $case_insensitive ) {
646
- //$data = str_ireplace( $from, $to, $data );
647
- $data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#i', $to, $data );
648
- } else {
649
- //$data = str_replace( $from, $to, $data );
650
- $data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#', $to, $data );
651
- }
652
-
653
- return $data;
654
- }
655
-
656
- /**
657
- * Set the job
658
- * @param string $table
659
- */
660
- private function setJob( $table ) {
661
- if( !empty( $this->options->job->current ) ) {
662
- return;
663
- }
664
-
665
- $this->options->job->current = $table;
666
- $this->options->job->start = 0;
667
- }
668
-
669
- /**
670
- * Start Job
671
- * @param string $new
672
- * @param string $old
673
- * @return bool
674
- */
675
- private function startJob( $new, $old ) {
676
- if( 0 != $this->options->job->start ) {
677
- return true;
678
- }
679
-
680
- $this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
681
-
682
- if( 0 == $this->options->job->total ) {
683
- $this->finishStep();
684
- return false;
685
- }
686
-
687
- return true;
688
- }
689
-
690
- /**
691
- * Finish the step
692
- */
693
- private function finishStep() {
694
- // This job is not finished yet
695
- if( $this->options->job->total > $this->options->job->start ) {
696
- return false;
697
- }
698
-
699
- // Add it to cloned tables listing
700
- $this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
701
-
702
- // Reset job
703
- $this->options->job = new \stdClass();
704
-
705
- return true;
706
- }
707
-
708
- /**
709
- * Drop table if necessary
710
- * @param string $new
711
- */
712
- private function dropTable( $new ) {
713
- $old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
714
-
715
- if( !$this->shouldDropTable( $new, $old ) ) {
716
- return;
717
- }
718
-
719
- $this->log( "DB Processing: {$new} already exists, dropping it first" );
720
- $this->db->query( "DROP TABLE {$new}" );
721
- }
722
-
723
- /**
724
- * Check if table needs to be dropped
725
- * @param string $new
726
- * @param string $old
727
- * @return bool
728
- */
729
- private function shouldDropTable( $new, $old ) {
730
- return (
731
- $old == $new &&
732
- (
733
- !isset( $this->options->job->current ) ||
734
- !isset( $this->options->job->start ) ||
735
- 0 == $this->options->job->start
736
- )
737
- );
738
- }
739
-
740
- /**
741
- * Check if WP is installed in subdir
742
- * @return boolean
743
- */
744
- private function isSubDir() {
745
- // Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
746
- // This is happening much more often than you would expect
747
- $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
748
- $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
749
-
750
- if( $home !== $siteurl ) {
751
- return true;
752
- }
753
- return false;
754
- }
755
-
756
- /**
757
- * Get the install sub directory if WP is installed in sub directory
758
- * @return string
759
- */
760
- private function getSubDir() {
761
- $home = get_option( 'home' );
762
- $siteurl = get_option( 'siteurl' );
763
-
764
- if( empty( $home ) || empty( $siteurl ) ) {
765
- return '/';
766
- }
767
-
768
- $dir = str_replace( $home, '', $siteurl );
769
- return '/' . str_replace( '/', '', $dir );
770
- }
771
-
772
- }
 
 
 
 
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\WPStaging;
11
+ use WPStaging\Utils\Strings;
12
+ use WPStaging\Utils\Helper;
13
+ use WPStaging\Utils\Multisite;
14
+ use WPStaging\Backend\Modules\Jobs\JobExecutable;
15
+
16
+ /**
17
+ * Class Database
18
+ * @package WPStaging\Backend\Modules\Jobs
19
+ */
20
+ class SearchReplace extends JobExecutable {
21
+
22
+ /**
23
+ * @var int
24
+ */
25
+ private $total = 0;
26
+
27
+ /**
28
+ * @var \WPDB
29
+ */
30
+ public $db;
31
+
32
+ /**
33
+ * The prefix of the new database tables which are used for the live site after updating tables
34
+ * @var string
35
+ */
36
+ public $tmpPrefix;
37
+
38
+ /**
39
+ * Initialize
40
+ */
41
+ public function initialize() {
42
+ $this->total = count( $this->options->tables );
43
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
44
+ $this->tmpPrefix = $this->options->prefix;
45
+ }
46
+
47
+ public function start() {
48
+ // Skip job. Nothing to do
49
+ if( $this->options->totalSteps === 0 ) {
50
+ $this->prepareResponse( true, false );
51
+ }
52
+
53
+ $this->run();
54
+
55
+ // Save option, progress
56
+ $this->saveOptions();
57
+
58
+ return ( object ) $this->response;
59
+ }
60
+
61
+ /**
62
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
63
+ * @return void
64
+ */
65
+ protected function calculateTotalSteps() {
66
+ $this->options->totalSteps = $this->total;
67
+ }
68
+
69
+ /**
70
+ * Execute the Current Step
71
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
72
+ * @return bool
73
+ */
74
+ protected function execute() {
75
+ // Over limits threshold
76
+ if( $this->isOverThreshold() ) {
77
+ // Prepare response and save current progress
78
+ $this->prepareResponse( false, false );
79
+ $this->saveOptions();
80
+ return false;
81
+ }
82
+
83
+ // No more steps, finished
84
+ if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
85
+ $this->prepareResponse( true, false );
86
+ return false;
87
+ }
88
+
89
+ // Table is excluded
90
+ if( in_array( $this->options->tables[$this->options->currentStep], $this->options->excludedTables ) ) {
91
+ $this->prepareResponse();
92
+ return true;
93
+ }
94
+
95
+ // Search & Replace
96
+ if( !$this->stopExecution() && !$this->updateTable( $this->options->tables[$this->options->currentStep] ) ) {
97
+ // Prepare Response
98
+ $this->prepareResponse( false, false );
99
+
100
+ // Not finished
101
+ return true;
102
+ }
103
+
104
+
105
+ // Prepare Response
106
+ $this->prepareResponse();
107
+
108
+ // Not finished
109
+ return true;
110
+ }
111
+
112
+ /**
113
+ * Stop Execution immediately
114
+ * return mixed bool | json
115
+ */
116
+ private function stopExecution() {
117
+ if( $this->db->prefix == $this->tmpPrefix ) {
118
+ $this->returnException( 'Fatal Error 9: Prefix ' . $this->db->prefix . ' is used for the live site hence it can not be used for the staging site as well. Please ask support@wp-staging.com how to resolve this.' );
119
+ }
120
+ return false;
121
+ }
122
+
123
+ /**
124
+ * Copy Tables
125
+ * @param string $tableName
126
+ * @return bool
127
+ */
128
+ private function updateTable( $tableName ) {
129
+ $strings = new Strings();
130
+ $table = $strings->str_replace_first( $this->db->prefix, '', $tableName );
131
+ $newTableName = $this->tmpPrefix . $table;
132
+
133
+ // Save current job
134
+ $this->setJob( $newTableName );
135
+
136
+ // Beginning of the job
137
+ if( !$this->startJob( $newTableName, $tableName ) ) {
138
+ return true;
139
+ }
140
+ // Copy data
141
+ $this->startReplace( $newTableName );
142
+
143
+ // Finis the step
144
+ return $this->finishStep();
145
+ }
146
+
147
+ /**
148
+ * Start search replace job
149
+ * @param string $new
150
+ * @param string $old
151
+ */
152
+ private function startReplace( $new ) {
153
+ $rows = $this->options->job->start + $this->settings->querySRLimit;
154
+ $this->log(
155
+ "DB Processing: Table {$new} {$this->options->job->start} to {$rows} records"
156
+ );
157
+
158
+ // Search & Replace
159
+ $this->searchReplace( $new, $rows, array() );
160
+
161
+ // Set new offset
162
+ $this->options->job->start += $this->settings->querySRLimit;
163
+ }
164
+
165
+ /**
166
+ * Returns the number of pages in a table.
167
+ * @access public
168
+ * @return int
169
+ */
170
+ private function get_pages_in_table( $table ) {
171
+ $table = esc_sql( $table );
172
+ $rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
173
+ $pages = ceil( $rows / $this->settings->querySRLimit );
174
+ return absint( $pages );
175
+ }
176
+
177
+ /**
178
+ * Gets the columns in a table.
179
+ * @access public
180
+ * @param string $table The table to check.
181
+ * @return array
182
+ */
183
+ private function get_columns( $table ) {
184
+ $primary_key = null;
185
+ $columns = array();
186
+ $fields = $this->db->get_results( 'DESCRIBE ' . $table );
187
+ if( is_array( $fields ) ) {
188
+ foreach ( $fields as $column ) {
189
+ $columns[] = $column->Field;
190
+ if( $column->Key == 'PRI' ) {
191
+ $primary_key = $column->Field;
192
+ }
193
+ }
194
+ }
195
+ return array($primary_key, $columns);
196
+ }
197
+
198
+ /**
199
+ * Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
200
+ *
201
+ * Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
202
+ * and to be compatible with batch processing.
203
+ *
204
+ * @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
205
+ *
206
+ * @access public
207
+ * @param string $table The table to run the replacement on.
208
+ * @param int $page The page/block to begin the query on.
209
+ * @param array $args An associative array containing arguements for this run.
210
+ * @return array
211
+ */
212
+ private function searchReplace( $table, $page, $args ) {
213
+
214
+ if( $this->thirdParty->isSearchReplaceExcluded( $table ) ) {
215
+ $this->log( "DB Processing: Skip {$table}", \WPStaging\Utils\Logger::TYPE_INFO );
216
+ return true;
217
+ }
218
+
219
+ // Load up the default settings for this chunk.
220
+ $table = esc_sql( $table );
221
+ $current_page = $this->options->job->start + $this->settings->querySRLimit;
222
+ $pages = $this->get_pages_in_table( $table );
223
+ //$done = false;
224
+
225
+
226
+ if( $this->isSubDir() ) {
227
+ // Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
228
+ $args['search_for'] = array(
229
+ rtrim( $this->multisiteHomeUrlWithoutScheme, "/" ) . $this->getSubDir(),
230
+ ABSPATH,
231
+ str_replace( '/', '\/', rtrim( $this->multisiteHomeUrlWithoutScheme, '/' ) ) . str_replace( '/', '\/', $this->getSubDir() ), // // Used by revslider and several visual editors
232
+ $this->getImagePathLive()
233
+ );
234
+
235
+
236
+ $args['replace_with'] = array(
237
+ rtrim( $this->multisiteDomainWithoutScheme, "/" ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName,
238
+ rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName,
239
+ str_replace( '/', '\/', rtrim( $this->multisiteDomainWithoutScheme, "/" ) ) . str_replace( '/', '\/', $this->getSubDir() ) . '\/' . $this->options->cloneDirectoryName, // Used by revslider and several visual editors
240
+ $this->getImagePathStaging()
241
+ );
242
+ } else {
243
+ $args['search_for'] = array(
244
+ rtrim( $this->multisiteHomeUrlWithoutScheme, '/' ),
245
+ ABSPATH,
246
+ str_replace( '/', '\/', rtrim( $this->multisiteHomeUrlWithoutScheme, '/' ) ),
247
+ $this->getImagePathLive()
248
+ );
249
+ $args['replace_with'] = array(
250
+ rtrim( $this->multisiteDomainWithoutScheme, '/' ) . '/' . $this->options->cloneDirectoryName,
251
+ rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName,
252
+ str_replace( '/', '\/', rtrim( $this->multisiteDomainWithoutScheme, '/' ) ) . '\/' . $this->options->cloneDirectoryName,
253
+ $this->getImagePathStaging()
254
+ );
255
+ }
256
+
257
+ //$this->log( 'Search: ' . $this->multisiteHomeUrlWithoutScheme . ' Replace: ' . rtrim( $this->multisiteDomainWithoutScheme, '/' ) . '/' . $this->options->cloneDirectoryName );
258
+
259
+
260
+ $args['replace_guids'] = 'off';
261
+ $args['dry_run'] = 'off';
262
+ $args['case_insensitive'] = false;
263
+ $args['replace_guids'] = 'off';
264
+ $args['replace_mails'] = 'off';
265
+
266
+ // Allow filtering of search & replace parameters
267
+ $args = apply_filters( 'wpstg_clone_searchreplace_params', $args );
268
+
269
+ // Get a list of columns in this table.
270
+ list( $primary_key, $columns ) = $this->get_columns( $table );
271
+
272
+ // Bail out early if there isn't a primary key.
273
+ // We commented this to search & replace through tables which have no primary keys like wp_revslider_slides
274
+ // @todo test this carefully. If it causes (performance) issues we need to activate it again!
275
+ // @since 2.4.4
276
+ // if( null === $primary_key ) {
277
+ // return false;
278
+ // }
279
+
280
+ $current_row = 0;
281
+ $start = $this->options->job->start;
282
+ $end = $this->settings->querySRLimit;
283
+
284
+ // Grab the content of the table.
285
+ $data = $this->db->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
286
+
287
+ // Filter certain rows option_name in wpstg_options
288
+ $filter = array(
289
+ 'Admin_custome_login_Slidshow',
290
+ 'Admin_custome_login_Social',
291
+ 'Admin_custome_login_logo',
292
+ 'Admin_custome_login_text',
293
+ 'Admin_custome_login_login',
294
+ 'Admin_custome_login_top',
295
+ 'Admin_custome_login_dashboard',
296
+ 'Admin_custome_login_Version',
297
+ 'upload_path',
298
+ );
299
+
300
+ apply_filters( 'wpstg_clone_searchreplace_excl_rows', $filter );
301
+
302
+ // Loop through the data.
303
+ foreach ( $data as $row ) {
304
+ $current_row++;
305
+ $update_sql = array();
306
+ $where_sql = array();
307
+ $upd = false;
308
+
309
+ // Skip rows below
310
+ if( isset( $row['option_name'] ) && in_array( $row['option_name'], $filter ) ) {
311
+ continue;
312
+ }
313
+
314
+ // Skip rows with transients (They can store huge data and we need to save memory)
315
+ if( isset( $row['option_name'] ) && strpos( $row['option_name'], '_transient' ) === 0 ) {
316
+ continue;
317
+ }
318
+
319
+
320
+ foreach ( $columns as $column ) {
321
+
322
+ $dataRow = $row[$column];
323
+
324
+ if( $column == $primary_key ) {
325
+ $where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
326
+ continue;
327
+ }
328
+
329
+ // Skip GUIDs by default.
330
+ if( 'on' !== $args['replace_guids'] && 'guid' == $column ) {
331
+ continue;
332
+ }
333
+
334
+
335
+ // Skip mail addresses
336
+ if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->multisiteDomainWithoutScheme ) ) {
337
+ continue;
338
+ }
339
+
340
+ // Check options table
341
+ if( $this->options->prefix . 'options' === $table ) {
342
+
343
+ // Skip certain options
344
+ if( isset( $should_skip ) && true === $should_skip ) {
345
+ $should_skip = false;
346
+ continue;
347
+ }
348
+
349
+ // Skip this row
350
+ if( 'wpstg_existing_clones_beta' === $dataRow ||
351
+ 'wpstg_existing_clones' === $dataRow ||
352
+ 'wpstg_settings' === $dataRow ||
353
+ 'wpstg_license_status' === $dataRow ||
354
+ 'siteurl' === $dataRow ||
355
+ 'home' === $dataRow
356
+ ) {
357
+ $should_skip = true;
358
+ }
359
+ }
360
+
361
+ // Check the path delimiter for / or \/ and remove one of those which prevents from resulting in wrong syntax like domain.com/staging\/.
362
+ // 1. local.wordpress.test -> local.wordpress.test/staging
363
+ // 2. local.wordpress.test\/ -> local.wordpress.test\/staging\/
364
+ $tmp = $args;
365
+ if( false === strpos( $dataRow, $tmp['search_for'][0] ) ) {
366
+ array_shift( $tmp['search_for'] ); // rtrim( $this->homeUrl, '/' ),
367
+ array_shift( $tmp['replace_with'] ); // rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
368
+ } else {
369
+ unset( $tmp['search_for'][1] );
370
+ unset( $tmp['replace_with'][1] );
371
+ // recount array
372
+ $tmp['search_for'] = array_values( $tmp['search_for'] );
373
+ $tmp['replace_with'] = array_values( $tmp['replace_with'] );
374
+ }
375
+
376
+ // Run a search replace on the data row and respect the serialisation.
377
+ $i = 0;
378
+ foreach ( $tmp['search_for'] as $replace ) {
379
+ //$this->log( "Search for: {$tmp['search_for'][$i]} Replace with {$tmp['replace_with'][$i]}", \WPStaging\Utils\Logger::TYPE_ERROR );
380
+ $dataRow = $this->recursive_unserialize_replace( $tmp['search_for'][$i], $tmp['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
381
+ $i++;
382
+ }
383
+ unset( $replace );
384
+ unset( $i );
385
+ unset( $tmp );
386
+
387
+ // Something was changed
388
+ if( $row[$column] != $dataRow ) {
389
+ $update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
390
+ $upd = true;
391
+ }
392
+ }
393
+
394
+ // Determine what to do with updates.
395
+ if( $args['dry_run'] === 'on' ) {
396
+ // Don't do anything if a dry run
397
+ } elseif( $upd && !empty( $where_sql ) ) {
398
+ // If there are changes to make, run the query.
399
+ $sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
400
+ $result = $this->db->query( $sql );
401
+
402
+ if( !$result ) {
403
+ $this->log( "Error updating row {$current_row} SQL: {$sql}", \WPStaging\Utils\Logger::TYPE_ERROR );
404
+ }
405
+ }
406
+ } // end row loop
407
+ unset( $row );
408
+ unset( $update_sql );
409
+ unset( $where_sql );
410
+ unset( $sql );
411
+
412
+
413
+ // DB Flush
414
+ $this->db->flush();
415
+ return true;
416
+ }
417
+
418
+ /**
419
+ * Get path to multisite image folder e.g. wp-content/blogs.dir/ID/files or wp-content/uploads/sites/ID
420
+ * @return string
421
+ */
422
+ private function getImagePathLive() {
423
+ // Check first which structure is used
424
+ $uploads = wp_upload_dir();
425
+ $basedir = $uploads['basedir'];
426
+ $blogId = get_current_blog_id();
427
+
428
+ if( false === strpos( $basedir, 'blogs.dir' ) ) {
429
+ // Since WP 3.5
430
+ $path = $blogId > 1 ?
431
+ 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR :
432
+ 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
433
+ } else {
434
+ // old blog structure
435
+ $path = $blogId > 1 ?
436
+ 'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR :
437
+ 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
438
+ }
439
+ return $path;
440
+ }
441
+
442
+ /**
443
+ * Get path to staging site image path wp-content/uploads
444
+ * @return string
445
+ */
446
+ private function getImagePathStaging() {
447
+ return 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
448
+ }
449
+
450
+ /**
451
+ * Adapted from interconnect/it's search/replace script.
452
+ *
453
+ * @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
454
+ *
455
+ * Take a serialised array and unserialise it replacing elements as needed and
456
+ * unserialising any subordinate arrays and performing the replace on those too.
457
+ *
458
+ * @access private
459
+ * @param string $from String we're looking to replace.
460
+ * @param string $to What we want it to be replaced with
461
+ * @param array $data Used to pass any subordinate arrays back to in.
462
+ * @param boolean $serialized Does the array passed via $data need serialising.
463
+ * @param sting|boolean $case_insensitive Set to 'on' if we should ignore case, false otherwise.
464
+ *
465
+ * @return string|array The original array with all elements replaced as needed.
466
+ */
467
+ private function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialized = false, $case_insensitive = false ) {
468
+ try {
469
+ // Some unserialized data cannot be re-serialized eg. SimpleXMLElements
470
+ if( is_serialized( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
471
+ $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
472
+ } elseif( is_array( $data ) ) {
473
+ $tmp = array();
474
+ foreach ( $data as $key => $value ) {
475
+ $tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
476
+ }
477
+
478
+ $data = $tmp;
479
+ unset( $tmp );
480
+ } elseif( is_object( $data ) ) {
481
+ $tmp = $data;
482
+ $props = get_object_vars( $data );
483
+ foreach ( $props as $key => $value ) {
484
+ if( $key === '' || ord( $key[0] ) === 0 ) {
485
+ continue;
486
+ }
487
+ $tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
488
+ }
489
+
490
+ $data = $tmp;
491
+ unset( $tmp );
492
+ } else {
493
+ if( is_string( $data ) ) {
494
+ if( !empty( $from ) && !empty( $to ) ) {
495
+ $data = $this->str_replace( $from, $to, $data, $case_insensitive );
496
+ }
497
+ }
498
+ }
499
+
500
+ if( $serialized ) {
501
+ return serialize( $data );
502
+ }
503
+ } catch ( Exception $error ) {
504
+
505
+ }
506
+
507
+ return $data;
508
+ }
509
+
510
+ // private function recursive_unserialize_replace_( $from = '', $to = '', $data = '', $serialised = false, $case_insensitive = false ) {
511
+ // try {
512
+ //
513
+ // // Check first if its an object and repair it if necessary
514
+ // //$data = $this->fixObject($data);
515
+ //
516
+ // if( is_string( $data ) && !is_serialized_string( $data ) && ( $unserialized = $this->unserialize( $data ) ) !== false ) {
517
+ // $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
518
+ // } elseif( is_array( $data ) ) {
519
+ // $_tmp = array();
520
+ // foreach ( $data as $key => $value ) {
521
+ // $_tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
522
+ // }
523
+ //
524
+ // $data = $_tmp;
525
+ // unset( $_tmp );
526
+ // }
527
+ //
528
+ // // Submitted by Tina Matter
529
+ // elseif( $this->isValidObject( $data ) ) {
530
+ // $_tmp = $data; // new $data_class( );
531
+ // $props = get_object_vars( $data );
532
+ // foreach ( $props as $key => $value ) {
533
+ // if( $key === '' || ord( $key[0] ) === 0 ) {
534
+ // continue;
535
+ // }
536
+ // $_tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
537
+ // }
538
+ //
539
+ // $data = $_tmp;
540
+ // unset( $_tmp );
541
+ // } elseif( is_serialized_string( $data ) ) {
542
+ // if( false !== ($data = $this->unserialize( $data )) ) {
543
+ // $data = $this->str_replace( $from, $to, $data, $case_insensitive );
544
+ // $data = serialize( $data );
545
+ // }
546
+ // } else {
547
+ // if( is_string( $data ) ) {
548
+ // $data = $this->str_replace( $from, $to, $data, $case_insensitive );
549
+ // }
550
+ // }
551
+ //
552
+ // if( $serialised ) {
553
+ // return serialize( $data );
554
+ // }
555
+ // } catch ( Exception $error ) {
556
+ //
557
+ // }
558
+ //
559
+ // return $data;
560
+ // }
561
+
562
+
563
+
564
+ /**
565
+ * Check if the object is a valid one and not __PHP_Incomplete_Class_Name
566
+ * Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
567
+ * @return boolean
568
+ */
569
+ // private function isValidObject( $data ) {
570
+ // if( !is_object( $data ) || gettype( $data ) != 'object' ) {
571
+ // return false;
572
+ // }
573
+ //
574
+ // $invalid_class_props = get_object_vars( $data );
575
+ //
576
+ // if( !isset( $invalid_class_props['__PHP_Incomplete_Class_Name'] ) ) {
577
+ // // Assume it must be an valid object
578
+ // return true;
579
+ // }
580
+ //
581
+ // $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
582
+ //
583
+ // if( !empty( $invalid_object_class ) ) {
584
+ // return false;
585
+ // }
586
+ //
587
+ // // Assume it must be an valid object
588
+ // return true;
589
+ // }
590
+
591
+ /**
592
+ * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
593
+ * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
594
+ * @access public
595
+ * @param string $input The string to escape.
596
+ * @return string
597
+ */
598
+ private function mysql_escape_mimic( $input ) {
599
+ if( is_array( $input ) ) {
600
+ return array_map( __METHOD__, $input );
601
+ }
602
+ if( !empty( $input ) && is_string( $input ) ) {
603
+ return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
604
+ }
605
+
606
+ return $input;
607
+ }
608
+
609
+ /**
610
+ * Return unserialized object or array
611
+ *
612
+ * @param string $serialized_string Serialized string.
613
+ * @param string $method The name of the caller method.
614
+ *
615
+ * @return mixed, false on failure
616
+ */
617
+ private static function unserialize( $serialized_string ) {
618
+ if( !is_serialized( $serialized_string ) ) {
619
+ return false;
620
+ }
621
+
622
+ $serialized_string = trim( $serialized_string );
623
+ $unserialized_string = @unserialize( $serialized_string );
624
+
625
+ return $unserialized_string;
626
+ }
627
+
628
+ /**
629
+ * Wrapper for str_replace
630
+ *
631
+ * @param string $from
632
+ * @param string $to
633
+ * @param string $data
634
+ * @param string|bool $case_insensitive
635
+ *
636
+ * @return string
637
+ */
638
+ private function str_replace( $from, $to, $data, $case_insensitive = false ) {
639
+
640
+ // Add filter
641
+ $excludes = apply_filters( 'wpstg_clone_searchreplace_excl', array() );
642
+
643
+ // Build pattern
644
+ $regexExclude = '';
645
+ foreach ( $excludes as $exclude ) {
646
+ $regexExclude .= $exclude . '(*SKIP)(FAIL)|';
647
+ }
648
+
649
+ if( 'on' === $case_insensitive ) {
650
+ //$data = str_ireplace( $from, $to, $data );
651
+ $data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#i', $to, $data );
652
+ } else {
653
+ //$data = str_replace( $from, $to, $data );
654
+ $data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#', $to, $data );
655
+ }
656
+
657
+ return $data;
658
+ }
659
+
660
+ /**
661
+ * Set the job
662
+ * @param string $table
663
+ */
664
+ private function setJob( $table ) {
665
+ if( !empty( $this->options->job->current ) ) {
666
+ return;
667
+ }
668
+
669
+ $this->options->job->current = $table;
670
+ $this->options->job->start = 0;
671
+ }
672
+
673
+ /**
674
+ * Start Job
675
+ * @param string $new
676
+ * @param string $old
677
+ * @return bool
678
+ */
679
+ private function startJob( $new, $old ) {
680
+ if( 0 != $this->options->job->start ) {
681
+ return true;
682
+ }
683
+
684
+ $this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
685
+
686
+ if( 0 == $this->options->job->total ) {
687
+ $this->finishStep();
688
+ return false;
689
+ }
690
+
691
+ return true;
692
+ }
693
+
694
+ /**
695
+ * Finish the step
696
+ */
697
+ private function finishStep() {
698
+ // This job is not finished yet
699
+ if( $this->options->job->total > $this->options->job->start ) {
700
+ return false;
701
+ }
702
+
703
+ // Add it to cloned tables listing
704
+ $this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
705
+
706
+ // Reset job
707
+ $this->options->job = new \stdClass();
708
+
709
+ return true;
710
+ }
711
+
712
+ /**
713
+ * Drop table if necessary
714
+ * @param string $new
715
+ */
716
+ private function dropTable( $new ) {
717
+ $old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
718
+
719
+ if( !$this->shouldDropTable( $new, $old ) ) {
720
+ return;
721
+ }
722
+
723
+ $this->log( "DB Processing: {$new} already exists, dropping it first" );
724
+ $this->db->query( "DROP TABLE {$new}" );
725
+ }
726
+
727
+ /**
728
+ * Check if table needs to be dropped
729
+ * @param string $new
730
+ * @param string $old
731
+ * @return bool
732
+ */
733
+ private function shouldDropTable( $new, $old ) {
734
+ return (
735
+ $old == $new &&
736
+ (
737
+ !isset( $this->options->job->current ) ||
738
+ !isset( $this->options->job->start ) ||
739
+ 0 == $this->options->job->start
740
+ )
741
+ );
742
+ }
743
+
744
+ /**
745
+ * Check if WP is installed in subdir
746
+ * @return boolean
747
+ */
748
+ private function isSubDir() {
749
+ // Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
750
+ // This is happening much more often than you would expect
751
+ $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
752
+ $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
753
+
754
+ if( $home !== $siteurl ) {
755
+ return true;
756
+ }
757
+ return false;
758
+ }
759
+
760
+ /**
761
+ * Get the install sub directory if WP is installed in sub directory
762
+ * @return string
763
+ */
764
+ private function getSubDir() {
765
+ $home = get_option( 'home' );
766
+ $siteurl = get_option( 'siteurl' );
767
+
768
+ if( empty( $home ) || empty( $siteurl ) ) {
769
+ return '/';
770
+ }
771
+
772
+ $dir = str_replace( $home, '', $siteurl );
773
+ return '/' . str_replace( '/', '', $dir );
774
+ }
775
+
776
+ }
apps/Backend/Modules/Jobs/SearchReplace.php CHANGED
@@ -216,8 +216,13 @@ class SearchReplace extends JobExecutable {
216
  * @return array
217
  */
218
  private function searchReplace( $table, $page, $args ) {
219
-
220
-
 
 
 
 
 
221
  // Load up the default settings for this chunk.
222
  $table = esc_sql( $table );
223
  $current_page = $this->options->job->start + $this->settings->querySRLimit;
@@ -299,7 +304,7 @@ class SearchReplace extends JobExecutable {
299
 
300
  // Loop through the data.
301
  foreach ( $data as $row ) {
302
- //$current_row++;
303
  $update_sql = array();
304
  $where_sql = array();
305
  $upd = false;
@@ -684,4 +689,5 @@ class SearchReplace extends JobExecutable {
684
  return '/' . str_replace( '/', '', $dir );
685
  }
686
 
 
687
  }
216
  * @return array
217
  */
218
  private function searchReplace( $table, $page, $args ) {
219
+
220
+ if( $this->thirdParty->isSearchReplaceExcluded($table) ) {
221
+ $this->log( "DB Processing: Skip {$table}", \WPStaging\Utils\Logger::TYPE_INFO );
222
+ return true;
223
+ }
224
+
225
+
226
  // Load up the default settings for this chunk.
227
  $table = esc_sql( $table );
228
  $current_page = $this->options->job->start + $this->settings->querySRLimit;
304
 
305
  // Loop through the data.
306
  foreach ( $data as $row ) {
307
+ $current_row++;
308
  $update_sql = array();
309
  $where_sql = array();
310
  $upd = false;
689
  return '/' . str_replace( '/', '', $dir );
690
  }
691
 
692
+
693
  }
apps/Backend/Modules/Jobs/Updating.php CHANGED
@@ -41,7 +41,25 @@ class Updating extends Job {
41
  $this->options->includedDirectories = array();
42
  $this->options->excludedDirectories = array();
43
  $this->options->extraDirectories = array();
44
- $this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  // Define mainJob to differentiate between cloning, updating and pushing
47
  $this->options->mainJob = 'updating';
@@ -194,7 +212,7 @@ class Updating extends Job {
194
  if( is_multisite() ) {
195
  $database = new muDatabase();
196
  } else {
197
- $database = new Database();
198
  }
199
  return $this->handleJobResponse( $database->start(), "directories" );
200
  }
@@ -207,7 +225,7 @@ class Updating extends Job {
207
  if( is_multisite() ) {
208
  $directories = new muDirectories();
209
  } else {
210
- $directories = new Directories();
211
  }
212
  return $this->handleJobResponse( $directories->start(), "files" );
213
  }
@@ -220,7 +238,7 @@ class Updating extends Job {
220
  if( is_multisite() ) {
221
  $files = new muFiles();
222
  } else {
223
- $files = new Files();
224
  }
225
  return $this->handleJobResponse( $files->start(), "data" );
226
  }
@@ -233,7 +251,7 @@ class Updating extends Job {
233
  if( is_multisite() ) {
234
  $data = new muData();
235
  } else {
236
- $data = new Data();
237
  }
238
  return $this->handleJobResponse( $data->start(), "finish" );
239
  }
@@ -246,7 +264,7 @@ class Updating extends Job {
246
  if( is_multisite() ) {
247
  $finish = new muFinish();
248
  } else {
249
- $finish = new Finish();
250
  }
251
  $finish = new Finish();
252
  return $this->handleJobResponse( $finish->start(), '' );
41
  $this->options->includedDirectories = array();
42
  $this->options->excludedDirectories = array();
43
  $this->options->extraDirectories = array();
44
+ $this->options->excludedFiles = array(
45
+ '.htaccess',
46
+ '.DS_Store',
47
+ '.git',
48
+ '.svn',
49
+ '.tmp',
50
+ 'desktop.ini',
51
+ '.gitignore',
52
+ '.log',
53
+ 'db.php',
54
+ 'object-cache.php'
55
+ );
56
+
57
+ $this->options->excludedFilesFullPath = array(
58
+ 'wp-content' . DIRECTORY_SEPARATOR . 'db.php',
59
+ 'wp-content' . DIRECTORY_SEPARATOR . 'object-cache.php',
60
+ 'wp-content' . DIRECTORY_SEPARATOR . 'advanced-cache.php'
61
+ );
62
+
63
 
64
  // Define mainJob to differentiate between cloning, updating and pushing
65
  $this->options->mainJob = 'updating';
212
  if( is_multisite() ) {
213
  $database = new muDatabase();
214
  } else {
215
+ $database = new Database();
216
  }
217
  return $this->handleJobResponse( $database->start(), "directories" );
218
  }
225
  if( is_multisite() ) {
226
  $directories = new muDirectories();
227
  } else {
228
+ $directories = new Directories();
229
  }
230
  return $this->handleJobResponse( $directories->start(), "files" );
231
  }
238
  if( is_multisite() ) {
239
  $files = new muFiles();
240
  } else {
241
+ $files = new Files();
242
  }
243
  return $this->handleJobResponse( $files->start(), "data" );
244
  }
251
  if( is_multisite() ) {
252
  $data = new muData();
253
  } else {
254
+ $data = new Data();
255
  }
256
  return $this->handleJobResponse( $data->start(), "finish" );
257
  }
264
  if( is_multisite() ) {
265
  $finish = new muFinish();
266
  } else {
267
+ $finish = new Finish();
268
  }
269
  $finish = new Finish();
270
  return $this->handleJobResponse( $finish->start(), '' );
apps/Backend/Modules/Views/Forms/Settings.php CHANGED
@@ -59,7 +59,7 @@ class Settings {
59
 
60
  $this->form["general"]->add(
61
  $element->setLabel( "DB Copy Query Limit" )
62
- ->setDefault( isset( $settings->queryLimit ) ? $settings->queryLimit : 5000 )
63
  );
64
  // DB Search & Replace Query Limit
65
  $element = new Numerical(
@@ -88,9 +88,7 @@ class Settings {
88
  );
89
 
90
  $this->form["general"]->add(
91
- $element->setLabel( "File Copy Limit" )
92
- ->setDefault( isset( $settings->fileLimit ) ? $settings->fileLimit : 1 )
93
-
94
  );
95
 
96
 
@@ -135,7 +133,7 @@ class Settings {
135
 
136
  $this->form["general"]->add(
137
  $element->setLabel( "CPU Load Priority" )
138
- ->setDefault( isset( $settings->cpuLoad ) ? $settings->cpuLoad : "fast" )
139
  );
140
 
141
 
59
 
60
  $this->form["general"]->add(
61
  $element->setLabel( "DB Copy Query Limit" )
62
+ ->setDefault( isset( $settings->queryLimit ) ? $settings->queryLimit : 10000 )
63
  );
64
  // DB Search & Replace Query Limit
65
  $element = new Numerical(
88
  );
89
 
90
  $this->form["general"]->add(
91
+ $element->setLabel( "File Copy Limit" )->setDefault( isset( $settings->fileLimit ) ? $settings->fileLimit : '50' )
 
 
92
  );
93
 
94
 
133
 
134
  $this->form["general"]->add(
135
  $element->setLabel( "CPU Load Priority" )
136
+ ->setDefault( isset( $settings->cpuLoad ) ? $settings->cpuLoad : "low" )
137
  );
138
 
139
 
apps/Backend/public/js/wpstg-admin.js CHANGED
@@ -399,14 +399,15 @@ var WPStaging = (function ($)
399
  console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
400
  console.log(textStatus);
401
 
402
- if (false === showErrors)
403
- {
404
- return false;
405
- }
406
 
 
407
 
408
  showError(
409
- "Fatal Unknown Error. Please try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report."
410
  );
411
  },
412
  success: function (data) {
@@ -434,6 +435,14 @@ var WPStaging = (function ($)
434
  obj.error = 'custom error';
435
  return JSON.stringify(obj);
436
 
 
 
 
 
 
 
 
 
437
  }
438
  }
439
  });
399
  console.log(xhr.status + ' ' + xhr.statusText + '---' + textStatus);
400
  console.log(textStatus);
401
 
402
+ // if (false === showErrors)
403
+ // {
404
+ // return false;
405
+ // }
406
 
407
+ var errorCode = "undefined" === typeof(xhr.status) ? "Unknown" : xhr.status;
408
 
409
  showError(
410
+ "Fatal Error: "+ errorCode +" Please try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report."
411
  );
412
  },
413
  success: function (data) {
435
  obj.error = 'custom error';
436
  return JSON.stringify(obj);
437
 
438
+ },
439
+ 429: function () {
440
+ showError("It looks like your server is rate limiting ajax requests. Please try to resume after a minute. If this still not works try the <a href='https://wp-staging.com/docs/wp-staging-settings-for-small-servers/' target='_blank'>WP Staging Small Server Settings</a> or submit an error report.\n\ ");
441
+ var obj = new Object();
442
+ obj.status = false;
443
+ obj.error = 'custom error';
444
+ return JSON.stringify(obj);
445
+
446
  }
447
  }
448
  });
apps/Backend/views/clone/multi-site/index.php CHANGED
@@ -1,3 +1,3 @@
1
  <span class="wpstg-notice-alert" style="margin-top:20px;">
2
- <?php echo __("WordPress Multisite is currently not supported!", "wp-staging")?>
3
  </span>
1
  <span class="wpstg-notice-alert" style="margin-top:20px;">
2
+ <?php echo sprintf(__('WordPress Multisite is not supported! Upgrade to <a href="%s" target="_blank">WP Staging Pro</a>', 'wp-staging'), 'https://wp-staging.com/')?>
3
  </span>
apps/Backend/views/settings/main-settings.php CHANGED
@@ -98,7 +98,7 @@
98
  To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it
99
  until you get no more errors during copying process.", "wp-staging" ); ?>
100
  <br>
101
- <strong> Default: 5000 </strong>
102
  </span>
103
  </div>
104
  </td>
@@ -139,7 +139,7 @@
139
  until you get no more errors during copying process.", "wp-staging" ); ?>
140
  <br>
141
  <br>
142
- <?php _e( "<strong>Important:</strong> If CPU Load Priority is <strong>Low</strong> set a file copy limit value of 10 or higher! Otherwise file copying process takes a lot of time.", "wp-staging" ); ?>
143
  <br>
144
  <br>
145
  <strong> Default: 1 </strong>
@@ -157,7 +157,7 @@
157
  <?php echo $form->label( "wpstg_settings[maxFileSize]" ) ?>
158
  <span class="description">
159
  <?php _e( "Maximum size of the files which are allowed to copy. All files larger than this value will be skipped.
160
- Note: Increase this option only if you have a good reason. Files larger than a few megabytes are in 99% of all cases logging and backup files which are not needed on a staging site.", "wp-staging" ); ?>
161
  <br>
162
  <strong>Default:</strong> 8 MB
163
  </span>
@@ -198,7 +198,7 @@
198
  (e.g. <strong>authorization error</strong>).
199
  Using a lower value results in lower cpu load on your server but also slower staging site creation.", "wp-staging" ); ?>
200
  <br>
201
- <strong>Default: </strong> Medium
202
  </span>
203
  </div>
204
  </td>
98
  To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it
99
  until you get no more errors during copying process.", "wp-staging" ); ?>
100
  <br>
101
+ <strong> Default: 10000 </strong>
102
  </span>
103
  </div>
104
  </td>
139
  until you get no more errors during copying process.", "wp-staging" ); ?>
140
  <br>
141
  <br>
142
+ <?php _e( "<strong>Important:</strong> If CPU Load Priority is <strong>Low</strong> set a file copy limit value of 50 or higher! Otherwise file copying process takes a lot of time.", "wp-staging" ); ?>
143
  <br>
144
  <br>
145
  <strong> Default: 1 </strong>
157
  <?php echo $form->label( "wpstg_settings[maxFileSize]" ) ?>
158
  <span class="description">
159
  <?php _e( "Maximum size of the files which are allowed to copy. All files larger than this value will be skipped.
160
+ Note: Increase this option only if you have a good reason. Files larger than a few megabytes are in 99% of all cases log and backup files which are not needed on a staging site.", "wp-staging" ); ?>
161
  <br>
162
  <strong>Default:</strong> 8 MB
163
  </span>
198
  (e.g. <strong>authorization error</strong>).
199
  Using a lower value results in lower cpu load on your server but also slower staging site creation.", "wp-staging" ); ?>
200
  <br>
201
+ <strong>Default: </strong> Low
202
  </span>
203
  </div>
204
  </td>
apps/Core/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
  /**
30
  * Plugin version
31
  */
32
- const VERSION = "2.3.7";
33
 
34
  /**
35
  * Plugin name
@@ -77,10 +77,6 @@ final class WPStaging {
77
  */
78
  private function __construct() {
79
 
80
- //$file = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR . self::SLUG . ".php";
81
- // Activation Hook
82
- //register_activation_hook( __FILE__, array($this, "onActivation") );
83
-
84
  $this->registerMain();
85
  $this->registerNamespaces();
86
  $this->loadLanguages();
@@ -103,10 +99,6 @@ final class WPStaging {
103
  /**
104
  * Method to be executed upon activation of the plugin
105
  */
106
- // public function onActivation() {
107
- // $Activation = new \WPStaging\Backend\Activation\Activation();
108
- // $Activation->install_dependancies();
109
- // }
110
 
111
  public function registerMain() {
112
  // Slug of the plugin
29
  /**
30
  * Plugin version
31
  */
32
+ const VERSION = "2.3.8";
33
 
34
  /**
35
  * Plugin name
77
  */
78
  private function __construct() {
79
 
 
 
 
 
80
  $this->registerMain();
81
  $this->registerNamespaces();
82
  $this->loadLanguages();
99
  /**
100
  * Method to be executed upon activation of the plugin
101
  */
 
 
 
 
102
 
103
  public function registerMain() {
104
  // Slug of the plugin
apps/Core/thirdParty/thirdPartyCompatibility.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPStaging\thirdParty;
4
+
5
+ use WPStaging\DI\InjectionAware;
6
+
7
+ /**
8
+ * Methods to use for third party plugin compatibility
9
+ *
10
+ * @author IronMan
11
+ */
12
+ class thirdPartyCompatibility extends InjectionAware
13
+ {
14
+
15
+ /**
16
+ * Define a list of tables which should not run through search & replace method
17
+ * @param string table name e.g. wpsptg1_cerber_files or wpstgtmp_4_cerber_files
18
+ * @return array
19
+ */
20
+ public function isSearchReplaceExcluded($table) {
21
+ $excludedTables = array(
22
+ '_cerber_files', // Cerber Security Plugin
23
+ );
24
+
25
+ foreach($excludedTables as $excludedTable){
26
+ if( false !== strpos($table, $excludedTable) ){
27
+ return true;
28
+ }
29
+ }
30
+ return false;
31
+ }
32
+
33
+ }
languages/wp-staging-de_DE.po CHANGED
@@ -1,504 +1,504 @@
1
- # Translation of Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable (latest release) in German
2
- # This file is distributed under the same license as the Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable (latest release) package.
3
- msgid ""
4
- msgstr ""
5
- "PO-Revision-Date: 2018-08-24 20:55:04+0000\n"
6
- "MIME-Version: 1.0\n"
7
- "Content-Type: text/plain; charset=UTF-8\n"
8
- "Content-Transfer-Encoding: 8bit\n"
9
- "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
- "X-Generator: GlotPress/2.4.0-alpha\n"
11
- "Language: de\n"
12
- "Project-Id-Version: Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable (latest release)\n"
13
-
14
- #. Author URI of the plugin/theme
15
- msgid "https://wp-staging.com"
16
- msgstr "https://wp-staging.com"
17
-
18
- #. Author of the plugin/theme
19
- msgid "WP-Staging"
20
- msgstr "WP-Staging"
21
-
22
- #. Description of the plugin/theme
23
- msgid "Create a staging clone site for testing & developing"
24
- msgstr "Erstelle eine Klon Webseite zum Testen und Entwickeln"
25
-
26
- #. Plugin URI of the plugin/theme
27
- msgid "https://wordpress.org/plugins/wp-staging"
28
- msgstr "https://wordpress.org/plugins/wp-staging"
29
-
30
- #: apps/Frontend/Frontend.php:88 apps/Frontend/loginForm.php:256
31
- msgid "Log In"
32
- msgstr "Anmelden"
33
-
34
- #: apps/Frontend/Frontend.php:87 apps/Frontend/loginForm.php:255
35
- msgid "Remember Me"
36
- msgstr "Erinnere mich"
37
-
38
- #: apps/Frontend/Frontend.php:86 apps/Frontend/loginForm.php:254
39
- msgid "Password"
40
- msgstr "Erinnere mich"
41
-
42
- #: apps/Frontend/Frontend.php:85 apps/Frontend/loginForm.php:253
43
- msgid "Username or Email Address"
44
- msgstr "Benutzername oder E-Mail Adresse"
45
-
46
- #: apps/Core/Utils/Report.php:31
47
- msgid "Please accept our privacy policy."
48
- msgstr "Bitte akzeptiere unsere Datenschutzvereinbarung"
49
-
50
- #: apps/Core/Utils/Report.php:29
51
- msgid "Please enter your issue."
52
- msgstr "Bitte beschreibe Dein Problem"
53
-
54
- #: apps/Core/Utils/Report.php:27
55
- msgid "Email address is not valid."
56
- msgstr "E-Mail Addresse ist nicht gültig"
57
-
58
- #: apps/Core/Cron/Cron.php:35
59
- msgid "Once a month"
60
- msgstr "Einmal im Monat"
61
-
62
- #: apps/Core/Cron/Cron.php:30
63
- msgid "Once Weekly"
64
- msgstr "Einmal wöchentlich"
65
-
66
- #: apps/Backend/views/welcome/welcome.php:13
67
- msgid "Comes with our 30-day no questions asked money back guarantee"
68
- msgstr "Kommt mit unserer 30-Tage Geld zurück Garantie"
69
-
70
- #: apps/Backend/views/welcome/welcome.php:10
71
- msgid "WP Staging is coded well for protection of your data"
72
- msgstr "WP Staging wurde sorgfältig entwickelt um Deine Daten zu schützen"
73
-
74
- #: apps/Backend/views/welcome/welcome.php:9
75
- msgid "Cloning process is fast and does not slow down website loading"
76
- msgstr "Der Klonvorgang ist schnell und beeinflusst nicht die Webseite Ladezeit"
77
-
78
- #: apps/Backend/views/welcome/welcome.php:8
79
- msgid "Staging Site is available to authenticated users only"
80
- msgstr "Staging Webseite ist nur für berechtigte Benutzer verfügbar"
81
-
82
- #: apps/Backend/views/welcome/welcome.php:7
83
- msgid "Copy plugin and theme files from staging to live site"
84
- msgstr "Kopiere Plugin und Theme Dateien von der Staging auf die Live Seite"
85
-
86
- #: apps/Backend/views/welcome/welcome.php:6
87
- msgid "Create a clone of your website with a simple click"
88
- msgstr "Erstelle einen Klon Deiner Webseite mit einem einfachen Klick"
89
-
90
- #: apps/Backend/views/welcome/welcome.php:4
91
- msgid " - Copy Themes & Plugins from Staging to Live Site"
92
- msgstr "Kopiere Themes & Plugins von der Staging auf die Live Seite"
93
-
94
- #: apps/Backend/views/welcome/welcome.php:4
95
- msgid "WP Staging Pro"
96
- msgstr "WP Staging Pro"
97
-
98
- #: apps/Backend/views/tools/tabs/import_export.php:52
99
- msgid "Import"
100
- msgstr "Importieren"
101
-
102
- #: apps/Backend/views/tools/tabs/import_export.php:33
103
- msgid "Import Settings"
104
- msgstr "Importiere Einstellungen"
105
-
106
- #: apps/Backend/views/tools/tabs/import_export.php:22
107
- msgid "Export"
108
- msgstr "Exportieren"
109
-
110
- #: apps/Backend/views/tools/tabs/import_export.php:5
111
- msgid "Export Settings"
112
- msgstr "Exportiere Einstellungen"
113
-
114
- #: apps/Backend/views/settings/main-settings.php:290
115
- msgid ""
116
- "Check this box if you like WP Staging to check sizes of each directory on scanning process.\n"
117
- " <br>\n"
118
- " Warning this may cause timeout problems in big directory / file structures."
119
- msgstr "Lasse WP Staging die Ordnergrösse bei jedem Scan Prozess überprüfen<br>Achtung, dies könnte bei großen Ordner / Datei Strukturen zu Timeout Problemen führen."
120
-
121
- #: apps/Backend/views/settings/main-settings.php:275
122
- msgid ""
123
- "Check this box if you like WP Staging to completely remove all of its data when the plugin is deleted.\n"
124
- " This will not remove staging sites files or database tables."
125
- msgstr "Lösche alle Einstellungen beim Löschen des Plugins"
126
-
127
- #: apps/Backend/views/settings/main-settings.php:261
128
- msgid "The Optimizer is a mu plugin which disables all other plugins during WP Staging processing. Usually this makes the cloning process more reliable. If you experience issues, disable the Optimizer."
129
- msgstr "Der Optimizer ist ein mu Plugin welches alle anderen Plugins während des Klonvorgangs deaktiviert. Gewöhnlich macht das den Klonvorgang sehr viel zuverlässiger. Deaktiviere diese Option wenn etwas nicht funktioniert."
130
-
131
- #: apps/Backend/views/settings/main-settings.php:246
132
- msgid ""
133
- "This will enable an extended debug mode which creates additional entries\n"
134
- " in <strong>wp-content/uploads/wp-staging/logs/logfile.log</strong>.\n"
135
- " Please enable this when we ask you to do so."
136
- msgstr "Dieser Modus schreibt zusätzliche Einträge in die Logdatei <strong>wp-content/uploads/wp-staging/logs/logfile.log</strong> Bitte aktiviere diesen Modus wenn Du ein Support Ticket eröffnest und wir Dich darum bitten."
137
-
138
- #: apps/Backend/views/settings/main-settings.php:196
139
- msgid ""
140
- "Using high will result in fast as possible processing but the cpu load\n"
141
- " increases and it's also possible that staging process gets interrupted because of too many ajax requests\n"
142
- " (e.g. <strong>authorization error</strong>).\n"
143
- " Using a lower value results in lower cpu load on your server but also slower staging site creation."
144
- msgstr "Die Verwendung von \"High\" erstellt die Staging Seite am schnellsten aber die CPU Load erhöht sich und es ist möglich, dass der Staging Prozess aufgrund zu vieler Ajax Anfragen durch einen Timeout unterbrochen wird.Die Verwendungung einer niedrigeren Einstellung führt zu einer geringeren CPU Load, aber auch zu einer langsameren Verarbeitung."
145
-
146
- #: apps/Backend/views/settings/main-settings.php:175
147
- msgid ""
148
- "Buffer size for the file copy process in megabyte.\n"
149
- " The higher the value the faster large files are copied.\n"
150
- " To find out the highest possible values try a high one and lower it until\n"
151
- " you get no errors during file copy process. Usually this value correlates directly\n"
152
- " with the memory consumption of php so make sure that\n"
153
- " it does not exceed any php.ini max_memory limits."
154
- msgstr "Buffer Grösse für den Datei Kopiervorgang in Megabyte. Je größer der Wert, desto schneller werden große Dateien kopiert. Größere Werte beeinflussen auch die maximale Speicherauslastung von PHP und sollte daher nicht zu gross gewählt werden."
155
-
156
- #: apps/Backend/views/settings/main-settings.php:159
157
- msgid ""
158
- "Maximum size of the files which are allowed to copy. All files larger than this value will be skipped. \n"
159
- " Note: Increase this option only if you have a good reason. Files larger than a few megabytes are in 99% of all cases logging and backup files which are not needed on a staging site."
160
- msgstr "Maximale Dateigrösse welche noch kopiert wird. Alle Dateien größer als dieser Wert werden übersprungen. Hinweis: Erhöhe diese Option nur wenn Du einen guten Grund dazu hast. Dateien größer als ein einige Megabyte sind in 99% aller Fälle Log und Backup Dateien welche für eine Staging Seite nicht notwendig sind."
161
-
162
- #: apps/Backend/views/settings/main-settings.php:142
163
- msgid "<strong>Important:</strong> If CPU Load Priority is <strong>Low</strong> set a file copy limit value of 10 or higher! Otherwise file copying process takes a lot of time."
164
- msgstr "<strong>Achtung</strong> Wenn die CPU Load Priority <strong>Low</strong> ist, setze das Datei Kopierlimit auf 10 oder höher! Andernfalls wird der Kopiervorgang sehr langsam sein."
165
-
166
- #: apps/Backend/views/settings/main-settings.php:136
167
- msgid ""
168
- "Number of files to copy that will be copied within one ajax request.\n"
169
- " The higher the value the faster the file copy process.\n"
170
- " To find out the highest possible values try a high value like 500 or more. If you get timeout issues, lower it\n"
171
- " until you get no more errors during copying process."
172
- msgstr "Anzahl der Dateien, die mit einer Ajax Abfrage kopiert werden. Je höher der Wert, desto schneller ist der Kopiervorgang. "
173
-
174
- #: apps/Backend/views/settings/main-settings.php:116
175
- msgid ""
176
- "Number of DB rows, that are processed within one ajax query.\n"
177
- " The higher the value the faster the database search & replace process.\n"
178
- " This is a high memory consumptive process. If you get timeouts lower this value!"
179
- msgstr "Anzahl der Datenbank Einträge welche zugleich verarbeitet werden. Je grösser der Wert desto schneller ist der Suchen & Ersetzen Prozess."
180
-
181
- #: apps/Backend/views/settings/main-settings.php:96
182
- msgid ""
183
- "Number of DB rows, that are copied within one ajax query.\n"
184
- " The higher the value the faster the database copy process.\n"
185
- " To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it\n"
186
- " until you get no more errors during copying process."
187
- msgstr "Anzahl der Datenbank Einträge, die zugleich kopiert werden. Je höher der Wert, desto schneller wird die Datenbank kopiert. "
188
-
189
- #: apps/Backend/views/clone/single-site/index.php:16
190
- msgid "Report Issue"
191
- msgstr "Fehlermeldung"
192
-
193
- #: apps/Backend/views/clone/single-site/index.php:12
194
- msgid "Cloning"
195
- msgstr "Klonen"
196
-
197
- #: apps/Backend/views/clone/single-site/index.php:8
198
- msgid "Scanning"
199
- msgstr "Scannen"
200
-
201
- #: apps/Backend/views/clone/single-site/index.php:4
202
- msgid "Overview"
203
- msgstr "Überblick"
204
-
205
- #: apps/Backend/views/clone/multi-site/index.php:2
206
- msgid "WordPress Multisite is currently not supported!"
207
- msgstr "WordPress Multisite wird nur WP Staging Pro unterstützt. "
208
-
209
- #: apps/Backend/views/clone/ajax/update.php:17
210
- msgid "Cancel Update"
211
- msgstr "Abbruch Klonvorgang"
212
-
213
- #: apps/Backend/views/clone/ajax/start.php:51
214
- msgid "Important Notes:"
215
- msgstr "Wichtige Hinweise"
216
-
217
- #: apps/Backend/views/clone/ajax/start.php:47
218
- msgid "Start again"
219
- msgstr "Starte Neu"
220
-
221
- #: apps/Backend/views/clone/ajax/start.php:35
222
- msgid "WP Staging successfully created a staging site in a sub-directory of your main site accessable from:<br><strong><a href=\"%1$s\" target=\"_blank\" id=\"wpstg-clone-url-1\">%1$s</a></strong>"
223
- msgstr ""
224
- "WP Staging hat erfolgreich eine Staging Seite erstellt. Erreichbar über \n"
225
- "<br><strong><a href=\"%1$s\" target=\"_blank\" id=\"wpstg-clone-url-1\">%1$s</a></strong>"
226
-
227
- #: apps/Backend/views/clone/ajax/start.php:21
228
- #: apps/Backend/views/clone/ajax/update.php:21
229
- msgid "Display working log"
230
- msgstr "Öffne Log Fenster"
231
-
232
- #: apps/Backend/views/clone/ajax/start.php:2
233
- #: apps/Backend/views/clone/ajax/update.php:2
234
- msgid "Processing, please wait..."
235
- msgstr "Verarbeite, bitte warten..."
236
-
237
- #: apps/Backend/views/clone/ajax/single-overview.php:36
238
- msgid "Delete"
239
- msgstr "Löschen"
240
-
241
- #: apps/Backend/views/clone/ajax/single-overview.php:32
242
- msgid "Update"
243
- msgstr "Aktualisieren"
244
-
245
- #: apps/Backend/views/clone/ajax/single-overview.php:28
246
- msgid "Open"
247
- msgstr "Öffnen"
248
-
249
- #: apps/Backend/views/clone/ajax/single-overview.php:11
250
- msgid "Your Staging Sites:"
251
- msgstr "Deine Staging Seiten"
252
-
253
- #: apps/Backend/views/clone/ajax/single-overview.php:3
254
- msgid "Create new staging site"
255
- msgstr "Erstelle neue Staging Seite"
256
-
257
- #: apps/Backend/views/clone/ajax/scan.php:142
258
- msgid "Check Disk Space"
259
- msgstr "Überprüfe Speicherplatz"
260
-
261
- #: apps/Backend/views/clone/ajax/scan.php:134
262
- msgid "Start Cloning"
263
- msgstr "Starte Klonen"
264
-
265
- #: apps/Backend/views/clone/ajax/scan.php:127
266
- msgid "Update Clone"
267
- msgstr "Aktualisiere Klon"
268
-
269
- #: apps/Backend/views/clone/ajax/scan.php:121
270
- msgid "Back"
271
- msgstr "Zurück"
272
-
273
- #: apps/Backend/views/clone/ajax/scan.php:110
274
- msgid "<strong>If you do not do that step, the staging site could be unavailable!</strong>"
275
- msgstr "<strong>Wenn Du diesen Schritt nicht durchführst könnte die Staging Seite nicht verfügbar sein</strong>"
276
-
277
- #: apps/Backend/views/clone/ajax/scan.php:106
278
- msgid "Set up first <a href=\"%1$s\"><strong>Login Custom Link</strong></a> if login to the admin dashboard is not reachable from the default url below:<pre>%2$s</pre>"
279
- msgstr "Erstelle zunächst einen <a href=\"%1$s\"><strong>Login Custom Link</strong></a> wenn der Login auf das admin Dashboard nicht von der Default URL verfügbar ist:<pre>%2$s</pre>"
280
-
281
- #: apps/Backend/views/clone/ajax/scan.php:104
282
- msgid "<strong>Important:</strong> Are you using a custom login url?"
283
- msgstr "<strong>Wichtig:</strong>Verwendest Du eine benutzerdefinierte Login URL?"
284
-
285
- #: apps/Backend/views/clone/ajax/scan.php:89
286
- msgid "All files will be copied to: "
287
- msgstr "Alle Dateien werden kopiert nach:"
288
-
289
- #: apps/Backend/views/clone/ajax/scan.php:69
290
- msgid "Extra directories to copy"
291
- msgstr "Extra Ordner zum Kopieren"
292
-
293
- #: apps/Backend/views/clone/ajax/scan.php:63
294
- msgid "Select folders to copy. Click on folder name to list subfolders!"
295
- msgstr "Wähle Ordner zum Kopieren aus. Klicke auf Ordnernamen um Unterordner aufzulisten."
296
-
297
- #: apps/Backend/views/clone/ajax/scan.php:58
298
- msgid "Files"
299
- msgstr "Dateien"
300
-
301
- #: apps/Backend/views/clone/ajax/scan.php:17
302
- msgid "DB Tables"
303
- msgstr "Datenbank Tabellen"
304
-
305
- #: apps/Backend/views/clone/ajax/scan.php:2
306
- msgid "Staging Site Name:"
307
- msgstr "Staging Seite Bezeichnung:"
308
-
309
- #: apps/Backend/views/clone/ajax/delete-confirmation.php:85
310
- msgid "Remove"
311
- msgstr "Entferne"
312
-
313
- #: apps/Backend/views/clone/ajax/delete-confirmation.php:81
314
- #: apps/Backend/views/clone/ajax/start.php:17
315
- msgid "Cancel"
316
- msgstr "Abbruch"
317
-
318
- #: apps/Backend/views/clone/ajax/delete-confirmation.php:66
319
- msgid "The folder below and all of its subfolders will be deleted. Unselect the checkbox for not deleting the files."
320
- msgstr "Der ausgewählte Ordner und alle Unterordner werden gelöscht. Abwählen um die Dateien nicht zu löschen."
321
-
322
- #: apps/Backend/views/clone/ajax/delete-confirmation.php:60
323
- msgid "Files to remove"
324
- msgstr "Dateien die entfernt werden"
325
-
326
- #: apps/Backend/views/clone/ajax/delete-confirmation.php:36
327
- msgid "Unselect database tables you do not want to delete:"
328
- msgstr "Wähle die Datenbank Tabellen ab, welche nicht gelöscht werden sollen"
329
-
330
- #: apps/Backend/views/clone/ajax/delete-confirmation.php:30
331
- msgid "DB tables to remove"
332
- msgstr "Datenbank Tabellen die entfernt werden"
333
-
334
- #: apps/Backend/views/clone/ajax/delete-confirmation.php:9
335
- msgid "Clone name:"
336
- msgstr "Klon Bezeichnung"
337
-
338
- #: apps/Backend/views/clone/ajax/delete-confirmation.php:4
339
- msgid "Attention: Check carefully if these database tables and files are safe to delete and do not belong to your live site!"
340
- msgstr "Achtung: Überprüfe sorgfältig ob diese Datenbank Tabellen und Dateien sicher gelöscht werden dürfen und nicht zu Deiner Live Seite gehören!"
341
-
342
- #: apps/Backend/views/_includes/report-issue.php:26
343
- msgid "Send Issue"
344
- msgstr "Abschicken"
345
-
346
- #: apps/Backend/views/_includes/report-issue.php:20
347
- msgid "By submitting, I accept the <a href=\"https://wp-staging.com/privacy-policy/\" target=\"_blank\">Privacy Policy</a> and consent that my email will be stored and processed for the purposes of proving support."
348
- msgstr "Mit Übertragen akzeptiere ich die <a href=\"https://wp-staging.com/privacy-policy/\" target=\"_blank\">Datenschutz Vereinbarung</a> und erlaube, dass meine E-Mail Adresse gespeichert und verarbeitet wird um meine Support Anfrage zu beantworten."
349
-
350
- #: apps/Backend/views/_includes/report-issue.php:12
351
- msgid "Optional: Submit the <a href=\"%s\" target=\"_blank\">System Log</a>. This helps us to resolve your technical issues."
352
- msgstr "Optional: Übertrage das <a href=\"%s\" target=\"_blank\">System Log</a>. Das hilft und Dein technisches Anliegen zu lösen."
353
-
354
- #: apps/Backend/views/_includes/messages/staging-directory-permission-problem.php:4
355
- msgid ""
356
- "WP Staging Folder Permission error:</strong>\n"
357
- " %1$s is not write and/or readable.\n"
358
- " <br>\n"
359
- " Check if the folder <strong>%1$s</strong> is writeable by php user www-data.\n"
360
- " File permissions should be chmod 755 or 777."
361
- msgstr ""
362
- "WP Staging Dateirechte Fehler:</strong>\n"
363
- " %1$s ist nicht schreib und/oder lesbar.\n"
364
- " <br>\n"
365
- " Überprüfe ob der Ordner <strong>%1$s</strong> beschreibbar ist vom PHP Benutzer www-data.\n"
366
- " File permissions should be chmod 755 or 777."
367
-
368
- #: apps/Backend/views/_includes/messages/rating.php:27
369
- msgid "No, not good enough"
370
- msgstr "Nein, nicht gut genug"
371
-
372
- #: apps/Backend/views/_includes/messages/rating.php:22
373
- msgid "I already did"
374
- msgstr "Habe ich schon getan"
375
-
376
- #: apps/Backend/views/_includes/messages/rating.php:17
377
- msgid "Ok, you deserved it"
378
- msgstr "Ok, hast Du verdient"
379
-
380
- #: apps/Backend/views/_includes/messages/rating.php:5
381
- msgid ""
382
- "P.S. Looking for a way to migrate the staging site database and copy plugins and theme files from staging to live site?<br/>\n"
383
- " Try out <a href=\"%1$s\" target=\"_blank\" style=\"color:white;font-weight:bold;\">WP Staging Pro</a>\n"
384
- " "
385
- msgstr ""
386
- "P.S. Suchst Du einen Weg die Staging Seite auf Deine Live Seite zu übertragen?<br/>\n"
387
- "Probiere <a href=\"%1$s\" target=\"_blank\" style=\"color:white;font-weight:bold;\">WP Staging Pro</a>\n"
388
- " aus"
389
-
390
- #: apps/Backend/views/_includes/messages/rating.php:2
391
- msgid ""
392
- " Awesome, you've been using <strong>WP Staging </strong> for more than 1 week.\n"
393
- " May I ask you to give it a <strong>5-star</strong> rating on Wordpress?"
394
- msgstr "Toll, Du verwendest <strong>WP Staging</strong> seit mehr als eine Woche. Darf ich Dich bitten eine 5-Sterne Bewertung auf WordPress.org abzugeben?"
395
-
396
- #: apps/Backend/views/_includes/header.php:28
397
- #: apps/Backend/views/settings/main-settings.php:29
398
- msgid "Share on Facebook"
399
- msgstr "Teile auf Facebook"
400
-
401
- #: apps/Backend/views/_includes/header.php:21
402
- #: apps/Backend/views/settings/main-settings.php:22
403
- msgid "Follow @wpstaging"
404
- msgstr "Folge @wpstaging"
405
-
406
- #: apps/Backend/views/_includes/header.php:14
407
- #: apps/Backend/views/settings/main-settings.php:15
408
- msgid "Tweet #wpstaging"
409
- msgstr "Tweet #wpstaging"
410
-
411
- #: apps/Backend/Pluginmeta/Pluginmeta.php:63
412
- msgid "Start Now"
413
- msgstr "Jetzt Starten"
414
-
415
- #: apps/Backend/Notices/Notices.php:145
416
- #: apps/Backend/views/_includes/messages/transient.php:7
417
- msgid "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging Pro."
418
- msgstr "WP Staging und WP Staging Pro können nicht beide gleichzeitig aktiv sein. Wir haben WP Staging Pro daher deaktiviert."
419
-
420
- #: apps/Backend/Notices/Notices.php:143
421
- #: apps/Backend/views/_includes/messages/transient.php:6
422
- msgid "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging."
423
- msgstr "WP Staging und WP Staging Pro können nicht beide gleichzeitig aktiv sein. Wir haben WP Staging daher deaktiviert."
424
-
425
- #: apps/Backend/Modules/Jobs/Directories.php:421
426
- #: apps/Backend/Modules/Jobs/Multisite/Directories.php:485
427
- msgid "Out of disk space."
428
- msgstr "Kein freier Speicherplatz verfügbar"
429
-
430
- #: apps/Backend/Modules/Jobs/Directories.php:417
431
- #: apps/Backend/Modules/Jobs/Multisite/Directories.php:481
432
- msgid "Unable to write to: %s"
433
- msgstr "Nicht möglich in %s zu schreiben"
434
-
435
- #: apps/Backend/Modules/Jobs/Directories.php:396
436
- #: apps/Backend/Modules/Jobs/Multisite/Directories.php:460
437
- msgid "Unable to open %s with mode %s"
438
- msgstr "Kann nicht öffnen %s mit Modus %s"
439
-
440
- #: apps/Backend/Administrator.php:296
441
- msgid "Please upload a file to import"
442
- msgstr "Bitte lade eine Datei zum importieren hoch"
443
-
444
- #: apps/Backend/Administrator.php:246
445
- msgid "System Info"
446
- msgstr "System Info"
447
-
448
- #: apps/Backend/Administrator.php:245
449
- msgid "Import/Export"
450
- msgstr "Import/Export"
451
-
452
- #: apps/Backend/Administrator.php:208
453
- msgid "General"
454
- msgstr "Allgemein"
455
-
456
- #: apps/Backend/Administrator.php:197
457
- msgid "License"
458
- msgstr "Lizenz"
459
-
460
- #: apps/Backend/Administrator.php:197
461
- msgid "WP Staging License"
462
- msgstr "WP Staging Lizenz"
463
-
464
- #: apps/Backend/Administrator.php:191
465
- msgid "Get WP Staging Pro"
466
- msgstr "Erhalte WP Staging Pro"
467
-
468
- #: apps/Backend/Administrator.php:191
469
- msgid "WP Staging Welcome"
470
- msgstr "WP Staging Willkommen"
471
-
472
- #: apps/Backend/Administrator.php:187
473
- msgid "Tools"
474
- msgstr "Werkzeuge"
475
-
476
- #: apps/Backend/Administrator.php:187
477
- msgid "WP Staging Tools"
478
- msgstr "WP Staging Werkzeuge"
479
-
480
- #: apps/Backend/Administrator.php:182 apps/Backend/Pluginmeta/Pluginmeta.php:42
481
- msgid "Settings"
482
- msgstr "Einstellungen"
483
-
484
- #: apps/Backend/Administrator.php:182
485
- msgid "WP Staging Settings"
486
- msgstr "WP Staging Einstellungen"
487
-
488
- #: apps/Backend/Administrator.php:177
489
- msgid "Sites / Start"
490
- msgstr "Seiten / Start"
491
-
492
- #: apps/Backend/Administrator.php:177
493
- msgid "WP Staging Jobs"
494
- msgstr "WP Staging Jobs"
495
-
496
- #. #-#-#-#-# wp-staging-code.pot (WP Staging 2.3.5) #-#-#-#-#
497
- #. Plugin Name of the plugin/theme
498
- #: apps/Backend/Administrator.php:172
499
- msgid "WP Staging"
500
- msgstr "WP Staging"
501
-
502
- #: apps/Backend/Administrator.php:142
503
- msgid "Settings updated."
504
  msgstr "Einstellungen aktualisiert"
1
+ # Translation of Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable (latest release) in German
2
+ # This file is distributed under the same license as the Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable (latest release) package.
3
+ msgid ""
4
+ msgstr ""
5
+ "PO-Revision-Date: 2018-08-24 20:55:04+0000\n"
6
+ "MIME-Version: 1.0\n"
7
+ "Content-Type: text/plain; charset=UTF-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
+ "X-Generator: GlotPress/2.4.0-alpha\n"
11
+ "Language: de\n"
12
+ "Project-Id-Version: Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable (latest release)\n"
13
+
14
+ #. Author URI of the plugin/theme
15
+ msgid "https://wp-staging.com"
16
+ msgstr "https://wp-staging.com"
17
+
18
+ #. Author of the plugin/theme
19
+ msgid "WP-Staging"
20
+ msgstr "WP-Staging"
21
+
22
+ #. Description of the plugin/theme
23
+ msgid "Create a staging clone site for testing & developing"
24
+ msgstr "Erstelle eine Klon Webseite zum Testen und Entwickeln"
25
+
26
+ #. Plugin URI of the plugin/theme
27
+ msgid "https://wordpress.org/plugins/wp-staging"
28
+ msgstr "https://wordpress.org/plugins/wp-staging"
29
+
30
+ #: apps/Frontend/Frontend.php:88 apps/Frontend/loginForm.php:256
31
+ msgid "Log In"
32
+ msgstr "Anmelden"
33
+
34
+ #: apps/Frontend/Frontend.php:87 apps/Frontend/loginForm.php:255
35
+ msgid "Remember Me"
36
+ msgstr "Erinnere mich"
37
+
38
+ #: apps/Frontend/Frontend.php:86 apps/Frontend/loginForm.php:254
39
+ msgid "Password"
40
+ msgstr "Erinnere mich"
41
+
42
+ #: apps/Frontend/Frontend.php:85 apps/Frontend/loginForm.php:253
43
+ msgid "Username or Email Address"
44
+ msgstr "Benutzername oder E-Mail Adresse"
45
+
46
+ #: apps/Core/Utils/Report.php:31
47
+ msgid "Please accept our privacy policy."
48
+ msgstr "Bitte akzeptiere unsere Datenschutzvereinbarung"
49
+
50
+ #: apps/Core/Utils/Report.php:29
51
+ msgid "Please enter your issue."
52
+ msgstr "Bitte beschreibe Dein Problem"
53
+
54
+ #: apps/Core/Utils/Report.php:27
55
+ msgid "Email address is not valid."
56
+ msgstr "E-Mail Addresse ist nicht gültig"
57
+
58
+ #: apps/Core/Cron/Cron.php:35
59
+ msgid "Once a month"
60
+ msgstr "Einmal im Monat"
61
+
62
+ #: apps/Core/Cron/Cron.php:30
63
+ msgid "Once Weekly"
64
+ msgstr "Einmal wöchentlich"
65
+
66
+ #: apps/Backend/views/welcome/welcome.php:13
67
+ msgid "Comes with our 30-day no questions asked money back guarantee"
68
+ msgstr "Kommt mit unserer 30-Tage Geld zurück Garantie"
69
+
70
+ #: apps/Backend/views/welcome/welcome.php:10
71
+ msgid "WP Staging is coded well for protection of your data"
72
+ msgstr "WP Staging wurde sorgfältig entwickelt um Deine Daten zu schützen"
73
+
74
+ #: apps/Backend/views/welcome/welcome.php:9
75
+ msgid "Cloning process is fast and does not slow down website loading"
76
+ msgstr "Der Klonvorgang ist schnell und beeinflusst nicht die Webseite Ladezeit"
77
+
78
+ #: apps/Backend/views/welcome/welcome.php:8
79
+ msgid "Staging Site is available to authenticated users only"
80
+ msgstr "Staging Webseite ist nur für berechtigte Benutzer verfügbar"
81
+
82
+ #: apps/Backend/views/welcome/welcome.php:7
83
+ msgid "Copy plugin and theme files from staging to live site"
84
+ msgstr "Kopiere Plugin und Theme Dateien von der Staging auf die Live Seite"
85
+
86
+ #: apps/Backend/views/welcome/welcome.php:6
87
+ msgid "Create a clone of your website with a simple click"
88
+ msgstr "Erstelle einen Klon Deiner Webseite mit einem einfachen Klick"
89
+
90
+ #: apps/Backend/views/welcome/welcome.php:4
91
+ msgid " - Copy Themes & Plugins from Staging to Live Site"
92
+ msgstr "Kopiere Themes & Plugins von der Staging auf die Live Seite"
93
+
94
+ #: apps/Backend/views/welcome/welcome.php:4
95
+ msgid "WP Staging Pro"
96
+ msgstr "WP Staging Pro"
97
+
98
+ #: apps/Backend/views/tools/tabs/import_export.php:52
99
+ msgid "Import"
100
+ msgstr "Importieren"
101
+
102
+ #: apps/Backend/views/tools/tabs/import_export.php:33
103
+ msgid "Import Settings"
104
+ msgstr "Importiere Einstellungen"
105
+
106
+ #: apps/Backend/views/tools/tabs/import_export.php:22
107
+ msgid "Export"
108
+ msgstr "Exportieren"
109
+
110
+ #: apps/Backend/views/tools/tabs/import_export.php:5
111
+ msgid "Export Settings"
112
+ msgstr "Exportiere Einstellungen"
113
+
114
+ #: apps/Backend/views/settings/main-settings.php:290
115
+ msgid ""
116
+ "Check this box if you like WP Staging to check sizes of each directory on scanning process.\n"
117
+ " <br>\n"
118
+ " Warning this may cause timeout problems in big directory / file structures."
119
+ msgstr "Lasse WP Staging die Ordnergrösse bei jedem Scan Prozess überprüfen<br>Achtung, dies könnte bei großen Ordner / Datei Strukturen zu Timeout Problemen führen."
120
+
121
+ #: apps/Backend/views/settings/main-settings.php:275
122
+ msgid ""
123
+ "Check this box if you like WP Staging to completely remove all of its data when the plugin is deleted.\n"
124
+ " This will not remove staging sites files or database tables."
125
+ msgstr "Lösche alle Einstellungen beim Löschen des Plugins"
126
+
127
+ #: apps/Backend/views/settings/main-settings.php:261
128
+ msgid "The Optimizer is a mu plugin which disables all other plugins during WP Staging processing. Usually this makes the cloning process more reliable. If you experience issues, disable the Optimizer."
129
+ msgstr "Der Optimizer ist ein mu Plugin welches alle anderen Plugins während des Klonvorgangs deaktiviert. Gewöhnlich macht das den Klonvorgang sehr viel zuverlässiger. Deaktiviere diese Option wenn etwas nicht funktioniert."
130
+
131
+ #: apps/Backend/views/settings/main-settings.php:246
132
+ msgid ""
133
+ "This will enable an extended debug mode which creates additional entries\n"
134
+ " in <strong>wp-content/uploads/wp-staging/logs/logfile.log</strong>.\n"
135
+ " Please enable this when we ask you to do so."
136
+ msgstr "Dieser Modus schreibt zusätzliche Einträge in die Logdatei <strong>wp-content/uploads/wp-staging/logs/logfile.log</strong> Bitte aktiviere diesen Modus wenn Du ein Support Ticket eröffnest und wir Dich darum bitten."
137
+
138
+ #: apps/Backend/views/settings/main-settings.php:196
139
+ msgid ""
140
+ "Using high will result in fast as possible processing but the cpu load\n"
141
+ " increases and it's also possible that staging process gets interrupted because of too many ajax requests\n"
142
+ " (e.g. <strong>authorization error</strong>).\n"
143
+ " Using a lower value results in lower cpu load on your server but also slower staging site creation."
144
+ msgstr "Die Verwendung von \"High\" erstellt die Staging Seite am schnellsten aber die CPU Load erhöht sich und es ist möglich, dass der Staging Prozess aufgrund zu vieler Ajax Anfragen durch einen Timeout unterbrochen wird.Die Verwendungung einer niedrigeren Einstellung führt zu einer geringeren CPU Load, aber auch zu einer langsameren Verarbeitung."
145
+
146
+ #: apps/Backend/views/settings/main-settings.php:175
147
+ msgid ""
148
+ "Buffer size for the file copy process in megabyte.\n"
149
+ " The higher the value the faster large files are copied.\n"
150
+ " To find out the highest possible values try a high one and lower it until\n"
151
+ " you get no errors during file copy process. Usually this value correlates directly\n"
152
+ " with the memory consumption of php so make sure that\n"
153
+ " it does not exceed any php.ini max_memory limits."
154
+ msgstr "Buffer Grösse für den Datei Kopiervorgang in Megabyte. Je größer der Wert, desto schneller werden große Dateien kopiert. Größere Werte beeinflussen auch die maximale Speicherauslastung von PHP und sollte daher nicht zu gross gewählt werden."
155
+
156
+ #: apps/Backend/views/settings/main-settings.php:159
157
+ msgid ""
158
+ "Maximum size of the files which are allowed to copy. All files larger than this value will be skipped. \n"
159
+ " Note: Increase this option only if you have a good reason. Files larger than a few megabytes are in 99% of all cases logging and backup files which are not needed on a staging site."
160
+ msgstr "Maximale Dateigrösse welche noch kopiert wird. Alle Dateien größer als dieser Wert werden übersprungen. Hinweis: Erhöhe diese Option nur wenn Du einen guten Grund dazu hast. Dateien größer als ein einige Megabyte sind in 99% aller Fälle Log und Backup Dateien welche für eine Staging Seite nicht notwendig sind."
161
+
162
+ #: apps/Backend/views/settings/main-settings.php:142
163
+ msgid "<strong>Important:</strong> If CPU Load Priority is <strong>Low</strong> set a file copy limit value of 10 or higher! Otherwise file copying process takes a lot of time."
164
+ msgstr "<strong>Achtung</strong> Wenn die CPU Load Priority <strong>Low</strong> ist, setze das Datei Kopierlimit auf 10 oder höher! Andernfalls wird der Kopiervorgang sehr langsam sein."
165
+
166
+ #: apps/Backend/views/settings/main-settings.php:136
167
+ msgid ""
168
+ "Number of files to copy that will be copied within one ajax request.\n"
169
+ " The higher the value the faster the file copy process.\n"
170
+ " To find out the highest possible values try a high value like 500 or more. If you get timeout issues, lower it\n"
171
+ " until you get no more errors during copying process."
172
+ msgstr "Anzahl der Dateien, die mit einer Ajax Abfrage kopiert werden. Je höher der Wert, desto schneller ist der Kopiervorgang. "
173
+
174
+ #: apps/Backend/views/settings/main-settings.php:116
175
+ msgid ""
176
+ "Number of DB rows, that are processed within one ajax query.\n"
177
+ " The higher the value the faster the database search & replace process.\n"
178
+ " This is a high memory consumptive process. If you get timeouts lower this value!"
179
+ msgstr "Anzahl der Datenbank Einträge welche zugleich verarbeitet werden. Je grösser der Wert desto schneller ist der Suchen & Ersetzen Prozess."
180
+
181
+ #: apps/Backend/views/settings/main-settings.php:96
182
+ msgid ""
183
+ "Number of DB rows, that are copied within one ajax query.\n"
184
+ " The higher the value the faster the database copy process.\n"
185
+ " To find out the highest possible values try a high value like 1.000 or more. If you get timeout issues, lower it\n"
186
+ " until you get no more errors during copying process."
187
+ msgstr "Anzahl der Datenbank Einträge, die zugleich kopiert werden. Je höher der Wert, desto schneller wird die Datenbank kopiert. "
188
+
189
+ #: apps/Backend/views/clone/single-site/index.php:16
190
+ msgid "Report Issue"
191
+ msgstr "Fehlermeldung"
192
+
193
+ #: apps/Backend/views/clone/single-site/index.php:12
194
+ msgid "Cloning"
195
+ msgstr "Klonen"
196
+
197
+ #: apps/Backend/views/clone/single-site/index.php:8
198
+ msgid "Scanning"
199
+ msgstr "Scannen"
200
+
201
+ #: apps/Backend/views/clone/single-site/index.php:4
202
+ msgid "Overview"
203
+ msgstr "Überblick"
204
+
205
+ #: apps/Backend/views/clone/multi-site/index.php:2
206
+ msgid "WordPress Multisite is currently not supported!"
207
+ msgstr "WordPress Multisite wird nur WP Staging Pro unterstützt. "
208
+
209
+ #: apps/Backend/views/clone/ajax/update.php:17
210
+ msgid "Cancel Update"
211
+ msgstr "Abbruch Klonvorgang"
212
+
213
+ #: apps/Backend/views/clone/ajax/start.php:51
214
+ msgid "Important Notes:"
215
+ msgstr "Wichtige Hinweise"
216
+
217
+ #: apps/Backend/views/clone/ajax/start.php:47
218
+ msgid "Start again"
219
+ msgstr "Starte Neu"
220
+
221
+ #: apps/Backend/views/clone/ajax/start.php:35
222
+ msgid "WP Staging successfully created a staging site in a sub-directory of your main site accessable from:<br><strong><a href=\"%1$s\" target=\"_blank\" id=\"wpstg-clone-url-1\">%1$s</a></strong>"
223
+ msgstr ""
224
+ "WP Staging hat erfolgreich eine Staging Seite erstellt. Erreichbar über \n"
225
+ "<br><strong><a href=\"%1$s\" target=\"_blank\" id=\"wpstg-clone-url-1\">%1$s</a></strong>"
226
+
227
+ #: apps/Backend/views/clone/ajax/start.php:21
228
+ #: apps/Backend/views/clone/ajax/update.php:21
229
+ msgid "Display working log"
230
+ msgstr "Öffne Log Fenster"
231
+
232
+ #: apps/Backend/views/clone/ajax/start.php:2
233
+ #: apps/Backend/views/clone/ajax/update.php:2
234
+ msgid "Processing, please wait..."
235
+ msgstr "Verarbeite, bitte warten..."
236
+
237
+ #: apps/Backend/views/clone/ajax/single-overview.php:36
238
+ msgid "Delete"
239
+ msgstr "Löschen"
240
+
241
+ #: apps/Backend/views/clone/ajax/single-overview.php:32
242
+ msgid "Update"
243
+ msgstr "Aktualisieren"
244
+
245
+ #: apps/Backend/views/clone/ajax/single-overview.php:28
246
+ msgid "Open"
247
+ msgstr "Öffnen"
248
+
249
+ #: apps/Backend/views/clone/ajax/single-overview.php:11
250
+ msgid "Your Staging Sites:"
251
+ msgstr "Deine Staging Seiten"
252
+
253
+ #: apps/Backend/views/clone/ajax/single-overview.php:3
254
+ msgid "Create new staging site"
255
+ msgstr "Erstelle neue Staging Seite"
256
+
257
+ #: apps/Backend/views/clone/ajax/scan.php:142
258
+ msgid "Check Disk Space"
259
+ msgstr "Überprüfe Speicherplatz"
260
+
261
+ #: apps/Backend/views/clone/ajax/scan.php:134
262
+ msgid "Start Cloning"
263
+ msgstr "Starte Klonen"
264
+
265
+ #: apps/Backend/views/clone/ajax/scan.php:127
266
+ msgid "Update Clone"
267
+ msgstr "Aktualisiere Klon"
268
+
269
+ #: apps/Backend/views/clone/ajax/scan.php:121
270
+ msgid "Back"
271
+ msgstr "Zurück"
272
+
273
+ #: apps/Backend/views/clone/ajax/scan.php:110
274
+ msgid "<strong>If you do not do that step, the staging site could be unavailable!</strong>"
275
+ msgstr "<strong>Wenn Du diesen Schritt nicht durchführst könnte die Staging Seite nicht verfügbar sein</strong>"
276
+
277
+ #: apps/Backend/views/clone/ajax/scan.php:106
278
+ msgid "Set up first <a href=\"%1$s\"><strong>Login Custom Link</strong></a> if login to the admin dashboard is not reachable from the default url below:<pre>%2$s</pre>"
279
+ msgstr "Erstelle zunächst einen <a href=\"%1$s\"><strong>Login Custom Link</strong></a> wenn der Login auf das admin Dashboard nicht von der Default URL verfügbar ist:<pre>%2$s</pre>"
280
+
281
+ #: apps/Backend/views/clone/ajax/scan.php:104
282
+ msgid "<strong>Important:</strong> Are you using a custom login url?"
283
+ msgstr "<strong>Wichtig:</strong>Verwendest Du eine benutzerdefinierte Login URL?"
284
+
285
+ #: apps/Backend/views/clone/ajax/scan.php:89
286
+ msgid "All files will be copied to: "
287
+ msgstr "Alle Dateien werden kopiert nach:"
288
+
289
+ #: apps/Backend/views/clone/ajax/scan.php:69
290
+ msgid "Extra directories to copy"
291
+ msgstr "Extra Ordner zum Kopieren"
292
+
293
+ #: apps/Backend/views/clone/ajax/scan.php:63
294
+ msgid "Select folders to copy. Click on folder name to list subfolders!"
295
+ msgstr "Wähle Ordner zum Kopieren aus. Klicke auf Ordnernamen um Unterordner aufzulisten."
296
+
297
+ #: apps/Backend/views/clone/ajax/scan.php:58
298
+ msgid "Files"
299
+ msgstr "Dateien"
300
+
301
+ #: apps/Backend/views/clone/ajax/scan.php:17
302
+ msgid "DB Tables"
303
+ msgstr "Datenbank Tabellen"
304
+
305
+ #: apps/Backend/views/clone/ajax/scan.php:2
306
+ msgid "Staging Site Name:"
307
+ msgstr "Staging Seite Bezeichnung:"
308
+
309
+ #: apps/Backend/views/clone/ajax/delete-confirmation.php:85
310
+ msgid "Remove"
311
+ msgstr "Entferne"
312
+
313
+ #: apps/Backend/views/clone/ajax/delete-confirmation.php:81
314
+ #: apps/Backend/views/clone/ajax/start.php:17
315
+ msgid "Cancel"
316
+ msgstr "Abbruch"
317
+
318
+ #: apps/Backend/views/clone/ajax/delete-confirmation.php:66
319
+ msgid "The folder below and all of its subfolders will be deleted. Unselect the checkbox for not deleting the files."
320
+ msgstr "Der ausgewählte Ordner und alle Unterordner werden gelöscht. Abwählen um die Dateien nicht zu löschen."
321
+
322
+ #: apps/Backend/views/clone/ajax/delete-confirmation.php:60
323
+ msgid "Files to remove"
324
+ msgstr "Dateien die entfernt werden"
325
+
326
+ #: apps/Backend/views/clone/ajax/delete-confirmation.php:36
327
+ msgid "Unselect database tables you do not want to delete:"
328
+ msgstr "Wähle die Datenbank Tabellen ab, welche nicht gelöscht werden sollen"
329
+
330
+ #: apps/Backend/views/clone/ajax/delete-confirmation.php:30
331
+ msgid "DB tables to remove"
332
+ msgstr "Datenbank Tabellen die entfernt werden"
333
+
334
+ #: apps/Backend/views/clone/ajax/delete-confirmation.php:9
335
+ msgid "Clone name:"
336
+ msgstr "Klon Bezeichnung"
337
+
338
+ #: apps/Backend/views/clone/ajax/delete-confirmation.php:4
339
+ msgid "Attention: Check carefully if these database tables and files are safe to delete and do not belong to your live site!"
340
+ msgstr "Achtung: Überprüfe sorgfältig ob diese Datenbank Tabellen und Dateien sicher gelöscht werden dürfen und nicht zu Deiner Live Seite gehören!"
341
+
342
+ #: apps/Backend/views/_includes/report-issue.php:26
343
+ msgid "Send Issue"
344
+ msgstr "Abschicken"
345
+
346
+ #: apps/Backend/views/_includes/report-issue.php:20
347
+ msgid "By submitting, I accept the <a href=\"https://wp-staging.com/privacy-policy/\" target=\"_blank\">Privacy Policy</a> and consent that my email will be stored and processed for the purposes of proving support."
348
+ msgstr "Mit Übertragen akzeptiere ich die <a href=\"https://wp-staging.com/privacy-policy/\" target=\"_blank\">Datenschutz Vereinbarung</a> und erlaube, dass meine E-Mail Adresse gespeichert und verarbeitet wird um meine Support Anfrage zu beantworten."
349
+
350
+ #: apps/Backend/views/_includes/report-issue.php:12
351
+ msgid "Optional: Submit the <a href=\"%s\" target=\"_blank\">System Log</a>. This helps us to resolve your technical issues."
352
+ msgstr "Optional: Übertrage das <a href=\"%s\" target=\"_blank\">System Log</a>. Das hilft und Dein technisches Anliegen zu lösen."
353
+
354
+ #: apps/Backend/views/_includes/messages/staging-directory-permission-problem.php:4
355
+ msgid ""
356
+ "WP Staging Folder Permission error:</strong>\n"
357
+ " %1$s is not write and/or readable.\n"
358
+ " <br>\n"
359
+ " Check if the folder <strong>%1$s</strong> is writeable by php user www-data.\n"
360
+ " File permissions should be chmod 755 or 777."
361
+ msgstr ""
362
+ "WP Staging Dateirechte Fehler:</strong>\n"
363
+ " %1$s ist nicht schreib und/oder lesbar.\n"
364
+ " <br>\n"
365
+ " Überprüfe ob der Ordner <strong>%1$s</strong> beschreibbar ist vom PHP Benutzer www-data.\n"
366
+ " File permissions should be chmod 755 or 777."
367
+
368
+ #: apps/Backend/views/_includes/messages/rating.php:27
369
+ msgid "No, not good enough"
370
+ msgstr "Nein, nicht gut genug"
371
+
372
+ #: apps/Backend/views/_includes/messages/rating.php:22
373
+ msgid "I already did"
374
+ msgstr "Habe ich schon getan"
375
+
376
+ #: apps/Backend/views/_includes/messages/rating.php:17
377
+ msgid "Ok, you deserved it"
378
+ msgstr "Ok, hast Du verdient"
379
+
380
+ #: apps/Backend/views/_includes/messages/rating.php:5
381
+ msgid ""
382
+ "P.S. Looking for a way to migrate the staging site database and copy plugins and theme files from staging to live site?<br/>\n"
383
+ " Try out <a href=\"%1$s\" target=\"_blank\" style=\"color:white;font-weight:bold;\">WP Staging Pro</a>\n"
384
+ " "
385
+ msgstr ""
386
+ "P.S. Suchst Du einen Weg die Staging Seite auf Deine Live Seite zu übertragen?<br/>\n"
387
+ "Probiere <a href=\"%1$s\" target=\"_blank\" style=\"color:white;font-weight:bold;\">WP Staging Pro</a>\n"
388
+ " aus"
389
+
390
+ #: apps/Backend/views/_includes/messages/rating.php:2
391
+ msgid ""
392
+ " Awesome, you've been using <strong>WP Staging </strong> for more than 1 week.\n"
393
+ " May I ask you to give it a <strong>5-star</strong> rating on Wordpress?"
394
+ msgstr "Toll, Du verwendest <strong>WP Staging</strong> seit mehr als eine Woche. Darf ich Dich bitten eine 5-Sterne Bewertung auf WordPress.org abzugeben?"
395
+
396
+ #: apps/Backend/views/_includes/header.php:28
397
+ #: apps/Backend/views/settings/main-settings.php:29
398
+ msgid "Share on Facebook"
399
+ msgstr "Teile auf Facebook"
400
+
401
+ #: apps/Backend/views/_includes/header.php:21
402
+ #: apps/Backend/views/settings/main-settings.php:22
403
+ msgid "Follow @wpstaging"
404
+ msgstr "Folge @wpstaging"
405
+
406
+ #: apps/Backend/views/_includes/header.php:14
407
+ #: apps/Backend/views/settings/main-settings.php:15
408
+ msgid "Tweet #wpstaging"
409
+ msgstr "Tweet #wpstaging"
410
+
411
+ #: apps/Backend/Pluginmeta/Pluginmeta.php:63
412
+ msgid "Start Now"
413
+ msgstr "Jetzt Starten"
414
+
415
+ #: apps/Backend/Notices/Notices.php:145
416
+ #: apps/Backend/views/_includes/messages/transient.php:7
417
+ msgid "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging Pro."
418
+ msgstr "WP Staging und WP Staging Pro können nicht beide gleichzeitig aktiv sein. Wir haben WP Staging Pro daher deaktiviert."
419
+
420
+ #: apps/Backend/Notices/Notices.php:143
421
+ #: apps/Backend/views/_includes/messages/transient.php:6
422
+ msgid "WP Staging and WP Staging Pro cannot both be active. We've automatically deactivated WP Staging."
423
+ msgstr "WP Staging und WP Staging Pro können nicht beide gleichzeitig aktiv sein. Wir haben WP Staging daher deaktiviert."
424
+
425
+ #: apps/Backend/Modules/Jobs/Directories.php:421
426
+ #: apps/Backend/Modules/Jobs/Multisite/Directories.php:485
427
+ msgid "Out of disk space."
428
+ msgstr "Kein freier Speicherplatz verfügbar"
429
+
430
+ #: apps/Backend/Modules/Jobs/Directories.php:417
431
+ #: apps/Backend/Modules/Jobs/Multisite/Directories.php:481
432
+ msgid "Unable to write to: %s"
433
+ msgstr "Nicht möglich in %s zu schreiben"
434
+
435
+ #: apps/Backend/Modules/Jobs/Directories.php:396
436
+ #: apps/Backend/Modules/Jobs/Multisite/Directories.php:460
437
+ msgid "Unable to open %s with mode %s"
438
+ msgstr "Kann nicht öffnen %s mit Modus %s"
439
+
440
+ #: apps/Backend/Administrator.php:296
441
+ msgid "Please upload a file to import"
442
+ msgstr "Bitte lade eine Datei zum importieren hoch"
443
+
444
+ #: apps/Backend/Administrator.php:246
445
+ msgid "System Info"
446
+ msgstr "System Info"
447
+
448
+ #: apps/Backend/Administrator.php:245
449
+ msgid "Import/Export"
450
+ msgstr "Import/Export"
451
+
452
+ #: apps/Backend/Administrator.php:208
453
+ msgid "General"
454
+ msgstr "Allgemein"
455
+
456
+ #: apps/Backend/Administrator.php:197
457
+ msgid "License"
458
+ msgstr "Lizenz"
459
+
460
+ #: apps/Backend/Administrator.php:197
461
+ msgid "WP Staging License"
462
+ msgstr "WP Staging Lizenz"
463
+
464
+ #: apps/Backend/Administrator.php:191
465
+ msgid "Get WP Staging Pro"
466
+ msgstr "Erhalte WP Staging Pro"
467
+
468
+ #: apps/Backend/Administrator.php:191
469
+ msgid "WP Staging Welcome"
470
+ msgstr "WP Staging Willkommen"
471
+
472
+ #: apps/Backend/Administrator.php:187
473
+ msgid "Tools"
474
+ msgstr "Werkzeuge"
475
+
476
+ #: apps/Backend/Administrator.php:187
477
+ msgid "WP Staging Tools"
478
+ msgstr "WP Staging Werkzeuge"
479
+
480
+ #: apps/Backend/Administrator.php:182 apps/Backend/Pluginmeta/Pluginmeta.php:42
481
+ msgid "Settings"
482
+ msgstr "Einstellungen"
483
+
484
+ #: apps/Backend/Administrator.php:182
485
+ msgid "WP Staging Settings"
486
+ msgstr "WP Staging Einstellungen"
487
+
488
+ #: apps/Backend/Administrator.php:177
489
+ msgid "Sites / Start"
490
+ msgstr "Seiten / Start"
491
+
492
+ #: apps/Backend/Administrator.php:177
493
+ msgid "WP Staging Jobs"
494
+ msgstr "WP Staging Jobs"
495
+
496
+ #. #-#-#-#-# wp-staging-code.pot (WP Staging 2.3.5) #-#-#-#-#
497
+ #. Plugin Name of the plugin/theme
498
+ #: apps/Backend/Administrator.php:172
499
+ msgid "WP Staging"
500
+ msgstr "WP Staging"
501
+
502
+ #: apps/Backend/Administrator.php:142
503
+ msgid "Settings updated."
504
  msgstr "Einstellungen aktualisiert"
languages/wp-staging-en_gb.po CHANGED
@@ -1,418 +1,418 @@
1
- # Translation of Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) in English (UK)
2
- # This file is distributed under the same license as the Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) package.
3
- msgid ""
4
- msgstr ""
5
- "PO-Revision-Date: 2015-12-01 15:35:13+0000\n"
6
- "MIME-Version: 1.0\n"
7
- "Content-Type: text/plain; charset=UTF-8\n"
8
- "Content-Transfer-Encoding: 8bit\n"
9
- "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
- "X-Generator: GlotPress/2.4.0-alpha\n"
11
- "Language: en_GB\n"
12
- "Project-Id-Version: Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release)\n"
13
-
14
- #. Short description.
15
- msgid "A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators."
16
- msgstr ""
17
-
18
- #. Plugin name.
19
- msgid "WP Staging - DB &amp; File Duplicator &amp; Migration"
20
- msgstr ""
21
-
22
- #. Found in changelog list item.
23
- msgid "Fix: Missing http(s) scheme after cloning multisites results in not working clones"
24
- msgstr ""
25
-
26
- #. Found in changelog list item.
27
- msgid "New: Support for Windows Azure cloud servers"
28
- msgstr ""
29
-
30
- #. Found in changelog list item.
31
- msgid "Fix: Do not search &amp; replace mail addresses"
32
- msgstr ""
33
-
34
- #. Found in changelog list item.
35
- msgid "Fix: Remove term 'Error' from login page"
36
- msgstr ""
37
-
38
- #. Found in changelog list item.
39
- msgid "New: Login options not needed any more - removed"
40
- msgstr ""
41
-
42
- #. Found in changelog list item.
43
- msgid "New: Detect if wp-config.php has been moved one folder level up"
44
- msgstr ""
45
-
46
- #. Found in changelog list item.
47
- msgid "New: Better error reporting"
48
- msgstr ""
49
-
50
- #. Found in changelog list item.
51
- msgid "New: Compatible up to WordPress 4.9.8"
52
- msgstr ""
53
-
54
- #. Found in changelog list item.
55
- msgid "New: Setting to specify the maximum allowed file size to copy"
56
- msgstr ""
57
-
58
- #. Found in changelog list item.
59
- msgid "Fix: Search &amp; replace path not correct if scheme http/https is not identical in wp-config and db wp_options"
60
- msgstr ""
61
-
62
- #. Found in changelog list item.
63
- msgid "Fix: Creating a new clone resets the custom wp staging user settings. E.g. ignores authentication setting"
64
- msgstr ""
65
-
66
- #. Found in changelog list item.
67
- msgid "Tweak: Remove certain debugging notices from the default log window"
68
- msgstr ""
69
-
70
- #. Found in changelog list item.
71
- msgid "Tweaks: remove term \"error\" from several log entries"
72
- msgstr ""
73
-
74
- #. Found in changelog list item.
75
- msgid "Fix: Search &amp; Replace not working if serialized object contains __PHP_Incomplete_Class_Name"
76
- msgstr ""
77
-
78
- #. Found in changelog list item.
79
- msgid "Fix: Skip search &amp; replace for objects where key is null"
80
- msgstr ""
81
-
82
- #. Found in changelog list item.
83
- msgid "Fix: Parameter must be an array or an object that implements Callable"
84
- msgstr ""
85
-
86
- #. Found in changelog list item.
87
- msgid "Fix: wp-config.php not copied when previous clone updating process has been failed"
88
- msgstr ""
89
-
90
- #. Found in changelog list item.
91
- msgid "Fix: Undefined object $this-&gt;tables"
92
- msgstr ""
93
-
94
- #. Found in changelog list item.
95
- msgid "Fix: Can not open upload folder in file selection menu"
96
- msgstr ""
97
-
98
- #. Found in changelog list item.
99
- msgid "Fix: Progress bar not shown as intented for clone updating process"
100
- msgstr ""
101
-
102
- #. Found in changelog list item.
103
- msgid "Fix: If cloning update process is interupted it may happen that staging site is not available any longer. (Updating the clone does not copy index.php to staging site again)"
104
- msgstr ""
105
-
106
- #. Found in changelog list item.
107
- msgid "Fix: Progress bar for step 'database' is not filling up to 100%"
108
- msgstr ""
109
-
110
- #. Found in changelog list item.
111
- msgid "New: Use the new progress bar for clone updating process"
112
- msgstr ""
113
-
114
- #. Found in changelog list item.
115
- msgid "New: Add multisite informations in system info log"
116
- msgstr ""
117
-
118
- #. Found in changelog list item.
119
- msgid "New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning."
120
- msgstr ""
121
-
122
- #. Found in changelog list item.
123
- msgid "Security: Do not allow to create a new staging site into a subfolder which already exists"
124
- msgstr ""
125
-
126
- #. Found in changelog list item.
127
- msgid "Fix: Missing trailingslash results to wrong absolute paths in database after Search &amp; Replace operation "
128
- msgstr ""
129
-
130
- #. Found in changelog list item.
131
- msgid "New: Compatible to WordPress 4.9.6"
132
- msgstr ""
133
-
134
- #. Found in changelog list item.
135
- msgid "New: Add new setting which allow to specify the search &amp; replace processing query limit"
136
- msgstr ""
137
-
138
- #. Found in changelog list item.
139
- msgid "New: Supports search &amp; replace for revslider image slider and several visual editors which are using non default serialized data"
140
- msgstr ""
141
-
142
- #. Found in changelog list item.
143
- msgid "New: Add filter 'wpstg_fiter_search_replace_rows' to exclude certain tables from search &amp; replace"
144
- msgstr ""
145
-
146
- #. Found in changelog list item.
147
- msgid "New: Add datetime timestamp internally to clone. (Used in WP Staging pro)"
148
- msgstr ""
149
-
150
- #. Found in changelog list item.
151
- msgid "New: Support for custom upload folder. For instance, if upload folder has been renamed and removed outsite wp-content folder"
152
- msgstr ""
153
-
154
- #. Found in changelog list item.
155
- msgid "New: Exclude tables for plugin wp_mail_smtp"
156
- msgstr ""
157
-
158
- #. Found in changelog list item.
159
- msgid "New: Add filter 'wpstg_filter_options_replace' to exclude certain tables from updating while cloning"
160
- msgstr ""
161
-
162
- #. Found in changelog list item.
163
- msgid "Fix: WP_SITEURL &amp; WP_HOME not replaced if constant contains php generated string"
164
- msgstr ""
165
-
166
- #. Found in changelog list item.
167
- msgid "Fix: Serialize replace is not working properly for serialized strings"
168
- msgstr ""
169
-
170
- #. Found in faq list item.
171
- msgid ""
172
- "I can not login to the staging site\n"
173
- "If you are using a security plugin like All In One WP Security &amp; Firewall you need to install latest version of WP Staging. \n"
174
- "Go to WP Staging &gt; Settings and add the slug to the custom login page which you set up in All In One WP Security &amp; Firewall plugin."
175
- msgstr ""
176
-
177
- #. Found in installation list item, faq list item.
178
- msgid "Upload and install it via the WordPress plugin backend wp-admin &gt; plugins &gt; add new &gt; uploads"
179
- msgstr ""
180
-
181
- #. Found in installation list item, faq list item.
182
- msgid "Download the file \"wp-staging.zip\":"
183
- msgstr ""
184
-
185
- #. Found in description paragraph.
186
- msgid ""
187
- "Permalinks are disabled on the staging site because the staging site is cloned into a subfolder and permalinks are not working on all systems\n"
188
- "without doing changes to the .htaccess (Apache server) or nginx.conf (Nginx Server).\n"
189
- "<a href=\"https://wp-staging.com/docs/activate-permalinks-staging-site/\" title=\"activate permalinks on staging site\">Read here</a> how to activate permalinks on the staging site."
190
- msgstr ""
191
-
192
- #. Found in description list item.
193
- msgid "New: Compatible to All In One WP Security &amp; Firewall"
194
- msgstr ""
195
-
196
- #. Found in description paragraph.
197
- msgid "Note: WordPress 5.0 will be shipped with a new visual editor called Gutenberg. Use WP Staging to check if Gutenberg editor is working as intended on your website and that all used plugins are compatible with that new editor."
198
- msgstr ""
199
-
200
- #. Found in changelog paragraph.
201
- msgid "Complete changelog: <a href=\"https://wp-staging.com/changelog.txt\">https://wp-staging.com/changelog.txt</a>"
202
- msgstr ""
203
-
204
- #. Found in description paragraph.
205
- msgid "https://wp-staging.com"
206
- msgstr ""
207
-
208
- #. Found in description paragraph.
209
- msgid ""
210
- "<strong>This cloning and staging plugin is well tested and work in progress. <br /><br />\n"
211
- "If you find any issue, please open a <a href=\"https://wp-staging.com/support/\" title=\"support ticket\">support ticket</a>.\n"
212
- "</strong>\n"
213
- "<br /><br />\n"
214
- "<strong>Note: </strong> For pushing &amp; migrating plugins and theme files to live site, check out the pro version <a href=\"https://wp-staging.com/\" title=\"WP Staging Pro\">https://wp-staging.com/</a>\n"
215
- "<br /><br />"
216
- msgstr ""
217
-
218
- #. Found in faq header.
219
- msgid "Installation Instructions"
220
- msgstr ""
221
-
222
- #. Found in description list item.
223
- msgid ""
224
- "WordPress duplicating process on windows server (not tested but will probably work) \n"
225
- "Edit: Duplication on windows server seems to be working well: <a href=\"https://wordpress.org/support/topic/wont-copy-files?replies=5\" title=\"Read more\">Read more</a> "
226
- msgstr ""
227
-
228
- #. Found in description list item.
229
- msgid ""
230
- "<strong>Safe: </strong> Access to staging site is granted for administrators only.\n"
231
- "<br /><br />\n"
232
- "<strong>More safe:</strong> \n"
233
- "<br />"
234
- msgstr ""
235
-
236
- #. Found in description paragraph.
237
- msgid "[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]"
238
- msgstr ""
239
-
240
- #. Found in description header.
241
- msgid "Official Site"
242
- msgstr ""
243
-
244
- #. Found in installation list item, faq list item.
245
- msgid "Activate the plugin through the 'Plugins' menu in WordPress."
246
- msgstr "Activate the plugin through the 'Plugins' menu in WordPress."
247
-
248
- #. Found in description paragraph.
249
- msgid ""
250
- "Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.\n"
251
- "After installation goto the settings page 'Staging' and do your adjustments there."
252
- msgstr ""
253
-
254
- #. Found in description paragraph.
255
- msgid ""
256
- "This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:\n"
257
- "Please open a <a href=\"https://wordpress.org/support/plugin/wp-staging/\" title=\"support request\">support request</a> and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines."
258
- msgstr ""
259
-
260
- #. Found in description paragraph.
261
- msgid ""
262
- "So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using\n"
263
- "the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!"
264
- msgstr ""
265
-
266
- #. Found in description paragraph.
267
- msgid ""
268
- "At first, i love the <a href=\"https://wordpress.org/plugins/duplicator/\" title=\"Duplicator plugin\">Duplicator plugin</a>. Duplicator is a great tool for migrating from development site to production one or from production site to development one. \n"
269
- "The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.\n"
270
- "However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful."
271
- msgstr ""
272
-
273
- #. Found in description paragraph.
274
- msgid ""
275
- "If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.\n"
276
- "WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.\n"
277
- "Both tools are excellent cooperating eachother."
278
- msgstr ""
279
-
280
- #. Found in description paragraph.
281
- msgid "This is were WP Staging steps in... Site cloning and staging site creation simplified!"
282
- msgstr ""
283
-
284
- #. Found in description paragraph.
285
- msgid ""
286
- "Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect \n"
287
- "of your local copy is working on your live website exactely as you would expect it. \n"
288
- "There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the \n"
289
- "the cpu performance can lead to unexpected results on your production website. \n"
290
- "There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform."
291
- msgstr ""
292
-
293
- #. Found in description paragraph.
294
- msgid "<strong> I think its time to change this, so i created \"WP Staging\" for WordPress migration of staging sites</strong>"
295
- msgstr ""
296
-
297
- #. Found in description paragraph.
298
- msgid ""
299
- "Some people are also afraid of installing plugins updates because they follow the rule \"never touch a running system\" with having in mind that untested updates are increasing the risk of breaking their site.\n"
300
- "I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior."
301
- msgstr ""
302
-
303
- #. Found in description paragraph.
304
- msgid ""
305
- "Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a \n"
306
- "up-to-date copy of your website."
307
- msgstr ""
308
-
309
- #. Found in description paragraph.
310
- msgid ""
311
- "Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.\n"
312
- "When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.\n"
313
- "This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)"
314
- msgstr ""
315
-
316
- #. Found in description paragraph.
317
- msgid "<strong>Change your workflow of updating themes and plugins data:</strong>"
318
- msgstr ""
319
-
320
- #. Found in description paragraph.
321
- msgid "WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!"
322
- msgstr ""
323
-
324
- #. Found in description paragraph.
325
- msgid "<em>* Time of creation depends on size of your database and file size</em>"
326
- msgstr ""
327
-
328
- #. Found in description list item.
329
- msgid "Everything running as expected? You are on the save side for migration of all these modifications to your production site!"
330
- msgstr ""
331
-
332
- #. Found in description list item.
333
- msgid "Test everything on your staging site first"
334
- msgstr ""
335
-
336
- #. Found in description list item.
337
- msgid "Customize theme, configuration and plugins or install new plugins"
338
- msgstr ""
339
-
340
- #. Found in description list item.
341
- msgid "Use WP Staging for migration of a production website to a clone site for staging purposes"
342
- msgstr ""
343
-
344
- #. Found in description list item.
345
- msgid "Wordpress migration of wordpress multisites (not tested)"
346
- msgstr ""
347
-
348
- #. Found in description list item.
349
- msgid "Extensive logging if duplication or migration process should fail."
350
- msgstr ""
351
-
352
- #. Found in description list item.
353
- msgid "Admin bar reflects that you are working on a staging site"
354
- msgstr ""
355
-
356
- #. Found in description list item.
357
- msgid "<strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power"
358
- msgstr ""
359
-
360
- #. Found in description list item.
361
- msgid "<strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!"
362
- msgstr ""
363
-
364
- #. Found in description header.
365
- msgid "How to install and setup?"
366
- msgstr ""
367
-
368
- #. Found in description header.
369
- msgid "Important"
370
- msgstr ""
371
-
372
- #. Found in description header.
373
- msgid "I need you feedback"
374
- msgstr ""
375
-
376
- #. Found in description header.
377
- msgid "What are the benefits compared to a plugin like Duplicator?"
378
- msgstr ""
379
-
380
- #. Found in description header.
381
- msgid "I just want to migrate the database from one installation to another"
382
- msgstr ""
383
-
384
- #. Found in description header.
385
- msgid " Can´t i just use my local wordpress development copy for testing like xampp / lampp? "
386
- msgstr ""
387
-
388
- #. Found in description header.
389
- msgid " Why should i use a staging website? "
390
- msgstr ""
391
-
392
- #. Found in description header.
393
- msgid "What does not work or is not tested when running wordpress migration?"
394
- msgstr ""
395
-
396
- #. Found in description header.
397
- msgid "Main Features"
398
- msgstr ""
399
-
400
- #. Found in description header.
401
- msgid " WP Staging for WordPress Migration "
402
- msgstr ""
403
-
404
- #. Screenshot description.
405
- msgid "Finish!"
406
- msgstr ""
407
-
408
- #. Screenshot description.
409
- msgid "Step 3. Wordpress Staging site creation in progress"
410
- msgstr ""
411
-
412
- #. Screenshot description.
413
- msgid "Step 2. Scanning your website for files and database tables"
414
- msgstr ""
415
-
416
- #. Screenshot description.
417
- msgid "Step 1. Create new WordPress staging site"
418
  msgstr ""
1
+ # Translation of Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) in English (UK)
2
+ # This file is distributed under the same license as the Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) package.
3
+ msgid ""
4
+ msgstr ""
5
+ "PO-Revision-Date: 2015-12-01 15:35:13+0000\n"
6
+ "MIME-Version: 1.0\n"
7
+ "Content-Type: text/plain; charset=UTF-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
+ "X-Generator: GlotPress/2.4.0-alpha\n"
11
+ "Language: en_GB\n"
12
+ "Project-Id-Version: Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release)\n"
13
+
14
+ #. Short description.
15
+ msgid "A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators."
16
+ msgstr ""
17
+
18
+ #. Plugin name.
19
+ msgid "WP Staging - DB &amp; File Duplicator &amp; Migration"
20
+ msgstr ""
21
+
22
+ #. Found in changelog list item.
23
+ msgid "Fix: Missing http(s) scheme after cloning multisites results in not working clones"
24
+ msgstr ""
25
+
26
+ #. Found in changelog list item.
27
+ msgid "New: Support for Windows Azure cloud servers"
28
+ msgstr ""
29
+
30
+ #. Found in changelog list item.
31
+ msgid "Fix: Do not search &amp; replace mail addresses"
32
+ msgstr ""
33
+
34
+ #. Found in changelog list item.
35
+ msgid "Fix: Remove term 'Error' from login page"
36
+ msgstr ""
37
+
38
+ #. Found in changelog list item.
39
+ msgid "New: Login options not needed any more - removed"
40
+ msgstr ""
41
+
42
+ #. Found in changelog list item.
43
+ msgid "New: Detect if wp-config.php has been moved one folder level up"
44
+ msgstr ""
45
+
46
+ #. Found in changelog list item.
47
+ msgid "New: Better error reporting"
48
+ msgstr ""
49
+
50
+ #. Found in changelog list item.
51
+ msgid "New: Compatible up to WordPress 4.9.8"
52
+ msgstr ""
53
+
54
+ #. Found in changelog list item.
55
+ msgid "New: Setting to specify the maximum allowed file size to copy"
56
+ msgstr ""
57
+
58
+ #. Found in changelog list item.
59
+ msgid "Fix: Search &amp; replace path not correct if scheme http/https is not identical in wp-config and db wp_options"
60
+ msgstr ""
61
+
62
+ #. Found in changelog list item.
63
+ msgid "Fix: Creating a new clone resets the custom wp staging user settings. E.g. ignores authentication setting"
64
+ msgstr ""
65
+
66
+ #. Found in changelog list item.
67
+ msgid "Tweak: Remove certain debugging notices from the default log window"
68
+ msgstr ""
69
+
70
+ #. Found in changelog list item.
71
+ msgid "Tweaks: remove term \"error\" from several log entries"
72
+ msgstr ""
73
+
74
+ #. Found in changelog list item.
75
+ msgid "Fix: Search &amp; Replace not working if serialized object contains __PHP_Incomplete_Class_Name"
76
+ msgstr ""
77
+
78
+ #. Found in changelog list item.
79
+ msgid "Fix: Skip search &amp; replace for objects where key is null"
80
+ msgstr ""
81
+
82
+ #. Found in changelog list item.
83
+ msgid "Fix: Parameter must be an array or an object that implements Callable"
84
+ msgstr ""
85
+
86
+ #. Found in changelog list item.
87
+ msgid "Fix: wp-config.php not copied when previous clone updating process has been failed"
88
+ msgstr ""
89
+
90
+ #. Found in changelog list item.
91
+ msgid "Fix: Undefined object $this-&gt;tables"
92
+ msgstr ""
93
+
94
+ #. Found in changelog list item.
95
+ msgid "Fix: Can not open upload folder in file selection menu"
96
+ msgstr ""
97
+
98
+ #. Found in changelog list item.
99
+ msgid "Fix: Progress bar not shown as intented for clone updating process"
100
+ msgstr ""
101
+
102
+ #. Found in changelog list item.
103
+ msgid "Fix: If cloning update process is interupted it may happen that staging site is not available any longer. (Updating the clone does not copy index.php to staging site again)"
104
+ msgstr ""
105
+
106
+ #. Found in changelog list item.
107
+ msgid "Fix: Progress bar for step 'database' is not filling up to 100%"
108
+ msgstr ""
109
+
110
+ #. Found in changelog list item.
111
+ msgid "New: Use the new progress bar for clone updating process"
112
+ msgstr ""
113
+
114
+ #. Found in changelog list item.
115
+ msgid "New: Add multisite informations in system info log"
116
+ msgstr ""
117
+
118
+ #. Found in changelog list item.
119
+ msgid "New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning."
120
+ msgstr ""
121
+
122
+ #. Found in changelog list item.
123
+ msgid "Security: Do not allow to create a new staging site into a subfolder which already exists"
124
+ msgstr ""
125
+
126
+ #. Found in changelog list item.
127
+ msgid "Fix: Missing trailingslash results to wrong absolute paths in database after Search &amp; Replace operation "
128
+ msgstr ""
129
+
130
+ #. Found in changelog list item.
131
+ msgid "New: Compatible to WordPress 4.9.6"
132
+ msgstr ""
133
+
134
+ #. Found in changelog list item.
135
+ msgid "New: Add new setting which allow to specify the search &amp; replace processing query limit"
136
+ msgstr ""
137
+
138
+ #. Found in changelog list item.
139
+ msgid "New: Supports search &amp; replace for revslider image slider and several visual editors which are using non default serialized data"
140
+ msgstr ""
141
+
142
+ #. Found in changelog list item.
143
+ msgid "New: Add filter 'wpstg_fiter_search_replace_rows' to exclude certain tables from search &amp; replace"
144
+ msgstr ""
145
+
146
+ #. Found in changelog list item.
147
+ msgid "New: Add datetime timestamp internally to clone. (Used in WP Staging pro)"
148
+ msgstr ""
149
+
150
+ #. Found in changelog list item.
151
+ msgid "New: Support for custom upload folder. For instance, if upload folder has been renamed and removed outsite wp-content folder"
152
+ msgstr ""
153
+
154
+ #. Found in changelog list item.
155
+ msgid "New: Exclude tables for plugin wp_mail_smtp"
156
+ msgstr ""
157
+
158
+ #. Found in changelog list item.
159
+ msgid "New: Add filter 'wpstg_filter_options_replace' to exclude certain tables from updating while cloning"
160
+ msgstr ""
161
+
162
+ #. Found in changelog list item.
163
+ msgid "Fix: WP_SITEURL &amp; WP_HOME not replaced if constant contains php generated string"
164
+ msgstr ""
165
+
166
+ #. Found in changelog list item.
167
+ msgid "Fix: Serialize replace is not working properly for serialized strings"
168
+ msgstr ""
169
+
170
+ #. Found in faq list item.
171
+ msgid ""
172
+ "I can not login to the staging site\n"
173
+ "If you are using a security plugin like All In One WP Security &amp; Firewall you need to install latest version of WP Staging. \n"
174
+ "Go to WP Staging &gt; Settings and add the slug to the custom login page which you set up in All In One WP Security &amp; Firewall plugin."
175
+ msgstr ""
176
+
177
+ #. Found in installation list item, faq list item.
178
+ msgid "Upload and install it via the WordPress plugin backend wp-admin &gt; plugins &gt; add new &gt; uploads"
179
+ msgstr ""
180
+
181
+ #. Found in installation list item, faq list item.
182
+ msgid "Download the file \"wp-staging.zip\":"
183
+ msgstr ""
184
+
185
+ #. Found in description paragraph.
186
+ msgid ""
187
+ "Permalinks are disabled on the staging site because the staging site is cloned into a subfolder and permalinks are not working on all systems\n"
188
+ "without doing changes to the .htaccess (Apache server) or nginx.conf (Nginx Server).\n"
189
+ "<a href=\"https://wp-staging.com/docs/activate-permalinks-staging-site/\" title=\"activate permalinks on staging site\">Read here</a> how to activate permalinks on the staging site."
190
+ msgstr ""
191
+
192
+ #. Found in description list item.
193
+ msgid "New: Compatible to All In One WP Security &amp; Firewall"
194
+ msgstr ""
195
+
196
+ #. Found in description paragraph.
197
+ msgid "Note: WordPress 5.0 will be shipped with a new visual editor called Gutenberg. Use WP Staging to check if Gutenberg editor is working as intended on your website and that all used plugins are compatible with that new editor."
198
+ msgstr ""
199
+
200
+ #. Found in changelog paragraph.
201
+ msgid "Complete changelog: <a href=\"https://wp-staging.com/changelog.txt\">https://wp-staging.com/changelog.txt</a>"
202
+ msgstr ""
203
+
204
+ #. Found in description paragraph.
205
+ msgid "https://wp-staging.com"
206
+ msgstr ""
207
+
208
+ #. Found in description paragraph.
209
+ msgid ""
210
+ "<strong>This cloning and staging plugin is well tested and work in progress. <br /><br />\n"
211
+ "If you find any issue, please open a <a href=\"https://wp-staging.com/support/\" title=\"support ticket\">support ticket</a>.\n"
212
+ "</strong>\n"
213
+ "<br /><br />\n"
214
+ "<strong>Note: </strong> For pushing &amp; migrating plugins and theme files to live site, check out the pro version <a href=\"https://wp-staging.com/\" title=\"WP Staging Pro\">https://wp-staging.com/</a>\n"
215
+ "<br /><br />"
216
+ msgstr ""
217
+
218
+ #. Found in faq header.
219
+ msgid "Installation Instructions"
220
+ msgstr ""
221
+
222
+ #. Found in description list item.
223
+ msgid ""
224
+ "WordPress duplicating process on windows server (not tested but will probably work) \n"
225
+ "Edit: Duplication on windows server seems to be working well: <a href=\"https://wordpress.org/support/topic/wont-copy-files?replies=5\" title=\"Read more\">Read more</a> "
226
+ msgstr ""
227
+
228
+ #. Found in description list item.
229
+ msgid ""
230
+ "<strong>Safe: </strong> Access to staging site is granted for administrators only.\n"
231
+ "<br /><br />\n"
232
+ "<strong>More safe:</strong> \n"
233
+ "<br />"
234
+ msgstr ""
235
+
236
+ #. Found in description paragraph.
237
+ msgid "[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]"
238
+ msgstr ""
239
+
240
+ #. Found in description header.
241
+ msgid "Official Site"
242
+ msgstr ""
243
+
244
+ #. Found in installation list item, faq list item.
245
+ msgid "Activate the plugin through the 'Plugins' menu in WordPress."
246
+ msgstr "Activate the plugin through the 'Plugins' menu in WordPress."
247
+
248
+ #. Found in description paragraph.
249
+ msgid ""
250
+ "Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.\n"
251
+ "After installation goto the settings page 'Staging' and do your adjustments there."
252
+ msgstr ""
253
+
254
+ #. Found in description paragraph.
255
+ msgid ""
256
+ "This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:\n"
257
+ "Please open a <a href=\"https://wordpress.org/support/plugin/wp-staging/\" title=\"support request\">support request</a> and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines."
258
+ msgstr ""
259
+
260
+ #. Found in description paragraph.
261
+ msgid ""
262
+ "So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using\n"
263
+ "the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!"
264
+ msgstr ""
265
+
266
+ #. Found in description paragraph.
267
+ msgid ""
268
+ "At first, i love the <a href=\"https://wordpress.org/plugins/duplicator/\" title=\"Duplicator plugin\">Duplicator plugin</a>. Duplicator is a great tool for migrating from development site to production one or from production site to development one. \n"
269
+ "The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.\n"
270
+ "However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful."
271
+ msgstr ""
272
+
273
+ #. Found in description paragraph.
274
+ msgid ""
275
+ "If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.\n"
276
+ "WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.\n"
277
+ "Both tools are excellent cooperating eachother."
278
+ msgstr ""
279
+
280
+ #. Found in description paragraph.
281
+ msgid "This is were WP Staging steps in... Site cloning and staging site creation simplified!"
282
+ msgstr ""
283
+
284
+ #. Found in description paragraph.
285
+ msgid ""
286
+ "Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect \n"
287
+ "of your local copy is working on your live website exactely as you would expect it. \n"
288
+ "There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the \n"
289
+ "the cpu performance can lead to unexpected results on your production website. \n"
290
+ "There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform."
291
+ msgstr ""
292
+
293
+ #. Found in description paragraph.
294
+ msgid "<strong> I think its time to change this, so i created \"WP Staging\" for WordPress migration of staging sites</strong>"
295
+ msgstr ""
296
+
297
+ #. Found in description paragraph.
298
+ msgid ""
299
+ "Some people are also afraid of installing plugins updates because they follow the rule \"never touch a running system\" with having in mind that untested updates are increasing the risk of breaking their site.\n"
300
+ "I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior."
301
+ msgstr ""
302
+
303
+ #. Found in description paragraph.
304
+ msgid ""
305
+ "Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a \n"
306
+ "up-to-date copy of your website."
307
+ msgstr ""
308
+
309
+ #. Found in description paragraph.
310
+ msgid ""
311
+ "Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.\n"
312
+ "When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.\n"
313
+ "This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)"
314
+ msgstr ""
315
+
316
+ #. Found in description paragraph.
317
+ msgid "<strong>Change your workflow of updating themes and plugins data:</strong>"
318
+ msgstr ""
319
+
320
+ #. Found in description paragraph.
321
+ msgid "WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!"
322
+ msgstr ""
323
+
324
+ #. Found in description paragraph.
325
+ msgid "<em>* Time of creation depends on size of your database and file size</em>"
326
+ msgstr ""
327
+
328
+ #. Found in description list item.
329
+ msgid "Everything running as expected? You are on the save side for migration of all these modifications to your production site!"
330
+ msgstr ""
331
+
332
+ #. Found in description list item.
333
+ msgid "Test everything on your staging site first"
334
+ msgstr ""
335
+
336
+ #. Found in description list item.
337
+ msgid "Customize theme, configuration and plugins or install new plugins"
338
+ msgstr ""
339
+
340
+ #. Found in description list item.
341
+ msgid "Use WP Staging for migration of a production website to a clone site for staging purposes"
342
+ msgstr ""
343
+
344
+ #. Found in description list item.
345
+ msgid "Wordpress migration of wordpress multisites (not tested)"
346
+ msgstr ""
347
+
348
+ #. Found in description list item.
349
+ msgid "Extensive logging if duplication or migration process should fail."
350
+ msgstr ""
351
+
352
+ #. Found in description list item.
353
+ msgid "Admin bar reflects that you are working on a staging site"
354
+ msgstr ""
355
+
356
+ #. Found in description list item.
357
+ msgid "<strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power"
358
+ msgstr ""
359
+
360
+ #. Found in description list item.
361
+ msgid "<strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!"
362
+ msgstr ""
363
+
364
+ #. Found in description header.
365
+ msgid "How to install and setup?"
366
+ msgstr ""
367
+
368
+ #. Found in description header.
369
+ msgid "Important"
370
+ msgstr ""
371
+
372
+ #. Found in description header.
373
+ msgid "I need you feedback"
374
+ msgstr ""
375
+
376
+ #. Found in description header.
377
+ msgid "What are the benefits compared to a plugin like Duplicator?"
378
+ msgstr ""
379
+
380
+ #. Found in description header.
381
+ msgid "I just want to migrate the database from one installation to another"
382
+ msgstr ""
383
+
384
+ #. Found in description header.
385
+ msgid " Can´t i just use my local wordpress development copy for testing like xampp / lampp? "
386
+ msgstr ""
387
+
388
+ #. Found in description header.
389
+ msgid " Why should i use a staging website? "
390
+ msgstr ""
391
+
392
+ #. Found in description header.
393
+ msgid "What does not work or is not tested when running wordpress migration?"
394
+ msgstr ""
395
+
396
+ #. Found in description header.
397
+ msgid "Main Features"
398
+ msgstr ""
399
+
400
+ #. Found in description header.
401
+ msgid " WP Staging for WordPress Migration "
402
+ msgstr ""
403
+
404
+ #. Screenshot description.
405
+ msgid "Finish!"
406
+ msgstr ""
407
+
408
+ #. Screenshot description.
409
+ msgid "Step 3. Wordpress Staging site creation in progress"
410
+ msgstr ""
411
+
412
+ #. Screenshot description.
413
+ msgid "Step 2. Scanning your website for files and database tables"
414
+ msgstr ""
415
+
416
+ #. Screenshot description.
417
+ msgid "Step 1. Create new WordPress staging site"
418
  msgstr ""
languages/wp-staging-it.po CHANGED
@@ -1,418 +1,418 @@
1
- # Translation of Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) in Italian
2
- # This file is distributed under the same license as the Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) package.
3
- msgid ""
4
- msgstr ""
5
- "PO-Revision-Date: 2015-12-01 15:35:13+0000\n"
6
- "MIME-Version: 1.0\n"
7
- "Content-Type: text/plain; charset=UTF-8\n"
8
- "Content-Transfer-Encoding: 8bit\n"
9
- "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
- "X-Generator: GlotPress/2.4.0-alpha\n"
11
- "Language: it\n"
12
- "Project-Id-Version: Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release)\n"
13
-
14
- #. Short description.
15
- msgid "A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators."
16
- msgstr ""
17
-
18
- #. Plugin name.
19
- msgid "WP Staging - DB &amp; File Duplicator &amp; Migration"
20
- msgstr ""
21
-
22
- #. Found in changelog list item.
23
- msgid "Fix: Missing http(s) scheme after cloning multisites results in not working clones"
24
- msgstr ""
25
-
26
- #. Found in changelog list item.
27
- msgid "New: Support for Windows Azure cloud servers"
28
- msgstr ""
29
-
30
- #. Found in changelog list item.
31
- msgid "Fix: Do not search &amp; replace mail addresses"
32
- msgstr ""
33
-
34
- #. Found in changelog list item.
35
- msgid "Fix: Remove term 'Error' from login page"
36
- msgstr ""
37
-
38
- #. Found in changelog list item.
39
- msgid "New: Login options not needed any more - removed"
40
- msgstr ""
41
-
42
- #. Found in changelog list item.
43
- msgid "New: Detect if wp-config.php has been moved one folder level up"
44
- msgstr ""
45
-
46
- #. Found in changelog list item.
47
- msgid "New: Better error reporting"
48
- msgstr ""
49
-
50
- #. Found in changelog list item.
51
- msgid "New: Compatible up to WordPress 4.9.8"
52
- msgstr ""
53
-
54
- #. Found in changelog list item.
55
- msgid "New: Setting to specify the maximum allowed file size to copy"
56
- msgstr ""
57
-
58
- #. Found in changelog list item.
59
- msgid "Fix: Search &amp; replace path not correct if scheme http/https is not identical in wp-config and db wp_options"
60
- msgstr ""
61
-
62
- #. Found in changelog list item.
63
- msgid "Fix: Creating a new clone resets the custom wp staging user settings. E.g. ignores authentication setting"
64
- msgstr ""
65
-
66
- #. Found in changelog list item.
67
- msgid "Tweak: Remove certain debugging notices from the default log window"
68
- msgstr ""
69
-
70
- #. Found in changelog list item.
71
- msgid "Tweaks: remove term \"error\" from several log entries"
72
- msgstr ""
73
-
74
- #. Found in changelog list item.
75
- msgid "Fix: Search &amp; Replace not working if serialized object contains __PHP_Incomplete_Class_Name"
76
- msgstr ""
77
-
78
- #. Found in changelog list item.
79
- msgid "Fix: Skip search &amp; replace for objects where key is null"
80
- msgstr ""
81
-
82
- #. Found in changelog list item.
83
- msgid "Fix: Parameter must be an array or an object that implements Callable"
84
- msgstr ""
85
-
86
- #. Found in changelog list item.
87
- msgid "Fix: wp-config.php not copied when previous clone updating process has been failed"
88
- msgstr ""
89
-
90
- #. Found in changelog list item.
91
- msgid "Fix: Undefined object $this-&gt;tables"
92
- msgstr ""
93
-
94
- #. Found in changelog list item.
95
- msgid "Fix: Can not open upload folder in file selection menu"
96
- msgstr ""
97
-
98
- #. Found in changelog list item.
99
- msgid "Fix: Progress bar not shown as intented for clone updating process"
100
- msgstr ""
101
-
102
- #. Found in changelog list item.
103
- msgid "Fix: If cloning update process is interupted it may happen that staging site is not available any longer. (Updating the clone does not copy index.php to staging site again)"
104
- msgstr ""
105
-
106
- #. Found in changelog list item.
107
- msgid "Fix: Progress bar for step 'database' is not filling up to 100%"
108
- msgstr ""
109
-
110
- #. Found in changelog list item.
111
- msgid "New: Use the new progress bar for clone updating process"
112
- msgstr ""
113
-
114
- #. Found in changelog list item.
115
- msgid "New: Add multisite informations in system info log"
116
- msgstr ""
117
-
118
- #. Found in changelog list item.
119
- msgid "New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning."
120
- msgstr ""
121
-
122
- #. Found in changelog list item.
123
- msgid "Security: Do not allow to create a new staging site into a subfolder which already exists"
124
- msgstr ""
125
-
126
- #. Found in changelog list item.
127
- msgid "Fix: Missing trailingslash results to wrong absolute paths in database after Search &amp; Replace operation "
128
- msgstr ""
129
-
130
- #. Found in changelog list item.
131
- msgid "New: Compatible to WordPress 4.9.6"
132
- msgstr ""
133
-
134
- #. Found in changelog list item.
135
- msgid "New: Add new setting which allow to specify the search &amp; replace processing query limit"
136
- msgstr ""
137
-
138
- #. Found in changelog list item.
139
- msgid "New: Supports search &amp; replace for revslider image slider and several visual editors which are using non default serialized data"
140
- msgstr ""
141
-
142
- #. Found in changelog list item.
143
- msgid "New: Add filter 'wpstg_fiter_search_replace_rows' to exclude certain tables from search &amp; replace"
144
- msgstr ""
145
-
146
- #. Found in changelog list item.
147
- msgid "New: Add datetime timestamp internally to clone. (Used in WP Staging pro)"
148
- msgstr ""
149
-
150
- #. Found in changelog list item.
151
- msgid "New: Support for custom upload folder. For instance, if upload folder has been renamed and removed outsite wp-content folder"
152
- msgstr ""
153
-
154
- #. Found in changelog list item.
155
- msgid "New: Exclude tables for plugin wp_mail_smtp"
156
- msgstr ""
157
-
158
- #. Found in changelog list item.
159
- msgid "New: Add filter 'wpstg_filter_options_replace' to exclude certain tables from updating while cloning"
160
- msgstr ""
161
-
162
- #. Found in changelog list item.
163
- msgid "Fix: WP_SITEURL &amp; WP_HOME not replaced if constant contains php generated string"
164
- msgstr ""
165
-
166
- #. Found in changelog list item.
167
- msgid "Fix: Serialize replace is not working properly for serialized strings"
168
- msgstr ""
169
-
170
- #. Found in faq list item.
171
- msgid ""
172
- "I can not login to the staging site\n"
173
- "If you are using a security plugin like All In One WP Security &amp; Firewall you need to install latest version of WP Staging. \n"
174
- "Go to WP Staging &gt; Settings and add the slug to the custom login page which you set up in All In One WP Security &amp; Firewall plugin."
175
- msgstr ""
176
-
177
- #. Found in installation list item, faq list item.
178
- msgid "Upload and install it via the WordPress plugin backend wp-admin &gt; plugins &gt; add new &gt; uploads"
179
- msgstr ""
180
-
181
- #. Found in installation list item, faq list item.
182
- msgid "Download the file \"wp-staging.zip\":"
183
- msgstr ""
184
-
185
- #. Found in description paragraph.
186
- msgid ""
187
- "Permalinks are disabled on the staging site because the staging site is cloned into a subfolder and permalinks are not working on all systems\n"
188
- "without doing changes to the .htaccess (Apache server) or nginx.conf (Nginx Server).\n"
189
- "<a href=\"https://wp-staging.com/docs/activate-permalinks-staging-site/\" title=\"activate permalinks on staging site\">Read here</a> how to activate permalinks on the staging site."
190
- msgstr ""
191
-
192
- #. Found in description list item.
193
- msgid "New: Compatible to All In One WP Security &amp; Firewall"
194
- msgstr ""
195
-
196
- #. Found in description paragraph.
197
- msgid "Note: WordPress 5.0 will be shipped with a new visual editor called Gutenberg. Use WP Staging to check if Gutenberg editor is working as intended on your website and that all used plugins are compatible with that new editor."
198
- msgstr ""
199
-
200
- #. Found in changelog paragraph.
201
- msgid "Complete changelog: <a href=\"https://wp-staging.com/changelog.txt\">https://wp-staging.com/changelog.txt</a>"
202
- msgstr ""
203
-
204
- #. Found in description paragraph.
205
- msgid "https://wp-staging.com"
206
- msgstr ""
207
-
208
- #. Found in description paragraph.
209
- msgid ""
210
- "<strong>This cloning and staging plugin is well tested and work in progress. <br /><br />\n"
211
- "If you find any issue, please open a <a href=\"https://wp-staging.com/support/\" title=\"support ticket\">support ticket</a>.\n"
212
- "</strong>\n"
213
- "<br /><br />\n"
214
- "<strong>Note: </strong> For pushing &amp; migrating plugins and theme files to live site, check out the pro version <a href=\"https://wp-staging.com/\" title=\"WP Staging Pro\">https://wp-staging.com/</a>\n"
215
- "<br /><br />"
216
- msgstr ""
217
-
218
- #. Found in faq header.
219
- msgid "Installation Instructions"
220
- msgstr ""
221
-
222
- #. Found in description list item.
223
- msgid ""
224
- "WordPress duplicating process on windows server (not tested but will probably work) \n"
225
- "Edit: Duplication on windows server seems to be working well: <a href=\"https://wordpress.org/support/topic/wont-copy-files?replies=5\" title=\"Read more\">Read more</a> "
226
- msgstr ""
227
-
228
- #. Found in description list item.
229
- msgid ""
230
- "<strong>Safe: </strong> Access to staging site is granted for administrators only.\n"
231
- "<br /><br />\n"
232
- "<strong>More safe:</strong> \n"
233
- "<br />"
234
- msgstr ""
235
-
236
- #. Found in description paragraph.
237
- msgid "[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]"
238
- msgstr ""
239
-
240
- #. Found in description header.
241
- msgid "Official Site"
242
- msgstr ""
243
-
244
- #. Found in installation list item, faq list item.
245
- msgid "Activate the plugin through the 'Plugins' menu in WordPress."
246
- msgstr "Attivare il plugin tramite il menu 'Plugin' di WordPress"
247
-
248
- #. Found in description paragraph.
249
- msgid ""
250
- "Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.\n"
251
- "After installation goto the settings page 'Staging' and do your adjustments there."
252
- msgstr ""
253
-
254
- #. Found in description paragraph.
255
- msgid ""
256
- "This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:\n"
257
- "Please open a <a href=\"https://wordpress.org/support/plugin/wp-staging/\" title=\"support request\">support request</a> and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines."
258
- msgstr ""
259
-
260
- #. Found in description paragraph.
261
- msgid ""
262
- "So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using\n"
263
- "the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!"
264
- msgstr ""
265
-
266
- #. Found in description paragraph.
267
- msgid ""
268
- "At first, i love the <a href=\"https://wordpress.org/plugins/duplicator/\" title=\"Duplicator plugin\">Duplicator plugin</a>. Duplicator is a great tool for migrating from development site to production one or from production site to development one. \n"
269
- "The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.\n"
270
- "However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful."
271
- msgstr ""
272
-
273
- #. Found in description paragraph.
274
- msgid ""
275
- "If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.\n"
276
- "WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.\n"
277
- "Both tools are excellent cooperating eachother."
278
- msgstr ""
279
-
280
- #. Found in description paragraph.
281
- msgid "This is were WP Staging steps in... Site cloning and staging site creation simplified!"
282
- msgstr ""
283
-
284
- #. Found in description paragraph.
285
- msgid ""
286
- "Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect \n"
287
- "of your local copy is working on your live website exactely as you would expect it. \n"
288
- "There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the \n"
289
- "the cpu performance can lead to unexpected results on your production website. \n"
290
- "There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform."
291
- msgstr ""
292
-
293
- #. Found in description paragraph.
294
- msgid "<strong> I think its time to change this, so i created \"WP Staging\" for WordPress migration of staging sites</strong>"
295
- msgstr ""
296
-
297
- #. Found in description paragraph.
298
- msgid ""
299
- "Some people are also afraid of installing plugins updates because they follow the rule \"never touch a running system\" with having in mind that untested updates are increasing the risk of breaking their site.\n"
300
- "I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior."
301
- msgstr ""
302
-
303
- #. Found in description paragraph.
304
- msgid ""
305
- "Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a \n"
306
- "up-to-date copy of your website."
307
- msgstr ""
308
-
309
- #. Found in description paragraph.
310
- msgid ""
311
- "Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.\n"
312
- "When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.\n"
313
- "This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)"
314
- msgstr ""
315
-
316
- #. Found in description paragraph.
317
- msgid "<strong>Change your workflow of updating themes and plugins data:</strong>"
318
- msgstr ""
319
-
320
- #. Found in description paragraph.
321
- msgid "WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!"
322
- msgstr ""
323
-
324
- #. Found in description paragraph.
325
- msgid "<em>* Time of creation depends on size of your database and file size</em>"
326
- msgstr ""
327
-
328
- #. Found in description list item.
329
- msgid "Everything running as expected? You are on the save side for migration of all these modifications to your production site!"
330
- msgstr ""
331
-
332
- #. Found in description list item.
333
- msgid "Test everything on your staging site first"
334
- msgstr ""
335
-
336
- #. Found in description list item.
337
- msgid "Customize theme, configuration and plugins or install new plugins"
338
- msgstr ""
339
-
340
- #. Found in description list item.
341
- msgid "Use WP Staging for migration of a production website to a clone site for staging purposes"
342
- msgstr ""
343
-
344
- #. Found in description list item.
345
- msgid "Wordpress migration of wordpress multisites (not tested)"
346
- msgstr ""
347
-
348
- #. Found in description list item.
349
- msgid "Extensive logging if duplication or migration process should fail."
350
- msgstr ""
351
-
352
- #. Found in description list item.
353
- msgid "Admin bar reflects that you are working on a staging site"
354
- msgstr ""
355
-
356
- #. Found in description list item.
357
- msgid "<strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power"
358
- msgstr ""
359
-
360
- #. Found in description list item.
361
- msgid "<strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!"
362
- msgstr ""
363
-
364
- #. Found in description header.
365
- msgid "How to install and setup?"
366
- msgstr ""
367
-
368
- #. Found in description header.
369
- msgid "Important"
370
- msgstr "Wichtig"
371
-
372
- #. Found in description header.
373
- msgid "I need you feedback"
374
- msgstr ""
375
-
376
- #. Found in description header.
377
- msgid "What are the benefits compared to a plugin like Duplicator?"
378
- msgstr ""
379
-
380
- #. Found in description header.
381
- msgid "I just want to migrate the database from one installation to another"
382
- msgstr ""
383
-
384
- #. Found in description header.
385
- msgid " Can´t i just use my local wordpress development copy for testing like xampp / lampp? "
386
- msgstr ""
387
-
388
- #. Found in description header.
389
- msgid " Why should i use a staging website? "
390
- msgstr ""
391
-
392
- #. Found in description header.
393
- msgid "What does not work or is not tested when running wordpress migration?"
394
- msgstr ""
395
-
396
- #. Found in description header.
397
- msgid "Main Features"
398
- msgstr ""
399
-
400
- #. Found in description header.
401
- msgid " WP Staging for WordPress Migration "
402
- msgstr ""
403
-
404
- #. Screenshot description.
405
- msgid "Finish!"
406
- msgstr ""
407
-
408
- #. Screenshot description.
409
- msgid "Step 3. Wordpress Staging site creation in progress"
410
- msgstr ""
411
-
412
- #. Screenshot description.
413
- msgid "Step 2. Scanning your website for files and database tables"
414
- msgstr ""
415
-
416
- #. Screenshot description.
417
- msgid "Step 1. Create new WordPress staging site"
418
  msgstr ""
1
+ # Translation of Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) in Italian
2
+ # This file is distributed under the same license as the Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) package.
3
+ msgid ""
4
+ msgstr ""
5
+ "PO-Revision-Date: 2015-12-01 15:35:13+0000\n"
6
+ "MIME-Version: 1.0\n"
7
+ "Content-Type: text/plain; charset=UTF-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
+ "X-Generator: GlotPress/2.4.0-alpha\n"
11
+ "Language: it\n"
12
+ "Project-Id-Version: Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release)\n"
13
+
14
+ #. Short description.
15
+ msgid "A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators."
16
+ msgstr ""
17
+
18
+ #. Plugin name.
19
+ msgid "WP Staging - DB &amp; File Duplicator &amp; Migration"
20
+ msgstr ""
21
+
22
+ #. Found in changelog list item.
23
+ msgid "Fix: Missing http(s) scheme after cloning multisites results in not working clones"
24
+ msgstr ""
25
+
26
+ #. Found in changelog list item.
27
+ msgid "New: Support for Windows Azure cloud servers"
28
+ msgstr ""
29
+
30
+ #. Found in changelog list item.
31
+ msgid "Fix: Do not search &amp; replace mail addresses"
32
+ msgstr ""
33
+
34
+ #. Found in changelog list item.
35
+ msgid "Fix: Remove term 'Error' from login page"
36
+ msgstr ""
37
+
38
+ #. Found in changelog list item.
39
+ msgid "New: Login options not needed any more - removed"
40
+ msgstr ""
41
+
42
+ #. Found in changelog list item.
43
+ msgid "New: Detect if wp-config.php has been moved one folder level up"
44
+ msgstr ""
45
+
46
+ #. Found in changelog list item.
47
+ msgid "New: Better error reporting"
48
+ msgstr ""
49
+
50
+ #. Found in changelog list item.
51
+ msgid "New: Compatible up to WordPress 4.9.8"
52
+ msgstr ""
53
+
54
+ #. Found in changelog list item.
55
+ msgid "New: Setting to specify the maximum allowed file size to copy"
56
+ msgstr ""
57
+
58
+ #. Found in changelog list item.
59
+ msgid "Fix: Search &amp; replace path not correct if scheme http/https is not identical in wp-config and db wp_options"
60
+ msgstr ""
61
+
62
+ #. Found in changelog list item.
63
+ msgid "Fix: Creating a new clone resets the custom wp staging user settings. E.g. ignores authentication setting"
64
+ msgstr ""
65
+
66
+ #. Found in changelog list item.
67
+ msgid "Tweak: Remove certain debugging notices from the default log window"
68
+ msgstr ""
69
+
70
+ #. Found in changelog list item.
71
+ msgid "Tweaks: remove term \"error\" from several log entries"
72
+ msgstr ""
73
+
74
+ #. Found in changelog list item.
75
+ msgid "Fix: Search &amp; Replace not working if serialized object contains __PHP_Incomplete_Class_Name"
76
+ msgstr ""
77
+
78
+ #. Found in changelog list item.
79
+ msgid "Fix: Skip search &amp; replace for objects where key is null"
80
+ msgstr ""
81
+
82
+ #. Found in changelog list item.
83
+ msgid "Fix: Parameter must be an array or an object that implements Callable"
84
+ msgstr ""
85
+
86
+ #. Found in changelog list item.
87
+ msgid "Fix: wp-config.php not copied when previous clone updating process has been failed"
88
+ msgstr ""
89
+
90
+ #. Found in changelog list item.
91
+ msgid "Fix: Undefined object $this-&gt;tables"
92
+ msgstr ""
93
+
94
+ #. Found in changelog list item.
95
+ msgid "Fix: Can not open upload folder in file selection menu"
96
+ msgstr ""
97
+
98
+ #. Found in changelog list item.
99
+ msgid "Fix: Progress bar not shown as intented for clone updating process"
100
+ msgstr ""
101
+
102
+ #. Found in changelog list item.
103
+ msgid "Fix: If cloning update process is interupted it may happen that staging site is not available any longer. (Updating the clone does not copy index.php to staging site again)"
104
+ msgstr ""
105
+
106
+ #. Found in changelog list item.
107
+ msgid "Fix: Progress bar for step 'database' is not filling up to 100%"
108
+ msgstr ""
109
+
110
+ #. Found in changelog list item.
111
+ msgid "New: Use the new progress bar for clone updating process"
112
+ msgstr ""
113
+
114
+ #. Found in changelog list item.
115
+ msgid "New: Add multisite informations in system info log"
116
+ msgstr ""
117
+
118
+ #. Found in changelog list item.
119
+ msgid "New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning."
120
+ msgstr ""
121
+
122
+ #. Found in changelog list item.
123
+ msgid "Security: Do not allow to create a new staging site into a subfolder which already exists"
124
+ msgstr ""
125
+
126
+ #. Found in changelog list item.
127
+ msgid "Fix: Missing trailingslash results to wrong absolute paths in database after Search &amp; Replace operation "
128
+ msgstr ""
129
+
130
+ #. Found in changelog list item.
131
+ msgid "New: Compatible to WordPress 4.9.6"
132
+ msgstr ""
133
+
134
+ #. Found in changelog list item.
135
+ msgid "New: Add new setting which allow to specify the search &amp; replace processing query limit"
136
+ msgstr ""
137
+
138
+ #. Found in changelog list item.
139
+ msgid "New: Supports search &amp; replace for revslider image slider and several visual editors which are using non default serialized data"
140
+ msgstr ""
141
+
142
+ #. Found in changelog list item.
143
+ msgid "New: Add filter 'wpstg_fiter_search_replace_rows' to exclude certain tables from search &amp; replace"
144
+ msgstr ""
145
+
146
+ #. Found in changelog list item.
147
+ msgid "New: Add datetime timestamp internally to clone. (Used in WP Staging pro)"
148
+ msgstr ""
149
+
150
+ #. Found in changelog list item.
151
+ msgid "New: Support for custom upload folder. For instance, if upload folder has been renamed and removed outsite wp-content folder"
152
+ msgstr ""
153
+
154
+ #. Found in changelog list item.
155
+ msgid "New: Exclude tables for plugin wp_mail_smtp"
156
+ msgstr ""
157
+
158
+ #. Found in changelog list item.
159
+ msgid "New: Add filter 'wpstg_filter_options_replace' to exclude certain tables from updating while cloning"
160
+ msgstr ""
161
+
162
+ #. Found in changelog list item.
163
+ msgid "Fix: WP_SITEURL &amp; WP_HOME not replaced if constant contains php generated string"
164
+ msgstr ""
165
+
166
+ #. Found in changelog list item.
167
+ msgid "Fix: Serialize replace is not working properly for serialized strings"
168
+ msgstr ""
169
+
170
+ #. Found in faq list item.
171
+ msgid ""
172
+ "I can not login to the staging site\n"
173
+ "If you are using a security plugin like All In One WP Security &amp; Firewall you need to install latest version of WP Staging. \n"
174
+ "Go to WP Staging &gt; Settings and add the slug to the custom login page which you set up in All In One WP Security &amp; Firewall plugin."
175
+ msgstr ""
176
+
177
+ #. Found in installation list item, faq list item.
178
+ msgid "Upload and install it via the WordPress plugin backend wp-admin &gt; plugins &gt; add new &gt; uploads"
179
+ msgstr ""
180
+
181
+ #. Found in installation list item, faq list item.
182
+ msgid "Download the file \"wp-staging.zip\":"
183
+ msgstr ""
184
+
185
+ #. Found in description paragraph.
186
+ msgid ""
187
+ "Permalinks are disabled on the staging site because the staging site is cloned into a subfolder and permalinks are not working on all systems\n"
188
+ "without doing changes to the .htaccess (Apache server) or nginx.conf (Nginx Server).\n"
189
+ "<a href=\"https://wp-staging.com/docs/activate-permalinks-staging-site/\" title=\"activate permalinks on staging site\">Read here</a> how to activate permalinks on the staging site."
190
+ msgstr ""
191
+
192
+ #. Found in description list item.
193
+ msgid "New: Compatible to All In One WP Security &amp; Firewall"
194
+ msgstr ""
195
+
196
+ #. Found in description paragraph.
197
+ msgid "Note: WordPress 5.0 will be shipped with a new visual editor called Gutenberg. Use WP Staging to check if Gutenberg editor is working as intended on your website and that all used plugins are compatible with that new editor."
198
+ msgstr ""
199
+
200
+ #. Found in changelog paragraph.
201
+ msgid "Complete changelog: <a href=\"https://wp-staging.com/changelog.txt\">https://wp-staging.com/changelog.txt</a>"
202
+ msgstr ""
203
+
204
+ #. Found in description paragraph.
205
+ msgid "https://wp-staging.com"
206
+ msgstr ""
207
+
208
+ #. Found in description paragraph.
209
+ msgid ""
210
+ "<strong>This cloning and staging plugin is well tested and work in progress. <br /><br />\n"
211
+ "If you find any issue, please open a <a href=\"https://wp-staging.com/support/\" title=\"support ticket\">support ticket</a>.\n"
212
+ "</strong>\n"
213
+ "<br /><br />\n"
214
+ "<strong>Note: </strong> For pushing &amp; migrating plugins and theme files to live site, check out the pro version <a href=\"https://wp-staging.com/\" title=\"WP Staging Pro\">https://wp-staging.com/</a>\n"
215
+ "<br /><br />"
216
+ msgstr ""
217
+
218
+ #. Found in faq header.
219
+ msgid "Installation Instructions"
220
+ msgstr ""
221
+
222
+ #. Found in description list item.
223
+ msgid ""
224
+ "WordPress duplicating process on windows server (not tested but will probably work) \n"
225
+ "Edit: Duplication on windows server seems to be working well: <a href=\"https://wordpress.org/support/topic/wont-copy-files?replies=5\" title=\"Read more\">Read more</a> "
226
+ msgstr ""
227
+
228
+ #. Found in description list item.
229
+ msgid ""
230
+ "<strong>Safe: </strong> Access to staging site is granted for administrators only.\n"
231
+ "<br /><br />\n"
232
+ "<strong>More safe:</strong> \n"
233
+ "<br />"
234
+ msgstr ""
235
+
236
+ #. Found in description paragraph.
237
+ msgid "[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]"
238
+ msgstr ""
239
+
240
+ #. Found in description header.
241
+ msgid "Official Site"
242
+ msgstr ""
243
+
244
+ #. Found in installation list item, faq list item.
245
+ msgid "Activate the plugin through the 'Plugins' menu in WordPress."
246
+ msgstr "Attivare il plugin tramite il menu 'Plugin' di WordPress"
247
+
248
+ #. Found in description paragraph.
249
+ msgid ""
250
+ "Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.\n"
251
+ "After installation goto the settings page 'Staging' and do your adjustments there."
252
+ msgstr ""
253
+
254
+ #. Found in description paragraph.
255
+ msgid ""
256
+ "This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:\n"
257
+ "Please open a <a href=\"https://wordpress.org/support/plugin/wp-staging/\" title=\"support request\">support request</a> and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines."
258
+ msgstr ""
259
+
260
+ #. Found in description paragraph.
261
+ msgid ""
262
+ "So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using\n"
263
+ "the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!"
264
+ msgstr ""
265
+
266
+ #. Found in description paragraph.
267
+ msgid ""
268
+ "At first, i love the <a href=\"https://wordpress.org/plugins/duplicator/\" title=\"Duplicator plugin\">Duplicator plugin</a>. Duplicator is a great tool for migrating from development site to production one or from production site to development one. \n"
269
+ "The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.\n"
270
+ "However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful."
271
+ msgstr ""
272
+
273
+ #. Found in description paragraph.
274
+ msgid ""
275
+ "If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.\n"
276
+ "WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.\n"
277
+ "Both tools are excellent cooperating eachother."
278
+ msgstr ""
279
+
280
+ #. Found in description paragraph.
281
+ msgid "This is were WP Staging steps in... Site cloning and staging site creation simplified!"
282
+ msgstr ""
283
+
284
+ #. Found in description paragraph.
285
+ msgid ""
286
+ "Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect \n"
287
+ "of your local copy is working on your live website exactely as you would expect it. \n"
288
+ "There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the \n"
289
+ "the cpu performance can lead to unexpected results on your production website. \n"
290
+ "There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform."
291
+ msgstr ""
292
+
293
+ #. Found in description paragraph.
294
+ msgid "<strong> I think its time to change this, so i created \"WP Staging\" for WordPress migration of staging sites</strong>"
295
+ msgstr ""
296
+
297
+ #. Found in description paragraph.
298
+ msgid ""
299
+ "Some people are also afraid of installing plugins updates because they follow the rule \"never touch a running system\" with having in mind that untested updates are increasing the risk of breaking their site.\n"
300
+ "I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior."
301
+ msgstr ""
302
+
303
+ #. Found in description paragraph.
304
+ msgid ""
305
+ "Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a \n"
306
+ "up-to-date copy of your website."
307
+ msgstr ""
308
+
309
+ #. Found in description paragraph.
310
+ msgid ""
311
+ "Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.\n"
312
+ "When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.\n"
313
+ "This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)"
314
+ msgstr ""
315
+
316
+ #. Found in description paragraph.
317
+ msgid "<strong>Change your workflow of updating themes and plugins data:</strong>"
318
+ msgstr ""
319
+
320
+ #. Found in description paragraph.
321
+ msgid "WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!"
322
+ msgstr ""
323
+
324
+ #. Found in description paragraph.
325
+ msgid "<em>* Time of creation depends on size of your database and file size</em>"
326
+ msgstr ""
327
+
328
+ #. Found in description list item.
329
+ msgid "Everything running as expected? You are on the save side for migration of all these modifications to your production site!"
330
+ msgstr ""
331
+
332
+ #. Found in description list item.
333
+ msgid "Test everything on your staging site first"
334
+ msgstr ""
335
+
336
+ #. Found in description list item.
337
+ msgid "Customize theme, configuration and plugins or install new plugins"
338
+ msgstr ""
339
+
340
+ #. Found in description list item.
341
+ msgid "Use WP Staging for migration of a production website to a clone site for staging purposes"
342
+ msgstr ""
343
+
344
+ #. Found in description list item.
345
+ msgid "Wordpress migration of wordpress multisites (not tested)"
346
+ msgstr ""
347
+
348
+ #. Found in description list item.
349
+ msgid "Extensive logging if duplication or migration process should fail."
350
+ msgstr ""
351
+
352
+ #. Found in description list item.
353
+ msgid "Admin bar reflects that you are working on a staging site"
354
+ msgstr ""
355
+
356
+ #. Found in description list item.
357
+ msgid "<strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power"
358
+ msgstr ""
359
+
360
+ #. Found in description list item.
361
+ msgid "<strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!"
362
+ msgstr ""
363
+
364
+ #. Found in description header.
365
+ msgid "How to install and setup?"
366
+ msgstr ""
367
+
368
+ #. Found in description header.
369
+ msgid "Important"
370
+ msgstr "Wichtig"
371
+
372
+ #. Found in description header.
373
+ msgid "I need you feedback"
374
+ msgstr ""
375
+
376
+ #. Found in description header.
377
+ msgid "What are the benefits compared to a plugin like Duplicator?"
378
+ msgstr ""
379
+
380
+ #. Found in description header.
381
+ msgid "I just want to migrate the database from one installation to another"
382
+ msgstr ""
383
+
384
+ #. Found in description header.
385
+ msgid " Can´t i just use my local wordpress development copy for testing like xampp / lampp? "
386
+ msgstr ""
387
+
388
+ #. Found in description header.
389
+ msgid " Why should i use a staging website? "
390
+ msgstr ""
391
+
392
+ #. Found in description header.
393
+ msgid "What does not work or is not tested when running wordpress migration?"
394
+ msgstr ""
395
+
396
+ #. Found in description header.
397
+ msgid "Main Features"
398
+ msgstr ""
399
+
400
+ #. Found in description header.
401
+ msgid " WP Staging for WordPress Migration "
402
+ msgstr ""
403
+
404
+ #. Screenshot description.
405
+ msgid "Finish!"
406
+ msgstr ""
407
+
408
+ #. Screenshot description.
409
+ msgid "Step 3. Wordpress Staging site creation in progress"
410
+ msgstr ""
411
+
412
+ #. Screenshot description.
413
+ msgid "Step 2. Scanning your website for files and database tables"
414
+ msgstr ""
415
+
416
+ #. Screenshot description.
417
+ msgid "Step 1. Create new WordPress staging site"
418
  msgstr ""
languages/wp-staging-it_1.po CHANGED
@@ -1,418 +1,418 @@
1
- # Translation of Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) in Italian
2
- # This file is distributed under the same license as the Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) package.
3
- msgid ""
4
- msgstr ""
5
- "PO-Revision-Date: 2015-12-01 15:35:13+0000\n"
6
- "MIME-Version: 1.0\n"
7
- "Content-Type: text/plain; charset=UTF-8\n"
8
- "Content-Transfer-Encoding: 8bit\n"
9
- "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
- "X-Generator: GlotPress/2.4.0-alpha\n"
11
- "Language: it\n"
12
- "Project-Id-Version: Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release)\n"
13
-
14
- #. Short description.
15
- msgid "A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators."
16
- msgstr ""
17
-
18
- #. Plugin name.
19
- msgid "WP Staging - DB &amp; File Duplicator &amp; Migration"
20
- msgstr ""
21
-
22
- #. Found in changelog list item.
23
- msgid "Fix: Missing http(s) scheme after cloning multisites results in not working clones"
24
- msgstr ""
25
-
26
- #. Found in changelog list item.
27
- msgid "New: Support for Windows Azure cloud servers"
28
- msgstr ""
29
-
30
- #. Found in changelog list item.
31
- msgid "Fix: Do not search &amp; replace mail addresses"
32
- msgstr ""
33
-
34
- #. Found in changelog list item.
35
- msgid "Fix: Remove term 'Error' from login page"
36
- msgstr ""
37
-
38
- #. Found in changelog list item.
39
- msgid "New: Login options not needed any more - removed"
40
- msgstr ""
41
-
42
- #. Found in changelog list item.
43
- msgid "New: Detect if wp-config.php has been moved one folder level up"
44
- msgstr ""
45
-
46
- #. Found in changelog list item.
47
- msgid "New: Better error reporting"
48
- msgstr ""
49
-
50
- #. Found in changelog list item.
51
- msgid "New: Compatible up to WordPress 4.9.8"
52
- msgstr ""
53
-
54
- #. Found in changelog list item.
55
- msgid "New: Setting to specify the maximum allowed file size to copy"
56
- msgstr ""
57
-
58
- #. Found in changelog list item.
59
- msgid "Fix: Search &amp; replace path not correct if scheme http/https is not identical in wp-config and db wp_options"
60
- msgstr ""
61
-
62
- #. Found in changelog list item.
63
- msgid "Fix: Creating a new clone resets the custom wp staging user settings. E.g. ignores authentication setting"
64
- msgstr ""
65
-
66
- #. Found in changelog list item.
67
- msgid "Tweak: Remove certain debugging notices from the default log window"
68
- msgstr ""
69
-
70
- #. Found in changelog list item.
71
- msgid "Tweaks: remove term \"error\" from several log entries"
72
- msgstr ""
73
-
74
- #. Found in changelog list item.
75
- msgid "Fix: Search &amp; Replace not working if serialized object contains __PHP_Incomplete_Class_Name"
76
- msgstr ""
77
-
78
- #. Found in changelog list item.
79
- msgid "Fix: Skip search &amp; replace for objects where key is null"
80
- msgstr ""
81
-
82
- #. Found in changelog list item.
83
- msgid "Fix: Parameter must be an array or an object that implements Callable"
84
- msgstr ""
85
-
86
- #. Found in changelog list item.
87
- msgid "Fix: wp-config.php not copied when previous clone updating process has been failed"
88
- msgstr ""
89
-
90
- #. Found in changelog list item.
91
- msgid "Fix: Undefined object $this-&gt;tables"
92
- msgstr ""
93
-
94
- #. Found in changelog list item.
95
- msgid "Fix: Can not open upload folder in file selection menu"
96
- msgstr ""
97
-
98
- #. Found in changelog list item.
99
- msgid "Fix: Progress bar not shown as intented for clone updating process"
100
- msgstr ""
101
-
102
- #. Found in changelog list item.
103
- msgid "Fix: If cloning update process is interupted it may happen that staging site is not available any longer. (Updating the clone does not copy index.php to staging site again)"
104
- msgstr ""
105
-
106
- #. Found in changelog list item.
107
- msgid "Fix: Progress bar for step 'database' is not filling up to 100%"
108
- msgstr ""
109
-
110
- #. Found in changelog list item.
111
- msgid "New: Use the new progress bar for clone updating process"
112
- msgstr ""
113
-
114
- #. Found in changelog list item.
115
- msgid "New: Add multisite informations in system info log"
116
- msgstr ""
117
-
118
- #. Found in changelog list item.
119
- msgid "New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning."
120
- msgstr ""
121
-
122
- #. Found in changelog list item.
123
- msgid "Security: Do not allow to create a new staging site into a subfolder which already exists"
124
- msgstr ""
125
-
126
- #. Found in changelog list item.
127
- msgid "Fix: Missing trailingslash results to wrong absolute paths in database after Search &amp; Replace operation "
128
- msgstr ""
129
-
130
- #. Found in changelog list item.
131
- msgid "New: Compatible to WordPress 4.9.6"
132
- msgstr ""
133
-
134
- #. Found in changelog list item.
135
- msgid "New: Add new setting which allow to specify the search &amp; replace processing query limit"
136
- msgstr ""
137
-
138
- #. Found in changelog list item.
139
- msgid "New: Supports search &amp; replace for revslider image slider and several visual editors which are using non default serialized data"
140
- msgstr ""
141
-
142
- #. Found in changelog list item.
143
- msgid "New: Add filter 'wpstg_fiter_search_replace_rows' to exclude certain tables from search &amp; replace"
144
- msgstr ""
145
-
146
- #. Found in changelog list item.
147
- msgid "New: Add datetime timestamp internally to clone. (Used in WP Staging pro)"
148
- msgstr ""
149
-
150
- #. Found in changelog list item.
151
- msgid "New: Support for custom upload folder. For instance, if upload folder has been renamed and removed outsite wp-content folder"
152
- msgstr ""
153
-
154
- #. Found in changelog list item.
155
- msgid "New: Exclude tables for plugin wp_mail_smtp"
156
- msgstr ""
157
-
158
- #. Found in changelog list item.
159
- msgid "New: Add filter 'wpstg_filter_options_replace' to exclude certain tables from updating while cloning"
160
- msgstr ""
161
-
162
- #. Found in changelog list item.
163
- msgid "Fix: WP_SITEURL &amp; WP_HOME not replaced if constant contains php generated string"
164
- msgstr ""
165
-
166
- #. Found in changelog list item.
167
- msgid "Fix: Serialize replace is not working properly for serialized strings"
168
- msgstr ""
169
-
170
- #. Found in faq list item.
171
- msgid ""
172
- "I can not login to the staging site\n"
173
- "If you are using a security plugin like All In One WP Security &amp; Firewall you need to install latest version of WP Staging. \n"
174
- "Go to WP Staging &gt; Settings and add the slug to the custom login page which you set up in All In One WP Security &amp; Firewall plugin."
175
- msgstr ""
176
-
177
- #. Found in installation list item, faq list item.
178
- msgid "Upload and install it via the WordPress plugin backend wp-admin &gt; plugins &gt; add new &gt; uploads"
179
- msgstr ""
180
-
181
- #. Found in installation list item, faq list item.
182
- msgid "Download the file \"wp-staging.zip\":"
183
- msgstr ""
184
-
185
- #. Found in description paragraph.
186
- msgid ""
187
- "Permalinks are disabled on the staging site because the staging site is cloned into a subfolder and permalinks are not working on all systems\n"
188
- "without doing changes to the .htaccess (Apache server) or nginx.conf (Nginx Server).\n"
189
- "<a href=\"https://wp-staging.com/docs/activate-permalinks-staging-site/\" title=\"activate permalinks on staging site\">Read here</a> how to activate permalinks on the staging site."
190
- msgstr ""
191
-
192
- #. Found in description list item.
193
- msgid "New: Compatible to All In One WP Security &amp; Firewall"
194
- msgstr ""
195
-
196
- #. Found in description paragraph.
197
- msgid "Note: WordPress 5.0 will be shipped with a new visual editor called Gutenberg. Use WP Staging to check if Gutenberg editor is working as intended on your website and that all used plugins are compatible with that new editor."
198
- msgstr ""
199
-
200
- #. Found in changelog paragraph.
201
- msgid "Complete changelog: <a href=\"https://wp-staging.com/changelog.txt\">https://wp-staging.com/changelog.txt</a>"
202
- msgstr ""
203
-
204
- #. Found in description paragraph.
205
- msgid "https://wp-staging.com"
206
- msgstr ""
207
-
208
- #. Found in description paragraph.
209
- msgid ""
210
- "<strong>This cloning and staging plugin is well tested and work in progress. <br /><br />\n"
211
- "If you find any issue, please open a <a href=\"https://wp-staging.com/support/\" title=\"support ticket\">support ticket</a>.\n"
212
- "</strong>\n"
213
- "<br /><br />\n"
214
- "<strong>Note: </strong> For pushing &amp; migrating plugins and theme files to live site, check out the pro version <a href=\"https://wp-staging.com/\" title=\"WP Staging Pro\">https://wp-staging.com/</a>\n"
215
- "<br /><br />"
216
- msgstr ""
217
-
218
- #. Found in faq header.
219
- msgid "Installation Instructions"
220
- msgstr ""
221
-
222
- #. Found in description list item.
223
- msgid ""
224
- "WordPress duplicating process on windows server (not tested but will probably work) \n"
225
- "Edit: Duplication on windows server seems to be working well: <a href=\"https://wordpress.org/support/topic/wont-copy-files?replies=5\" title=\"Read more\">Read more</a> "
226
- msgstr ""
227
-
228
- #. Found in description list item.
229
- msgid ""
230
- "<strong>Safe: </strong> Access to staging site is granted for administrators only.\n"
231
- "<br /><br />\n"
232
- "<strong>More safe:</strong> \n"
233
- "<br />"
234
- msgstr ""
235
-
236
- #. Found in description paragraph.
237
- msgid "[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]"
238
- msgstr ""
239
-
240
- #. Found in description header.
241
- msgid "Official Site"
242
- msgstr ""
243
-
244
- #. Found in installation list item, faq list item.
245
- msgid "Activate the plugin through the 'Plugins' menu in WordPress."
246
- msgstr "Attivare il plugin tramite il menu 'Plugin' di WordPress"
247
-
248
- #. Found in description paragraph.
249
- msgid ""
250
- "Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.\n"
251
- "After installation goto the settings page 'Staging' and do your adjustments there."
252
- msgstr ""
253
-
254
- #. Found in description paragraph.
255
- msgid ""
256
- "This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:\n"
257
- "Please open a <a href=\"https://wordpress.org/support/plugin/wp-staging/\" title=\"support request\">support request</a> and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines."
258
- msgstr ""
259
-
260
- #. Found in description paragraph.
261
- msgid ""
262
- "So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using\n"
263
- "the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!"
264
- msgstr ""
265
-
266
- #. Found in description paragraph.
267
- msgid ""
268
- "At first, i love the <a href=\"https://wordpress.org/plugins/duplicator/\" title=\"Duplicator plugin\">Duplicator plugin</a>. Duplicator is a great tool for migrating from development site to production one or from production site to development one. \n"
269
- "The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.\n"
270
- "However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful."
271
- msgstr ""
272
-
273
- #. Found in description paragraph.
274
- msgid ""
275
- "If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.\n"
276
- "WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.\n"
277
- "Both tools are excellent cooperating eachother."
278
- msgstr ""
279
-
280
- #. Found in description paragraph.
281
- msgid "This is were WP Staging steps in... Site cloning and staging site creation simplified!"
282
- msgstr ""
283
-
284
- #. Found in description paragraph.
285
- msgid ""
286
- "Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect \n"
287
- "of your local copy is working on your live website exactely as you would expect it. \n"
288
- "There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the \n"
289
- "the cpu performance can lead to unexpected results on your production website. \n"
290
- "There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform."
291
- msgstr ""
292
-
293
- #. Found in description paragraph.
294
- msgid "<strong> I think its time to change this, so i created \"WP Staging\" for WordPress migration of staging sites</strong>"
295
- msgstr ""
296
-
297
- #. Found in description paragraph.
298
- msgid ""
299
- "Some people are also afraid of installing plugins updates because they follow the rule \"never touch a running system\" with having in mind that untested updates are increasing the risk of breaking their site.\n"
300
- "I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior."
301
- msgstr ""
302
-
303
- #. Found in description paragraph.
304
- msgid ""
305
- "Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a \n"
306
- "up-to-date copy of your website."
307
- msgstr ""
308
-
309
- #. Found in description paragraph.
310
- msgid ""
311
- "Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.\n"
312
- "When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.\n"
313
- "This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)"
314
- msgstr ""
315
-
316
- #. Found in description paragraph.
317
- msgid "<strong>Change your workflow of updating themes and plugins data:</strong>"
318
- msgstr ""
319
-
320
- #. Found in description paragraph.
321
- msgid "WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!"
322
- msgstr ""
323
-
324
- #. Found in description paragraph.
325
- msgid "<em>* Time of creation depends on size of your database and file size</em>"
326
- msgstr ""
327
-
328
- #. Found in description list item.
329
- msgid "Everything running as expected? You are on the save side for migration of all these modifications to your production site!"
330
- msgstr ""
331
-
332
- #. Found in description list item.
333
- msgid "Test everything on your staging site first"
334
- msgstr ""
335
-
336
- #. Found in description list item.
337
- msgid "Customize theme, configuration and plugins or install new plugins"
338
- msgstr ""
339
-
340
- #. Found in description list item.
341
- msgid "Use WP Staging for migration of a production website to a clone site for staging purposes"
342
- msgstr ""
343
-
344
- #. Found in description list item.
345
- msgid "Wordpress migration of wordpress multisites (not tested)"
346
- msgstr ""
347
-
348
- #. Found in description list item.
349
- msgid "Extensive logging if duplication or migration process should fail."
350
- msgstr ""
351
-
352
- #. Found in description list item.
353
- msgid "Admin bar reflects that you are working on a staging site"
354
- msgstr ""
355
-
356
- #. Found in description list item.
357
- msgid "<strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power"
358
- msgstr ""
359
-
360
- #. Found in description list item.
361
- msgid "<strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!"
362
- msgstr ""
363
-
364
- #. Found in description header.
365
- msgid "How to install and setup?"
366
- msgstr ""
367
-
368
- #. Found in description header.
369
- msgid "Important"
370
- msgstr "Wichtig"
371
-
372
- #. Found in description header.
373
- msgid "I need you feedback"
374
- msgstr ""
375
-
376
- #. Found in description header.
377
- msgid "What are the benefits compared to a plugin like Duplicator?"
378
- msgstr ""
379
-
380
- #. Found in description header.
381
- msgid "I just want to migrate the database from one installation to another"
382
- msgstr ""
383
-
384
- #. Found in description header.
385
- msgid " Can´t i just use my local wordpress development copy for testing like xampp / lampp? "
386
- msgstr ""
387
-
388
- #. Found in description header.
389
- msgid " Why should i use a staging website? "
390
- msgstr ""
391
-
392
- #. Found in description header.
393
- msgid "What does not work or is not tested when running wordpress migration?"
394
- msgstr ""
395
-
396
- #. Found in description header.
397
- msgid "Main Features"
398
- msgstr ""
399
-
400
- #. Found in description header.
401
- msgid " WP Staging for WordPress Migration "
402
- msgstr ""
403
-
404
- #. Screenshot description.
405
- msgid "Finish!"
406
- msgstr ""
407
-
408
- #. Screenshot description.
409
- msgid "Step 3. Wordpress Staging site creation in progress"
410
- msgstr ""
411
-
412
- #. Screenshot description.
413
- msgid "Step 2. Scanning your website for files and database tables"
414
- msgstr ""
415
-
416
- #. Screenshot description.
417
- msgid "Step 1. Create new WordPress staging site"
418
  msgstr ""
1
+ # Translation of Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) in Italian
2
+ # This file is distributed under the same license as the Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release) package.
3
+ msgid ""
4
+ msgstr ""
5
+ "PO-Revision-Date: 2015-12-01 15:35:13+0000\n"
6
+ "MIME-Version: 1.0\n"
7
+ "Content-Type: text/plain; charset=UTF-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
+ "X-Generator: GlotPress/2.4.0-alpha\n"
11
+ "Language: it\n"
12
+ "Project-Id-Version: Plugins - WP Staging &#8211; DB &amp; File Duplicator &amp; Migration - Stable Readme (latest release)\n"
13
+
14
+ #. Short description.
15
+ msgid "A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators."
16
+ msgstr ""
17
+
18
+ #. Plugin name.
19
+ msgid "WP Staging - DB &amp; File Duplicator &amp; Migration"
20
+ msgstr ""
21
+
22
+ #. Found in changelog list item.
23
+ msgid "Fix: Missing http(s) scheme after cloning multisites results in not working clones"
24
+ msgstr ""
25
+
26
+ #. Found in changelog list item.
27
+ msgid "New: Support for Windows Azure cloud servers"
28
+ msgstr ""
29
+
30
+ #. Found in changelog list item.
31
+ msgid "Fix: Do not search &amp; replace mail addresses"
32
+ msgstr ""
33
+
34
+ #. Found in changelog list item.
35
+ msgid "Fix: Remove term 'Error' from login page"
36
+ msgstr ""
37
+
38
+ #. Found in changelog list item.
39
+ msgid "New: Login options not needed any more - removed"
40
+ msgstr ""
41
+
42
+ #. Found in changelog list item.
43
+ msgid "New: Detect if wp-config.php has been moved one folder level up"
44
+ msgstr ""
45
+
46
+ #. Found in changelog list item.
47
+ msgid "New: Better error reporting"
48
+ msgstr ""
49
+
50
+ #. Found in changelog list item.
51
+ msgid "New: Compatible up to WordPress 4.9.8"
52
+ msgstr ""
53
+
54
+ #. Found in changelog list item.
55
+ msgid "New: Setting to specify the maximum allowed file size to copy"
56
+ msgstr ""
57
+
58
+ #. Found in changelog list item.
59
+ msgid "Fix: Search &amp; replace path not correct if scheme http/https is not identical in wp-config and db wp_options"
60
+ msgstr ""
61
+
62
+ #. Found in changelog list item.
63
+ msgid "Fix: Creating a new clone resets the custom wp staging user settings. E.g. ignores authentication setting"
64
+ msgstr ""
65
+
66
+ #. Found in changelog list item.
67
+ msgid "Tweak: Remove certain debugging notices from the default log window"
68
+ msgstr ""
69
+
70
+ #. Found in changelog list item.
71
+ msgid "Tweaks: remove term \"error\" from several log entries"
72
+ msgstr ""
73
+
74
+ #. Found in changelog list item.
75
+ msgid "Fix: Search &amp; Replace not working if serialized object contains __PHP_Incomplete_Class_Name"
76
+ msgstr ""
77
+
78
+ #. Found in changelog list item.
79
+ msgid "Fix: Skip search &amp; replace for objects where key is null"
80
+ msgstr ""
81
+
82
+ #. Found in changelog list item.
83
+ msgid "Fix: Parameter must be an array or an object that implements Callable"
84
+ msgstr ""
85
+
86
+ #. Found in changelog list item.
87
+ msgid "Fix: wp-config.php not copied when previous clone updating process has been failed"
88
+ msgstr ""
89
+
90
+ #. Found in changelog list item.
91
+ msgid "Fix: Undefined object $this-&gt;tables"
92
+ msgstr ""
93
+
94
+ #. Found in changelog list item.
95
+ msgid "Fix: Can not open upload folder in file selection menu"
96
+ msgstr ""
97
+
98
+ #. Found in changelog list item.
99
+ msgid "Fix: Progress bar not shown as intented for clone updating process"
100
+ msgstr ""
101
+
102
+ #. Found in changelog list item.
103
+ msgid "Fix: If cloning update process is interupted it may happen that staging site is not available any longer. (Updating the clone does not copy index.php to staging site again)"
104
+ msgstr ""
105
+
106
+ #. Found in changelog list item.
107
+ msgid "Fix: Progress bar for step 'database' is not filling up to 100%"
108
+ msgstr ""
109
+
110
+ #. Found in changelog list item.
111
+ msgid "New: Use the new progress bar for clone updating process"
112
+ msgstr ""
113
+
114
+ #. Found in changelog list item.
115
+ msgid "New: Add multisite informations in system info log"
116
+ msgstr ""
117
+
118
+ #. Found in changelog list item.
119
+ msgid "New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning."
120
+ msgstr ""
121
+
122
+ #. Found in changelog list item.
123
+ msgid "Security: Do not allow to create a new staging site into a subfolder which already exists"
124
+ msgstr ""
125
+
126
+ #. Found in changelog list item.
127
+ msgid "Fix: Missing trailingslash results to wrong absolute paths in database after Search &amp; Replace operation "
128
+ msgstr ""
129
+
130
+ #. Found in changelog list item.
131
+ msgid "New: Compatible to WordPress 4.9.6"
132
+ msgstr ""
133
+
134
+ #. Found in changelog list item.
135
+ msgid "New: Add new setting which allow to specify the search &amp; replace processing query limit"
136
+ msgstr ""
137
+
138
+ #. Found in changelog list item.
139
+ msgid "New: Supports search &amp; replace for revslider image slider and several visual editors which are using non default serialized data"
140
+ msgstr ""
141
+
142
+ #. Found in changelog list item.
143
+ msgid "New: Add filter 'wpstg_fiter_search_replace_rows' to exclude certain tables from search &amp; replace"
144
+ msgstr ""
145
+
146
+ #. Found in changelog list item.
147
+ msgid "New: Add datetime timestamp internally to clone. (Used in WP Staging pro)"
148
+ msgstr ""
149
+
150
+ #. Found in changelog list item.
151
+ msgid "New: Support for custom upload folder. For instance, if upload folder has been renamed and removed outsite wp-content folder"
152
+ msgstr ""
153
+
154
+ #. Found in changelog list item.
155
+ msgid "New: Exclude tables for plugin wp_mail_smtp"
156
+ msgstr ""
157
+
158
+ #. Found in changelog list item.
159
+ msgid "New: Add filter 'wpstg_filter_options_replace' to exclude certain tables from updating while cloning"
160
+ msgstr ""
161
+
162
+ #. Found in changelog list item.
163
+ msgid "Fix: WP_SITEURL &amp; WP_HOME not replaced if constant contains php generated string"
164
+ msgstr ""
165
+
166
+ #. Found in changelog list item.
167
+ msgid "Fix: Serialize replace is not working properly for serialized strings"
168
+ msgstr ""
169
+
170
+ #. Found in faq list item.
171
+ msgid ""
172
+ "I can not login to the staging site\n"
173
+ "If you are using a security plugin like All In One WP Security &amp; Firewall you need to install latest version of WP Staging. \n"
174
+ "Go to WP Staging &gt; Settings and add the slug to the custom login page which you set up in All In One WP Security &amp; Firewall plugin."
175
+ msgstr ""
176
+
177
+ #. Found in installation list item, faq list item.
178
+ msgid "Upload and install it via the WordPress plugin backend wp-admin &gt; plugins &gt; add new &gt; uploads"
179
+ msgstr ""
180
+
181
+ #. Found in installation list item, faq list item.
182
+ msgid "Download the file \"wp-staging.zip\":"
183
+ msgstr ""
184
+
185
+ #. Found in description paragraph.
186
+ msgid ""
187
+ "Permalinks are disabled on the staging site because the staging site is cloned into a subfolder and permalinks are not working on all systems\n"
188
+ "without doing changes to the .htaccess (Apache server) or nginx.conf (Nginx Server).\n"
189
+ "<a href=\"https://wp-staging.com/docs/activate-permalinks-staging-site/\" title=\"activate permalinks on staging site\">Read here</a> how to activate permalinks on the staging site."
190
+ msgstr ""
191
+
192
+ #. Found in description list item.
193
+ msgid "New: Compatible to All In One WP Security &amp; Firewall"
194
+ msgstr ""
195
+
196
+ #. Found in description paragraph.
197
+ msgid "Note: WordPress 5.0 will be shipped with a new visual editor called Gutenberg. Use WP Staging to check if Gutenberg editor is working as intended on your website and that all used plugins are compatible with that new editor."
198
+ msgstr ""
199
+
200
+ #. Found in changelog paragraph.
201
+ msgid "Complete changelog: <a href=\"https://wp-staging.com/changelog.txt\">https://wp-staging.com/changelog.txt</a>"
202
+ msgstr ""
203
+
204
+ #. Found in description paragraph.
205
+ msgid "https://wp-staging.com"
206
+ msgstr ""
207
+
208
+ #. Found in description paragraph.
209
+ msgid ""
210
+ "<strong>This cloning and staging plugin is well tested and work in progress. <br /><br />\n"
211
+ "If you find any issue, please open a <a href=\"https://wp-staging.com/support/\" title=\"support ticket\">support ticket</a>.\n"
212
+ "</strong>\n"
213
+ "<br /><br />\n"
214
+ "<strong>Note: </strong> For pushing &amp; migrating plugins and theme files to live site, check out the pro version <a href=\"https://wp-staging.com/\" title=\"WP Staging Pro\">https://wp-staging.com/</a>\n"
215
+ "<br /><br />"
216
+ msgstr ""
217
+
218
+ #. Found in faq header.
219
+ msgid "Installation Instructions"
220
+ msgstr ""
221
+
222
+ #. Found in description list item.
223
+ msgid ""
224
+ "WordPress duplicating process on windows server (not tested but will probably work) \n"
225
+ "Edit: Duplication on windows server seems to be working well: <a href=\"https://wordpress.org/support/topic/wont-copy-files?replies=5\" title=\"Read more\">Read more</a> "
226
+ msgstr ""
227
+
228
+ #. Found in description list item.
229
+ msgid ""
230
+ "<strong>Safe: </strong> Access to staging site is granted for administrators only.\n"
231
+ "<br /><br />\n"
232
+ "<strong>More safe:</strong> \n"
233
+ "<br />"
234
+ msgstr ""
235
+
236
+ #. Found in description paragraph.
237
+ msgid "[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]"
238
+ msgstr ""
239
+
240
+ #. Found in description header.
241
+ msgid "Official Site"
242
+ msgstr ""
243
+
244
+ #. Found in installation list item, faq list item.
245
+ msgid "Activate the plugin through the 'Plugins' menu in WordPress."
246
+ msgstr "Attivare il plugin tramite il menu 'Plugin' di WordPress"
247
+
248
+ #. Found in description paragraph.
249
+ msgid ""
250
+ "Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.\n"
251
+ "After installation goto the settings page 'Staging' and do your adjustments there."
252
+ msgstr ""
253
+
254
+ #. Found in description paragraph.
255
+ msgid ""
256
+ "This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:\n"
257
+ "Please open a <a href=\"https://wordpress.org/support/plugin/wp-staging/\" title=\"support request\">support request</a> and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines."
258
+ msgstr ""
259
+
260
+ #. Found in description paragraph.
261
+ msgid ""
262
+ "So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using\n"
263
+ "the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!"
264
+ msgstr ""
265
+
266
+ #. Found in description paragraph.
267
+ msgid ""
268
+ "At first, i love the <a href=\"https://wordpress.org/plugins/duplicator/\" title=\"Duplicator plugin\">Duplicator plugin</a>. Duplicator is a great tool for migrating from development site to production one or from production site to development one. \n"
269
+ "The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.\n"
270
+ "However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful."
271
+ msgstr ""
272
+
273
+ #. Found in description paragraph.
274
+ msgid ""
275
+ "If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.\n"
276
+ "WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.\n"
277
+ "Both tools are excellent cooperating eachother."
278
+ msgstr ""
279
+
280
+ #. Found in description paragraph.
281
+ msgid "This is were WP Staging steps in... Site cloning and staging site creation simplified!"
282
+ msgstr ""
283
+
284
+ #. Found in description paragraph.
285
+ msgid ""
286
+ "Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect \n"
287
+ "of your local copy is working on your live website exactely as you would expect it. \n"
288
+ "There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the \n"
289
+ "the cpu performance can lead to unexpected results on your production website. \n"
290
+ "There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform."
291
+ msgstr ""
292
+
293
+ #. Found in description paragraph.
294
+ msgid "<strong> I think its time to change this, so i created \"WP Staging\" for WordPress migration of staging sites</strong>"
295
+ msgstr ""
296
+
297
+ #. Found in description paragraph.
298
+ msgid ""
299
+ "Some people are also afraid of installing plugins updates because they follow the rule \"never touch a running system\" with having in mind that untested updates are increasing the risk of breaking their site.\n"
300
+ "I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior."
301
+ msgstr ""
302
+
303
+ #. Found in description paragraph.
304
+ msgid ""
305
+ "Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a \n"
306
+ "up-to-date copy of your website."
307
+ msgstr ""
308
+
309
+ #. Found in description paragraph.
310
+ msgid ""
311
+ "Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.\n"
312
+ "When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.\n"
313
+ "This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)"
314
+ msgstr ""
315
+
316
+ #. Found in description paragraph.
317
+ msgid "<strong>Change your workflow of updating themes and plugins data:</strong>"
318
+ msgstr ""
319
+
320
+ #. Found in description paragraph.
321
+ msgid "WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!"
322
+ msgstr ""
323
+
324
+ #. Found in description paragraph.
325
+ msgid "<em>* Time of creation depends on size of your database and file size</em>"
326
+ msgstr ""
327
+
328
+ #. Found in description list item.
329
+ msgid "Everything running as expected? You are on the save side for migration of all these modifications to your production site!"
330
+ msgstr ""
331
+
332
+ #. Found in description list item.
333
+ msgid "Test everything on your staging site first"
334
+ msgstr ""
335
+
336
+ #. Found in description list item.
337
+ msgid "Customize theme, configuration and plugins or install new plugins"
338
+ msgstr ""
339
+
340
+ #. Found in description list item.
341
+ msgid "Use WP Staging for migration of a production website to a clone site for staging purposes"
342
+ msgstr ""
343
+
344
+ #. Found in description list item.
345
+ msgid "Wordpress migration of wordpress multisites (not tested)"
346
+ msgstr ""
347
+
348
+ #. Found in description list item.
349
+ msgid "Extensive logging if duplication or migration process should fail."
350
+ msgstr ""
351
+
352
+ #. Found in description list item.
353
+ msgid "Admin bar reflects that you are working on a staging site"
354
+ msgstr ""
355
+
356
+ #. Found in description list item.
357
+ msgid "<strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power"
358
+ msgstr ""
359
+
360
+ #. Found in description list item.
361
+ msgid "<strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!"
362
+ msgstr ""
363
+
364
+ #. Found in description header.
365
+ msgid "How to install and setup?"
366
+ msgstr ""
367
+
368
+ #. Found in description header.
369
+ msgid "Important"
370
+ msgstr "Wichtig"
371
+
372
+ #. Found in description header.
373
+ msgid "I need you feedback"
374
+ msgstr ""
375
+
376
+ #. Found in description header.
377
+ msgid "What are the benefits compared to a plugin like Duplicator?"
378
+ msgstr ""
379
+
380
+ #. Found in description header.
381
+ msgid "I just want to migrate the database from one installation to another"
382
+ msgstr ""
383
+
384
+ #. Found in description header.
385
+ msgid " Can´t i just use my local wordpress development copy for testing like xampp / lampp? "
386
+ msgstr ""
387
+
388
+ #. Found in description header.
389
+ msgid " Why should i use a staging website? "
390
+ msgstr ""
391
+
392
+ #. Found in description header.
393
+ msgid "What does not work or is not tested when running wordpress migration?"
394
+ msgstr ""
395
+
396
+ #. Found in description header.
397
+ msgid "Main Features"
398
+ msgstr ""
399
+
400
+ #. Found in description header.
401
+ msgid " WP Staging for WordPress Migration "
402
+ msgstr ""
403
+
404
+ #. Screenshot description.
405
+ msgid "Finish!"
406
+ msgstr ""
407
+
408
+ #. Screenshot description.
409
+ msgid "Step 3. Wordpress Staging site creation in progress"
410
+ msgstr ""
411
+
412
+ #. Screenshot description.
413
+ msgid "Step 2. Scanning your website for files and database tables"
414
+ msgstr ""
415
+
416
+ #. Screenshot description.
417
+ msgid "Step 1. Create new WordPress staging site"
418
  msgstr ""
readme.txt CHANGED
@@ -9,7 +9,7 @@ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
  Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
  Requires at least: 3.6+
11
  Tested up to: 4.9
12
- Stable tag: 2.3.7
13
  Requires PHP: 5.3
14
 
15
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
@@ -146,6 +146,14 @@ https://wp-staging.com
146
 
147
  == Changelog ==
148
 
 
 
 
 
 
 
 
 
149
  = 2.3.7 =
150
  * New: Increase cloning performance
151
  * New: Show admin notice if php version and wordpress version do not meet requirements.
@@ -194,6 +202,6 @@ Complete changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-sta
194
 
195
  == Upgrade Notice ==
196
 
197
- = 2.3.7 =
198
  * New: Compatible to WordPress 4.9.8. Important fixes!
199
 
9
  Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
  Requires at least: 3.6+
11
  Tested up to: 4.9
12
+ Stable tag: 2.3.8
13
  Requires PHP: 5.3
14
 
15
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
146
 
147
  == Changelog ==
148
 
149
+ = 2.3.8 =
150
+ * Tweak: Lowering default performance settings for more reliability during cloning
151
+ * New: Set WP_CACHE to false in wp-config.php after cloning to prevent log in issues to staging site
152
+ * New: Compatibility mode to skip certain tables from third party plugins from beeing searched & replaced
153
+ * Fix: Do not clone db.php, object-cache.php and advanced-cache.php
154
+ * Fix: Show error message if ajax requests fail for any reason
155
+
156
+
157
  = 2.3.7 =
158
  * New: Increase cloning performance
159
  * New: Show admin notice if php version and wordpress version do not meet requirements.
202
 
203
  == Upgrade Notice ==
204
 
205
+ = 2.3.8 =
206
  * New: Compatible to WordPress 4.9.8. Important fixes!
207
 
wp-staging.php CHANGED
@@ -7,7 +7,7 @@
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
- * Version: 2.3.7
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
 
@@ -51,7 +51,7 @@ if( !defined( 'WPSTG_PLUGIN_URL' ) ) {
51
 
52
  // Version
53
  if( !defined( 'WPSTGPRO_VERSION' ) ) {
54
- define( 'WPSTGPRO_VERSION', '2.3.7' );
55
  }
56
 
57
  /**
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
+ * Version: 2.3.8
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
 
51
 
52
  // Version
53
  if( !defined( 'WPSTGPRO_VERSION' ) ) {
54
+ define( 'WPSTGPRO_VERSION', '2.3.8' );
55
  }
56
 
57
  /**