PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.0
4.11.0 4.10.0 4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Staging / Sites.php
wp-staging / Staging Last commit date
Ajax 1 day ago Dto 1 day ago Interfaces 1 day ago Jobs 1 day ago Renderer 1 day ago Service 1 day ago Tasks 1 day ago Traits 1 day ago CloneOptions.php 1 day ago FirstRun.php 1 day ago Sites.php 1 day ago StagingServiceProvider.php 1 day ago
Sites.php
414 lines
1 <?php
2
3 namespace WPStaging\Staging;
4
5 use Exception;
6 use WPStaging\Framework\Exceptions\WPStagingException;
7 use WPStaging\Staging\Dto\StagingSiteDto;
8
9
10
11
12
13
14
15
16
17
18 class Sites
19 {
20
21
22
23 const STAGING_SITES_OPTION = 'wpstg_staging_sites';
24
25
26
27
28 const STAGING_LOGIN_LINK_SETTINGS = 'wpstg_login_link_settings';
29
30
31
32
33
34 const OLD_STAGING_SITES_OPTION = 'wpstg_existing_clones_beta';
35
36
37
38
39
40 const BACKUP_STAGING_SITES_OPTION = 'wpstg_staging_sites_backup';
41
42
43
44
45
46 const MISSING_CLONE_NAME_ROUTINE_EXECUTED = 'wpstg_missing_cloneName_routine_executed';
47
48
49
50
51 const STAGING_EXCLUDED_FILES_OPTION = 'wpstg_clone_excluded_files_list';
52
53
54
55
56 const STAGING_EXCLUDED_GD_FILES_OPTION = 'wpstg_clone_excluded_gd_files_list';
57
58
59
60
61 const THROW_EXCEPTION = true;
62
63
64
65
66
67
68
69 public function getSortedStagingSites()
70 {
71 $stagingSites = $this->tryGettingStagingSites(self::THROW_EXCEPTION);
72
73
74 if (empty($stagingSites) || count($stagingSites) === 1) {
75 return $stagingSites;
76 }
77
78
79 uasort($stagingSites, function ($site1, $site2) {
80
81
82 if ($site1['datetime'] === $site2['datetime']) {
83 return strcmp($site2['directoryName'], $site1['directoryName']);
84 }
85
86 if (!isset($site1['datetime'])) {
87 return 1;
88 }
89
90 if (!isset($site2['datetime'])) {
91 return -1;
92 }
93
94 return $site2['datetime'] < $site1['datetime'] ? -1 : 1;
95 });
96
97 return $stagingSites;
98 }
99
100
101
102
103
104
105
106 public function upgradeStagingSitesOption()
107 {
108 $newSitesOption = get_option(self::STAGING_SITES_OPTION, []);
109
110
111 if (!is_array($newSitesOption)) {
112 $newSitesOption = [];
113 }
114
115
116 $oldSitesOption = get_option(self::OLD_STAGING_SITES_OPTION, []);
117
118
119 if (empty($oldSitesOption)) {
120 return;
121 }
122
123
124 $allStagingSites = $newSitesOption;
125
126 foreach ($oldSitesOption as $oldSiteSlug => $oldSite) {
127
128 if (!array_key_exists($oldSiteSlug, $allStagingSites)) {
129 $allStagingSites[$oldSiteSlug] = $oldSite;
130 continue;
131 }
132
133
134 if ($allStagingSites[$oldSiteSlug]['path'] === $oldSite['path']) {
135 continue;
136 }
137
138
139 $i = 0;
140
141 do {
142 $oldSiteSlug = $oldSiteSlug . '_' . $i;
143 } while (array_key_exists($oldSiteSlug, $allStagingSites));
144
145 $allStagingSites[$oldSiteSlug] = $oldSite;
146 }
147
148 if ($this->updateStagingSites($allStagingSites)) {
149
150 update_option(self::BACKUP_STAGING_SITES_OPTION, $oldSitesOption, false);
151 delete_option(self::OLD_STAGING_SITES_OPTION);
152 }
153 }
154
155
156
157
158
159
160
161
162 public function tryGettingStagingSites(bool $throwException = false): array
163 {
164 $stagingSites = get_option(self::STAGING_SITES_OPTION, []);
165 if (empty($stagingSites)) {
166 return [];
167 }
168
169 if (is_array($stagingSites)) {
170 return $stagingSites;
171 }
172
173 if ($throwException) {
174 throw new WPStagingException('Staging sites option is not an array.');
175 }
176
177 return [];
178 }
179
180
181
182
183
184
185
186 public function updateStagingSites($stagingSites)
187 {
188 return update_option(self::STAGING_SITES_OPTION, $stagingSites, false);
189 }
190
191
192
193
194 public function addMissingCloneNameUpgradeStructure()
195 {
196 $isAdded = get_option(self::MISSING_CLONE_NAME_ROUTINE_EXECUTED, false);
197 if ($isAdded) {
198 return;
199 }
200
201
202 $sites = $this->tryGettingStagingSites();
203
204
205 if (empty($sites)) {
206 update_option(self::MISSING_CLONE_NAME_ROUTINE_EXECUTED, true);
207 return;
208 }
209
210
211 foreach ($sites as $key => $site) {
212 if (isset($sites[$key]['cloneName'])) {
213 continue;
214 }
215
216 $sites[$key]['cloneName'] = $sites[$key]['directoryName'];
217 }
218
219 $this->updateStagingSites($sites);
220 update_option(self::MISSING_CLONE_NAME_ROUTINE_EXECUTED, true);
221 }
222
223
224
225
226
227
228
229 public function sanitizeDirectoryName($cloneName)
230 {
231 $cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($cloneName));
232 return substr($cloneDirectoryName, 0, 16);
233 }
234
235
236
237
238
239
240
241
242 public function generateStagingSiteName(string $fallback): string
243 {
244 $nameList = [
245 'enterprise',
246 'voyager',
247 'defiant',
248 'discovery',
249 'excelsior',
250 'intrepid',
251 'constitution',
252 'reliant',
253 'grissom',
254 'yamato',
255 'excelsior',
256 'venture',
257 'cerritos',
258 'prometheus',
259 'bellerophon',
260 'sanpablo',
261 'sutherland',
262 'shenzhou',
263 'titan',
264 'reliant',
265 'stargazer',
266 'franklin',
267 'protostar',
268 ];
269
270 shuffle($nameList);
271
272
273 $existingDirectoryNames = wp_list_pluck($this->tryGettingStagingSites(), 'directoryName');
274
275 foreach ($nameList as $name) {
276 $name = $this->sanitizeDirectoryName(sanitize_text_field($name));
277 if (!empty($name) && $this->isNameAvailableForNewSite($name, $existingDirectoryNames)) {
278 return $name;
279 }
280 }
281
282 $fallback = $this->sanitizeDirectoryName($fallback);
283 if (!empty($fallback) && $this->isNameAvailableForNewSite($fallback, $existingDirectoryNames)) {
284 return $fallback;
285 }
286
287 for ($i = 1; $i <= 10000; $i++) {
288 $name = $this->sanitizeDirectoryName(sprintf('staging-%d', $i));
289 if ($this->isNameAvailableForNewSite($name, $existingDirectoryNames)) {
290 return $name;
291 }
292 }
293
294 return empty($fallback) ? 'staging' : $fallback;
295 }
296
297
298
299
300
301
302
303
304
305
306 private function isNameAvailableForNewSite(string $name, array $existingDirectoryNames): bool
307 {
308 if (file_exists(trailingslashit(get_home_path()) . $name)) {
309 return false;
310 }
311
312 return !in_array($name, $existingDirectoryNames, true);
313 }
314
315
316
317
318
319
320
321
322 public function isCloneExists($directoryName)
323 {
324 $cloneDirectoryPath = trailingslashit(get_home_path()) . $directoryName;
325 if (is_file($cloneDirectoryPath)) {
326 return sprintf(esc_html__("Warning: Use another site name! A file named %s already exists where the staging site would be created.", 'wp-staging'), $directoryName);
327 }
328
329 if (!wpstg_is_empty_dir($cloneDirectoryPath)) {
330 return sprintf(esc_html__("Warning: Use another site name! Clone destination directory %s already exists and is not empty. As default, WP STAGING uses the site name as subdirectory for the clone.", 'wp-staging'), $cloneDirectoryPath);
331 }
332
333 $stagingSites = $this->tryGettingStagingSites();
334 foreach ($stagingSites as $site) {
335 if ($site['directoryName'] === $directoryName) {
336 return sprintf(esc_html__("Site name %s is already in use, please choose another name for the staging site.", "wp-staging"), $directoryName);
337 }
338 }
339
340 return false;
341 }
342
343
344
345
346
347 public function getStagingDirectories(): array
348 {
349 $stagingSites = $this->tryGettingStagingSites();
350 return wp_list_pluck($stagingSites, 'path');
351 }
352
353
354
355
356
357
358
359 public function getStagingSiteDtoByCloneId(string $cloneId): StagingSiteDto
360 {
361 $stagingSites = $this->tryGettingStagingSites();
362 if (empty($stagingSites)) {
363 throw new Exception('No staging sites found.');
364 }
365
366 if (!array_key_exists($cloneId, $stagingSites)) {
367 throw new Exception('Staging site not found.');
368 }
369
370 $stagingSiteArray = $stagingSites[$cloneId];
371 $stagingSiteDto = new StagingSiteDto();
372 $stagingSiteDto->hydrate($stagingSiteArray);
373 $stagingSiteDto->setCloneId($cloneId);
374
375 return $stagingSiteDto;
376 }
377
378
379
380
381
382
383
384 public function getStagingSiteDtoByCloneName(string $cloneName): StagingSiteDto
385 {
386 $stagingSites = $this->tryGettingStagingSites();
387 if (empty($stagingSites)) {
388 throw new Exception('No staging sites found.');
389 }
390
391 foreach ($stagingSites as $cloneId => $stagingSiteArray) {
392 if ($stagingSiteArray['cloneName'] === $cloneName) {
393 $stagingSiteDto = new StagingSiteDto();
394 $stagingSiteDto->hydrate($stagingSiteArray);
395 $stagingSiteDto->setCloneId($cloneId);
396
397 return $stagingSiteDto;
398 }
399 }
400
401 throw new Exception('Staging site not found.');
402 }
403
404
405
406
407
408 public function isExistingClone(string $clone): bool
409 {
410 $existingClones = get_option(self::STAGING_SITES_OPTION, []);
411 return isset($existingClones[$clone]);
412 }
413 }
414