Files
laipower/wp-content/plugins/activitypub/includes/scheduler/class-collection-sync.php

103 lines
3.0 KiB
PHP

<?php
/**
* Collection Sync Scheduler.
*
* Handles async reconciliation when FEP-8fcf Collection-Synchronization
* digest mismatches are detected.
*
* @package Activitypub
*/
namespace Activitypub\Scheduler;
use Activitypub\Collection\Following;
use Activitypub\Http;
use function Activitypub\get_url_authority;
/**
* Collection_Sync class.
*/
class Collection_Sync {
/**
* Initialize the scheduler.
*/
public static function init() {
\add_action( 'activitypub_collection_sync', array( self::class, 'schedule_reconciliation' ), 10, 4 );
\add_action( 'activitypub_followers_sync_reconcile', array( self::class, 'reconcile_followers' ), 10, 3 );
}
/**
* Schedule a reconciliation job.
*
* @param string $type The collection type (e.g., 'followers').
* @param int $user_id The local user ID.
* @param string $actor_url The remote actor URL.
* @param array $params The Collection-Synchronization header parameters.
*/
public static function schedule_reconciliation( $type, $user_id, $actor_url, $params ) {
// Schedule async processing to avoid blocking the inbox.
\wp_schedule_single_event(
time() + MINUTE_IN_SECONDS,
"activitypub_{$type}_sync_reconcile",
array( $user_id, $actor_url, $params )
);
}
/**
* Reconcile followers based on remote partial collection.
*
* @param int $user_id The local user ID.
* @param string $actor_url The remote actor URL.
* @param array $params The Collection-Synchronization header parameters.
*/
public static function reconcile_followers( $user_id, $actor_url, $params ) {
if ( empty( $params['url'] ) ) {
return;
}
// Fetch the authoritative partial followers collection.
$data = Http::get_remote_object( $params['url'], 5 * MINUTE_IN_SECONDS );
if ( \is_wp_error( $data ) || ! isset( $data['orderedItems'] ) || ! \is_array( $data['orderedItems'] ) ) {
return;
}
$remote_followers = $data['orderedItems'];
// Get our authority.
$home_authority = get_url_authority( \home_url() );
$accepted = Following::get_by_authority( $user_id, $home_authority );
foreach ( $accepted as $following ) {
$key = array_search( $following->guid, $remote_followers, true );
if ( false === $key ) {
Following::reject( $following, $user_id );
} else {
unset( $remote_followers[ $key ] );
}
}
$remote_followers = array_values( $remote_followers ); // Reindex.
$pending = Following::get_by_authority( $user_id, $home_authority, Following::PENDING_META_KEY );
foreach ( $pending as $following ) {
$key = array_search( $following->guid, $remote_followers, true );
if ( false === $key ) {
Following::reject( $following, $user_id );
} else {
Following::accept( $following, $user_id );
unset( $remote_followers[ $key ] );
}
}
/**
* Action triggered after reconciliation is complete.
*
* @param int $user_id The local user ID that triggered the reconciliation.
* @param string $actor_url The remote actor URL.
*/
\do_action( 'activitypub_followers_sync_reconciled', $user_id, $actor_url );
}
}