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 +152 -11 1.3.11trunk View file →
@@ -45,8 +45,9 @@
45 45 $this->add_route( '/getSettings', 'getSettings' );
46 46 $this->add_route( '/saveConfig', 'saveConfig', 'POST' );
47 47 $this->add_route( '/refreshCounts', 'refreshCounts', 'POST' );
48 48 $this->add_route( '/getErrorLogs', 'getErrorLogs', 'POST' );
49 + $this->add_route( '/clearErrorLogs', 'clearErrorLogs', 'POST' );
49 50
50 51 // Upgrade Database Routes
51 52 $this->add_route( '/upgrade/status', 'getUpgradeStatus' );
52 53 $this->add_route( '/upgrade/start', 'startUpgrade', 'POST' );
@@ -56,8 +57,10 @@
56 57 $this->add_route( '/sync/start', 'startSync', 'POST' );
57 58 $this->add_route( '/sync/pause', 'pauseSync', 'POST' );
58 59 $this->add_route( '/sync/resume', 'resumeSync', 'POST' );
59 60 $this->add_route( '/sync/stop', 'stopSync', 'POST' );
61 + $this->add_route( '/sync/tick', 'tickSync', 'POST' );
62 + $this->add_route( '/sync/setMethod', 'setSyncMethod', 'POST' );
60 63
61 64 $this->add_route( '/sync/retrySingle', 'retrySingle', 'POST' );
62 65 }
63 66
@@ -163,8 +166,12 @@
163 166 'settings' => Utils::get_settings(),
164 167 ],
165 168 'sync' => Sync::instance()->get_status(),
166 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 + ] : [],
167 174 ];
168 175
169 176 return new WP_REST_Response( $data, 200 );
170 177 }
@@ -201,8 +208,9 @@
201 208 $updated = Utils::update_option('credentials', $existing, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
202 209 $updatedSettings = Utils::update_option('settings', $settings, Schema::getConstant('GLOBAL_SETTINGS_KEY'));
203 210
204 211 if(!$updated) $serviceOk = false;
212 + if(!$updatedSettings) $settingsOk = false;
205 213 }
206 214
207 215
208 216 if($action === 'all' || $action === 'service'){
@@ -335,8 +343,32 @@
335 343 }
336 344
337 345
338 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 + /**
339 371 * Get Status
340 372 * @since 1.2.13
341 373 */
342 374 public function getStatus( $request ) {
@@ -367,9 +399,9 @@
367 399 * @since 1.2.13
368 400 */
369 401 public function startSync( $data ) {
370 402 $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
371 - if(empty($action)) {
403 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
372 404 $result = [
373 405 'success' => false,
374 406 'message' => esc_html__('Invalid action', 'media-cloud-sync')
375 407 ];
@@ -383,8 +415,14 @@
383 415 return new WP_REST_Response( $result, 200 );
384 416 }
385 417 $status = Sync::instance()->start($action);
386 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 +
387 425 $result = [
388 426 'success' => true,
389 427 'status' => $status,
390 428 'message' => esc_html__('Sync started successfully', 'media-cloud-sync')
@@ -398,9 +436,9 @@
398 436 * @since 1.2.13
399 437 */
400 438 public function pauseSync( $data ) {
401 439 $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
402 - if(empty($action)) {
440 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
403 441 $result = [
404 442 'success' => false,
405 443 'message' => esc_html__('Invalid action', 'media-cloud-sync')
406 444 ];
@@ -417,14 +455,46 @@
417 455 }
418 456
419 457
420 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 + /**
421 491 * Resume Sync
422 492 * @since 1.2.13
423 493 */
424 494 public function resumeSync( $data ) {
425 495 $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
426 - if(empty($action)) {
496 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
427 497 $result = [
428 498 'success' => false,
429 499 'message' => esc_html__('Invalid action', 'media-cloud-sync')
430 500 ];
@@ -446,9 +516,9 @@
446 516 * @since 1.2.13
447 517 */
448 518 public function stopSync( $data ) {
449 519 $action = isset($data->get_params()['action']) ? $data->get_params()['action'] : '';
450 - if(empty($action)) {
520 + if ( ! Sync::instance()->get_class_by_action( $action ) ) {
451 521 $result = [
452 522 'success' => false,
453 523 'message' => esc_html__('Invalid action', 'media-cloud-sync')
454 524 ];
@@ -469,8 +539,67 @@
469 539 }
470 540
471 541
472 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 + /**
473 602 * Retry a Media Sync Error
474 603 */
475 604 public function retrySingle( $data ) {
476 605 $params = $data->get_params();
@@ -486,11 +615,23 @@
486 615 return new WP_REST_Response( $result, 200 );
487 616 }
488 617 // Retry Sync
489 618 $handler = Sync::instance()->get_class_by_action($type);
490 - if($handler) {
491 - $handler->retry_single($id, $source_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 );
492 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);
493 634
494 635 $status = Utils::is_empty(Logger::instance()->get_log($type, $id, $source_type)) ? true : false;
495 636
496 637 $result = [
@@ -515,9 +656,9 @@
515 656 $prefixes = array_keys($source_labels);
516 657 foreach($detailed_counts as $source_type => $counts) {
517 658 $found_prefix = false;
518 659 foreach($prefixes as $prefix) {
519 - $found_prefix = strpos($source_type, $prefix) === 0 ? $prefix : false;;
660 + $found_prefix = strpos($source_type, $prefix) === 0 ? $prefix : false;
520 661 if($found_prefix !== false) break;
521 662 }
522 663
523 664 if($found_prefix !== false) {
@@ -522,13 +663,13 @@
522 663
523 664 if($found_prefix !== false) {
524 665 $sorted_counts[$found_prefix] = [
525 666 'label' => $source_labels[$found_prefix],
526 - 'uploaded' => isset($sorted_counts[$found_prefix]['data']['uploaded'])
527 - ? $sorted_counts[$found_prefix]['data']['uploaded'] + $counts['uploaded']
667 + 'uploaded' => isset( $sorted_counts[ $found_prefix ]['uploaded'] )
668 + ? $sorted_counts[ $found_prefix ]['uploaded'] + $counts['uploaded']
528 669 : $counts['uploaded'],
529 - 'total' => isset($sorted_counts[$found_prefix]['data']['total'])
530 - ? $sorted_counts[$found_prefix]['data']['total'] + $counts['total']
670 + 'total' => isset( $sorted_counts[ $found_prefix ]['total'] )
671 + ? $sorted_counts[ $found_prefix ]['total'] + $counts['total']
531 672 : $counts['total']
532 673 ];
533 674 }
534 675 }