Version Description
- Fix: Do not search & replace mail addresses
Download this release
Release Info
Developer | ReneHermi |
Plugin | WP Staging – DB & File Duplicator & Migration |
Version | 2.3.2 |
Comparing to | |
See all releases |
Code changes from version 2.3.1 to 2.3.2
- apps/Backend/Modules/Jobs/Cloning.php +274 -274
- apps/Backend/Modules/Jobs/Data.php +727 -727
- apps/Backend/Modules/Jobs/Database.php +281 -281
- apps/Backend/Modules/Jobs/Delete.php +429 -429
- apps/Backend/Modules/Jobs/Files.php +329 -329
- apps/Backend/Modules/Jobs/Job.php +466 -466
- apps/Backend/Modules/Jobs/Scan.php +403 -403
- apps/Backend/Modules/Jobs/SearchReplace.php +708 -694
- apps/Backend/Modules/Jobs/Updating.php +247 -247
- apps/Backend/Modules/SystemInfo.php +502 -502
- apps/Backend/Modules/Views/Forms/Settings.php +237 -237
- apps/Backend/views/clone/ajax/update.php +32 -32
- apps/Backend/views/clone/includes/footer.php +3 -3
- apps/Core/Utils/Helper.php +81 -81
- apps/Core/Utils/Multisite.php +26 -26
- apps/Core/WPStaging.php +429 -429
- readme.txt +200 -289
- wp-staging.php +1 -1
apps/Backend/Modules/Jobs/Cloning.php
CHANGED
@@ -1,275 +1,275 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
3 |
-
|
4 |
-
use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
|
5 |
-
use WPStaging\WPStaging;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class Cloning
|
9 |
-
* @package WPStaging\Backend\Modules\Jobs
|
10 |
-
*/
|
11 |
-
class Cloning extends Job
|
12 |
-
{
|
13 |
-
/**
|
14 |
-
* Initialize is called in \Job
|
15 |
-
*/
|
16 |
-
public function initialize(){
|
17 |
-
$this->db = WPStaging::getInstance()->get("wpdb");
|
18 |
-
}
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Save Chosen Cloning Settings
|
22 |
-
* @return bool
|
23 |
-
*/
|
24 |
-
public function save()
|
25 |
-
{
|
26 |
-
if (!isset($_POST) || !isset($_POST["cloneID"]))
|
27 |
-
{
|
28 |
-
return false;
|
29 |
-
}
|
30 |
-
|
31 |
-
// Generate Options
|
32 |
-
// Clone
|
33 |
-
$this->options->clone = $_POST["cloneID"];
|
34 |
-
$this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
|
35 |
-
$this->options->cloneNumber = 1;
|
36 |
-
$this->options->prefix = $this->setStagingPrefix();
|
37 |
-
$this->options->includedDirectories = array();
|
38 |
-
$this->options->excludedDirectories = array();
|
39 |
-
$this->options->extraDirectories = array();
|
40 |
-
$this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
|
41 |
-
$this->options->currentStep = 0;
|
42 |
-
|
43 |
-
// Job
|
44 |
-
$this->options->job = new \stdClass();
|
45 |
-
|
46 |
-
// Check if clone data already exists and use that one
|
47 |
-
if (isset($this->options->existingClones[$this->options->clone]) )
|
48 |
-
{
|
49 |
-
|
50 |
-
$this->options->cloneNumber = $this->options->existingClones[$this->options->clone]->number;
|
51 |
-
|
52 |
-
$this->options->prefix = isset($this->options->existingClones[$this->options->clone]->prefix) ?
|
53 |
-
$this->options->existingClones[$this->options->clone]->prefix :
|
54 |
-
$this->setStagingPrefix();
|
55 |
-
|
56 |
-
} // Clone does not exist but there are other clones in db
|
57 |
-
// Get data and increment it
|
58 |
-
elseif (!empty($this->options->existingClones))
|
59 |
-
{
|
60 |
-
$this->options->cloneNumber = count($this->options->existingClones)+1;
|
61 |
-
$this->options->prefix = $this->setStagingPrefix();
|
62 |
-
}
|
63 |
-
|
64 |
-
// Included Tables
|
65 |
-
if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"]))
|
66 |
-
{
|
67 |
-
$this->options->tables = $_POST["includedTables"];
|
68 |
-
} else {
|
69 |
-
$this->options->tables = array();
|
70 |
-
}
|
71 |
-
// Excluded Tables
|
72 |
-
// if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
|
73 |
-
// {
|
74 |
-
// $this->options->excludedTables = $_POST["excludedTables"];
|
75 |
-
// }
|
76 |
-
|
77 |
-
// Excluded Directories
|
78 |
-
if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
|
79 |
-
{
|
80 |
-
$this->options->excludedDirectories = $_POST["excludedDirectories"];
|
81 |
-
}
|
82 |
-
|
83 |
-
|
84 |
-
// Excluded Directories TOTAL
|
85 |
-
// Do not copy these folders and plugins
|
86 |
-
$excludedDirectories = array(
|
87 |
-
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
|
88 |
-
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
|
89 |
-
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wp-super-cache',
|
90 |
-
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'peters-login-redirect',
|
91 |
-
);
|
92 |
-
|
93 |
-
$this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
|
94 |
-
|
95 |
-
// Included Directories
|
96 |
-
if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
|
97 |
-
{
|
98 |
-
$this->options->includedDirectories = $_POST["includedDirectories"];
|
99 |
-
}
|
100 |
-
|
101 |
-
// Extra Directories
|
102 |
-
if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
|
103 |
-
{
|
104 |
-
$this->options->extraDirectories = $_POST["extraDirectories"];
|
105 |
-
}
|
106 |
-
|
107 |
-
// Directories to Copy
|
108 |
-
$this->options->directoriesToCopy = array_merge(
|
109 |
-
$this->options->includedDirectories,
|
110 |
-
$this->options->extraDirectories
|
111 |
-
);
|
112 |
-
|
113 |
-
array_unshift($this->options->directoriesToCopy, ABSPATH);
|
114 |
-
|
115 |
-
// Delete files to copy listing
|
116 |
-
$this->cache->delete("files_to_copy");
|
117 |
-
|
118 |
-
return $this->saveOptions();
|
119 |
-
}
|
120 |
-
|
121 |
-
/**
|
122 |
-
* Create a new staging prefix which does not already exists in database
|
123 |
-
*/
|
124 |
-
private function setStagingPrefix() {
|
125 |
-
|
126 |
-
// Get & find a new prefix that does not already exist in database.
|
127 |
-
// Loop through up to 1000 different possible prefixes should be enough here;)
|
128 |
-
for($i=0; $i <= 10000; $i++){
|
129 |
-
$this->options->prefix = isset($this->options->existingClones) ?
|
130 |
-
'wpstg' . (count($this->options->existingClones)+$i) . '_' :
|
131 |
-
'wpstg' . $i . '_';
|
132 |
-
|
133 |
-
$sql = "SHOW TABLE STATUS LIKE '{$this->options->prefix}%'";
|
134 |
-
$tables = $this->db->get_results($sql);
|
135 |
-
|
136 |
-
// Prefix does not exists. We can use it
|
137 |
-
if (!$tables){
|
138 |
-
//$this->returnException('new ' . $this->options->prefix);
|
139 |
-
return $this->options->prefix;
|
140 |
-
}
|
141 |
-
}
|
142 |
-
$this->returnException("Fatal Error: Can not create staging prefix. '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
|
143 |
-
wp_die("Fatal Error: Can not create staging prefix. Prefix '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
|
144 |
-
}
|
145 |
-
|
146 |
-
/**
|
147 |
-
* Check if potential new prefix of staging site would be identical with live site.
|
148 |
-
* @return boolean
|
149 |
-
*/
|
150 |
-
private function isPrefixIdentical(){
|
151 |
-
$db = WPStaging::getInstance()->get("wpdb");
|
152 |
-
|
153 |
-
$livePrefix = $db->prefix;
|
154 |
-
$stagingPrefix = $this->options->prefix;
|
155 |
-
|
156 |
-
if ($livePrefix == $stagingPrefix){
|
157 |
-
return true;
|
158 |
-
}
|
159 |
-
return false;
|
160 |
-
}
|
161 |
-
|
162 |
-
/**
|
163 |
-
* Start the cloning job
|
164 |
-
*/
|
165 |
-
public function start()
|
166 |
-
{
|
167 |
-
if (null === $this->options->currentJob)
|
168 |
-
{
|
169 |
-
$this->log("Cloning job for {$this->options->clone} finished");
|
170 |
-
return true;
|
171 |
-
}
|
172 |
-
|
173 |
-
$methodName = "job" . ucwords($this->options->currentJob);
|
174 |
-
|
175 |
-
if (!method_exists($this, $methodName))
|
176 |
-
{
|
177 |
-
$this->log("Can't execute job; Job's method {$methodName} is not found");
|
178 |
-
throw new JobNotFoundException($methodName);
|
179 |
-
}
|
180 |
-
|
181 |
-
// Call the job
|
182 |
-
//$this->log("execute job: Job's method {$methodName}");
|
183 |
-
return $this->{$methodName}();
|
184 |
-
}
|
185 |
-
|
186 |
-
/**
|
187 |
-
* @param object $response
|
188 |
-
* @param string $nextJob
|
189 |
-
* @return object
|
190 |
-
*/
|
191 |
-
private function handleJobResponse($response, $nextJob)
|
192 |
-
{
|
193 |
-
// Job is not done
|
194 |
-
if (true !== $response->status)
|
195 |
-
{
|
196 |
-
return $response;
|
197 |
-
}
|
198 |
-
|
199 |
-
$this->options->currentJob = $nextJob;
|
200 |
-
$this->options->currentStep = 0;
|
201 |
-
$this->options->totalSteps = 0;
|
202 |
-
|
203 |
-
// Save options
|
204 |
-
$this->saveOptions();
|
205 |
-
|
206 |
-
return $response;
|
207 |
-
}
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
/**
|
214 |
-
* Clone Database
|
215 |
-
* @return object
|
216 |
-
*/
|
217 |
-
public function jobDatabase()
|
218 |
-
{
|
219 |
-
$database = new Database();
|
220 |
-
return $this->handleJobResponse($database->start(), "SearchReplace");
|
221 |
-
}
|
222 |
-
|
223 |
-
/**
|
224 |
-
* Search & Replace
|
225 |
-
* @return object
|
226 |
-
*/
|
227 |
-
public function jobSearchReplace()
|
228 |
-
{
|
229 |
-
$searchReplace = new SearchReplace();
|
230 |
-
return $this->handleJobResponse($searchReplace->start(), "directories");
|
231 |
-
}
|
232 |
-
|
233 |
-
/**
|
234 |
-
* Get All Files From Selected Directories Recursively Into a File
|
235 |
-
* @return object
|
236 |
-
*/
|
237 |
-
public function jobDirectories()
|
238 |
-
{
|
239 |
-
$directories = new Directories();
|
240 |
-
return $this->handleJobResponse($directories->start(), "files");
|
241 |
-
}
|
242 |
-
|
243 |
-
/**
|
244 |
-
* Copy Files
|
245 |
-
* @return object
|
246 |
-
*/
|
247 |
-
public function jobFiles()
|
248 |
-
{
|
249 |
-
$files = new Files();
|
250 |
-
return $this->handleJobResponse($files->start(), "data");
|
251 |
-
}
|
252 |
-
|
253 |
-
/**
|
254 |
-
* Replace Data
|
255 |
-
* @return object
|
256 |
-
*/
|
257 |
-
public function jobData()
|
258 |
-
{
|
259 |
-
$data = new Data();
|
260 |
-
return $this->handleJobResponse($data->start(), "finish");
|
261 |
-
}
|
262 |
-
|
263 |
-
|
264 |
-
/**
|
265 |
-
* Save Clone Data
|
266 |
-
* @return object
|
267 |
-
*/
|
268 |
-
public function jobFinish()
|
269 |
-
{
|
270 |
-
$finish = new Finish();
|
271 |
-
return $this->handleJobResponse($finish->start(), '');
|
272 |
-
}
|
273 |
-
|
274 |
-
|
275 |
}
|
1 |
+
<?php
|
2 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
3 |
+
|
4 |
+
use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
|
5 |
+
use WPStaging\WPStaging;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Cloning
|
9 |
+
* @package WPStaging\Backend\Modules\Jobs
|
10 |
+
*/
|
11 |
+
class Cloning extends Job
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Initialize is called in \Job
|
15 |
+
*/
|
16 |
+
public function initialize(){
|
17 |
+
$this->db = WPStaging::getInstance()->get("wpdb");
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Save Chosen Cloning Settings
|
22 |
+
* @return bool
|
23 |
+
*/
|
24 |
+
public function save()
|
25 |
+
{
|
26 |
+
if (!isset($_POST) || !isset($_POST["cloneID"]))
|
27 |
+
{
|
28 |
+
return false;
|
29 |
+
}
|
30 |
+
|
31 |
+
// Generate Options
|
32 |
+
// Clone
|
33 |
+
$this->options->clone = $_POST["cloneID"];
|
34 |
+
$this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
|
35 |
+
$this->options->cloneNumber = 1;
|
36 |
+
$this->options->prefix = $this->setStagingPrefix();
|
37 |
+
$this->options->includedDirectories = array();
|
38 |
+
$this->options->excludedDirectories = array();
|
39 |
+
$this->options->extraDirectories = array();
|
40 |
+
$this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
|
41 |
+
$this->options->currentStep = 0;
|
42 |
+
|
43 |
+
// Job
|
44 |
+
$this->options->job = new \stdClass();
|
45 |
+
|
46 |
+
// Check if clone data already exists and use that one
|
47 |
+
if (isset($this->options->existingClones[$this->options->clone]) )
|
48 |
+
{
|
49 |
+
|
50 |
+
$this->options->cloneNumber = $this->options->existingClones[$this->options->clone]->number;
|
51 |
+
|
52 |
+
$this->options->prefix = isset($this->options->existingClones[$this->options->clone]->prefix) ?
|
53 |
+
$this->options->existingClones[$this->options->clone]->prefix :
|
54 |
+
$this->setStagingPrefix();
|
55 |
+
|
56 |
+
} // Clone does not exist but there are other clones in db
|
57 |
+
// Get data and increment it
|
58 |
+
elseif (!empty($this->options->existingClones))
|
59 |
+
{
|
60 |
+
$this->options->cloneNumber = count($this->options->existingClones)+1;
|
61 |
+
$this->options->prefix = $this->setStagingPrefix();
|
62 |
+
}
|
63 |
+
|
64 |
+
// Included Tables
|
65 |
+
if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"]))
|
66 |
+
{
|
67 |
+
$this->options->tables = $_POST["includedTables"];
|
68 |
+
} else {
|
69 |
+
$this->options->tables = array();
|
70 |
+
}
|
71 |
+
// Excluded Tables
|
72 |
+
// if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
|
73 |
+
// {
|
74 |
+
// $this->options->excludedTables = $_POST["excludedTables"];
|
75 |
+
// }
|
76 |
+
|
77 |
+
// Excluded Directories
|
78 |
+
if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
|
79 |
+
{
|
80 |
+
$this->options->excludedDirectories = $_POST["excludedDirectories"];
|
81 |
+
}
|
82 |
+
|
83 |
+
|
84 |
+
// Excluded Directories TOTAL
|
85 |
+
// Do not copy these folders and plugins
|
86 |
+
$excludedDirectories = array(
|
87 |
+
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
|
88 |
+
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login',
|
89 |
+
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wp-super-cache',
|
90 |
+
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'peters-login-redirect',
|
91 |
+
);
|
92 |
+
|
93 |
+
$this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
|
94 |
+
|
95 |
+
// Included Directories
|
96 |
+
if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
|
97 |
+
{
|
98 |
+
$this->options->includedDirectories = $_POST["includedDirectories"];
|
99 |
+
}
|
100 |
+
|
101 |
+
// Extra Directories
|
102 |
+
if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
|
103 |
+
{
|
104 |
+
$this->options->extraDirectories = $_POST["extraDirectories"];
|
105 |
+
}
|
106 |
+
|
107 |
+
// Directories to Copy
|
108 |
+
$this->options->directoriesToCopy = array_merge(
|
109 |
+
$this->options->includedDirectories,
|
110 |
+
$this->options->extraDirectories
|
111 |
+
);
|
112 |
+
|
113 |
+
array_unshift($this->options->directoriesToCopy, ABSPATH);
|
114 |
+
|
115 |
+
// Delete files to copy listing
|
116 |
+
$this->cache->delete("files_to_copy");
|
117 |
+
|
118 |
+
return $this->saveOptions();
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Create a new staging prefix which does not already exists in database
|
123 |
+
*/
|
124 |
+
private function setStagingPrefix() {
|
125 |
+
|
126 |
+
// Get & find a new prefix that does not already exist in database.
|
127 |
+
// Loop through up to 1000 different possible prefixes should be enough here;)
|
128 |
+
for($i=0; $i <= 10000; $i++){
|
129 |
+
$this->options->prefix = isset($this->options->existingClones) ?
|
130 |
+
'wpstg' . (count($this->options->existingClones)+$i) . '_' :
|
131 |
+
'wpstg' . $i . '_';
|
132 |
+
|
133 |
+
$sql = "SHOW TABLE STATUS LIKE '{$this->options->prefix}%'";
|
134 |
+
$tables = $this->db->get_results($sql);
|
135 |
+
|
136 |
+
// Prefix does not exists. We can use it
|
137 |
+
if (!$tables){
|
138 |
+
//$this->returnException('new ' . $this->options->prefix);
|
139 |
+
return $this->options->prefix;
|
140 |
+
}
|
141 |
+
}
|
142 |
+
$this->returnException("Fatal Error: Can not create staging prefix. '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
|
143 |
+
wp_die("Fatal Error: Can not create staging prefix. Prefix '{$this->options->prefix}' already exists! Stopping for security reasons. Contact support@wp-staging.com");
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Check if potential new prefix of staging site would be identical with live site.
|
148 |
+
* @return boolean
|
149 |
+
*/
|
150 |
+
private function isPrefixIdentical(){
|
151 |
+
$db = WPStaging::getInstance()->get("wpdb");
|
152 |
+
|
153 |
+
$livePrefix = $db->prefix;
|
154 |
+
$stagingPrefix = $this->options->prefix;
|
155 |
+
|
156 |
+
if ($livePrefix == $stagingPrefix){
|
157 |
+
return true;
|
158 |
+
}
|
159 |
+
return false;
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Start the cloning job
|
164 |
+
*/
|
165 |
+
public function start()
|
166 |
+
{
|
167 |
+
if (null === $this->options->currentJob)
|
168 |
+
{
|
169 |
+
$this->log("Cloning job for {$this->options->clone} finished");
|
170 |
+
return true;
|
171 |
+
}
|
172 |
+
|
173 |
+
$methodName = "job" . ucwords($this->options->currentJob);
|
174 |
+
|
175 |
+
if (!method_exists($this, $methodName))
|
176 |
+
{
|
177 |
+
$this->log("Can't execute job; Job's method {$methodName} is not found");
|
178 |
+
throw new JobNotFoundException($methodName);
|
179 |
+
}
|
180 |
+
|
181 |
+
// Call the job
|
182 |
+
//$this->log("execute job: Job's method {$methodName}");
|
183 |
+
return $this->{$methodName}();
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* @param object $response
|
188 |
+
* @param string $nextJob
|
189 |
+
* @return object
|
190 |
+
*/
|
191 |
+
private function handleJobResponse($response, $nextJob)
|
192 |
+
{
|
193 |
+
// Job is not done
|
194 |
+
if (true !== $response->status)
|
195 |
+
{
|
196 |
+
return $response;
|
197 |
+
}
|
198 |
+
|
199 |
+
$this->options->currentJob = $nextJob;
|
200 |
+
$this->options->currentStep = 0;
|
201 |
+
$this->options->totalSteps = 0;
|
202 |
+
|
203 |
+
// Save options
|
204 |
+
$this->saveOptions();
|
205 |
+
|
206 |
+
return $response;
|
207 |
+
}
|
208 |
+
|
209 |
+
|
210 |
+
|
211 |
+
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Clone Database
|
215 |
+
* @return object
|
216 |
+
*/
|
217 |
+
public function jobDatabase()
|
218 |
+
{
|
219 |
+
$database = new Database();
|
220 |
+
return $this->handleJobResponse($database->start(), "SearchReplace");
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Search & Replace
|
225 |
+
* @return object
|
226 |
+
*/
|
227 |
+
public function jobSearchReplace()
|
228 |
+
{
|
229 |
+
$searchReplace = new SearchReplace();
|
230 |
+
return $this->handleJobResponse($searchReplace->start(), "directories");
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Get All Files From Selected Directories Recursively Into a File
|
235 |
+
* @return object
|
236 |
+
*/
|
237 |
+
public function jobDirectories()
|
238 |
+
{
|
239 |
+
$directories = new Directories();
|
240 |
+
return $this->handleJobResponse($directories->start(), "files");
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Copy Files
|
245 |
+
* @return object
|
246 |
+
*/
|
247 |
+
public function jobFiles()
|
248 |
+
{
|
249 |
+
$files = new Files();
|
250 |
+
return $this->handleJobResponse($files->start(), "data");
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Replace Data
|
255 |
+
* @return object
|
256 |
+
*/
|
257 |
+
public function jobData()
|
258 |
+
{
|
259 |
+
$data = new Data();
|
260 |
+
return $this->handleJobResponse($data->start(), "finish");
|
261 |
+
}
|
262 |
+
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Save Clone Data
|
266 |
+
* @return object
|
267 |
+
*/
|
268 |
+
public function jobFinish()
|
269 |
+
{
|
270 |
+
$finish = new Finish();
|
271 |
+
return $this->handleJobResponse($finish->start(), '');
|
272 |
+
}
|
273 |
+
|
274 |
+
|
275 |
}
|
apps/Backend/Modules/Jobs/Data.php
CHANGED
@@ -1,727 +1,727 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\Utils\Logger;
|
11 |
-
use WPStaging\WPStaging;
|
12 |
-
use WPStaging\Utils\Helper;
|
13 |
-
use WPStaging\Utils\Strings;
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Class Data
|
17 |
-
* @package WPStaging\Backend\Modules\Jobs
|
18 |
-
*/
|
19 |
-
class Data extends JobExecutable {
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @var \wpdb
|
23 |
-
*/
|
24 |
-
private $db;
|
25 |
-
|
26 |
-
/**
|
27 |
-
* @var string
|
28 |
-
*/
|
29 |
-
private $prefix;
|
30 |
-
|
31 |
-
/**
|
32 |
-
*
|
33 |
-
* @var string
|
34 |
-
*/
|
35 |
-
private $homeUrl;
|
36 |
-
|
37 |
-
/**
|
38 |
-
* Tables e.g wpstg3_options
|
39 |
-
* @var array
|
40 |
-
*/
|
41 |
-
private $tables;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Initialize
|
45 |
-
*/
|
46 |
-
public function initialize() {
|
47 |
-
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
48 |
-
|
49 |
-
$this->prefix = $this->options->prefix;
|
50 |
-
|
51 |
-
$this->getTables();
|
52 |
-
|
53 |
-
$helper = new Helper();
|
54 |
-
|
55 |
-
$this->homeUrl = $helper->get_home_url();
|
56 |
-
|
57 |
-
|
58 |
-
// Fix current step
|
59 |
-
if( 0 == $this->options->currentStep ) {
|
60 |
-
$this->options->currentStep = 1;
|
61 |
-
}
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Get a list of tables to copy
|
66 |
-
*/
|
67 |
-
private function getTables() {
|
68 |
-
$strings = new Strings();
|
69 |
-
$this->tables = array();
|
70 |
-
foreach ( $this->options->tables as $table ) {
|
71 |
-
$this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
|
72 |
-
}
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
77 |
-
* @return void
|
78 |
-
*/
|
79 |
-
protected function calculateTotalSteps() {
|
80 |
-
$this->options->totalSteps = 12;
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* Start Module
|
85 |
-
* @return object
|
86 |
-
*/
|
87 |
-
public function start() {
|
88 |
-
// Execute steps
|
89 |
-
$this->run();
|
90 |
-
|
91 |
-
// Save option, progress
|
92 |
-
$this->saveOptions();
|
93 |
-
|
94 |
-
return ( object ) $this->response;
|
95 |
-
}
|
96 |
-
|
97 |
-
/**
|
98 |
-
* Execute the Current Step
|
99 |
-
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
100 |
-
* @return bool
|
101 |
-
*/
|
102 |
-
protected function execute() {
|
103 |
-
// Fatal error. Let this happen never and break here immediately
|
104 |
-
if( $this->isRoot() ) {
|
105 |
-
return false;
|
106 |
-
}
|
107 |
-
|
108 |
-
// Over limits threshold
|
109 |
-
if( $this->isOverThreshold() ) {
|
110 |
-
// Prepare response and save current progress
|
111 |
-
$this->prepareResponse( false, false );
|
112 |
-
$this->saveOptions();
|
113 |
-
return false;
|
114 |
-
}
|
115 |
-
|
116 |
-
// No more steps, finished
|
117 |
-
if( $this->isFinished() ) {
|
118 |
-
$this->prepareResponse( true, false );
|
119 |
-
return false;
|
120 |
-
}
|
121 |
-
|
122 |
-
// Execute step
|
123 |
-
$stepMethodName = "step" . $this->options->currentStep;
|
124 |
-
if( !$this->{$stepMethodName}() ) {
|
125 |
-
$this->prepareResponse( false, false );
|
126 |
-
return false;
|
127 |
-
}
|
128 |
-
|
129 |
-
// Prepare Response
|
130 |
-
$this->prepareResponse();
|
131 |
-
|
132 |
-
// Not finished
|
133 |
-
return true;
|
134 |
-
}
|
135 |
-
|
136 |
-
/**
|
137 |
-
* Checks Whether There is Any Job to Execute or Not
|
138 |
-
* @return bool
|
139 |
-
*/
|
140 |
-
protected function isFinished() {
|
141 |
-
return (
|
142 |
-
$this->options->currentStep > $this->options->totalSteps ||
|
143 |
-
!method_exists( $this, "step" . $this->options->currentStep )
|
144 |
-
);
|
145 |
-
}
|
146 |
-
|
147 |
-
/**
|
148 |
-
* Check if current operation is done on the root folder or on the live DB
|
149 |
-
* @return boolean
|
150 |
-
*/
|
151 |
-
protected function isRoot() {
|
152 |
-
|
153 |
-
// Prefix is the same as the one of live site
|
154 |
-
$wpdb = WPStaging::getInstance()->get( "wpdb" );
|
155 |
-
if( $wpdb->prefix === $this->prefix ) {
|
156 |
-
return true;
|
157 |
-
}
|
158 |
-
|
159 |
-
// CloneName is empty
|
160 |
-
$name = ( array ) $this->options->cloneDirectoryName;
|
161 |
-
if( empty( $name ) ) {
|
162 |
-
return true;
|
163 |
-
}
|
164 |
-
|
165 |
-
// Live Path === Staging path
|
166 |
-
if( $this->homeUrl . $this->options->cloneDirectoryName === $this->homeUrl ) {
|
167 |
-
return true;
|
168 |
-
}
|
169 |
-
|
170 |
-
return false;
|
171 |
-
}
|
172 |
-
|
173 |
-
/**
|
174 |
-
* Check if table exists
|
175 |
-
* @param string $table
|
176 |
-
* @return boolean
|
177 |
-
*/
|
178 |
-
protected function isTable( $table ) {
|
179 |
-
if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
|
180 |
-
$this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
|
181 |
-
return false;
|
182 |
-
}
|
183 |
-
return true;
|
184 |
-
}
|
185 |
-
|
186 |
-
/**
|
187 |
-
* Get the install sub directory if WP is installed in sub directory
|
188 |
-
* @return string
|
189 |
-
*/
|
190 |
-
protected function getSubDir() {
|
191 |
-
$home = get_option( 'home' );
|
192 |
-
$siteurl = get_option( 'siteurl' );
|
193 |
-
|
194 |
-
if( empty( $home ) || empty( $siteurl ) ) {
|
195 |
-
return '/';
|
196 |
-
}
|
197 |
-
|
198 |
-
$dir = str_replace( $home, '', $siteurl );
|
199 |
-
return '/' . str_replace( '/', '', $dir ) . '/';
|
200 |
-
}
|
201 |
-
|
202 |
-
/**
|
203 |
-
* Replace "siteurl" and "home"
|
204 |
-
* @return bool
|
205 |
-
*/
|
206 |
-
protected function step1() {
|
207 |
-
$this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
|
208 |
-
|
209 |
-
// Skip - Table does not exist
|
210 |
-
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
211 |
-
return true;
|
212 |
-
}
|
213 |
-
// Skip - Table is not selected or updated
|
214 |
-
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
215 |
-
$this->log( "Preparing Data Step1: Skipping" );
|
216 |
-
return true;
|
217 |
-
}
|
218 |
-
|
219 |
-
// Installed in sub-directory
|
220 |
-
if( $this->isSubDir() ) {
|
221 |
-
$this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName );
|
222 |
-
// Replace URLs
|
223 |
-
$result = $this->db->query(
|
224 |
-
$this->db->prepare(
|
225 |
-
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName
|
226 |
-
)
|
227 |
-
);
|
228 |
-
} else {
|
229 |
-
$this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName );
|
230 |
-
// Replace URLs
|
231 |
-
$result = $this->db->query(
|
232 |
-
$this->db->prepare(
|
233 |
-
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->homeUrl . '/' . $this->options->cloneDirectoryName
|
234 |
-
)
|
235 |
-
);
|
236 |
-
}
|
237 |
-
|
238 |
-
|
239 |
-
// All good
|
240 |
-
if( $result ) {
|
241 |
-
return true;
|
242 |
-
}
|
243 |
-
|
244 |
-
$this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
245 |
-
return false;
|
246 |
-
}
|
247 |
-
|
248 |
-
/**
|
249 |
-
* Update "wpstg_is_staging_site"
|
250 |
-
* @return bool
|
251 |
-
*/
|
252 |
-
protected function step2() {
|
253 |
-
|
254 |
-
$this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
|
255 |
-
|
256 |
-
// Skip - Table does not exist
|
257 |
-
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
258 |
-
return true;
|
259 |
-
}
|
260 |
-
// Skip - Table is not selected or updated
|
261 |
-
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
262 |
-
$this->log( "Preparing Data Step2: Skipping" );
|
263 |
-
return true;
|
264 |
-
}
|
265 |
-
|
266 |
-
$result = $this->db->query(
|
267 |
-
$this->db->prepare(
|
268 |
-
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
|
269 |
-
)
|
270 |
-
);
|
271 |
-
|
272 |
-
// No errors but no option name such as wpstg_is_staging_site
|
273 |
-
if( '' === $this->db->last_error && 0 == $result ) {
|
274 |
-
$result = $this->db->query(
|
275 |
-
$this->db->prepare(
|
276 |
-
"INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
|
277 |
-
)
|
278 |
-
);
|
279 |
-
}
|
280 |
-
|
281 |
-
// All good
|
282 |
-
if( $result ) {
|
283 |
-
return true;
|
284 |
-
}
|
285 |
-
|
286 |
-
$this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
287 |
-
return false;
|
288 |
-
}
|
289 |
-
|
290 |
-
/**
|
291 |
-
* Update rewrite_rules
|
292 |
-
* @return bool
|
293 |
-
*/
|
294 |
-
protected function step3() {
|
295 |
-
|
296 |
-
$this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
|
297 |
-
|
298 |
-
// Skip - Table does not exist
|
299 |
-
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
300 |
-
return true;
|
301 |
-
}
|
302 |
-
|
303 |
-
// Skip - Table is not selected or updated
|
304 |
-
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
305 |
-
$this->log( "Preparing Data Step3: Skipping" );
|
306 |
-
return true;
|
307 |
-
}
|
308 |
-
|
309 |
-
$result = $this->db->query(
|
310 |
-
$this->db->prepare(
|
311 |
-
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
|
312 |
-
)
|
313 |
-
);
|
314 |
-
|
315 |
-
// All good
|
316 |
-
if( $result ) {
|
317 |
-
return true;
|
318 |
-
}
|
319 |
-
|
320 |
-
$this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
321 |
-
return true;
|
322 |
-
}
|
323 |
-
|
324 |
-
/**
|
325 |
-
* Update Table Prefix in wp_usermeta and wp_options
|
326 |
-
* @return bool
|
327 |
-
*/
|
328 |
-
protected function step4() {
|
329 |
-
$this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
|
330 |
-
|
331 |
-
// Skip - Table does not exist
|
332 |
-
if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
|
333 |
-
return true;
|
334 |
-
}
|
335 |
-
|
336 |
-
// Skip - Table is not selected or updated
|
337 |
-
if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
|
338 |
-
$this->log( "Preparing Data Step4: Skipping" );
|
339 |
-
return true;
|
340 |
-
}
|
341 |
-
|
342 |
-
$update = $this->db->query(
|
343 |
-
$this->db->prepare(
|
344 |
-
"UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
|
345 |
-
)
|
346 |
-
);
|
347 |
-
|
348 |
-
if( !$update ) {
|
349 |
-
$this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
|
350 |
-
$this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
|
351 |
-
return false;
|
352 |
-
}
|
353 |
-
|
354 |
-
// if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
355 |
-
// return true;
|
356 |
-
// }
|
357 |
-
// $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
358 |
-
//
|
359 |
-
// // Filter the rows below. Do not update them!
|
360 |
-
// $filters = array(
|
361 |
-
// 'wp_mail_smtp',
|
362 |
-
// 'wp_mail_smtp_version',
|
363 |
-
// 'wp_mail_smtp_debug',
|
364 |
-
// );
|
365 |
-
//
|
366 |
-
// $filters = apply_filters('wpstg_filter_options_replace', $filters);
|
367 |
-
//
|
368 |
-
// $where = "";
|
369 |
-
// foreach($filters as $filter){
|
370 |
-
// $where .= " AND option_name <> '" . $filter . "'";
|
371 |
-
// }
|
372 |
-
//
|
373 |
-
// $updateOptions = $this->db->query(
|
374 |
-
// $this->db->prepare(
|
375 |
-
// "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
|
376 |
-
// )
|
377 |
-
// );
|
378 |
-
//
|
379 |
-
// if( !$updateOptions ) {
|
380 |
-
// $this->log( "Preparing Data Step4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
|
381 |
-
// $this->returnException( "Data Crunching Step 4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
382 |
-
// return false;
|
383 |
-
// }
|
384 |
-
|
385 |
-
return true;
|
386 |
-
}
|
387 |
-
|
388 |
-
/**
|
389 |
-
* Update $table_prefix in wp-config.php
|
390 |
-
* @return bool
|
391 |
-
*/
|
392 |
-
protected function step5() {
|
393 |
-
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
|
394 |
-
|
395 |
-
$this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
|
396 |
-
if( false === ($content = file_get_contents( $path )) ) {
|
397 |
-
$this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
|
398 |
-
return false;
|
399 |
-
}
|
400 |
-
|
401 |
-
// Replace table prefix
|
402 |
-
$content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
|
403 |
-
|
404 |
-
// Replace URLs
|
405 |
-
$content = str_replace( $this->homeUrl, $this->homeUrl . '/' . $this->options->cloneDirectoryName, $content );
|
406 |
-
|
407 |
-
if( false === @file_put_contents( $path, $content ) ) {
|
408 |
-
$this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
|
409 |
-
return false;
|
410 |
-
}
|
411 |
-
|
412 |
-
return true;
|
413 |
-
}
|
414 |
-
|
415 |
-
/**
|
416 |
-
* Reset index.php to original file
|
417 |
-
* This is needed if live site is located in subfolder
|
418 |
-
* Check first if main wordpress is used in subfolder and index.php in parent directory
|
419 |
-
* @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
|
420 |
-
* @return bool
|
421 |
-
*/
|
422 |
-
protected function step6() {
|
423 |
-
|
424 |
-
if( !$this->isSubDir() ) {
|
425 |
-
$this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
|
426 |
-
return true;
|
427 |
-
}
|
428 |
-
|
429 |
-
$path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
|
430 |
-
|
431 |
-
if( false === ($content = file_get_contents( $path )) ) {
|
432 |
-
$this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
|
433 |
-
return false;
|
434 |
-
}
|
435 |
-
|
436 |
-
|
437 |
-
if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
|
438 |
-
$this->log(
|
439 |
-
"Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
|
440 |
-
);
|
441 |
-
return false;
|
442 |
-
}
|
443 |
-
$this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
|
444 |
-
|
445 |
-
$pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
|
446 |
-
|
447 |
-
$replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
|
448 |
-
$replace.= " // Changed by WP-Staging";
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
453 |
-
$this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
|
454 |
-
return false;
|
455 |
-
}
|
456 |
-
|
457 |
-
if( false === @file_put_contents( $path, $content ) ) {
|
458 |
-
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
459 |
-
return false;
|
460 |
-
}
|
461 |
-
$this->Log( "Preparing Data: Finished Step 6 successfully" );
|
462 |
-
return true;
|
463 |
-
}
|
464 |
-
|
465 |
-
/**
|
466 |
-
* Update wpstg_rmpermalinks_executed
|
467 |
-
* @return bool
|
468 |
-
*/
|
469 |
-
protected function step7() {
|
470 |
-
|
471 |
-
$this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
|
472 |
-
|
473 |
-
// Skip - Table does not exist
|
474 |
-
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
475 |
-
return true;
|
476 |
-
}
|
477 |
-
|
478 |
-
// Skip - Table is not selected or updated
|
479 |
-
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
480 |
-
$this->log( "Preparing Data Step7: Skipping" );
|
481 |
-
return true;
|
482 |
-
}
|
483 |
-
|
484 |
-
$result = $this->db->query(
|
485 |
-
$this->db->prepare(
|
486 |
-
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
|
487 |
-
)
|
488 |
-
);
|
489 |
-
|
490 |
-
// All good
|
491 |
-
if( $result ) {
|
492 |
-
$this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
|
493 |
-
return true;
|
494 |
-
}
|
495 |
-
|
496 |
-
$this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
|
497 |
-
return true;
|
498 |
-
}
|
499 |
-
|
500 |
-
/**
|
501 |
-
* Update permalink_structure
|
502 |
-
* @return bool
|
503 |
-
*/
|
504 |
-
protected function step8() {
|
505 |
-
|
506 |
-
$this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
|
507 |
-
|
508 |
-
// Skip - Table does not exist
|
509 |
-
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
510 |
-
return true;
|
511 |
-
}
|
512 |
-
|
513 |
-
// Skip - Table is not selected or updated
|
514 |
-
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
515 |
-
$this->log( "Preparing Data Step8: Skipping" );
|
516 |
-
return true;
|
517 |
-
}
|
518 |
-
|
519 |
-
$result = $this->db->query(
|
520 |
-
$this->db->prepare(
|
521 |
-
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
|
522 |
-
)
|
523 |
-
);
|
524 |
-
|
525 |
-
// All good
|
526 |
-
if( $result ) {
|
527 |
-
$this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
|
528 |
-
return true;
|
529 |
-
}
|
530 |
-
|
531 |
-
$this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
532 |
-
return true;
|
533 |
-
}
|
534 |
-
|
535 |
-
/**
|
536 |
-
* Update blog_public option to not allow staging site to be indexed by search engines
|
537 |
-
* @return bool
|
538 |
-
*/
|
539 |
-
protected function step9() {
|
540 |
-
|
541 |
-
$this->log( "Preparing Data Step9: Set staging site to noindex" );
|
542 |
-
|
543 |
-
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
544 |
-
return true;
|
545 |
-
}
|
546 |
-
|
547 |
-
// Skip - Table is not selected or updated
|
548 |
-
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
549 |
-
$this->log( "Preparing Data Step9: Skipping" );
|
550 |
-
return true;
|
551 |
-
}
|
552 |
-
|
553 |
-
$result = $this->db->query(
|
554 |
-
$this->db->prepare(
|
555 |
-
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
|
556 |
-
)
|
557 |
-
);
|
558 |
-
|
559 |
-
// All good
|
560 |
-
if( $result ) {
|
561 |
-
$this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
|
562 |
-
return true;
|
563 |
-
}
|
564 |
-
|
565 |
-
$this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
|
566 |
-
return true;
|
567 |
-
}
|
568 |
-
|
569 |
-
/**
|
570 |
-
* Update WP_HOME in wp-config.php
|
571 |
-
* @return bool
|
572 |
-
*/
|
573 |
-
protected function step10() {
|
574 |
-
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
|
575 |
-
|
576 |
-
$this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
|
577 |
-
|
578 |
-
if( false === ($content = file_get_contents( $path )) ) {
|
579 |
-
$this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
580 |
-
return false;
|
581 |
-
}
|
582 |
-
|
583 |
-
|
584 |
-
// Get WP_HOME from wp-config.php
|
585 |
-
preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
|
586 |
-
|
587 |
-
if( !empty( $matches[1] ) ) {
|
588 |
-
$matches[1];
|
589 |
-
|
590 |
-
$pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
|
591 |
-
|
592 |
-
$replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
593 |
-
$replace.= " // Changed by WP-Staging";
|
594 |
-
|
595 |
-
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
596 |
-
$this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
|
597 |
-
return false;
|
598 |
-
}
|
599 |
-
} else {
|
600 |
-
$this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
|
601 |
-
}
|
602 |
-
|
603 |
-
if( false === @file_put_contents( $path, $content ) ) {
|
604 |
-
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
|
605 |
-
return false;
|
606 |
-
}
|
607 |
-
$this->Log( "Preparing Data: Finished Step 11 successfully" );
|
608 |
-
return true;
|
609 |
-
}
|
610 |
-
|
611 |
-
/**
|
612 |
-
* Update WP_SITEURL in wp-config.php
|
613 |
-
* @return bool
|
614 |
-
*/
|
615 |
-
protected function step11() {
|
616 |
-
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
|
617 |
-
|
618 |
-
$this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
|
619 |
-
|
620 |
-
if( false === ($content = file_get_contents( $path )) ) {
|
621 |
-
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
622 |
-
return false;
|
623 |
-
}
|
624 |
-
|
625 |
-
|
626 |
-
// Get WP_SITEURL from wp-config.php
|
627 |
-
preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
|
628 |
-
|
629 |
-
if( !empty( $matches[1] ) ) {
|
630 |
-
$matches[1];
|
631 |
-
|
632 |
-
$pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
|
633 |
-
|
634 |
-
$replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
635 |
-
$replace.= " // Changed by WP-Staging";
|
636 |
-
|
637 |
-
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
638 |
-
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
|
639 |
-
return false;
|
640 |
-
}
|
641 |
-
} else {
|
642 |
-
$this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
|
643 |
-
}
|
644 |
-
|
645 |
-
|
646 |
-
if( false === @file_put_contents( $path, $content ) ) {
|
647 |
-
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
|
648 |
-
return false;
|
649 |
-
}
|
650 |
-
$this->Log( "Preparing Data: Finished Step 11 successfully" );
|
651 |
-
return true;
|
652 |
-
}
|
653 |
-
|
654 |
-
/**
|
655 |
-
* Update Table Prefix in wp_options
|
656 |
-
* @return bool
|
657 |
-
*/
|
658 |
-
protected function step12() {
|
659 |
-
$this->log( "Preparing Data Step12: Updating db prefix in {$this->prefix}options. Error: {$this->db->last_error}" );
|
660 |
-
|
661 |
-
// Skip - Table does not exist
|
662 |
-
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
663 |
-
return true;
|
664 |
-
}
|
665 |
-
|
666 |
-
// Skip - Table is not selected or updated
|
667 |
-
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
668 |
-
$this->log( "Preparing Data Step12: Skipping" );
|
669 |
-
return true;
|
670 |
-
}
|
671 |
-
|
672 |
-
|
673 |
-
$this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
674 |
-
|
675 |
-
// Filter the rows below. Do not update them!
|
676 |
-
$filters = array(
|
677 |
-
'wp_mail_smtp',
|
678 |
-
'wp_mail_smtp_version',
|
679 |
-
'wp_mail_smtp_debug',
|
680 |
-
);
|
681 |
-
|
682 |
-
$filters = apply_filters( 'wpstg_filter_options_replace', $filters );
|
683 |
-
|
684 |
-
$where = "";
|
685 |
-
foreach ( $filters as $filter ) {
|
686 |
-
$where .= " AND option_name <> '" . $filter . "'";
|
687 |
-
}
|
688 |
-
|
689 |
-
$updateOptions = $this->db->query(
|
690 |
-
$this->db->prepare(
|
691 |
-
"UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
|
692 |
-
)
|
693 |
-
);
|
694 |
-
|
695 |
-
if( !$updateOptions ) {
|
696 |
-
$this->log( "Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
|
697 |
-
$this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
698 |
-
return false;
|
699 |
-
}
|
700 |
-
|
701 |
-
return true;
|
702 |
-
}
|
703 |
-
|
704 |
-
/**
|
705 |
-
* Return URL to staging site
|
706 |
-
* @return string
|
707 |
-
*/
|
708 |
-
protected function getStagingSiteUrl() {
|
709 |
-
if( $this->isSubDir() ) {
|
710 |
-
return rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName;
|
711 |
-
}
|
712 |
-
|
713 |
-
return rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName;
|
714 |
-
}
|
715 |
-
|
716 |
-
/**
|
717 |
-
* Check if WP is installed in subdir
|
718 |
-
* @return boolean
|
719 |
-
*/
|
720 |
-
protected function isSubDir() {
|
721 |
-
if( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
|
722 |
-
return true;
|
723 |
-
}
|
724 |
-
return false;
|
725 |
-
}
|
726 |
-
|
727 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\Utils\Logger;
|
11 |
+
use WPStaging\WPStaging;
|
12 |
+
use WPStaging\Utils\Helper;
|
13 |
+
use WPStaging\Utils\Strings;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Class Data
|
17 |
+
* @package WPStaging\Backend\Modules\Jobs
|
18 |
+
*/
|
19 |
+
class Data extends JobExecutable {
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var \wpdb
|
23 |
+
*/
|
24 |
+
private $db;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $prefix;
|
30 |
+
|
31 |
+
/**
|
32 |
+
*
|
33 |
+
* @var string
|
34 |
+
*/
|
35 |
+
private $homeUrl;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Tables e.g wpstg3_options
|
39 |
+
* @var array
|
40 |
+
*/
|
41 |
+
private $tables;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Initialize
|
45 |
+
*/
|
46 |
+
public function initialize() {
|
47 |
+
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
48 |
+
|
49 |
+
$this->prefix = $this->options->prefix;
|
50 |
+
|
51 |
+
$this->getTables();
|
52 |
+
|
53 |
+
$helper = new Helper();
|
54 |
+
|
55 |
+
$this->homeUrl = $helper->get_home_url();
|
56 |
+
|
57 |
+
|
58 |
+
// Fix current step
|
59 |
+
if( 0 == $this->options->currentStep ) {
|
60 |
+
$this->options->currentStep = 1;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Get a list of tables to copy
|
66 |
+
*/
|
67 |
+
private function getTables() {
|
68 |
+
$strings = new Strings();
|
69 |
+
$this->tables = array();
|
70 |
+
foreach ( $this->options->tables as $table ) {
|
71 |
+
$this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
77 |
+
* @return void
|
78 |
+
*/
|
79 |
+
protected function calculateTotalSteps() {
|
80 |
+
$this->options->totalSteps = 12;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Start Module
|
85 |
+
* @return object
|
86 |
+
*/
|
87 |
+
public function start() {
|
88 |
+
// Execute steps
|
89 |
+
$this->run();
|
90 |
+
|
91 |
+
// Save option, progress
|
92 |
+
$this->saveOptions();
|
93 |
+
|
94 |
+
return ( object ) $this->response;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Execute the Current Step
|
99 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
100 |
+
* @return bool
|
101 |
+
*/
|
102 |
+
protected function execute() {
|
103 |
+
// Fatal error. Let this happen never and break here immediately
|
104 |
+
if( $this->isRoot() ) {
|
105 |
+
return false;
|
106 |
+
}
|
107 |
+
|
108 |
+
// Over limits threshold
|
109 |
+
if( $this->isOverThreshold() ) {
|
110 |
+
// Prepare response and save current progress
|
111 |
+
$this->prepareResponse( false, false );
|
112 |
+
$this->saveOptions();
|
113 |
+
return false;
|
114 |
+
}
|
115 |
+
|
116 |
+
// No more steps, finished
|
117 |
+
if( $this->isFinished() ) {
|
118 |
+
$this->prepareResponse( true, false );
|
119 |
+
return false;
|
120 |
+
}
|
121 |
+
|
122 |
+
// Execute step
|
123 |
+
$stepMethodName = "step" . $this->options->currentStep;
|
124 |
+
if( !$this->{$stepMethodName}() ) {
|
125 |
+
$this->prepareResponse( false, false );
|
126 |
+
return false;
|
127 |
+
}
|
128 |
+
|
129 |
+
// Prepare Response
|
130 |
+
$this->prepareResponse();
|
131 |
+
|
132 |
+
// Not finished
|
133 |
+
return true;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Checks Whether There is Any Job to Execute or Not
|
138 |
+
* @return bool
|
139 |
+
*/
|
140 |
+
protected function isFinished() {
|
141 |
+
return (
|
142 |
+
$this->options->currentStep > $this->options->totalSteps ||
|
143 |
+
!method_exists( $this, "step" . $this->options->currentStep )
|
144 |
+
);
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Check if current operation is done on the root folder or on the live DB
|
149 |
+
* @return boolean
|
150 |
+
*/
|
151 |
+
protected function isRoot() {
|
152 |
+
|
153 |
+
// Prefix is the same as the one of live site
|
154 |
+
$wpdb = WPStaging::getInstance()->get( "wpdb" );
|
155 |
+
if( $wpdb->prefix === $this->prefix ) {
|
156 |
+
return true;
|
157 |
+
}
|
158 |
+
|
159 |
+
// CloneName is empty
|
160 |
+
$name = ( array ) $this->options->cloneDirectoryName;
|
161 |
+
if( empty( $name ) ) {
|
162 |
+
return true;
|
163 |
+
}
|
164 |
+
|
165 |
+
// Live Path === Staging path
|
166 |
+
if( $this->homeUrl . $this->options->cloneDirectoryName === $this->homeUrl ) {
|
167 |
+
return true;
|
168 |
+
}
|
169 |
+
|
170 |
+
return false;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Check if table exists
|
175 |
+
* @param string $table
|
176 |
+
* @return boolean
|
177 |
+
*/
|
178 |
+
protected function isTable( $table ) {
|
179 |
+
if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
|
180 |
+
$this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
|
181 |
+
return false;
|
182 |
+
}
|
183 |
+
return true;
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Get the install sub directory if WP is installed in sub directory
|
188 |
+
* @return string
|
189 |
+
*/
|
190 |
+
protected function getSubDir() {
|
191 |
+
$home = get_option( 'home' );
|
192 |
+
$siteurl = get_option( 'siteurl' );
|
193 |
+
|
194 |
+
if( empty( $home ) || empty( $siteurl ) ) {
|
195 |
+
return '/';
|
196 |
+
}
|
197 |
+
|
198 |
+
$dir = str_replace( $home, '', $siteurl );
|
199 |
+
return '/' . str_replace( '/', '', $dir ) . '/';
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Replace "siteurl" and "home"
|
204 |
+
* @return bool
|
205 |
+
*/
|
206 |
+
protected function step1() {
|
207 |
+
$this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
|
208 |
+
|
209 |
+
// Skip - Table does not exist
|
210 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
211 |
+
return true;
|
212 |
+
}
|
213 |
+
// Skip - Table is not selected or updated
|
214 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
215 |
+
$this->log( "Preparing Data Step1: Skipping" );
|
216 |
+
return true;
|
217 |
+
}
|
218 |
+
|
219 |
+
// Installed in sub-directory
|
220 |
+
if( $this->isSubDir() ) {
|
221 |
+
$this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName );
|
222 |
+
// Replace URLs
|
223 |
+
$result = $this->db->query(
|
224 |
+
$this->db->prepare(
|
225 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName
|
226 |
+
)
|
227 |
+
);
|
228 |
+
} else {
|
229 |
+
$this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName );
|
230 |
+
// Replace URLs
|
231 |
+
$result = $this->db->query(
|
232 |
+
$this->db->prepare(
|
233 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->homeUrl . '/' . $this->options->cloneDirectoryName
|
234 |
+
)
|
235 |
+
);
|
236 |
+
}
|
237 |
+
|
238 |
+
|
239 |
+
// All good
|
240 |
+
if( $result ) {
|
241 |
+
return true;
|
242 |
+
}
|
243 |
+
|
244 |
+
$this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
245 |
+
return false;
|
246 |
+
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* Update "wpstg_is_staging_site"
|
250 |
+
* @return bool
|
251 |
+
*/
|
252 |
+
protected function step2() {
|
253 |
+
|
254 |
+
$this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
|
255 |
+
|
256 |
+
// Skip - Table does not exist
|
257 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
258 |
+
return true;
|
259 |
+
}
|
260 |
+
// Skip - Table is not selected or updated
|
261 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
262 |
+
$this->log( "Preparing Data Step2: Skipping" );
|
263 |
+
return true;
|
264 |
+
}
|
265 |
+
|
266 |
+
$result = $this->db->query(
|
267 |
+
$this->db->prepare(
|
268 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
|
269 |
+
)
|
270 |
+
);
|
271 |
+
|
272 |
+
// No errors but no option name such as wpstg_is_staging_site
|
273 |
+
if( '' === $this->db->last_error && 0 == $result ) {
|
274 |
+
$result = $this->db->query(
|
275 |
+
$this->db->prepare(
|
276 |
+
"INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
|
277 |
+
)
|
278 |
+
);
|
279 |
+
}
|
280 |
+
|
281 |
+
// All good
|
282 |
+
if( $result ) {
|
283 |
+
return true;
|
284 |
+
}
|
285 |
+
|
286 |
+
$this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
287 |
+
return false;
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Update rewrite_rules
|
292 |
+
* @return bool
|
293 |
+
*/
|
294 |
+
protected function step3() {
|
295 |
+
|
296 |
+
$this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
|
297 |
+
|
298 |
+
// Skip - Table does not exist
|
299 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
300 |
+
return true;
|
301 |
+
}
|
302 |
+
|
303 |
+
// Skip - Table is not selected or updated
|
304 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
305 |
+
$this->log( "Preparing Data Step3: Skipping" );
|
306 |
+
return true;
|
307 |
+
}
|
308 |
+
|
309 |
+
$result = $this->db->query(
|
310 |
+
$this->db->prepare(
|
311 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
|
312 |
+
)
|
313 |
+
);
|
314 |
+
|
315 |
+
// All good
|
316 |
+
if( $result ) {
|
317 |
+
return true;
|
318 |
+
}
|
319 |
+
|
320 |
+
$this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
321 |
+
return true;
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Update Table Prefix in wp_usermeta and wp_options
|
326 |
+
* @return bool
|
327 |
+
*/
|
328 |
+
protected function step4() {
|
329 |
+
$this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
|
330 |
+
|
331 |
+
// Skip - Table does not exist
|
332 |
+
if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
|
333 |
+
return true;
|
334 |
+
}
|
335 |
+
|
336 |
+
// Skip - Table is not selected or updated
|
337 |
+
if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
|
338 |
+
$this->log( "Preparing Data Step4: Skipping" );
|
339 |
+
return true;
|
340 |
+
}
|
341 |
+
|
342 |
+
$update = $this->db->query(
|
343 |
+
$this->db->prepare(
|
344 |
+
"UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
|
345 |
+
)
|
346 |
+
);
|
347 |
+
|
348 |
+
if( !$update ) {
|
349 |
+
$this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
|
350 |
+
$this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
|
351 |
+
return false;
|
352 |
+
}
|
353 |
+
|
354 |
+
// if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
355 |
+
// return true;
|
356 |
+
// }
|
357 |
+
// $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
358 |
+
//
|
359 |
+
// // Filter the rows below. Do not update them!
|
360 |
+
// $filters = array(
|
361 |
+
// 'wp_mail_smtp',
|
362 |
+
// 'wp_mail_smtp_version',
|
363 |
+
// 'wp_mail_smtp_debug',
|
364 |
+
// );
|
365 |
+
//
|
366 |
+
// $filters = apply_filters('wpstg_filter_options_replace', $filters);
|
367 |
+
//
|
368 |
+
// $where = "";
|
369 |
+
// foreach($filters as $filter){
|
370 |
+
// $where .= " AND option_name <> '" . $filter . "'";
|
371 |
+
// }
|
372 |
+
//
|
373 |
+
// $updateOptions = $this->db->query(
|
374 |
+
// $this->db->prepare(
|
375 |
+
// "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
|
376 |
+
// )
|
377 |
+
// );
|
378 |
+
//
|
379 |
+
// if( !$updateOptions ) {
|
380 |
+
// $this->log( "Preparing Data Step4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
|
381 |
+
// $this->returnException( "Data Crunching Step 4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
382 |
+
// return false;
|
383 |
+
// }
|
384 |
+
|
385 |
+
return true;
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Update $table_prefix in wp-config.php
|
390 |
+
* @return bool
|
391 |
+
*/
|
392 |
+
protected function step5() {
|
393 |
+
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
|
394 |
+
|
395 |
+
$this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
|
396 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
397 |
+
$this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
|
398 |
+
return false;
|
399 |
+
}
|
400 |
+
|
401 |
+
// Replace table prefix
|
402 |
+
$content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
|
403 |
+
|
404 |
+
// Replace URLs
|
405 |
+
$content = str_replace( $this->homeUrl, $this->homeUrl . '/' . $this->options->cloneDirectoryName, $content );
|
406 |
+
|
407 |
+
if( false === @file_put_contents( $path, $content ) ) {
|
408 |
+
$this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
|
409 |
+
return false;
|
410 |
+
}
|
411 |
+
|
412 |
+
return true;
|
413 |
+
}
|
414 |
+
|
415 |
+
/**
|
416 |
+
* Reset index.php to original file
|
417 |
+
* This is needed if live site is located in subfolder
|
418 |
+
* Check first if main wordpress is used in subfolder and index.php in parent directory
|
419 |
+
* @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
|
420 |
+
* @return bool
|
421 |
+
*/
|
422 |
+
protected function step6() {
|
423 |
+
|
424 |
+
if( !$this->isSubDir() ) {
|
425 |
+
$this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
|
426 |
+
return true;
|
427 |
+
}
|
428 |
+
|
429 |
+
$path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
|
430 |
+
|
431 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
432 |
+
$this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
|
433 |
+
return false;
|
434 |
+
}
|
435 |
+
|
436 |
+
|
437 |
+
if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
|
438 |
+
$this->log(
|
439 |
+
"Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
|
440 |
+
);
|
441 |
+
return false;
|
442 |
+
}
|
443 |
+
$this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
|
444 |
+
|
445 |
+
$pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
|
446 |
+
|
447 |
+
$replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
|
448 |
+
$replace.= " // Changed by WP-Staging";
|
449 |
+
|
450 |
+
|
451 |
+
|
452 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
453 |
+
$this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
|
454 |
+
return false;
|
455 |
+
}
|
456 |
+
|
457 |
+
if( false === @file_put_contents( $path, $content ) ) {
|
458 |
+
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
459 |
+
return false;
|
460 |
+
}
|
461 |
+
$this->Log( "Preparing Data: Finished Step 6 successfully" );
|
462 |
+
return true;
|
463 |
+
}
|
464 |
+
|
465 |
+
/**
|
466 |
+
* Update wpstg_rmpermalinks_executed
|
467 |
+
* @return bool
|
468 |
+
*/
|
469 |
+
protected function step7() {
|
470 |
+
|
471 |
+
$this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
|
472 |
+
|
473 |
+
// Skip - Table does not exist
|
474 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
475 |
+
return true;
|
476 |
+
}
|
477 |
+
|
478 |
+
// Skip - Table is not selected or updated
|
479 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
480 |
+
$this->log( "Preparing Data Step7: Skipping" );
|
481 |
+
return true;
|
482 |
+
}
|
483 |
+
|
484 |
+
$result = $this->db->query(
|
485 |
+
$this->db->prepare(
|
486 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
|
487 |
+
)
|
488 |
+
);
|
489 |
+
|
490 |
+
// All good
|
491 |
+
if( $result ) {
|
492 |
+
$this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
|
493 |
+
return true;
|
494 |
+
}
|
495 |
+
|
496 |
+
$this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
|
497 |
+
return true;
|
498 |
+
}
|
499 |
+
|
500 |
+
/**
|
501 |
+
* Update permalink_structure
|
502 |
+
* @return bool
|
503 |
+
*/
|
504 |
+
protected function step8() {
|
505 |
+
|
506 |
+
$this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
|
507 |
+
|
508 |
+
// Skip - Table does not exist
|
509 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
510 |
+
return true;
|
511 |
+
}
|
512 |
+
|
513 |
+
// Skip - Table is not selected or updated
|
514 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
515 |
+
$this->log( "Preparing Data Step8: Skipping" );
|
516 |
+
return true;
|
517 |
+
}
|
518 |
+
|
519 |
+
$result = $this->db->query(
|
520 |
+
$this->db->prepare(
|
521 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
|
522 |
+
)
|
523 |
+
);
|
524 |
+
|
525 |
+
// All good
|
526 |
+
if( $result ) {
|
527 |
+
$this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
|
528 |
+
return true;
|
529 |
+
}
|
530 |
+
|
531 |
+
$this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
532 |
+
return true;
|
533 |
+
}
|
534 |
+
|
535 |
+
/**
|
536 |
+
* Update blog_public option to not allow staging site to be indexed by search engines
|
537 |
+
* @return bool
|
538 |
+
*/
|
539 |
+
protected function step9() {
|
540 |
+
|
541 |
+
$this->log( "Preparing Data Step9: Set staging site to noindex" );
|
542 |
+
|
543 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
544 |
+
return true;
|
545 |
+
}
|
546 |
+
|
547 |
+
// Skip - Table is not selected or updated
|
548 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
549 |
+
$this->log( "Preparing Data Step9: Skipping" );
|
550 |
+
return true;
|
551 |
+
}
|
552 |
+
|
553 |
+
$result = $this->db->query(
|
554 |
+
$this->db->prepare(
|
555 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
|
556 |
+
)
|
557 |
+
);
|
558 |
+
|
559 |
+
// All good
|
560 |
+
if( $result ) {
|
561 |
+
$this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
|
562 |
+
return true;
|
563 |
+
}
|
564 |
+
|
565 |
+
$this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
|
566 |
+
return true;
|
567 |
+
}
|
568 |
+
|
569 |
+
/**
|
570 |
+
* Update WP_HOME in wp-config.php
|
571 |
+
* @return bool
|
572 |
+
*/
|
573 |
+
protected function step10() {
|
574 |
+
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
|
575 |
+
|
576 |
+
$this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
|
577 |
+
|
578 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
579 |
+
$this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
580 |
+
return false;
|
581 |
+
}
|
582 |
+
|
583 |
+
|
584 |
+
// Get WP_HOME from wp-config.php
|
585 |
+
preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
|
586 |
+
|
587 |
+
if( !empty( $matches[1] ) ) {
|
588 |
+
$matches[1];
|
589 |
+
|
590 |
+
$pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
|
591 |
+
|
592 |
+
$replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
593 |
+
$replace.= " // Changed by WP-Staging";
|
594 |
+
|
595 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
596 |
+
$this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
|
597 |
+
return false;
|
598 |
+
}
|
599 |
+
} else {
|
600 |
+
$this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
|
601 |
+
}
|
602 |
+
|
603 |
+
if( false === @file_put_contents( $path, $content ) ) {
|
604 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
|
605 |
+
return false;
|
606 |
+
}
|
607 |
+
$this->Log( "Preparing Data: Finished Step 11 successfully" );
|
608 |
+
return true;
|
609 |
+
}
|
610 |
+
|
611 |
+
/**
|
612 |
+
* Update WP_SITEURL in wp-config.php
|
613 |
+
* @return bool
|
614 |
+
*/
|
615 |
+
protected function step11() {
|
616 |
+
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
|
617 |
+
|
618 |
+
$this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
|
619 |
+
|
620 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
621 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
622 |
+
return false;
|
623 |
+
}
|
624 |
+
|
625 |
+
|
626 |
+
// Get WP_SITEURL from wp-config.php
|
627 |
+
preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
|
628 |
+
|
629 |
+
if( !empty( $matches[1] ) ) {
|
630 |
+
$matches[1];
|
631 |
+
|
632 |
+
$pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
|
633 |
+
|
634 |
+
$replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
635 |
+
$replace.= " // Changed by WP-Staging";
|
636 |
+
|
637 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
638 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
|
639 |
+
return false;
|
640 |
+
}
|
641 |
+
} else {
|
642 |
+
$this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
|
643 |
+
}
|
644 |
+
|
645 |
+
|
646 |
+
if( false === @file_put_contents( $path, $content ) ) {
|
647 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
|
648 |
+
return false;
|
649 |
+
}
|
650 |
+
$this->Log( "Preparing Data: Finished Step 11 successfully" );
|
651 |
+
return true;
|
652 |
+
}
|
653 |
+
|
654 |
+
/**
|
655 |
+
* Update Table Prefix in wp_options
|
656 |
+
* @return bool
|
657 |
+
*/
|
658 |
+
protected function step12() {
|
659 |
+
$this->log( "Preparing Data Step12: Updating db prefix in {$this->prefix}options. Error: {$this->db->last_error}" );
|
660 |
+
|
661 |
+
// Skip - Table does not exist
|
662 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
663 |
+
return true;
|
664 |
+
}
|
665 |
+
|
666 |
+
// Skip - Table is not selected or updated
|
667 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
668 |
+
$this->log( "Preparing Data Step12: Skipping" );
|
669 |
+
return true;
|
670 |
+
}
|
671 |
+
|
672 |
+
|
673 |
+
$this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
674 |
+
|
675 |
+
// Filter the rows below. Do not update them!
|
676 |
+
$filters = array(
|
677 |
+
'wp_mail_smtp',
|
678 |
+
'wp_mail_smtp_version',
|
679 |
+
'wp_mail_smtp_debug',
|
680 |
+
);
|
681 |
+
|
682 |
+
$filters = apply_filters( 'wpstg_filter_options_replace', $filters );
|
683 |
+
|
684 |
+
$where = "";
|
685 |
+
foreach ( $filters as $filter ) {
|
686 |
+
$where .= " AND option_name <> '" . $filter . "'";
|
687 |
+
}
|
688 |
+
|
689 |
+
$updateOptions = $this->db->query(
|
690 |
+
$this->db->prepare(
|
691 |
+
"UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
|
692 |
+
)
|
693 |
+
);
|
694 |
+
|
695 |
+
if( !$updateOptions ) {
|
696 |
+
$this->log( "Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
|
697 |
+
$this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
698 |
+
return false;
|
699 |
+
}
|
700 |
+
|
701 |
+
return true;
|
702 |
+
}
|
703 |
+
|
704 |
+
/**
|
705 |
+
* Return URL to staging site
|
706 |
+
* @return string
|
707 |
+
*/
|
708 |
+
protected function getStagingSiteUrl() {
|
709 |
+
if( $this->isSubDir() ) {
|
710 |
+
return rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName;
|
711 |
+
}
|
712 |
+
|
713 |
+
return rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName;
|
714 |
+
}
|
715 |
+
|
716 |
+
/**
|
717 |
+
* Check if WP is installed in subdir
|
718 |
+
* @return boolean
|
719 |
+
*/
|
720 |
+
protected function isSubDir() {
|
721 |
+
if( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
|
722 |
+
return true;
|
723 |
+
}
|
724 |
+
return false;
|
725 |
+
}
|
726 |
+
|
727 |
+
}
|
apps/Backend/Modules/Jobs/Database.php
CHANGED
@@ -1,282 +1,282 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
3 |
-
|
4 |
-
// No Direct Access
|
5 |
-
if (!defined("WPINC"))
|
6 |
-
{
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\WPStaging;
|
11 |
-
use WPStaging\Utils\Strings;
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Class Database
|
15 |
-
* @package WPStaging\Backend\Modules\Jobs
|
16 |
-
*/
|
17 |
-
class Database extends JobExecutable
|
18 |
-
{
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @var int
|
22 |
-
*/
|
23 |
-
private $total = 0;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* @var \WPDB
|
27 |
-
*/
|
28 |
-
private $db;
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Initialize
|
32 |
-
*/
|
33 |
-
public function initialize()
|
34 |
-
{
|
35 |
-
// Variables
|
36 |
-
$this->total = count($this->options->tables);
|
37 |
-
$this->db = WPStaging::getInstance()->get("wpdb");
|
38 |
-
$this->isFatalError();
|
39 |
-
|
40 |
-
}
|
41 |
-
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Return fatal error and stops here if subfolder already exists
|
45 |
-
* and mainJob is not updating the clone
|
46 |
-
* @return boolean
|
47 |
-
*/
|
48 |
-
private function isFatalError(){
|
49 |
-
$path = trailingslashit(get_home_path()) . $this->options->cloneDirectoryName;
|
50 |
-
if (isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && is_dir($path)){
|
51 |
-
$this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
|
52 |
-
}
|
53 |
-
return false;
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
58 |
-
* @return void
|
59 |
-
*/
|
60 |
-
protected function calculateTotalSteps()
|
61 |
-
{
|
62 |
-
$this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
|
63 |
-
}
|
64 |
-
|
65 |
-
/**
|
66 |
-
* Execute the Current Step
|
67 |
-
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
68 |
-
* @return bool
|
69 |
-
*/
|
70 |
-
protected function execute()
|
71 |
-
{
|
72 |
-
// Over limits threshold
|
73 |
-
if ($this->isOverThreshold())
|
74 |
-
{
|
75 |
-
// Prepare response and save current progress
|
76 |
-
$this->prepareResponse(false, false);
|
77 |
-
$this->saveOptions();
|
78 |
-
return false;
|
79 |
-
}
|
80 |
-
|
81 |
-
// No more steps, finished
|
82 |
-
if ($this->options->currentStep > $this->total || !isset($this->options->tables[$this->options->currentStep]))
|
83 |
-
{
|
84 |
-
$this->prepareResponse(true, false);
|
85 |
-
return false;
|
86 |
-
}
|
87 |
-
|
88 |
-
// Table is excluded
|
89 |
-
// if (in_array($this->options->tables[$this->options->currentStep]->name, $this->options->excludedTables))
|
90 |
-
// {
|
91 |
-
// $this->prepareResponse();
|
92 |
-
// return true;
|
93 |
-
// }
|
94 |
-
|
95 |
-
// Copy table
|
96 |
-
//if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
|
97 |
-
if (!$this->copyTable($this->options->tables[$this->options->currentStep]))
|
98 |
-
{
|
99 |
-
// Prepare Response
|
100 |
-
$this->prepareResponse(false, false);
|
101 |
-
|
102 |
-
// Not finished
|
103 |
-
return true;
|
104 |
-
}
|
105 |
-
|
106 |
-
// Prepare Response
|
107 |
-
$this->prepareResponse();
|
108 |
-
|
109 |
-
// Not finished
|
110 |
-
return true;
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Get new prefix for the staging site
|
115 |
-
* @return string
|
116 |
-
*/
|
117 |
-
private function getStagingPrefix(){
|
118 |
-
$stagingPrefix = $this->options->prefix;
|
119 |
-
// Make sure prefix of staging site is NEVER identical to prefix of live site!
|
120 |
-
if ( $stagingPrefix == $this->db->prefix ){
|
121 |
-
wp_die('Fatal error 7: The new database table prefix '. $stagingPrefix .' would be identical to the table prefix of the live site. Please open a support ticket to support@wp-staging.com');
|
122 |
-
}
|
123 |
-
return $stagingPrefix;
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* No worries, SQL queries don't eat from PHP execution time!
|
128 |
-
* @param string $tableName
|
129 |
-
* @return bool
|
130 |
-
*/
|
131 |
-
private function copyTable($tableName)
|
132 |
-
{
|
133 |
-
|
134 |
-
$strings = new Strings();
|
135 |
-
$tableName = is_object($tableName) ? $tableName->name : $tableName;
|
136 |
-
$newTableName = $this->getStagingPrefix() . $strings->str_replace_first($this->db->prefix, null, $tableName);
|
137 |
-
|
138 |
-
// Drop table if necessary
|
139 |
-
$this->dropTable($newTableName);
|
140 |
-
|
141 |
-
// Save current job
|
142 |
-
$this->setJob($newTableName);
|
143 |
-
|
144 |
-
// Beginning of the job
|
145 |
-
if (!$this->startJob($newTableName, $tableName))
|
146 |
-
{
|
147 |
-
return true;
|
148 |
-
}
|
149 |
-
|
150 |
-
// Copy data
|
151 |
-
$this->copyData($newTableName, $tableName);
|
152 |
-
|
153 |
-
// Finis the step
|
154 |
-
return $this->finishStep();
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Copy data from old table to new table
|
159 |
-
* @param string $new
|
160 |
-
* @param string $old
|
161 |
-
*/
|
162 |
-
private function copyData($new, $old)
|
163 |
-
{
|
164 |
-
$rows = $this->options->job->start+$this->settings->queryLimit;
|
165 |
-
$this->log(
|
166 |
-
"DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
|
167 |
-
);
|
168 |
-
|
169 |
-
$limitation = '';
|
170 |
-
|
171 |
-
if (0 < (int) $this->settings->queryLimit)
|
172 |
-
{
|
173 |
-
$limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
|
174 |
-
}
|
175 |
-
|
176 |
-
$this->db->query(
|
177 |
-
"INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
|
178 |
-
);
|
179 |
-
|
180 |
-
// Set new offset
|
181 |
-
$this->options->job->start += $this->settings->queryLimit;
|
182 |
-
}
|
183 |
-
|
184 |
-
/**
|
185 |
-
* Set the job
|
186 |
-
* @param string $table
|
187 |
-
*/
|
188 |
-
private function setJob($table)
|
189 |
-
{
|
190 |
-
if (isset($this->options->job->current))
|
191 |
-
{
|
192 |
-
return;
|
193 |
-
}
|
194 |
-
|
195 |
-
$this->options->job->current = $table;
|
196 |
-
$this->options->job->start = 0;
|
197 |
-
}
|
198 |
-
|
199 |
-
/**
|
200 |
-
* Start Job
|
201 |
-
* @param string $new
|
202 |
-
* @param string $old
|
203 |
-
* @return bool
|
204 |
-
*/
|
205 |
-
private function startJob($new, $old)
|
206 |
-
{
|
207 |
-
if (0 != $this->options->job->start)
|
208 |
-
{
|
209 |
-
return true;
|
210 |
-
}
|
211 |
-
|
212 |
-
$this->log("DB Copy: Creating table {$new}");
|
213 |
-
|
214 |
-
$this->db->query("CREATE TABLE {$new} LIKE {$old}");
|
215 |
-
|
216 |
-
$this->options->job->total = (int) $this->db->get_var("SELECT COUNT(1) FROM {$old}");
|
217 |
-
|
218 |
-
if (0 == $this->options->job->total)
|
219 |
-
{
|
220 |
-
$this->finishStep();
|
221 |
-
return false;
|
222 |
-
}
|
223 |
-
|
224 |
-
return true;
|
225 |
-
}
|
226 |
-
|
227 |
-
/**
|
228 |
-
* Finish the step
|
229 |
-
*/
|
230 |
-
private function finishStep()
|
231 |
-
{
|
232 |
-
// This job is not finished yet
|
233 |
-
if ($this->options->job->total > $this->options->job->start)
|
234 |
-
{
|
235 |
-
return false;
|
236 |
-
}
|
237 |
-
|
238 |
-
// Add it to cloned tables listing
|
239 |
-
$this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
|
240 |
-
|
241 |
-
// Reset job
|
242 |
-
$this->options->job = new \stdClass();
|
243 |
-
|
244 |
-
return true;
|
245 |
-
}
|
246 |
-
|
247 |
-
/**
|
248 |
-
* Drop table if necessary
|
249 |
-
* @param string $new
|
250 |
-
*/
|
251 |
-
private function dropTable($new)
|
252 |
-
{
|
253 |
-
$old = $this->db->get_var($this->db->prepare("SHOW TABLES LIKE %s", $new));
|
254 |
-
|
255 |
-
if (!$this->shouldDropTable($new, $old))
|
256 |
-
{
|
257 |
-
return;
|
258 |
-
}
|
259 |
-
|
260 |
-
$this->log("DB Copy: {$new} already exists, dropping it first");
|
261 |
-
$this->db->query("DROP TABLE {$new}");
|
262 |
-
}
|
263 |
-
|
264 |
-
/**
|
265 |
-
* Check if table needs to be dropped
|
266 |
-
* @param string $new
|
267 |
-
* @param string $old
|
268 |
-
* @return bool
|
269 |
-
*/
|
270 |
-
private function shouldDropTable($new, $old)
|
271 |
-
{
|
272 |
-
return (
|
273 |
-
$old === $new &&
|
274 |
-
(
|
275 |
-
!isset($this->options->job->current) ||
|
276 |
-
!isset($this->options->job->start) ||
|
277 |
-
0 == $this->options->job->start
|
278 |
-
)
|
279 |
-
);
|
280 |
-
}
|
281 |
-
|
282 |
}
|
1 |
+
<?php
|
2 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
3 |
+
|
4 |
+
// No Direct Access
|
5 |
+
if (!defined("WPINC"))
|
6 |
+
{
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\WPStaging;
|
11 |
+
use WPStaging\Utils\Strings;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class Database
|
15 |
+
* @package WPStaging\Backend\Modules\Jobs
|
16 |
+
*/
|
17 |
+
class Database extends JobExecutable
|
18 |
+
{
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var int
|
22 |
+
*/
|
23 |
+
private $total = 0;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var \WPDB
|
27 |
+
*/
|
28 |
+
private $db;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Initialize
|
32 |
+
*/
|
33 |
+
public function initialize()
|
34 |
+
{
|
35 |
+
// Variables
|
36 |
+
$this->total = count($this->options->tables);
|
37 |
+
$this->db = WPStaging::getInstance()->get("wpdb");
|
38 |
+
$this->isFatalError();
|
39 |
+
|
40 |
+
}
|
41 |
+
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Return fatal error and stops here if subfolder already exists
|
45 |
+
* and mainJob is not updating the clone
|
46 |
+
* @return boolean
|
47 |
+
*/
|
48 |
+
private function isFatalError(){
|
49 |
+
$path = trailingslashit(get_home_path()) . $this->options->cloneDirectoryName;
|
50 |
+
if (isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && is_dir($path)){
|
51 |
+
$this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
|
52 |
+
}
|
53 |
+
return false;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
58 |
+
* @return void
|
59 |
+
*/
|
60 |
+
protected function calculateTotalSteps()
|
61 |
+
{
|
62 |
+
$this->options->totalSteps = $this->total === 0 ? 1 : $this->total;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Execute the Current Step
|
67 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
68 |
+
* @return bool
|
69 |
+
*/
|
70 |
+
protected function execute()
|
71 |
+
{
|
72 |
+
// Over limits threshold
|
73 |
+
if ($this->isOverThreshold())
|
74 |
+
{
|
75 |
+
// Prepare response and save current progress
|
76 |
+
$this->prepareResponse(false, false);
|
77 |
+
$this->saveOptions();
|
78 |
+
return false;
|
79 |
+
}
|
80 |
+
|
81 |
+
// No more steps, finished
|
82 |
+
if ($this->options->currentStep > $this->total || !isset($this->options->tables[$this->options->currentStep]))
|
83 |
+
{
|
84 |
+
$this->prepareResponse(true, false);
|
85 |
+
return false;
|
86 |
+
}
|
87 |
+
|
88 |
+
// Table is excluded
|
89 |
+
// if (in_array($this->options->tables[$this->options->currentStep]->name, $this->options->excludedTables))
|
90 |
+
// {
|
91 |
+
// $this->prepareResponse();
|
92 |
+
// return true;
|
93 |
+
// }
|
94 |
+
|
95 |
+
// Copy table
|
96 |
+
//if (!$this->copyTable($this->options->tables[$this->options->currentStep]->name))
|
97 |
+
if (!$this->copyTable($this->options->tables[$this->options->currentStep]))
|
98 |
+
{
|
99 |
+
// Prepare Response
|
100 |
+
$this->prepareResponse(false, false);
|
101 |
+
|
102 |
+
// Not finished
|
103 |
+
return true;
|
104 |
+
}
|
105 |
+
|
106 |
+
// Prepare Response
|
107 |
+
$this->prepareResponse();
|
108 |
+
|
109 |
+
// Not finished
|
110 |
+
return true;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Get new prefix for the staging site
|
115 |
+
* @return string
|
116 |
+
*/
|
117 |
+
private function getStagingPrefix(){
|
118 |
+
$stagingPrefix = $this->options->prefix;
|
119 |
+
// Make sure prefix of staging site is NEVER identical to prefix of live site!
|
120 |
+
if ( $stagingPrefix == $this->db->prefix ){
|
121 |
+
wp_die('Fatal error 7: The new database table prefix '. $stagingPrefix .' would be identical to the table prefix of the live site. Please open a support ticket to support@wp-staging.com');
|
122 |
+
}
|
123 |
+
return $stagingPrefix;
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* No worries, SQL queries don't eat from PHP execution time!
|
128 |
+
* @param string $tableName
|
129 |
+
* @return bool
|
130 |
+
*/
|
131 |
+
private function copyTable($tableName)
|
132 |
+
{
|
133 |
+
|
134 |
+
$strings = new Strings();
|
135 |
+
$tableName = is_object($tableName) ? $tableName->name : $tableName;
|
136 |
+
$newTableName = $this->getStagingPrefix() . $strings->str_replace_first($this->db->prefix, null, $tableName);
|
137 |
+
|
138 |
+
// Drop table if necessary
|
139 |
+
$this->dropTable($newTableName);
|
140 |
+
|
141 |
+
// Save current job
|
142 |
+
$this->setJob($newTableName);
|
143 |
+
|
144 |
+
// Beginning of the job
|
145 |
+
if (!$this->startJob($newTableName, $tableName))
|
146 |
+
{
|
147 |
+
return true;
|
148 |
+
}
|
149 |
+
|
150 |
+
// Copy data
|
151 |
+
$this->copyData($newTableName, $tableName);
|
152 |
+
|
153 |
+
// Finis the step
|
154 |
+
return $this->finishStep();
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Copy data from old table to new table
|
159 |
+
* @param string $new
|
160 |
+
* @param string $old
|
161 |
+
*/
|
162 |
+
private function copyData($new, $old)
|
163 |
+
{
|
164 |
+
$rows = $this->options->job->start+$this->settings->queryLimit;
|
165 |
+
$this->log(
|
166 |
+
"DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
|
167 |
+
);
|
168 |
+
|
169 |
+
$limitation = '';
|
170 |
+
|
171 |
+
if (0 < (int) $this->settings->queryLimit)
|
172 |
+
{
|
173 |
+
$limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
|
174 |
+
}
|
175 |
+
|
176 |
+
$this->db->query(
|
177 |
+
"INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
|
178 |
+
);
|
179 |
+
|
180 |
+
// Set new offset
|
181 |
+
$this->options->job->start += $this->settings->queryLimit;
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Set the job
|
186 |
+
* @param string $table
|
187 |
+
*/
|
188 |
+
private function setJob($table)
|
189 |
+
{
|
190 |
+
if (isset($this->options->job->current))
|
191 |
+
{
|
192 |
+
return;
|
193 |
+
}
|
194 |
+
|
195 |
+
$this->options->job->current = $table;
|
196 |
+
$this->options->job->start = 0;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Start Job
|
201 |
+
* @param string $new
|
202 |
+
* @param string $old
|
203 |
+
* @return bool
|
204 |
+
*/
|
205 |
+
private function startJob($new, $old)
|
206 |
+
{
|
207 |
+
if (0 != $this->options->job->start)
|
208 |
+
{
|
209 |
+
return true;
|
210 |
+
}
|
211 |
+
|
212 |
+
$this->log("DB Copy: Creating table {$new}");
|
213 |
+
|
214 |
+
$this->db->query("CREATE TABLE {$new} LIKE {$old}");
|
215 |
+
|
216 |
+
$this->options->job->total = (int) $this->db->get_var("SELECT COUNT(1) FROM {$old}");
|
217 |
+
|
218 |
+
if (0 == $this->options->job->total)
|
219 |
+
{
|
220 |
+
$this->finishStep();
|
221 |
+
return false;
|
222 |
+
}
|
223 |
+
|
224 |
+
return true;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Finish the step
|
229 |
+
*/
|
230 |
+
private function finishStep()
|
231 |
+
{
|
232 |
+
// This job is not finished yet
|
233 |
+
if ($this->options->job->total > $this->options->job->start)
|
234 |
+
{
|
235 |
+
return false;
|
236 |
+
}
|
237 |
+
|
238 |
+
// Add it to cloned tables listing
|
239 |
+
$this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
|
240 |
+
|
241 |
+
// Reset job
|
242 |
+
$this->options->job = new \stdClass();
|
243 |
+
|
244 |
+
return true;
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Drop table if necessary
|
249 |
+
* @param string $new
|
250 |
+
*/
|
251 |
+
private function dropTable($new)
|
252 |
+
{
|
253 |
+
$old = $this->db->get_var($this->db->prepare("SHOW TABLES LIKE %s", $new));
|
254 |
+
|
255 |
+
if (!$this->shouldDropTable($new, $old))
|
256 |
+
{
|
257 |
+
return;
|
258 |
+
}
|
259 |
+
|
260 |
+
$this->log("DB Copy: {$new} already exists, dropping it first");
|
261 |
+
$this->db->query("DROP TABLE {$new}");
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Check if table needs to be dropped
|
266 |
+
* @param string $new
|
267 |
+
* @param string $old
|
268 |
+
* @return bool
|
269 |
+
*/
|
270 |
+
private function shouldDropTable($new, $old)
|
271 |
+
{
|
272 |
+
return (
|
273 |
+
$old === $new &&
|
274 |
+
(
|
275 |
+
!isset($this->options->job->current) ||
|
276 |
+
!isset($this->options->job->start) ||
|
277 |
+
0 == $this->options->job->start
|
278 |
+
)
|
279 |
+
);
|
280 |
+
}
|
281 |
+
|
282 |
}
|
apps/Backend/Modules/Jobs/Delete.php
CHANGED
@@ -1,429 +1,429 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
-
|
5 |
-
use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
|
6 |
-
use WPStaging\Utils\Directories;
|
7 |
-
use WPStaging\Utils\Logger;
|
8 |
-
use WPStaging\WPStaging;
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Class Delete
|
12 |
-
* @package WPStaging\Backend\Modules\Jobs
|
13 |
-
*/
|
14 |
-
class Delete extends Job {
|
15 |
-
|
16 |
-
/**
|
17 |
-
* @var false
|
18 |
-
*/
|
19 |
-
private $clone = false;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @var null|object
|
23 |
-
*/
|
24 |
-
private $tables = null;
|
25 |
-
|
26 |
-
/**
|
27 |
-
* @var object|null
|
28 |
-
*/
|
29 |
-
private $job = null;
|
30 |
-
|
31 |
-
/**
|
32 |
-
* @var bool
|
33 |
-
*/
|
34 |
-
private $forceDeleteDirectories = false;
|
35 |
-
|
36 |
-
/**
|
37 |
-
*
|
38 |
-
* @var object
|
39 |
-
*/
|
40 |
-
public $wpdb;
|
41 |
-
|
42 |
-
public function __construct() {
|
43 |
-
parent::__construct();
|
44 |
-
$this->wpdb = WPStaging::getInstance()->get("wpdb");
|
45 |
-
}
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Sets Clone and Table Records
|
49 |
-
* @param null|array $clone
|
50 |
-
*/
|
51 |
-
public function setData($clone = null) {
|
52 |
-
if (!is_array($clone)) {
|
53 |
-
$this->getCloneRecords();
|
54 |
-
} else {
|
55 |
-
$this->clone = (object) $clone;
|
56 |
-
$this->forceDeleteDirectories = true;
|
57 |
-
}
|
58 |
-
|
59 |
-
$this->getTableRecords();
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Get clone
|
64 |
-
* @param null|string $name
|
65 |
-
* @throws CloneNotFoundException
|
66 |
-
*/
|
67 |
-
private function getCloneRecords($name = null) {
|
68 |
-
if (null === $name && !isset($_POST["clone"])) {
|
69 |
-
$this->log("Clone name is not set", Logger::TYPE_FATAL);
|
70 |
-
throw new CloneNotFoundException();
|
71 |
-
}
|
72 |
-
|
73 |
-
if (null === $name) {
|
74 |
-
$name = $_POST["clone"];
|
75 |
-
}
|
76 |
-
|
77 |
-
$clones = get_option("wpstg_existing_clones_beta", array());
|
78 |
-
|
79 |
-
if (empty($clones) || !isset($clones[$name])) {
|
80 |
-
$this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
|
81 |
-
throw new CloneNotFoundException();
|
82 |
-
}
|
83 |
-
|
84 |
-
$this->clone = $clones[$name];
|
85 |
-
$this->clone["name"] = $name;
|
86 |
-
|
87 |
-
$this->clone = (object) $this->clone;
|
88 |
-
|
89 |
-
unset($clones);
|
90 |
-
}
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Get Tables
|
94 |
-
*/
|
95 |
-
private function getTableRecords() {
|
96 |
-
//$wpdb = WPStaging::getInstance()->get("wpdb");
|
97 |
-
$this->wpdb = WPStaging::getInstance()->get("wpdb");
|
98 |
-
|
99 |
-
$stagingPrefix = $this->getStagingPrefix();
|
100 |
-
|
101 |
-
$tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$stagingPrefix}%'");
|
102 |
-
|
103 |
-
$this->tables = array();
|
104 |
-
|
105 |
-
foreach ($tables as $table) {
|
106 |
-
$this->tables[] = array(
|
107 |
-
"name" => $table->Name,
|
108 |
-
"size" => $this->formatSize(($table->Data_length + $table->Index_length))
|
109 |
-
);
|
110 |
-
}
|
111 |
-
|
112 |
-
$this->tables = json_decode(json_encode($this->tables));
|
113 |
-
}
|
114 |
-
|
115 |
-
/**
|
116 |
-
* Check and return prefix of the staging site
|
117 |
-
*/
|
118 |
-
public function getStagingPrefix() {
|
119 |
-
// Prefix not defined! Happens if staging site has ben generated with older version of wpstg
|
120 |
-
// Try to get staging prefix from wp-config.php of staging site
|
121 |
-
if (empty($this->clone->prefix)) {
|
122 |
-
// Throw error
|
123 |
-
$path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
|
124 |
-
if (false === ($content = @file_get_contents($path))) {
|
125 |
-
$this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
|
126 |
-
// Create a random prefix which hopefully never exists.
|
127 |
-
$this->clone->prefix = rand(7, 15) . '_';
|
128 |
-
} else {
|
129 |
-
|
130 |
-
// Get prefix from wp-config.php
|
131 |
-
//preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
|
132 |
-
preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
|
133 |
-
//wp_die(var_dump($matches));
|
134 |
-
|
135 |
-
if (!empty($matches[1])) {
|
136 |
-
$this->clone->prefix = $matches[1];
|
137 |
-
} else {
|
138 |
-
$this->log("Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
|
139 |
-
// Create a random prefix which hopefully never exists.
|
140 |
-
return $this->clone->prefix = rand(7, 15) . '_';
|
141 |
-
}
|
142 |
-
}
|
143 |
-
}
|
144 |
-
|
145 |
-
// Check if staging prefix is the same as the live prefix
|
146 |
-
if ($this->wpdb->prefix == $this->clone->prefix) {
|
147 |
-
$this->log("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
|
148 |
-
wp_die("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
|
149 |
-
}
|
150 |
-
|
151 |
-
// Else
|
152 |
-
return $this->clone->prefix;
|
153 |
-
}
|
154 |
-
|
155 |
-
/**
|
156 |
-
* Format bytes into human readable form
|
157 |
-
* @param int $bytes
|
158 |
-
* @param int $precision
|
159 |
-
* @return string
|
160 |
-
*/
|
161 |
-
public function formatSize($bytes, $precision = 2) {
|
162 |
-
if ((int) $bytes < 1) {
|
163 |
-
return '';
|
164 |
-
}
|
165 |
-
|
166 |
-
$units = array('B', "KB", "MB", "GB", "TB");
|
167 |
-
|
168 |
-
$bytes = (int) $bytes;
|
169 |
-
$base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
|
170 |
-
$pow = pow(1000, $base - floor($base)); // Same rule for 1000
|
171 |
-
|
172 |
-
return round($pow, $precision) . ' ' . $units[(int) floor($base)];
|
173 |
-
}
|
174 |
-
|
175 |
-
/**
|
176 |
-
* @return false
|
177 |
-
*/
|
178 |
-
public function getClone() {
|
179 |
-
return $this->clone;
|
180 |
-
}
|
181 |
-
|
182 |
-
/**
|
183 |
-
* @return null|object
|
184 |
-
*/
|
185 |
-
public function getTables() {
|
186 |
-
return $this->tables;
|
187 |
-
}
|
188 |
-
|
189 |
-
/**
|
190 |
-
* Start Module
|
191 |
-
* @param null|array $clone
|
192 |
-
* @return bool
|
193 |
-
*/
|
194 |
-
public function start($clone = null) {
|
195 |
-
// Set data
|
196 |
-
$this->setData($clone);
|
197 |
-
|
198 |
-
// Get the job first
|
199 |
-
$this->getJob();
|
200 |
-
|
201 |
-
$method = "delete" . ucwords($this->job->current);
|
202 |
-
return $this->{$method}();
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* Get job data
|
207 |
-
*/
|
208 |
-
private function getJob() {
|
209 |
-
$this->job = $this->cache->get("delete_job_{$this->clone->name}");
|
210 |
-
|
211 |
-
|
212 |
-
if (null !== $this->job) {
|
213 |
-
return;
|
214 |
-
}
|
215 |
-
|
216 |
-
// Generate JOB
|
217 |
-
$this->job = (object) array(
|
218 |
-
"current" => "tables",
|
219 |
-
"nextDirectoryToDelete" => $this->clone->path,
|
220 |
-
"name" => $this->clone->name
|
221 |
-
);
|
222 |
-
|
223 |
-
$this->cache->save("delete_job_{$this->clone->name}", $this->job);
|
224 |
-
}
|
225 |
-
|
226 |
-
/**
|
227 |
-
* @return bool
|
228 |
-
*/
|
229 |
-
private function updateJob() {
|
230 |
-
$this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
|
231 |
-
return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
|
232 |
-
}
|
233 |
-
|
234 |
-
/**
|
235 |
-
* @return array
|
236 |
-
*/
|
237 |
-
private function getTablesToRemove() {
|
238 |
-
$tables = $this->getTableNames();
|
239 |
-
|
240 |
-
if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"])) {
|
241 |
-
return $tables;
|
242 |
-
}
|
243 |
-
|
244 |
-
return array_diff($tables, $_POST["excludedTables"]);
|
245 |
-
}
|
246 |
-
|
247 |
-
/**
|
248 |
-
* @return array
|
249 |
-
*/
|
250 |
-
private function getTableNames() {
|
251 |
-
return (!is_array($this->tables)) ? array() : array_map(function($value) {
|
252 |
-
return ($value->name);
|
253 |
-
}, $this->tables);
|
254 |
-
}
|
255 |
-
|
256 |
-
/**
|
257 |
-
* Delete Tables
|
258 |
-
*/
|
259 |
-
public function deleteTables() {
|
260 |
-
if ($this->isOverThreshold()) {
|
261 |
-
return;
|
262 |
-
}
|
263 |
-
|
264 |
-
//$wpdb = WPStaging::getInstance()->get("wpdb");
|
265 |
-
|
266 |
-
foreach ($this->getTablesToRemove() as $table) {
|
267 |
-
// PROTECTION: Never delete any table that beginns with wp prefix of live site
|
268 |
-
if ($this->startsWith($table, $this->wpdb->prefix)) {
|
269 |
-
$this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
|
270 |
-
return false;
|
271 |
-
} else {
|
272 |
-
$this->wpdb->query("DROP TABLE {$table}");
|
273 |
-
}
|
274 |
-
}
|
275 |
-
|
276 |
-
// Move on to the next
|
277 |
-
$this->job->current = "directory";
|
278 |
-
$this->updateJob();
|
279 |
-
}
|
280 |
-
|
281 |
-
/**
|
282 |
-
* Check if a strings start with a specific string
|
283 |
-
* @param string $haystack
|
284 |
-
* @param string $needle
|
285 |
-
* @return bool
|
286 |
-
*/
|
287 |
-
protected function startsWith($haystack, $needle) {
|
288 |
-
$length = strlen($needle);
|
289 |
-
return (substr($haystack, 0, $length) === $needle);
|
290 |
-
}
|
291 |
-
|
292 |
-
/**
|
293 |
-
* Delete complete directory including all files and subfolders
|
294 |
-
*
|
295 |
-
* @throws InvalidArgumentException
|
296 |
-
*/
|
297 |
-
public function deleteDirectory() {
|
298 |
-
if ($this->isFatalError()) {
|
299 |
-
$this->returnException('Can not delete directory: ' . $this->clone->path . '. This seems to be the root directory. Please contact support@wp-staging.com');
|
300 |
-
throw new \Exception('Can not delete directory: ' . $this->clone->path . ' This seems to be the root directory. Please contact support@wp-staging.com');
|
301 |
-
}
|
302 |
-
// Finished or path does not exist
|
303 |
-
if (
|
304 |
-
empty($this->clone->path) ||
|
305 |
-
$this->clone->path == get_home_path() ||
|
306 |
-
!is_dir($this->clone->path)) {
|
307 |
-
|
308 |
-
$this->job->current = "finish";
|
309 |
-
$this->updateJob();
|
310 |
-
return $this->returnFinish();
|
311 |
-
}
|
312 |
-
|
313 |
-
$this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
|
314 |
-
|
315 |
-
// Just to make sure the root dir is never deleted!
|
316 |
-
if ($this->clone->path == get_home_path()) {
|
317 |
-
$this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
|
318 |
-
$this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
|
319 |
-
}
|
320 |
-
|
321 |
-
// Check if threshold is reached
|
322 |
-
if ($this->isOverThreshold()) {
|
323 |
-
//$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
|
324 |
-
return;
|
325 |
-
}
|
326 |
-
|
327 |
-
$di = new \RecursiveDirectoryIterator($this->clone->path, \FilesystemIterator::SKIP_DOTS);
|
328 |
-
$ri = new \RecursiveIteratorIterator($di, \RecursiveIteratorIterator::CHILD_FIRST);
|
329 |
-
foreach ($ri as $file) {
|
330 |
-
//$file->isDir() ? @rmdir($file) : unlink($file);
|
331 |
-
$this->deleteFile($file);
|
332 |
-
if ($this->isOverThreshold()) {
|
333 |
-
//$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
|
334 |
-
return;
|
335 |
-
}
|
336 |
-
}
|
337 |
-
|
338 |
-
if (@rmdir($this->clone->path)) {
|
339 |
-
return $this->returnFinish();
|
340 |
-
}
|
341 |
-
return;
|
342 |
-
}
|
343 |
-
|
344 |
-
/**
|
345 |
-
* Delete file
|
346 |
-
* @param object iterator $file
|
347 |
-
*/
|
348 |
-
private function deleteFile($file) {
|
349 |
-
if ($file->isDir()) {
|
350 |
-
if (!@rmdir($file)) {
|
351 |
-
$this->returnException('Permission Error: Can not delete folder ' . $file);
|
352 |
-
}
|
353 |
-
} else {
|
354 |
-
if (!unlink($file)) {
|
355 |
-
$this->returnException('Permission Error: Can not delete file ' . $file);
|
356 |
-
}
|
357 |
-
}
|
358 |
-
}
|
359 |
-
|
360 |
-
/**
|
361 |
-
* @return bool
|
362 |
-
*/
|
363 |
-
public function isDirectoryDeletingFinished() {
|
364 |
-
return (
|
365 |
-
(false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
|
366 |
-
!is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
|
367 |
-
);
|
368 |
-
}
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
/**
|
373 |
-
*
|
374 |
-
* @return boolean
|
375 |
-
*/
|
376 |
-
public function isFatalError(){
|
377 |
-
$homePath = rtrim(get_home_path(), "/");
|
378 |
-
if (rtrim($this->clone->path,"/") == $homePath){
|
379 |
-
return true;
|
380 |
-
}
|
381 |
-
return false;
|
382 |
-
}
|
383 |
-
|
384 |
-
/**
|
385 |
-
* Finish / Update Existing Clones
|
386 |
-
*/
|
387 |
-
public function deleteFinish() {
|
388 |
-
$existingClones = get_option("wpstg_existing_clones_beta", array());
|
389 |
-
|
390 |
-
// Check if clones still exist
|
391 |
-
$this->log("Verifying existing clones...");
|
392 |
-
foreach ($existingClones as $name => $clone) {
|
393 |
-
if (!is_dir($clone["path"])) {
|
394 |
-
unset($existingClones[$name]);
|
395 |
-
}
|
396 |
-
}
|
397 |
-
$this->log("Existing clones verified!");
|
398 |
-
|
399 |
-
if (false === update_option("wpstg_existing_clones_beta", $existingClones)) {
|
400 |
-
$this->log("Failed to save {$this->options->clone}'s clone job data to database'");
|
401 |
-
}
|
402 |
-
|
403 |
-
// Delete cached file
|
404 |
-
$this->cache->delete("delete_job_{$this->clone->name}");
|
405 |
-
$this->cache->delete("delete_directories_{$this->clone->name}");
|
406 |
-
|
407 |
-
//return true;
|
408 |
-
$response = array('delete' => 'finished');
|
409 |
-
wp_die(json_encode($response));
|
410 |
-
}
|
411 |
-
|
412 |
-
/**
|
413 |
-
* Get json response
|
414 |
-
* return json
|
415 |
-
*/
|
416 |
-
private function returnFinish($message = '') {
|
417 |
-
|
418 |
-
$this->deleteFinish();
|
419 |
-
|
420 |
-
wp_die(json_encode(array(
|
421 |
-
'job' => 'delete',
|
422 |
-
'status' => true,
|
423 |
-
'message' => $message,
|
424 |
-
'error' => false,
|
425 |
-
'delete' => 'finished'
|
426 |
-
)));
|
427 |
-
}
|
428 |
-
|
429 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
+
|
5 |
+
use WPStaging\Backend\Modules\Jobs\Exceptions\CloneNotFoundException;
|
6 |
+
use WPStaging\Utils\Directories;
|
7 |
+
use WPStaging\Utils\Logger;
|
8 |
+
use WPStaging\WPStaging;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class Delete
|
12 |
+
* @package WPStaging\Backend\Modules\Jobs
|
13 |
+
*/
|
14 |
+
class Delete extends Job {
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var false
|
18 |
+
*/
|
19 |
+
private $clone = false;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var null|object
|
23 |
+
*/
|
24 |
+
private $tables = null;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var object|null
|
28 |
+
*/
|
29 |
+
private $job = null;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var bool
|
33 |
+
*/
|
34 |
+
private $forceDeleteDirectories = false;
|
35 |
+
|
36 |
+
/**
|
37 |
+
*
|
38 |
+
* @var object
|
39 |
+
*/
|
40 |
+
public $wpdb;
|
41 |
+
|
42 |
+
public function __construct() {
|
43 |
+
parent::__construct();
|
44 |
+
$this->wpdb = WPStaging::getInstance()->get("wpdb");
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Sets Clone and Table Records
|
49 |
+
* @param null|array $clone
|
50 |
+
*/
|
51 |
+
public function setData($clone = null) {
|
52 |
+
if (!is_array($clone)) {
|
53 |
+
$this->getCloneRecords();
|
54 |
+
} else {
|
55 |
+
$this->clone = (object) $clone;
|
56 |
+
$this->forceDeleteDirectories = true;
|
57 |
+
}
|
58 |
+
|
59 |
+
$this->getTableRecords();
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Get clone
|
64 |
+
* @param null|string $name
|
65 |
+
* @throws CloneNotFoundException
|
66 |
+
*/
|
67 |
+
private function getCloneRecords($name = null) {
|
68 |
+
if (null === $name && !isset($_POST["clone"])) {
|
69 |
+
$this->log("Clone name is not set", Logger::TYPE_FATAL);
|
70 |
+
throw new CloneNotFoundException();
|
71 |
+
}
|
72 |
+
|
73 |
+
if (null === $name) {
|
74 |
+
$name = $_POST["clone"];
|
75 |
+
}
|
76 |
+
|
77 |
+
$clones = get_option("wpstg_existing_clones_beta", array());
|
78 |
+
|
79 |
+
if (empty($clones) || !isset($clones[$name])) {
|
80 |
+
$this->log("Couldn't find clone name {$name} or no existing clone", Logger::TYPE_FATAL);
|
81 |
+
throw new CloneNotFoundException();
|
82 |
+
}
|
83 |
+
|
84 |
+
$this->clone = $clones[$name];
|
85 |
+
$this->clone["name"] = $name;
|
86 |
+
|
87 |
+
$this->clone = (object) $this->clone;
|
88 |
+
|
89 |
+
unset($clones);
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Get Tables
|
94 |
+
*/
|
95 |
+
private function getTableRecords() {
|
96 |
+
//$wpdb = WPStaging::getInstance()->get("wpdb");
|
97 |
+
$this->wpdb = WPStaging::getInstance()->get("wpdb");
|
98 |
+
|
99 |
+
$stagingPrefix = $this->getStagingPrefix();
|
100 |
+
|
101 |
+
$tables = $this->wpdb->get_results("SHOW TABLE STATUS LIKE '{$stagingPrefix}%'");
|
102 |
+
|
103 |
+
$this->tables = array();
|
104 |
+
|
105 |
+
foreach ($tables as $table) {
|
106 |
+
$this->tables[] = array(
|
107 |
+
"name" => $table->Name,
|
108 |
+
"size" => $this->formatSize(($table->Data_length + $table->Index_length))
|
109 |
+
);
|
110 |
+
}
|
111 |
+
|
112 |
+
$this->tables = json_decode(json_encode($this->tables));
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Check and return prefix of the staging site
|
117 |
+
*/
|
118 |
+
public function getStagingPrefix() {
|
119 |
+
// Prefix not defined! Happens if staging site has ben generated with older version of wpstg
|
120 |
+
// Try to get staging prefix from wp-config.php of staging site
|
121 |
+
if (empty($this->clone->prefix)) {
|
122 |
+
// Throw error
|
123 |
+
$path = ABSPATH . $this->clone->directoryName . "/wp-config.php";
|
124 |
+
if (false === ($content = @file_get_contents($path))) {
|
125 |
+
$this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
|
126 |
+
// Create a random prefix which hopefully never exists.
|
127 |
+
$this->clone->prefix = rand(7, 15) . '_';
|
128 |
+
} else {
|
129 |
+
|
130 |
+
// Get prefix from wp-config.php
|
131 |
+
//preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
|
132 |
+
preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
|
133 |
+
//wp_die(var_dump($matches));
|
134 |
+
|
135 |
+
if (!empty($matches[1])) {
|
136 |
+
$this->clone->prefix = $matches[1];
|
137 |
+
} else {
|
138 |
+
$this->log("Fatal Error: Can not delete staging site. Can not find Prefix. '{$matches[1]}'. Stopping for security reasons. Creating a new staging site will likely resolve this the next time. Contact support@wp-staging.com");
|
139 |
+
// Create a random prefix which hopefully never exists.
|
140 |
+
return $this->clone->prefix = rand(7, 15) . '_';
|
141 |
+
}
|
142 |
+
}
|
143 |
+
}
|
144 |
+
|
145 |
+
// Check if staging prefix is the same as the live prefix
|
146 |
+
if ($this->wpdb->prefix == $this->clone->prefix) {
|
147 |
+
$this->log("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
|
148 |
+
wp_die("Fatal Error: Can not delete staging site. Prefix. '{$this->clone->prefix}' is used for the live site. Creating a new staging site will likely resolve this the next time. Stopping for security reasons. Contact support@wp-staging.com");
|
149 |
+
}
|
150 |
+
|
151 |
+
// Else
|
152 |
+
return $this->clone->prefix;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Format bytes into human readable form
|
157 |
+
* @param int $bytes
|
158 |
+
* @param int $precision
|
159 |
+
* @return string
|
160 |
+
*/
|
161 |
+
public function formatSize($bytes, $precision = 2) {
|
162 |
+
if ((int) $bytes < 1) {
|
163 |
+
return '';
|
164 |
+
}
|
165 |
+
|
166 |
+
$units = array('B', "KB", "MB", "GB", "TB");
|
167 |
+
|
168 |
+
$bytes = (int) $bytes;
|
169 |
+
$base = log($bytes) / log(1000); // 1024 would be for MiB KiB etc
|
170 |
+
$pow = pow(1000, $base - floor($base)); // Same rule for 1000
|
171 |
+
|
172 |
+
return round($pow, $precision) . ' ' . $units[(int) floor($base)];
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* @return false
|
177 |
+
*/
|
178 |
+
public function getClone() {
|
179 |
+
return $this->clone;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* @return null|object
|
184 |
+
*/
|
185 |
+
public function getTables() {
|
186 |
+
return $this->tables;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Start Module
|
191 |
+
* @param null|array $clone
|
192 |
+
* @return bool
|
193 |
+
*/
|
194 |
+
public function start($clone = null) {
|
195 |
+
// Set data
|
196 |
+
$this->setData($clone);
|
197 |
+
|
198 |
+
// Get the job first
|
199 |
+
$this->getJob();
|
200 |
+
|
201 |
+
$method = "delete" . ucwords($this->job->current);
|
202 |
+
return $this->{$method}();
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Get job data
|
207 |
+
*/
|
208 |
+
private function getJob() {
|
209 |
+
$this->job = $this->cache->get("delete_job_{$this->clone->name}");
|
210 |
+
|
211 |
+
|
212 |
+
if (null !== $this->job) {
|
213 |
+
return;
|
214 |
+
}
|
215 |
+
|
216 |
+
// Generate JOB
|
217 |
+
$this->job = (object) array(
|
218 |
+
"current" => "tables",
|
219 |
+
"nextDirectoryToDelete" => $this->clone->path,
|
220 |
+
"name" => $this->clone->name
|
221 |
+
);
|
222 |
+
|
223 |
+
$this->cache->save("delete_job_{$this->clone->name}", $this->job);
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* @return bool
|
228 |
+
*/
|
229 |
+
private function updateJob() {
|
230 |
+
$this->job->nextDirectoryToDelete = trim($this->job->nextDirectoryToDelete);
|
231 |
+
return $this->cache->save("delete_job_{$this->clone->name}", $this->job);
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* @return array
|
236 |
+
*/
|
237 |
+
private function getTablesToRemove() {
|
238 |
+
$tables = $this->getTableNames();
|
239 |
+
|
240 |
+
if (!isset($_POST["excludedTables"]) || !is_array($_POST["excludedTables"]) || empty($_POST["excludedTables"])) {
|
241 |
+
return $tables;
|
242 |
+
}
|
243 |
+
|
244 |
+
return array_diff($tables, $_POST["excludedTables"]);
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* @return array
|
249 |
+
*/
|
250 |
+
private function getTableNames() {
|
251 |
+
return (!is_array($this->tables)) ? array() : array_map(function($value) {
|
252 |
+
return ($value->name);
|
253 |
+
}, $this->tables);
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Delete Tables
|
258 |
+
*/
|
259 |
+
public function deleteTables() {
|
260 |
+
if ($this->isOverThreshold()) {
|
261 |
+
return;
|
262 |
+
}
|
263 |
+
|
264 |
+
//$wpdb = WPStaging::getInstance()->get("wpdb");
|
265 |
+
|
266 |
+
foreach ($this->getTablesToRemove() as $table) {
|
267 |
+
// PROTECTION: Never delete any table that beginns with wp prefix of live site
|
268 |
+
if ($this->startsWith($table, $this->wpdb->prefix)) {
|
269 |
+
$this->log("Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL);
|
270 |
+
return false;
|
271 |
+
} else {
|
272 |
+
$this->wpdb->query("DROP TABLE {$table}");
|
273 |
+
}
|
274 |
+
}
|
275 |
+
|
276 |
+
// Move on to the next
|
277 |
+
$this->job->current = "directory";
|
278 |
+
$this->updateJob();
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* Check if a strings start with a specific string
|
283 |
+
* @param string $haystack
|
284 |
+
* @param string $needle
|
285 |
+
* @return bool
|
286 |
+
*/
|
287 |
+
protected function startsWith($haystack, $needle) {
|
288 |
+
$length = strlen($needle);
|
289 |
+
return (substr($haystack, 0, $length) === $needle);
|
290 |
+
}
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Delete complete directory including all files and subfolders
|
294 |
+
*
|
295 |
+
* @throws InvalidArgumentException
|
296 |
+
*/
|
297 |
+
public function deleteDirectory() {
|
298 |
+
if ($this->isFatalError()) {
|
299 |
+
$this->returnException('Can not delete directory: ' . $this->clone->path . '. This seems to be the root directory. Please contact support@wp-staging.com');
|
300 |
+
throw new \Exception('Can not delete directory: ' . $this->clone->path . ' This seems to be the root directory. Please contact support@wp-staging.com');
|
301 |
+
}
|
302 |
+
// Finished or path does not exist
|
303 |
+
if (
|
304 |
+
empty($this->clone->path) ||
|
305 |
+
$this->clone->path == get_home_path() ||
|
306 |
+
!is_dir($this->clone->path)) {
|
307 |
+
|
308 |
+
$this->job->current = "finish";
|
309 |
+
$this->updateJob();
|
310 |
+
return $this->returnFinish();
|
311 |
+
}
|
312 |
+
|
313 |
+
$this->log("Delete staging site: " . $this->clone->path, Logger::TYPE_INFO);
|
314 |
+
|
315 |
+
// Just to make sure the root dir is never deleted!
|
316 |
+
if ($this->clone->path == get_home_path()) {
|
317 |
+
$this->log("Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
|
318 |
+
$this->returnException('Fatal Error 8: Trying to delete root of WP installation!');
|
319 |
+
}
|
320 |
+
|
321 |
+
// Check if threshold is reached
|
322 |
+
if ($this->isOverThreshold()) {
|
323 |
+
//$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
|
324 |
+
return;
|
325 |
+
}
|
326 |
+
|
327 |
+
$di = new \RecursiveDirectoryIterator($this->clone->path, \FilesystemIterator::SKIP_DOTS);
|
328 |
+
$ri = new \RecursiveIteratorIterator($di, \RecursiveIteratorIterator::CHILD_FIRST);
|
329 |
+
foreach ($ri as $file) {
|
330 |
+
//$file->isDir() ? @rmdir($file) : unlink($file);
|
331 |
+
$this->deleteFile($file);
|
332 |
+
if ($this->isOverThreshold()) {
|
333 |
+
//$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
|
334 |
+
return;
|
335 |
+
}
|
336 |
+
}
|
337 |
+
|
338 |
+
if (@rmdir($this->clone->path)) {
|
339 |
+
return $this->returnFinish();
|
340 |
+
}
|
341 |
+
return;
|
342 |
+
}
|
343 |
+
|
344 |
+
/**
|
345 |
+
* Delete file
|
346 |
+
* @param object iterator $file
|
347 |
+
*/
|
348 |
+
private function deleteFile($file) {
|
349 |
+
if ($file->isDir()) {
|
350 |
+
if (!@rmdir($file)) {
|
351 |
+
$this->returnException('Permission Error: Can not delete folder ' . $file);
|
352 |
+
}
|
353 |
+
} else {
|
354 |
+
if (!unlink($file)) {
|
355 |
+
$this->returnException('Permission Error: Can not delete file ' . $file);
|
356 |
+
}
|
357 |
+
}
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* @return bool
|
362 |
+
*/
|
363 |
+
public function isDirectoryDeletingFinished() {
|
364 |
+
return (
|
365 |
+
(false === $this->forceDeleteDirectories && (!isset($_POST["deleteDir"]) || '1' !== $_POST["deleteDir"])) ||
|
366 |
+
!is_dir($this->clone->path) || ABSPATH === $this->job->nextDirectoryToDelete
|
367 |
+
);
|
368 |
+
}
|
369 |
+
|
370 |
+
|
371 |
+
|
372 |
+
/**
|
373 |
+
*
|
374 |
+
* @return boolean
|
375 |
+
*/
|
376 |
+
public function isFatalError(){
|
377 |
+
$homePath = rtrim(get_home_path(), "/");
|
378 |
+
if (rtrim($this->clone->path,"/") == $homePath){
|
379 |
+
return true;
|
380 |
+
}
|
381 |
+
return false;
|
382 |
+
}
|
383 |
+
|
384 |
+
/**
|
385 |
+
* Finish / Update Existing Clones
|
386 |
+
*/
|
387 |
+
public function deleteFinish() {
|
388 |
+
$existingClones = get_option("wpstg_existing_clones_beta", array());
|
389 |
+
|
390 |
+
// Check if clones still exist
|
391 |
+
$this->log("Verifying existing clones...");
|
392 |
+
foreach ($existingClones as $name => $clone) {
|
393 |
+
if (!is_dir($clone["path"])) {
|
394 |
+
unset($existingClones[$name]);
|
395 |
+
}
|
396 |
+
}
|
397 |
+
$this->log("Existing clones verified!");
|
398 |
+
|
399 |
+
if (false === update_option("wpstg_existing_clones_beta", $existingClones)) {
|
400 |
+
$this->log("Failed to save {$this->options->clone}'s clone job data to database'");
|
401 |
+
}
|
402 |
+
|
403 |
+
// Delete cached file
|
404 |
+
$this->cache->delete("delete_job_{$this->clone->name}");
|
405 |
+
$this->cache->delete("delete_directories_{$this->clone->name}");
|
406 |
+
|
407 |
+
//return true;
|
408 |
+
$response = array('delete' => 'finished');
|
409 |
+
wp_die(json_encode($response));
|
410 |
+
}
|
411 |
+
|
412 |
+
/**
|
413 |
+
* Get json response
|
414 |
+
* return json
|
415 |
+
*/
|
416 |
+
private function returnFinish($message = '') {
|
417 |
+
|
418 |
+
$this->deleteFinish();
|
419 |
+
|
420 |
+
wp_die(json_encode(array(
|
421 |
+
'job' => 'delete',
|
422 |
+
'status' => true,
|
423 |
+
'message' => $message,
|
424 |
+
'error' => false,
|
425 |
+
'delete' => 'finished'
|
426 |
+
)));
|
427 |
+
}
|
428 |
+
|
429 |
+
}
|
apps/Backend/Modules/Jobs/Files.php
CHANGED
@@ -1,329 +1,329 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
use WPStaging\Utils\Logger;
|
7 |
-
|
8 |
-
if (!defined("WPINC")) {
|
9 |
-
die;
|
10 |
-
}
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Class Files
|
14 |
-
* @package WPStaging\Backend\Modules\Jobs
|
15 |
-
*/
|
16 |
-
class Files extends JobExecutable {
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @var \SplFileObject
|
20 |
-
*/
|
21 |
-
private $file;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @var int
|
25 |
-
*/
|
26 |
-
private $maxFilesPerRun;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @var string
|
30 |
-
*/
|
31 |
-
private $destination;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Initialization
|
35 |
-
*/
|
36 |
-
public function initialize() {
|
37 |
-
|
38 |
-
$this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
|
39 |
-
|
40 |
-
$filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
|
41 |
-
|
42 |
-
if (is_file($filePath)) {
|
43 |
-
$this->file = new \SplFileObject($filePath, 'r');
|
44 |
-
}
|
45 |
-
|
46 |
-
// Informational logs
|
47 |
-
if (0 == $this->options->currentStep) {
|
48 |
-
$this->log("Copying files...");
|
49 |
-
}
|
50 |
-
|
51 |
-
$this->settings->batchSize = $this->settings->batchSize * 1000000;
|
52 |
-
$this->maxFilesPerRun = $this->settings->fileLimit;
|
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 = ceil($this->options->totalFiles / $this->maxFilesPerRun);
|
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 |
-
// Finished
|
70 |
-
if ($this->isFinished()) {
|
71 |
-
$this->log("Copying files finished");
|
72 |
-
$this->prepareResponse(true, false);
|
73 |
-
return false;
|
74 |
-
}
|
75 |
-
|
76 |
-
// Get files and copy'em
|
77 |
-
if (!$this->getFilesAndCopy()) {
|
78 |
-
$this->prepareResponse(false, false);
|
79 |
-
return false;
|
80 |
-
}
|
81 |
-
|
82 |
-
// Prepare and return response
|
83 |
-
$this->prepareResponse();
|
84 |
-
|
85 |
-
// Not finished
|
86 |
-
return true;
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* Get files and copy
|
91 |
-
* @return bool
|
92 |
-
*/
|
93 |
-
private function getFilesAndCopy() {
|
94 |
-
// Over limits threshold
|
95 |
-
if ($this->isOverThreshold()) {
|
96 |
-
// Prepare response and save current progress
|
97 |
-
$this->prepareResponse(false, false);
|
98 |
-
$this->saveOptions();
|
99 |
-
return false;
|
100 |
-
}
|
101 |
-
|
102 |
-
// Go to last copied line and than to next one
|
103 |
-
//if ($this->options->copiedFiles != 0) {
|
104 |
-
if (isset($this->options->copiedFiles) && $this->options->copiedFiles != 0) {
|
105 |
-
$this->file->seek($this->options->copiedFiles - 1);
|
106 |
-
}
|
107 |
-
|
108 |
-
$this->file->setFlags(\SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
|
109 |
-
|
110 |
-
|
111 |
-
// Loop x files at a time
|
112 |
-
for ($i = 0; $i < $this->maxFilesPerRun; $i++) {
|
113 |
-
|
114 |
-
// Increment copied files
|
115 |
-
// Do this anytime to make sure to not stuck in the same step / files
|
116 |
-
$this->options->copiedFiles++;
|
117 |
-
|
118 |
-
// End of file
|
119 |
-
if ($this->file->eof()) {
|
120 |
-
break;
|
121 |
-
}
|
122 |
-
|
123 |
-
|
124 |
-
$file = $this->file->fgets();
|
125 |
-
//$this->debugLog('copy file ' . $file, Logger::TYPE_DEBUG);
|
126 |
-
$this->copyFile($file);
|
127 |
-
}
|
128 |
-
|
129 |
-
$totalFiles = $this->options->copiedFiles;
|
130 |
-
// Log this only every 50 entries to keep the log small and to not block the rendering browser
|
131 |
-
if ($this->options->copiedFiles %50 == 0){
|
132 |
-
$this->log("Total {$totalFiles} files processed");
|
133 |
-
}
|
134 |
-
|
135 |
-
return true;
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Checks Whether There is Any Job to Execute or Not
|
140 |
-
* @return bool
|
141 |
-
*/
|
142 |
-
private function isFinished() {
|
143 |
-
return (
|
144 |
-
$this->options->currentStep > $this->options->totalSteps ||
|
145 |
-
$this->options->copiedFiles >= $this->options->totalFiles
|
146 |
-
);
|
147 |
-
}
|
148 |
-
|
149 |
-
/**
|
150 |
-
* @param string $file
|
151 |
-
* @return bool
|
152 |
-
*/
|
153 |
-
private function copyFile($file) {
|
154 |
-
$file = trim(ABSPATH . $file);
|
155 |
-
|
156 |
-
$directory = dirname($file);
|
157 |
-
|
158 |
-
// Get file size
|
159 |
-
$fileSize = filesize($file);
|
160 |
-
|
161 |
-
// Directory is excluded
|
162 |
-
if ($this->isDirectoryExcluded($directory)) {
|
163 |
-
$this->debugLog("Skipping directory by rule: {$file}", Logger::TYPE_INFO);
|
164 |
-
return false;
|
165 |
-
}
|
166 |
-
|
167 |
-
// File is excluded
|
168 |
-
if ($this->isFileExcluded($file)) {
|
169 |
-
$this->debugLog("Skipping file by rule: {$file}", Logger::TYPE_INFO);
|
170 |
-
return false;
|
171 |
-
}
|
172 |
-
|
173 |
-
// File is over maximum allowed file size (8MB)
|
174 |
-
if ($fileSize >= $this->settings->maxFileSize * 1000000) {
|
175 |
-
$this->log("Skipping big file: {$file}", Logger::TYPE_INFO);
|
176 |
-
return false;
|
177 |
-
}
|
178 |
-
|
179 |
-
// Invalid file, skipping it as if succeeded
|
180 |
-
if (!is_file($file)) {
|
181 |
-
$this->debugLog("Not a file {$file}");
|
182 |
-
return true;
|
183 |
-
}
|
184 |
-
// Invalid file, skipping it as if succeeded
|
185 |
-
if (!is_readable($file)) {
|
186 |
-
$this->log("Can't read file {$file}");
|
187 |
-
return true;
|
188 |
-
}
|
189 |
-
|
190 |
-
// Failed to get destination
|
191 |
-
if (false === ($destination = $this->getDestination($file))) {
|
192 |
-
$this->log("Can't get the destination of {$file}");
|
193 |
-
return false;
|
194 |
-
}
|
195 |
-
|
196 |
-
// File is over batch size
|
197 |
-
if ($fileSize >= $this->settings->batchSize) {
|
198 |
-
$this->log("Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO);
|
199 |
-
return $this->copyBig($file, $destination, $this->settings->batchSize);
|
200 |
-
}
|
201 |
-
|
202 |
-
// Attempt to copy
|
203 |
-
if (!@copy($file, $destination)) {
|
204 |
-
$this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
|
205 |
-
return false;
|
206 |
-
}
|
207 |
-
|
208 |
-
return true;
|
209 |
-
|
210 |
-
// Good old PHP
|
211 |
-
//return $this->copy($file, $destination);
|
212 |
-
}
|
213 |
-
|
214 |
-
/**
|
215 |
-
* Gets destination file and checks if the directory exists, if it does not attempts to create it.
|
216 |
-
* If creating destination directory fails, it returns false, gives destination full path otherwise
|
217 |
-
* @param string $file
|
218 |
-
* @return bool|string
|
219 |
-
*/
|
220 |
-
private function getDestination($file) {
|
221 |
-
$relativePath = str_replace(ABSPATH, null, $file);
|
222 |
-
$destinationPath = $this->destination . $relativePath;
|
223 |
-
$destinationDirectory = dirname($destinationPath);
|
224 |
-
|
225 |
-
if (!is_dir($destinationDirectory) && !@mkdir($destinationDirectory, 0775, true)) {
|
226 |
-
$this->log("Files: Can not create directory {$destinationDirectory}", Logger::TYPE_ERROR);
|
227 |
-
return false;
|
228 |
-
}
|
229 |
-
|
230 |
-
return $destinationPath;
|
231 |
-
}
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
/**
|
237 |
-
* Copy bigger files than $this->settings->batchSize
|
238 |
-
* @param string $src
|
239 |
-
* @param string $dst
|
240 |
-
* @param int $buffersize
|
241 |
-
* @return boolean
|
242 |
-
*/
|
243 |
-
private function copyBig($src, $dst, $buffersize) {
|
244 |
-
$src = fopen($src, 'r');
|
245 |
-
$dest = fopen($dst, 'w');
|
246 |
-
|
247 |
-
// Try first method:
|
248 |
-
while (!feof($src)) {
|
249 |
-
if (false === fwrite($dest, fread($src, $buffersize))) {
|
250 |
-
$error = true;
|
251 |
-
}
|
252 |
-
}
|
253 |
-
// Try second method if first one failed
|
254 |
-
if (isset($error) && ($error === true)) {
|
255 |
-
while (!feof($src)) {
|
256 |
-
if (false === stream_copy_to_stream($src, $dest, 1024)) {
|
257 |
-
$this->log("Can not copy big file; {$src} -> {$dest}");
|
258 |
-
fclose($src);
|
259 |
-
fclose($dest);
|
260 |
-
return false;
|
261 |
-
}
|
262 |
-
}
|
263 |
-
}
|
264 |
-
// Close any open handler
|
265 |
-
fclose($src);
|
266 |
-
fclose($dest);
|
267 |
-
return true;
|
268 |
-
}
|
269 |
-
|
270 |
-
/**
|
271 |
-
* Check if file is excluded from copying process
|
272 |
-
*
|
273 |
-
* @param string $file filename including ending
|
274 |
-
* @return boolean
|
275 |
-
*/
|
276 |
-
private function isFileExcluded($file) {
|
277 |
-
$excluded = false;
|
278 |
-
foreach ($this->options->excludedFiles as $excludedFile) {
|
279 |
-
if (stripos(strrev($file), strrev($excludedFile)) === 0) {
|
280 |
-
$excluded = true;
|
281 |
-
break;
|
282 |
-
}
|
283 |
-
}
|
284 |
-
|
285 |
-
// Do not copy wp-config.php if the clone gets updated. This is for security purposes,
|
286 |
-
// because if the updating process fails, the staging site would not be accessable any longer
|
287 |
-
if (isset($this->options->mainJob ) && $this->options->mainJob == "updating" && stripos(strrev($file), strrev("wp-config.php")) === 0){
|
288 |
-
$excluded = true;
|
289 |
-
}
|
290 |
-
|
291 |
-
|
292 |
-
return $excluded;
|
293 |
-
}
|
294 |
-
|
295 |
-
/**
|
296 |
-
* Check if directory is excluded from copying
|
297 |
-
* @param string $directory
|
298 |
-
* @return bool
|
299 |
-
*/
|
300 |
-
private function isDirectoryExcluded($directory) {
|
301 |
-
// Make sure that wp-staging-pro directory / plugin is never excluded
|
302 |
-
if (false !== strpos( $directory, 'wp-staging' ) || false !== strpos( $directory, 'wp-staging-pro' ) ){
|
303 |
-
return false;
|
304 |
-
}
|
305 |
-
|
306 |
-
foreach ($this->options->excludedDirectories as $excludedDirectory) {
|
307 |
-
if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
|
308 |
-
return true;
|
309 |
-
}
|
310 |
-
}
|
311 |
-
|
312 |
-
return false;
|
313 |
-
}
|
314 |
-
|
315 |
-
/**
|
316 |
-
* Check if directory is an extra directory and should be copied
|
317 |
-
* @param string $directory
|
318 |
-
* @return boolean
|
319 |
-
*/
|
320 |
-
private function isExtraDirectory($directory) {
|
321 |
-
foreach ($this->options->extraDirectories as $extraDirectory) {
|
322 |
-
if (strpos($directory, $extraDirectory) === 0) {
|
323 |
-
return true;
|
324 |
-
}
|
325 |
-
}
|
326 |
-
|
327 |
-
return false;
|
328 |
-
}
|
329 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
use WPStaging\Utils\Logger;
|
7 |
+
|
8 |
+
if (!defined("WPINC")) {
|
9 |
+
die;
|
10 |
+
}
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Class Files
|
14 |
+
* @package WPStaging\Backend\Modules\Jobs
|
15 |
+
*/
|
16 |
+
class Files extends JobExecutable {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var \SplFileObject
|
20 |
+
*/
|
21 |
+
private $file;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var int
|
25 |
+
*/
|
26 |
+
private $maxFilesPerRun;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
private $destination;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Initialization
|
35 |
+
*/
|
36 |
+
public function initialize() {
|
37 |
+
|
38 |
+
$this->destination = ABSPATH . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR;
|
39 |
+
|
40 |
+
$filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
|
41 |
+
|
42 |
+
if (is_file($filePath)) {
|
43 |
+
$this->file = new \SplFileObject($filePath, 'r');
|
44 |
+
}
|
45 |
+
|
46 |
+
// Informational logs
|
47 |
+
if (0 == $this->options->currentStep) {
|
48 |
+
$this->log("Copying files...");
|
49 |
+
}
|
50 |
+
|
51 |
+
$this->settings->batchSize = $this->settings->batchSize * 1000000;
|
52 |
+
$this->maxFilesPerRun = $this->settings->fileLimit;
|
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 = ceil($this->options->totalFiles / $this->maxFilesPerRun);
|
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 |
+
// Finished
|
70 |
+
if ($this->isFinished()) {
|
71 |
+
$this->log("Copying files finished");
|
72 |
+
$this->prepareResponse(true, false);
|
73 |
+
return false;
|
74 |
+
}
|
75 |
+
|
76 |
+
// Get files and copy'em
|
77 |
+
if (!$this->getFilesAndCopy()) {
|
78 |
+
$this->prepareResponse(false, false);
|
79 |
+
return false;
|
80 |
+
}
|
81 |
+
|
82 |
+
// Prepare and return response
|
83 |
+
$this->prepareResponse();
|
84 |
+
|
85 |
+
// Not finished
|
86 |
+
return true;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Get files and copy
|
91 |
+
* @return bool
|
92 |
+
*/
|
93 |
+
private function getFilesAndCopy() {
|
94 |
+
// Over limits threshold
|
95 |
+
if ($this->isOverThreshold()) {
|
96 |
+
// Prepare response and save current progress
|
97 |
+
$this->prepareResponse(false, false);
|
98 |
+
$this->saveOptions();
|
99 |
+
return false;
|
100 |
+
}
|
101 |
+
|
102 |
+
// Go to last copied line and than to next one
|
103 |
+
//if ($this->options->copiedFiles != 0) {
|
104 |
+
if (isset($this->options->copiedFiles) && $this->options->copiedFiles != 0) {
|
105 |
+
$this->file->seek($this->options->copiedFiles - 1);
|
106 |
+
}
|
107 |
+
|
108 |
+
$this->file->setFlags(\SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
|
109 |
+
|
110 |
+
|
111 |
+
// Loop x files at a time
|
112 |
+
for ($i = 0; $i < $this->maxFilesPerRun; $i++) {
|
113 |
+
|
114 |
+
// Increment copied files
|
115 |
+
// Do this anytime to make sure to not stuck in the same step / files
|
116 |
+
$this->options->copiedFiles++;
|
117 |
+
|
118 |
+
// End of file
|
119 |
+
if ($this->file->eof()) {
|
120 |
+
break;
|
121 |
+
}
|
122 |
+
|
123 |
+
|
124 |
+
$file = $this->file->fgets();
|
125 |
+
//$this->debugLog('copy file ' . $file, Logger::TYPE_DEBUG);
|
126 |
+
$this->copyFile($file);
|
127 |
+
}
|
128 |
+
|
129 |
+
$totalFiles = $this->options->copiedFiles;
|
130 |
+
// Log this only every 50 entries to keep the log small and to not block the rendering browser
|
131 |
+
if ($this->options->copiedFiles %50 == 0){
|
132 |
+
$this->log("Total {$totalFiles} files processed");
|
133 |
+
}
|
134 |
+
|
135 |
+
return true;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Checks Whether There is Any Job to Execute or Not
|
140 |
+
* @return bool
|
141 |
+
*/
|
142 |
+
private function isFinished() {
|
143 |
+
return (
|
144 |
+
$this->options->currentStep > $this->options->totalSteps ||
|
145 |
+
$this->options->copiedFiles >= $this->options->totalFiles
|
146 |
+
);
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* @param string $file
|
151 |
+
* @return bool
|
152 |
+
*/
|
153 |
+
private function copyFile($file) {
|
154 |
+
$file = trim(ABSPATH . $file);
|
155 |
+
|
156 |
+
$directory = dirname($file);
|
157 |
+
|
158 |
+
// Get file size
|
159 |
+
$fileSize = filesize($file);
|
160 |
+
|
161 |
+
// Directory is excluded
|
162 |
+
if ($this->isDirectoryExcluded($directory)) {
|
163 |
+
$this->debugLog("Skipping directory by rule: {$file}", Logger::TYPE_INFO);
|
164 |
+
return false;
|
165 |
+
}
|
166 |
+
|
167 |
+
// File is excluded
|
168 |
+
if ($this->isFileExcluded($file)) {
|
169 |
+
$this->debugLog("Skipping file by rule: {$file}", Logger::TYPE_INFO);
|
170 |
+
return false;
|
171 |
+
}
|
172 |
+
|
173 |
+
// File is over maximum allowed file size (8MB)
|
174 |
+
if ($fileSize >= $this->settings->maxFileSize * 1000000) {
|
175 |
+
$this->log("Skipping big file: {$file}", Logger::TYPE_INFO);
|
176 |
+
return false;
|
177 |
+
}
|
178 |
+
|
179 |
+
// Invalid file, skipping it as if succeeded
|
180 |
+
if (!is_file($file)) {
|
181 |
+
$this->debugLog("Not a file {$file}");
|
182 |
+
return true;
|
183 |
+
}
|
184 |
+
// Invalid file, skipping it as if succeeded
|
185 |
+
if (!is_readable($file)) {
|
186 |
+
$this->log("Can't read file {$file}");
|
187 |
+
return true;
|
188 |
+
}
|
189 |
+
|
190 |
+
// Failed to get destination
|
191 |
+
if (false === ($destination = $this->getDestination($file))) {
|
192 |
+
$this->log("Can't get the destination of {$file}");
|
193 |
+
return false;
|
194 |
+
}
|
195 |
+
|
196 |
+
// File is over batch size
|
197 |
+
if ($fileSize >= $this->settings->batchSize) {
|
198 |
+
$this->log("Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO);
|
199 |
+
return $this->copyBig($file, $destination, $this->settings->batchSize);
|
200 |
+
}
|
201 |
+
|
202 |
+
// Attempt to copy
|
203 |
+
if (!@copy($file, $destination)) {
|
204 |
+
$this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
|
205 |
+
return false;
|
206 |
+
}
|
207 |
+
|
208 |
+
return true;
|
209 |
+
|
210 |
+
// Good old PHP
|
211 |
+
//return $this->copy($file, $destination);
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Gets destination file and checks if the directory exists, if it does not attempts to create it.
|
216 |
+
* If creating destination directory fails, it returns false, gives destination full path otherwise
|
217 |
+
* @param string $file
|
218 |
+
* @return bool|string
|
219 |
+
*/
|
220 |
+
private function getDestination($file) {
|
221 |
+
$relativePath = str_replace(ABSPATH, null, $file);
|
222 |
+
$destinationPath = $this->destination . $relativePath;
|
223 |
+
$destinationDirectory = dirname($destinationPath);
|
224 |
+
|
225 |
+
if (!is_dir($destinationDirectory) && !@mkdir($destinationDirectory, 0775, true)) {
|
226 |
+
$this->log("Files: Can not create directory {$destinationDirectory}", Logger::TYPE_ERROR);
|
227 |
+
return false;
|
228 |
+
}
|
229 |
+
|
230 |
+
return $destinationPath;
|
231 |
+
}
|
232 |
+
|
233 |
+
|
234 |
+
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Copy bigger files than $this->settings->batchSize
|
238 |
+
* @param string $src
|
239 |
+
* @param string $dst
|
240 |
+
* @param int $buffersize
|
241 |
+
* @return boolean
|
242 |
+
*/
|
243 |
+
private function copyBig($src, $dst, $buffersize) {
|
244 |
+
$src = fopen($src, 'r');
|
245 |
+
$dest = fopen($dst, 'w');
|
246 |
+
|
247 |
+
// Try first method:
|
248 |
+
while (!feof($src)) {
|
249 |
+
if (false === fwrite($dest, fread($src, $buffersize))) {
|
250 |
+
$error = true;
|
251 |
+
}
|
252 |
+
}
|
253 |
+
// Try second method if first one failed
|
254 |
+
if (isset($error) && ($error === true)) {
|
255 |
+
while (!feof($src)) {
|
256 |
+
if (false === stream_copy_to_stream($src, $dest, 1024)) {
|
257 |
+
$this->log("Can not copy big file; {$src} -> {$dest}");
|
258 |
+
fclose($src);
|
259 |
+
fclose($dest);
|
260 |
+
return false;
|
261 |
+
}
|
262 |
+
}
|
263 |
+
}
|
264 |
+
// Close any open handler
|
265 |
+
fclose($src);
|
266 |
+
fclose($dest);
|
267 |
+
return true;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Check if file is excluded from copying process
|
272 |
+
*
|
273 |
+
* @param string $file filename including ending
|
274 |
+
* @return boolean
|
275 |
+
*/
|
276 |
+
private function isFileExcluded($file) {
|
277 |
+
$excluded = false;
|
278 |
+
foreach ($this->options->excludedFiles as $excludedFile) {
|
279 |
+
if (stripos(strrev($file), strrev($excludedFile)) === 0) {
|
280 |
+
$excluded = true;
|
281 |
+
break;
|
282 |
+
}
|
283 |
+
}
|
284 |
+
|
285 |
+
// Do not copy wp-config.php if the clone gets updated. This is for security purposes,
|
286 |
+
// because if the updating process fails, the staging site would not be accessable any longer
|
287 |
+
if (isset($this->options->mainJob ) && $this->options->mainJob == "updating" && stripos(strrev($file), strrev("wp-config.php")) === 0){
|
288 |
+
$excluded = true;
|
289 |
+
}
|
290 |
+
|
291 |
+
|
292 |
+
return $excluded;
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Check if directory is excluded from copying
|
297 |
+
* @param string $directory
|
298 |
+
* @return bool
|
299 |
+
*/
|
300 |
+
private function isDirectoryExcluded($directory) {
|
301 |
+
// Make sure that wp-staging-pro directory / plugin is never excluded
|
302 |
+
if (false !== strpos( $directory, 'wp-staging' ) || false !== strpos( $directory, 'wp-staging-pro' ) ){
|
303 |
+
return false;
|
304 |
+
}
|
305 |
+
|
306 |
+
foreach ($this->options->excludedDirectories as $excludedDirectory) {
|
307 |
+
if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
|
308 |
+
return true;
|
309 |
+
}
|
310 |
+
}
|
311 |
+
|
312 |
+
return false;
|
313 |
+
}
|
314 |
+
|
315 |
+
/**
|
316 |
+
* Check if directory is an extra directory and should be copied
|
317 |
+
* @param string $directory
|
318 |
+
* @return boolean
|
319 |
+
*/
|
320 |
+
private function isExtraDirectory($directory) {
|
321 |
+
foreach ($this->options->extraDirectories as $extraDirectory) {
|
322 |
+
if (strpos($directory, $extraDirectory) === 0) {
|
323 |
+
return true;
|
324 |
+
}
|
325 |
+
}
|
326 |
+
|
327 |
+
return false;
|
328 |
+
}
|
329 |
+
}
|
apps/Backend/Modules/Jobs/Job.php
CHANGED
@@ -1,466 +1,466 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
|
11 |
-
use WPStaging\Utils\Logger;
|
12 |
-
use WPStaging\WPStaging;
|
13 |
-
use WPStaging\Utils\Cache;
|
14 |
-
use WPStaging\Utils\Multisite;
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Class Job
|
18 |
-
* @package WPStaging\Backend\Modules\Jobs
|
19 |
-
*/
|
20 |
-
abstract class Job implements JobInterface {
|
21 |
-
|
22 |
-
const EXECUTION_TIME_RATIO = 0.8;
|
23 |
-
const MAX_MEMORY_RATIO = 0.8;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* @var Cache
|
27 |
-
*/
|
28 |
-
protected $cache;
|
29 |
-
|
30 |
-
/**
|
31 |
-
* @var Logger
|
32 |
-
*/
|
33 |
-
protected $logger;
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @var bool
|
37 |
-
*/
|
38 |
-
protected $hasLoggedFileNameSet = false;
|
39 |
-
|
40 |
-
/**
|
41 |
-
* @var object
|
42 |
-
*/
|
43 |
-
protected $options;
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @var object
|
47 |
-
*/
|
48 |
-
protected $settings;
|
49 |
-
|
50 |
-
/**
|
51 |
-
* System total maximum memory consumption
|
52 |
-
* @var int
|
53 |
-
*/
|
54 |
-
protected $maxMemoryLimit;
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Script maximum memory consumption
|
58 |
-
* @var int
|
59 |
-
*/
|
60 |
-
protected $memoryLimit;
|
61 |
-
|
62 |
-
/**
|
63 |
-
* @var int
|
64 |
-
*/
|
65 |
-
protected $maxExecutionTime;
|
66 |
-
|
67 |
-
/**
|
68 |
-
* @var int
|
69 |
-
*/
|
70 |
-
protected $executionLimit;
|
71 |
-
|
72 |
-
/**
|
73 |
-
* @var int
|
74 |
-
*/
|
75 |
-
protected $totalRecursion;
|
76 |
-
|
77 |
-
/**
|
78 |
-
* @var int
|
79 |
-
*/
|
80 |
-
protected $maxRecursionLimit;
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Multisite Home Url
|
84 |
-
* @var string
|
85 |
-
*/
|
86 |
-
protected $multisiteHomeUrl;
|
87 |
-
|
88 |
-
/**
|
89 |
-
* @var int
|
90 |
-
*/
|
91 |
-
protected $start;
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Job constructor.
|
95 |
-
*/
|
96 |
-
public function __construct() {
|
97 |
-
// Get max limits
|
98 |
-
$this->start = $this->time();
|
99 |
-
$this->maxMemoryLimit = $this->getMemoryInBytes( @ini_get( "memory_limit" ) );
|
100 |
-
|
101 |
-
$multisite = new Multisite;
|
102 |
-
$this->multisiteHomeUrl = $multisite->getHomeURL();
|
103 |
-
|
104 |
-
//$this->maxExecutionTime = (int) ini_get("max_execution_time");
|
105 |
-
$this->maxExecutionTime = ( int ) 30;
|
106 |
-
|
107 |
-
// if ($this->maxExecutionTime < 1 || $this->maxExecutionTime > 30)
|
108 |
-
// {
|
109 |
-
// $this->maxExecutionTime = 30;
|
110 |
-
// }
|
111 |
-
// Services
|
112 |
-
$this->cache = new Cache( -1, \WPStaging\WPStaging::getContentDir() );
|
113 |
-
$this->logger = WPStaging::getInstance()->get( "logger" );
|
114 |
-
|
115 |
-
// Settings and Options
|
116 |
-
$this->options = $this->cache->get( "clone_options" );
|
117 |
-
|
118 |
-
$this->settings = ( object ) get_option( "wpstg_settings", array() );
|
119 |
-
|
120 |
-
if( !$this->options ) {
|
121 |
-
$this->options = new \stdClass();
|
122 |
-
}
|
123 |
-
|
124 |
-
if( isset( $this->options->existingClones ) && is_object( $this->options->existingClones ) ) {
|
125 |
-
$this->options->existingClones = json_decode( json_encode( $this->options->existingClones ), true );
|
126 |
-
}
|
127 |
-
|
128 |
-
// check default options
|
129 |
-
if( !isset( $this->settings ) ||
|
130 |
-
!isset( $this->settings->queryLimit ) ||
|
131 |
-
!isset( $this->settings->querySRLimit ) ||
|
132 |
-
!isset( $this->settings->batchSize ) ||
|
133 |
-
!isset( $this->settings->cpuLoad ) ||
|
134 |
-
!isset( $this->settings->maxFileSize ) ||
|
135 |
-
!isset( $this->settings->fileLimit )
|
136 |
-
) {
|
137 |
-
$this->settings = new \stdClass();
|
138 |
-
$this->setDefaultSettings();
|
139 |
-
}
|
140 |
-
|
141 |
-
// Set limits accordingly to CPU LIMITS
|
142 |
-
$this->setLimits();
|
143 |
-
|
144 |
-
$this->maxRecursionLimit = ( int ) ini_get( "xdebug.max_nesting_level" );
|
145 |
-
|
146 |
-
/*
|
147 |
-
* This is needed to make sure that maxRecursionLimit = -1
|
148 |
-
* if xdebug is not used in production env.
|
149 |
-
* For using xdebug, maxRecursionLimit must be larger
|
150 |
-
* otherwise xdebug is throwing an error 500 while debugging
|
151 |
-
*/
|
152 |
-
if( $this->maxRecursionLimit < 1 ) {
|
153 |
-
$this->maxRecursionLimit = -1;
|
154 |
-
} else {
|
155 |
-
$this->maxRecursionLimit = $this->maxRecursionLimit - 50; // just to make sure
|
156 |
-
}
|
157 |
-
|
158 |
-
if( method_exists( $this, "initialize" ) ) {
|
159 |
-
$this->initialize();
|
160 |
-
}
|
161 |
-
}
|
162 |
-
|
163 |
-
/**
|
164 |
-
* Job destructor
|
165 |
-
*/
|
166 |
-
public function __destruct() {
|
167 |
-
// Commit logs
|
168 |
-
$this->logger->commit();
|
169 |
-
}
|
170 |
-
|
171 |
-
/**
|
172 |
-
* Set default settings
|
173 |
-
*/
|
174 |
-
protected function setDefaultSettings() {
|
175 |
-
$this->settings->queryLimit = "20000";
|
176 |
-
$this->settings->querySRLimit = "5000";
|
177 |
-
$this->settings->fileLimit = "1";
|
178 |
-
$this->settings->batchSize = "2";
|
179 |
-
$this->settings->cpuLoad = 'medium';
|
180 |
-
$this->settings->maxFileSize = 8;
|
181 |
-
update_option( 'wpstg_settings', $this->settings );
|
182 |
-
}
|
183 |
-
|
184 |
-
/**
|
185 |
-
* Set limits accordingly to
|
186 |
-
*/
|
187 |
-
protected function setLimits() {
|
188 |
-
|
189 |
-
if( !isset( $this->settings->cpuLoad ) ) {
|
190 |
-
$this->settings->cpuLoad = "medium";
|
191 |
-
}
|
192 |
-
|
193 |
-
$memoryLimit = self::MAX_MEMORY_RATIO;
|
194 |
-
$timeLimit = self::EXECUTION_TIME_RATIO;
|
195 |
-
|
196 |
-
switch ( $this->settings->cpuLoad ) {
|
197 |
-
case "medium":
|
198 |
-
//$memoryLimit= $memoryLimit / 2; // 0.4
|
199 |
-
$timeLimit = $timeLimit / 2;
|
200 |
-
break;
|
201 |
-
case "low":
|
202 |
-
//$memoryLimit= $memoryLimit / 4; // 0.2
|
203 |
-
$timeLimit = $timeLimit / 4;
|
204 |
-
break;
|
205 |
-
|
206 |
-
case "fast": // 0.8
|
207 |
-
default:
|
208 |
-
break;
|
209 |
-
}
|
210 |
-
|
211 |
-
$this->memoryLimit = $this->maxMemoryLimit * $memoryLimit;
|
212 |
-
$this->executionLimit = $this->maxExecutionTime * $timeLimit;
|
213 |
-
}
|
214 |
-
|
215 |
-
/**
|
216 |
-
* Save options
|
217 |
-
* @param null|array|object $options
|
218 |
-
* @return bool
|
219 |
-
*/
|
220 |
-
protected function saveOptions( $options = null ) {
|
221 |
-
// Get default options
|
222 |
-
if( null === $options ) {
|
223 |
-
$options = $this->options;
|
224 |
-
}
|
225 |
-
|
226 |
-
// Ensure that it is an object
|
227 |
-
$options = json_decode( json_encode( $options ) );
|
228 |
-
return $this->cache->save( "clone_options", $options );
|
229 |
-
}
|
230 |
-
|
231 |
-
/**
|
232 |
-
* @return object
|
233 |
-
*/
|
234 |
-
public function getOptions() {
|
235 |
-
return $this->options;
|
236 |
-
}
|
237 |
-
|
238 |
-
/**
|
239 |
-
* @param string $memory
|
240 |
-
* @return int
|
241 |
-
*/
|
242 |
-
protected function getMemoryInBytes( $memory ) {
|
243 |
-
// Handle unlimited ones
|
244 |
-
if( 1 > ( int ) $memory ) {
|
245 |
-
//return (int) $memory;
|
246 |
-
// 128 MB default value
|
247 |
-
return ( int ) 134217728;
|
248 |
-
}
|
249 |
-
|
250 |
-
$bytes = ( int ) $memory; // grab only the number
|
251 |
-
$size = trim( str_replace( $bytes, null, strtolower( $memory ) ) ); // strip away number and lower-case it
|
252 |
-
// Actual calculation
|
253 |
-
switch ( $size ) {
|
254 |
-
case 'k':
|
255 |
-
$bytes *= 1024;
|
256 |
-
break;
|
257 |
-
case 'm':
|
258 |
-
$bytes *= (1024 * 1024);
|
259 |
-
break;
|
260 |
-
case 'g':
|
261 |
-
$bytes *= (1024 * 1024 * 1024);
|
262 |
-
break;
|
263 |
-
}
|
264 |
-
|
265 |
-
return $bytes;
|
266 |
-
}
|
267 |
-
|
268 |
-
/**
|
269 |
-
* Format bytes into ini_set favorable form
|
270 |
-
* @param int $bytes
|
271 |
-
* @return string
|
272 |
-
*/
|
273 |
-
protected function formatBytes( $bytes ) {
|
274 |
-
if( ( int ) $bytes < 1 ) {
|
275 |
-
return '';
|
276 |
-
}
|
277 |
-
|
278 |
-
$units = array('B', 'K', 'M', 'G'); // G since PHP 5.1.x so we are good!
|
279 |
-
|
280 |
-
$bytes = ( int ) $bytes;
|
281 |
-
$base = log( $bytes ) / log( 1000 );
|
282 |
-
$pow = pow( 1000, $base - floor( $base ) );
|
283 |
-
|
284 |
-
return round( $pow, 0 ) . $units[( int ) floor( $base )];
|
285 |
-
}
|
286 |
-
|
287 |
-
/**
|
288 |
-
* Get current time in seconds
|
289 |
-
* @return float
|
290 |
-
*/
|
291 |
-
protected function time() {
|
292 |
-
$time = microtime();
|
293 |
-
$time = explode( ' ', $time );
|
294 |
-
$time = $time[1] + $time[0];
|
295 |
-
return $time;
|
296 |
-
}
|
297 |
-
|
298 |
-
/**
|
299 |
-
* @return bool
|
300 |
-
*/
|
301 |
-
protected function isOverThreshold() {
|
302 |
-
// Check if the memory is over threshold
|
303 |
-
$usedMemory = ( int ) @memory_get_usage( true );
|
304 |
-
|
305 |
-
$this->debugLog( 'Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Max Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script Memory Limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_DEBUG );
|
306 |
-
|
307 |
-
if( $usedMemory >= $this->memoryLimit ) {
|
308 |
-
$this->log( 'Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script memory limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_ERROR );
|
309 |
-
//$this->resetMemory();
|
310 |
-
return true;
|
311 |
-
}
|
312 |
-
|
313 |
-
if( $this->isRecursionLimit() ) {
|
314 |
-
//$this->log('RESET RECURSION');
|
315 |
-
return true;
|
316 |
-
}
|
317 |
-
|
318 |
-
// Check if execution time is over threshold
|
319 |
-
///$time = round($this->start + $this->time(), 4);
|
320 |
-
$time = round( $this->time() - $this->start, 4 );
|
321 |
-
|
322 |
-
if( $time >= $this->executionLimit ) {
|
323 |
-
$this->debugLog( 'RESET TIME: current time: ' . $time . ', Start Time: ' . $this->start . ', exec time limit: ' . $this->executionLimit );
|
324 |
-
return true;
|
325 |
-
}
|
326 |
-
|
327 |
-
return false;
|
328 |
-
}
|
329 |
-
|
330 |
-
/**
|
331 |
-
* Attempt to reset memory
|
332 |
-
* @return bool
|
333 |
-
* memory
|
334 |
-
*/
|
335 |
-
// protected function resetMemory()
|
336 |
-
// {
|
337 |
-
// $newMemoryLimit = $this->maxMemoryLimit * 2;
|
338 |
-
//
|
339 |
-
// // Failed to set
|
340 |
-
// if (false === ini_set("memory_limit", $this->formatBytes($newMemoryLimit)))
|
341 |
-
// {
|
342 |
-
// $this->log('Can not free some memory', Logger::TYPE_CRITICAL);
|
343 |
-
// return false;
|
344 |
-
// }
|
345 |
-
//
|
346 |
-
// // Double checking
|
347 |
-
// $newMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
|
348 |
-
// if ($newMemoryLimit <= $this->maxMemoryLimit)
|
349 |
-
// {
|
350 |
-
// return false;
|
351 |
-
// }
|
352 |
-
//
|
353 |
-
// // Set the new Maximum memory limit
|
354 |
-
// $this->maxMemoryLimit = $newMemoryLimit;
|
355 |
-
//
|
356 |
-
// // Calculate threshold limit
|
357 |
-
// $this->memoryLimit = $newMemoryLimit * self::MAX_MEMORY_RATIO;
|
358 |
-
//
|
359 |
-
// return true;
|
360 |
-
// }
|
361 |
-
|
362 |
-
/**
|
363 |
-
* Attempt to reset time
|
364 |
-
* @return bool
|
365 |
-
*
|
366 |
-
* @deprecated since version 2.0.0
|
367 |
-
|
368 |
-
*/
|
369 |
-
// protected function resetTime()
|
370 |
-
// {
|
371 |
-
// // Attempt to reset timeout
|
372 |
-
// if (!@set_time_limit($this->maxExecutionTime))
|
373 |
-
// {
|
374 |
-
// return false;
|
375 |
-
// }
|
376 |
-
//
|
377 |
-
// // Increase execution limit
|
378 |
-
// $this->executionLimit = $this->executionLimit * 2;
|
379 |
-
//
|
380 |
-
// return true;
|
381 |
-
// }
|
382 |
-
|
383 |
-
/**
|
384 |
-
* Reset time limit and memory
|
385 |
-
* @return bool
|
386 |
-
*
|
387 |
-
* @deprecated since version 2.0.0
|
388 |
-
*/
|
389 |
-
// protected function reset()
|
390 |
-
// {
|
391 |
-
// // Attempt to reset time
|
392 |
-
// if (!$this->resetTime())
|
393 |
-
// {
|
394 |
-
// return false;
|
395 |
-
// }
|
396 |
-
//
|
397 |
-
// // Attempt to reset memory
|
398 |
-
// if (!$this->resetMemory())
|
399 |
-
// {
|
400 |
-
// return false;
|
401 |
-
// }
|
402 |
-
//
|
403 |
-
// return true;
|
404 |
-
// }
|
405 |
-
|
406 |
-
/**
|
407 |
-
* Checks if calls are over recursion limit
|
408 |
-
* @return bool
|
409 |
-
*/
|
410 |
-
protected function isRecursionLimit() {
|
411 |
-
return ($this->maxRecursionLimit > 0 && $this->totalRecursion >= $this->maxRecursionLimit);
|
412 |
-
}
|
413 |
-
|
414 |
-
/**
|
415 |
-
* @param string $msg
|
416 |
-
* @param string $type
|
417 |
-
*/
|
418 |
-
protected function log( $msg, $type = Logger::TYPE_INFO ) {
|
419 |
-
|
420 |
-
if( !isset( $this->options->clone ) ) {
|
421 |
-
$this->options->clone = date( DATE_ATOM, mktime( 0, 0, 0, 7, 1, 2000 ) );
|
422 |
-
}
|
423 |
-
|
424 |
-
if( false === $this->hasLoggedFileNameSet && 0 < strlen( $this->options->clone ) ) {
|
425 |
-
$this->logger->setFileName( $this->options->clone );
|
426 |
-
$this->hasLoggedFileNameSet = true;
|
427 |
-
}
|
428 |
-
|
429 |
-
$this->logger->add( $msg, $type );
|
430 |
-
}
|
431 |
-
|
432 |
-
/**
|
433 |
-
* @param string $msg
|
434 |
-
* @param string $type
|
435 |
-
*/
|
436 |
-
protected function debugLog( $msg, $type = Logger::TYPE_INFO ) {
|
437 |
-
|
438 |
-
if( !isset( $this->options->clone ) ) {
|
439 |
-
$this->options->clone = date( DATE_ATOM, mktime( 0, 0, 0, 7, 1, 2000 ) );
|
440 |
-
}
|
441 |
-
|
442 |
-
if( false === $this->hasLoggedFileNameSet && 0 < strlen( $this->options->clone ) ) {
|
443 |
-
$this->logger->setFileName( $this->options->clone );
|
444 |
-
$this->hasLoggedFileNameSet = true;
|
445 |
-
}
|
446 |
-
|
447 |
-
|
448 |
-
if( isset( $this->settings->debugMode ) ) {
|
449 |
-
$this->logger->add( $msg, $type );
|
450 |
-
}
|
451 |
-
}
|
452 |
-
|
453 |
-
/**
|
454 |
-
* Throw a errror message via json and stop further execution
|
455 |
-
* @param string $message
|
456 |
-
*/
|
457 |
-
protected function returnException( $message = '' ) {
|
458 |
-
wp_die( json_encode( array(
|
459 |
-
'job' => isset( $this->options->currentJob ) ? $this->options->currentJob : '',
|
460 |
-
'status' => false,
|
461 |
-
'message' => $message,
|
462 |
-
'error' => true
|
463 |
-
) ) );
|
464 |
-
}
|
465 |
-
|
466 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
|
11 |
+
use WPStaging\Utils\Logger;
|
12 |
+
use WPStaging\WPStaging;
|
13 |
+
use WPStaging\Utils\Cache;
|
14 |
+
use WPStaging\Utils\Multisite;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Class Job
|
18 |
+
* @package WPStaging\Backend\Modules\Jobs
|
19 |
+
*/
|
20 |
+
abstract class Job implements JobInterface {
|
21 |
+
|
22 |
+
const EXECUTION_TIME_RATIO = 0.8;
|
23 |
+
const MAX_MEMORY_RATIO = 0.8;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var Cache
|
27 |
+
*/
|
28 |
+
protected $cache;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @var Logger
|
32 |
+
*/
|
33 |
+
protected $logger;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var bool
|
37 |
+
*/
|
38 |
+
protected $hasLoggedFileNameSet = false;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var object
|
42 |
+
*/
|
43 |
+
protected $options;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var object
|
47 |
+
*/
|
48 |
+
protected $settings;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* System total maximum memory consumption
|
52 |
+
* @var int
|
53 |
+
*/
|
54 |
+
protected $maxMemoryLimit;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Script maximum memory consumption
|
58 |
+
* @var int
|
59 |
+
*/
|
60 |
+
protected $memoryLimit;
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @var int
|
64 |
+
*/
|
65 |
+
protected $maxExecutionTime;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @var int
|
69 |
+
*/
|
70 |
+
protected $executionLimit;
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @var int
|
74 |
+
*/
|
75 |
+
protected $totalRecursion;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* @var int
|
79 |
+
*/
|
80 |
+
protected $maxRecursionLimit;
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Multisite Home Url
|
84 |
+
* @var string
|
85 |
+
*/
|
86 |
+
protected $multisiteHomeUrl;
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @var int
|
90 |
+
*/
|
91 |
+
protected $start;
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Job constructor.
|
95 |
+
*/
|
96 |
+
public function __construct() {
|
97 |
+
// Get max limits
|
98 |
+
$this->start = $this->time();
|
99 |
+
$this->maxMemoryLimit = $this->getMemoryInBytes( @ini_get( "memory_limit" ) );
|
100 |
+
|
101 |
+
$multisite = new Multisite;
|
102 |
+
$this->multisiteHomeUrl = $multisite->getHomeURL();
|
103 |
+
|
104 |
+
//$this->maxExecutionTime = (int) ini_get("max_execution_time");
|
105 |
+
$this->maxExecutionTime = ( int ) 30;
|
106 |
+
|
107 |
+
// if ($this->maxExecutionTime < 1 || $this->maxExecutionTime > 30)
|
108 |
+
// {
|
109 |
+
// $this->maxExecutionTime = 30;
|
110 |
+
// }
|
111 |
+
// Services
|
112 |
+
$this->cache = new Cache( -1, \WPStaging\WPStaging::getContentDir() );
|
113 |
+
$this->logger = WPStaging::getInstance()->get( "logger" );
|
114 |
+
|
115 |
+
// Settings and Options
|
116 |
+
$this->options = $this->cache->get( "clone_options" );
|
117 |
+
|
118 |
+
$this->settings = ( object ) get_option( "wpstg_settings", array() );
|
119 |
+
|
120 |
+
if( !$this->options ) {
|
121 |
+
$this->options = new \stdClass();
|
122 |
+
}
|
123 |
+
|
124 |
+
if( isset( $this->options->existingClones ) && is_object( $this->options->existingClones ) ) {
|
125 |
+
$this->options->existingClones = json_decode( json_encode( $this->options->existingClones ), true );
|
126 |
+
}
|
127 |
+
|
128 |
+
// check default options
|
129 |
+
if( !isset( $this->settings ) ||
|
130 |
+
!isset( $this->settings->queryLimit ) ||
|
131 |
+
!isset( $this->settings->querySRLimit ) ||
|
132 |
+
!isset( $this->settings->batchSize ) ||
|
133 |
+
!isset( $this->settings->cpuLoad ) ||
|
134 |
+
!isset( $this->settings->maxFileSize ) ||
|
135 |
+
!isset( $this->settings->fileLimit )
|
136 |
+
) {
|
137 |
+
$this->settings = new \stdClass();
|
138 |
+
$this->setDefaultSettings();
|
139 |
+
}
|
140 |
+
|
141 |
+
// Set limits accordingly to CPU LIMITS
|
142 |
+
$this->setLimits();
|
143 |
+
|
144 |
+
$this->maxRecursionLimit = ( int ) ini_get( "xdebug.max_nesting_level" );
|
145 |
+
|
146 |
+
/*
|
147 |
+
* This is needed to make sure that maxRecursionLimit = -1
|
148 |
+
* if xdebug is not used in production env.
|
149 |
+
* For using xdebug, maxRecursionLimit must be larger
|
150 |
+
* otherwise xdebug is throwing an error 500 while debugging
|
151 |
+
*/
|
152 |
+
if( $this->maxRecursionLimit < 1 ) {
|
153 |
+
$this->maxRecursionLimit = -1;
|
154 |
+
} else {
|
155 |
+
$this->maxRecursionLimit = $this->maxRecursionLimit - 50; // just to make sure
|
156 |
+
}
|
157 |
+
|
158 |
+
if( method_exists( $this, "initialize" ) ) {
|
159 |
+
$this->initialize();
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Job destructor
|
165 |
+
*/
|
166 |
+
public function __destruct() {
|
167 |
+
// Commit logs
|
168 |
+
$this->logger->commit();
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Set default settings
|
173 |
+
*/
|
174 |
+
protected function setDefaultSettings() {
|
175 |
+
$this->settings->queryLimit = "20000";
|
176 |
+
$this->settings->querySRLimit = "5000";
|
177 |
+
$this->settings->fileLimit = "1";
|
178 |
+
$this->settings->batchSize = "2";
|
179 |
+
$this->settings->cpuLoad = 'medium';
|
180 |
+
$this->settings->maxFileSize = 8;
|
181 |
+
update_option( 'wpstg_settings', $this->settings );
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Set limits accordingly to
|
186 |
+
*/
|
187 |
+
protected function setLimits() {
|
188 |
+
|
189 |
+
if( !isset( $this->settings->cpuLoad ) ) {
|
190 |
+
$this->settings->cpuLoad = "medium";
|
191 |
+
}
|
192 |
+
|
193 |
+
$memoryLimit = self::MAX_MEMORY_RATIO;
|
194 |
+
$timeLimit = self::EXECUTION_TIME_RATIO;
|
195 |
+
|
196 |
+
switch ( $this->settings->cpuLoad ) {
|
197 |
+
case "medium":
|
198 |
+
//$memoryLimit= $memoryLimit / 2; // 0.4
|
199 |
+
$timeLimit = $timeLimit / 2;
|
200 |
+
break;
|
201 |
+
case "low":
|
202 |
+
//$memoryLimit= $memoryLimit / 4; // 0.2
|
203 |
+
$timeLimit = $timeLimit / 4;
|
204 |
+
break;
|
205 |
+
|
206 |
+
case "fast": // 0.8
|
207 |
+
default:
|
208 |
+
break;
|
209 |
+
}
|
210 |
+
|
211 |
+
$this->memoryLimit = $this->maxMemoryLimit * $memoryLimit;
|
212 |
+
$this->executionLimit = $this->maxExecutionTime * $timeLimit;
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* Save options
|
217 |
+
* @param null|array|object $options
|
218 |
+
* @return bool
|
219 |
+
*/
|
220 |
+
protected function saveOptions( $options = null ) {
|
221 |
+
// Get default options
|
222 |
+
if( null === $options ) {
|
223 |
+
$options = $this->options;
|
224 |
+
}
|
225 |
+
|
226 |
+
// Ensure that it is an object
|
227 |
+
$options = json_decode( json_encode( $options ) );
|
228 |
+
return $this->cache->save( "clone_options", $options );
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* @return object
|
233 |
+
*/
|
234 |
+
public function getOptions() {
|
235 |
+
return $this->options;
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* @param string $memory
|
240 |
+
* @return int
|
241 |
+
*/
|
242 |
+
protected function getMemoryInBytes( $memory ) {
|
243 |
+
// Handle unlimited ones
|
244 |
+
if( 1 > ( int ) $memory ) {
|
245 |
+
//return (int) $memory;
|
246 |
+
// 128 MB default value
|
247 |
+
return ( int ) 134217728;
|
248 |
+
}
|
249 |
+
|
250 |
+
$bytes = ( int ) $memory; // grab only the number
|
251 |
+
$size = trim( str_replace( $bytes, null, strtolower( $memory ) ) ); // strip away number and lower-case it
|
252 |
+
// Actual calculation
|
253 |
+
switch ( $size ) {
|
254 |
+
case 'k':
|
255 |
+
$bytes *= 1024;
|
256 |
+
break;
|
257 |
+
case 'm':
|
258 |
+
$bytes *= (1024 * 1024);
|
259 |
+
break;
|
260 |
+
case 'g':
|
261 |
+
$bytes *= (1024 * 1024 * 1024);
|
262 |
+
break;
|
263 |
+
}
|
264 |
+
|
265 |
+
return $bytes;
|
266 |
+
}
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Format bytes into ini_set favorable form
|
270 |
+
* @param int $bytes
|
271 |
+
* @return string
|
272 |
+
*/
|
273 |
+
protected function formatBytes( $bytes ) {
|
274 |
+
if( ( int ) $bytes < 1 ) {
|
275 |
+
return '';
|
276 |
+
}
|
277 |
+
|
278 |
+
$units = array('B', 'K', 'M', 'G'); // G since PHP 5.1.x so we are good!
|
279 |
+
|
280 |
+
$bytes = ( int ) $bytes;
|
281 |
+
$base = log( $bytes ) / log( 1000 );
|
282 |
+
$pow = pow( 1000, $base - floor( $base ) );
|
283 |
+
|
284 |
+
return round( $pow, 0 ) . $units[( int ) floor( $base )];
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Get current time in seconds
|
289 |
+
* @return float
|
290 |
+
*/
|
291 |
+
protected function time() {
|
292 |
+
$time = microtime();
|
293 |
+
$time = explode( ' ', $time );
|
294 |
+
$time = $time[1] + $time[0];
|
295 |
+
return $time;
|
296 |
+
}
|
297 |
+
|
298 |
+
/**
|
299 |
+
* @return bool
|
300 |
+
*/
|
301 |
+
protected function isOverThreshold() {
|
302 |
+
// Check if the memory is over threshold
|
303 |
+
$usedMemory = ( int ) @memory_get_usage( true );
|
304 |
+
|
305 |
+
$this->debugLog( 'Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Max Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script Memory Limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_DEBUG );
|
306 |
+
|
307 |
+
if( $usedMemory >= $this->memoryLimit ) {
|
308 |
+
$this->log( 'Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script memory limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_ERROR );
|
309 |
+
//$this->resetMemory();
|
310 |
+
return true;
|
311 |
+
}
|
312 |
+
|
313 |
+
if( $this->isRecursionLimit() ) {
|
314 |
+
//$this->log('RESET RECURSION');
|
315 |
+
return true;
|
316 |
+
}
|
317 |
+
|
318 |
+
// Check if execution time is over threshold
|
319 |
+
///$time = round($this->start + $this->time(), 4);
|
320 |
+
$time = round( $this->time() - $this->start, 4 );
|
321 |
+
|
322 |
+
if( $time >= $this->executionLimit ) {
|
323 |
+
$this->debugLog( 'RESET TIME: current time: ' . $time . ', Start Time: ' . $this->start . ', exec time limit: ' . $this->executionLimit );
|
324 |
+
return true;
|
325 |
+
}
|
326 |
+
|
327 |
+
return false;
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Attempt to reset memory
|
332 |
+
* @return bool
|
333 |
+
* memory
|
334 |
+
*/
|
335 |
+
// protected function resetMemory()
|
336 |
+
// {
|
337 |
+
// $newMemoryLimit = $this->maxMemoryLimit * 2;
|
338 |
+
//
|
339 |
+
// // Failed to set
|
340 |
+
// if (false === ini_set("memory_limit", $this->formatBytes($newMemoryLimit)))
|
341 |
+
// {
|
342 |
+
// $this->log('Can not free some memory', Logger::TYPE_CRITICAL);
|
343 |
+
// return false;
|
344 |
+
// }
|
345 |
+
//
|
346 |
+
// // Double checking
|
347 |
+
// $newMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
|
348 |
+
// if ($newMemoryLimit <= $this->maxMemoryLimit)
|
349 |
+
// {
|
350 |
+
// return false;
|
351 |
+
// }
|
352 |
+
//
|
353 |
+
// // Set the new Maximum memory limit
|
354 |
+
// $this->maxMemoryLimit = $newMemoryLimit;
|
355 |
+
//
|
356 |
+
// // Calculate threshold limit
|
357 |
+
// $this->memoryLimit = $newMemoryLimit * self::MAX_MEMORY_RATIO;
|
358 |
+
//
|
359 |
+
// return true;
|
360 |
+
// }
|
361 |
+
|
362 |
+
/**
|
363 |
+
* Attempt to reset time
|
364 |
+
* @return bool
|
365 |
+
*
|
366 |
+
* @deprecated since version 2.0.0
|
367 |
+
|
368 |
+
*/
|
369 |
+
// protected function resetTime()
|
370 |
+
// {
|
371 |
+
// // Attempt to reset timeout
|
372 |
+
// if (!@set_time_limit($this->maxExecutionTime))
|
373 |
+
// {
|
374 |
+
// return false;
|
375 |
+
// }
|
376 |
+
//
|
377 |
+
// // Increase execution limit
|
378 |
+
// $this->executionLimit = $this->executionLimit * 2;
|
379 |
+
//
|
380 |
+
// return true;
|
381 |
+
// }
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Reset time limit and memory
|
385 |
+
* @return bool
|
386 |
+
*
|
387 |
+
* @deprecated since version 2.0.0
|
388 |
+
*/
|
389 |
+
// protected function reset()
|
390 |
+
// {
|
391 |
+
// // Attempt to reset time
|
392 |
+
// if (!$this->resetTime())
|
393 |
+
// {
|
394 |
+
// return false;
|
395 |
+
// }
|
396 |
+
//
|
397 |
+
// // Attempt to reset memory
|
398 |
+
// if (!$this->resetMemory())
|
399 |
+
// {
|
400 |
+
// return false;
|
401 |
+
// }
|
402 |
+
//
|
403 |
+
// return true;
|
404 |
+
// }
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Checks if calls are over recursion limit
|
408 |
+
* @return bool
|
409 |
+
*/
|
410 |
+
protected function isRecursionLimit() {
|
411 |
+
return ($this->maxRecursionLimit > 0 && $this->totalRecursion >= $this->maxRecursionLimit);
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* @param string $msg
|
416 |
+
* @param string $type
|
417 |
+
*/
|
418 |
+
protected function log( $msg, $type = Logger::TYPE_INFO ) {
|
419 |
+
|
420 |
+
if( !isset( $this->options->clone ) ) {
|
421 |
+
$this->options->clone = date( DATE_ATOM, mktime( 0, 0, 0, 7, 1, 2000 ) );
|
422 |
+
}
|
423 |
+
|
424 |
+
if( false === $this->hasLoggedFileNameSet && 0 < strlen( $this->options->clone ) ) {
|
425 |
+
$this->logger->setFileName( $this->options->clone );
|
426 |
+
$this->hasLoggedFileNameSet = true;
|
427 |
+
}
|
428 |
+
|
429 |
+
$this->logger->add( $msg, $type );
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* @param string $msg
|
434 |
+
* @param string $type
|
435 |
+
*/
|
436 |
+
protected function debugLog( $msg, $type = Logger::TYPE_INFO ) {
|
437 |
+
|
438 |
+
if( !isset( $this->options->clone ) ) {
|
439 |
+
$this->options->clone = date( DATE_ATOM, mktime( 0, 0, 0, 7, 1, 2000 ) );
|
440 |
+
}
|
441 |
+
|
442 |
+
if( false === $this->hasLoggedFileNameSet && 0 < strlen( $this->options->clone ) ) {
|
443 |
+
$this->logger->setFileName( $this->options->clone );
|
444 |
+
$this->hasLoggedFileNameSet = true;
|
445 |
+
}
|
446 |
+
|
447 |
+
|
448 |
+
if( isset( $this->settings->debugMode ) ) {
|
449 |
+
$this->logger->add( $msg, $type );
|
450 |
+
}
|
451 |
+
}
|
452 |
+
|
453 |
+
/**
|
454 |
+
* Throw a errror message via json and stop further execution
|
455 |
+
* @param string $message
|
456 |
+
*/
|
457 |
+
protected function returnException( $message = '' ) {
|
458 |
+
wp_die( json_encode( array(
|
459 |
+
'job' => isset( $this->options->currentJob ) ? $this->options->currentJob : '',
|
460 |
+
'status' => false,
|
461 |
+
'message' => $message,
|
462 |
+
'error' => true
|
463 |
+
) ) );
|
464 |
+
}
|
465 |
+
|
466 |
+
}
|
apps/Backend/Modules/Jobs/Scan.php
CHANGED
@@ -1,403 +1,403 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\Utils\Directories;
|
11 |
-
use WPStaging\WPStaging;
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Class Scan
|
15 |
-
* @package WPStaging\Backend\Modules\Jobs
|
16 |
-
*/
|
17 |
-
class Scan extends Job {
|
18 |
-
|
19 |
-
/**
|
20 |
-
* @var array
|
21 |
-
*/
|
22 |
-
private $directories = array();
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @var Directories
|
26 |
-
*/
|
27 |
-
private $objDirectories;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Upon class initialization
|
31 |
-
*/
|
32 |
-
protected function initialize() {
|
33 |
-
$this->objDirectories = new Directories();
|
34 |
-
|
35 |
-
// Database Tables
|
36 |
-
$this->getTables();
|
37 |
-
|
38 |
-
// Get directories
|
39 |
-
$this->directories();
|
40 |
-
|
41 |
-
$this->db = WPStaging::getInstance()->get( 'wpdb' );
|
42 |
-
$this->prefix = $this->db->prefix;
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* Start Module
|
47 |
-
* @return $this
|
48 |
-
*/
|
49 |
-
public function start() {
|
50 |
-
// Basic Options
|
51 |
-
$this->options->root = str_replace( array("\\", '/'), DIRECTORY_SEPARATOR, ABSPATH );
|
52 |
-
$this->options->existingClones = get_option( "wpstg_existing_clones_beta", array() );
|
53 |
-
$this->options->current = null;
|
54 |
-
|
55 |
-
if( isset( $_POST["clone"] ) && array_key_exists( $_POST["clone"], $this->options->existingClones ) ) {
|
56 |
-
$this->options->current = $_POST["clone"];
|
57 |
-
}
|
58 |
-
|
59 |
-
// Tables
|
60 |
-
//$this->options->excludedTables = array();
|
61 |
-
$this->options->clonedTables = array();
|
62 |
-
|
63 |
-
// Files
|
64 |
-
$this->options->totalFiles = 0;
|
65 |
-
$this->options->totalFileSize = 0;
|
66 |
-
$this->options->copiedFiles = 0;
|
67 |
-
|
68 |
-
|
69 |
-
// Directories
|
70 |
-
$this->options->includedDirectories = array();
|
71 |
-
$this->options->includedExtraDirectories = array();
|
72 |
-
$this->options->excludedDirectories = array();
|
73 |
-
$this->options->extraDirectories = array();
|
74 |
-
$this->options->directoriesToCopy = array();
|
75 |
-
$this->options->scannedDirectories = array();
|
76 |
-
|
77 |
-
// Job
|
78 |
-
$this->options->currentJob = "database";
|
79 |
-
$this->options->currentStep = 0;
|
80 |
-
$this->options->totalSteps = 0;
|
81 |
-
|
82 |
-
// Define mainJob to differentiate between cloning, updating and pushing
|
83 |
-
$this->options->mainJob = 'cloning';
|
84 |
-
|
85 |
-
// Delete previous cached files
|
86 |
-
$this->cache->delete( "files_to_copy" );
|
87 |
-
$this->cache->delete( "clone_options" );
|
88 |
-
|
89 |
-
// Save options
|
90 |
-
$this->saveOptions();
|
91 |
-
|
92 |
-
return $this;
|
93 |
-
}
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Format bytes into human readable form
|
97 |
-
* @param int $bytes
|
98 |
-
* @param int $precision
|
99 |
-
* @return string
|
100 |
-
*/
|
101 |
-
public function formatSize( $bytes, $precision = 2 ) {
|
102 |
-
if( ( double ) $bytes < 1 ) {
|
103 |
-
return '';
|
104 |
-
}
|
105 |
-
|
106 |
-
$units = array('B', "KB", "MB", "GB", "TB");
|
107 |
-
|
108 |
-
$bytes = ( double ) $bytes;
|
109 |
-
$base = log( $bytes ) / log( 1000 ); // 1024 would be for MiB KiB etc
|
110 |
-
$pow = pow( 1000, $base - floor( $base ) ); // Same rule for 1000
|
111 |
-
|
112 |
-
return round( $pow, $precision ) . ' ' . $units[( int ) floor( $base )];
|
113 |
-
}
|
114 |
-
|
115 |
-
/**
|
116 |
-
* @param null|string $directories
|
117 |
-
* @param bool $forceDisabled
|
118 |
-
* @return string
|
119 |
-
*/
|
120 |
-
public function directoryListing( $directories = null, $forceDisabled = false ) {
|
121 |
-
if( null == $directories ) {
|
122 |
-
$directories = $this->directories;
|
123 |
-
}
|
124 |
-
|
125 |
-
// Sort results
|
126 |
-
uksort( $directories, 'strcasecmp' );
|
127 |
-
|
128 |
-
$output = '';
|
129 |
-
foreach ( $directories as $name => $directory ) {
|
130 |
-
// Not a directory, possibly a symlink, therefore we will skip it
|
131 |
-
if( !is_array( $directory ) ) {
|
132 |
-
continue;
|
133 |
-
}
|
134 |
-
|
135 |
-
// Need to preserve keys so no array_shift()
|
136 |
-
$data = reset( $directory );
|
137 |
-
unset( $directory[key( $directory )] );
|
138 |
-
|
139 |
-
$isChecked = (
|
140 |
-
empty( $this->options->includedDirectories ) ||
|
141 |
-
in_array( $data["path"], $this->options->includedDirectories )
|
142 |
-
);
|
143 |
-
|
144 |
-
//$isDisabled = ($this->options->existingClones && isset($this->options->existingClones[$name]));
|
145 |
-
// Include wp core folders and their sub dirs.
|
146 |
-
// Exclude all other folders (default setting)
|
147 |
-
$isDisabled = ($name !== 'wp-admin' &&
|
148 |
-
$name !== 'wp-includes' &&
|
149 |
-
$name !== 'wp-content') &&
|
150 |
-
false === strpos( strrev( $data["path"] ), strrev( "wp-admin" ) ) &&
|
151 |
-
false === strpos( strrev( $data["path"] ), strrev( "wp-includes" ) ) &&
|
152 |
-
false === strpos( strrev( $data["path"] ), strrev( "wp-content" ) ) ? true : false;
|
153 |
-
|
154 |
-
// Extra class to differentiate between wp core and non core folders
|
155 |
-
$class = !$isDisabled ? 'wpstg-root' : 'wpstg-extra';
|
156 |
-
|
157 |
-
|
158 |
-
$output .= "<div class='wpstg-dir'>";
|
159 |
-
$output .= "<input type='checkbox' class='wpstg-check-dir " . $class . "'";
|
160 |
-
|
161 |
-
if( $isChecked && !$isDisabled && !$forceDisabled )
|
162 |
-
$output .= " checked";
|
163 |
-
//if ($forceDisabled || $isDisabled) $output .= " disabled";
|
164 |
-
|
165 |
-
$output .= " name='selectedDirectories[]' value='{$data["path"]}'>";
|
166 |
-
|
167 |
-
$output .= "<a href='#' class='wpstg-expand-dirs ";
|
168 |
-
if( !$isChecked || $isDisabled )
|
169 |
-
$output .= " disabled";
|
170 |
-
$output .= "'>{$name}";
|
171 |
-
$output .= "</a>";
|
172 |
-
|
173 |
-
$output .= "<span class='wpstg-size-info'>{$this->formatSize( $data["size"] )}</span>";
|
174 |
-
|
175 |
-
if( !empty( $directory ) ) {
|
176 |
-
$output .= "<div class='wpstg-dir wpstg-subdir'>";
|
177 |
-
$output .= $this->directoryListing( $directory, $isDisabled );
|
178 |
-
$output .= "</div>";
|
179 |
-
}
|
180 |
-
|
181 |
-
$output .= "</div>";
|
182 |
-
}
|
183 |
-
|
184 |
-
return $output;
|
185 |
-
}
|
186 |
-
|
187 |
-
/**
|
188 |
-
* Checks if there is enough free disk space to create staging site
|
189 |
-
* Returns null when can't run disk_free_space function one way or another
|
190 |
-
* @return bool|null
|
191 |
-
*/
|
192 |
-
public function hasFreeDiskSpace() {
|
193 |
-
if( !function_exists( "disk_free_space" ) ) {
|
194 |
-
return null;
|
195 |
-
}
|
196 |
-
|
197 |
-
$freeSpace = @disk_free_space( ABSPATH );
|
198 |
-
|
199 |
-
if( false === $freeSpace ) {
|
200 |
-
$data = array(
|
201 |
-
'freespace' => false,
|
202 |
-
'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( ABSPATH ) )
|
203 |
-
);
|
204 |
-
echo json_encode( $data );
|
205 |
-
die();
|
206 |
-
}
|
207 |
-
|
208 |
-
|
209 |
-
$data = array(
|
210 |
-
'freespace' => $this->formatSize( $freeSpace ),
|
211 |
-
'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( ABSPATH ) )
|
212 |
-
);
|
213 |
-
|
214 |
-
echo json_encode( $data );
|
215 |
-
die();
|
216 |
-
}
|
217 |
-
|
218 |
-
/**
|
219 |
-
* Get Database Tables
|
220 |
-
*/
|
221 |
-
protected function getTables() {
|
222 |
-
$wpDB = WPStaging::getInstance()->get( "wpdb" );
|
223 |
-
|
224 |
-
if( strlen( $wpDB->prefix ) > 0 ) {
|
225 |
-
//$prefix = str_replace('_', '', $wpDB->prefix);
|
226 |
-
$sql = "SHOW TABLE STATUS LIKE '{$wpDB->prefix}%'";
|
227 |
-
} else {
|
228 |
-
$sql = "SHOW TABLE STATUS";
|
229 |
-
}
|
230 |
-
|
231 |
-
$tables = $wpDB->get_results( $sql );
|
232 |
-
|
233 |
-
$currentTables = array();
|
234 |
-
|
235 |
-
// Reset excluded Tables than loop through all tables
|
236 |
-
$this->options->excludedTables = array();
|
237 |
-
foreach ( $tables as $table ) {
|
238 |
-
|
239 |
-
// Exclude WP Staging Tables
|
240 |
-
// if (0 === strpos($table->Name, "wpstg"))
|
241 |
-
// {
|
242 |
-
// continue;
|
243 |
-
// }
|
244 |
-
// Create array of unchecked tables
|
245 |
-
if( !empty( $wpDB->prefix ) && 0 !== strpos( $table->Name, $wpDB->prefix ) ) {
|
246 |
-
$this->options->excludedTables[] = $table->Name;
|
247 |
-
}
|
248 |
-
|
249 |
-
|
250 |
-
$currentTables[] = array(
|
251 |
-
"name" => $table->Name,
|
252 |
-
"size" => ($table->Data_length + $table->Index_length)
|
253 |
-
);
|
254 |
-
}
|
255 |
-
|
256 |
-
$this->options->tables = json_decode( json_encode( $currentTables ) );
|
257 |
-
}
|
258 |
-
|
259 |
-
/**
|
260 |
-
* Get directories and main meta data about'em recursively
|
261 |
-
*/
|
262 |
-
protected function directories() {
|
263 |
-
$directories = new \DirectoryIterator( ABSPATH );
|
264 |
-
|
265 |
-
foreach ( $directories as $directory ) {
|
266 |
-
// Not a valid directory
|
267 |
-
if( false === ($path = $this->getPath( $directory )) ) {
|
268 |
-
continue;
|
269 |
-
}
|
270 |
-
|
271 |
-
$this->handleDirectory( $path );
|
272 |
-
|
273 |
-
// Get Sub-directories
|
274 |
-
$this->getSubDirectories( $directory->getRealPath() );
|
275 |
-
}
|
276 |
-
|
277 |
-
// Gather Plugins
|
278 |
-
$this->getSubDirectories( WP_PLUGIN_DIR );
|
279 |
-
|
280 |
-
// Gather Themes
|
281 |
-
$this->getSubDirectories( WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "themes" );
|
282 |
-
|
283 |
-
// Gather Default Uploads Folder
|
284 |
-
//$this->getSubDirectories(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "uploads");
|
285 |
-
// Gather Custom Uploads Folder if there is one
|
286 |
-
$this->getSubDirectories( $this->getUploadDir() );
|
287 |
-
}
|
288 |
-
|
289 |
-
/**
|
290 |
-
* @param string $path
|
291 |
-
*/
|
292 |
-
protected function getSubDirectories( $path ) {
|
293 |
-
$directories = new \DirectoryIterator( $path );
|
294 |
-
|
295 |
-
foreach ( $directories as $directory ) {
|
296 |
-
// Not a valid directory
|
297 |
-
if( false === ($path = $this->getPath( $directory )) ) {
|
298 |
-
continue;
|
299 |
-
}
|
300 |
-
|
301 |
-
$this->handleDirectory( $path );
|
302 |
-
}
|
303 |
-
}
|
304 |
-
|
305 |
-
/**
|
306 |
-
* Get Path from $directory
|
307 |
-
* @param \SplFileInfo $directory
|
308 |
-
* @return string|false
|
309 |
-
*/
|
310 |
-
protected function getPath( $directory ) {
|
311 |
-
|
312 |
-
/*
|
313 |
-
* Do not follow root path like src/web/..
|
314 |
-
* This must be done before \SplFileInfo->isDir() is used!
|
315 |
-
* Prevents open base dir restriction fatal errors
|
316 |
-
*/
|
317 |
-
if( strpos( $directory->getRealPath(), ABSPATH ) !== 0 ) {
|
318 |
-
return false;
|
319 |
-
}
|
320 |
-
$path = str_replace( ABSPATH, null, $directory->getRealPath() );
|
321 |
-
|
322 |
-
// Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
|
323 |
-
if( !$directory->isDir() || strlen( $path ) < 1 ) {
|
324 |
-
return false;
|
325 |
-
}
|
326 |
-
|
327 |
-
return $path;
|
328 |
-
}
|
329 |
-
|
330 |
-
/**
|
331 |
-
* Organizes $this->directories
|
332 |
-
* @param string $path
|
333 |
-
*/
|
334 |
-
protected function handleDirectory( $path ) {
|
335 |
-
|
336 |
-
$directoryArray = explode( DIRECTORY_SEPARATOR, $path );
|
337 |
-
$total = (is_array($directoryArray) || $directoryArray instanceof Countable ) ? count( $directoryArray ) : 0;
|
338 |
-
|
339 |
-
if( $total < 1 ) {
|
340 |
-
return;
|
341 |
-
}
|
342 |
-
|
343 |
-
$total = $total - 1;
|
344 |
-
$currentArray = &$this->directories;
|
345 |
-
|
346 |
-
for ( $i = 0; $i <= $total; $i++ ) {
|
347 |
-
if( !isset( $currentArray[$directoryArray[$i]] ) ) {
|
348 |
-
$currentArray[$directoryArray[$i]] = array();
|
349 |
-
}
|
350 |
-
|
351 |
-
$currentArray = &$currentArray[$directoryArray[$i]];
|
352 |
-
|
353 |
-
// Attach meta data to the end
|
354 |
-
if( $i < $total ) {
|
355 |
-
continue;
|
356 |
-
}
|
357 |
-
|
358 |
-
$fullPath = ABSPATH . $path;
|
359 |
-
$size = $this->getDirectorySize( $fullPath );
|
360 |
-
|
361 |
-
$currentArray["metaData"] = array(
|
362 |
-
"size" => $size,
|
363 |
-
"path" => ABSPATH . $path,
|
364 |
-
);
|
365 |
-
}
|
366 |
-
}
|
367 |
-
|
368 |
-
/**
|
369 |
-
* Gets size of given directory
|
370 |
-
* @param string $path
|
371 |
-
* @return int|null
|
372 |
-
*/
|
373 |
-
protected function getDirectorySize( $path ) {
|
374 |
-
if( !isset( $this->settings->checkDirectorySize ) || '1' !== $this->settings->checkDirectorySize ) {
|
375 |
-
return null;
|
376 |
-
}
|
377 |
-
|
378 |
-
return $this->objDirectories->size( $path );
|
379 |
-
}
|
380 |
-
|
381 |
-
/**
|
382 |
-
* Get total size of a directory including all its subdirectories
|
383 |
-
* @param string $dir
|
384 |
-
* @return int
|
385 |
-
*/
|
386 |
-
function getDirectorySizeInclSubdirs( $dir ) {
|
387 |
-
$size = 0;
|
388 |
-
foreach ( glob( rtrim( $dir, '/' ) . '/*', GLOB_NOSORT ) as $each ) {
|
389 |
-
$size += is_file( $each ) ? filesize( $each ) : $this->getDirectorySizeInclSubdirs( $each );
|
390 |
-
}
|
391 |
-
return $size;
|
392 |
-
}
|
393 |
-
|
394 |
-
/**
|
395 |
-
* Get relative WP uploads path
|
396 |
-
* @return string
|
397 |
-
*/
|
398 |
-
protected function getUploadDir() {
|
399 |
-
$uploads = wp_upload_dir();
|
400 |
-
return $uploads['basedir'];
|
401 |
-
}
|
402 |
-
|
403 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\Utils\Directories;
|
11 |
+
use WPStaging\WPStaging;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class Scan
|
15 |
+
* @package WPStaging\Backend\Modules\Jobs
|
16 |
+
*/
|
17 |
+
class Scan extends Job {
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @var array
|
21 |
+
*/
|
22 |
+
private $directories = array();
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var Directories
|
26 |
+
*/
|
27 |
+
private $objDirectories;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Upon class initialization
|
31 |
+
*/
|
32 |
+
protected function initialize() {
|
33 |
+
$this->objDirectories = new Directories();
|
34 |
+
|
35 |
+
// Database Tables
|
36 |
+
$this->getTables();
|
37 |
+
|
38 |
+
// Get directories
|
39 |
+
$this->directories();
|
40 |
+
|
41 |
+
$this->db = WPStaging::getInstance()->get( 'wpdb' );
|
42 |
+
$this->prefix = $this->db->prefix;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Start Module
|
47 |
+
* @return $this
|
48 |
+
*/
|
49 |
+
public function start() {
|
50 |
+
// Basic Options
|
51 |
+
$this->options->root = str_replace( array("\\", '/'), DIRECTORY_SEPARATOR, ABSPATH );
|
52 |
+
$this->options->existingClones = get_option( "wpstg_existing_clones_beta", array() );
|
53 |
+
$this->options->current = null;
|
54 |
+
|
55 |
+
if( isset( $_POST["clone"] ) && array_key_exists( $_POST["clone"], $this->options->existingClones ) ) {
|
56 |
+
$this->options->current = $_POST["clone"];
|
57 |
+
}
|
58 |
+
|
59 |
+
// Tables
|
60 |
+
//$this->options->excludedTables = array();
|
61 |
+
$this->options->clonedTables = array();
|
62 |
+
|
63 |
+
// Files
|
64 |
+
$this->options->totalFiles = 0;
|
65 |
+
$this->options->totalFileSize = 0;
|
66 |
+
$this->options->copiedFiles = 0;
|
67 |
+
|
68 |
+
|
69 |
+
// Directories
|
70 |
+
$this->options->includedDirectories = array();
|
71 |
+
$this->options->includedExtraDirectories = array();
|
72 |
+
$this->options->excludedDirectories = array();
|
73 |
+
$this->options->extraDirectories = array();
|
74 |
+
$this->options->directoriesToCopy = array();
|
75 |
+
$this->options->scannedDirectories = array();
|
76 |
+
|
77 |
+
// Job
|
78 |
+
$this->options->currentJob = "database";
|
79 |
+
$this->options->currentStep = 0;
|
80 |
+
$this->options->totalSteps = 0;
|
81 |
+
|
82 |
+
// Define mainJob to differentiate between cloning, updating and pushing
|
83 |
+
$this->options->mainJob = 'cloning';
|
84 |
+
|
85 |
+
// Delete previous cached files
|
86 |
+
$this->cache->delete( "files_to_copy" );
|
87 |
+
$this->cache->delete( "clone_options" );
|
88 |
+
|
89 |
+
// Save options
|
90 |
+
$this->saveOptions();
|
91 |
+
|
92 |
+
return $this;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Format bytes into human readable form
|
97 |
+
* @param int $bytes
|
98 |
+
* @param int $precision
|
99 |
+
* @return string
|
100 |
+
*/
|
101 |
+
public function formatSize( $bytes, $precision = 2 ) {
|
102 |
+
if( ( double ) $bytes < 1 ) {
|
103 |
+
return '';
|
104 |
+
}
|
105 |
+
|
106 |
+
$units = array('B', "KB", "MB", "GB", "TB");
|
107 |
+
|
108 |
+
$bytes = ( double ) $bytes;
|
109 |
+
$base = log( $bytes ) / log( 1000 ); // 1024 would be for MiB KiB etc
|
110 |
+
$pow = pow( 1000, $base - floor( $base ) ); // Same rule for 1000
|
111 |
+
|
112 |
+
return round( $pow, $precision ) . ' ' . $units[( int ) floor( $base )];
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* @param null|string $directories
|
117 |
+
* @param bool $forceDisabled
|
118 |
+
* @return string
|
119 |
+
*/
|
120 |
+
public function directoryListing( $directories = null, $forceDisabled = false ) {
|
121 |
+
if( null == $directories ) {
|
122 |
+
$directories = $this->directories;
|
123 |
+
}
|
124 |
+
|
125 |
+
// Sort results
|
126 |
+
uksort( $directories, 'strcasecmp' );
|
127 |
+
|
128 |
+
$output = '';
|
129 |
+
foreach ( $directories as $name => $directory ) {
|
130 |
+
// Not a directory, possibly a symlink, therefore we will skip it
|
131 |
+
if( !is_array( $directory ) ) {
|
132 |
+
continue;
|
133 |
+
}
|
134 |
+
|
135 |
+
// Need to preserve keys so no array_shift()
|
136 |
+
$data = reset( $directory );
|
137 |
+
unset( $directory[key( $directory )] );
|
138 |
+
|
139 |
+
$isChecked = (
|
140 |
+
empty( $this->options->includedDirectories ) ||
|
141 |
+
in_array( $data["path"], $this->options->includedDirectories )
|
142 |
+
);
|
143 |
+
|
144 |
+
//$isDisabled = ($this->options->existingClones && isset($this->options->existingClones[$name]));
|
145 |
+
// Include wp core folders and their sub dirs.
|
146 |
+
// Exclude all other folders (default setting)
|
147 |
+
$isDisabled = ($name !== 'wp-admin' &&
|
148 |
+
$name !== 'wp-includes' &&
|
149 |
+
$name !== 'wp-content') &&
|
150 |
+
false === strpos( strrev( $data["path"] ), strrev( "wp-admin" ) ) &&
|
151 |
+
false === strpos( strrev( $data["path"] ), strrev( "wp-includes" ) ) &&
|
152 |
+
false === strpos( strrev( $data["path"] ), strrev( "wp-content" ) ) ? true : false;
|
153 |
+
|
154 |
+
// Extra class to differentiate between wp core and non core folders
|
155 |
+
$class = !$isDisabled ? 'wpstg-root' : 'wpstg-extra';
|
156 |
+
|
157 |
+
|
158 |
+
$output .= "<div class='wpstg-dir'>";
|
159 |
+
$output .= "<input type='checkbox' class='wpstg-check-dir " . $class . "'";
|
160 |
+
|
161 |
+
if( $isChecked && !$isDisabled && !$forceDisabled )
|
162 |
+
$output .= " checked";
|
163 |
+
//if ($forceDisabled || $isDisabled) $output .= " disabled";
|
164 |
+
|
165 |
+
$output .= " name='selectedDirectories[]' value='{$data["path"]}'>";
|
166 |
+
|
167 |
+
$output .= "<a href='#' class='wpstg-expand-dirs ";
|
168 |
+
if( !$isChecked || $isDisabled )
|
169 |
+
$output .= " disabled";
|
170 |
+
$output .= "'>{$name}";
|
171 |
+
$output .= "</a>";
|
172 |
+
|
173 |
+
$output .= "<span class='wpstg-size-info'>{$this->formatSize( $data["size"] )}</span>";
|
174 |
+
|
175 |
+
if( !empty( $directory ) ) {
|
176 |
+
$output .= "<div class='wpstg-dir wpstg-subdir'>";
|
177 |
+
$output .= $this->directoryListing( $directory, $isDisabled );
|
178 |
+
$output .= "</div>";
|
179 |
+
}
|
180 |
+
|
181 |
+
$output .= "</div>";
|
182 |
+
}
|
183 |
+
|
184 |
+
return $output;
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Checks if there is enough free disk space to create staging site
|
189 |
+
* Returns null when can't run disk_free_space function one way or another
|
190 |
+
* @return bool|null
|
191 |
+
*/
|
192 |
+
public function hasFreeDiskSpace() {
|
193 |
+
if( !function_exists( "disk_free_space" ) ) {
|
194 |
+
return null;
|
195 |
+
}
|
196 |
+
|
197 |
+
$freeSpace = @disk_free_space( ABSPATH );
|
198 |
+
|
199 |
+
if( false === $freeSpace ) {
|
200 |
+
$data = array(
|
201 |
+
'freespace' => false,
|
202 |
+
'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( ABSPATH ) )
|
203 |
+
);
|
204 |
+
echo json_encode( $data );
|
205 |
+
die();
|
206 |
+
}
|
207 |
+
|
208 |
+
|
209 |
+
$data = array(
|
210 |
+
'freespace' => $this->formatSize( $freeSpace ),
|
211 |
+
'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( ABSPATH ) )
|
212 |
+
);
|
213 |
+
|
214 |
+
echo json_encode( $data );
|
215 |
+
die();
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Get Database Tables
|
220 |
+
*/
|
221 |
+
protected function getTables() {
|
222 |
+
$wpDB = WPStaging::getInstance()->get( "wpdb" );
|
223 |
+
|
224 |
+
if( strlen( $wpDB->prefix ) > 0 ) {
|
225 |
+
//$prefix = str_replace('_', '', $wpDB->prefix);
|
226 |
+
$sql = "SHOW TABLE STATUS LIKE '{$wpDB->prefix}%'";
|
227 |
+
} else {
|
228 |
+
$sql = "SHOW TABLE STATUS";
|
229 |
+
}
|
230 |
+
|
231 |
+
$tables = $wpDB->get_results( $sql );
|
232 |
+
|
233 |
+
$currentTables = array();
|
234 |
+
|
235 |
+
// Reset excluded Tables than loop through all tables
|
236 |
+
$this->options->excludedTables = array();
|
237 |
+
foreach ( $tables as $table ) {
|
238 |
+
|
239 |
+
// Exclude WP Staging Tables
|
240 |
+
// if (0 === strpos($table->Name, "wpstg"))
|
241 |
+
// {
|
242 |
+
// continue;
|
243 |
+
// }
|
244 |
+
// Create array of unchecked tables
|
245 |
+
if( !empty( $wpDB->prefix ) && 0 !== strpos( $table->Name, $wpDB->prefix ) ) {
|
246 |
+
$this->options->excludedTables[] = $table->Name;
|
247 |
+
}
|
248 |
+
|
249 |
+
|
250 |
+
$currentTables[] = array(
|
251 |
+
"name" => $table->Name,
|
252 |
+
"size" => ($table->Data_length + $table->Index_length)
|
253 |
+
);
|
254 |
+
}
|
255 |
+
|
256 |
+
$this->options->tables = json_decode( json_encode( $currentTables ) );
|
257 |
+
}
|
258 |
+
|
259 |
+
/**
|
260 |
+
* Get directories and main meta data about'em recursively
|
261 |
+
*/
|
262 |
+
protected function directories() {
|
263 |
+
$directories = new \DirectoryIterator( ABSPATH );
|
264 |
+
|
265 |
+
foreach ( $directories as $directory ) {
|
266 |
+
// Not a valid directory
|
267 |
+
if( false === ($path = $this->getPath( $directory )) ) {
|
268 |
+
continue;
|
269 |
+
}
|
270 |
+
|
271 |
+
$this->handleDirectory( $path );
|
272 |
+
|
273 |
+
// Get Sub-directories
|
274 |
+
$this->getSubDirectories( $directory->getRealPath() );
|
275 |
+
}
|
276 |
+
|
277 |
+
// Gather Plugins
|
278 |
+
$this->getSubDirectories( WP_PLUGIN_DIR );
|
279 |
+
|
280 |
+
// Gather Themes
|
281 |
+
$this->getSubDirectories( WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "themes" );
|
282 |
+
|
283 |
+
// Gather Default Uploads Folder
|
284 |
+
//$this->getSubDirectories(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "uploads");
|
285 |
+
// Gather Custom Uploads Folder if there is one
|
286 |
+
$this->getSubDirectories( $this->getUploadDir() );
|
287 |
+
}
|
288 |
+
|
289 |
+
/**
|
290 |
+
* @param string $path
|
291 |
+
*/
|
292 |
+
protected function getSubDirectories( $path ) {
|
293 |
+
$directories = new \DirectoryIterator( $path );
|
294 |
+
|
295 |
+
foreach ( $directories as $directory ) {
|
296 |
+
// Not a valid directory
|
297 |
+
if( false === ($path = $this->getPath( $directory )) ) {
|
298 |
+
continue;
|
299 |
+
}
|
300 |
+
|
301 |
+
$this->handleDirectory( $path );
|
302 |
+
}
|
303 |
+
}
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Get Path from $directory
|
307 |
+
* @param \SplFileInfo $directory
|
308 |
+
* @return string|false
|
309 |
+
*/
|
310 |
+
protected function getPath( $directory ) {
|
311 |
+
|
312 |
+
/*
|
313 |
+
* Do not follow root path like src/web/..
|
314 |
+
* This must be done before \SplFileInfo->isDir() is used!
|
315 |
+
* Prevents open base dir restriction fatal errors
|
316 |
+
*/
|
317 |
+
if( strpos( $directory->getRealPath(), ABSPATH ) !== 0 ) {
|
318 |
+
return false;
|
319 |
+
}
|
320 |
+
$path = str_replace( ABSPATH, null, $directory->getRealPath() );
|
321 |
+
|
322 |
+
// Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
|
323 |
+
if( !$directory->isDir() || strlen( $path ) < 1 ) {
|
324 |
+
return false;
|
325 |
+
}
|
326 |
+
|
327 |
+
return $path;
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Organizes $this->directories
|
332 |
+
* @param string $path
|
333 |
+
*/
|
334 |
+
protected function handleDirectory( $path ) {
|
335 |
+
|
336 |
+
$directoryArray = explode( DIRECTORY_SEPARATOR, $path );
|
337 |
+
$total = (is_array($directoryArray) || $directoryArray instanceof Countable ) ? count( $directoryArray ) : 0;
|
338 |
+
|
339 |
+
if( $total < 1 ) {
|
340 |
+
return;
|
341 |
+
}
|
342 |
+
|
343 |
+
$total = $total - 1;
|
344 |
+
$currentArray = &$this->directories;
|
345 |
+
|
346 |
+
for ( $i = 0; $i <= $total; $i++ ) {
|
347 |
+
if( !isset( $currentArray[$directoryArray[$i]] ) ) {
|
348 |
+
$currentArray[$directoryArray[$i]] = array();
|
349 |
+
}
|
350 |
+
|
351 |
+
$currentArray = &$currentArray[$directoryArray[$i]];
|
352 |
+
|
353 |
+
// Attach meta data to the end
|
354 |
+
if( $i < $total ) {
|
355 |
+
continue;
|
356 |
+
}
|
357 |
+
|
358 |
+
$fullPath = ABSPATH . $path;
|
359 |
+
$size = $this->getDirectorySize( $fullPath );
|
360 |
+
|
361 |
+
$currentArray["metaData"] = array(
|
362 |
+
"size" => $size,
|
363 |
+
"path" => ABSPATH . $path,
|
364 |
+
);
|
365 |
+
}
|
366 |
+
}
|
367 |
+
|
368 |
+
/**
|
369 |
+
* Gets size of given directory
|
370 |
+
* @param string $path
|
371 |
+
* @return int|null
|
372 |
+
*/
|
373 |
+
protected function getDirectorySize( $path ) {
|
374 |
+
if( !isset( $this->settings->checkDirectorySize ) || '1' !== $this->settings->checkDirectorySize ) {
|
375 |
+
return null;
|
376 |
+
}
|
377 |
+
|
378 |
+
return $this->objDirectories->size( $path );
|
379 |
+
}
|
380 |
+
|
381 |
+
/**
|
382 |
+
* Get total size of a directory including all its subdirectories
|
383 |
+
* @param string $dir
|
384 |
+
* @return int
|
385 |
+
*/
|
386 |
+
function getDirectorySizeInclSubdirs( $dir ) {
|
387 |
+
$size = 0;
|
388 |
+
foreach ( glob( rtrim( $dir, '/' ) . '/*', GLOB_NOSORT ) as $each ) {
|
389 |
+
$size += is_file( $each ) ? filesize( $each ) : $this->getDirectorySizeInclSubdirs( $each );
|
390 |
+
}
|
391 |
+
return $size;
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* Get relative WP uploads path
|
396 |
+
* @return string
|
397 |
+
*/
|
398 |
+
protected function getUploadDir() {
|
399 |
+
$uploads = wp_upload_dir();
|
400 |
+
return $uploads['basedir'];
|
401 |
+
}
|
402 |
+
|
403 |
+
}
|
apps/Backend/Modules/Jobs/SearchReplace.php
CHANGED
@@ -1,694 +1,708 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\WPStaging;
|
11 |
-
use WPStaging\Utils\Strings;
|
12 |
-
use WPStaging\Utils\Helper;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Class Database
|
16 |
-
* @package WPStaging\Backend\Modules\Jobs
|
17 |
-
*/
|
18 |
-
class SearchReplace extends JobExecutable {
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @var int
|
22 |
-
*/
|
23 |
-
private $total = 0;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* @var \WPDB
|
27 |
-
*/
|
28 |
-
public $db;
|
29 |
-
|
30 |
-
/**
|
31 |
-
*
|
32 |
-
* @var string
|
33 |
-
*/
|
34 |
-
private $homeUrl;
|
35 |
-
|
36 |
-
/**
|
37 |
-
* The prefix of the new database tables which are used for the live site after updating tables
|
38 |
-
* @var string
|
39 |
-
*/
|
40 |
-
public $tmpPrefix;
|
41 |
-
|
42 |
-
/**
|
43 |
-
* Initialize
|
44 |
-
*/
|
45 |
-
public function initialize() {
|
46 |
-
$this->total = count( $this->options->tables );
|
47 |
-
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
48 |
-
//$this->tmpPrefix = 'wpstgtmp_';
|
49 |
-
$this->tmpPrefix = $this->options->prefix;
|
50 |
-
$helper = new Helper();
|
51 |
-
//$this->homeUrl = $helper->get_home_url();
|
52 |
-
$this->homeUrl = $helper->get_home_url_without_scheme();
|
53 |
-
|
54 |
-
}
|
55 |
-
|
56 |
-
public function start() {
|
57 |
-
// Skip job. Nothing to do
|
58 |
-
if( $this->options->totalSteps === 0 ) {
|
59 |
-
$this->prepareResponse( true, false );
|
60 |
-
}
|
61 |
-
|
62 |
-
$this->run();
|
63 |
-
|
64 |
-
// Save option, progress
|
65 |
-
$this->saveOptions();
|
66 |
-
|
67 |
-
return ( object ) $this->response;
|
68 |
-
}
|
69 |
-
|
70 |
-
/**
|
71 |
-
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
72 |
-
* @return void
|
73 |
-
*/
|
74 |
-
protected function calculateTotalSteps() {
|
75 |
-
$this->options->totalSteps = $this->total;
|
76 |
-
}
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Execute the Current Step
|
80 |
-
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
81 |
-
* @return bool
|
82 |
-
*/
|
83 |
-
protected function execute() {
|
84 |
-
// Over limits threshold
|
85 |
-
if( $this->isOverThreshold() ) {
|
86 |
-
// Prepare response and save current progress
|
87 |
-
$this->prepareResponse( false, false );
|
88 |
-
$this->saveOptions();
|
89 |
-
return false;
|
90 |
-
}
|
91 |
-
|
92 |
-
// No more steps, finished
|
93 |
-
if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
|
94 |
-
$this->prepareResponse( true, false );
|
95 |
-
return false;
|
96 |
-
}
|
97 |
-
|
98 |
-
// Table is excluded
|
99 |
-
if( in_array( $this->options->tables[$this->options->currentStep], $this->options->excludedTables ) ) {
|
100 |
-
$this->prepareResponse();
|
101 |
-
return true;
|
102 |
-
}
|
103 |
-
|
104 |
-
// Search & Replace
|
105 |
-
if( !$this->stopExecution() && !$this->updateTable( $this->options->tables[$this->options->currentStep] ) ) {
|
106 |
-
// Prepare Response
|
107 |
-
$this->prepareResponse( false, false );
|
108 |
-
|
109 |
-
// Not finished
|
110 |
-
return true;
|
111 |
-
}
|
112 |
-
|
113 |
-
|
114 |
-
// Prepare Response
|
115 |
-
$this->prepareResponse();
|
116 |
-
|
117 |
-
// Not finished
|
118 |
-
return true;
|
119 |
-
}
|
120 |
-
|
121 |
-
|
122 |
-
/**
|
123 |
-
* Stop Execution immediately
|
124 |
-
* return mixed bool | json
|
125 |
-
*/
|
126 |
-
private function stopExecution() {
|
127 |
-
if( $this->db->prefix == $this->tmpPrefix ) {
|
128 |
-
$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.' );
|
129 |
-
}
|
130 |
-
return false;
|
131 |
-
}
|
132 |
-
|
133 |
-
/**
|
134 |
-
* Copy Tables
|
135 |
-
* @param string $tableName
|
136 |
-
* @return bool
|
137 |
-
*/
|
138 |
-
private function updateTable( $tableName ) {
|
139 |
-
$strings = new Strings();
|
140 |
-
$table = $strings->str_replace_first( $this->db->prefix, '', $tableName );
|
141 |
-
$newTableName = $this->tmpPrefix . $table;
|
142 |
-
|
143 |
-
// Save current job
|
144 |
-
$this->setJob( $newTableName );
|
145 |
-
|
146 |
-
// Beginning of the job
|
147 |
-
if( !$this->startJob( $newTableName, $tableName ) ) {
|
148 |
-
return true;
|
149 |
-
}
|
150 |
-
// Copy data
|
151 |
-
$this->startReplace( $newTableName );
|
152 |
-
|
153 |
-
// Finis the step
|
154 |
-
return $this->finishStep();
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Start search replace job
|
159 |
-
* @param string $new
|
160 |
-
* @param string $old
|
161 |
-
*/
|
162 |
-
private function startReplace( $new ) {
|
163 |
-
$rows = $this->options->job->start + $this->settings->querySRLimit;
|
164 |
-
$this->log(
|
165 |
-
"DB Processing: Table {$new} {$this->options->job->start} to {$rows} records"
|
166 |
-
);
|
167 |
-
|
168 |
-
// Search & Replace
|
169 |
-
$this->searchReplace( $new, $rows, array() );
|
170 |
-
|
171 |
-
// Set new offset
|
172 |
-
$this->options->job->start += $this->settings->querySRLimit;
|
173 |
-
}
|
174 |
-
|
175 |
-
/**
|
176 |
-
* Returns the number of pages in a table.
|
177 |
-
* @access public
|
178 |
-
* @return int
|
179 |
-
*/
|
180 |
-
private function get_pages_in_table( $table ) {
|
181 |
-
$table = esc_sql( $table );
|
182 |
-
$rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
|
183 |
-
$pages = ceil( $rows / $this->settings->querySRLimit );
|
184 |
-
return absint( $pages );
|
185 |
-
}
|
186 |
-
|
187 |
-
/**
|
188 |
-
* Gets the columns in a table.
|
189 |
-
* @access public
|
190 |
-
* @param string $table The table to check.
|
191 |
-
* @return array
|
192 |
-
*/
|
193 |
-
private function get_columns( $table ) {
|
194 |
-
$primary_key = null;
|
195 |
-
$columns = array();
|
196 |
-
$fields = $this->db->get_results( 'DESCRIBE ' . $table );
|
197 |
-
if( is_array( $fields ) ) {
|
198 |
-
foreach ( $fields as $column ) {
|
199 |
-
$columns[] = $column->Field;
|
200 |
-
if( $column->Key == 'PRI' ) {
|
201 |
-
$primary_key = $column->Field;
|
202 |
-
}
|
203 |
-
}
|
204 |
-
}
|
205 |
-
return array($primary_key, $columns);
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
* Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
|
210 |
-
*
|
211 |
-
* Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
|
212 |
-
* and to be compatible with batch processing.
|
213 |
-
*
|
214 |
-
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
215 |
-
*
|
216 |
-
* @access public
|
217 |
-
* @param string $table The table to run the replacement on.
|
218 |
-
* @param int $page The page/block to begin the query on.
|
219 |
-
* @param array $args An associative array containing arguements for this run.
|
220 |
-
* @return array
|
221 |
-
*/
|
222 |
-
private function searchReplace( $table, $page, $args ) {
|
223 |
-
|
224 |
-
|
225 |
-
// Load up the default settings for this chunk.
|
226 |
-
$table = esc_sql( $table );
|
227 |
-
$current_page = $this->options->job->start + $this->settings->querySRLimit;
|
228 |
-
$pages = $this->get_pages_in_table( $table );
|
229 |
-
//$done = false;
|
230 |
-
|
231 |
-
|
232 |
-
if( $this->isSubDir() ) {
|
233 |
-
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
|
234 |
-
$args['search_for'] = array(
|
235 |
-
rtrim( $this->homeUrl, "/" ) . $this->getSubDir(),
|
236 |
-
rtrim( ABSPATH, '/' ),
|
237 |
-
$this->homeUrl . str_replace('/', '\/', $this->getSubDir()) // // Used by revslider and several visual editors
|
238 |
-
|
239 |
-
);
|
240 |
-
|
241 |
-
$args['replace_with'] = array(
|
242 |
-
rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName,
|
243 |
-
rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName,
|
244 |
-
$this->homeUrl . str_replace('/', '\/', $this->getSubDir()) . '\/' . $this->options->cloneDirectoryName, // Used by revslider and several visual editors
|
245 |
-
);
|
246 |
-
} else {
|
247 |
-
$args['search_for'] = array(
|
248 |
-
rtrim( $this->homeUrl, '/' ),
|
249 |
-
rtrim( ABSPATH, '/' ),
|
250 |
-
$this->homeUrl . '\/'
|
251 |
-
);
|
252 |
-
$args['replace_with'] = array(
|
253 |
-
rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
|
254 |
-
rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName,
|
255 |
-
$this->homeUrl . '\/' . $this->options->cloneDirectoryName,
|
256 |
-
);
|
257 |
-
}
|
258 |
-
|
259 |
-
|
260 |
-
$args['replace_guids'] = 'off';
|
261 |
-
$args['dry_run'] = 'off';
|
262 |
-
$args['case_insensitive'] = false;
|
263 |
-
$args['replace_guids'] = 'off';
|
264 |
-
|
265 |
-
|
266 |
-
list
|
267 |
-
|
268 |
-
|
269 |
-
//
|
270 |
-
//
|
271 |
-
// @
|
272 |
-
|
273 |
-
|
274 |
-
//
|
275 |
-
//
|
276 |
-
|
277 |
-
|
278 |
-
$
|
279 |
-
$
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
'
|
288 |
-
'
|
289 |
-
'
|
290 |
-
'
|
291 |
-
'
|
292 |
-
'
|
293 |
-
'
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
if(
|
376 |
-
$this->
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
*
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
//
|
423 |
-
//
|
424 |
-
//
|
425 |
-
//
|
426 |
-
// $
|
427 |
-
//
|
428 |
-
//
|
429 |
-
//
|
430 |
-
//
|
431 |
-
//
|
432 |
-
//
|
433 |
-
//
|
434 |
-
//
|
435 |
-
//
|
436 |
-
//
|
437 |
-
//
|
438 |
-
//
|
439 |
-
//
|
440 |
-
//
|
441 |
-
//
|
442 |
-
//
|
443 |
-
//
|
444 |
-
//
|
445 |
-
//
|
446 |
-
//
|
447 |
-
//
|
448 |
-
//
|
449 |
-
//
|
450 |
-
//
|
451 |
-
//
|
452 |
-
//
|
453 |
-
//
|
454 |
-
//
|
455 |
-
//
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
$
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
//
|
519 |
-
//
|
520 |
-
//
|
521 |
-
//
|
522 |
-
//
|
523 |
-
//
|
524 |
-
//
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
*
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
*
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
*
|
585 |
-
* @
|
586 |
-
*/
|
587 |
-
private function
|
588 |
-
if(
|
589 |
-
|
590 |
-
}
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
*
|
599 |
-
* @param string $
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
$this->options->job->
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
*
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\WPStaging;
|
11 |
+
use WPStaging\Utils\Strings;
|
12 |
+
use WPStaging\Utils\Helper;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Class Database
|
16 |
+
* @package WPStaging\Backend\Modules\Jobs
|
17 |
+
*/
|
18 |
+
class SearchReplace extends JobExecutable {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var int
|
22 |
+
*/
|
23 |
+
private $total = 0;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var \WPDB
|
27 |
+
*/
|
28 |
+
public $db;
|
29 |
+
|
30 |
+
/**
|
31 |
+
*
|
32 |
+
* @var string
|
33 |
+
*/
|
34 |
+
private $homeUrl;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* The prefix of the new database tables which are used for the live site after updating tables
|
38 |
+
* @var string
|
39 |
+
*/
|
40 |
+
public $tmpPrefix;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Initialize
|
44 |
+
*/
|
45 |
+
public function initialize() {
|
46 |
+
$this->total = count( $this->options->tables );
|
47 |
+
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
48 |
+
//$this->tmpPrefix = 'wpstgtmp_';
|
49 |
+
$this->tmpPrefix = $this->options->prefix;
|
50 |
+
$helper = new Helper();
|
51 |
+
//$this->homeUrl = $helper->get_home_url();
|
52 |
+
$this->homeUrl = $helper->get_home_url_without_scheme();
|
53 |
+
|
54 |
+
}
|
55 |
+
|
56 |
+
public function start() {
|
57 |
+
// Skip job. Nothing to do
|
58 |
+
if( $this->options->totalSteps === 0 ) {
|
59 |
+
$this->prepareResponse( true, false );
|
60 |
+
}
|
61 |
+
|
62 |
+
$this->run();
|
63 |
+
|
64 |
+
// Save option, progress
|
65 |
+
$this->saveOptions();
|
66 |
+
|
67 |
+
return ( object ) $this->response;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
72 |
+
* @return void
|
73 |
+
*/
|
74 |
+
protected function calculateTotalSteps() {
|
75 |
+
$this->options->totalSteps = $this->total;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Execute the Current Step
|
80 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
81 |
+
* @return bool
|
82 |
+
*/
|
83 |
+
protected function execute() {
|
84 |
+
// Over limits threshold
|
85 |
+
if( $this->isOverThreshold() ) {
|
86 |
+
// Prepare response and save current progress
|
87 |
+
$this->prepareResponse( false, false );
|
88 |
+
$this->saveOptions();
|
89 |
+
return false;
|
90 |
+
}
|
91 |
+
|
92 |
+
// No more steps, finished
|
93 |
+
if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
|
94 |
+
$this->prepareResponse( true, false );
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
|
98 |
+
// Table is excluded
|
99 |
+
if( in_array( $this->options->tables[$this->options->currentStep], $this->options->excludedTables ) ) {
|
100 |
+
$this->prepareResponse();
|
101 |
+
return true;
|
102 |
+
}
|
103 |
+
|
104 |
+
// Search & Replace
|
105 |
+
if( !$this->stopExecution() && !$this->updateTable( $this->options->tables[$this->options->currentStep] ) ) {
|
106 |
+
// Prepare Response
|
107 |
+
$this->prepareResponse( false, false );
|
108 |
+
|
109 |
+
// Not finished
|
110 |
+
return true;
|
111 |
+
}
|
112 |
+
|
113 |
+
|
114 |
+
// Prepare Response
|
115 |
+
$this->prepareResponse();
|
116 |
+
|
117 |
+
// Not finished
|
118 |
+
return true;
|
119 |
+
}
|
120 |
+
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Stop Execution immediately
|
124 |
+
* return mixed bool | json
|
125 |
+
*/
|
126 |
+
private function stopExecution() {
|
127 |
+
if( $this->db->prefix == $this->tmpPrefix ) {
|
128 |
+
$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.' );
|
129 |
+
}
|
130 |
+
return false;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Copy Tables
|
135 |
+
* @param string $tableName
|
136 |
+
* @return bool
|
137 |
+
*/
|
138 |
+
private function updateTable( $tableName ) {
|
139 |
+
$strings = new Strings();
|
140 |
+
$table = $strings->str_replace_first( $this->db->prefix, '', $tableName );
|
141 |
+
$newTableName = $this->tmpPrefix . $table;
|
142 |
+
|
143 |
+
// Save current job
|
144 |
+
$this->setJob( $newTableName );
|
145 |
+
|
146 |
+
// Beginning of the job
|
147 |
+
if( !$this->startJob( $newTableName, $tableName ) ) {
|
148 |
+
return true;
|
149 |
+
}
|
150 |
+
// Copy data
|
151 |
+
$this->startReplace( $newTableName );
|
152 |
+
|
153 |
+
// Finis the step
|
154 |
+
return $this->finishStep();
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Start search replace job
|
159 |
+
* @param string $new
|
160 |
+
* @param string $old
|
161 |
+
*/
|
162 |
+
private function startReplace( $new ) {
|
163 |
+
$rows = $this->options->job->start + $this->settings->querySRLimit;
|
164 |
+
$this->log(
|
165 |
+
"DB Processing: Table {$new} {$this->options->job->start} to {$rows} records"
|
166 |
+
);
|
167 |
+
|
168 |
+
// Search & Replace
|
169 |
+
$this->searchReplace( $new, $rows, array() );
|
170 |
+
|
171 |
+
// Set new offset
|
172 |
+
$this->options->job->start += $this->settings->querySRLimit;
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Returns the number of pages in a table.
|
177 |
+
* @access public
|
178 |
+
* @return int
|
179 |
+
*/
|
180 |
+
private function get_pages_in_table( $table ) {
|
181 |
+
$table = esc_sql( $table );
|
182 |
+
$rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
|
183 |
+
$pages = ceil( $rows / $this->settings->querySRLimit );
|
184 |
+
return absint( $pages );
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Gets the columns in a table.
|
189 |
+
* @access public
|
190 |
+
* @param string $table The table to check.
|
191 |
+
* @return array
|
192 |
+
*/
|
193 |
+
private function get_columns( $table ) {
|
194 |
+
$primary_key = null;
|
195 |
+
$columns = array();
|
196 |
+
$fields = $this->db->get_results( 'DESCRIBE ' . $table );
|
197 |
+
if( is_array( $fields ) ) {
|
198 |
+
foreach ( $fields as $column ) {
|
199 |
+
$columns[] = $column->Field;
|
200 |
+
if( $column->Key == 'PRI' ) {
|
201 |
+
$primary_key = $column->Field;
|
202 |
+
}
|
203 |
+
}
|
204 |
+
}
|
205 |
+
return array($primary_key, $columns);
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
|
210 |
+
*
|
211 |
+
* Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
|
212 |
+
* and to be compatible with batch processing.
|
213 |
+
*
|
214 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
215 |
+
*
|
216 |
+
* @access public
|
217 |
+
* @param string $table The table to run the replacement on.
|
218 |
+
* @param int $page The page/block to begin the query on.
|
219 |
+
* @param array $args An associative array containing arguements for this run.
|
220 |
+
* @return array
|
221 |
+
*/
|
222 |
+
private function searchReplace( $table, $page, $args ) {
|
223 |
+
|
224 |
+
|
225 |
+
// Load up the default settings for this chunk.
|
226 |
+
$table = esc_sql( $table );
|
227 |
+
$current_page = $this->options->job->start + $this->settings->querySRLimit;
|
228 |
+
$pages = $this->get_pages_in_table( $table );
|
229 |
+
//$done = false;
|
230 |
+
|
231 |
+
|
232 |
+
if( $this->isSubDir() ) {
|
233 |
+
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
|
234 |
+
$args['search_for'] = array(
|
235 |
+
rtrim( $this->homeUrl, "/" ) . $this->getSubDir(),
|
236 |
+
rtrim( ABSPATH, '/' ),
|
237 |
+
$this->homeUrl . str_replace('/', '\/', $this->getSubDir()) // // Used by revslider and several visual editors
|
238 |
+
|
239 |
+
);
|
240 |
+
|
241 |
+
$args['replace_with'] = array(
|
242 |
+
rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName,
|
243 |
+
rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName,
|
244 |
+
$this->homeUrl . str_replace('/', '\/', $this->getSubDir()) . '\/' . $this->options->cloneDirectoryName, // Used by revslider and several visual editors
|
245 |
+
);
|
246 |
+
} else {
|
247 |
+
$args['search_for'] = array(
|
248 |
+
rtrim( $this->homeUrl, '/' ),
|
249 |
+
rtrim( ABSPATH, '/' ),
|
250 |
+
$this->homeUrl . '\/'
|
251 |
+
);
|
252 |
+
$args['replace_with'] = array(
|
253 |
+
rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
|
254 |
+
rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName,
|
255 |
+
$this->homeUrl . '\/' . $this->options->cloneDirectoryName,
|
256 |
+
);
|
257 |
+
}
|
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 |
+
// Get a list of columns in this table.
|
267 |
+
list( $primary_key, $columns ) = $this->get_columns( $table );
|
268 |
+
|
269 |
+
// Bail out early if there isn't a primary key.
|
270 |
+
// We commented this to search & replace through tables which have no primary keys like wp_revslider_slides
|
271 |
+
// @todo test this carefully. If it causes (performance) issues we need to activate it again!
|
272 |
+
// @since 2.4.4
|
273 |
+
|
274 |
+
// if( null === $primary_key ) {
|
275 |
+
// return false;
|
276 |
+
// }
|
277 |
+
|
278 |
+
$current_row = 0;
|
279 |
+
$start = $this->options->job->start;
|
280 |
+
$end = $this->settings->querySRLimit;
|
281 |
+
|
282 |
+
// Grab the content of the table.
|
283 |
+
$data = $this->db->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
|
284 |
+
|
285 |
+
// Filter certain rows (of other plugins)
|
286 |
+
$filter = array(
|
287 |
+
'Admin_custome_login_Slidshow',
|
288 |
+
'Admin_custome_login_Social',
|
289 |
+
'Admin_custome_login_logo',
|
290 |
+
'Admin_custome_login_text',
|
291 |
+
'Admin_custome_login_login',
|
292 |
+
'Admin_custome_login_top',
|
293 |
+
'Admin_custome_login_dashboard',
|
294 |
+
'Admin_custome_login_Version',
|
295 |
+
);
|
296 |
+
|
297 |
+
apply_filters('wpstg_fiter_search_replace_rows', $filter);
|
298 |
+
|
299 |
+
// // Do not search & replace any strings below
|
300 |
+
// $filterStrings = array(
|
301 |
+
// '@' . $this->homeUrl // Mail addresses
|
302 |
+
// );
|
303 |
+
|
304 |
+
//apply_filters('wpstg_fiter_search_replace_strings', $filterStrings);
|
305 |
+
|
306 |
+
|
307 |
+
// Loop through the data.
|
308 |
+
foreach ( $data as $row ) {
|
309 |
+
$current_row++;
|
310 |
+
$update_sql = array();
|
311 |
+
$where_sql = array();
|
312 |
+
$upd = false;
|
313 |
+
|
314 |
+
// Skip rows below
|
315 |
+
if (isset($row['option_name']) && in_array($row['option_name'], $filter)){
|
316 |
+
continue;
|
317 |
+
}
|
318 |
+
|
319 |
+
// Skip rows with transients (They can store huge data and we need to save memory)
|
320 |
+
if( isset( $row['option_name'] ) && strpos( $row['option_name'], '_transient' ) === 0 ) {
|
321 |
+
continue;
|
322 |
+
}
|
323 |
+
|
324 |
+
foreach ( $columns as $column ) {
|
325 |
+
|
326 |
+
$dataRow = $row[$column];
|
327 |
+
|
328 |
+
if( $column == $primary_key ) {
|
329 |
+
$where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
330 |
+
continue;
|
331 |
+
}
|
332 |
+
|
333 |
+
// Skip GUIDs by default.
|
334 |
+
if( 'on' !== $args['replace_guids'] && 'guid' == $column ) {
|
335 |
+
continue;
|
336 |
+
}
|
337 |
+
|
338 |
+
// Skip mail addresses
|
339 |
+
if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->homeUrl ) ) {
|
340 |
+
continue;
|
341 |
+
}
|
342 |
+
|
343 |
+
// Check options table
|
344 |
+
if( $this->options->prefix . 'options' === $table ) {
|
345 |
+
|
346 |
+
// Skip certain options
|
347 |
+
if( isset( $should_skip ) && true === $should_skip ) {
|
348 |
+
$should_skip = false;
|
349 |
+
continue;
|
350 |
+
}
|
351 |
+
|
352 |
+
// Skip this rows
|
353 |
+
if( 'wpstg_existing_clones_beta' === $dataRow ||
|
354 |
+
'wpstg_existing_clones' === $dataRow ||
|
355 |
+
'wpstg_settings' === $dataRow ||
|
356 |
+
'wpstg_license_status' === $dataRow ||
|
357 |
+
'siteurl' === $dataRow ||
|
358 |
+
'home' === $dataRow
|
359 |
+
) {
|
360 |
+
$should_skip = true;
|
361 |
+
}
|
362 |
+
}
|
363 |
+
|
364 |
+
|
365 |
+
// Run a search replace on the data that'll respect the serialisation.
|
366 |
+
$i = 0;
|
367 |
+
foreach ( $args['search_for'] as $replace ) {
|
368 |
+
$dataRow = $this->recursive_unserialize_replace( $args['search_for'][$i], $args['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
|
369 |
+
$i++;
|
370 |
+
}
|
371 |
+
unset( $replace );
|
372 |
+
unset( $i );
|
373 |
+
|
374 |
+
// Something was changed
|
375 |
+
if( $row[$column] != $dataRow ) {
|
376 |
+
$update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
377 |
+
$upd = true;
|
378 |
+
}
|
379 |
+
}
|
380 |
+
|
381 |
+
// Determine what to do with updates.
|
382 |
+
if( $args['dry_run'] === 'on' ) {
|
383 |
+
// Don't do anything if a dry run
|
384 |
+
} elseif( $upd && !empty( $where_sql ) ) {
|
385 |
+
// If there are changes to make, run the query.
|
386 |
+
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
|
387 |
+
$result = $this->db->query( $sql );
|
388 |
+
|
389 |
+
if( !$result ) {
|
390 |
+
$this->log( "Error updating row {$current_row}", \WPStaging\Utils\Logger::TYPE_ERROR );
|
391 |
+
}
|
392 |
+
}
|
393 |
+
} // end row loop
|
394 |
+
unset( $row );
|
395 |
+
unset( $update_sql );
|
396 |
+
unset( $where_sql );
|
397 |
+
unset( $sql );
|
398 |
+
|
399 |
+
|
400 |
+
// DB Flush
|
401 |
+
$this->db->flush();
|
402 |
+
return true;
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Adapted from interconnect/it's search/replace script.
|
407 |
+
*
|
408 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
409 |
+
*
|
410 |
+
* Take a serialised array and unserialise it replacing elements as needed and
|
411 |
+
* unserialising any subordinate arrays and performing the replace on those too.
|
412 |
+
*
|
413 |
+
* @access private
|
414 |
+
* @param string $from String we're looking to replace.
|
415 |
+
* @param string $to What we want it to be replaced with
|
416 |
+
* @param array $data Used to pass any subordinate arrays back to in.
|
417 |
+
* @param boolean $serialised Does the array passed via $data need serialising.
|
418 |
+
* @param sting|boolean $case_insensitive Set to 'on' if we should ignore case, false otherwise.
|
419 |
+
*
|
420 |
+
* @return string|array The original array with all elements replaced as needed.
|
421 |
+
*/
|
422 |
+
// private function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialised = false, $case_insensitive = false ) {
|
423 |
+
// try {
|
424 |
+
//
|
425 |
+
// if( is_string( $data ) && !is_serialized_string( $data ) && ( $unserialized = $this->unserialize( $data ) ) !== false ) {
|
426 |
+
// $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
|
427 |
+
// } elseif( is_array( $data ) ) {
|
428 |
+
// $_tmp = array();
|
429 |
+
// foreach ( $data as $key => $value ) {
|
430 |
+
// $_tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
431 |
+
// }
|
432 |
+
//
|
433 |
+
// $data = $_tmp;
|
434 |
+
// unset( $_tmp );
|
435 |
+
// }
|
436 |
+
//
|
437 |
+
// // Submitted by Tina Matter
|
438 |
+
// elseif( $this->isValidObject($data) ) {
|
439 |
+
// $_tmp = $data; // new $data_class( );
|
440 |
+
// $props = get_object_vars( $data );
|
441 |
+
// foreach ( $props as $key => $value ) {
|
442 |
+
// if( $key === '' || ord( $key[0] ) === 0 ) {
|
443 |
+
// continue;
|
444 |
+
// }
|
445 |
+
// $_tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
446 |
+
// }
|
447 |
+
//
|
448 |
+
// $data = $_tmp;
|
449 |
+
// unset($_tmp);
|
450 |
+
// } elseif (is_serialized_string($data)) {
|
451 |
+
// if (false !== ($data = $this->unserialize($data)) ) {
|
452 |
+
// $data = $this->str_replace($from, $to, $data, $case_insensitive);
|
453 |
+
// $data = serialize($data);
|
454 |
+
// }
|
455 |
+
// } else {
|
456 |
+
// if( is_string( $data ) ) {
|
457 |
+
// $data = $this->str_replace( $from, $to, $data, $case_insensitive );
|
458 |
+
// }
|
459 |
+
// }
|
460 |
+
//
|
461 |
+
// if( $serialised ) {
|
462 |
+
// return serialize( $data );
|
463 |
+
// }
|
464 |
+
// } catch ( Exception $error ) {
|
465 |
+
//
|
466 |
+
// }
|
467 |
+
//
|
468 |
+
// return $data;
|
469 |
+
// }
|
470 |
+
private function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialized = false, $case_insensitive = false ) {
|
471 |
+
try {
|
472 |
+
// Some unserialized data cannot be re-serialized eg. SimpleXMLElements
|
473 |
+
if( is_serialized( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
|
474 |
+
$data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
|
475 |
+
} elseif( is_array( $data ) ) {
|
476 |
+
$tmp = array();
|
477 |
+
foreach ( $data as $key => $value ) {
|
478 |
+
$tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
479 |
+
}
|
480 |
+
|
481 |
+
$data = $tmp;
|
482 |
+
unset( $tmp );
|
483 |
+
} elseif( is_object( $data ) ) {
|
484 |
+
$tmp = $data;
|
485 |
+
$props = get_object_vars( $data );
|
486 |
+
foreach ( $props as $key => $value ) {
|
487 |
+
if( $key === '' || ord( $key[0] ) === 0 ) {
|
488 |
+
continue;
|
489 |
+
}
|
490 |
+
$tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
491 |
+
}
|
492 |
+
|
493 |
+
$data = $tmp;
|
494 |
+
unset( $tmp );
|
495 |
+
} else {
|
496 |
+
if( is_string( $data ) ) {
|
497 |
+
if( !empty( $from ) && !empty( $to ) ) {
|
498 |
+
$data = $this->str_replace( $from, $to, $data, $case_insensitive );
|
499 |
+
}
|
500 |
+
}
|
501 |
+
}
|
502 |
+
|
503 |
+
if( $serialized ) {
|
504 |
+
return serialize( $data );
|
505 |
+
}
|
506 |
+
} catch ( Exception $error ) {
|
507 |
+
|
508 |
+
}
|
509 |
+
|
510 |
+
return $data;
|
511 |
+
}
|
512 |
+
|
513 |
+
/**
|
514 |
+
* Check if the object is a valid one and not __PHP_Incomplete_Class_Name
|
515 |
+
* Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
|
516 |
+
* @return boolean
|
517 |
+
*/
|
518 |
+
// private function isValidObject($data){
|
519 |
+
// if( !is_object( $data ) || gettype( $data ) != 'object' ) {
|
520 |
+
// return false;
|
521 |
+
// }
|
522 |
+
//
|
523 |
+
// $invalid_class_props = get_object_vars( $data );
|
524 |
+
//
|
525 |
+
// if (!isset($invalid_class_props['__PHP_Incomplete_Class_Name'])){
|
526 |
+
// // Assume it must be an valid object
|
527 |
+
// return true;
|
528 |
+
// }
|
529 |
+
//
|
530 |
+
// $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
|
531 |
+
//
|
532 |
+
// if( !empty( $invalid_object_class ) ) {
|
533 |
+
// return false;
|
534 |
+
// }
|
535 |
+
//
|
536 |
+
// // Assume it must be an valid object
|
537 |
+
// return true;
|
538 |
+
// }
|
539 |
+
|
540 |
+
/**
|
541 |
+
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
|
542 |
+
* @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
|
543 |
+
* @access public
|
544 |
+
* @param string $input The string to escape.
|
545 |
+
* @return string
|
546 |
+
*/
|
547 |
+
private function mysql_escape_mimic( $input ) {
|
548 |
+
if( is_array( $input ) ) {
|
549 |
+
return array_map( __METHOD__, $input );
|
550 |
+
}
|
551 |
+
if( !empty( $input ) && is_string( $input ) ) {
|
552 |
+
return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
|
553 |
+
}
|
554 |
+
|
555 |
+
return $input;
|
556 |
+
}
|
557 |
+
|
558 |
+
/**
|
559 |
+
* Return unserialized object or array
|
560 |
+
*
|
561 |
+
* @param string $serialized_string Serialized string.
|
562 |
+
* @param string $method The name of the caller method.
|
563 |
+
*
|
564 |
+
* @return mixed, false on failure
|
565 |
+
*/
|
566 |
+
private static function unserialize( $serialized_string ) {
|
567 |
+
if( !is_serialized( $serialized_string ) ) {
|
568 |
+
return false;
|
569 |
+
}
|
570 |
+
|
571 |
+
$serialized_string = trim( $serialized_string );
|
572 |
+
$unserialized_string = @unserialize( $serialized_string );
|
573 |
+
|
574 |
+
return $unserialized_string;
|
575 |
+
}
|
576 |
+
|
577 |
+
/**
|
578 |
+
* Wrapper for str_replace
|
579 |
+
*
|
580 |
+
* @param string $from
|
581 |
+
* @param string $to
|
582 |
+
* @param string $data
|
583 |
+
* @param string|bool $case_insensitive
|
584 |
+
*
|
585 |
+
* @return string
|
586 |
+
*/
|
587 |
+
private function str_replace( $from, $to, $data, $case_insensitive = false ) {
|
588 |
+
if( 'on' === $case_insensitive ) {
|
589 |
+
$data = str_ireplace( $from, $to, $data );
|
590 |
+
} else {
|
591 |
+
$data = str_replace( $from, $to, $data );
|
592 |
+
}
|
593 |
+
|
594 |
+
return $data;
|
595 |
+
}
|
596 |
+
|
597 |
+
/**
|
598 |
+
* Set the job
|
599 |
+
* @param string $table
|
600 |
+
*/
|
601 |
+
private function setJob( $table ) {
|
602 |
+
if( !empty( $this->options->job->current ) ) {
|
603 |
+
return;
|
604 |
+
}
|
605 |
+
|
606 |
+
$this->options->job->current = $table;
|
607 |
+
$this->options->job->start = 0;
|
608 |
+
}
|
609 |
+
|
610 |
+
/**
|
611 |
+
* Start Job
|
612 |
+
* @param string $new
|
613 |
+
* @param string $old
|
614 |
+
* @return bool
|
615 |
+
*/
|
616 |
+
private function startJob( $new, $old ) {
|
617 |
+
if( 0 != $this->options->job->start ) {
|
618 |
+
return true;
|
619 |
+
}
|
620 |
+
|
621 |
+
$this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
|
622 |
+
|
623 |
+
if( 0 == $this->options->job->total ) {
|
624 |
+
$this->finishStep();
|
625 |
+
return false;
|
626 |
+
}
|
627 |
+
|
628 |
+
return true;
|
629 |
+
}
|
630 |
+
|
631 |
+
/**
|
632 |
+
* Finish the step
|
633 |
+
*/
|
634 |
+
private function finishStep() {
|
635 |
+
// This job is not finished yet
|
636 |
+
if( $this->options->job->total > $this->options->job->start ) {
|
637 |
+
return false;
|
638 |
+
}
|
639 |
+
|
640 |
+
// Add it to cloned tables listing
|
641 |
+
$this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
|
642 |
+
|
643 |
+
// Reset job
|
644 |
+
$this->options->job = new \stdClass();
|
645 |
+
|
646 |
+
return true;
|
647 |
+
}
|
648 |
+
|
649 |
+
/**
|
650 |
+
* Drop table if necessary
|
651 |
+
* @param string $new
|
652 |
+
*/
|
653 |
+
private function dropTable( $new ) {
|
654 |
+
$old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
|
655 |
+
|
656 |
+
if( !$this->shouldDropTable( $new, $old ) ) {
|
657 |
+
return;
|
658 |
+
}
|
659 |
+
|
660 |
+
$this->log( "DB Processing: {$new} already exists, dropping it first" );
|
661 |
+
$this->db->query( "DROP TABLE {$new}" );
|
662 |
+
}
|
663 |
+
|
664 |
+
/**
|
665 |
+
* Check if table needs to be dropped
|
666 |
+
* @param string $new
|
667 |
+
* @param string $old
|
668 |
+
* @return bool
|
669 |
+
*/
|
670 |
+
private function shouldDropTable( $new, $old ) {
|
671 |
+
return (
|
672 |
+
$old == $new &&
|
673 |
+
(
|
674 |
+
!isset( $this->options->job->current ) ||
|
675 |
+
!isset( $this->options->job->start ) ||
|
676 |
+
0 == $this->options->job->start
|
677 |
+
)
|
678 |
+
);
|
679 |
+
}
|
680 |
+
|
681 |
+
/**
|
682 |
+
* Check if WP is installed in subdir
|
683 |
+
* @return boolean
|
684 |
+
*/
|
685 |
+
private function isSubDir(){
|
686 |
+
if ( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
|
687 |
+
return true;
|
688 |
+
}
|
689 |
+
return false;
|
690 |
+
}
|
691 |
+
|
692 |
+
/**
|
693 |
+
* Get the install sub directory if WP is installed in sub directory
|
694 |
+
* @return string
|
695 |
+
*/
|
696 |
+
private function getSubDir() {
|
697 |
+
$home = get_option( 'home' );
|
698 |
+
$siteurl = get_option( 'siteurl' );
|
699 |
+
|
700 |
+
if( empty( $home ) || empty( $siteurl ) ) {
|
701 |
+
return '/';
|
702 |
+
}
|
703 |
+
|
704 |
+
$dir = str_replace( $home, '', $siteurl );
|
705 |
+
return '/' . str_replace( '/', '', $dir );
|
706 |
+
}
|
707 |
+
|
708 |
+
}
|
apps/Backend/Modules/Jobs/Updating.php
CHANGED
@@ -1,248 +1,248 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPStaging\Backend\Modules\Jobs;
|
3 |
-
|
4 |
-
use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
|
5 |
-
use WPStaging\WPStaging;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class Cloning
|
9 |
-
* @package WPStaging\Backend\Modules\Jobs
|
10 |
-
*/
|
11 |
-
class Updating extends Job
|
12 |
-
{
|
13 |
-
/**
|
14 |
-
* Initialize is called in \Job
|
15 |
-
*/
|
16 |
-
public function initialize(){
|
17 |
-
$this->db = WPStaging::getInstance()->get("wpdb");
|
18 |
-
}
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Save Chosen Cloning Settings
|
22 |
-
* @return bool
|
23 |
-
*/
|
24 |
-
public function save()
|
25 |
-
{
|
26 |
-
if (!isset($_POST) || !isset($_POST["cloneID"]))
|
27 |
-
{
|
28 |
-
return false;
|
29 |
-
}
|
30 |
-
|
31 |
-
// Generate Options
|
32 |
-
// Clone
|
33 |
-
$this->options->clone = $_POST["cloneID"];
|
34 |
-
$this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
|
35 |
-
$this->options->cloneNumber = 1;
|
36 |
-
$this->options->includedDirectories = array();
|
37 |
-
$this->options->excludedDirectories = array();
|
38 |
-
$this->options->extraDirectories = array();
|
39 |
-
$this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
|
40 |
-
|
41 |
-
// Define mainJob to differentiate between cloning, updating and pushing
|
42 |
-
$this->options->mainJob = 'updating';
|
43 |
-
|
44 |
-
// Job
|
45 |
-
$this->options->job = new \stdClass();
|
46 |
-
|
47 |
-
// Check if clone data already exists and use that one
|
48 |
-
if (isset($this->options->existingClones[$this->options->clone]) )
|
49 |
-
{
|
50 |
-
$this->options->cloneNumber = $this->options->existingClones[$this->options->clone]['number'];
|
51 |
-
$this->options->prefix = $this->getStagingPrefix();
|
52 |
-
} else {
|
53 |
-
wp_die('Fatal Error: Can not update clone because there is no clone data.');
|
54 |
-
}
|
55 |
-
|
56 |
-
|
57 |
-
// Excluded Tables
|
58 |
-
// if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
|
59 |
-
// {
|
60 |
-
// $this->options->excludedTables = $_POST["excludedTables"];
|
61 |
-
// }
|
62 |
-
// Included Tables
|
63 |
-
if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"]))
|
64 |
-
{
|
65 |
-
$this->options->tables = $_POST["includedTables"];
|
66 |
-
} else {
|
67 |
-
$this->options->tables = array();
|
68 |
-
}
|
69 |
-
|
70 |
-
// Excluded Directories
|
71 |
-
if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
|
72 |
-
{
|
73 |
-
$this->options->excludedDirectories = $_POST["excludedDirectories"];
|
74 |
-
}
|
75 |
-
|
76 |
-
// Excluded Directories TOTAL
|
77 |
-
// Do not copy these folders and plugins
|
78 |
-
$excludedDirectories = array(
|
79 |
-
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
|
80 |
-
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login'
|
81 |
-
);
|
82 |
-
|
83 |
-
$this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
|
84 |
-
|
85 |
-
// Included Directories
|
86 |
-
if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
|
87 |
-
{
|
88 |
-
$this->options->includedDirectories = $_POST["includedDirectories"];
|
89 |
-
}
|
90 |
-
|
91 |
-
// Extra Directories
|
92 |
-
if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
|
93 |
-
{
|
94 |
-
$this->options->extraDirectories = $_POST["extraDirectories"];
|
95 |
-
}
|
96 |
-
|
97 |
-
// Directories to Copy
|
98 |
-
$this->options->directoriesToCopy = array_merge(
|
99 |
-
$this->options->includedDirectories,
|
100 |
-
$this->options->extraDirectories
|
101 |
-
);
|
102 |
-
|
103 |
-
array_unshift($this->options->directoriesToCopy, ABSPATH);
|
104 |
-
|
105 |
-
// Delete files to copy listing
|
106 |
-
$this->cache->delete("files_to_copy");
|
107 |
-
|
108 |
-
return $this->saveOptions();
|
109 |
-
}
|
110 |
-
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Check and return prefix of the staging site
|
114 |
-
*/
|
115 |
-
public function getStagingPrefix() {
|
116 |
-
// prefix not defined! Happens if staging site has ben generated with older version of wpstg
|
117 |
-
// Try to get staging prefix from wp-config.php of staging site
|
118 |
-
$this->options->prefix = $this->options->existingClones[$this->options->clone]['prefix'];
|
119 |
-
if (empty($this->options->prefix)) {
|
120 |
-
// Throw error if wp-config.php is not readable
|
121 |
-
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
|
122 |
-
if (false === ($content = @file_get_contents($path))) {
|
123 |
-
$this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
|
124 |
-
$this->returnException("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
125 |
-
wp_die("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
126 |
-
} else {
|
127 |
-
// Get prefix from wp-config.php
|
128 |
-
preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
|
129 |
-
|
130 |
-
if (!empty($matches[1])) {
|
131 |
-
$this->options->prefix = $matches[1];
|
132 |
-
} else {
|
133 |
-
$this->returnException("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
134 |
-
wp_die("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
135 |
-
}
|
136 |
-
}
|
137 |
-
}
|
138 |
-
|
139 |
-
// Die() if staging prefix is the same as the live prefix
|
140 |
-
if ($this->db->prefix == $this->options->prefix) {
|
141 |
-
$this->log("Fatal Error: Can not updatte staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
142 |
-
wp_die("Fatal Error: Can not update staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
143 |
-
}
|
144 |
-
|
145 |
-
// Else
|
146 |
-
return $this->options->prefix;
|
147 |
-
}
|
148 |
-
|
149 |
-
/**
|
150 |
-
* Start the cloning job
|
151 |
-
*/
|
152 |
-
public function start()
|
153 |
-
{
|
154 |
-
if (null === $this->options->currentJob)
|
155 |
-
{
|
156 |
-
$this->log("Cloning job for {$this->options->clone} finished");
|
157 |
-
return true;
|
158 |
-
}
|
159 |
-
|
160 |
-
$methodName = "job" . ucwords($this->options->currentJob);
|
161 |
-
|
162 |
-
if (!method_exists($this, $methodName))
|
163 |
-
{
|
164 |
-
$this->log("Can't execute job; Job's method {$methodName} is not found");
|
165 |
-
throw new JobNotFoundException($methodName);
|
166 |
-
}
|
167 |
-
|
168 |
-
// Call the job
|
169 |
-
//$this->log("execute job: Job's method {$methodName}");
|
170 |
-
return $this->{$methodName}();
|
171 |
-
}
|
172 |
-
|
173 |
-
/**
|
174 |
-
* @param object $response
|
175 |
-
* @param string $nextJob
|
176 |
-
* @return object
|
177 |
-
*/
|
178 |
-
private function handleJobResponse($response, $nextJob)
|
179 |
-
{
|
180 |
-
// Job is not done
|
181 |
-
if (true !== $response->status)
|
182 |
-
{
|
183 |
-
return $response;
|
184 |
-
}
|
185 |
-
|
186 |
-
$this->options->currentJob = $nextJob;
|
187 |
-
$this->options->currentStep = 0;
|
188 |
-
$this->options->totalSteps = 0;
|
189 |
-
|
190 |
-
// Save options
|
191 |
-
$this->saveOptions();
|
192 |
-
|
193 |
-
return $response;
|
194 |
-
}
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
/**
|
200 |
-
* Clone Database
|
201 |
-
* @return object
|
202 |
-
*/
|
203 |
-
public function jobDatabase()
|
204 |
-
{
|
205 |
-
$database = new Database();
|
206 |
-
return $this->handleJobResponse($database->start(), "directories");
|
207 |
-
}
|
208 |
-
|
209 |
-
/**
|
210 |
-
* Get All Files From Selected Directories Recursively Into a File
|
211 |
-
* @return object
|
212 |
-
*/
|
213 |
-
public function jobDirectories()
|
214 |
-
{
|
215 |
-
$directories = new Directories();
|
216 |
-
return $this->handleJobResponse($directories->start(), "files");
|
217 |
-
}
|
218 |
-
|
219 |
-
/**
|
220 |
-
* Copy Files
|
221 |
-
* @return object
|
222 |
-
*/
|
223 |
-
public function jobFiles()
|
224 |
-
{
|
225 |
-
$files = new Files();
|
226 |
-
return $this->handleJobResponse($files->start(), "data");
|
227 |
-
}
|
228 |
-
|
229 |
-
/**
|
230 |
-
* Replace Data
|
231 |
-
* @return object
|
232 |
-
*/
|
233 |
-
public function jobData()
|
234 |
-
{
|
235 |
-
$data = new Data();
|
236 |
-
return $this->handleJobResponse($data->start(), "finish");
|
237 |
-
}
|
238 |
-
|
239 |
-
/**
|
240 |
-
* Save Clone Data
|
241 |
-
* @return object
|
242 |
-
*/
|
243 |
-
public function jobFinish()
|
244 |
-
{
|
245 |
-
$finish = new Finish();
|
246 |
-
return $this->handleJobResponse($finish->start(), '');
|
247 |
-
}
|
248 |
}
|
1 |
+
<?php
|
2 |
+
namespace WPStaging\Backend\Modules\Jobs;
|
3 |
+
|
4 |
+
use WPStaging\Backend\Modules\Jobs\Exceptions\JobNotFoundException;
|
5 |
+
use WPStaging\WPStaging;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Cloning
|
9 |
+
* @package WPStaging\Backend\Modules\Jobs
|
10 |
+
*/
|
11 |
+
class Updating extends Job
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Initialize is called in \Job
|
15 |
+
*/
|
16 |
+
public function initialize(){
|
17 |
+
$this->db = WPStaging::getInstance()->get("wpdb");
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Save Chosen Cloning Settings
|
22 |
+
* @return bool
|
23 |
+
*/
|
24 |
+
public function save()
|
25 |
+
{
|
26 |
+
if (!isset($_POST) || !isset($_POST["cloneID"]))
|
27 |
+
{
|
28 |
+
return false;
|
29 |
+
}
|
30 |
+
|
31 |
+
// Generate Options
|
32 |
+
// Clone
|
33 |
+
$this->options->clone = $_POST["cloneID"];
|
34 |
+
$this->options->cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($this->options->clone));
|
35 |
+
$this->options->cloneNumber = 1;
|
36 |
+
$this->options->includedDirectories = array();
|
37 |
+
$this->options->excludedDirectories = array();
|
38 |
+
$this->options->extraDirectories = array();
|
39 |
+
$this->options->excludedFiles = array('.htaccess', '.DS_Store', '.git', '.svn', '.tmp', 'desktop.ini', '.gitignore', '.log');
|
40 |
+
|
41 |
+
// Define mainJob to differentiate between cloning, updating and pushing
|
42 |
+
$this->options->mainJob = 'updating';
|
43 |
+
|
44 |
+
// Job
|
45 |
+
$this->options->job = new \stdClass();
|
46 |
+
|
47 |
+
// Check if clone data already exists and use that one
|
48 |
+
if (isset($this->options->existingClones[$this->options->clone]) )
|
49 |
+
{
|
50 |
+
$this->options->cloneNumber = $this->options->existingClones[$this->options->clone]['number'];
|
51 |
+
$this->options->prefix = $this->getStagingPrefix();
|
52 |
+
} else {
|
53 |
+
wp_die('Fatal Error: Can not update clone because there is no clone data.');
|
54 |
+
}
|
55 |
+
|
56 |
+
|
57 |
+
// Excluded Tables
|
58 |
+
// if (isset($_POST["excludedTables"]) && is_array($_POST["excludedTables"]))
|
59 |
+
// {
|
60 |
+
// $this->options->excludedTables = $_POST["excludedTables"];
|
61 |
+
// }
|
62 |
+
// Included Tables
|
63 |
+
if (isset($_POST["includedTables"]) && is_array($_POST["includedTables"]))
|
64 |
+
{
|
65 |
+
$this->options->tables = $_POST["includedTables"];
|
66 |
+
} else {
|
67 |
+
$this->options->tables = array();
|
68 |
+
}
|
69 |
+
|
70 |
+
// Excluded Directories
|
71 |
+
if (isset($_POST["excludedDirectories"]) && is_array($_POST["excludedDirectories"]))
|
72 |
+
{
|
73 |
+
$this->options->excludedDirectories = $_POST["excludedDirectories"];
|
74 |
+
}
|
75 |
+
|
76 |
+
// Excluded Directories TOTAL
|
77 |
+
// Do not copy these folders and plugins
|
78 |
+
$excludedDirectories = array(
|
79 |
+
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'cache',
|
80 |
+
ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'wps-hide-login'
|
81 |
+
);
|
82 |
+
|
83 |
+
$this->options->excludedDirectories = array_merge($excludedDirectories, $this->options->excludedDirectories);
|
84 |
+
|
85 |
+
// Included Directories
|
86 |
+
if (isset($_POST["includedDirectories"]) && is_array($_POST["includedDirectories"]))
|
87 |
+
{
|
88 |
+
$this->options->includedDirectories = $_POST["includedDirectories"];
|
89 |
+
}
|
90 |
+
|
91 |
+
// Extra Directories
|
92 |
+
if (isset($_POST["extraDirectories"]) && !empty($_POST["extraDirectories"]) )
|
93 |
+
{
|
94 |
+
$this->options->extraDirectories = $_POST["extraDirectories"];
|
95 |
+
}
|
96 |
+
|
97 |
+
// Directories to Copy
|
98 |
+
$this->options->directoriesToCopy = array_merge(
|
99 |
+
$this->options->includedDirectories,
|
100 |
+
$this->options->extraDirectories
|
101 |
+
);
|
102 |
+
|
103 |
+
array_unshift($this->options->directoriesToCopy, ABSPATH);
|
104 |
+
|
105 |
+
// Delete files to copy listing
|
106 |
+
$this->cache->delete("files_to_copy");
|
107 |
+
|
108 |
+
return $this->saveOptions();
|
109 |
+
}
|
110 |
+
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Check and return prefix of the staging site
|
114 |
+
*/
|
115 |
+
public function getStagingPrefix() {
|
116 |
+
// prefix not defined! Happens if staging site has ben generated with older version of wpstg
|
117 |
+
// Try to get staging prefix from wp-config.php of staging site
|
118 |
+
$this->options->prefix = $this->options->existingClones[$this->options->clone]['prefix'];
|
119 |
+
if (empty($this->options->prefix)) {
|
120 |
+
// Throw error if wp-config.php is not readable
|
121 |
+
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
|
122 |
+
if (false === ($content = @file_get_contents($path))) {
|
123 |
+
$this->log("Can not open {$path}. Can't read contents", Logger::TYPE_ERROR);
|
124 |
+
$this->returnException("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
125 |
+
wp_die("Fatal Error: Can not read {$path} to get correct table prefix. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
126 |
+
} else {
|
127 |
+
// Get prefix from wp-config.php
|
128 |
+
preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
|
129 |
+
|
130 |
+
if (!empty($matches[1])) {
|
131 |
+
$this->options->prefix = $matches[1];
|
132 |
+
} else {
|
133 |
+
$this->returnException("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
134 |
+
wp_die("Fatal Error: Can not detect prefix from {$path}. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
135 |
+
}
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
// Die() if staging prefix is the same as the live prefix
|
140 |
+
if ($this->db->prefix == $this->options->prefix) {
|
141 |
+
$this->log("Fatal Error: Can not updatte staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
142 |
+
wp_die("Fatal Error: Can not update staging site. Prefix. '{$this->options->prefix}' is used for the live site. Stopping for security reasons. Deleting this staging site and creating a new one could fix this issue. Otherwise contact us support@wp-staging.com");
|
143 |
+
}
|
144 |
+
|
145 |
+
// Else
|
146 |
+
return $this->options->prefix;
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Start the cloning job
|
151 |
+
*/
|
152 |
+
public function start()
|
153 |
+
{
|
154 |
+
if (null === $this->options->currentJob)
|
155 |
+
{
|
156 |
+
$this->log("Cloning job for {$this->options->clone} finished");
|
157 |
+
return true;
|
158 |
+
}
|
159 |
+
|
160 |
+
$methodName = "job" . ucwords($this->options->currentJob);
|
161 |
+
|
162 |
+
if (!method_exists($this, $methodName))
|
163 |
+
{
|
164 |
+
$this->log("Can't execute job; Job's method {$methodName} is not found");
|
165 |
+
throw new JobNotFoundException($methodName);
|
166 |
+
}
|
167 |
+
|
168 |
+
// Call the job
|
169 |
+
//$this->log("execute job: Job's method {$methodName}");
|
170 |
+
return $this->{$methodName}();
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* @param object $response
|
175 |
+
* @param string $nextJob
|
176 |
+
* @return object
|
177 |
+
*/
|
178 |
+
private function handleJobResponse($response, $nextJob)
|
179 |
+
{
|
180 |
+
// Job is not done
|
181 |
+
if (true !== $response->status)
|
182 |
+
{
|
183 |
+
return $response;
|
184 |
+
}
|
185 |
+
|
186 |
+
$this->options->currentJob = $nextJob;
|
187 |
+
$this->options->currentStep = 0;
|
188 |
+
$this->options->totalSteps = 0;
|
189 |
+
|
190 |
+
// Save options
|
191 |
+
$this->saveOptions();
|
192 |
+
|
193 |
+
return $response;
|
194 |
+
}
|
195 |
+
|
196 |
+
|
197 |
+
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Clone Database
|
201 |
+
* @return object
|
202 |
+
*/
|
203 |
+
public function jobDatabase()
|
204 |
+
{
|
205 |
+
$database = new Database();
|
206 |
+
return $this->handleJobResponse($database->start(), "directories");
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Get All Files From Selected Directories Recursively Into a File
|
211 |
+
* @return object
|
212 |
+
*/
|
213 |
+
public function jobDirectories()
|
214 |
+
{
|
215 |
+
$directories = new Directories();
|
216 |
+
return $this->handleJobResponse($directories->start(), "files");
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Copy Files
|
221 |
+
* @return object
|
222 |
+
*/
|
223 |
+
public function jobFiles()
|
224 |
+
{
|
225 |
+
$files = new Files();
|
226 |
+
return $this->handleJobResponse($files->start(), "data");
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Replace Data
|
231 |
+
* @return object
|
232 |
+
*/
|
233 |
+
public function jobData()
|
234 |
+
{
|
235 |
+
$data = new Data();
|
236 |
+
return $this->handleJobResponse($data->start(), "finish");
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Save Clone Data
|
241 |
+
* @return object
|
242 |
+
*/
|
243 |
+
public function jobFinish()
|
244 |
+
{
|
245 |
+
$finish = new Finish();
|
246 |
+
return $this->handleJobResponse($finish->start(), '');
|
247 |
+
}
|
248 |
}
|
apps/Backend/Modules/SystemInfo.php
CHANGED
@@ -1,502 +1,502 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules;
|
4 |
-
|
5 |
-
use WPStaging\DI\InjectionAware;
|
6 |
-
use WPStaging\Library\Browser;
|
7 |
-
use WPStaging\WPStaging;
|
8 |
-
use WPStaging\Utils;
|
9 |
-
|
10 |
-
// No Direct Access
|
11 |
-
if( !defined( "WPINC" ) ) {
|
12 |
-
die;
|
13 |
-
}
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Class SystemInfo
|
17 |
-
* @package WPStaging\Backend\Modules
|
18 |
-
*/
|
19 |
-
class SystemInfo extends InjectionAware {
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @var bool
|
23 |
-
*/
|
24 |
-
private $isMultiSite;
|
25 |
-
|
26 |
-
/**
|
27 |
-
*
|
28 |
-
* @var obj
|
29 |
-
*/
|
30 |
-
private $helper;
|
31 |
-
|
32 |
-
/**
|
33 |
-
* Initialize class
|
34 |
-
*/
|
35 |
-
public function initialize() {
|
36 |
-
$this->isMultiSite = is_multisite();
|
37 |
-
$this->helper = new Utils\Helper();
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Magic method
|
42 |
-
* @return string
|
43 |
-
*/
|
44 |
-
public function __toString() {
|
45 |
-
return $this->get();
|
46 |
-
}
|
47 |
-
|
48 |
-
/**
|
49 |
-
* Get System Information as text
|
50 |
-
* @return string
|
51 |
-
*/
|
52 |
-
public function get() {
|
53 |
-
$output = "### Begin System Info ###" . PHP_EOL . PHP_EOL;
|
54 |
-
|
55 |
-
$output .= $this->wpstaging();
|
56 |
-
|
57 |
-
$output .= $this->site();
|
58 |
-
|
59 |
-
$output .= $this->browser();
|
60 |
-
|
61 |
-
$output .= $this->wp();
|
62 |
-
|
63 |
-
$output .= $this->plugins();
|
64 |
-
|
65 |
-
$output .= $this->multiSitePlugins();
|
66 |
-
|
67 |
-
$output .= $this->server();
|
68 |
-
|
69 |
-
$output .= $this->php();
|
70 |
-
|
71 |
-
$output .= $this->phpExtensions();
|
72 |
-
|
73 |
-
$output .= PHP_EOL . "### End System Info ###";
|
74 |
-
|
75 |
-
return $output;
|
76 |
-
}
|
77 |
-
|
78 |
-
/**
|
79 |
-
* @param string $string
|
80 |
-
* @return string
|
81 |
-
*/
|
82 |
-
public function header( $string ) {
|
83 |
-
return PHP_EOL . "-- {$string}" . PHP_EOL . PHP_EOL;
|
84 |
-
}
|
85 |
-
|
86 |
-
/**
|
87 |
-
* Formating title and the value
|
88 |
-
* @param string $title
|
89 |
-
* @param string $value
|
90 |
-
* @return string
|
91 |
-
*/
|
92 |
-
public function info( $title, $value ) {
|
93 |
-
return str_pad( $title, 56, ' ', STR_PAD_RIGHT ) . $value . PHP_EOL;
|
94 |
-
}
|
95 |
-
|
96 |
-
/**
|
97 |
-
* Theme Information
|
98 |
-
* @return string
|
99 |
-
*/
|
100 |
-
public function theme() {
|
101 |
-
// Versions earlier than 3.4
|
102 |
-
if( get_bloginfo( "version" ) < "3.4" ) {
|
103 |
-
$themeData = get_theme_data( get_stylesheet_directory() . "/style.css" );
|
104 |
-
return "{$themeData["Name"]} {$themeData["Version"]}";
|
105 |
-
}
|
106 |
-
|
107 |
-
$themeData = wp_get_theme();
|
108 |
-
return "{$themeData->Name} {$themeData->Version}";
|
109 |
-
}
|
110 |
-
|
111 |
-
/**
|
112 |
-
* Site Information
|
113 |
-
* @return string
|
114 |
-
*/
|
115 |
-
public function site() {
|
116 |
-
$output = "-- Site Info" . PHP_EOL . PHP_EOL;
|
117 |
-
$output .= $this->info( "Site URL:", site_url() );
|
118 |
-
$output .= $this->info( "Home URL:", $this->helper->get_home_url() );
|
119 |
-
$output .= $this->info( "Home Path:", get_home_path() );
|
120 |
-
$output .= $this->info( "ABSPATH:", ABSPATH );
|
121 |
-
$output .= $this->info( "Installed in subdir:", ( $this->isSubDir() ? 'Yes' : 'No' ) );
|
122 |
-
$output .= $this->info( "Multisite:", ($this->isMultiSite ? "Yes" : "No" ) );
|
123 |
-
$output .= $this->info( "Multisite Blog ID:", get_current_blog_id() );
|
124 |
-
|
125 |
-
return apply_filters( "wpstg_sysinfo_after_site_info", $output );
|
126 |
-
}
|
127 |
-
|
128 |
-
/**
|
129 |
-
* Wp Staging plugin Information
|
130 |
-
* @return string
|
131 |
-
*/
|
132 |
-
public function wpstaging() {
|
133 |
-
// Get wpstg settings
|
134 |
-
$settings = ( object ) get_option( 'wpstg_settings', array() );
|
135 |
-
|
136 |
-
// Clones data < 1.1.6.x
|
137 |
-
$clones = ( object ) get_option( 'wpstg_existing_clones', array() );
|
138 |
-
// Clones data version > 2.x
|
139 |
-
$clonesBeta = get_option( 'wpstg_existing_clones_beta' );
|
140 |
-
|
141 |
-
|
142 |
-
$output = "-- WP Staging Settings" . PHP_EOL . PHP_EOL;
|
143 |
-
$output .= $this->info( "Query Limit:", isset( $settings->queryLimit ) ? $settings->queryLimit : 'undefined' );
|
144 |
-
$output .= $this->info( "File Copy Limit:", isset( $settings->fileLimit ) ? $settings->fileLimit : 'undefined' );
|
145 |
-
$output .= $this->info( "Batch Size:", isset( $settings->batchSize ) ? $settings->batchSize : 'undefined' );
|
146 |
-
$output .= $this->info( "CPU Load:", isset( $settings->cpuLoad ) ? $settings->cpuLoad : 'undefined' );
|
147 |
-
$output .= $this->info( "WP in Subdir:", isset( $settings->wpSubDirectory ) ? $settings->wpSubDirectory : 'false' );
|
148 |
-
$output .= $this->info( "Login Custom Link:", isset( $settings->loginSlug ) ? $settings->loginSlug : 'false' );
|
149 |
-
|
150 |
-
$output .= PHP_EOL . PHP_EOL . "-- Available Sites Version < 1.1.6.x" . PHP_EOL . PHP_EOL;
|
151 |
-
|
152 |
-
$i = 1;
|
153 |
-
foreach ( $clones as $key => $value ) {
|
154 |
-
$output .= $this->info( "Site name & subfolder :", $value );
|
155 |
-
}
|
156 |
-
$output .= PHP_EOL . PHP_EOL . "-- Available Sites Version > 2.0.x" . PHP_EOL . PHP_EOL;
|
157 |
-
|
158 |
-
foreach ( $clonesBeta as $key => $clone ) {
|
159 |
-
$output .= $this->info( "Number:", isset( $clone['number'] ) ? $clone['number'] : 'undefined' );
|
160 |
-
$output .= $this->info( "directoryName:", isset( $clone['directoryName'] ) ? $clone['directoryName'] : 'undefined' );
|
161 |
-
$output .= $this->info( "Path:", isset( $clone['path'] ) ? $clone['path'] : 'undefined' );
|
162 |
-
$output .= $this->info( "URL:", isset( $clone['url'] ) ? $clone['url'] : 'undefined' );
|
163 |
-
$output .= $this->info( "DB Prefix:", isset( $clone['prefix'] ) ? $clone['prefix'] : 'undefined' );
|
164 |
-
$output .= $this->info( "DB Prefix wp-config.php:", $this->getStagingPrefix( $clone ) );
|
165 |
-
$output .= $this->info( "Version:", isset( $clone['version'] ) ? $clone['version'] : 'undefined' ) . PHP_EOL . PHP_EOL;
|
166 |
-
}
|
167 |
-
|
168 |
-
|
169 |
-
$output .= $this->info( "Raw Clones Data:", json_encode( get_option( 'wpstg_existing_clones_beta', 'undefined' ) ) );
|
170 |
-
|
171 |
-
$output .= '' . PHP_EOL;
|
172 |
-
|
173 |
-
|
174 |
-
//$output .= PHP_EOL . PHP_EOL;
|
175 |
-
|
176 |
-
$output .= $this->info( "Plugin Version:", get_option( 'wpstg_version', 'undefined' ) );
|
177 |
-
$output .= $this->info( "Install Date:", get_option( 'wpstg_installDate', 'undefined' ) );
|
178 |
-
$output .= $this->info( "Upgraded from:", get_option( 'wpstg_version_upgraded_from', 'undefined' ) );
|
179 |
-
$output .= $this->info( "Is Staging Site:", get_option( 'wpstg_is_staging_site', 'undefined' ) ) . PHP_EOL . PHP_EOL;
|
180 |
-
|
181 |
-
|
182 |
-
return apply_filters( "wpstg_sysinfo_after_wpstaging_info", $output );
|
183 |
-
}
|
184 |
-
|
185 |
-
/**
|
186 |
-
* Browser Information
|
187 |
-
* @return string
|
188 |
-
*/
|
189 |
-
public function browser() {
|
190 |
-
$output = $this->header( "User Browser" );
|
191 |
-
$output .= (new Browser);
|
192 |
-
|
193 |
-
return apply_filters( "wpstg_sysinfo_after_user_browser", $output );
|
194 |
-
}
|
195 |
-
|
196 |
-
/**
|
197 |
-
* Frontpage Information when frontpage is set to "page"
|
198 |
-
* @return string
|
199 |
-
*/
|
200 |
-
public function frontPage() {
|
201 |
-
if( get_option( "show_on_front" ) !== "page" ) {
|
202 |
-
return '';
|
203 |
-
}
|
204 |
-
|
205 |
-
$frontPageID = get_option( "page_on_front" );
|
206 |
-
$blogPageID = get_option( "page_for_posts" );
|
207 |
-
|
208 |
-
// Front Page
|
209 |
-
$pageFront = ($frontPageID != 0) ? get_the_title( $frontPageID ) . " (#{$frontPageID})" : "Unset";
|
210 |
-
// Blog Page ID
|
211 |
-
$pageBlog = ($blogPageID != 0) ? get_the_title( $blogPageID ) . " (#{$blogPageID})" : "Unset";
|
212 |
-
|
213 |
-
$output = $this->info( "Page On Front:", $pageFront );
|
214 |
-
$output .= $this->info( "Page For Posts:", $pageBlog );
|
215 |
-
|
216 |
-
return $output;
|
217 |
-
}
|
218 |
-
|
219 |
-
/**
|
220 |
-
* Check wp_remote_post() functionality
|
221 |
-
* @return string
|
222 |
-
*/
|
223 |
-
public function wpRemotePost() {
|
224 |
-
// Make sure wp_remote_post() is working
|
225 |
-
$wpRemotePost = "wp_remote_post() does not work";
|
226 |
-
|
227 |
-
// Send request
|
228 |
-
$response = wp_remote_post(
|
229 |
-
"https://www.paypal.com/cgi-bin/webscr", array(
|
230 |
-
"sslverify" => false,
|
231 |
-
"timeout" => 60,
|
232 |
-
"user-agent" => "WPSTG/" . WPStaging::VERSION,
|
233 |
-
"body" => array("cmd" => "_notify-validate")
|
234 |
-
)
|
235 |
-
);
|
236 |
-
|
237 |
-
// Validate it worked
|
238 |
-
if( !is_wp_error( $response ) && 200 <= $response["response"]["code"] && 300 > $response["response"]["code"] ) {
|
239 |
-
$wpRemotePost = "wp_remote_post() works";
|
240 |
-
}
|
241 |
-
|
242 |
-
return $this->info( "Remote Post:", $wpRemotePost );
|
243 |
-
}
|
244 |
-
|
245 |
-
/**
|
246 |
-
* WordPress Configuration
|
247 |
-
* @return string
|
248 |
-
*/
|
249 |
-
public function wp() {
|
250 |
-
$output = $this->header( "WordPress Configuration" );
|
251 |
-
$output .= $this->info( "Version:", get_bloginfo( "version" ) );
|
252 |
-
$output .= $this->info( "Language:", (defined( "WPLANG" ) && WPLANG) ? WPLANG : "en_US" );
|
253 |
-
|
254 |
-
$permalinkStructure = get_option( "permalink_structure" );
|
255 |
-
;
|
256 |
-
$output .= $this->info( "Permalink Structure:", ($permalinkStructure) ? $permalinkStructure : "Default" );
|
257 |
-
|
258 |
-
$output .= $this->info( "Active Theme:", $this->theme() );
|
259 |
-
$output .= $this->info( "Show On Front:", get_option( "show_on_front" ) );
|
260 |
-
|
261 |
-
// Frontpage information
|
262 |
-
$output .= $this->frontPage();
|
263 |
-
|
264 |
-
// WP Remote Post
|
265 |
-
$output .= $this->wpRemotePost();
|
266 |
-
|
267 |
-
// Table Prefix
|
268 |
-
$wpDB = $this->di->get( "wpdb" );
|
269 |
-
$tablePrefix = "DB Prefix: " . $wpDB->prefix . ' ';
|
270 |
-
$tablePrefix .= "Length: " . strlen( $wpDB->prefix ) . " Status: ";
|
271 |
-
$tablePrefix .= (strlen( $wpDB->prefix ) > 16) ? " ERROR: Too long" : " Acceptable";
|
272 |
-
|
273 |
-
$output .= $this->info( "Table Prefix:", $tablePrefix );
|
274 |
-
|
275 |
-
// Constants
|
276 |
-
$output .= $this->info( "WP Content Path:", WP_CONTENT_DIR );
|
277 |
-
$output .= $this->info( "WP Plugin Dir:", WP_PLUGIN_DIR );
|
278 |
-
if (defined('UPLOADS'))
|
279 |
-
$output .= $this->info( "WP UPLOADS CONST:", UPLOADS );
|
280 |
-
$uploads = wp_upload_dir();
|
281 |
-
$output .= $this->info( "WP Uploads Dir:", $uploads['basedir'] );
|
282 |
-
if (defined('WP_TEMP_DIR'))
|
283 |
-
$output .= $this->info( "WP Temp Dir:", WP_TEMP_DIR );
|
284 |
-
|
285 |
-
// WP Debug
|
286 |
-
$output .= $this->info( "WP_DEBUG:", (defined( "WP_DEBUG" )) ? WP_DEBUG ? "Enabled" : "Disabled" : "Not set" );
|
287 |
-
$output .= $this->info( "Memory Limit:", WP_MEMORY_LIMIT );
|
288 |
-
$output .= $this->info( "Registered Post Stati:", implode( ", ", \get_post_stati() ) );
|
289 |
-
|
290 |
-
return apply_filters( "wpstg_sysinfo_after_wpstg_config", $output );
|
291 |
-
}
|
292 |
-
|
293 |
-
/**
|
294 |
-
* List of Active Plugins
|
295 |
-
* @param array $plugins
|
296 |
-
* @param array $activePlugins
|
297 |
-
* @return string
|
298 |
-
*/
|
299 |
-
public function activePlugins( $plugins, $activePlugins ) {
|
300 |
-
$output = $this->header( "WordPress Active Plugins" );
|
301 |
-
|
302 |
-
foreach ( $plugins as $path => $plugin ) {
|
303 |
-
if( !in_array( $path, $activePlugins ) ) {
|
304 |
-
continue;
|
305 |
-
}
|
306 |
-
|
307 |
-
$output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
|
308 |
-
}
|
309 |
-
|
310 |
-
return apply_filters( "wpstg_sysinfo_after_wordpress_plugins", $output );
|
311 |
-
}
|
312 |
-
|
313 |
-
/**
|
314 |
-
* List of Inactive Plugins
|
315 |
-
* @param array $plugins
|
316 |
-
* @param array $activePlugins
|
317 |
-
* @return string
|
318 |
-
*/
|
319 |
-
public function inactivePlugins( $plugins, $activePlugins ) {
|
320 |
-
$output = $this->header( "WordPress Inactive Plugins" );
|
321 |
-
|
322 |
-
foreach ( $plugins as $path => $plugin ) {
|
323 |
-
if( in_array( $path, $activePlugins ) ) {
|
324 |
-
continue;
|
325 |
-
}
|
326 |
-
|
327 |
-
$output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
|
328 |
-
}
|
329 |
-
|
330 |
-
return apply_filters( "wpstg_sysinfo_after_wordpress_plugins_inactive", $output );
|
331 |
-
}
|
332 |
-
|
333 |
-
/**
|
334 |
-
* Get list of active and inactive plugins
|
335 |
-
* @return string
|
336 |
-
*/
|
337 |
-
public function plugins() {
|
338 |
-
// Get plugins and active plugins
|
339 |
-
$plugins = get_plugins();
|
340 |
-
$activePlugins = get_option( "active_plugins", array() );
|
341 |
-
|
342 |
-
// Active plugins
|
343 |
-
$output = $this->activePlugins( $plugins, $activePlugins );
|
344 |
-
$output .= $this->inactivePlugins( $plugins, $activePlugins );
|
345 |
-
|
346 |
-
return $output;
|
347 |
-
}
|
348 |
-
|
349 |
-
/**
|
350 |
-
* Multisite Plugins
|
351 |
-
* @return string
|
352 |
-
*/
|
353 |
-
public function multiSitePlugins() {
|
354 |
-
if( !$this->isMultiSite ) {
|
355 |
-
return '';
|
356 |
-
}
|
357 |
-
|
358 |
-
$output = $this->header( "Network Active Plugins" );
|
359 |
-
|
360 |
-
$plugins = wp_get_active_network_plugins();
|
361 |
-
$activePlugins = get_site_option( "active_sitewide_plugins", array() );
|
362 |
-
|
363 |
-
foreach ( $plugins as $pluginPath ) {
|
364 |
-
$pluginBase = plugin_basename( $pluginPath );
|
365 |
-
|
366 |
-
if( !array_key_exists( $pluginBase, $activePlugins ) ) {
|
367 |
-
continue;
|
368 |
-
}
|
369 |
-
|
370 |
-
$plugin = get_plugin_data( $pluginPath );
|
371 |
-
|
372 |
-
$output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
|
373 |
-
}
|
374 |
-
unset( $plugins, $activePlugins );
|
375 |
-
|
376 |
-
return $output;
|
377 |
-
}
|
378 |
-
|
379 |
-
/**
|
380 |
-
* Server Information
|
381 |
-
* @return string
|
382 |
-
*/
|
383 |
-
public function server() {
|
384 |
-
// Server Configuration
|
385 |
-
$output = $this->header( "Webserver Configuration" );
|
386 |
-
|
387 |
-
$output .= $this->info( "PHP Version:", PHP_VERSION );
|
388 |
-
$output .= $this->info( "MySQL Version:", $this->di->get( "wpdb" )->db_version() );
|
389 |
-
$output .= $this->info( "Webserver Info:", $_SERVER["SERVER_SOFTWARE"] );
|
390 |
-
|
391 |
-
return apply_filters( "wpstg_sysinfo_after_webserver_config", $output );
|
392 |
-
}
|
393 |
-
|
394 |
-
/**
|
395 |
-
* PHP Configuration
|
396 |
-
* @return string
|
397 |
-
*/
|
398 |
-
public function php() {
|
399 |
-
$output = $this->header( "PHP Configuration" );
|
400 |
-
$output .= $this->info( "Safe Mode:", ($this->isSafeModeEnabled() ? "Enabled" : "Disabled" ) );
|
401 |
-
$output .= $this->info( "Memory Limit:", ini_get( "memory_limit" ) );
|
402 |
-
$output .= $this->info( "Upload Max Size:", ini_get( "upload_max_filesize" ) );
|
403 |
-
$output .= $this->info( "Post Max Size:", ini_get( "post_max_size" ) );
|
404 |
-
$output .= $this->info( "Upload Max Filesize:", ini_get( "upload_max_filesize" ) );
|
405 |
-
$output .= $this->info( "Time Limit:", ini_get( "max_execution_time" ) );
|
406 |
-
$output .= $this->info( "Max Input Vars:", ini_get( "max_input_vars" ) );
|
407 |
-
|
408 |
-
$displayErrors = ini_get( "display_errors" );
|
409 |
-
$output .= $this->info( "Display Errors:", ($displayErrors) ? "On ({$displayErrors})" : "N/A" );
|
410 |
-
|
411 |
-
return apply_filters( "wpstg_sysinfo_after_php_config", $output );
|
412 |
-
}
|
413 |
-
|
414 |
-
/**
|
415 |
-
* Check if PHP is on Safe Mode
|
416 |
-
* @return bool
|
417 |
-
*/
|
418 |
-
public function isSafeModeEnabled() {
|
419 |
-
return (
|
420 |
-
version_compare( PHP_VERSION, "5.4.0", '<' ) &&
|
421 |
-
@ini_get( "safe_mode" )
|
422 |
-
);
|
423 |
-
}
|
424 |
-
|
425 |
-
/**
|
426 |
-
* Checks if function exists or not
|
427 |
-
* @param string $functionName
|
428 |
-
* @return string
|
429 |
-
*/
|
430 |
-
public function isSupported( $functionName ) {
|
431 |
-
return (function_exists( $functionName )) ? "Supported" : "Not Supported";
|
432 |
-
}
|
433 |
-
|
434 |
-
/**
|
435 |
-
* Checks if class or extension is loaded / exists to determine if it is installed or not
|
436 |
-
* @param string $name
|
437 |
-
* @param bool $isClass
|
438 |
-
* @return string
|
439 |
-
*/
|
440 |
-
public function isInstalled( $name, $isClass = true ) {
|
441 |
-
if( true === $isClass ) {
|
442 |
-
return (class_exists( $name )) ? "Installed" : "Not Installed";
|
443 |
-
} else {
|
444 |
-
return (extension_loaded( $name )) ? "Installed" : "Not Installed";
|
445 |
-
}
|
446 |
-
}
|
447 |
-
|
448 |
-
/**
|
449 |
-
* Gets Installed Important PHP Extensions
|
450 |
-
* @return string
|
451 |
-
*/
|
452 |
-
public function phpExtensions() {
|
453 |
-
// Important PHP Extensions
|
454 |
-
$output = $this->header( "PHP Extensions" );
|
455 |
-
$output .= $this->info( "cURL:", $this->isSupported( "curl_init" ) );
|
456 |
-
$output .= $this->info( "fsockopen:", $this->isSupported( "fsockopen" ) );
|
457 |
-
$output .= $this->info( "SOAP Client:", $this->isInstalled( "SoapClient" ) );
|
458 |
-
$output .= $this->info( "Suhosin:", $this->isInstalled( "suhosin", false ) );
|
459 |
-
|
460 |
-
return apply_filters( "wpstg_sysinfo_after_php_ext", $output );
|
461 |
-
}
|
462 |
-
|
463 |
-
/**
|
464 |
-
* Check if WP is installed in subdir
|
465 |
-
* @return boolean
|
466 |
-
*/
|
467 |
-
private function isSubDir() {
|
468 |
-
if( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
|
469 |
-
return true;
|
470 |
-
}
|
471 |
-
return false;
|
472 |
-
}
|
473 |
-
|
474 |
-
/**
|
475 |
-
* Check and return prefix of the staging site
|
476 |
-
*/
|
477 |
-
/**
|
478 |
-
* Try to get the staging prefix from wp-config.php of staging site
|
479 |
-
* @param array $clone
|
480 |
-
* @return sting
|
481 |
-
*/
|
482 |
-
private function getStagingPrefix( $clone = array() ) {
|
483 |
-
// Throw error
|
484 |
-
$path = ABSPATH . $clone['directoryName'] . "/wp-config.php";
|
485 |
-
if( false === ($content = @file_get_contents( $path )) ) {
|
486 |
-
return 'Can\'t find staging wp-config.php';
|
487 |
-
} else {
|
488 |
-
|
489 |
-
// Get prefix from wp-config.php
|
490 |
-
//preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
|
491 |
-
preg_match( "/table_prefix\s*=\s*'(\w*)';/", $content, $matches );
|
492 |
-
//wp_die(var_dump($matches));
|
493 |
-
|
494 |
-
if( !empty( $matches[1] ) ) {
|
495 |
-
return $matches[1];
|
496 |
-
} else {
|
497 |
-
return 'No table_prefix in wp-config.php';
|
498 |
-
}
|
499 |
-
}
|
500 |
-
}
|
501 |
-
|
502 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules;
|
4 |
+
|
5 |
+
use WPStaging\DI\InjectionAware;
|
6 |
+
use WPStaging\Library\Browser;
|
7 |
+
use WPStaging\WPStaging;
|
8 |
+
use WPStaging\Utils;
|
9 |
+
|
10 |
+
// No Direct Access
|
11 |
+
if( !defined( "WPINC" ) ) {
|
12 |
+
die;
|
13 |
+
}
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Class SystemInfo
|
17 |
+
* @package WPStaging\Backend\Modules
|
18 |
+
*/
|
19 |
+
class SystemInfo extends InjectionAware {
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var bool
|
23 |
+
*/
|
24 |
+
private $isMultiSite;
|
25 |
+
|
26 |
+
/**
|
27 |
+
*
|
28 |
+
* @var obj
|
29 |
+
*/
|
30 |
+
private $helper;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Initialize class
|
34 |
+
*/
|
35 |
+
public function initialize() {
|
36 |
+
$this->isMultiSite = is_multisite();
|
37 |
+
$this->helper = new Utils\Helper();
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Magic method
|
42 |
+
* @return string
|
43 |
+
*/
|
44 |
+
public function __toString() {
|
45 |
+
return $this->get();
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Get System Information as text
|
50 |
+
* @return string
|
51 |
+
*/
|
52 |
+
public function get() {
|
53 |
+
$output = "### Begin System Info ###" . PHP_EOL . PHP_EOL;
|
54 |
+
|
55 |
+
$output .= $this->wpstaging();
|
56 |
+
|
57 |
+
$output .= $this->site();
|
58 |
+
|
59 |
+
$output .= $this->browser();
|
60 |
+
|
61 |
+
$output .= $this->wp();
|
62 |
+
|
63 |
+
$output .= $this->plugins();
|
64 |
+
|
65 |
+
$output .= $this->multiSitePlugins();
|
66 |
+
|
67 |
+
$output .= $this->server();
|
68 |
+
|
69 |
+
$output .= $this->php();
|
70 |
+
|
71 |
+
$output .= $this->phpExtensions();
|
72 |
+
|
73 |
+
$output .= PHP_EOL . "### End System Info ###";
|
74 |
+
|
75 |
+
return $output;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* @param string $string
|
80 |
+
* @return string
|
81 |
+
*/
|
82 |
+
public function header( $string ) {
|
83 |
+
return PHP_EOL . "-- {$string}" . PHP_EOL . PHP_EOL;
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Formating title and the value
|
88 |
+
* @param string $title
|
89 |
+
* @param string $value
|
90 |
+
* @return string
|
91 |
+
*/
|
92 |
+
public function info( $title, $value ) {
|
93 |
+
return str_pad( $title, 56, ' ', STR_PAD_RIGHT ) . $value . PHP_EOL;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Theme Information
|
98 |
+
* @return string
|
99 |
+
*/
|
100 |
+
public function theme() {
|
101 |
+
// Versions earlier than 3.4
|
102 |
+
if( get_bloginfo( "version" ) < "3.4" ) {
|
103 |
+
$themeData = get_theme_data( get_stylesheet_directory() . "/style.css" );
|
104 |
+
return "{$themeData["Name"]} {$themeData["Version"]}";
|
105 |
+
}
|
106 |
+
|
107 |
+
$themeData = wp_get_theme();
|
108 |
+
return "{$themeData->Name} {$themeData->Version}";
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Site Information
|
113 |
+
* @return string
|
114 |
+
*/
|
115 |
+
public function site() {
|
116 |
+
$output = "-- Site Info" . PHP_EOL . PHP_EOL;
|
117 |
+
$output .= $this->info( "Site URL:", site_url() );
|
118 |
+
$output .= $this->info( "Home URL:", $this->helper->get_home_url() );
|
119 |
+
$output .= $this->info( "Home Path:", get_home_path() );
|
120 |
+
$output .= $this->info( "ABSPATH:", ABSPATH );
|
121 |
+
$output .= $this->info( "Installed in subdir:", ( $this->isSubDir() ? 'Yes' : 'No' ) );
|
122 |
+
$output .= $this->info( "Multisite:", ($this->isMultiSite ? "Yes" : "No" ) );
|
123 |
+
$output .= $this->info( "Multisite Blog ID:", get_current_blog_id() );
|
124 |
+
|
125 |
+
return apply_filters( "wpstg_sysinfo_after_site_info", $output );
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Wp Staging plugin Information
|
130 |
+
* @return string
|
131 |
+
*/
|
132 |
+
public function wpstaging() {
|
133 |
+
// Get wpstg settings
|
134 |
+
$settings = ( object ) get_option( 'wpstg_settings', array() );
|
135 |
+
|
136 |
+
// Clones data < 1.1.6.x
|
137 |
+
$clones = ( object ) get_option( 'wpstg_existing_clones', array() );
|
138 |
+
// Clones data version > 2.x
|
139 |
+
$clonesBeta = get_option( 'wpstg_existing_clones_beta' );
|
140 |
+
|
141 |
+
|
142 |
+
$output = "-- WP Staging Settings" . PHP_EOL . PHP_EOL;
|
143 |
+
$output .= $this->info( "Query Limit:", isset( $settings->queryLimit ) ? $settings->queryLimit : 'undefined' );
|
144 |
+
$output .= $this->info( "File Copy Limit:", isset( $settings->fileLimit ) ? $settings->fileLimit : 'undefined' );
|
145 |
+
$output .= $this->info( "Batch Size:", isset( $settings->batchSize ) ? $settings->batchSize : 'undefined' );
|
146 |
+
$output .= $this->info( "CPU Load:", isset( $settings->cpuLoad ) ? $settings->cpuLoad : 'undefined' );
|
147 |
+
$output .= $this->info( "WP in Subdir:", isset( $settings->wpSubDirectory ) ? $settings->wpSubDirectory : 'false' );
|
148 |
+
$output .= $this->info( "Login Custom Link:", isset( $settings->loginSlug ) ? $settings->loginSlug : 'false' );
|
149 |
+
|
150 |
+
$output .= PHP_EOL . PHP_EOL . "-- Available Sites Version < 1.1.6.x" . PHP_EOL . PHP_EOL;
|
151 |
+
|
152 |
+
$i = 1;
|
153 |
+
foreach ( $clones as $key => $value ) {
|
154 |
+
$output .= $this->info( "Site name & subfolder :", $value );
|
155 |
+
}
|
156 |
+
$output .= PHP_EOL . PHP_EOL . "-- Available Sites Version > 2.0.x" . PHP_EOL . PHP_EOL;
|
157 |
+
|
158 |
+
foreach ( $clonesBeta as $key => $clone ) {
|
159 |
+
$output .= $this->info( "Number:", isset( $clone['number'] ) ? $clone['number'] : 'undefined' );
|
160 |
+
$output .= $this->info( "directoryName:", isset( $clone['directoryName'] ) ? $clone['directoryName'] : 'undefined' );
|
161 |
+
$output .= $this->info( "Path:", isset( $clone['path'] ) ? $clone['path'] : 'undefined' );
|
162 |
+
$output .= $this->info( "URL:", isset( $clone['url'] ) ? $clone['url'] : 'undefined' );
|
163 |
+
$output .= $this->info( "DB Prefix:", isset( $clone['prefix'] ) ? $clone['prefix'] : 'undefined' );
|
164 |
+
$output .= $this->info( "DB Prefix wp-config.php:", $this->getStagingPrefix( $clone ) );
|
165 |
+
$output .= $this->info( "Version:", isset( $clone['version'] ) ? $clone['version'] : 'undefined' ) . PHP_EOL . PHP_EOL;
|
166 |
+
}
|
167 |
+
|
168 |
+
|
169 |
+
$output .= $this->info( "Raw Clones Data:", json_encode( get_option( 'wpstg_existing_clones_beta', 'undefined' ) ) );
|
170 |
+
|
171 |
+
$output .= '' . PHP_EOL;
|
172 |
+
|
173 |
+
|
174 |
+
//$output .= PHP_EOL . PHP_EOL;
|
175 |
+
|
176 |
+
$output .= $this->info( "Plugin Version:", get_option( 'wpstg_version', 'undefined' ) );
|
177 |
+
$output .= $this->info( "Install Date:", get_option( 'wpstg_installDate', 'undefined' ) );
|
178 |
+
$output .= $this->info( "Upgraded from:", get_option( 'wpstg_version_upgraded_from', 'undefined' ) );
|
179 |
+
$output .= $this->info( "Is Staging Site:", get_option( 'wpstg_is_staging_site', 'undefined' ) ) . PHP_EOL . PHP_EOL;
|
180 |
+
|
181 |
+
|
182 |
+
return apply_filters( "wpstg_sysinfo_after_wpstaging_info", $output );
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Browser Information
|
187 |
+
* @return string
|
188 |
+
*/
|
189 |
+
public function browser() {
|
190 |
+
$output = $this->header( "User Browser" );
|
191 |
+
$output .= (new Browser);
|
192 |
+
|
193 |
+
return apply_filters( "wpstg_sysinfo_after_user_browser", $output );
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Frontpage Information when frontpage is set to "page"
|
198 |
+
* @return string
|
199 |
+
*/
|
200 |
+
public function frontPage() {
|
201 |
+
if( get_option( "show_on_front" ) !== "page" ) {
|
202 |
+
return '';
|
203 |
+
}
|
204 |
+
|
205 |
+
$frontPageID = get_option( "page_on_front" );
|
206 |
+
$blogPageID = get_option( "page_for_posts" );
|
207 |
+
|
208 |
+
// Front Page
|
209 |
+
$pageFront = ($frontPageID != 0) ? get_the_title( $frontPageID ) . " (#{$frontPageID})" : "Unset";
|
210 |
+
// Blog Page ID
|
211 |
+
$pageBlog = ($blogPageID != 0) ? get_the_title( $blogPageID ) . " (#{$blogPageID})" : "Unset";
|
212 |
+
|
213 |
+
$output = $this->info( "Page On Front:", $pageFront );
|
214 |
+
$output .= $this->info( "Page For Posts:", $pageBlog );
|
215 |
+
|
216 |
+
return $output;
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Check wp_remote_post() functionality
|
221 |
+
* @return string
|
222 |
+
*/
|
223 |
+
public function wpRemotePost() {
|
224 |
+
// Make sure wp_remote_post() is working
|
225 |
+
$wpRemotePost = "wp_remote_post() does not work";
|
226 |
+
|
227 |
+
// Send request
|
228 |
+
$response = wp_remote_post(
|
229 |
+
"https://www.paypal.com/cgi-bin/webscr", array(
|
230 |
+
"sslverify" => false,
|
231 |
+
"timeout" => 60,
|
232 |
+
"user-agent" => "WPSTG/" . WPStaging::VERSION,
|
233 |
+
"body" => array("cmd" => "_notify-validate")
|
234 |
+
)
|
235 |
+
);
|
236 |
+
|
237 |
+
// Validate it worked
|
238 |
+
if( !is_wp_error( $response ) && 200 <= $response["response"]["code"] && 300 > $response["response"]["code"] ) {
|
239 |
+
$wpRemotePost = "wp_remote_post() works";
|
240 |
+
}
|
241 |
+
|
242 |
+
return $this->info( "Remote Post:", $wpRemotePost );
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* WordPress Configuration
|
247 |
+
* @return string
|
248 |
+
*/
|
249 |
+
public function wp() {
|
250 |
+
$output = $this->header( "WordPress Configuration" );
|
251 |
+
$output .= $this->info( "Version:", get_bloginfo( "version" ) );
|
252 |
+
$output .= $this->info( "Language:", (defined( "WPLANG" ) && WPLANG) ? WPLANG : "en_US" );
|
253 |
+
|
254 |
+
$permalinkStructure = get_option( "permalink_structure" );
|
255 |
+
;
|
256 |
+
$output .= $this->info( "Permalink Structure:", ($permalinkStructure) ? $permalinkStructure : "Default" );
|
257 |
+
|
258 |
+
$output .= $this->info( "Active Theme:", $this->theme() );
|
259 |
+
$output .= $this->info( "Show On Front:", get_option( "show_on_front" ) );
|
260 |
+
|
261 |
+
// Frontpage information
|
262 |
+
$output .= $this->frontPage();
|
263 |
+
|
264 |
+
// WP Remote Post
|
265 |
+
$output .= $this->wpRemotePost();
|
266 |
+
|
267 |
+
// Table Prefix
|
268 |
+
$wpDB = $this->di->get( "wpdb" );
|
269 |
+
$tablePrefix = "DB Prefix: " . $wpDB->prefix . ' ';
|
270 |
+
$tablePrefix .= "Length: " . strlen( $wpDB->prefix ) . " Status: ";
|
271 |
+
$tablePrefix .= (strlen( $wpDB->prefix ) > 16) ? " ERROR: Too long" : " Acceptable";
|
272 |
+
|
273 |
+
$output .= $this->info( "Table Prefix:", $tablePrefix );
|
274 |
+
|
275 |
+
// Constants
|
276 |
+
$output .= $this->info( "WP Content Path:", WP_CONTENT_DIR );
|
277 |
+
$output .= $this->info( "WP Plugin Dir:", WP_PLUGIN_DIR );
|
278 |
+
if (defined('UPLOADS'))
|
279 |
+
$output .= $this->info( "WP UPLOADS CONST:", UPLOADS );
|
280 |
+
$uploads = wp_upload_dir();
|
281 |
+
$output .= $this->info( "WP Uploads Dir:", $uploads['basedir'] );
|
282 |
+
if (defined('WP_TEMP_DIR'))
|
283 |
+
$output .= $this->info( "WP Temp Dir:", WP_TEMP_DIR );
|
284 |
+
|
285 |
+
// WP Debug
|
286 |
+
$output .= $this->info( "WP_DEBUG:", (defined( "WP_DEBUG" )) ? WP_DEBUG ? "Enabled" : "Disabled" : "Not set" );
|
287 |
+
$output .= $this->info( "Memory Limit:", WP_MEMORY_LIMIT );
|
288 |
+
$output .= $this->info( "Registered Post Stati:", implode( ", ", \get_post_stati() ) );
|
289 |
+
|
290 |
+
return apply_filters( "wpstg_sysinfo_after_wpstg_config", $output );
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* List of Active Plugins
|
295 |
+
* @param array $plugins
|
296 |
+
* @param array $activePlugins
|
297 |
+
* @return string
|
298 |
+
*/
|
299 |
+
public function activePlugins( $plugins, $activePlugins ) {
|
300 |
+
$output = $this->header( "WordPress Active Plugins" );
|
301 |
+
|
302 |
+
foreach ( $plugins as $path => $plugin ) {
|
303 |
+
if( !in_array( $path, $activePlugins ) ) {
|
304 |
+
continue;
|
305 |
+
}
|
306 |
+
|
307 |
+
$output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
|
308 |
+
}
|
309 |
+
|
310 |
+
return apply_filters( "wpstg_sysinfo_after_wordpress_plugins", $output );
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* List of Inactive Plugins
|
315 |
+
* @param array $plugins
|
316 |
+
* @param array $activePlugins
|
317 |
+
* @return string
|
318 |
+
*/
|
319 |
+
public function inactivePlugins( $plugins, $activePlugins ) {
|
320 |
+
$output = $this->header( "WordPress Inactive Plugins" );
|
321 |
+
|
322 |
+
foreach ( $plugins as $path => $plugin ) {
|
323 |
+
if( in_array( $path, $activePlugins ) ) {
|
324 |
+
continue;
|
325 |
+
}
|
326 |
+
|
327 |
+
$output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
|
328 |
+
}
|
329 |
+
|
330 |
+
return apply_filters( "wpstg_sysinfo_after_wordpress_plugins_inactive", $output );
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* Get list of active and inactive plugins
|
335 |
+
* @return string
|
336 |
+
*/
|
337 |
+
public function plugins() {
|
338 |
+
// Get plugins and active plugins
|
339 |
+
$plugins = get_plugins();
|
340 |
+
$activePlugins = get_option( "active_plugins", array() );
|
341 |
+
|
342 |
+
// Active plugins
|
343 |
+
$output = $this->activePlugins( $plugins, $activePlugins );
|
344 |
+
$output .= $this->inactivePlugins( $plugins, $activePlugins );
|
345 |
+
|
346 |
+
return $output;
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* Multisite Plugins
|
351 |
+
* @return string
|
352 |
+
*/
|
353 |
+
public function multiSitePlugins() {
|
354 |
+
if( !$this->isMultiSite ) {
|
355 |
+
return '';
|
356 |
+
}
|
357 |
+
|
358 |
+
$output = $this->header( "Network Active Plugins" );
|
359 |
+
|
360 |
+
$plugins = wp_get_active_network_plugins();
|
361 |
+
$activePlugins = get_site_option( "active_sitewide_plugins", array() );
|
362 |
+
|
363 |
+
foreach ( $plugins as $pluginPath ) {
|
364 |
+
$pluginBase = plugin_basename( $pluginPath );
|
365 |
+
|
366 |
+
if( !array_key_exists( $pluginBase, $activePlugins ) ) {
|
367 |
+
continue;
|
368 |
+
}
|
369 |
+
|
370 |
+
$plugin = get_plugin_data( $pluginPath );
|
371 |
+
|
372 |
+
$output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
|
373 |
+
}
|
374 |
+
unset( $plugins, $activePlugins );
|
375 |
+
|
376 |
+
return $output;
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Server Information
|
381 |
+
* @return string
|
382 |
+
*/
|
383 |
+
public function server() {
|
384 |
+
// Server Configuration
|
385 |
+
$output = $this->header( "Webserver Configuration" );
|
386 |
+
|
387 |
+
$output .= $this->info( "PHP Version:", PHP_VERSION );
|
388 |
+
$output .= $this->info( "MySQL Version:", $this->di->get( "wpdb" )->db_version() );
|
389 |
+
$output .= $this->info( "Webserver Info:", $_SERVER["SERVER_SOFTWARE"] );
|
390 |
+
|
391 |
+
return apply_filters( "wpstg_sysinfo_after_webserver_config", $output );
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* PHP Configuration
|
396 |
+
* @return string
|
397 |
+
*/
|
398 |
+
public function php() {
|
399 |
+
$output = $this->header( "PHP Configuration" );
|
400 |
+
$output .= $this->info( "Safe Mode:", ($this->isSafeModeEnabled() ? "Enabled" : "Disabled" ) );
|
401 |
+
$output .= $this->info( "Memory Limit:", ini_get( "memory_limit" ) );
|
402 |
+
$output .= $this->info( "Upload Max Size:", ini_get( "upload_max_filesize" ) );
|
403 |
+
$output .= $this->info( "Post Max Size:", ini_get( "post_max_size" ) );
|
404 |
+
$output .= $this->info( "Upload Max Filesize:", ini_get( "upload_max_filesize" ) );
|
405 |
+
$output .= $this->info( "Time Limit:", ini_get( "max_execution_time" ) );
|
406 |
+
$output .= $this->info( "Max Input Vars:", ini_get( "max_input_vars" ) );
|
407 |
+
|
408 |
+
$displayErrors = ini_get( "display_errors" );
|
409 |
+
$output .= $this->info( "Display Errors:", ($displayErrors) ? "On ({$displayErrors})" : "N/A" );
|
410 |
+
|
411 |
+
return apply_filters( "wpstg_sysinfo_after_php_config", $output );
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* Check if PHP is on Safe Mode
|
416 |
+
* @return bool
|
417 |
+
*/
|
418 |
+
public function isSafeModeEnabled() {
|
419 |
+
return (
|
420 |
+
version_compare( PHP_VERSION, "5.4.0", '<' ) &&
|
421 |
+
@ini_get( "safe_mode" )
|
422 |
+
);
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* Checks if function exists or not
|
427 |
+
* @param string $functionName
|
428 |
+
* @return string
|
429 |
+
*/
|
430 |
+
public function isSupported( $functionName ) {
|
431 |
+
return (function_exists( $functionName )) ? "Supported" : "Not Supported";
|
432 |
+
}
|
433 |
+
|
434 |
+
/**
|
435 |
+
* Checks if class or extension is loaded / exists to determine if it is installed or not
|
436 |
+
* @param string $name
|
437 |
+
* @param bool $isClass
|
438 |
+
* @return string
|
439 |
+
*/
|
440 |
+
public function isInstalled( $name, $isClass = true ) {
|
441 |
+
if( true === $isClass ) {
|
442 |
+
return (class_exists( $name )) ? "Installed" : "Not Installed";
|
443 |
+
} else {
|
444 |
+
return (extension_loaded( $name )) ? "Installed" : "Not Installed";
|
445 |
+
}
|
446 |
+
}
|
447 |
+
|
448 |
+
/**
|
449 |
+
* Gets Installed Important PHP Extensions
|
450 |
+
* @return string
|
451 |
+
*/
|
452 |
+
public function phpExtensions() {
|
453 |
+
// Important PHP Extensions
|
454 |
+
$output = $this->header( "PHP Extensions" );
|
455 |
+
$output .= $this->info( "cURL:", $this->isSupported( "curl_init" ) );
|
456 |
+
$output .= $this->info( "fsockopen:", $this->isSupported( "fsockopen" ) );
|
457 |
+
$output .= $this->info( "SOAP Client:", $this->isInstalled( "SoapClient" ) );
|
458 |
+
$output .= $this->info( "Suhosin:", $this->isInstalled( "suhosin", false ) );
|
459 |
+
|
460 |
+
return apply_filters( "wpstg_sysinfo_after_php_ext", $output );
|
461 |
+
}
|
462 |
+
|
463 |
+
/**
|
464 |
+
* Check if WP is installed in subdir
|
465 |
+
* @return boolean
|
466 |
+
*/
|
467 |
+
private function isSubDir() {
|
468 |
+
if( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
|
469 |
+
return true;
|
470 |
+
}
|
471 |
+
return false;
|
472 |
+
}
|
473 |
+
|
474 |
+
/**
|
475 |
+
* Check and return prefix of the staging site
|
476 |
+
*/
|
477 |
+
/**
|
478 |
+
* Try to get the staging prefix from wp-config.php of staging site
|
479 |
+
* @param array $clone
|
480 |
+
* @return sting
|
481 |
+
*/
|
482 |
+
private function getStagingPrefix( $clone = array() ) {
|
483 |
+
// Throw error
|
484 |
+
$path = ABSPATH . $clone['directoryName'] . "/wp-config.php";
|
485 |
+
if( false === ($content = @file_get_contents( $path )) ) {
|
486 |
+
return 'Can\'t find staging wp-config.php';
|
487 |
+
} else {
|
488 |
+
|
489 |
+
// Get prefix from wp-config.php
|
490 |
+
//preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
|
491 |
+
preg_match( "/table_prefix\s*=\s*'(\w*)';/", $content, $matches );
|
492 |
+
//wp_die(var_dump($matches));
|
493 |
+
|
494 |
+
if( !empty( $matches[1] ) ) {
|
495 |
+
return $matches[1];
|
496 |
+
} else {
|
497 |
+
return 'No table_prefix in wp-config.php';
|
498 |
+
}
|
499 |
+
}
|
500 |
+
}
|
501 |
+
|
502 |
+
}
|
apps/Backend/Modules/Views/Forms/Settings.php
CHANGED
@@ -1,237 +1,237 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Views\Forms;
|
4 |
-
|
5 |
-
use WPStaging\Forms\Elements\Check;
|
6 |
-
use WPStaging\Forms\Elements\Numerical;
|
7 |
-
use WPStaging\Forms\Elements\Select;
|
8 |
-
use WPStaging\Forms\Elements\SelectMultiple;
|
9 |
-
use WPStaging\Forms\Elements\Text;
|
10 |
-
use WPStaging\Forms\Form;
|
11 |
-
use WPStaging\Backend\Modules\Views\Tabs\Tabs;
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Class Settings
|
15 |
-
* @package WPStaging\Backend\Modules\Views\Forms
|
16 |
-
*/
|
17 |
-
class Settings {
|
18 |
-
|
19 |
-
/**
|
20 |
-
* @var array
|
21 |
-
*/
|
22 |
-
private $form = array();
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @var Tabs
|
26 |
-
*/
|
27 |
-
private $tabs;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Settings constructor.
|
31 |
-
* @param Tabs $tabs
|
32 |
-
*/
|
33 |
-
public function __construct( $tabs ) {
|
34 |
-
$this->tabs = $tabs;
|
35 |
-
|
36 |
-
foreach ( $this->tabs->get() as $id => $name ) {
|
37 |
-
if( !method_exists( $this, $id ) ) {
|
38 |
-
continue;
|
39 |
-
}
|
40 |
-
|
41 |
-
$this->{$id}();
|
42 |
-
}
|
43 |
-
}
|
44 |
-
|
45 |
-
private function general() {
|
46 |
-
$this->form["general"] = new Form();
|
47 |
-
|
48 |
-
$settings = json_decode( json_encode( get_option( "wpstg_settings", array() ) ) );
|
49 |
-
|
50 |
-
// DB Copy Query Limit
|
51 |
-
$element = new Numerical(
|
52 |
-
"wpstg_settings[queryLimit]", array(
|
53 |
-
"class" => "medium-text",
|
54 |
-
"step" => 1,
|
55 |
-
"max" => 999999,
|
56 |
-
"min" => 0
|
57 |
-
)
|
58 |
-
);
|
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(
|
66 |
-
"wpstg_settings[querySRLimit]", array(
|
67 |
-
"class" => "medium-text",
|
68 |
-
"step" => 1,
|
69 |
-
"max" => 999999,
|
70 |
-
"min" => 0
|
71 |
-
)
|
72 |
-
);
|
73 |
-
|
74 |
-
$this->form["general"]->add(
|
75 |
-
$element->setLabel( "DB Search & Replace Limit" )
|
76 |
-
->setDefault( isset( $settings->querySRLimit ) ? $settings->querySRLimit : 5000 )
|
77 |
-
);
|
78 |
-
|
79 |
-
$options = array('1' => '1', '10' => '10', '50' => '50', '250' => '250', '500' => '500', '1000' => '1000');
|
80 |
-
// DB Copy Query Limit
|
81 |
-
$element = new Select(
|
82 |
-
"wpstg_settings[fileLimit]", $options, array(
|
83 |
-
"class" => "medium-text",
|
84 |
-
"step" => 1,
|
85 |
-
"max" => 999999,
|
86 |
-
"min" => 0
|
87 |
-
)
|
88 |
-
);
|
89 |
-
|
90 |
-
$this->form["general"]->add(
|
91 |
-
$element->setLabel( "File Copy Limit" )->setDefault( isset( $settings->fileLimit ) ? $settings->fileLimit : 1 )
|
92 |
-
);
|
93 |
-
|
94 |
-
|
95 |
-
// File Copy Batch Size
|
96 |
-
$element = new Numerical(
|
97 |
-
"wpstg_settings[maxFileSize]", array(
|
98 |
-
"class" => "medium-text",
|
99 |
-
"step" => 1,
|
100 |
-
"max" => 999999,
|
101 |
-
"min" => 0
|
102 |
-
)
|
103 |
-
);
|
104 |
-
|
105 |
-
$this->form["general"]->add(
|
106 |
-
$element->setLabel( "Maximum File Size (MB)" )
|
107 |
-
->setDefault( isset( $settings->maxFileSize ) ? $settings->maxFileSize : 8 )
|
108 |
-
);
|
109 |
-
|
110 |
-
// File Copy Batch Size
|
111 |
-
$element = new Numerical(
|
112 |
-
"wpstg_settings[batchSize]", array(
|
113 |
-
"class" => "medium-text",
|
114 |
-
"step" => 1,
|
115 |
-
"max" => 999999,
|
116 |
-
"min" => 0
|
117 |
-
)
|
118 |
-
);
|
119 |
-
|
120 |
-
$this->form["general"]->add(
|
121 |
-
$element->setLabel( "File Copy Batch Size" )
|
122 |
-
->setDefault( isset( $settings->batchSize ) ? $settings->batchSize : 2 )
|
123 |
-
);
|
124 |
-
|
125 |
-
// CPU load priority
|
126 |
-
$element = new Select(
|
127 |
-
"wpstg_settings[cpuLoad]", array(
|
128 |
-
"high" => "High (fast)",
|
129 |
-
"medium" => "Medium (average)",
|
130 |
-
"low" => "Low (slow)"
|
131 |
-
)
|
132 |
-
);
|
133 |
-
|
134 |
-
$this->form["general"]->add(
|
135 |
-
$element->setLabel( "CPU Load Priority" )
|
136 |
-
->setDefault( isset( $settings->cpuLoad ) ? $settings->cpuLoad : "fast" )
|
137 |
-
);
|
138 |
-
|
139 |
-
|
140 |
-
// Optimizer
|
141 |
-
$element = new Check(
|
142 |
-
"wpstg_settings[optimizer]", array('1' => "")
|
143 |
-
);
|
144 |
-
|
145 |
-
$this->form["general"]->add(
|
146 |
-
$element->setLabel( "Optimizer" )
|
147 |
-
->setDefault( (isset( $settings->optimizer )) ? $settings->optimizer : null )
|
148 |
-
);
|
149 |
-
|
150 |
-
// Plugins
|
151 |
-
$plugins = array();
|
152 |
-
|
153 |
-
foreach ( get_plugins() as $key => $data ) {
|
154 |
-
if( "wp-staging/wp-staging.php" === $key ) {
|
155 |
-
continue;
|
156 |
-
}
|
157 |
-
|
158 |
-
$plugins[$key] = $data["Name"];
|
159 |
-
}
|
160 |
-
|
161 |
-
$element = new Select(
|
162 |
-
"wpstg_settings[blackListedPlugins][]", $plugins, array(
|
163 |
-
"multiple" => "multiple",
|
164 |
-
"style" => "min-height:400px;"
|
165 |
-
)
|
166 |
-
);
|
167 |
-
|
168 |
-
$this->form["general"]->add(
|
169 |
-
$element->setDefault( (isset( $settings->blackListedPlugins )) ? $settings->blackListedPlugins : null )
|
170 |
-
);
|
171 |
-
|
172 |
-
// Disable admin authorization
|
173 |
-
$element = new Check(
|
174 |
-
"wpstg_settings[disableAdminLogin]", array('1' => '')
|
175 |
-
);
|
176 |
-
|
177 |
-
$this->form["general"]->add(
|
178 |
-
$element->setLabel( "Disable admin authorization" )
|
179 |
-
->setDefault( (isset( $settings->disableAdminLogin )) ? $settings->disableAdminLogin : null )
|
180 |
-
);
|
181 |
-
|
182 |
-
// WordPress in subdirectory
|
183 |
-
$element = new Check(
|
184 |
-
"wpstg_settings[wpSubDirectory]", array('1' => '')
|
185 |
-
);
|
186 |
-
|
187 |
-
$this->form["general"]->add(
|
188 |
-
$element->setLabel( "Wordpress in subdirectory" )
|
189 |
-
->setDefault( (isset( $settings->wpSubDirectory )) ? $settings->wpSubDirectory : null )
|
190 |
-
);
|
191 |
-
|
192 |
-
// Debug Mode
|
193 |
-
$element = new Check(
|
194 |
-
"wpstg_settings[debugMode]", array('1' => '')
|
195 |
-
);
|
196 |
-
|
197 |
-
$this->form["general"]->add(
|
198 |
-
$element->setLabel( "Debug Mode" )
|
199 |
-
->setDefault( (isset( $settings->debugMode )) ? $settings->debugMode : null )
|
200 |
-
);
|
201 |
-
|
202 |
-
// Remove Data on Uninstall?
|
203 |
-
$element = new Check(
|
204 |
-
"wpstg_settings[unInstallOnDelete]", array('1' => '')
|
205 |
-
);
|
206 |
-
|
207 |
-
$this->form["general"]->add(
|
208 |
-
$element->setLabel( "Remove Data on Uninstall?" )
|
209 |
-
->setDefault( (isset( $settings->unInstallOnDelete )) ? $settings->unInstallOnDelete : null )
|
210 |
-
);
|
211 |
-
|
212 |
-
// Check Directory Sizes
|
213 |
-
$element = new Check(
|
214 |
-
"wpstg_settings[checkDirectorySize]", array('1' => '')
|
215 |
-
);
|
216 |
-
|
217 |
-
$this->form["general"]->add(
|
218 |
-
$element->setLabel( "Check Directory Size" )
|
219 |
-
->setDefault( (isset( $settings->checkDirectorySize )) ? $settings->checkDirectorySize : null )
|
220 |
-
);
|
221 |
-
|
222 |
-
// Set login post id
|
223 |
-
$element = new Text( 'wpstg_settings[loginSlug]', array() );
|
224 |
-
$this->form["general"]->add(
|
225 |
-
$element->setLabel( "Login Custom Link" )->setDefault( isset( $settings->loginSlug ) ? $settings->loginSlug : '' )
|
226 |
-
);
|
227 |
-
}
|
228 |
-
|
229 |
-
/**
|
230 |
-
* @param string $name
|
231 |
-
* @return array|Form
|
232 |
-
*/
|
233 |
-
public function get( $name = null ) {
|
234 |
-
return (null === $name) ? $this->form : $this->form[$name];
|
235 |
-
}
|
236 |
-
|
237 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Views\Forms;
|
4 |
+
|
5 |
+
use WPStaging\Forms\Elements\Check;
|
6 |
+
use WPStaging\Forms\Elements\Numerical;
|
7 |
+
use WPStaging\Forms\Elements\Select;
|
8 |
+
use WPStaging\Forms\Elements\SelectMultiple;
|
9 |
+
use WPStaging\Forms\Elements\Text;
|
10 |
+
use WPStaging\Forms\Form;
|
11 |
+
use WPStaging\Backend\Modules\Views\Tabs\Tabs;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class Settings
|
15 |
+
* @package WPStaging\Backend\Modules\Views\Forms
|
16 |
+
*/
|
17 |
+
class Settings {
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @var array
|
21 |
+
*/
|
22 |
+
private $form = array();
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var Tabs
|
26 |
+
*/
|
27 |
+
private $tabs;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Settings constructor.
|
31 |
+
* @param Tabs $tabs
|
32 |
+
*/
|
33 |
+
public function __construct( $tabs ) {
|
34 |
+
$this->tabs = $tabs;
|
35 |
+
|
36 |
+
foreach ( $this->tabs->get() as $id => $name ) {
|
37 |
+
if( !method_exists( $this, $id ) ) {
|
38 |
+
continue;
|
39 |
+
}
|
40 |
+
|
41 |
+
$this->{$id}();
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
private function general() {
|
46 |
+
$this->form["general"] = new Form();
|
47 |
+
|
48 |
+
$settings = json_decode( json_encode( get_option( "wpstg_settings", array() ) ) );
|
49 |
+
|
50 |
+
// DB Copy Query Limit
|
51 |
+
$element = new Numerical(
|
52 |
+
"wpstg_settings[queryLimit]", array(
|
53 |
+
"class" => "medium-text",
|
54 |
+
"step" => 1,
|
55 |
+
"max" => 999999,
|
56 |
+
"min" => 0
|
57 |
+
)
|
58 |
+
);
|
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(
|
66 |
+
"wpstg_settings[querySRLimit]", array(
|
67 |
+
"class" => "medium-text",
|
68 |
+
"step" => 1,
|
69 |
+
"max" => 999999,
|
70 |
+
"min" => 0
|
71 |
+
)
|
72 |
+
);
|
73 |
+
|
74 |
+
$this->form["general"]->add(
|
75 |
+
$element->setLabel( "DB Search & Replace Limit" )
|
76 |
+
->setDefault( isset( $settings->querySRLimit ) ? $settings->querySRLimit : 5000 )
|
77 |
+
);
|
78 |
+
|
79 |
+
$options = array('1' => '1', '10' => '10', '50' => '50', '250' => '250', '500' => '500', '1000' => '1000');
|
80 |
+
// DB Copy Query Limit
|
81 |
+
$element = new Select(
|
82 |
+
"wpstg_settings[fileLimit]", $options, array(
|
83 |
+
"class" => "medium-text",
|
84 |
+
"step" => 1,
|
85 |
+
"max" => 999999,
|
86 |
+
"min" => 0
|
87 |
+
)
|
88 |
+
);
|
89 |
+
|
90 |
+
$this->form["general"]->add(
|
91 |
+
$element->setLabel( "File Copy Limit" )->setDefault( isset( $settings->fileLimit ) ? $settings->fileLimit : 1 )
|
92 |
+
);
|
93 |
+
|
94 |
+
|
95 |
+
// File Copy Batch Size
|
96 |
+
$element = new Numerical(
|
97 |
+
"wpstg_settings[maxFileSize]", array(
|
98 |
+
"class" => "medium-text",
|
99 |
+
"step" => 1,
|
100 |
+
"max" => 999999,
|
101 |
+
"min" => 0
|
102 |
+
)
|
103 |
+
);
|
104 |
+
|
105 |
+
$this->form["general"]->add(
|
106 |
+
$element->setLabel( "Maximum File Size (MB)" )
|
107 |
+
->setDefault( isset( $settings->maxFileSize ) ? $settings->maxFileSize : 8 )
|
108 |
+
);
|
109 |
+
|
110 |
+
// File Copy Batch Size
|
111 |
+
$element = new Numerical(
|
112 |
+
"wpstg_settings[batchSize]", array(
|
113 |
+
"class" => "medium-text",
|
114 |
+
"step" => 1,
|
115 |
+
"max" => 999999,
|
116 |
+
"min" => 0
|
117 |
+
)
|
118 |
+
);
|
119 |
+
|
120 |
+
$this->form["general"]->add(
|
121 |
+
$element->setLabel( "File Copy Batch Size" )
|
122 |
+
->setDefault( isset( $settings->batchSize ) ? $settings->batchSize : 2 )
|
123 |
+
);
|
124 |
+
|
125 |
+
// CPU load priority
|
126 |
+
$element = new Select(
|
127 |
+
"wpstg_settings[cpuLoad]", array(
|
128 |
+
"high" => "High (fast)",
|
129 |
+
"medium" => "Medium (average)",
|
130 |
+
"low" => "Low (slow)"
|
131 |
+
)
|
132 |
+
);
|
133 |
+
|
134 |
+
$this->form["general"]->add(
|
135 |
+
$element->setLabel( "CPU Load Priority" )
|
136 |
+
->setDefault( isset( $settings->cpuLoad ) ? $settings->cpuLoad : "fast" )
|
137 |
+
);
|
138 |
+
|
139 |
+
|
140 |
+
// Optimizer
|
141 |
+
$element = new Check(
|
142 |
+
"wpstg_settings[optimizer]", array('1' => "")
|
143 |
+
);
|
144 |
+
|
145 |
+
$this->form["general"]->add(
|
146 |
+
$element->setLabel( "Optimizer" )
|
147 |
+
->setDefault( (isset( $settings->optimizer )) ? $settings->optimizer : null )
|
148 |
+
);
|
149 |
+
|
150 |
+
// Plugins
|
151 |
+
$plugins = array();
|
152 |
+
|
153 |
+
foreach ( get_plugins() as $key => $data ) {
|
154 |
+
if( "wp-staging/wp-staging.php" === $key ) {
|
155 |
+
continue;
|
156 |
+
}
|
157 |
+
|
158 |
+
$plugins[$key] = $data["Name"];
|
159 |
+
}
|
160 |
+
|
161 |
+
$element = new Select(
|
162 |
+
"wpstg_settings[blackListedPlugins][]", $plugins, array(
|
163 |
+
"multiple" => "multiple",
|
164 |
+
"style" => "min-height:400px;"
|
165 |
+
)
|
166 |
+
);
|
167 |
+
|
168 |
+
$this->form["general"]->add(
|
169 |
+
$element->setDefault( (isset( $settings->blackListedPlugins )) ? $settings->blackListedPlugins : null )
|
170 |
+
);
|
171 |
+
|
172 |
+
// Disable admin authorization
|
173 |
+
$element = new Check(
|
174 |
+
"wpstg_settings[disableAdminLogin]", array('1' => '')
|
175 |
+
);
|
176 |
+
|
177 |
+
$this->form["general"]->add(
|
178 |
+
$element->setLabel( "Disable admin authorization" )
|
179 |
+
->setDefault( (isset( $settings->disableAdminLogin )) ? $settings->disableAdminLogin : null )
|
180 |
+
);
|
181 |
+
|
182 |
+
// WordPress in subdirectory
|
183 |
+
$element = new Check(
|
184 |
+
"wpstg_settings[wpSubDirectory]", array('1' => '')
|
185 |
+
);
|
186 |
+
|
187 |
+
$this->form["general"]->add(
|
188 |
+
$element->setLabel( "Wordpress in subdirectory" )
|
189 |
+
->setDefault( (isset( $settings->wpSubDirectory )) ? $settings->wpSubDirectory : null )
|
190 |
+
);
|
191 |
+
|
192 |
+
// Debug Mode
|
193 |
+
$element = new Check(
|
194 |
+
"wpstg_settings[debugMode]", array('1' => '')
|
195 |
+
);
|
196 |
+
|
197 |
+
$this->form["general"]->add(
|
198 |
+
$element->setLabel( "Debug Mode" )
|
199 |
+
->setDefault( (isset( $settings->debugMode )) ? $settings->debugMode : null )
|
200 |
+
);
|
201 |
+
|
202 |
+
// Remove Data on Uninstall?
|
203 |
+
$element = new Check(
|
204 |
+
"wpstg_settings[unInstallOnDelete]", array('1' => '')
|
205 |
+
);
|
206 |
+
|
207 |
+
$this->form["general"]->add(
|
208 |
+
$element->setLabel( "Remove Data on Uninstall?" )
|
209 |
+
->setDefault( (isset( $settings->unInstallOnDelete )) ? $settings->unInstallOnDelete : null )
|
210 |
+
);
|
211 |
+
|
212 |
+
// Check Directory Sizes
|
213 |
+
$element = new Check(
|
214 |
+
"wpstg_settings[checkDirectorySize]", array('1' => '')
|
215 |
+
);
|
216 |
+
|
217 |
+
$this->form["general"]->add(
|
218 |
+
$element->setLabel( "Check Directory Size" )
|
219 |
+
->setDefault( (isset( $settings->checkDirectorySize )) ? $settings->checkDirectorySize : null )
|
220 |
+
);
|
221 |
+
|
222 |
+
// Set login post id
|
223 |
+
$element = new Text( 'wpstg_settings[loginSlug]', array() );
|
224 |
+
$this->form["general"]->add(
|
225 |
+
$element->setLabel( "Login Custom Link" )->setDefault( isset( $settings->loginSlug ) ? $settings->loginSlug : '' )
|
226 |
+
);
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* @param string $name
|
231 |
+
* @return array|Form
|
232 |
+
*/
|
233 |
+
public function get( $name = null ) {
|
234 |
+
return (null === $name) ? $this->form : $this->form[$name];
|
235 |
+
}
|
236 |
+
|
237 |
+
}
|
apps/Backend/views/clone/ajax/update.php
CHANGED
@@ -1,33 +1,33 @@
|
|
1 |
-
<div class=successfullying-section">
|
2 |
-
<h2 id="wpstg-processing-header"><?php echo __("Processing, please wait...", "wpstg")?></h2>
|
3 |
-
<div class="wpstg-progress-bar">
|
4 |
-
<div class="wpstg-progress" id="wpstg-progress-db" style="width:0;overflow: hidden;"></div>
|
5 |
-
<div class="wpstg-progress" id="wpstg-progress-sr" style="width:0;background-color:#3c9ee4;overflow: hidden;"></div>
|
6 |
-
<div class="wpstg-progress" id="wpstg-progress-dirs" style="width:0;background-color:#3a96d7;overflow: hidden;"></div>
|
7 |
-
<div class="wpstg-progress" id="wpstg-progress-files" style="width:0;background-color:#378cc9;overflow: hidden;"></div>
|
8 |
-
</div>
|
9 |
-
<div style="clear:both;">
|
10 |
-
<div id="wpstg-processing-status"></div>
|
11 |
-
<div id="wpstg-processing-timer"></div>
|
12 |
-
</div>
|
13 |
-
<div style="clear: both;"></div>
|
14 |
-
</div>
|
15 |
-
|
16 |
-
<button type="button" id="wpstg-cancel-cloning-update" class="wpstg-link-btn button-primary">
|
17 |
-
<?php echo __("Cancel Update", "wpstg")?>
|
18 |
-
</button>
|
19 |
-
|
20 |
-
<button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
|
21 |
-
<?php _e('Display working log', 'wpstg')?>
|
22 |
-
</button>
|
23 |
-
|
24 |
-
<div>
|
25 |
-
<span id="wpstg-cloning-result"></span>
|
26 |
-
</div>
|
27 |
-
|
28 |
-
|
29 |
-
<div id="wpstg-error-wrapper">
|
30 |
-
<div id="wpstg-error-details"></div>
|
31 |
-
</div>
|
32 |
-
|
33 |
<div id="wpstg-log-details"></div>
|
1 |
+
<div class=successfullying-section">
|
2 |
+
<h2 id="wpstg-processing-header"><?php echo __("Processing, please wait...", "wpstg")?></h2>
|
3 |
+
<div class="wpstg-progress-bar">
|
4 |
+
<div class="wpstg-progress" id="wpstg-progress-db" style="width:0;overflow: hidden;"></div>
|
5 |
+
<div class="wpstg-progress" id="wpstg-progress-sr" style="width:0;background-color:#3c9ee4;overflow: hidden;"></div>
|
6 |
+
<div class="wpstg-progress" id="wpstg-progress-dirs" style="width:0;background-color:#3a96d7;overflow: hidden;"></div>
|
7 |
+
<div class="wpstg-progress" id="wpstg-progress-files" style="width:0;background-color:#378cc9;overflow: hidden;"></div>
|
8 |
+
</div>
|
9 |
+
<div style="clear:both;">
|
10 |
+
<div id="wpstg-processing-status"></div>
|
11 |
+
<div id="wpstg-processing-timer"></div>
|
12 |
+
</div>
|
13 |
+
<div style="clear: both;"></div>
|
14 |
+
</div>
|
15 |
+
|
16 |
+
<button type="button" id="wpstg-cancel-cloning-update" class="wpstg-link-btn button-primary">
|
17 |
+
<?php echo __("Cancel Update", "wpstg")?>
|
18 |
+
</button>
|
19 |
+
|
20 |
+
<button type="button" id="wpstg-show-log-button" class="button" data-clone="<?php echo $cloning->getOptions()->clone?>" style="margin-top: 5px;display:none;">
|
21 |
+
<?php _e('Display working log', 'wpstg')?>
|
22 |
+
</button>
|
23 |
+
|
24 |
+
<div>
|
25 |
+
<span id="wpstg-cloning-result"></span>
|
26 |
+
</div>
|
27 |
+
|
28 |
+
|
29 |
+
<div id="wpstg-error-wrapper">
|
30 |
+
<div id="wpstg-error-details"></div>
|
31 |
+
</div>
|
32 |
+
|
33 |
<div id="wpstg-log-details"></div>
|
apps/Backend/views/clone/includes/footer.php
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<div style="clear:both;">Something not working? Open a <a href="https://wp-staging.com/support" target="_blank" rel="external nofollow"> support ticket</a> and we help you to fix it quickly.</div>
|
2 |
-
<div id="wpstg-error-wrapper">
|
3 |
-
<div id="wpstg-error-details"></div>
|
4 |
</div>
|
1 |
+
<div style="clear:both;">Something not working? Open a <a href="https://wp-staging.com/support" target="_blank" rel="external nofollow"> support ticket</a> and we help you to fix it quickly.</div>
|
2 |
+
<div id="wpstg-error-wrapper">
|
3 |
+
<div id="wpstg-error-details"></div>
|
4 |
</div>
|
apps/Core/Utils/Helper.php
CHANGED
@@ -1,81 +1,81 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Utils;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if (!defined("WPINC"))
|
7 |
-
{
|
8 |
-
die;
|
9 |
-
}
|
10 |
-
|
11 |
-
class Helper {
|
12 |
-
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Retrieves the URL for a given site where the front end is accessible.
|
16 |
-
* This is from WordPress source 4.9.5/src/wp-includes/link-template.php
|
17 |
-
* home_url filter has been removed here to make sure that wpml can not overwrite that value!
|
18 |
-
*
|
19 |
-
* Returns the 'home' option with the appropriate protocol. The protocol will be 'https'
|
20 |
-
* if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
|
21 |
-
* If `$scheme` is 'http' or 'https', is_ssl() is overridden.
|
22 |
-
*
|
23 |
-
* @since 3.0.0
|
24 |
-
*
|
25 |
-
* @global string $pagenow
|
26 |
-
*
|
27 |
-
* @param int $blog_id Optional. Site ID. Default null (current site).
|
28 |
-
* @param string $path Optional. Path relative to the home URL. Default empty.
|
29 |
-
* @param string|null $scheme Optional. Scheme to give the home URL context. Accepts
|
30 |
-
* 'http', 'https', 'relative', 'rest', or null. Default null.
|
31 |
-
* @return string Home URL link with optional path appended.
|
32 |
-
*/
|
33 |
-
public function get_home_url( $blog_id = null, $path = '', $scheme = null ) {
|
34 |
-
global $pagenow;
|
35 |
-
|
36 |
-
$orig_scheme = $scheme;
|
37 |
-
|
38 |
-
if( empty( $blog_id ) || !is_multisite() ) {
|
39 |
-
$url = get_option( 'home' );
|
40 |
-
} else {
|
41 |
-
switch_to_blog( $blog_id );
|
42 |
-
$url = get_option( 'home' );
|
43 |
-
restore_current_blog();
|
44 |
-
}
|
45 |
-
|
46 |
-
if( !in_array( $scheme, array('http', 'https', 'relative') ) ) {
|
47 |
-
if( is_ssl() && !is_admin() && 'wp-login.php' !== $pagenow )
|
48 |
-
$scheme = 'https';
|
49 |
-
else
|
50 |
-
$scheme = parse_url( $url, PHP_URL_SCHEME );
|
51 |
-
}
|
52 |
-
|
53 |
-
$url = set_url_scheme( $url, $scheme );
|
54 |
-
|
55 |
-
if( $path && is_string( $path ) )
|
56 |
-
$url .= '/' . ltrim( $path, '/' );
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Filters the home URL.
|
60 |
-
*
|
61 |
-
* @since 3.0.0
|
62 |
-
*
|
63 |
-
* @param string $url The complete home URL including scheme and path.
|
64 |
-
* @param string $path Path relative to the home URL. Blank string if no path is specified.
|
65 |
-
* @param string|null $orig_scheme Scheme to give the home URL context. Accepts 'http', 'https',
|
66 |
-
* 'relative', 'rest', or null.
|
67 |
-
* @param int|null $blog_id Site ID, or null for the current site.
|
68 |
-
*/
|
69 |
-
return $url;
|
70 |
-
}
|
71 |
-
|
72 |
-
/**
|
73 |
-
* Return home url without scheme
|
74 |
-
* @param string $str
|
75 |
-
* @return string
|
76 |
-
*/
|
77 |
-
public function get_home_url_without_scheme() {
|
78 |
-
return preg_replace( '#^https?://#', '', rtrim( $this->get_home_url(), '/' ) );
|
79 |
-
}
|
80 |
-
|
81 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Utils;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if (!defined("WPINC"))
|
7 |
+
{
|
8 |
+
die;
|
9 |
+
}
|
10 |
+
|
11 |
+
class Helper {
|
12 |
+
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Retrieves the URL for a given site where the front end is accessible.
|
16 |
+
* This is from WordPress source 4.9.5/src/wp-includes/link-template.php
|
17 |
+
* home_url filter has been removed here to make sure that wpml can not overwrite that value!
|
18 |
+
*
|
19 |
+
* Returns the 'home' option with the appropriate protocol. The protocol will be 'https'
|
20 |
+
* if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
|
21 |
+
* If `$scheme` is 'http' or 'https', is_ssl() is overridden.
|
22 |
+
*
|
23 |
+
* @since 3.0.0
|
24 |
+
*
|
25 |
+
* @global string $pagenow
|
26 |
+
*
|
27 |
+
* @param int $blog_id Optional. Site ID. Default null (current site).
|
28 |
+
* @param string $path Optional. Path relative to the home URL. Default empty.
|
29 |
+
* @param string|null $scheme Optional. Scheme to give the home URL context. Accepts
|
30 |
+
* 'http', 'https', 'relative', 'rest', or null. Default null.
|
31 |
+
* @return string Home URL link with optional path appended.
|
32 |
+
*/
|
33 |
+
public function get_home_url( $blog_id = null, $path = '', $scheme = null ) {
|
34 |
+
global $pagenow;
|
35 |
+
|
36 |
+
$orig_scheme = $scheme;
|
37 |
+
|
38 |
+
if( empty( $blog_id ) || !is_multisite() ) {
|
39 |
+
$url = get_option( 'home' );
|
40 |
+
} else {
|
41 |
+
switch_to_blog( $blog_id );
|
42 |
+
$url = get_option( 'home' );
|
43 |
+
restore_current_blog();
|
44 |
+
}
|
45 |
+
|
46 |
+
if( !in_array( $scheme, array('http', 'https', 'relative') ) ) {
|
47 |
+
if( is_ssl() && !is_admin() && 'wp-login.php' !== $pagenow )
|
48 |
+
$scheme = 'https';
|
49 |
+
else
|
50 |
+
$scheme = parse_url( $url, PHP_URL_SCHEME );
|
51 |
+
}
|
52 |
+
|
53 |
+
$url = set_url_scheme( $url, $scheme );
|
54 |
+
|
55 |
+
if( $path && is_string( $path ) )
|
56 |
+
$url .= '/' . ltrim( $path, '/' );
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Filters the home URL.
|
60 |
+
*
|
61 |
+
* @since 3.0.0
|
62 |
+
*
|
63 |
+
* @param string $url The complete home URL including scheme and path.
|
64 |
+
* @param string $path Path relative to the home URL. Blank string if no path is specified.
|
65 |
+
* @param string|null $orig_scheme Scheme to give the home URL context. Accepts 'http', 'https',
|
66 |
+
* 'relative', 'rest', or null.
|
67 |
+
* @param int|null $blog_id Site ID, or null for the current site.
|
68 |
+
*/
|
69 |
+
return $url;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Return home url without scheme
|
74 |
+
* @param string $str
|
75 |
+
* @return string
|
76 |
+
*/
|
77 |
+
public function get_home_url_without_scheme() {
|
78 |
+
return preg_replace( '#^https?://#', '', rtrim( $this->get_home_url(), '/' ) );
|
79 |
+
}
|
80 |
+
|
81 |
+
}
|
apps/Core/Utils/Multisite.php
CHANGED
@@ -1,26 +1,26 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Utils;
|
4 |
-
|
5 |
-
use WPStaging\Utils\Helper;
|
6 |
-
|
7 |
-
// No Direct Access
|
8 |
-
if( !defined( "WPINC" ) ) {
|
9 |
-
die;
|
10 |
-
}
|
11 |
-
|
12 |
-
class Multisite {
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Get multisite main site homeurl
|
16 |
-
* @return string
|
17 |
-
*/
|
18 |
-
public function getHomeURL() {
|
19 |
-
$helper = new Helper();
|
20 |
-
|
21 |
-
$url = $helper->get_home_url();
|
22 |
-
$result = parse_url( $url );
|
23 |
-
return $result['scheme'] . "://" . $result['host'];
|
24 |
-
}
|
25 |
-
|
26 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Utils;
|
4 |
+
|
5 |
+
use WPStaging\Utils\Helper;
|
6 |
+
|
7 |
+
// No Direct Access
|
8 |
+
if( !defined( "WPINC" ) ) {
|
9 |
+
die;
|
10 |
+
}
|
11 |
+
|
12 |
+
class Multisite {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Get multisite main site homeurl
|
16 |
+
* @return string
|
17 |
+
*/
|
18 |
+
public function getHomeURL() {
|
19 |
+
$helper = new Helper();
|
20 |
+
|
21 |
+
$url = $helper->get_home_url();
|
22 |
+
$result = parse_url( $url );
|
23 |
+
return $result['scheme'] . "://" . $result['host'];
|
24 |
+
}
|
25 |
+
|
26 |
+
}
|
apps/Core/WPStaging.php
CHANGED
@@ -1,429 +1,429 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
// Ensure to include autoloader class
|
11 |
-
require_once __DIR__ . DIRECTORY_SEPARATOR . "Utils" . DIRECTORY_SEPARATOR . "Autoloader.php";
|
12 |
-
|
13 |
-
use WPStaging\Backend\Administrator;
|
14 |
-
use WPStaging\DTO\Settings;
|
15 |
-
use WPStaging\Frontend\Frontend;
|
16 |
-
use WPStaging\Utils\Autoloader;
|
17 |
-
use WPStaging\Utils\Cache;
|
18 |
-
use WPStaging\Utils\Loader;
|
19 |
-
use WPStaging\Utils\Logger;
|
20 |
-
use WPStaging\DI\InjectionAware;
|
21 |
-
use WPStaging\Cron\Cron;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Class WPStaging
|
25 |
-
* @package WPStaging
|
26 |
-
*/
|
27 |
-
final class WPStaging {
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Plugin version
|
31 |
-
*/
|
32 |
-
const VERSION = "2.3.
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Plugin name
|
36 |
-
*/
|
37 |
-
const NAME = "WP Staging";
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Plugin slug
|
41 |
-
*/
|
42 |
-
const SLUG = "wp-staging";
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Compatible WP Version
|
46 |
-
*/
|
47 |
-
const WP_COMPATIBLE = "4.9.
|
48 |
-
|
49 |
-
/**
|
50 |
-
* Slug: Either wp-staging or wp-staging-pro
|
51 |
-
* @var string
|
52 |
-
*/
|
53 |
-
public $slug;
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Absolute plugin path
|
57 |
-
* @var string
|
58 |
-
*/
|
59 |
-
public $pluginPath;
|
60 |
-
|
61 |
-
/**
|
62 |
-
* Services
|
63 |
-
* @var array
|
64 |
-
*/
|
65 |
-
private $services;
|
66 |
-
|
67 |
-
/**
|
68 |
-
* Singleton instance
|
69 |
-
* @var WPStaging
|
70 |
-
*/
|
71 |
-
private static $instance;
|
72 |
-
|
73 |
-
/**
|
74 |
-
* WPStaging constructor.
|
75 |
-
*/
|
76 |
-
private function __construct() {
|
77 |
-
|
78 |
-
$file = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR . self::SLUG . ".php";
|
79 |
-
|
80 |
-
$this->registerMain();
|
81 |
-
$this->registerNamespaces();
|
82 |
-
$this->loadLanguages();
|
83 |
-
$this->loadDependencies();
|
84 |
-
$this->defineHooks();
|
85 |
-
// Licensing stuff be loaded in wpstg core to make cron hook available from frontpage
|
86 |
-
$this->initLicensing();
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* Method to be executed upon activation of the plugin
|
91 |
-
*/
|
92 |
-
public function onActivation() {
|
93 |
-
$Activation = new \WPStaging\Backend\Activation\Activation();
|
94 |
-
$Activation->install_dependancies();
|
95 |
-
}
|
96 |
-
|
97 |
-
public function registerMain() {
|
98 |
-
// Slug of the plugin
|
99 |
-
$this->slug = plugin_basename( dirname( dirname( dirname( __FILE__ ) ) ) );
|
100 |
-
|
101 |
-
// absolute path to the main plugin dir
|
102 |
-
$this->pluginPath = plugin_dir_path( dirname( dirname( __FILE__ ) ) );
|
103 |
-
|
104 |
-
// URL to apps folder
|
105 |
-
$this->url = plugin_dir_url( dirname( __FILE__ ) );
|
106 |
-
|
107 |
-
// URL to backend public folder folder
|
108 |
-
$this->backend_url = plugin_dir_url( dirname( __FILE__ ) ) . "Backend/public/";
|
109 |
-
|
110 |
-
// URL to frontend public folder folder
|
111 |
-
$this->frontend_url = plugin_dir_url( dirname( __FILE__ ) ) . "Frontend/public/";
|
112 |
-
}
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Define Hooks
|
116 |
-
*/
|
117 |
-
public function defineHooks() {
|
118 |
-
$loader = $this->get( "loader" );
|
119 |
-
$loader->addAction( "admin_enqueue_scripts", $this, "enqueueElements", 100 );
|
120 |
-
$loader->addAction( "wp_enqueue_scripts", $this, "enqueueElements", 100 );
|
121 |
-
$this->addIntervals();
|
122 |
-
}
|
123 |
-
|
124 |
-
/**
|
125 |
-
* Add new cron time event "weekly"
|
126 |
-
*/
|
127 |
-
public function addIntervals(){
|
128 |
-
$interval = new Cron();
|
129 |
-
}
|
130 |
-
|
131 |
-
/**
|
132 |
-
* Scripts and Styles
|
133 |
-
* @param string $hook
|
134 |
-
*/
|
135 |
-
public function enqueueElements( $hook ) {
|
136 |
-
|
137 |
-
// Load this css file on frontend and backend on all pages if current site is a staging site
|
138 |
-
if( $this->isStagingSite() ) {
|
139 |
-
wp_enqueue_style( "wpstg-admin-bar", $this->backend_url . "css/wpstg-admin-bar.css", $this->getVersion() );
|
140 |
-
}
|
141 |
-
|
142 |
-
$availablePages = array(
|
143 |
-
"toplevel_page_wpstg_clone",
|
144 |
-
"wp-staging_page_wpstg-settings",
|
145 |
-
"wp-staging_page_wpstg-tools",
|
146 |
-
"wp-staging_page_wpstg-license",
|
147 |
-
"wp-staging_page_wpstg-welcome",
|
148 |
-
);
|
149 |
-
|
150 |
-
// Load these css and js files only on wp staging admin pages
|
151 |
-
if( !in_array( $hook, $availablePages ) || !is_admin() ) {
|
152 |
-
return;
|
153 |
-
}
|
154 |
-
|
155 |
-
// Load admin js files
|
156 |
-
wp_enqueue_script(
|
157 |
-
"wpstg-admin-script", $this->backend_url . "js/wpstg-admin.js", array("jquery"), $this->getVersion(), false
|
158 |
-
);
|
159 |
-
|
160 |
-
// Load admin css files
|
161 |
-
wp_enqueue_style(
|
162 |
-
"wpstg-admin", $this->backend_url . "css/wpstg-admin.css", $this->getVersion()
|
163 |
-
);
|
164 |
-
|
165 |
-
wp_localize_script( "wpstg-admin-script", "wpstg", array(
|
166 |
-
"nonce" => wp_create_nonce( "wpstg_ajax_nonce" ),
|
167 |
-
"mu_plugin_confirmation" => __(
|
168 |
-
"If confirmed we will install an additional WordPress 'Must Use' plugin. "
|
169 |
-
. "This plugin will allow us to control which plugins are loaded during "
|
170 |
-
. "WP Staging specific operations. Do you wish to continue?", "wpstg"
|
171 |
-
),
|
172 |
-
"plugin_compatibility_settings_problem" => __(
|
173 |
-
"A problem occurred when trying to change the plugin compatibility setting.", "wpstg"
|
174 |
-
),
|
175 |
-
"saved" => __( "Saved", "The settings were saved successfully", "wpstg" ),
|
176 |
-
"status" => __( "Status", "Current request status", "wpstg" ),
|
177 |
-
"response" => __( "Response", "The message the server responded with", "wpstg" ),
|
178 |
-
"blacklist_problem" => __(
|
179 |
-
"A problem occurred when trying to add plugins to backlist.", "wpstg"
|
180 |
-
),
|
181 |
-
"cpuLoad" => $this->getCPULoadSetting(),
|
182 |
-
"settings" => ( object ) array(), // TODO add settings?
|
183 |
-
"tblprefix" => self::getTablePrefix()
|
184 |
-
) );
|
185 |
-
}
|
186 |
-
|
187 |
-
/**
|
188 |
-
* Get table prefix of the current site
|
189 |
-
* @return string
|
190 |
-
*/
|
191 |
-
public static function getTablePrefix(){
|
192 |
-
$wpDB = WPStaging::getInstance()->get("wpdb");
|
193 |
-
return $wpDB->prefix;
|
194 |
-
}
|
195 |
-
|
196 |
-
/**
|
197 |
-
* Caching and logging folder
|
198 |
-
*
|
199 |
-
* @return string
|
200 |
-
*/
|
201 |
-
public static function getContentDir() {
|
202 |
-
$wp_upload_dir = wp_upload_dir();
|
203 |
-
$path = $wp_upload_dir['basedir'] . '/wp-staging';
|
204 |
-
wp_mkdir_p( $path );
|
205 |
-
return apply_filters( 'wpstg_get_upload_dir', $path . DIRECTORY_SEPARATOR );
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
* Register used namespaces
|
210 |
-
*/
|
211 |
-
private function registerNamespaces() {
|
212 |
-
$autoloader = new Autoloader();
|
213 |
-
$this->set( "autoloader", $autoloader );
|
214 |
-
|
215 |
-
// Autoloader
|
216 |
-
$autoloader->registerNamespaces( array(
|
217 |
-
"WPStaging" => array(
|
218 |
-
$this->pluginPath . 'apps' . DIRECTORY_SEPARATOR,
|
219 |
-
$this->pluginPath . 'apps' . DIRECTORY_SEPARATOR . 'Core' . DIRECTORY_SEPARATOR,
|
220 |
-
)
|
221 |
-
) );
|
222 |
-
|
223 |
-
// Register namespaces
|
224 |
-
$autoloader->register();
|
225 |
-
}
|
226 |
-
|
227 |
-
/**
|
228 |
-
* Get Instance
|
229 |
-
* @return WPStaging
|
230 |
-
*/
|
231 |
-
public static function getInstance() {
|
232 |
-
if( null === static::$instance ) {
|
233 |
-
static::$instance = new static();
|
234 |
-
}
|
235 |
-
|
236 |
-
return static::$instance;
|
237 |
-
}
|
238 |
-
|
239 |
-
/**
|
240 |
-
* Prevent cloning
|
241 |
-
* @return void
|
242 |
-
*/
|
243 |
-
private function __clone() {
|
244 |
-
|
245 |
-
}
|
246 |
-
|
247 |
-
/**
|
248 |
-
* Prevent unserialization
|
249 |
-
* @return void
|
250 |
-
*/
|
251 |
-
private function __wakeup() {
|
252 |
-
|
253 |
-
}
|
254 |
-
|
255 |
-
/**
|
256 |
-
* Load Dependencies
|
257 |
-
*/
|
258 |
-
private function loadDependencies() {
|
259 |
-
// Set loader
|
260 |
-
$this->set( "loader", new Loader() );
|
261 |
-
|
262 |
-
// Set cache
|
263 |
-
$this->set( "cache", new Cache() );
|
264 |
-
|
265 |
-
// Set logger
|
266 |
-
$this->set( "logger", new Logger() );
|
267 |
-
|
268 |
-
// Set settings
|
269 |
-
$this->set( "settings", new Settings() );
|
270 |
-
|
271 |
-
// Set Administrator
|
272 |
-
if( is_admin() ) {
|
273 |
-
new Administrator( $this );
|
274 |
-
} else {
|
275 |
-
new Frontend( $this );
|
276 |
-
}
|
277 |
-
}
|
278 |
-
|
279 |
-
/**
|
280 |
-
* Execute Plugin
|
281 |
-
*/
|
282 |
-
public function run() {
|
283 |
-
$this->get( "loader" )->run();
|
284 |
-
}
|
285 |
-
|
286 |
-
/**
|
287 |
-
* Set a variable to DI with given name
|
288 |
-
* @param string $name
|
289 |
-
* @param mixed $variable
|
290 |
-
* @return $this
|
291 |
-
*/
|
292 |
-
public function set( $name, $variable ) {
|
293 |
-
// It is a function
|
294 |
-
if( is_callable( $variable ) )
|
295 |
-
$variable = $variable();
|
296 |
-
|
297 |
-
// Add it to services
|
298 |
-
$this->services[$name] = $variable;
|
299 |
-
|
300 |
-
return $this;
|
301 |
-
}
|
302 |
-
|
303 |
-
/**
|
304 |
-
* Get given name index from DI
|
305 |
-
* @param string $name
|
306 |
-
* @return mixed|null
|
307 |
-
*/
|
308 |
-
public function get( $name ) {
|
309 |
-
return (isset( $this->services[$name] )) ? $this->services[$name] : null;
|
310 |
-
}
|
311 |
-
|
312 |
-
/**
|
313 |
-
* @return string
|
314 |
-
*/
|
315 |
-
public function getVersion() {
|
316 |
-
return self::VERSION;
|
317 |
-
}
|
318 |
-
|
319 |
-
/**
|
320 |
-
* @return string
|
321 |
-
*/
|
322 |
-
public function getName() {
|
323 |
-
return self::NAME;
|
324 |
-
}
|
325 |
-
|
326 |
-
/**
|
327 |
-
* @return string
|
328 |
-
*/
|
329 |
-
public static function getSlug() {
|
330 |
-
return plugin_basename( dirname( dirname( dirname( __FILE__ ) ) ) );
|
331 |
-
}
|
332 |
-
|
333 |
-
/**
|
334 |
-
* Get path to main plugin file
|
335 |
-
* @return string
|
336 |
-
*/
|
337 |
-
public function getPath() {
|
338 |
-
return dirname( dirname( __FILE__ ) );
|
339 |
-
}
|
340 |
-
|
341 |
-
/**
|
342 |
-
* Get main plugin url
|
343 |
-
* @return type
|
344 |
-
*/
|
345 |
-
public function getUrl() {
|
346 |
-
return plugin_dir_url( dirname( __FILE__ ) );
|
347 |
-
}
|
348 |
-
|
349 |
-
/**
|
350 |
-
* @return array|mixed|object
|
351 |
-
*/
|
352 |
-
public function getCPULoadSetting() {
|
353 |
-
$options = $this->get( "settings" );
|
354 |
-
$setting = $options->getCpuLoad();
|
355 |
-
|
356 |
-
switch ( $setting ) {
|
357 |
-
case "high":
|
358 |
-
$cpuLoad = 0;
|
359 |
-
break;
|
360 |
-
|
361 |
-
case "medium":
|
362 |
-
$cpuLoad = 1000;
|
363 |
-
break;
|
364 |
-
|
365 |
-
case "low":
|
366 |
-
$cpuLoad = 1500;
|
367 |
-
break;
|
368 |
-
|
369 |
-
case "default":
|
370 |
-
default:
|
371 |
-
$cpuLoad = 1000;
|
372 |
-
}
|
373 |
-
|
374 |
-
return $cpuLoad;
|
375 |
-
}
|
376 |
-
|
377 |
-
/**
|
378 |
-
* Load language file
|
379 |
-
*/
|
380 |
-
public function loadLanguages() {
|
381 |
-
$languagesDirectory = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR;
|
382 |
-
$languagesDirectory.= "vars" . DIRECTORY_SEPARATOR . "languages" . DIRECTORY_SEPARATOR;
|
383 |
-
|
384 |
-
// Set filter for plugins languages directory
|
385 |
-
$languagesDirectory = apply_filters( "wpstg_languages_directory", $languagesDirectory );
|
386 |
-
|
387 |
-
// Traditional WP plugin locale filter
|
388 |
-
$locale = apply_filters( "plugin_locale", get_locale(), "wpstg" );
|
389 |
-
$moFile = sprintf( '%1$s-%2$s.mo', "wpstg", $locale );
|
390 |
-
|
391 |
-
// Setup paths to current locale file
|
392 |
-
$moFileLocal = $languagesDirectory . $moFile;
|
393 |
-
$moFileGlobal = WP_LANG_DIR . DIRECTORY_SEPARATOR . "wpstg" . DIRECTORY_SEPARATOR . $moFile;
|
394 |
-
|
395 |
-
// Global file (/wp-content/languages/WPSTG)
|
396 |
-
if( file_exists( $moFileGlobal ) ) {
|
397 |
-
load_textdomain( "wpstg", $moFileGlobal );
|
398 |
-
}
|
399 |
-
// Local file (/wp-content/plugins/wp-staging/languages/)
|
400 |
-
elseif( file_exists( $moFileLocal ) ) {
|
401 |
-
load_textdomain( "wpstg", $moFileGlobal );
|
402 |
-
}
|
403 |
-
// Default file
|
404 |
-
else {
|
405 |
-
load_plugin_textdomain( "wpstg", false, $languagesDirectory );
|
406 |
-
}
|
407 |
-
}
|
408 |
-
|
409 |
-
/**
|
410 |
-
* Check if it is a staging site
|
411 |
-
* @return bool
|
412 |
-
*/
|
413 |
-
private function isStagingSite() {
|
414 |
-
return ("true" === get_option( "wpstg_is_staging_site" ));
|
415 |
-
}
|
416 |
-
|
417 |
-
/**
|
418 |
-
* Initialize licensing functions
|
419 |
-
* @return boolean
|
420 |
-
*/
|
421 |
-
public function initLicensing() {
|
422 |
-
// Add licensing stuff if class exists
|
423 |
-
if( class_exists( 'WPStaging\Backend\Pro\Licensing\Licensing' ) ) {
|
424 |
-
$licensing = new Backend\Pro\Licensing\Licensing();
|
425 |
-
}
|
426 |
-
return false;
|
427 |
-
}
|
428 |
-
|
429 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
// Ensure to include autoloader class
|
11 |
+
require_once __DIR__ . DIRECTORY_SEPARATOR . "Utils" . DIRECTORY_SEPARATOR . "Autoloader.php";
|
12 |
+
|
13 |
+
use WPStaging\Backend\Administrator;
|
14 |
+
use WPStaging\DTO\Settings;
|
15 |
+
use WPStaging\Frontend\Frontend;
|
16 |
+
use WPStaging\Utils\Autoloader;
|
17 |
+
use WPStaging\Utils\Cache;
|
18 |
+
use WPStaging\Utils\Loader;
|
19 |
+
use WPStaging\Utils\Logger;
|
20 |
+
use WPStaging\DI\InjectionAware;
|
21 |
+
use WPStaging\Cron\Cron;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Class WPStaging
|
25 |
+
* @package WPStaging
|
26 |
+
*/
|
27 |
+
final class WPStaging {
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Plugin version
|
31 |
+
*/
|
32 |
+
const VERSION = "2.3.2";
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Plugin name
|
36 |
+
*/
|
37 |
+
const NAME = "WP Staging";
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Plugin slug
|
41 |
+
*/
|
42 |
+
const SLUG = "wp-staging";
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Compatible WP Version
|
46 |
+
*/
|
47 |
+
const WP_COMPATIBLE = "4.9.7";
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Slug: Either wp-staging or wp-staging-pro
|
51 |
+
* @var string
|
52 |
+
*/
|
53 |
+
public $slug;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Absolute plugin path
|
57 |
+
* @var string
|
58 |
+
*/
|
59 |
+
public $pluginPath;
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Services
|
63 |
+
* @var array
|
64 |
+
*/
|
65 |
+
private $services;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Singleton instance
|
69 |
+
* @var WPStaging
|
70 |
+
*/
|
71 |
+
private static $instance;
|
72 |
+
|
73 |
+
/**
|
74 |
+
* WPStaging constructor.
|
75 |
+
*/
|
76 |
+
private function __construct() {
|
77 |
+
|
78 |
+
$file = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR . self::SLUG . ".php";
|
79 |
+
|
80 |
+
$this->registerMain();
|
81 |
+
$this->registerNamespaces();
|
82 |
+
$this->loadLanguages();
|
83 |
+
$this->loadDependencies();
|
84 |
+
$this->defineHooks();
|
85 |
+
// Licensing stuff be loaded in wpstg core to make cron hook available from frontpage
|
86 |
+
$this->initLicensing();
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Method to be executed upon activation of the plugin
|
91 |
+
*/
|
92 |
+
public function onActivation() {
|
93 |
+
$Activation = new \WPStaging\Backend\Activation\Activation();
|
94 |
+
$Activation->install_dependancies();
|
95 |
+
}
|
96 |
+
|
97 |
+
public function registerMain() {
|
98 |
+
// Slug of the plugin
|
99 |
+
$this->slug = plugin_basename( dirname( dirname( dirname( __FILE__ ) ) ) );
|
100 |
+
|
101 |
+
// absolute path to the main plugin dir
|
102 |
+
$this->pluginPath = plugin_dir_path( dirname( dirname( __FILE__ ) ) );
|
103 |
+
|
104 |
+
// URL to apps folder
|
105 |
+
$this->url = plugin_dir_url( dirname( __FILE__ ) );
|
106 |
+
|
107 |
+
// URL to backend public folder folder
|
108 |
+
$this->backend_url = plugin_dir_url( dirname( __FILE__ ) ) . "Backend/public/";
|
109 |
+
|
110 |
+
// URL to frontend public folder folder
|
111 |
+
$this->frontend_url = plugin_dir_url( dirname( __FILE__ ) ) . "Frontend/public/";
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Define Hooks
|
116 |
+
*/
|
117 |
+
public function defineHooks() {
|
118 |
+
$loader = $this->get( "loader" );
|
119 |
+
$loader->addAction( "admin_enqueue_scripts", $this, "enqueueElements", 100 );
|
120 |
+
$loader->addAction( "wp_enqueue_scripts", $this, "enqueueElements", 100 );
|
121 |
+
$this->addIntervals();
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Add new cron time event "weekly"
|
126 |
+
*/
|
127 |
+
public function addIntervals(){
|
128 |
+
$interval = new Cron();
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Scripts and Styles
|
133 |
+
* @param string $hook
|
134 |
+
*/
|
135 |
+
public function enqueueElements( $hook ) {
|
136 |
+
|
137 |
+
// Load this css file on frontend and backend on all pages if current site is a staging site
|
138 |
+
if( $this->isStagingSite() ) {
|
139 |
+
wp_enqueue_style( "wpstg-admin-bar", $this->backend_url . "css/wpstg-admin-bar.css", $this->getVersion() );
|
140 |
+
}
|
141 |
+
|
142 |
+
$availablePages = array(
|
143 |
+
"toplevel_page_wpstg_clone",
|
144 |
+
"wp-staging_page_wpstg-settings",
|
145 |
+
"wp-staging_page_wpstg-tools",
|
146 |
+
"wp-staging_page_wpstg-license",
|
147 |
+
"wp-staging_page_wpstg-welcome",
|
148 |
+
);
|
149 |
+
|
150 |
+
// Load these css and js files only on wp staging admin pages
|
151 |
+
if( !in_array( $hook, $availablePages ) || !is_admin() ) {
|
152 |
+
return;
|
153 |
+
}
|
154 |
+
|
155 |
+
// Load admin js files
|
156 |
+
wp_enqueue_script(
|
157 |
+
"wpstg-admin-script", $this->backend_url . "js/wpstg-admin.js", array("jquery"), $this->getVersion(), false
|
158 |
+
);
|
159 |
+
|
160 |
+
// Load admin css files
|
161 |
+
wp_enqueue_style(
|
162 |
+
"wpstg-admin", $this->backend_url . "css/wpstg-admin.css", $this->getVersion()
|
163 |
+
);
|
164 |
+
|
165 |
+
wp_localize_script( "wpstg-admin-script", "wpstg", array(
|
166 |
+
"nonce" => wp_create_nonce( "wpstg_ajax_nonce" ),
|
167 |
+
"mu_plugin_confirmation" => __(
|
168 |
+
"If confirmed we will install an additional WordPress 'Must Use' plugin. "
|
169 |
+
. "This plugin will allow us to control which plugins are loaded during "
|
170 |
+
. "WP Staging specific operations. Do you wish to continue?", "wpstg"
|
171 |
+
),
|
172 |
+
"plugin_compatibility_settings_problem" => __(
|
173 |
+
"A problem occurred when trying to change the plugin compatibility setting.", "wpstg"
|
174 |
+
),
|
175 |
+
"saved" => __( "Saved", "The settings were saved successfully", "wpstg" ),
|
176 |
+
"status" => __( "Status", "Current request status", "wpstg" ),
|
177 |
+
"response" => __( "Response", "The message the server responded with", "wpstg" ),
|
178 |
+
"blacklist_problem" => __(
|
179 |
+
"A problem occurred when trying to add plugins to backlist.", "wpstg"
|
180 |
+
),
|
181 |
+
"cpuLoad" => $this->getCPULoadSetting(),
|
182 |
+
"settings" => ( object ) array(), // TODO add settings?
|
183 |
+
"tblprefix" => self::getTablePrefix()
|
184 |
+
) );
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Get table prefix of the current site
|
189 |
+
* @return string
|
190 |
+
*/
|
191 |
+
public static function getTablePrefix(){
|
192 |
+
$wpDB = WPStaging::getInstance()->get("wpdb");
|
193 |
+
return $wpDB->prefix;
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Caching and logging folder
|
198 |
+
*
|
199 |
+
* @return string
|
200 |
+
*/
|
201 |
+
public static function getContentDir() {
|
202 |
+
$wp_upload_dir = wp_upload_dir();
|
203 |
+
$path = $wp_upload_dir['basedir'] . '/wp-staging';
|
204 |
+
wp_mkdir_p( $path );
|
205 |
+
return apply_filters( 'wpstg_get_upload_dir', $path . DIRECTORY_SEPARATOR );
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Register used namespaces
|
210 |
+
*/
|
211 |
+
private function registerNamespaces() {
|
212 |
+
$autoloader = new Autoloader();
|
213 |
+
$this->set( "autoloader", $autoloader );
|
214 |
+
|
215 |
+
// Autoloader
|
216 |
+
$autoloader->registerNamespaces( array(
|
217 |
+
"WPStaging" => array(
|
218 |
+
$this->pluginPath . 'apps' . DIRECTORY_SEPARATOR,
|
219 |
+
$this->pluginPath . 'apps' . DIRECTORY_SEPARATOR . 'Core' . DIRECTORY_SEPARATOR,
|
220 |
+
)
|
221 |
+
) );
|
222 |
+
|
223 |
+
// Register namespaces
|
224 |
+
$autoloader->register();
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Get Instance
|
229 |
+
* @return WPStaging
|
230 |
+
*/
|
231 |
+
public static function getInstance() {
|
232 |
+
if( null === static::$instance ) {
|
233 |
+
static::$instance = new static();
|
234 |
+
}
|
235 |
+
|
236 |
+
return static::$instance;
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Prevent cloning
|
241 |
+
* @return void
|
242 |
+
*/
|
243 |
+
private function __clone() {
|
244 |
+
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Prevent unserialization
|
249 |
+
* @return void
|
250 |
+
*/
|
251 |
+
private function __wakeup() {
|
252 |
+
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Load Dependencies
|
257 |
+
*/
|
258 |
+
private function loadDependencies() {
|
259 |
+
// Set loader
|
260 |
+
$this->set( "loader", new Loader() );
|
261 |
+
|
262 |
+
// Set cache
|
263 |
+
$this->set( "cache", new Cache() );
|
264 |
+
|
265 |
+
// Set logger
|
266 |
+
$this->set( "logger", new Logger() );
|
267 |
+
|
268 |
+
// Set settings
|
269 |
+
$this->set( "settings", new Settings() );
|
270 |
+
|
271 |
+
// Set Administrator
|
272 |
+
if( is_admin() ) {
|
273 |
+
new Administrator( $this );
|
274 |
+
} else {
|
275 |
+
new Frontend( $this );
|
276 |
+
}
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* Execute Plugin
|
281 |
+
*/
|
282 |
+
public function run() {
|
283 |
+
$this->get( "loader" )->run();
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Set a variable to DI with given name
|
288 |
+
* @param string $name
|
289 |
+
* @param mixed $variable
|
290 |
+
* @return $this
|
291 |
+
*/
|
292 |
+
public function set( $name, $variable ) {
|
293 |
+
// It is a function
|
294 |
+
if( is_callable( $variable ) )
|
295 |
+
$variable = $variable();
|
296 |
+
|
297 |
+
// Add it to services
|
298 |
+
$this->services[$name] = $variable;
|
299 |
+
|
300 |
+
return $this;
|
301 |
+
}
|
302 |
+
|
303 |
+
/**
|
304 |
+
* Get given name index from DI
|
305 |
+
* @param string $name
|
306 |
+
* @return mixed|null
|
307 |
+
*/
|
308 |
+
public function get( $name ) {
|
309 |
+
return (isset( $this->services[$name] )) ? $this->services[$name] : null;
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* @return string
|
314 |
+
*/
|
315 |
+
public function getVersion() {
|
316 |
+
return self::VERSION;
|
317 |
+
}
|
318 |
+
|
319 |
+
/**
|
320 |
+
* @return string
|
321 |
+
*/
|
322 |
+
public function getName() {
|
323 |
+
return self::NAME;
|
324 |
+
}
|
325 |
+
|
326 |
+
/**
|
327 |
+
* @return string
|
328 |
+
*/
|
329 |
+
public static function getSlug() {
|
330 |
+
return plugin_basename( dirname( dirname( dirname( __FILE__ ) ) ) );
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* Get path to main plugin file
|
335 |
+
* @return string
|
336 |
+
*/
|
337 |
+
public function getPath() {
|
338 |
+
return dirname( dirname( __FILE__ ) );
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Get main plugin url
|
343 |
+
* @return type
|
344 |
+
*/
|
345 |
+
public function getUrl() {
|
346 |
+
return plugin_dir_url( dirname( __FILE__ ) );
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* @return array|mixed|object
|
351 |
+
*/
|
352 |
+
public function getCPULoadSetting() {
|
353 |
+
$options = $this->get( "settings" );
|
354 |
+
$setting = $options->getCpuLoad();
|
355 |
+
|
356 |
+
switch ( $setting ) {
|
357 |
+
case "high":
|
358 |
+
$cpuLoad = 0;
|
359 |
+
break;
|
360 |
+
|
361 |
+
case "medium":
|
362 |
+
$cpuLoad = 1000;
|
363 |
+
break;
|
364 |
+
|
365 |
+
case "low":
|
366 |
+
$cpuLoad = 1500;
|
367 |
+
break;
|
368 |
+
|
369 |
+
case "default":
|
370 |
+
default:
|
371 |
+
$cpuLoad = 1000;
|
372 |
+
}
|
373 |
+
|
374 |
+
return $cpuLoad;
|
375 |
+
}
|
376 |
+
|
377 |
+
/**
|
378 |
+
* Load language file
|
379 |
+
*/
|
380 |
+
public function loadLanguages() {
|
381 |
+
$languagesDirectory = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . self::SLUG . DIRECTORY_SEPARATOR;
|
382 |
+
$languagesDirectory.= "vars" . DIRECTORY_SEPARATOR . "languages" . DIRECTORY_SEPARATOR;
|
383 |
+
|
384 |
+
// Set filter for plugins languages directory
|
385 |
+
$languagesDirectory = apply_filters( "wpstg_languages_directory", $languagesDirectory );
|
386 |
+
|
387 |
+
// Traditional WP plugin locale filter
|
388 |
+
$locale = apply_filters( "plugin_locale", get_locale(), "wpstg" );
|
389 |
+
$moFile = sprintf( '%1$s-%2$s.mo', "wpstg", $locale );
|
390 |
+
|
391 |
+
// Setup paths to current locale file
|
392 |
+
$moFileLocal = $languagesDirectory . $moFile;
|
393 |
+
$moFileGlobal = WP_LANG_DIR . DIRECTORY_SEPARATOR . "wpstg" . DIRECTORY_SEPARATOR . $moFile;
|
394 |
+
|
395 |
+
// Global file (/wp-content/languages/WPSTG)
|
396 |
+
if( file_exists( $moFileGlobal ) ) {
|
397 |
+
load_textdomain( "wpstg", $moFileGlobal );
|
398 |
+
}
|
399 |
+
// Local file (/wp-content/plugins/wp-staging/languages/)
|
400 |
+
elseif( file_exists( $moFileLocal ) ) {
|
401 |
+
load_textdomain( "wpstg", $moFileGlobal );
|
402 |
+
}
|
403 |
+
// Default file
|
404 |
+
else {
|
405 |
+
load_plugin_textdomain( "wpstg", false, $languagesDirectory );
|
406 |
+
}
|
407 |
+
}
|
408 |
+
|
409 |
+
/**
|
410 |
+
* Check if it is a staging site
|
411 |
+
* @return bool
|
412 |
+
*/
|
413 |
+
private function isStagingSite() {
|
414 |
+
return ("true" === get_option( "wpstg_is_staging_site" ));
|
415 |
+
}
|
416 |
+
|
417 |
+
/**
|
418 |
+
* Initialize licensing functions
|
419 |
+
* @return boolean
|
420 |
+
*/
|
421 |
+
public function initLicensing() {
|
422 |
+
// Add licensing stuff if class exists
|
423 |
+
if( class_exists( 'WPStaging\Backend\Pro\Licensing\Licensing' ) ) {
|
424 |
+
$licensing = new Backend\Pro\Licensing\Licensing();
|
425 |
+
}
|
426 |
+
return false;
|
427 |
+
}
|
428 |
+
|
429 |
+
}
|
readme.txt
CHANGED
@@ -1,289 +1,200 @@
|
|
1 |
-
=== WP Staging - DB & File Duplicator & Migration ===
|
2 |
-
|
3 |
-
Author URL: https://wordpress.org/plugins/wp-staging
|
4 |
-
Plugin URL: https://wordpress.org/plugins/wp-staging
|
5 |
-
Contributors: ReneHermi, WP-Staging
|
6 |
-
Donate link: https://wordpress.org/plugins/wp-staging
|
7 |
-
License: GPLv2 or later
|
8 |
-
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
-
Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
|
10 |
-
Requires at least: 3.6+
|
11 |
-
Tested up to: 4.9
|
12 |
-
Stable tag: 2.3.
|
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.
|
16 |
-
|
17 |
-
== Description ==
|
18 |
-
|
19 |
-
<strong>This cloning and staging plugin is well tested but work in progress. <br><br>
|
20 |
-
If you find any issue, please open a [support ticket](https://wp-staging.com/support/ "support ticket").
|
21 |
-
</strong>
|
22 |
-
<br /><br />
|
23 |
-
<strong>Note: </strong> For pushing & migrating plugins and theme files to live site, check out [https://wp-staging.com/](https://wp-staging.com/ "WP Staging Pro")
|
24 |
-
<br /><br />
|
25 |
-
<blockquote>
|
26 |
-
<h4> WP Staging for WordPress Migration </h4>
|
27 |
-
This duplicator plugin allows you to create an staging or development environment in seconds* <br /> <br />
|
28 |
-
It creates a clone of your website into a subfolder of your main WordPress installation including an entire copy of your database.
|
29 |
-
This sounds pretty simple and yes it is! All the hard time-consumptive database and file copying stuff including url replacements is done in the background.
|
30 |
-
<br /> <br />
|
31 |
-
I created this plugin because all other solutions are way too complex, overloaded with dozens of options or having server requirements which are not available on most shared hosting solutions.
|
32 |
-
All these reasons prevent user from testing new plugins and updates first before installing them on their live website, so its time to release a plugin which has the potential to be merged into everyone´s wordpress workflow.
|
33 |
-
<br /><br />
|
34 |
-
<p><small><em>* Time of creation depends on size of your database and file size</em></small></p>
|
35 |
-
</blockquote>
|
36 |
-
|
37 |
-
WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!
|
38 |
-
<p>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.</p>
|
39 |
-
|
40 |
-
|
41 |
-
[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
|
42 |
-
|
43 |
-
= Main Features =
|
44 |
-
|
45 |
-
* <strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!
|
46 |
-
* <strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power
|
47 |
-
* <strong>Safe: </strong> Access to staging site is granted for administrators only.
|
48 |
-
<br /><br />
|
49 |
-
<strong>More safe:</strong>
|
50 |
-
<br>
|
51 |
-
* Admin bar reflects that you are working on a staging site
|
52 |
-
* Extensive logging if duplication or migration process should fail.
|
53 |
-
* New: Compatible to All In One WP Security & Firewall
|
54 |
-
|
55 |
-
= What does not work or is not tested when running wordpress migration? =
|
56 |
-
|
57 |
-
* Wordpress migration of wordpress multisites (not tested)
|
58 |
-
* WordPress duplicating process on windows server (not tested but will probably work)
|
59 |
-
Edit: Duplication on windows server seems to be working well: [Read more](https://wordpress.org/support/topic/wont-copy-files?replies=5 "Read more")
|
60 |
-
|
61 |
-
|
62 |
-
<strong>Change your workflow of updating themes and plugins data:</strong>
|
63 |
-
|
64 |
-
1. Use WP Staging for migration of a production website to a clone site for staging purposes
|
65 |
-
2. Customize theme, configuration and plugins or install new plugins
|
66 |
-
3. Test everything on your staging site first
|
67 |
-
4. Everything running as expected? You are on the save side for migration of all these modifications to your production site!
|
68 |
-
|
69 |
-
|
70 |
-
<h3> Why should i use a staging website? </h3>
|
71 |
-
|
72 |
-
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.
|
73 |
-
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.
|
74 |
-
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)
|
75 |
-
|
76 |
-
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
|
77 |
-
up-to-date copy of your website.
|
78 |
-
|
79 |
-
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.
|
80 |
-
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.
|
81 |
-
|
82 |
-
<strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
|
83 |
-
|
84 |
-
<h3> Can´t i just use my local wordpress development copy for testing like xampp / lampp? </h3>
|
85 |
-
|
86 |
-
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
|
87 |
-
of your local copy is working on your live website exactely as you would expect it.
|
88 |
-
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
|
89 |
-
the cpu performance can lead to unexpected results on your production website.
|
90 |
-
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.
|
91 |
-
|
92 |
-
This is were WP Staging steps in... Site cloning and staging site creation simplified!
|
93 |
-
|
94 |
-
<h3>I just want to migrate the database from one installation to another</h3>
|
95 |
-
If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.
|
96 |
-
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.
|
97 |
-
Both tools are excellent cooperating eachother.
|
98 |
-
|
99 |
-
<h3>What are the benefits compared to a plugin like Duplicator?</h3>
|
100 |
-
At first, i love the [Duplicator plugin](https://wordpress.org/plugins/duplicator/ "Duplicator plugin"). Duplicator is a great tool for migrating from development site to production one or from production site to development one.
|
101 |
-
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.
|
102 |
-
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.
|
103 |
-
|
104 |
-
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
|
105 |
-
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!
|
106 |
-
|
107 |
-
= I need you feedback =
|
108 |
-
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:
|
109 |
-
Please open a [support request](https://wordpress.org/support/plugin/wp-staging/ "support request") and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines.
|
110 |
-
|
111 |
-
|
112 |
-
= Important =
|
113 |
-
|
114 |
-
Permalinks are disabled on the staging site because the staging site is cloned into a subfolder and permalinks are not working on all systems
|
115 |
-
without doing changes to the .htaccess (Apache server) or nginx.conf (Nginx Server).
|
116 |
-
[Read here](https://wp-staging.com/docs/activate-permalinks-staging-site/ "activate permalinks on staging site") how to activate permalinks on the staging site.
|
117 |
-
|
118 |
-
|
119 |
-
= How to install and setup? =
|
120 |
-
Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.
|
121 |
-
After installation goto the settings page 'Staging' and do your adjustments there.
|
122 |
-
|
123 |
-
|
124 |
-
== Frequently Asked Questions ==
|
125 |
-
|
126 |
-
* I can not login to the staging site
|
127 |
-
If you are using a security plugin like All In One WP Security & Firewall you need to install latest version of WP Staging.
|
128 |
-
Go to WP Staging > Settings and add the slug to the custom login page which you set up in All In One WP Security & Firewall plugin.
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
== Official Site ==
|
133 |
-
https://wp-staging.com
|
134 |
-
|
135 |
-
== Installation ==
|
136 |
-
1. Download the file "wp-staging.zip":
|
137 |
-
2. Upload and install it via the WordPress plugin backend wp-admin > plugins > add new > uploads
|
138 |
-
3. Activate the plugin through the 'Plugins' menu in WordPress.
|
139 |
-
|
140 |
-
== Screenshots ==
|
141 |
-
|
142 |
-
1. Step 1. Create new WordPress staging site
|
143 |
-
2. Step 2. Scanning your website for files and database tables
|
144 |
-
3. Step 3. Wordpress Staging site creation in progress
|
145 |
-
4. Finish!
|
146 |
-
|
147 |
-
== Changelog ==
|
148 |
-
|
149 |
-
= 2.3.
|
150 |
-
* Fix:
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
*
|
156 |
-
|
157 |
-
|
158 |
-
*
|
159 |
-
* New:
|
160 |
-
*
|
161 |
-
*
|
162 |
-
*
|
163 |
-
* Fix:
|
164 |
-
* Fix:
|
165 |
-
* Fix:
|
166 |
-
* Fix:
|
167 |
-
* Fix:
|
168 |
-
* Fix:
|
169 |
-
*
|
170 |
-
*
|
171 |
-
|
172 |
-
|
173 |
-
*
|
174 |
-
|
175 |
-
= 2.2.
|
176 |
-
*
|
177 |
-
|
178 |
-
|
179 |
-
* New: Add
|
180 |
-
* New:
|
181 |
-
* New:
|
182 |
-
* New: Add
|
183 |
-
* New:
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
* New:
|
200 |
-
|
201 |
-
|
202 |
-
= 2.2.5 =
|
203 |
-
* New: Compatible to WP 4.9.5
|
204 |
-
* New: Allow to select and copy extra folders that are on the root level
|
205 |
-
* New: Use fully custom login form to prevent access denied issues on sites where access to wp-login.php is denied or redirection plugins are used
|
206 |
-
* Fix: Incorrect login path to staging site if WordPress is installed in subdirectory
|
207 |
-
* Fix: Login url is wrong if WP is installed in subfolder
|
208 |
-
* Fix: If PHP 5.6.34 is used, the cloning process could be unfinished due to use of private member in protected class
|
209 |
-
* Tweak: Only wp root folders are pre selected before cloning is starting
|
210 |
-
* Tweak: Change WP_HOME or WP_SITEURL constants of staging site if they are defined in wp-config.php
|
211 |
-
|
212 |
-
|
213 |
-
= 2.2.4 =
|
214 |
-
* New: Replace even hardcoded links and server path by using search & replace through all staging site database tables
|
215 |
-
* New: New and improved progress bar with elapsed time
|
216 |
-
* Fix: Cancel cloning does not clean up unused tables and leads to duplicate tables
|
217 |
-
* Tweak: Wordings in rating admin notice
|
218 |
-
* Tweak: Better error messages
|
219 |
-
* Tweak: Open staging site in same window from login request
|
220 |
-
* Fix: Set meta noindex for staging site to make it non indexable for search engines
|
221 |
-
|
222 |
-
|
223 |
-
= 2.2.3 =
|
224 |
-
* Fix: Change default login link to wp-admin
|
225 |
-
* Fix: Unneccessary duplicates of wpstg tables in db
|
226 |
-
|
227 |
-
= 2.2.2 =
|
228 |
-
* Fix: Undefined property: stdClass::$loginSlug
|
229 |
-
|
230 |
-
= 2.2.1 =
|
231 |
-
* New: Option to set Custom Login Link if there is one
|
232 |
-
* New: Set meta noindex for staging site to make it non indexable for search engines
|
233 |
-
* New: Better multiple folder selection. Allows to unselect a parent folder without collapsing all child folders
|
234 |
-
* New: Sorted list of folders to copy
|
235 |
-
* Fix: Can not login to staging site if plugin All In One WP Security & Firewall is used
|
236 |
-
* Fix: Staging site not reachable because permalinks are not disabled under certain conditions
|
237 |
-
|
238 |
-
= 2.2.0 =
|
239 |
-
* Fix: Old staging site is not listed and pushing is not working properly if plugin is updated from wp staging version 1.6 and lower
|
240 |
-
|
241 |
-
= 2.1.9 =
|
242 |
-
* New: Performance improvement increase db query limit to 5000
|
243 |
-
* New: Detect automatically if WordPress is installed in sub folder
|
244 |
-
* Tweak: Tested up to WP 4.9.4
|
245 |
-
* Fix: Updating from an old version 1.1.6 < to latest version deletes the staging sites listing table
|
246 |
-
* Fix: Reduce memory size of the logging window to prevent browser timeouts
|
247 |
-
* Fix: Can not copy db table if table name contains the db prefix multiple times
|
248 |
-
* Fix: Some excluded folders are not ignored during copy process
|
249 |
-
* Fix: mod_security is causing script termination
|
250 |
-
* Fix: Skip directory listings for symlinks
|
251 |
-
|
252 |
-
= 2.1.8 =
|
253 |
-
* Fix: Increase the max memory consumption
|
254 |
-
|
255 |
-
= 2.1.7 =
|
256 |
-
* Tweak: Return more human readable error notices
|
257 |
-
* Fix: Cloning process stops due to file permission issue
|
258 |
-
* Fix: Exclude WP Super Cache from copying process because of bug in WP Super Cache, see https://github.com/Automattic/wp-super-cache/issues/505
|
259 |
-
|
260 |
-
= 2.1.6 =
|
261 |
-
* New: increased speed for cloning process by factor 5, using new method of file agregation
|
262 |
-
* New: Skip files larger than 8MB
|
263 |
-
* Fix: Additional checks to ensure that the root path is never deleted
|
264 |
-
* New: Compatible up to WP 4.9.1
|
265 |
-
|
266 |
-
= 2.1.5 =
|
267 |
-
* Fix. Change link to support
|
268 |
-
* Fix: Missing files in clone site if copy file limit is higher than 1
|
269 |
-
|
270 |
-
= 2.1.4 =
|
271 |
-
* Fix: Link to the staging site is missing a slash if WordPress is installed in subdir
|
272 |
-
* Tweak: Allow file copy limit 1 to prevent copy timeouts
|
273 |
-
|
274 |
-
= 2.1.3 =
|
275 |
-
* New: Add more details to tools->system info log for better debugging
|
276 |
-
* New: Add buttons to select all default wp tables with one click
|
277 |
-
* New: Show used db table in list of staging sites
|
278 |
-
* Fix: Delete staging site not possible if db prefix is same as one of the live site
|
279 |
-
* Fix: Edit/Update clone function is duplicating tables.
|
280 |
-
* Fix: Other staging site can be overwritten when Edit/Update clone function is executed
|
281 |
-
* Fix: Several improvements to improve reliability and prevent timeouts and fatal errors during cloning
|
282 |
-
|
283 |
-
Complete changelog: [https://wp-staging.com/changelog.txt](https://wp-staging.com/changelog.txt)
|
284 |
-
|
285 |
-
== Upgrade Notice ==
|
286 |
-
|
287 |
-
= 2.2.8 =
|
288 |
-
* New: Compatible to WordPress 4.9.6
|
289 |
-
|
1 |
+
=== WP Staging - DB & File Duplicator & Migration ===
|
2 |
+
|
3 |
+
Author URL: https://wordpress.org/plugins/wp-staging
|
4 |
+
Plugin URL: https://wordpress.org/plugins/wp-staging
|
5 |
+
Contributors: ReneHermi, WP-Staging
|
6 |
+
Donate link: https://wordpress.org/plugins/wp-staging
|
7 |
+
License: GPLv2 or later
|
8 |
+
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
+
Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
|
10 |
+
Requires at least: 3.6+
|
11 |
+
Tested up to: 4.9
|
12 |
+
Stable tag: 2.3.2
|
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.
|
16 |
+
|
17 |
+
== Description ==
|
18 |
+
|
19 |
+
<strong>This cloning and staging plugin is well tested but work in progress. <br><br>
|
20 |
+
If you find any issue, please open a [support ticket](https://wp-staging.com/support/ "support ticket").
|
21 |
+
</strong>
|
22 |
+
<br /><br />
|
23 |
+
<strong>Note: </strong> For pushing & migrating plugins and theme files to live site, check out [https://wp-staging.com/](https://wp-staging.com/ "WP Staging Pro")
|
24 |
+
<br /><br />
|
25 |
+
<blockquote>
|
26 |
+
<h4> WP Staging for WordPress Migration </h4>
|
27 |
+
This duplicator plugin allows you to create an staging or development environment in seconds* <br /> <br />
|
28 |
+
It creates a clone of your website into a subfolder of your main WordPress installation including an entire copy of your database.
|
29 |
+
This sounds pretty simple and yes it is! All the hard time-consumptive database and file copying stuff including url replacements is done in the background.
|
30 |
+
<br /> <br />
|
31 |
+
I created this plugin because all other solutions are way too complex, overloaded with dozens of options or having server requirements which are not available on most shared hosting solutions.
|
32 |
+
All these reasons prevent user from testing new plugins and updates first before installing them on their live website, so its time to release a plugin which has the potential to be merged into everyone´s wordpress workflow.
|
33 |
+
<br /><br />
|
34 |
+
<p><small><em>* Time of creation depends on size of your database and file size</em></small></p>
|
35 |
+
</blockquote>
|
36 |
+
|
37 |
+
WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!
|
38 |
+
<p>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.</p>
|
39 |
+
|
40 |
+
|
41 |
+
[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
|
42 |
+
|
43 |
+
= Main Features =
|
44 |
+
|
45 |
+
* <strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!
|
46 |
+
* <strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power
|
47 |
+
* <strong>Safe: </strong> Access to staging site is granted for administrators only.
|
48 |
+
<br /><br />
|
49 |
+
<strong>More safe:</strong>
|
50 |
+
<br>
|
51 |
+
* Admin bar reflects that you are working on a staging site
|
52 |
+
* Extensive logging if duplication or migration process should fail.
|
53 |
+
* New: Compatible to All In One WP Security & Firewall
|
54 |
+
|
55 |
+
= What does not work or is not tested when running wordpress migration? =
|
56 |
+
|
57 |
+
* Wordpress migration of wordpress multisites (not tested)
|
58 |
+
* WordPress duplicating process on windows server (not tested but will probably work)
|
59 |
+
Edit: Duplication on windows server seems to be working well: [Read more](https://wordpress.org/support/topic/wont-copy-files?replies=5 "Read more")
|
60 |
+
|
61 |
+
|
62 |
+
<strong>Change your workflow of updating themes and plugins data:</strong>
|
63 |
+
|
64 |
+
1. Use WP Staging for migration of a production website to a clone site for staging purposes
|
65 |
+
2. Customize theme, configuration and plugins or install new plugins
|
66 |
+
3. Test everything on your staging site first
|
67 |
+
4. Everything running as expected? You are on the save side for migration of all these modifications to your production site!
|
68 |
+
|
69 |
+
|
70 |
+
<h3> Why should i use a staging website? </h3>
|
71 |
+
|
72 |
+
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.
|
73 |
+
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.
|
74 |
+
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)
|
75 |
+
|
76 |
+
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
|
77 |
+
up-to-date copy of your website.
|
78 |
+
|
79 |
+
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.
|
80 |
+
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.
|
81 |
+
|
82 |
+
<strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
|
83 |
+
|
84 |
+
<h3> Can´t i just use my local wordpress development copy for testing like xampp / lampp? </h3>
|
85 |
+
|
86 |
+
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
|
87 |
+
of your local copy is working on your live website exactely as you would expect it.
|
88 |
+
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
|
89 |
+
the cpu performance can lead to unexpected results on your production website.
|
90 |
+
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.
|
91 |
+
|
92 |
+
This is were WP Staging steps in... Site cloning and staging site creation simplified!
|
93 |
+
|
94 |
+
<h3>I just want to migrate the database from one installation to another</h3>
|
95 |
+
If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.
|
96 |
+
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.
|
97 |
+
Both tools are excellent cooperating eachother.
|
98 |
+
|
99 |
+
<h3>What are the benefits compared to a plugin like Duplicator?</h3>
|
100 |
+
At first, i love the [Duplicator plugin](https://wordpress.org/plugins/duplicator/ "Duplicator plugin"). Duplicator is a great tool for migrating from development site to production one or from production site to development one.
|
101 |
+
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.
|
102 |
+
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.
|
103 |
+
|
104 |
+
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
|
105 |
+
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!
|
106 |
+
|
107 |
+
= I need you feedback =
|
108 |
+
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:
|
109 |
+
Please open a [support request](https://wordpress.org/support/plugin/wp-staging/ "support request") and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines.
|
110 |
+
|
111 |
+
|
112 |
+
= Important =
|
113 |
+
|
114 |
+
Permalinks are disabled on the staging site because the staging site is cloned into a subfolder and permalinks are not working on all systems
|
115 |
+
without doing changes to the .htaccess (Apache server) or nginx.conf (Nginx Server).
|
116 |
+
[Read here](https://wp-staging.com/docs/activate-permalinks-staging-site/ "activate permalinks on staging site") how to activate permalinks on the staging site.
|
117 |
+
|
118 |
+
|
119 |
+
= How to install and setup? =
|
120 |
+
Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.
|
121 |
+
After installation goto the settings page 'Staging' and do your adjustments there.
|
122 |
+
|
123 |
+
|
124 |
+
== Frequently Asked Questions ==
|
125 |
+
|
126 |
+
* I can not login to the staging site
|
127 |
+
If you are using a security plugin like All In One WP Security & Firewall you need to install latest version of WP Staging.
|
128 |
+
Go to WP Staging > Settings and add the slug to the custom login page which you set up in All In One WP Security & Firewall plugin.
|
129 |
+
|
130 |
+
|
131 |
+
|
132 |
+
== Official Site ==
|
133 |
+
https://wp-staging.com
|
134 |
+
|
135 |
+
== Installation ==
|
136 |
+
1. Download the file "wp-staging.zip":
|
137 |
+
2. Upload and install it via the WordPress plugin backend wp-admin > plugins > add new > uploads
|
138 |
+
3. Activate the plugin through the 'Plugins' menu in WordPress.
|
139 |
+
|
140 |
+
== Screenshots ==
|
141 |
+
|
142 |
+
1. Step 1. Create new WordPress staging site
|
143 |
+
2. Step 2. Scanning your website for files and database tables
|
144 |
+
3. Step 3. Wordpress Staging site creation in progress
|
145 |
+
4. Finish!
|
146 |
+
|
147 |
+
== Changelog ==
|
148 |
+
|
149 |
+
= 2.3.2 =
|
150 |
+
* Fix: Do not search & replace mail addresses
|
151 |
+
|
152 |
+
= 2.3.1 =
|
153 |
+
* Fix: Creating a new clone resets the custom wp staging user settings. E.g. ignores authentication setting
|
154 |
+
* Fix: Search & replace path not correct if scheme http/https is not identical in wp-config and db wp_options
|
155 |
+
* New: Setting to specify the maximum allowed file size to copy
|
156 |
+
|
157 |
+
= 2.3.0 =
|
158 |
+
* Security: Do not allow to create a new staging site into a subfolder which already exists
|
159 |
+
* New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning.
|
160 |
+
* New: Add multisite informations in system info log
|
161 |
+
* New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning.
|
162 |
+
* New: Use the new progress bar for clone updating process
|
163 |
+
* Fix: Progress bar for step 'database' is not filling up to 100%
|
164 |
+
* 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)
|
165 |
+
* Fix: Progress bar not shown as intented for clone updating process
|
166 |
+
* Fix: Can not open upload folder in file selection menu
|
167 |
+
* Fix: Undefined object $this->tables
|
168 |
+
* Fix: wp-config.php not copied when previous clone updating process has been failed
|
169 |
+
* Fix: Parameter must be an array or an object that implements Callable
|
170 |
+
* Fix: Skip search & replace for objects where key is null
|
171 |
+
* Fix: Search & Replace not working if serialized object contains __PHP_Incomplete_Class_Name
|
172 |
+
* Tweaks: remove term "error" from several log entries
|
173 |
+
* Tweak: Remove certain debugging notices from the default log window
|
174 |
+
|
175 |
+
= 2.2.9 =
|
176 |
+
* Fix: Missing trailingslash results to wrong absolute paths in database after Search & Replace operation
|
177 |
+
|
178 |
+
= 2.2.8 =
|
179 |
+
* New: Add filter 'wpstg_filter_options_replace' to exclude certain tables from updating while cloning
|
180 |
+
* New: Exclude tables for plugin wp_mail_smtp
|
181 |
+
* New: Support for custom upload folder. For instance, if upload folder has been renamed and removed outsite wp-content folder
|
182 |
+
* New: Add datetime timestamp internally to clone. (Used in WP Staging pro)
|
183 |
+
* New: Add filter 'wpstg_fiter_search_replace_rows' to exclude certain tables from search & replace
|
184 |
+
* New: Supports search & replace for revslider image slider and several visual editors which are using non default serialized data
|
185 |
+
* New: Add new setting which allow to specify the search & replace processing query limit
|
186 |
+
* New: Compatible to WordPress 4.9.6
|
187 |
+
|
188 |
+
|
189 |
+
= 2.2.7 =
|
190 |
+
* Fix: Serialize replace is not working properly for serialized strings
|
191 |
+
* Fix: WP_SITEURL & WP_HOME not replaced if constant contains php generated string
|
192 |
+
|
193 |
+
|
194 |
+
Complete changelog: [https://wp-staging.com/changelog.txt](https://wp-staging.com/changelog.txt)
|
195 |
+
|
196 |
+
== Upgrade Notice ==
|
197 |
+
|
198 |
+
= 2.2.8 =
|
199 |
+
* New: Compatible to WordPress 4.9.6
|
200 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
11 |
* Text Domain: wpstg
|
12 |
* Domain Path: /languages/
|
13 |
|
7 |
* Author: WP-Staging
|
8 |
* Author URI: https://wp-staging.com
|
9 |
* Contributors: ReneHermi, ilgityildirim
|
10 |
+
* Version: 2.3.2
|
11 |
* Text Domain: wpstg
|
12 |
* Domain Path: /languages/
|
13 |
|