PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.7
Backup Migration v2.1.7
2.1.7 2.1.6 2.1.5.2 trunk 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.6.1 1.4.7 1.4.8 1.4.9 1.4.9.1 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.5.1
backup-backup / includes / external / external-storage-manager.php
backup-backup / includes / external Last commit date
contracts 2 weeks ago backupbliss.php 2 weeks ago controller.php 2 weeks ago dropbox.php 2 weeks ago external-storage-manager.php 2 weeks ago ftp.php 2 weeks ago google-drive.php 2 weeks ago s3.php 2 weeks ago
external-storage-manager.php
381 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin\External;
5
6 // Use
7 use BMI\Plugin\Dashboard as Dashboard;
8
9 // Exit on direct access
10 if (!defined('ABSPATH')) {
11 exit;
12 }
13
14 /**
15 * BMI_External_Storage_Manager
16 *
17 * Centralized manager for all external storage services.
18 * Provides clean methods to check enabled/configured storages without hardcoding option names.
19 */
20 class BMI_External_Storage_Manager {
21
22 private static $instance = null;
23 private $storageConfig = [];
24 private $enabledStorages = [];
25 private $configuredStorages = [];
26
27 /**
28 * Get singleton instance
29 */
30 public static function getInstance() {
31 if (self::$instance === null) {
32 self::$instance = new self();
33 }
34 return self::$instance;
35 }
36
37 /**
38 * Constructor - Initialize storage configuration
39 */
40 private function __construct() {
41 $this->initializeStorageConfig();
42 }
43
44 /**
45 * Initialize the storage configuration array
46 * Maps service names to their option keys, classes, and files
47 */
48 private function initializeStorageConfig() {
49 $this->storageConfig = [
50 'backupbliss' => [
51 'option_key' => 'STORAGE::EXTERNAL::BACKUPBLISS',
52 'class' => 'BMI_External_BackupBliss',
53 'file' => BMI_INCLUDES . '/external/backupbliss.php',
54 'label' => 'BackupBliss',
55 'namespace' => '\\BMI\\Plugin\\External\\BMI_External_BackupBliss'
56 ],
57 'dropbox' => [
58 'option_key' => 'STORAGE::EXTERNAL::DROPBOX',
59 'class' => 'BMI_External_Dropbox',
60 'file' => BMI_INCLUDES . '/external/dropbox.php',
61 'label' => 'Dropbox',
62 'namespace' => '\\BMI\\Plugin\\External\\BMI_External_Dropbox'
63 ],
64 'gdrive' => [
65 'option_key' => 'STORAGE::EXTERNAL::GDRIVE',
66 'class' => 'BMI_External_GDrive',
67 'file' => BMI_INCLUDES . '/external/google-drive.php',
68 'label' => 'Google Drive',
69 'namespace' => '\\BMI\\Plugin\\External\\BMI_External_GDrive'
70 ],
71 'ftp' => [
72 'option_key' => 'STORAGE::EXTERNAL::FTP',
73 'class' => 'BMI_External_FTP',
74 'file' => BMI_INCLUDES . '/external/ftp.php',
75 'label' => 'FTP',
76 'namespace' => '\\BMI\\Plugin\\External\\BMI_External_FTP'
77 ],
78 'aws' => [
79 'option_key' => 'STORAGE::EXTERNAL::AWS',
80 'class' => 'BMI_External_S3',
81 'file' => BMI_INCLUDES . '/external/s3.php',
82 'label' => 'Amazon S3',
83 'namespace' => '\\BMI\\Plugin\\External\\BMI_External_S3',
84 'constructor_args' => ['aws']
85 ],
86 'wasabi' => [
87 'option_key' => 'STORAGE::EXTERNAL::WASABI',
88 'class' => 'BMI_External_S3',
89 'file' => BMI_INCLUDES . '/external/s3.php',
90 'label' => 'Wasabi',
91 'namespace' => '\\BMI\\Plugin\\External\\BMI_External_S3',
92 'constructor_args' => ['wasabi']
93 ]
94 ];
95
96 // Allow pro version or addons to register additional storages
97 $this->storageConfig = apply_filters('bmi_external_storages', $this->storageConfig);
98 }
99
100 /**
101 * Get all available storage services
102 *
103 * @return array List of all registered storage services
104 */
105 public function getAvailableStorages() {
106 return array_keys($this->storageConfig);
107 }
108
109 /**
110 * Get all storage configurations
111 *
112 * @return array Complete storage configuration array
113 */
114 public function getStorageConfigs() {
115 return $this->storageConfig;
116 }
117
118 /**
119 * Get configuration for a specific storage
120 *
121 * @param string $serviceName The service name (e.g., 'dropbox', 'gdrive')
122 * @return array|null Storage configuration or null if not found
123 */
124 public function getStorageConfig($serviceName) {
125 return isset($this->storageConfig[$serviceName]) ? $this->storageConfig[$serviceName] : null;
126 }
127
128 /**
129 * Check if a storage service is enabled (option is true)
130 *
131 * @param string $serviceName The service name (e.g., 'dropbox', 'gdrive')
132 * @return bool True if enabled, false otherwise
133 */
134 public function isStorageEnabled($serviceName) {
135 $config = $this->getStorageConfig($serviceName);
136 if (!$config) {
137 return false;
138 }
139
140 $value = Dashboard\bmi_get_config($config['option_key']);
141 return ($value === true || $value === 'true');
142 }
143
144 /**
145 * Get list of all enabled storage services
146 *
147 * @param bool $skipCache Whether to skip cache and force fresh check
148 * @return array Array of enabled service names
149 */
150 public function getEnabledStorages($skipCache = false) {
151 if (!$skipCache && !empty($this->enabledStorages)) {
152 return $this->enabledStorages;
153 }
154
155 $enabled = [];
156 foreach ($this->storageConfig as $serviceName => $config) {
157 if ($this->isStorageEnabled($serviceName)) {
158 $enabled[] = $serviceName;
159 }
160 }
161
162 $this->enabledStorages = $enabled;
163 return $this->enabledStorages;
164 }
165
166 /**
167 * Check if ANY external storage is enabled
168 *
169 * @param bool $skipCache Whether to skip cache and force fresh check
170 * @return bool True if at least one storage is enabled
171 */
172 public function isAnyStorageEnabled($skipCache = false) {
173 return count($this->getEnabledStorages($skipCache)) > 0;
174 }
175
176 /**
177 * Get an instance of a storage class
178 *
179 * @param string $serviceName The service name (e.g., 'dropbox', 'gdrive')
180 * @return object|null Instance of the storage class or null if not found
181 */
182 public function getStorageInstance($serviceName) {
183 $config = $this->getStorageConfig($serviceName);
184 if (!$config) {
185 return null;
186 }
187
188 // Require the file if it exists
189 if (file_exists($config['file'])) {
190 require_once $config['file'];
191 } else {
192 return null;
193 }
194
195 // Create instance with constructor args if specified
196 $className = $config['namespace'];
197 if (isset($config['constructor_args'])) {
198 return new $className(...$config['constructor_args']);
199 } else {
200 return new $className();
201 }
202 }
203
204 /**
205 * Verify if a storage service is fully configured (enabled + connection verified)
206 *
207 * @param string $serviceName The service name
208 * @return bool True if storage is enabled and connection is verified
209 */
210 public function isStorageConfigured($serviceName) {
211 $config = $this->getStorageConfig($serviceName);
212 if (!$config) {
213 return false;
214 }
215
216 $instance = $this->getStorageInstance($serviceName);
217 if ($instance && method_exists($instance, 'verifyConnection')) {
218 try {
219 $result = $instance->verifyConnection();
220 return $result === true || (is_array($result) && isset($result['result']) && $result['result'] === 'connected');
221 } catch (\Exception $e) {
222 return false;
223 }
224 }
225
226 // For storages without verify method, just check if enabled
227 return true;
228 }
229
230 /**
231 * Get list of fully configured storage services
232 *
233 * @param bool $skipCache Whether to skip cache and force fresh check
234 * @return array Array of configured service names with details
235 */
236 public function getConfiguredStorages($skipCache = false) {
237 if (!$skipCache && !empty($this->configuredStorages)) {
238 return $this->configuredStorages;
239 }
240
241 $configured = [];
242 $enabledStorages = $this->getEnabledStorages($skipCache);
243
244 foreach ($enabledStorages as $serviceName) {
245 if ($this->isStorageConfigured($serviceName)) {
246 $config = $this->getStorageConfig($serviceName);
247 $configured[$serviceName] = [
248 'name' => $serviceName,
249 'label' => $config['label'],
250 'option_key' => $config['option_key']
251 ];
252 }
253 }
254
255 $this->configuredStorages = $configured;
256 return $this->configuredStorages;
257 }
258
259 /**
260 * Check if ANY external storage is fully configured
261 *
262 * @param bool $skipCache Whether to skip cache and force fresh check
263 * @return bool True if at least one storage is configured
264 */
265 public function isAnyStorageConfigured($skipCache = false) {
266 return count($this->getConfiguredStorages($skipCache)) > 0;
267 }
268
269 /**
270 * Get detailed status of all storages
271 *
272 * @param bool $skipCache Whether to skip cache and force fresh check
273 * @return array Array with each storage's status
274 */
275 public function getAllStoragesStatus($skipCache = false) {
276 $status = [];
277
278 foreach ($this->storageConfig as $serviceName => $config) {
279 $enabled = $this->isStorageEnabled($serviceName);
280 $configured = false;
281
282 if ($enabled) {
283 $configured = $this->isStorageConfigured($serviceName);
284 }
285
286 $status[$serviceName] = [
287 'label' => $config['label'],
288 'enabled' => $enabled,
289 'configured' => $configured,
290 'option_key' => $config['option_key']
291 ];
292 }
293
294 return $status;
295 }
296
297 /**
298 * Get count of enabled storages
299 *
300 * @param bool $skipCache Whether to skip cache
301 * @return int Number of enabled storages
302 */
303 public function getEnabledStoragesCount($skipCache = false) {
304 return count($this->getEnabledStorages($skipCache));
305 }
306
307 /**
308 * Get count of configured storages
309 *
310 * @param bool $skipCache Whether to skip cache
311 * @return int Number of configured storages
312 */
313 public function getConfiguredStoragesCount($skipCache = false) {
314 return count($this->getConfiguredStorages($skipCache));
315 }
316
317 /**
318 * Check if specific storages are enabled (OR condition)
319 *
320 * @param array $serviceNames Array of service names to check
321 * @return bool True if any of the specified storages is enabled
322 */
323 public function isAnyOfTheseEnabled($serviceNames) {
324 foreach ($serviceNames as $serviceName) {
325 if ($this->isStorageEnabled($serviceName)) {
326 return true;
327 }
328 }
329 return false;
330 }
331
332 /**
333 * Check if all specific storages are enabled (AND condition)
334 *
335 * @param array $serviceNames Array of service names to check
336 * @return bool True if all specified storages are enabled
337 */
338 public function areAllEnabled($serviceNames) {
339 foreach ($serviceNames as $serviceName) {
340 if (!$this->isStorageEnabled($serviceName)) {
341 return false;
342 }
343 }
344 return true;
345 }
346
347 /**
348 * Check available space for a specific storage service
349 *
350 * @param string $serviceName The service name (e.g., 'dropbox', 'gdrive')
351 * @return int|false Available space in bytes or false on failure
352 */
353 public function getAvailableSpace($serviceName) {
354 $instance = $this->getStorageInstance($serviceName);
355 if ($instance && method_exists($instance, 'getAvailableSpace')) {
356 try {
357 return $instance->getAvailableSpace();
358 } catch (\Exception $e) {
359 return false;
360 }
361 }
362 return false;
363 }
364
365 /**
366 * Delete a backup across all configured external storages
367 *
368 * @param string $hash The backup hash/md5
369 */
370 public function deleteBackup($hash) {
371 $storages = $this->getConfiguredStorages();
372 foreach ($storages as $serviceName) {
373 $instance = $this->getStorageInstance($serviceName['name']);
374 if ($instance instanceof \BMI\Plugin\External\Contracts\DeleteBackup) {
375 $instance->deleteBackup($hash);
376 }
377 }
378 }
379
380 }
381