PluginProbe
Media Cloud Sync / 1.3.11
Media Cloud Sync v1.3.11
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / base / services / docean.php

docean.php in Media Cloud Sync 1.3.11, at includes/base/services/docean.php

948 lines 38.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Dudlewebs\WPMCS;
3
4 defined('ABSPATH') || exit;
5
6 // Libraries
7 use Dudlewebs\WPMCS\s3\Aws\S3\S3Client;
8 use Dudlewebs\WPMCS\s3\Aws\Exception\AwsException;
9 use Dudlewebs\WPMCS\s3\Aws\S3\Exception\S3Exception;
10 use Dudlewebs\WPMCS\s3\Aws\S3\MultipartUploader;
11 use Dudlewebs\WPMCS\s3\Aws\Exception\MultipartUploadException;
12 use Exception;
13
14 class DOcean {
15 private $assets_url;
16 private $version;
17 private $token;
18
19 protected $config;
20 protected $bucketConfig;
21 protected $settings;
22 protected $credentials;
23 protected $bucket_name;
24 protected $cdnConfig;
25
26 public $service = 'docean';
27 public $DOClient = false;
28
29 /**
30 * Admin constructor.
31 * @since 1.0.0
32 */
33 public function __construct() {
34 $this->assets_url = WPMCS_ASSETS_URL;
35 $this->version = WPMCS_VERSION;
36 $this->token = WPMCS_TOKEN;
37
38 // Initialize setup
39 $this->init();
40 }
41
42 /**
43 * Initialise Client
44 */
45 public function init() {
46 $this->settings = Utils::get_settings();
47 $this->credentials = Utils::get_credentials();
48 $this->config = isset($this->credentials['config']) && !empty($this->credentials['config'])
49 ? $this->credentials['config']
50 : [];
51 $this->bucketConfig = isset($this->credentials['bucketConfig']) && !empty($this->credentials['bucketConfig'])
52 ? $this->credentials['bucketConfig']
53 : [];
54 $this->bucket_name = isset($this->bucketConfig['bucket_name']) && !empty($this->bucketConfig['bucket_name'])
55 ? $this->bucketConfig['bucket_name']
56 : '';
57 $this->cdnConfig = isset($this->credentials['cdn']) && !empty($this->credentials['cdn'])
58 ? $this->credentials['cdn']
59 : [];
60
61 if (
62 isset($this->config['region']) && !empty($this->config['region']) &&
63 isset($this->config['access_key']) && !empty($this->config['access_key']) &&
64 isset($this->config['secret_key']) && !empty($this->config['secret_key'])
65 ) {
66 $endpoint = $this->get_domain();
67
68 $this->DOClient = new S3Client([
69 'version' => '2006-03-01',
70 'region' => $this->config['region'],
71 'endpoint' => $endpoint, // DigitalOcean Spaces requires a custom endpoint
72 'use_accelerate_endpoint' => false,
73 'use_path_style_endpoint' => true, // DigitalOcean Spaces often requires path-style endpoints
74 'use_aws_shared_config_files' => false,
75 'credentials' => [
76 'key' => $this->config['access_key'],
77 'secret' => $this->config['secret_key'],
78 ],
79 ]);
80 }
81
82 }
83
84
85 /**
86 * Verify Credentials
87 * @since 1.0.0
88 * @return boolean
89 */
90 public function verifyCredentials($config = []) {
91 $region = isset($config['region']) ? $config['region'] : '';
92 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
93 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
94 if (!empty($region) && !empty($access_key) && !empty($secret_key)) {
95 try {
96 $endpoint = $this->get_domain($region);
97
98 $DOClient = new S3Client([
99 'version' => '2006-03-01',
100 'region' => $region,
101 'endpoint' => $endpoint,
102 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
103 'use_accelerate_endpoint' => false,
104 'use_aws_shared_config_files' => false,
105 'credentials' => [
106 'key' => $access_key,
107 'secret' => $secret_key,
108 ],
109 ]);
110
111 $result = [
112 'success' => false,
113 'code' => 200,
114 'message' => esc_html__('Please check the authorization details', 'media-cloud-sync'),
115 ];
116
117 try {
118 $DOClient->listObjectsV2([
119 'Bucket' => $this->token . '_dummy-bucket-for-auth-check'
120 ]);
121
122 // If we reach here, the credentials are valid
123 $result = [
124 'success' => true,
125 'code' => 200,
126 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'),
127 ];
128 } catch (AwsException $e) {
129 $code = $e->getAwsErrorCode();
130
131 $validErrors = [
132 'AccessDenied',
133 'NoSuchBucket',
134 'AllAccessDisabled',
135 'AuthorizationHeaderMalformed',
136 'PermanentRedirect',
137 'InvalidBucketName',
138 ];
139
140 if (in_array($code, $validErrors)) {
141 // If we reach here, the credentials are valid
142 $result = [
143 'success' => true,
144 'code' => 200,
145 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'),
146 ];
147 }
148 }
149
150 if($result['success'] == false) {
151 return $result;
152 }
153 try {
154 $buckets = $DOClient->listBuckets();
155 $newBucketFormat = [];
156 if(isset($buckets['Buckets']) && !empty($buckets['Buckets'])){
157 foreach($buckets['Buckets'] as $bucket) {
158 if(isset($bucket['Name'])) {
159 $newBucketFormat[] = ['Name' => $bucket['Name'], 'CreationDate' => $bucket['CreationDate'] ?? ''];
160 }
161 }
162 }
163 $result['buckets_data']['buckets'] = $newBucketFormat;
164 $result['buckets_data']['message'] = esc_html__('Buckets listed successfully', 'media-cloud-sync');
165 $result['buckets_data']['status'] = true;
166 } catch (S3Exception $e) {
167 $result ['buckets_data']['buckets'] = [];
168 $result ['buckets_data']['message'] = esc_html__('Unable to list buckets, Please check the bucket listing permission', 'media-cloud-sync');
169 $result ['buckets_data']['status'] = false;
170 } catch (Exception $e) {
171 $result ['buckets_data']['buckets'] = [];
172 $result ['buckets_data']['message'] = esc_html__('Unable to list buckets, Please check the bucket listing permission', 'media-cloud-sync');
173 $result ['buckets_data']['status'] = false;
174 }
175 return $result;
176 } catch (S3Exception $ex) {
177 return array('message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
178 } catch (Exception $ex) {
179 return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
180 }
181 }
182 return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false);
183 }
184
185 /**
186 * Verify Bucket
187 * @since 1.0.0
188 * @return boolean
189 */
190 public function verifyBucketExist( $config = [], $bucketConfig = [] ) {
191 $region = isset($config['region']) ? $config['region'] : '';
192 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
193 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
194 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
195
196 if (!empty($region) && !empty($access_key) && !empty($secret_key) && !empty($bucket_name)) {
197 try {
198 $endpoint = $this->get_domain($region);
199
200 $DOClient = new S3Client([
201 'version' => '2006-03-01',
202 'region' => $region,
203 'endpoint' => $endpoint,
204 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
205 'use_accelerate_endpoint' => false,
206 'use_aws_shared_config_files' => false,
207 'credentials' => [
208 'key' => $access_key,
209 'secret' => $secret_key,
210 ],
211 ]);
212
213 //get S3 object
214 $bucket_found = false;
215 try {
216 $DOClient->getObject([
217 'Bucket' => $bucket_name,
218 'Key' => $this->token . '_dummy-object-for-bucket-exist-check'
219 ]);
220 $bucket_found = true;
221 } catch (AwsException $e) {
222 $code = $e->getAwsErrorCode();
223 if ($code === 'NoSuchKey') {
224 $bucket_found = true;
225 }
226 }
227
228 if($bucket_found) {
229 return array('message' => esc_html__('Bucket exist', 'media-cloud-sync'), 'code' => 200, 'success' => true);
230 } else {
231 return array('message' => esc_html__("Bucket choosen does not exist / does not have read permission", 'media-cloud-sync'), 'code' => 200, 'success' => false);
232 }
233 }
234 catch (S3Exception $ex) {
235 return array('message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
236 } catch (Exception $ex) {
237 return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
238 }
239 }
240 return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false);
241 }
242
243 /**
244 * Create Bucket
245 * @since 1.0.0
246 * @return boolean
247 */
248 public function createBucket( $config = [], $bucketConfig = [] ) {
249 $region = isset($config['region']) ? $config['region'] : '';
250 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
251 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
252 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
253
254 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
255 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
256 }
257
258 try {
259 $endpoint = $this->get_domain($region);
260
261 $DOClient = new S3Client([
262 'version' => '2006-03-01',
263 'region' => $region,
264 'endpoint' => $endpoint,
265 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
266 'use_accelerate_endpoint' => false,
267 'use_aws_shared_config_files' => false,
268 'credentials' => [
269 'key' => $access_key,
270 'secret' => $secret_key,
271 ],
272 ]);
273
274 // Create Bucket
275 $DOClient->createBucket([
276 'Bucket' => $bucket_name,
277 ]);
278
279 // Optionally wait for bucket existence (recommended)
280 $DOClient->waitUntil('BucketExists', ['Bucket' => $bucket_name]);
281
282 try {
283 $this->putBucketPolicy($bucket_name, $DOClient);
284
285 return [
286 'message' => esc_html__('Bucket created successfully.', 'media-cloud-sync'),
287 'data' => [
288 'Name' => $bucket_name,
289 'CreationDate' => date('Y-m-d\TH:i:s\Z'),
290 ],
291 'code' => 200,
292 'success' => true,
293 ];
294
295 } catch (AwsException $ex) {
296 return ['message' => esc_html__('Bucket created. But the following error happened while setting the public access,', 'media-cloud-sync') . ' ' . $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
297 }
298 } catch (AwsException $ex) {
299 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
300 } catch (S3Exception $ex) {
301 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
302 } catch (Exception $ex) {
303 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
304 }
305 }
306
307
308 /**
309 * Add Bucket Policy
310 */
311 private function putBucketPolicy($bucket, $DOClient = false) {
312 if($DOClient == false) {
313 $DOClient = $this->DOClient;
314 }
315
316 if(empty($bucket)) return false;
317
318 $policy = json_encode([
319 "Version" => "2012-10-17",
320 "Statement" => [
321 [
322 "Effect" => "Allow",
323 "Principal" => "*",
324 "Action" => [
325 "s3:DeleteObjectTagging",
326 "s3:ListBucketMultipartUploads",
327 "s3:DeleteObjectVersion",
328 "s3:ListBucket",
329 "s3:DeleteObjectVersionTagging",
330 "s3:GetBucketAcl",
331 "s3:ListMultipartUploadParts",
332 "s3:PutObject",
333 "s3:GetObjectAcl",
334 "s3:GetObject",
335 "s3:AbortMultipartUpload",
336 "s3:DeleteObject",
337 "s3:GetBucketLocation",
338 "s3:PutObjectAcl",
339 "s3:putBucketOwnershipControls",
340 "s3:putBucketPolicy"
341 ],
342 "Resource" => [
343 "arn:aws:s3:::$bucket/*",
344 "arn:aws:s3:::$bucket"
345 ]
346 ]
347 ]
348 ]);
349
350 try {
351 // Add bucket policy
352 $DOClient->putBucketPolicy(['Bucket' => $bucket, 'Policy' => $policy]);
353
354 return true;
355 } catch (AwsException $ex) {
356 return false; // Handle AWS specific exceptions
357 } catch (S3Exception $ex) {
358 return false; // Handle S3 specific exceptions
359 } catch (Exception $ex) {
360 return false; // Handle general exceptions
361 }
362 }
363
364
365
366 /**
367 * Check Bucket Write Permission
368 * @since 1.0.0
369 */
370 public function verifyObjectWritePermission( $config = [], $bucketConfig = [] ) {
371 $region = isset($config['region']) ? $config['region'] : '';
372 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
373 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
374 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
375
376 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
377 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
378 }
379
380 try {
381 $endpoint = $this->get_domain($region);
382
383 $DOClient = new S3Client([
384 'version' => '2006-03-01',
385 'region' => $region,
386 'endpoint' => $endpoint,
387 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
388 'use_accelerate_endpoint' => false,
389 'use_aws_shared_config_files' => false,
390 'credentials' => [
391 'key' => $access_key,
392 'secret' => $secret_key,
393 ],
394 ]);
395
396 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
397
398
399 // Create a dummy object to check write permission
400 $DOClient->putObject([
401 'Bucket' => $bucket_name,
402 'Key' => $object_key,
403 'Body' => 'This is a test object to check write permission.',
404 ]);
405 // Check if the object was created successfully
406 if ($this->exists($object_key, $bucket_name, $DOClient)) {
407 return ['message' => esc_html__('Bucket write permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true];
408 } else {
409 return ['message' => esc_html__('Bucket write permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false];
410 }
411
412 } catch (AwsException $ex) {
413 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
414 } catch (S3Exception $ex) {
415 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
416 } catch (Exception $ex) {
417 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
418 }
419 }
420
421
422
423 /**
424 * Check Bucket Delete Permission
425 * @since 1.0.0
426 */
427 public function verifyObjectDeletePermission( $config = [], $bucketConfig = [] ) {
428 $region = isset($config['region']) ? $config['region'] : '';
429 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
430 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
431 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
432
433 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
434 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
435 }
436
437 try {
438 $endpoint = $this->get_domain($region);
439
440 $DOClient = new S3Client([
441 'version' => '2006-03-01',
442 'region' => $region,
443 'endpoint' => $endpoint,
444 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
445 'use_accelerate_endpoint' => false,
446 'use_aws_shared_config_files' => false,
447 'credentials' => [
448 'key' => $access_key,
449 'secret' => $secret_key,
450 ],
451 ]);
452
453 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
454
455 // Create a dummy object to check dlete permission
456 $DOClient->deleteObject([
457 'Bucket' => $bucket_name,
458 'Key' => $object_key,
459 ]);
460
461 // Check if the object was created successfully
462 if (!$this->exists($object_key, $bucket_name, $DOClient)) {
463 return ['message' => esc_html__('Bucket delete permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true];
464 } else {
465 return ['message' => esc_html__('Bucket delete permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false];
466 }
467
468 } catch (AwsException $ex) {
469 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
470 } catch (S3Exception $ex) {
471 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
472 } catch (Exception $ex) {
473 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
474 }
475 }
476
477
478 /**
479 * Check Bucket Read Permission
480 * @since 1.2.4
481 */
482 public function verifyObjectReadPermission() {
483 $result = [
484 'status' => false,
485 'message' => '',
486 'lastChecked' => time(),
487 ];
488 if (empty($this->DOClient) || empty($this->bucket_name)) {
489 $result['message'] = esc_html__('Invalid Request', 'media-cloud-sync');
490 return ['message' => esc_html__('Invalid Request', 'media-cloud-sync'), 'code' => 200, 'success' => false, 'lastChecked' => time()];
491 }
492
493 try {
494 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
495
496 // Check if the object was created successfully
497 if (!$this->exists($object_key)) {
498 // Create a dummy object to check write permission
499 $this->DOClient->putObject([
500 'Bucket' => $this->bucket_name,
501 'Key' => $object_key,
502 'Body' => 'This is a test object to check permission.',
503 ]);
504 }
505
506
507 $url = $this->generate_file_url($object_key);
508 $cdn_url = Cdn::may_generate_cdn_url($url, $object_key);
509 $headers = @get_headers($cdn_url);
510
511 if (strpos($headers[0], '200') !== false) {
512 $result['status'] = true;
513 $result['message'] = esc_html__('Objects are accessible to Read', 'media-cloud-sync');
514 } else if (strpos($headers[0], '403') !== false) {
515 $result['status'] = false;
516 if($this->cdnConfig['service'] == $this->service) {
517 $result['message'] = esc_html__('Access Denied. Please check your bucket policy. Public Read Access is required.', 'media-cloud-sync');
518 } else {
519 $result['message'] = esc_html__('Access Denied. Please check your bucket policy', 'media-cloud-sync');
520 }
521 $result['message'] = esc_html__('Access Denied. Please check your bucket policy', 'media-cloud-sync');
522 } else if (strpos($headers[0], '404') !== false) {
523 $result['status'] = false;
524 $result['message'] = esc_html__('Object not found. Please check your bucket policy', 'media-cloud-sync');
525 } else if (strpos($headers[0], '500') !== false) {
526 $result['status'] = false;
527 $result['message'] = esc_html__('Internal Server error. Please check your bucket policy', 'media-cloud-sync');
528 } else {
529 $result['status'] = false;
530 $result['message'] = esc_html__('Objects are not accessible to read', 'media-cloud-sync');
531 }
532
533 $this->deleteSingle($object_key);
534 return [
535 'message' => $result['message'],
536 'code' => 200,
537 'success' => $result['status'],
538 'lastChecked' => $result['lastChecked'],
539 ];
540 } catch (AwsException $ex) {
541 $result['message'] = $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync');
542 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false, 'lastChecked' => time()];
543 } catch (S3Exception $ex) {
544 $result['message'] = $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync');
545 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false, 'lastChecked' => time()];
546 } catch (Exception $ex) {
547 $result['message'] = $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync');
548 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false, 'lastChecked' => time()];
549 }
550 }
551
552 /**
553 * isConfigured Function To Identify the congfigurations are correct
554 * @since 1.0.0
555 */
556 public function isConfigured(){
557 if ($this->DOClient) {
558 try {
559 $DOClient->listObjectsV2([
560 'Bucket' => $this->token . '_dummy-bucket-for-auth-check'
561 ]);
562
563 // If we reach here, the credentials are valid
564 return true;
565 } catch (AwsException $e) {
566 $code = $e->getAwsErrorCode();
567
568 $validErrors = [
569 'AccessDenied',
570 'NoSuchBucket',
571 'AllAccessDisabled',
572 'AuthorizationHeaderMalformed',
573 'PermanentRedirect',
574 'InvalidBucketName',
575 ];
576
577 if (in_array($code, $validErrors)) {
578 // If we reach here, the credentials are valid
579 return false;
580 } else {
581 // If we reach here, the credentials are not valid
582 return false;
583 }
584 }
585 }
586 return false;
587 }
588
589 /**
590 * Make Object Private
591 * @since 1.0.0
592 *
593 */
594 public function toPrivate($key) {
595 if(!$key) return false;
596 try {
597 $this->DOClient->putObjectAcl([
598 'Bucket' => $this->bucket_name,
599 'Key' => $key,
600 'ACL' => 'private'
601 ]);
602 return true;
603 } catch (AwsException $ex) {
604 return false;
605 }
606 return false;
607 }
608
609
610
611 /**
612 * Make Object Public
613 * @since 1.0.0
614 *
615 */
616 public function toPublic($key) {
617 if(!$key) return false;
618 try {
619 $this->DOClient->putObjectAcl([
620 'Bucket' => $this->bucket_name,
621 'Key' => $key,
622 'ACL' => 'public-read'
623 ]);
624 return true;
625 } catch (AwsException $ex) {
626 return false;
627 }
628 return false;
629 }
630
631
632
633 /**
634 * Check the object exist
635 * @since 1.1.8
636 */
637 public function exists($key, $bucket_name = '', $client = null) {
638 if(!$key) return false;
639
640 try {
641 $client = $client ?? $this->DOClient;
642 $bucket_name = !empty($bucket_name) ? $bucket_name : $this->bucket_name;
643 if($client->doesObjectExistV2($bucket_name, $key)) {
644 return true;
645 }
646 } catch (AwsException $ex) {
647 return false;
648 }
649 catch (S3Exception $ex) {
650 return false;
651 } catch (Exception $ex) {
652 return false;
653 }
654 }
655
656 /**
657 * Upload Single
658 * @since 1.0.0
659 * @return boolean
660 */
661 public function uploadSingle($absolute_source_path, $relative_source_path, $prefix='') {
662 $result = array();
663 if (
664 isset($absolute_source_path) && !empty($absolute_source_path) &&
665 isset($relative_source_path) && !empty($relative_source_path)
666 ) {
667 $file_name = wp_basename( $relative_source_path );
668 if ($file_name) {
669 $upload_path = Utils::generate_object_key($relative_source_path, $prefix);
670
671 // Decide Multipart upload or normal put object
672 if (filesize($absolute_source_path) <= Schema::getConstant('DOCEAN_MULTIPART_MIN_FILE_SIZE')) {
673 // Upload a publicly accessible file. The file size and type are determined by the SDK.
674 try {
675 $handle = fopen($absolute_source_path, 'rb');
676 $upload = $this->DOClient->putObject([
677 'Bucket' => $this->bucket_name,
678 'Key' => $upload_path,
679 'Body' => $handle,
680 ]);
681 if (is_resource($handle)) {
682 fclose($handle);
683 }
684
685 $result = array(
686 'success' => true,
687 'code' => 200,
688 'file_url' => $this->generate_file_url($upload_path),
689 'key' => $upload_path,
690 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync')
691 );
692 } catch (AwsException $e) {
693 $result = array(
694 'success' => false,
695 'code' => 200,
696 'message' => $e->getMessage()
697 );
698 }
699 } else {
700 $multiUploader = new MultipartUploader($this->DOClient, $absolute_source_path, [
701 'bucket' => $this->bucket_name,
702 'key' => $upload_path,
703 ]);
704
705 try {
706 do {
707 try {
708 $uploaded = $multiUploader->upload();
709 } catch (MultipartUploadException $e) {
710 $multiUploader = new MultipartUploader($this->DOClient, $absolute_source_path, [
711 'state' => $e->getState(),
712 ]);
713 }
714 } while (!isset($uploaded));
715
716 if (isset($uploaded['ObjectURL']) && !empty($uploaded['ObjectURL'])) {
717 $result = array(
718 'success' => true,
719 'code' => 200,
720 'file_url' => $this->generate_file_url($upload_path),
721 'key' => $upload_path,
722 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync')
723 );
724 } else {
725 $result = array(
726 'success' => false,
727 'code' => 200,
728 'message' => esc_html__('Something happened while uploading to server', 'media-cloud-sync')
729 );
730 }
731 } catch (MultipartUploadException $e) {
732 $result = array(
733 'success' => false,
734 'code' => 200,
735 'message' => $e->getMessage()
736 );
737 }
738 }
739 } else {
740 $result = array(
741 'success' => false,
742 'code' => 200,
743 'message' => esc_html__('Check the file you are trying to upload. Please try again', 'media-cloud-sync')
744 );
745 }
746 } else {
747 $result = array(
748 'success' => false,
749 'code' => 200,
750 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
751 );
752 }
753 return $result;
754 }
755
756 /**
757 * Save object to server
758 * @since 1.0.0
759 */
760 public function object_to_server($key, $save_path) {
761 try {
762 $getObject = $this->DOClient->GetObject([
763 'Bucket' => $this->bucket_name,
764 'Key' => $key,
765 'SaveAs' => $save_path
766 ]);
767 if (file_exists($save_path)) {
768 return true;
769 }
770 } catch (AwsException $e) {
771 return false;
772 }
773 return false;
774 }
775
776 /**
777 * Copy to new path
778 * @since 1.3.4
779 */
780 public function copy_to_new_path($key, $new_path) {
781 try {
782 // Step 1: Verify object exists at old location
783 if (!$this->exists($key)) {
784 return [
785 'message' => esc_html__('Original file not found' , 'media-cloud-sync'),
786 'code' => 200,
787 'success' => false
788 ];
789 }
790 // Step 2: Copy object
791 if (!$this->exists($new_path)) {
792 $this->DOClient->copyObject([
793 'Bucket' => $this->bucket_name,
794 'CopySource' => "{$this->bucket_name}/{$key}",
795 'Key' => $new_path,
796 'MetadataDirective' => 'COPY',
797 ]);
798 }
799 // Step 3: Verify object exists at new location
800 if ($this->exists($new_path)) {
801 return [
802 'success' => true,
803 'code' => 200,
804 'message' => esc_html__('File copied successfully', 'media-cloud-sync')
805 ];
806 }
807 } catch (AwsException $e) {
808 return [
809 'success' => false,
810 'code' => 200,
811 'message' => $e->getMessage()
812 ];
813 }
814 return [
815 'success' => false,
816 'code' => 200,
817 'message' => esc_html__('File not copied', 'media-cloud-sync')
818 ];
819 }
820
821
822 /**
823 * Delete Single
824 * @since 1.0.0
825 * @return boolean
826 */
827 public function deleteSingle($key) {
828 $result = array();
829 if (isset($key) && !empty($key)) {
830 try {
831 $this->DOClient->deleteObject([
832 'Bucket' => $this->bucket_name,
833 'Key' => $key
834 ]);
835
836 if (!$this->exists($key)) {
837 $result = array(
838 'success' => true,
839 'code' => 200,
840 'message' => esc_html__('Deleted Successfully', 'media-cloud-sync')
841 );
842 } else {
843 $result = array(
844 'success' => false,
845 'code' => 200,
846 'message' => esc_html__('File not deleted', 'media-cloud-sync')
847 );
848 }
849 } catch (AwsException $e) {
850 $result = array(
851 'success' => false,
852 'code' => 200,
853 'message' => $e->getMessage()
854 );
855 }
856 } else {
857 $result = array(
858 'success' => false,
859 'code' => 200,
860 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
861 );
862 }
863 return $result;
864 }
865
866 /**
867 * get private URL
868 * @since 1.0.0
869 * @return boolean
870 */
871 public function get_private_url($key) {
872 $result = array();
873 if (isset($key) && !empty($key)) {
874 try {
875 $cmd = $this->DOClient->getCommand('GetObject', [
876 'Bucket' => $this->bucket_name,
877 'Key' => $key
878 ]);
879
880 $expires = isset($this->settings['private_url_expire']) ? $this->settings['private_url_expire'] : 20;
881
882 $request = $this->DOClient->createPresignedRequest($cmd, sprintf('+%s minutes', $expires));
883
884 if ($privateUrl = (string)$request->getUri()) {
885 $result = array(
886 'success' => true,
887 'code' => 200,
888 'file_url' => $privateUrl,
889 'message' => esc_html__('Got Private URL Successfully', 'media-cloud-sync')
890 );
891 } else {
892 $result = array(
893 'success' => false,
894 'code' => 200,
895 'message' => esc_html__('Error getting Private URL', 'media-cloud-sync')
896 );
897 }
898 } catch (AwsException $e) {
899 $result = array(
900 'success' => false,
901 'code' => 200,
902 'message' => $e->getMessage()
903 );
904 }
905 } else {
906 $result = array(
907 'success' => false,
908 'code' => 200,
909 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
910 );
911 }
912 return $result;
913 }
914
915 /**
916 * Generate file URL
917 */
918 public function generate_file_url($key){
919 $domain = $this->get_domain();
920
921 return apply_filters('wpmcs_generate_do_file_url',
922 $domain . '/' . $this->bucket_name . '/' . $key,
923 $domain,
924 $this->bucket_name,
925 $key
926 );
927 }
928
929 /**
930 * Is Provider URL
931 * @since 1.3.6
932 */
933 public function is_provider_url($url) {
934 $domain = $this->get_domain();
935 return (strpos($url, $domain . '/' . $this->bucket_name . '/') !== false);
936 }
937
938 /**
939 * Get domain URL
940 */
941 public function get_domain($region = '') {
942 if(empty($region)) {
943 $region = isset($this->config['region']) ? $this->config['region'] : '';
944 }
945 return "https://{$region}.digitaloceanspaces.com";
946 }
947
948 }