PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.3.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.3.0
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 11 months ago Dto 11 months ago Interfaces 11 months ago Jobs 11 months ago Service 11 months ago Tasks 10 months ago Traits 11 months ago CloneOptions.php 1 year ago FirstRun.php 10 months ago Sites.php 1 year ago StagingServiceProvider.php 11 months ago
Sites.php
304 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 * Class Sites
11 *
12 * This is used to manage settings on the staging site
13 *
14 * @package WPStaging\Staging
15 *
16 * @todo Manage staging sites option CRUD here?
17 */
18 class Sites
19 {
20 /**
21 * The option that stores the staging sites
22 */
23 const STAGING_SITES_OPTION = 'wpstg_staging_sites';
24
25 /**
26 * The option that stores login link settings
27 */
28 const STAGING_LOGIN_LINK_SETTINGS = 'wpstg_login_link_settings';
29
30 /**
31 * The old option that was used to store the staging sites
32 * @deprecated 4.0.5
33 */
34 const OLD_STAGING_SITES_OPTION = 'wpstg_existing_clones_beta';
35
36 /**
37 * Before upgrading structure, backup old staging site options
38 * @since 4.0.6
39 */
40 const BACKUP_STAGING_SITES_OPTION = 'wpstg_staging_sites_backup';
41
42 /**
43 * Missing cloneName routine executed
44 * @since 4.0.7
45 */
46 const MISSING_CLONE_NAME_ROUTINE_EXECUTED = 'wpstg_missing_cloneName_routine_executed';
47
48 /**
49 * The option that stores the excluded files from cloning process
50 */
51 const STAGING_EXCLUDED_FILES_OPTION = 'wpstg_clone_excluded_files_list';
52
53 /**
54 * The option that stores Godaddy the excluded files from cloning process
55 */
56 const STAGING_EXCLUDED_GD_FILES_OPTION = 'wpstg_clone_excluded_gd_files_list';
57
58 /**
59 * @var bool
60 */
61 const THROW_EXCEPTION = true;
62
63 /**
64 * Return list of staging sites in descending order of their creation time.
65 *
66 * @return array
67 * @throws WPStagingException
68 */
69 public function getSortedStagingSites()
70 {
71 $stagingSites = $this->tryGettingStagingSites(self::THROW_EXCEPTION);
72
73 // No need to sort if no sites or only one site
74 if (empty($stagingSites) || count($stagingSites) === 1) {
75 return $stagingSites;
76 }
77
78 // Sort staging sites in descending order
79 uasort($stagingSites, function ($site1, $site2) {
80 // If datetime is same, sort by directory name
81 // Will also work if both sites datetime are not set
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 * Copy data from old staging site option wpstg_existing_clones_beta to new staging site option wpstg_staging_sites
102 *
103 * @see \WPStaging\Backend\Upgrade\Upgrade::upgrade2_8_7 (Free version)
104 * @see \WPStaging\Backend\Pro\Upgrade\Upgrade::upgrade4_0_5 (Pro version)
105 */
106 public function upgradeStagingSitesOption()
107 {
108 $newSitesOption = get_option(self::STAGING_SITES_OPTION, []);
109
110 // If its no valid array, it is broken
111 if (!is_array($newSitesOption)) {
112 $newSitesOption = [];
113 }
114
115 // Get the staging sites from old option
116 $oldSitesOption = get_option(self::OLD_STAGING_SITES_OPTION, []);
117
118 // Early bail: No sites to migrate
119 if (empty($oldSitesOption)) {
120 return;
121 }
122
123 // Convert old format to new, including when there are staging sites in both formats
124 $allStagingSites = $newSitesOption;
125
126 foreach ($oldSitesOption as $oldSiteSlug => $oldSite) {
127 // Migrate old site to new format
128 if (!array_key_exists($oldSiteSlug, $allStagingSites)) {
129 $allStagingSites[$oldSiteSlug] = $oldSite;
130 continue;
131 }
132
133 // If key exists and path matches, skip
134 if ($allStagingSites[$oldSiteSlug]['path'] === $oldSite['path']) {
135 continue;
136 }
137
138 // Migrate old site to new format when site slug exists in both options
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 // Keep a backup just in case
150 update_option(self::BACKUP_STAGING_SITES_OPTION, $oldSitesOption, false);
151 delete_option(self::OLD_STAGING_SITES_OPTION);
152 }
153 }
154
155 /**
156 * Will try getting staging sites
157 *
158 * @param bool $throwException
159 * @return array
160 * @throws WPStagingException
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 * Update staging sites option
182 *
183 * @param array $stagingSites
184 * @return bool
185 */
186 public function updateStagingSites($stagingSites)
187 {
188 return update_option(self::STAGING_SITES_OPTION, $stagingSites, false);
189 }
190
191 /**
192 * Upgrade the staging site data structure, add the missing cloneName, if not present
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 // Current options
202 $sites = $this->tryGettingStagingSites();
203
204 // Early bail if no sites
205 if (empty($sites)) {
206 update_option(self::MISSING_CLONE_NAME_ROUTINE_EXECUTED, true);
207 return;
208 }
209
210 // Add missing cloneName if not exists
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 * Sanitize the clone name to be used as directory
225 *
226 * @param string $cloneName
227 * @return string
228 */
229 public function sanitizeDirectoryName($cloneName)
230 {
231 $cloneDirectoryName = preg_replace("#\W+#", '-', strtolower($cloneName));
232 return substr($cloneDirectoryName, 0, 16);
233 }
234
235 /**
236 * Return false if site not exists else return reason behind existing
237 *
238 * @param string $directoryName
239 * @return bool|string
240 * @throws WPStagingException
241 */
242 public function isCloneExists($directoryName)
243 {
244 $cloneDirectoryPath = trailingslashit(get_home_path()) . $directoryName;
245 if (!wpstg_is_empty_dir($cloneDirectoryPath)) {
246 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);
247 }
248
249 $stagingSites = $this->tryGettingStagingSites();
250 foreach ($stagingSites as $site) {
251 if ($site['directoryName'] === $directoryName) {
252 return sprintf(esc_html__("Site name %s is already in use, please choose another name for the staging site.", "wp-staging"), $directoryName);
253 }
254 }
255
256 return false;
257 }
258
259 /**
260 * @return array
261 * @throws WPStagingException
262 */
263 public function getStagingDirectories(): array
264 {
265 $stagingSites = $this->tryGettingStagingSites();
266 return wp_list_pluck($stagingSites, 'path');
267 }
268
269 /**
270 * @param string $cloneId
271 * @return StagingSiteDto
272 *
273 * @throws Exception
274 */
275 public function getStagingSiteDtoByCloneId(string $cloneId): StagingSiteDto
276 {
277 $stagingSites = $this->tryGettingStagingSites();
278 if (empty($stagingSites)) {
279 throw new Exception('No staging sites found.');
280 }
281
282 if (!array_key_exists($cloneId, $stagingSites)) {
283 throw new Exception('Staging site not found.');
284 }
285
286 $stagingSiteArray = $stagingSites[$cloneId];
287 $stagingSiteDto = new StagingSiteDto();
288 $stagingSiteDto->hydrate($stagingSiteArray);
289 $stagingSiteDto->setCloneId($cloneId);
290
291 return $stagingSiteDto;
292 }
293
294 /**
295 * @param string $clone
296 * @return bool
297 */
298 public function isExistingClone(string $clone): bool
299 {
300 $existingClones = get_option(self::STAGING_SITES_OPTION, []);
301 return isset($existingClones[$clone]);
302 }
303 }
304