PluginProbe
Media Cloud Sync / 1.4.1
Media Cloud Sync v1.4.1
1.4.1 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 All 35 releases
media-cloud-sync / includes / api.php

api.php in Media Cloud Sync 1.4.1, at includes/api.php

762 lines 24.2 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 use WP_REST_Response;
5
6 defined('ABSPATH') || exit;
7
8 class Api {
9 private static $instance = null;
10 private $token;
11 private $version;
12 private $assets_url;
13
14 /**
15 * Constructor
16 * @since 1.0.0
17 */
18
19 public function __construct() {
20 $this->assets_url = WPMCS_ASSETS_URL;
21 $this->version = WPMCS_VERSION;
22 $this->token = WPMCS_TOKEN;
23
24 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
25 }
26
27
28 /**
29 * Register API routes
30 */
31
32 public function register_routes() {
33 $this->add_route( '/verifyCredentials', 'verifyCredentials', 'POST' );
34 $this->add_route( '/bucket/checkExist', 'verifyBucketExist', 'POST' );
35 $this->add_route( '/bucket/addNew', 'createBucket', 'POST' );
36 $this->add_route( '/permissions/status', 'verifyStatus', 'POST' );
37 $this->add_route( '/permissions/write', 'verifyWrite', 'POST' );
38 $this->add_route( '/permissions/delete', 'verifyObjectDelete', 'POST' );
39 $this->add_route( '/permissions/read', 'verifyRead', 'POST' );
40
41 $this->add_route( '/security/get_security', 'getBucketSecuritySettings', 'POST' );
42 $this->add_route( '/security/block_public_access', 'changePublicAccess', 'POST' );
43 $this->add_route( '/security/object_ownership_enforce', 'changeObjectOwnership', 'POST' );
44
45 $this->add_route( '/getSettings', 'getSettings' );
46 $this->add_route( '/saveConfig', 'saveConfig', 'POST' );
47 $this->add_route( '/refreshCounts', 'refreshCounts', 'POST' );
48 $this->add_route( '/getErrorLogs', 'getErrorLogs', 'POST' );
49 $this->add_route( '/clearErrorLogs', 'clearErrorLogs', 'POST' );
50
51 // Upgrade Database Routes
52 $this->add_route( '/upgrade/status', 'getUpgradeStatus' );
53 $this->add_route( '/upgrade/start', 'startUpgrade', 'POST' );
54
55 // Sync Routes
56 $this->add_route( '/sync/status', 'getStatus' );
57 $this->add_route( '/sync/start', 'startSync', 'POST' );
58 $this->add_route( '/sync/pause', 'pauseSync', 'POST' );
59 $this->add_route( '/sync/resume', 'resumeSync', 'POST' );
60 $this->add_route( '/sync/stop', 'stopSync', 'POST' );
61 $this->add_route( '/sync/tick', 'tickSync', 'POST' );
62 $this->add_route( '/sync/setMethod', 'setSyncMethod', 'POST' );
63
64 $this->add_route( '/sync/retrySingle', 'retrySingle', 'POST' );
65 }
66
67 /**
68 * Verify the Service Credentials
69 */
70 public function verifyCredentials( $data ) {
71 return new WP_REST_Response( Service::instance()->verifyCredentials($data->get_params()), 200 );
72 }
73
74 /**
75 * Verify the Bucket exist
76 */
77 public function verifyBucketExist( $data ) {
78 return new WP_REST_Response( Service::instance()->verifyBucketExist($data->get_params()), 200 );
79 }
80
81 /**
82 * Create the bucket
83 */
84 public function createBucket( $data ) {
85 return new WP_REST_Response( Service::instance()->createBucket($data->get_params()), 200 );
86 }
87
88 /**
89 * Saved connection status checks (storage, CDN).
90 * @since 1.3.11
91 */
92 public function verifyStatus( $data ) {
93 $params = $data->get_params();
94 $check = isset( $params['check'] ) ? sanitize_text_field( $params['check'] ) : 'storage';
95 return new WP_REST_Response( Service::instance()->verifyStatus( $check ), 200 );
96 }
97
98 /**
99 * Verify bucket write permission (configure wizard) or saved storage status (legacy route).
100 * @since 1.3.11
101 */
102 public function verifyWrite( $data ) {
103 $params = $data->get_params();
104 if ( !empty( $params['service'] ) ) {
105 return new WP_REST_Response( Service::instance()->verifyObjectWritePermission( $params ), 200 );
106 }
107 return new WP_REST_Response( Service::instance()->verifyStatus( 'storage' ), 200 );
108 }
109
110 /**
111 * Verify the Bucket delete Permission
112 */
113 public function verifyObjectDelete( $data ) {
114 return new WP_REST_Response( Service::instance()->verifyObjectDeletePermission($data->get_params()), 200 );
115 }
116
117 /**
118 * Verify delivery read access (legacy route).
119 * @since 1.3.11
120 */
121 public function verifyRead( $data ) {
122 return new WP_REST_Response( Service::instance()->verifyStatus( 'cdn' ), 200 );
123 }
124
125 /**
126 * Get the Security Settings
127 */
128 public function getBucketSecuritySettings( $data ) {
129 return new WP_REST_Response( Service::instance()->getBucketSecuritySettings($data->get_params()), 200 );
130 }
131
132
133 /**
134 * Change the Public Access
135 */
136 public function changePublicAccess( $data ) {
137 return new WP_REST_Response( Service::instance()->changePublicAccess($data->get_params()), 200 );
138 }
139
140
141 /**
142 * Change the Object Ownership
143 */
144 public function changeObjectOwnership( $data ) {
145 return new WP_REST_Response( Service::instance()->changeObjectOwnership($data->get_params()), 200 );
146 }
147
148 /**
149 * Get Settings
150 */
151 public function getSettings( ) {
152 // Fetch and update media counts for make sure they are up to date
153 Counter::fetch_and_update();
154
155 $data = [
156 'credentials' => Utils::get_credentials( '', [], true ),
157 'serviceEnabled' => Utils::is_service_enabled(),
158 'serviceError' => Utils::get_service_configuration_error(),
159 'common' => [
160 'version' => defined('WPMCS_PRO_VERSION') ? WPMCS_PRO_VERSION : WPMCS_VERSION,
161 'counts' => [
162 'all' => Counter::get_count(),
163 'categorized' => $this->getSortedMediaCounts()
164 ],
165 'status' => Utils::get_status('', false),
166 'settings' => Utils::get_settings(),
167 ],
168 'sync' => Sync::instance()->get_status(),
169 'license' => defined('WPMCS_PRO_VERSION') ? Utils::get_safe_license_data() : [],
170 'file_manager' => defined('WPMCS_PRO_VERSION') ? [
171 'delete_enabled' => (bool) Utils::get_option('delete_enabled', false, Schema::getConstant('FILE_MANAGER_SETTINGS_KEY')),
172 'root_folder' => Utils::get_option('root_folder', '', Schema::getConstant('FILE_MANAGER_SETTINGS_KEY')),
173 ] : [],
174 ];
175
176 return new WP_REST_Response( $data, 200 );
177 }
178
179 /**
180 * Save the Configurations
181 */
182 public function saveConfig( $data ) {
183 $saveData = $data->get_params();
184 $result = [
185 'success' => false,
186 'message' => esc_html__('Something went wrong. Invalid data recieved', 'media-cloud-sync')
187 ];
188
189 $action = isset($saveData['action']) ? $saveData['action'] : false;
190 if($action === false) return new WP_REST_Response( $result, 200 );
191
192 $service = isset($saveData['service']) ? $saveData['service'] : false;
193 $serviceLabel = isset($saveData['serviceLabel']) ? $saveData['serviceLabel'] : '';
194 $cdn = isset($saveData['cdn']) ? $saveData['cdn'] : [];
195 $config = isset($saveData['config']) ? $saveData['config'] : false;
196 $configSource = isset($saveData['configSource']) ? $saveData['configSource'] : 'database';
197 $bucketConfig = isset($saveData['bucketConfig']) ? $saveData['bucketConfig'] : [];
198 $security = isset($saveData['security']) ? $saveData['security'] : [];
199 $settings = isset($saveData['settings']) ? $saveData['settings'] : [];
200
201 $serviceOk = true;
202 $settingsOk = true;
203
204 if( $action === 'cdn' ){
205 // Switching delivery provider resets the previously selected one's own fields
206 // back to their defaults (see ChooseCDN.jsx) — that can include private_path/
207 // enable_private_media, since those live in a delivery entry's own field list,
208 // so this write needs the same guard the plain settings save uses below.
209 $validation = apply_filters( 'wpmcs_validate_settings_save', ['ok' => true, 'message' => ''], $settings, $action );
210 if ( empty( $validation['ok'] ) ) {
211 return new WP_REST_Response( [
212 'success' => false,
213 'message' => !empty($validation['message']) ? $validation['message'] : esc_html__('Settings could not be saved.', 'media-cloud-sync')
214 ], 200 );
215 }
216
217 $existing = Utils::get_credentials();
218 $existing['cdn'] = $cdn;
219
220 $updated = Utils::update_option('credentials', $existing, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
221 $updatedSettings = Utils::update_option('settings', $settings, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
222
223 if(!$updated) $serviceOk = false;
224 if(!$updatedSettings) $settingsOk = false;
225
226 // Only once both writes actually persisted — a failed credentials write can
227 // leave the object cache holding an unpersisted bucket identity (see
228 // Cache::set_object_cache()), so gating on $settingsOk alone isn't enough.
229 if ($serviceOk && $settingsOk) {
230 do_action('wpmcs_after_settings_save', $settings, $action);
231 }
232 }
233
234
235 if($action === 'all' || $action === 'service'){
236 // In 'config' source mode the credentials live in the WPMCS_CONFIG constant,
237 // so an empty config payload is expected and must not block the save.
238 if ( $service === false || ($configSource !== 'config' && empty($config)) || empty($bucketConfig)) return new WP_REST_Response( $result, 200 );
239
240 $updated = Utils::update_option(
241 'credentials',
242 [
243 'service' => $service,
244 'serviceLabel' => $serviceLabel,
245 'cdn' => $cdn,
246 'config' => $configSource === 'config' ? [] : $config,
247 'configSource' => $configSource,
248 'bucketConfig' => $bucketConfig,
249 'security' => $security
250 ],
251 Schema::getConstant('GLOBAL_SETTINGS_KEY')
252 );
253 if(!$updated) $serviceOk = false;
254
255 if($serviceOk) {
256 Utils::set_status('cdnRead', [
257 'status' => false,
258 'message' => '',
259 'lastChecked' => null
260 ]);
261 Utils::set_status('storageCredentials', [
262 'status' => false,
263 'message' => '',
264 'lastChecked' => null
265 ]);
266 }
267 }
268
269 if(($action === 'all' || $action === 'settings')) {
270 // Extension point for a pro-only settings-save guard (e.g. private media
271 // refusing a private_path/base_path change while items are still private
272 // under the currently-applied path) — free has no opinion here, default
273 // is "allow". ['ok' => bool, 'message' => string].
274 $validation = apply_filters( 'wpmcs_validate_settings_save', ['ok' => true, 'message' => ''], $settings, $action );
275 if ( empty( $validation['ok'] ) ) {
276 return new WP_REST_Response( [
277 'success' => false,
278 'message' => !empty($validation['message']) ? $validation['message'] : esc_html__('Settings could not be saved.', 'media-cloud-sync')
279 ], 200 );
280 }
281
282 $updatedSettings = Utils::update_option('settings', $settings, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
283 if(!$updatedSettings) $settingsOk = false;
284
285 // Only once both writes actually persisted — a failed credentials write can
286 // leave the object cache holding an unpersisted bucket identity (see
287 // Cache::set_object_cache()), so gating on $settingsOk alone isn't enough.
288 if ($serviceOk && $settingsOk) {
289 do_action('wpmcs_after_settings_save', $settings, $action);
290 }
291 }
292
293 // Clear plugin caches and refresh counts after any configuration save.
294 if(in_array($action, ['all', 'service', 'settings', 'cdn'], true)) {
295 if($action === 'all' || $action === 'service' || $action === 'settings') {
296 Integration::instance()->clear_all_meta(false);
297 }
298
299 if($action === 'all' || $action === 'settings') {
300 Utils::clear_all_content_meta(false, false);
301 }
302
303 Cache::flush_object_cache();
304 Counter::instance()->fetch_and_update();
305 }
306
307
308 if($serviceOk && $settingsOk) {
309 $result = [
310 'success' => true,
311 'message' => esc_html__('Configuration saved successfully', 'media-cloud-sync')
312 ];
313 }
314
315 return new WP_REST_Response( $result, 200 );
316 }
317
318
319 /**
320 * Refresh the Media Counts
321 *
322 */
323 public function refreshCounts( ) {
324 Cache::flush_object_cache();
325 Integration::instance()->clear_all_meta();
326 Counter::instance()->fetch_and_update();
327
328 $result = [
329 'success' => true,
330 'counts' => [
331 'all' => Counter::get_count(),
332 'categorized' => $this->getSortedMediaCounts()
333 ],
334 'message' => esc_html__('Media counts refreshed successfully', 'media-cloud-sync')
335 ];
336 return new WP_REST_Response( $result, 200 );
337 }
338
339
340 /**
341 * Start Upgrade
342 * @since 1.3.6
343 */
344 public function startUpgrade( $request ) {
345 $result = Upgrade::instance()->start_upgrade();
346 return new WP_REST_Response( $result, 200 );
347 }
348
349
350 /**
351 * Get Upgrade Status
352 * @since 1.3.6
353 */ public function getUpgradeStatus( $request ) {
354 $result = Upgrade::instance()->get_progress();
355 return new WP_REST_Response( $result, 200 );
356 }
357
358
359 /**
360 * Get Media Error Logs
361 * @since 1.2.13
362 */
363 public function getErrorLogs( $request ) {
364 $params = $request->get_params();
365 $type = isset($params['type']) ? $params['type'] : 'all';
366 $page = isset($params['page']) ? $params['page'] : null;
367 $per_page = isset($params['per_page']) ? $params['per_page'] : null;
368
369 if($type !== 'all') {
370 $logs = Logger::instance()->get_logs_by_type($type, $page, $per_page);
371 } else {
372 $logs = Logger::instance()->get_all_logs($page, $per_page);
373 }
374
375 $result = [
376 'success' => true,
377 'logs' => $logs,
378 'message' => esc_html__('Error logs fetched successfully', 'media-cloud-sync')
379 ];
380 return new WP_REST_Response( $result, 200 );
381 }
382
383
384 /**
385 * Clear all error logs for a sync type (e.g. stale entries from earlier failed attempts).
386 * @since 1.3.13
387 */
388 public function clearErrorLogs( $request ) {
389 $type = isset($request->get_params()['type']) ? $request->get_params()['type'] : '';
390
391 if (empty($type) || !Sync::instance()->get_class_by_action($type)) {
392 return new WP_REST_Response( [
393 'success' => false,
394 'message' => esc_html__('Invalid action', 'media-cloud-sync')
395 ], 200 );
396 }
397
398 Logger::instance()->remove_logs_by_type($type);
399
400 $result = [
401 'success' => true,
402 'message' => esc_html__('Error logs cleared', 'media-cloud-sync')
403 ];
404 return new WP_REST_Response( $result, 200 );
405 }
406
407
408 /**
409 * Get Status
410 * @since 1.2.13
411 */
412 public function getStatus( $request ) {
413 // Fetch and update media counts for make sure they are up to date
414 $action = isset($request->get_params()['action']) ? $request->get_params()['action'] : 'all';
415 $status = Sync::instance()->get_status();
416
417 if($action !== 'all') {
418 $status = isset($status[$action]) ? $status[$action] : [];
419 }
420
421 $result = [
422 'success' => true,
423 'status' => $status,
424 'counts' => [
425 'all' => Counter::get_count(),
426 'categorized' => $this->getSortedMediaCounts()
427 ],
428 'message' => esc_html__('Status fetched successfully', 'media-cloud-sync')
429 ];
430
431 return new WP_REST_Response( $result, 200 );
432 }
433
434
435 /**
436 * Start Sync
437 * @since 1.2.13
438 */
439 public function startSync( $data ) {
440 $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
441 if ( ! Sync::instance()->get_class_by_action( $action ) ) {
442 $result = [
443 'success' => false,
444 'message' => esc_html__('Invalid action', 'media-cloud-sync')
445 ];
446 return new WP_REST_Response( $result, 200 );
447 }
448 if(!Utils::is_service_enabled()) {
449 $result = [
450 'success' => false,
451 'message' => Utils::get_service_configuration_error() ?: esc_html__('Storage is not configured correctly.', 'media-cloud-sync')
452 ];
453 return new WP_REST_Response( $result, 200 );
454 }
455 $options = isset($data->get_params()['options']) && is_array($data->get_params()['options']) ? $data->get_params()['options'] : [];
456 $status = Sync::instance()->start($action, $options);
457
458 // Sync::start() passes a job's own precondition-failure array straight through
459 // (e.g. no destination configured) instead of masking it as a started status.
460 if (is_array($status) && isset($status['success']) && $status['success'] === false) {
461 return new WP_REST_Response( $status, 200 );
462 }
463
464 $result = [
465 'success' => true,
466 'status' => $status,
467 'message' => esc_html__('Sync started successfully', 'media-cloud-sync')
468 ];
469 return new WP_REST_Response( $result, 200 );
470 }
471
472
473 /**
474 * Pause Sync
475 * @since 1.2.13
476 */
477 public function pauseSync( $data ) {
478 $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
479 if ( ! Sync::instance()->get_class_by_action( $action ) ) {
480 $result = [
481 'success' => false,
482 'message' => esc_html__('Invalid action', 'media-cloud-sync')
483 ];
484 return new WP_REST_Response( $result, 200 );
485 }
486 $status = Sync::instance()->pause($action);
487
488 $result = [
489 'success' => true,
490 'status' => $status,
491 'message' => esc_html__('Sync paused successfully', 'media-cloud-sync')
492 ];
493 return new WP_REST_Response( $result, 200 );
494 }
495
496
497 /**
498 * Save the sync method ('ajax'|'cron'|'mixed') preference for a sync type.
499 * @since 1.3.13
500 */
501 public function setSyncMethod( $data ) {
502 $params = $data->get_params();
503 $action = isset($params['action']) ? sanitize_text_field($params['action']) : '';
504 $method = isset($params['sync_method']) ? sanitize_text_field($params['sync_method']) : '';
505
506 if ( ! Sync::instance()->get_class_by_action( $action ) ) {
507 return new WP_REST_Response( [
508 'success' => false,
509 'message' => esc_html__('Invalid action', 'media-cloud-sync')
510 ], 200 );
511 }
512
513 $saved = Sync::instance()->set_sync_method($action, $method);
514 if(!$saved) {
515 return new WP_REST_Response( [
516 'success' => false,
517 'message' => esc_html__('Invalid sync method', 'media-cloud-sync')
518 ], 200 );
519 }
520
521 $result = [
522 'success' => true,
523 'sync_method' => $method,
524 'status' => Sync::instance()->get_class_by_action($action)->get_status(),
525 ];
526 return new WP_REST_Response( $result, 200 );
527 }
528
529 /**
530 * Resume Sync
531 * @since 1.2.13
532 */
533 public function resumeSync( $data ) {
534 $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
535 if ( ! Sync::instance()->get_class_by_action( $action ) ) {
536 $result = [
537 'success' => false,
538 'message' => esc_html__('Invalid action', 'media-cloud-sync')
539 ];
540 return new WP_REST_Response( $result, 200 );
541 }
542 $status = Sync::instance()->resume($action);
543
544 $result = [
545 'success' => true,
546 'status' => $status,
547 'message' => esc_html__('Sync resumed successfully', 'media-cloud-sync')
548 ];
549 return new WP_REST_Response( $result, 200 );
550 }
551
552
553 /**
554 * Stop Sync
555 * @since 1.2.13
556 */
557 public function stopSync( $data ) {
558 $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
559 if ( ! Sync::instance()->get_class_by_action( $action ) ) {
560 $result = [
561 'success' => false,
562 'message' => esc_html__('Invalid action', 'media-cloud-sync')
563 ];
564 return new WP_REST_Response( $result, 200 );
565 }
566 $status = Sync::instance()->stop($action);
567
568 $result = [
569 'success' => true,
570 'status' => $status,
571 'counts' => [
572 'all' => Counter::get_count(),
573 'categorized' => $this->getSortedMediaCounts()
574 ],
575 'message' => esc_html__('Sync stopped successfully', 'media-cloud-sync')
576 ];
577 return new WP_REST_Response( $result, 200 );
578 }
579
580
581 /**
582 * Tick Sync — process a small batch synchronously, driving Ajax/Mixed sync mode.
583 * @since 1.3.13
584 */
585 public function tickSync( $data ) {
586 $params = $data->get_params();
587 $action = isset($params['action']) ? $params['action'] : '';
588
589 if ( ! Sync::instance()->get_class_by_action( $action ) ) {
590 $result = [
591 'success' => false,
592 'message' => esc_html__('Invalid action', 'media-cloud-sync')
593 ];
594 return new WP_REST_Response( $result, 200 );
595 }
596
597 // Defense in depth — the frontend should never call this in free/unlicensed mode, but
598 // the endpoint must not trust that, and must not silently no-op: an unlicensed call
599 // needs a clear rejection so the frontend's tick loop stops instead of retrying forever.
600 if ( ! Utils::is_pro_licensed() ) {
601 $result = [
602 'success' => false,
603 'message' => esc_html__('Ajax sync requires an active Pro license', 'media-cloud-sync')
604 ];
605 return new WP_REST_Response( $result, 200 );
606 }
607
608 // Clamp regardless of what the frontend sends — the endpoint itself must enforce the
609 // ceiling, since a batch of slow uploads processed sequentially in one request risks
610 // exceeding reverse-proxy/webserver timeouts that process_iterations() can't see.
611 $batch = isset($params['batch']) ? (int) $params['batch'] : 5;
612 $batch = min(10, max(1, $batch));
613
614 $status = Sync::instance()->tick($action, $batch);
615
616 $result = [
617 'success' => true,
618 'status' => $status,
619 'message' => esc_html__('Sync ticked successfully', 'media-cloud-sync')
620 ];
621
622 // Deliberately omitted on every tick (this endpoint may be hit ~once/second while a
623 // sync runs, and Counter::get_count() is a real query) — but the Dashboard's own
624 // Statistics widget reads a *different* redux slice (common.counts) than the Sync
625 // screen's progress (sync[action]), only refreshed via the slower ~2-3s useSyncPolling
626 // cadence. That's an acceptable few-seconds lag while a sync is actively running, but
627 // right when it finishes it becomes a visibly wrong, stuck-looking number on the
628 // Dashboard until the next poll happens to catch up. Only pay for the query at that
629 // one transition, not on every tick.
630 if ( isset( $status['status'] ) && $status['status'] !== 'running' ) {
631 $result['counts'] = [
632 'all' => Counter::get_count(),
633 'categorized' => $this->getSortedMediaCounts()
634 ];
635 }
636 return new WP_REST_Response( $result, 200 );
637 }
638
639
640 /**
641 * Retry a Media Sync Error
642 */
643 public function retrySingle( $data ) {
644 $params = $data->get_params();
645 $id = isset($params['id']) ? $params['id'] : '';
646 $source_type = isset($params['source_type']) ? $params['source_type'] : 'media_library';
647 $type = isset($params['type']) ? $params['type'] : '';
648 $status = false;
649 if(empty($id) || empty($source_type) || empty($type)) {
650 $result = [
651 'success' => false,
652 'message' => esc_html__('Invalid parameters', 'media-cloud-sync')
653 ];
654 return new WP_REST_Response( $result, 200 );
655 }
656 // Retry Sync
657 $handler = Sync::instance()->get_class_by_action($type);
658 if(!$handler) {
659 $result = [
660 'success' => false,
661 'message' => esc_html__('Invalid action', 'media-cloud-sync')
662 ];
663 return new WP_REST_Response( $result, 200 );
664 }
665 if(!method_exists($handler, 'retry_single')) {
666 $result = [
667 'success' => false,
668 'message' => esc_html__('Retry is not available for this sync type.', 'media-cloud-sync')
669 ];
670 return new WP_REST_Response( $result, 200 );
671 }
672 $handler->retry_single($id, $source_type);
673
674 $status = Utils::is_empty(Logger::instance()->get_log($type, $id, $source_type)) ? true : false;
675
676 $result = [
677 'success' => $status,
678 'message' => $status ? esc_html__('Media synced successfully', 'media-cloud-sync') : esc_html__('Media sync failed', 'media-cloud-sync')
679 ];
680 return new WP_REST_Response( $result, 200 );
681 }
682
683
684 /**
685 * Get Sorted Media Counts
686 * Sorts media counts by source type
687 * @since 1.2.13
688 */
689 private function getSortedMediaCounts() {
690 $sorted_counts = [];
691 $detailed_counts = Counter::get_count( 'all', false );
692 if(!empty($detailed_counts)) {
693 $source_labels = Integration::$source_labels;
694 if(!empty($source_labels)) {
695 $prefixes = array_keys($source_labels);
696 foreach($detailed_counts as $source_type => $counts) {
697 $found_prefix = false;
698 foreach($prefixes as $prefix) {
699 $found_prefix = strpos($source_type, $prefix) === 0 ? $prefix : false;
700 if($found_prefix !== false) break;
701 }
702
703 if($found_prefix !== false) {
704 $sorted_counts[$found_prefix] = [
705 'label' => $source_labels[$found_prefix],
706 'uploaded' => isset( $sorted_counts[ $found_prefix ]['uploaded'] )
707 ? $sorted_counts[ $found_prefix ]['uploaded'] + $counts['uploaded']
708 : $counts['uploaded'],
709 'total' => isset( $sorted_counts[ $found_prefix ]['total'] )
710 ? $sorted_counts[ $found_prefix ]['total'] + $counts['total']
711 : $counts['total']
712 ];
713 }
714 }
715 }
716 }
717
718 return $sorted_counts;
719 }
720
721
722 /**
723 * Helper function to create Adding Route
724 * @since 1.0.0
725 */
726 private function add_route( $slug, $callBack, $method = 'GET' ) {
727 register_rest_route(
728 $this->token . '/v1',
729 $slug,
730 array(
731 'methods' => $method,
732 'callback' => array( $this, $callBack ),
733 'permission_callback' => array( $this, 'getPermission' ),
734 ) );
735 }
736
737 /**
738 * Permission Callback
739 **/
740 public function getPermission() {
741 if ( current_user_can( 'manage_options' ) ) {
742 return true;
743 } else {
744 return false;
745 }
746 }
747
748 /**
749 * Ensures only one instance of Class is loaded or can be loaded.
750 *
751 * @return Api Class instance
752 * @since 1.0.0
753 * @static
754 */
755 public static function instance(){
756 if (is_null(self::$instance)) {
757 self::$instance = new self();
758 }
759 return self::$instance;
760 }
761 }
762