PluginProbe
Media Cloud Sync / trunk
Media Cloud Sync vtrunk
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
← All changes | includes/api.php +499 -33 1.2.13trunk View file →
@@ -30,20 +30,39 @@
30 30 */
31 31
32 32 public function register_routes() {
33 33 $this->add_route( '/verifyCredentials', 'verifyCredentials', 'POST' );
34 - $this->add_route( '/permissions/checkExist', 'verifyBucketExist', 'POST' );
35 - $this->add_route( '/permissions/addNewBucket', 'createBucket', 'POST' );
36 - $this->add_route( '/permissions/write', 'verifyObjectWrite', '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' );
37 38 $this->add_route( '/permissions/delete', 'verifyObjectDelete', 'POST' );
38 - $this->add_route( '/permissions/read', 'verifyObjectRead', 'POST' );
39 + $this->add_route( '/permissions/read', 'verifyRead', 'POST' );
39 40
40 41 $this->add_route( '/security/get_security', 'getBucketSecuritySettings', 'POST' );
41 42 $this->add_route( '/security/block_public_access', 'changePublicAccess', 'POST' );
42 43 $this->add_route( '/security/object_ownership_enforce', 'changeObjectOwnership', 'POST' );
43 44
45 + $this->add_route( '/getSettings', 'getSettings' );
44 46 $this->add_route( '/saveConfig', 'saveConfig', 'POST' );
45 - $this->add_route( '/getSettings', 'getSettings' );
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' );
46 65 }
47 66
48 67 /**
49 68 * Verify the Service Credentials
@@ -65,17 +84,31 @@
65 84 public function createBucket( $data ) {
66 85 return new WP_REST_Response( Service::instance()->createBucket($data->get_params()), 200 );
67 86 }
68 87
69 -
70 88 /**
71 - * Verify the Bucket Write Permission
89 + * Saved connection status checks (storage, CDN).
90 + * @since 1.3.11
72 91 */
73 - public function verifyObjectWrite( $data ) {
74 - return new WP_REST_Response( Service::instance()->verifyObjectWritePermission($data->get_params()), 200 );
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 );
75 96 }
76 97
77 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 + /**
78 111 * Verify the Bucket delete Permission
79 112 */
80 113 public function verifyObjectDelete( $data ) {
81 114 return new WP_REST_Response( Service::instance()->verifyObjectDeletePermission($data->get_params()), 200 );
@@ -81,12 +114,13 @@
81 114 return new WP_REST_Response( Service::instance()->verifyObjectDeletePermission($data->get_params()), 200 );
82 115 }
83 116
84 117 /**
85 - * Verify the Bucket Read Permission
118 + * Verify delivery read access (legacy route).
119 + * @since 1.3.11
86 120 */
87 - public function verifyObjectRead( $data ) {
88 - return new WP_REST_Response( Service::instance()->verifyObjectReadPermission($data->get_params()), 200 );
121 + public function verifyRead( $data ) {
122 + return new WP_REST_Response( Service::instance()->verifyStatus( 'cdn' ), 200 );
89 123 }
90 124
91 125 /**
92 126 * Get the Security Settings
@@ -114,16 +148,30 @@
114 148 /**
115 149 * Get Settings
116 150 */
117 151 public function getSettings( ) {
152 + // Fetch and update media counts for make sure they are up to date
153 + Counter::fetch_and_update();
154 +
118 155 $data = [
119 - 'credentials' => Utils::get_credentials(),
120 - 'common' => [
121 - 'version' => WPMCS_VERSION,
122 - 'uploaded' => Counter::get_count( 'uploaded' ),
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 + ],
123 165 'status' => Utils::get_status('', false),
124 166 'settings' => Utils::get_settings(),
125 - ]
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 + ] : [],
126 174 ];
127 175
128 176 return new WP_REST_Response( $data, 200 );
129 177 }
@@ -144,8 +192,9 @@
144 192 $service = isset($saveData['service']) ? $saveData['service'] : false;
145 193 $serviceLabel = isset($saveData['serviceLabel']) ? $saveData['serviceLabel'] : '';
146 194 $cdn = isset($saveData['cdn']) ? $saveData['cdn'] : [];
147 195 $config = isset($saveData['config']) ? $saveData['config'] : false;
196 + $configSource = isset($saveData['configSource']) ? $saveData['configSource'] : 'database';
148 197 $bucketConfig = isset($saveData['bucketConfig']) ? $saveData['bucketConfig'] : [];
149 198 $security = isset($saveData['security']) ? $saveData['security'] : [];
150 199 $settings = isset($saveData['settings']) ? $saveData['settings'] : [];
151 200
@@ -159,13 +208,16 @@
159 208 $updated = Utils::update_option('credentials', $existing, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
160 209 $updatedSettings = Utils::update_option('settings', $settings, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
161 210
162 211 if(!$updated) $serviceOk = false;
212 + if(!$updatedSettings) $settingsOk = false;
163 213 }
164 214
165 215
166 216 if($action === 'all' || $action === 'service'){
167 - if ( $service === false || empty($config) || empty($bucketConfig)) return new WP_REST_Response( $result, 200 );
217 + // In 'config' source mode the credentials live in the WPMCS_CONFIG constant,
218 + // so an empty config payload is expected and must not block the save.
219 + if ( $service === false || ($configSource !== 'config' && empty($config)) || empty($bucketConfig)) return new WP_REST_Response( $result, 200 );
168 220
169 221 $updated = Utils::update_option(
170 222 'credentials',
171 223 [
@@ -171,9 +223,10 @@
171 223 [
172 224 'service' => $service,
173 225 'serviceLabel' => $serviceLabel,
174 226 'cdn' => $cdn,
175 - 'config' => $config,
227 + 'config' => $configSource === 'config' ? [] : $config,
228 + 'configSource' => $configSource,
176 229 'bucketConfig' => $bucketConfig,
177 230 'security' => $security
178 231 ],
179 232 Schema::getConstant('GLOBAL_SETTINGS_KEY')
@@ -178,8 +231,21 @@
178 231 ],
179 232 Schema::getConstant('GLOBAL_SETTINGS_KEY')
180 233 );
181 234 if(!$updated) $serviceOk = false;
235 +
236 + if($serviceOk) {
237 + Utils::set_status('cdnRead', [
238 + 'status' => false,
239 + 'message' => '',
240 + 'lastChecked' => null
241 + ]);
242 + Utils::set_status('storageCredentials', [
243 + 'status' => false,
244 + 'message' => '',
245 + 'lastChecked' => null
246 + ]);
247 + }
182 248 }
183 249
184 250 if(($action === 'all' || $action === 'settings')) {
185 251 $updatedSettings = Utils::update_option('settings', $settings, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
@@ -185,15 +251,24 @@
185 251 $updatedSettings = Utils::update_option('settings', $settings, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
186 252 if(!$updatedSettings) $settingsOk = false;
187 253 }
188 254
255 + // Clear plugin caches and refresh counts after any configuration save.
256 + if(in_array($action, ['all', 'service', 'settings', 'cdn'], true)) {
257 + if($action === 'all' || $action === 'service' || $action === 'settings') {
258 + Integration::instance()->clear_all_meta(false);
259 + }
189 260
261 + if($action === 'all' || $action === 'settings') {
262 + Utils::clear_all_content_meta(false, false);
263 + }
264 +
265 + Cache::flush_object_cache();
266 + Counter::instance()->fetch_and_update();
267 + }
268 +
269 +
190 270 if($serviceOk && $settingsOk) {
191 - Utils::set_status('cdnRead', [
192 - 'status' => false,
193 - 'message' => '',
194 - 'lastChecked' => null
195 - ]);
196 271 $result = [
197 272 'success' => true,
198 273 'message' => esc_html__('Configuration saved successfully', 'media-cloud-sync')
199 274 ];
@@ -202,17 +277,408 @@
202 277 return new WP_REST_Response( $result, 200 );
203 278 }
204 279
205 280
206 - /**
207 - * Add Custom Mime Type JSON
208 - * @since 1.0.0
209 - */
210 - public function add_custom_mime_type_json($mimes) {
211 - $mimes['json'] = 'application/json';
212 - // Return the array back to the function with our added mime type.
213 - return $mimes;
214 - }
281 + /**
282 + * Refresh the Media Counts
283 + *
284 + */
285 + public function refreshCounts( ) {
286 + Cache::flush_object_cache();
287 + Integration::instance()->clear_all_meta();
288 + Counter::instance()->fetch_and_update();
289 +
290 + $result = [
291 + 'success' => true,
292 + 'counts' => [
293 + 'all' => Counter::get_count(),
294 + 'categorized' => $this->getSortedMediaCounts()
295 + ],
296 + 'message' => esc_html__('Media counts refreshed successfully', 'media-cloud-sync')
297 + ];
298 + return new WP_REST_Response( $result, 200 );
299 + }
300 +
301 +
302 + /**
303 + * Start Upgrade
304 + * @since 1.3.6
305 + */
306 + public function startUpgrade( $request ) {
307 + $result = Upgrade::instance()->start_upgrade();
308 + return new WP_REST_Response( $result, 200 );
309 + }
310 +
311 +
312 + /**
313 + * Get Upgrade Status
314 + * @since 1.3.6
315 + */ public function getUpgradeStatus( $request ) {
316 + $result = Upgrade::instance()->get_progress();
317 + return new WP_REST_Response( $result, 200 );
318 + }
319 +
320 +
321 + /**
322 + * Get Media Error Logs
323 + * @since 1.2.13
324 + */
325 + public function getErrorLogs( $request ) {
326 + $params = $request->get_params();
327 + $type = isset($params['type']) ? $params['type'] : 'all';
328 + $page = isset($params['page']) ? $params['page'] : null;
329 + $per_page = isset($params['per_page']) ? $params['per_page'] : null;
330 +
331 + if($type !== 'all') {
332 + $logs = Logger::instance()->get_logs_by_type($type, $page, $per_page);
333 + } else {
334 + $logs = Logger::instance()->get_all_logs($page, $per_page);
335 + }
336 +
337 + $result = [
338 + 'success' => true,
339 + 'logs' => $logs,
340 + 'message' => esc_html__('Error logs fetched successfully', 'media-cloud-sync')
341 + ];
342 + return new WP_REST_Response( $result, 200 );
343 + }
344 +
345 +
346 + /**
347 + * Clear all error logs for a sync type (e.g. stale entries from earlier failed attempts).
348 + * @since 1.3.13
349 + */
350 + public function clearErrorLogs( $request ) {
351 + $type = isset($request->get_params()['type']) ? $request->get_params()['type'] : '';
352 +
353 + if (empty($type) || !Sync::instance()->get_class_by_action($type)) {
354 + return new WP_REST_Response( [
355 + 'success' => false,
356 + 'message' => esc_html__('Invalid action', 'media-cloud-sync')
357 + ], 200 );
358 + }
359 +
360 + Logger::instance()->remove_logs_by_type($type);
361 +
362 + $result = [
363 + 'success' => true,
364 + 'message' => esc_html__('Error logs cleared', 'media-cloud-sync')
365 + ];
366 + return new WP_REST_Response( $result, 200 );
367 + }
368 +
369 +
370 + /**
371 + * Get Status
372 + * @since 1.2.13
373 + */
374 + public function getStatus( $request ) {
375 + // Fetch and update media counts for make sure they are up to date
376 + $action = isset($request->get_params()['action']) ? $request->get_params()['action'] : 'all';
377 + $status = Sync::instance()->get_status();
378 +
379 + if($action !== 'all') {
380 + $status = isset($status[$action]) ? $status[$action] : [];
381 + }
382 +
383 + $result = [
384 + 'success' => true,
385 + 'status' => $status,
386 + 'counts' => [
387 + 'all' => Counter::get_count(),
388 + 'categorized' => $this->getSortedMediaCounts()
389 + ],
390 + 'message' => esc_html__('Status fetched successfully', 'media-cloud-sync')
391 + ];
392 +
393 + return new WP_REST_Response( $result, 200 );
394 + }
395 +
396 +
397 + /**
398 + * Start Sync
399 + * @since 1.2.13
400 + */
401 + public function startSync( $data ) {
402 + $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
403 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
404 + $result = [
405 + 'success' => false,
406 + 'message' => esc_html__('Invalid action', 'media-cloud-sync')
407 + ];
408 + return new WP_REST_Response( $result, 200 );
409 + }
410 + if(!Utils::is_service_enabled()) {
411 + $result = [
412 + 'success' => false,
413 + 'message' => Utils::get_service_configuration_error() ?: esc_html__('Storage is not configured correctly.', 'media-cloud-sync')
414 + ];
415 + return new WP_REST_Response( $result, 200 );
416 + }
417 + $status = Sync::instance()->start($action);
418 +
419 + // Sync::start() passes a job's own precondition-failure array straight through
420 + // (e.g. no destination configured) instead of masking it as a started status.
421 + if (is_array($status) && isset($status['success']) && $status['success'] === false) {
422 + return new WP_REST_Response( $status, 200 );
423 + }
424 +
425 + $result = [
426 + 'success' => true,
427 + 'status' => $status,
428 + 'message' => esc_html__('Sync started successfully', 'media-cloud-sync')
429 + ];
430 + return new WP_REST_Response( $result, 200 );
431 + }
432 +
433 +
434 + /**
435 + * Pause Sync
436 + * @since 1.2.13
437 + */
438 + public function pauseSync( $data ) {
439 + $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
440 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
441 + $result = [
442 + 'success' => false,
443 + 'message' => esc_html__('Invalid action', 'media-cloud-sync')
444 + ];
445 + return new WP_REST_Response( $result, 200 );
446 + }
447 + $status = Sync::instance()->pause($action);
448 +
449 + $result = [
450 + 'success' => true,
451 + 'status' => $status,
452 + 'message' => esc_html__('Sync paused successfully', 'media-cloud-sync')
453 + ];
454 + return new WP_REST_Response( $result, 200 );
455 + }
456 +
457 +
458 + /**
459 + * Save the sync method ('ajax'|'cron'|'mixed') preference for a sync type.
460 + * @since 1.3.13
461 + */
462 + public function setSyncMethod( $data ) {
463 + $params = $data->get_params();
464 + $action = isset($params['action']) ? sanitize_text_field($params['action']) : '';
465 + $method = isset($params['sync_method']) ? sanitize_text_field($params['sync_method']) : '';
466 +
467 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
468 + return new WP_REST_Response( [
469 + 'success' => false,
470 + 'message' => esc_html__('Invalid action', 'media-cloud-sync')
471 + ], 200 );
472 + }
473 +
474 + $saved = Sync::instance()->set_sync_method($action, $method);
475 + if(!$saved) {
476 + return new WP_REST_Response( [
477 + 'success' => false,
478 + 'message' => esc_html__('Invalid sync method', 'media-cloud-sync')
479 + ], 200 );
480 + }
481 +
482 + $result = [
483 + 'success' => true,
484 + 'sync_method' => $method,
485 + 'status' => Sync::instance()->get_class_by_action($action)->get_status(),
486 + ];
487 + return new WP_REST_Response( $result, 200 );
488 + }
489 +
490 + /**
491 + * Resume Sync
492 + * @since 1.2.13
493 + */
494 + public function resumeSync( $data ) {
495 + $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
496 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
497 + $result = [
498 + 'success' => false,
499 + 'message' => esc_html__('Invalid action', 'media-cloud-sync')
500 + ];
501 + return new WP_REST_Response( $result, 200 );
502 + }
503 + $status = Sync::instance()->resume($action);
504 +
505 + $result = [
506 + 'success' => true,
507 + 'status' => $status,
508 + 'message' => esc_html__('Sync resumed successfully', 'media-cloud-sync')
509 + ];
510 + return new WP_REST_Response( $result, 200 );
511 + }
512 +
513 +
514 + /**
515 + * Stop Sync
516 + * @since 1.2.13
517 + */
518 + public function stopSync( $data ) {
519 + $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
520 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
521 + $result = [
522 + 'success' => false,
523 + 'message' => esc_html__('Invalid action', 'media-cloud-sync')
524 + ];
525 + return new WP_REST_Response( $result, 200 );
526 + }
527 + $status = Sync::instance()->stop($action);
528 +
529 + $result = [
530 + 'success' => true,
531 + 'status' => $status,
532 + 'counts' => [
533 + 'all' => Counter::get_count(),
534 + 'categorized' => $this->getSortedMediaCounts()
535 + ],
536 + 'message' => esc_html__('Sync stopped successfully', 'media-cloud-sync')
537 + ];
538 + return new WP_REST_Response( $result, 200 );
539 + }
540 +
541 +
542 + /**
543 + * Tick Sync — process a small batch synchronously, driving Ajax/Mixed sync mode.
544 + * @since 1.3.13
545 + */
546 + public function tickSync( $data ) {
547 + $params = $data->get_params();
548 + $action = isset($params['action']) ? $params['action'] : '';
549 +
550 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
551 + $result = [
552 + 'success' => false,
553 + 'message' => esc_html__('Invalid action', 'media-cloud-sync')
554 + ];
555 + return new WP_REST_Response( $result, 200 );
556 + }
557 +
558 + // Defense in depth — the frontend should never call this in free/unlicensed mode, but
559 + // the endpoint must not trust that, and must not silently no-op: an unlicensed call
560 + // needs a clear rejection so the frontend's tick loop stops instead of retrying forever.
561 + if ( ! Utils::is_pro_licensed() ) {
562 + $result = [
563 + 'success' => false,
564 + 'message' => esc_html__('Ajax sync requires an active Pro license', 'media-cloud-sync')
565 + ];
566 + return new WP_REST_Response( $result, 200 );
567 + }
568 +
569 + // Clamp regardless of what the frontend sends — the endpoint itself must enforce the
570 + // ceiling, since a batch of slow uploads processed sequentially in one request risks
571 + // exceeding reverse-proxy/webserver timeouts that process_iterations() can't see.
572 + $batch = isset($params['batch']) ? (int) $params['batch'] : 5;
573 + $batch = min(10, max(1, $batch));
574 +
575 + $status = Sync::instance()->tick($action, $batch);
576 +
577 + $result = [
578 + 'success' => true,
579 + 'status' => $status,
580 + 'message' => esc_html__('Sync ticked successfully', 'media-cloud-sync')
581 + ];
582 +
583 + // Deliberately omitted on every tick (this endpoint may be hit ~once/second while a
584 + // sync runs, and Counter::get_count() is a real query) — but the Dashboard's own
585 + // Statistics widget reads a *different* redux slice (common.counts) than the Sync
586 + // screen's progress (sync[action]), only refreshed via the slower ~2-3s useSyncPolling
587 + // cadence. That's an acceptable few-seconds lag while a sync is actively running, but
588 + // right when it finishes it becomes a visibly wrong, stuck-looking number on the
589 + // Dashboard until the next poll happens to catch up. Only pay for the query at that
590 + // one transition, not on every tick.
591 + if ( isset( $status['status'] ) && $status['status'] !== 'running' ) {
592 + $result['counts'] = [
593 + 'all' => Counter::get_count(),
594 + 'categorized' => $this->getSortedMediaCounts()
595 + ];
596 + }
597 + return new WP_REST_Response( $result, 200 );
598 + }
599 +
600 +
601 + /**
602 + * Retry a Media Sync Error
603 + */
604 + public function retrySingle( $data ) {
605 + $params = $data->get_params();
606 + $id = isset($params['id']) ? $params['id'] : '';
607 + $source_type = isset($params['source_type']) ? $params['source_type'] : 'media_library';
608 + $type = isset($params['type']) ? $params['type'] : '';
609 + $status = false;
610 + if(empty($id) || empty($source_type) || empty($type)) {
611 + $result = [
612 + 'success' => false,
613 + 'message' => esc_html__('Invalid parameters', 'media-cloud-sync')
614 + ];
615 + return new WP_REST_Response( $result, 200 );
616 + }
617 + // Retry Sync
618 + $handler = Sync::instance()->get_class_by_action($type);
619 + if(!$handler) {
620 + $result = [
621 + 'success' => false,
622 + 'message' => esc_html__('Invalid action', 'media-cloud-sync')
623 + ];
624 + return new WP_REST_Response( $result, 200 );
625 + }
626 + if(!method_exists($handler, 'retry_single')) {
627 + $result = [
628 + 'success' => false,
629 + 'message' => esc_html__('Retry is not available for this sync type.', 'media-cloud-sync')
630 + ];
631 + return new WP_REST_Response( $result, 200 );
632 + }
633 + $handler->retry_single($id, $source_type);
634 +
635 + $status = Utils::is_empty(Logger::instance()->get_log($type, $id, $source_type)) ? true : false;
636 +
637 + $result = [
638 + 'success' => $status,
639 + 'message' => $status ? esc_html__('Media synced successfully', 'media-cloud-sync') : esc_html__('Media sync failed', 'media-cloud-sync')
640 + ];
641 + return new WP_REST_Response( $result, 200 );
642 + }
643 +
644 +
645 + /**
646 + * Get Sorted Media Counts
647 + * Sorts media counts by source type
648 + * @since 1.2.13
649 + */
650 + private function getSortedMediaCounts() {
651 + $sorted_counts = [];
652 + $detailed_counts = Counter::get_count( 'all', false );
653 + if(!empty($detailed_counts)) {
654 + $source_labels = Integration::$source_labels;
655 + if(!empty($source_labels)) {
656 + $prefixes = array_keys($source_labels);
657 + foreach($detailed_counts as $source_type => $counts) {
658 + $found_prefix = false;
659 + foreach($prefixes as $prefix) {
660 + $found_prefix = strpos($source_type, $prefix) === 0 ? $prefix : false;
661 + if($found_prefix !== false) break;
662 + }
663 +
664 + if($found_prefix !== false) {
665 + $sorted_counts[$found_prefix] = [
666 + 'label' => $source_labels[$found_prefix],
667 + 'uploaded' => isset( $sorted_counts[ $found_prefix ]['uploaded'] )
668 + ? $sorted_counts[ $found_prefix ]['uploaded'] + $counts['uploaded']
669 + : $counts['uploaded'],
670 + 'total' => isset( $sorted_counts[ $found_prefix ]['total'] )
671 + ? $sorted_counts[ $found_prefix ]['total'] + $counts['total']
672 + : $counts['total']
673 + ];
674 + }
675 + }
676 + }
677 + }
678 +
679 + return $sorted_counts;
680 + }
215 681
216 682
217 683 /**
218 684 * Helper function to create Adding Route