updated plugin ActivityPub version 9.1.0

This commit is contained in:
2026-07-28 15:03:10 +00:00
committed by Gitium
parent bf428f0e45
commit ff806e1811
217 changed files with 6098 additions and 3025 deletions

View File

@ -49,6 +49,10 @@ class Actor {
\add_action( 'add_option_activitypub_actor_mode', array( self::class, 'blog_user_update' ) );
\add_action( 'update_option_activitypub_actor_mode', array( self::class, 'blog_user_update' ) );
// The Starter Kit policy is part of every actor profile.
\add_action( 'add_option_activitypub_default_feature_policy', array( self::class, 'schedule_all_profile_updates' ) );
\add_action( 'update_option_activitypub_default_feature_policy', array( self::class, 'schedule_all_profile_updates' ) );
\add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 );
\add_action( 'post_stuck', array( self::class, 'sticky_post_update' ) );
@ -84,7 +88,7 @@ class Actor {
'user_url',
);
if ( in_array( $meta_key, $fields, true ) ) {
if ( \in_array( $meta_key, $fields, true ) ) {
self::schedule_profile_update( $user_id );
}
}
@ -132,13 +136,28 @@ class Actor {
}
}
/**
* Send profile updates for all local actors.
*
* Used when a site-wide setting that is part of every actor profile
* changes, like the Starter Kit policy (FEP-7aa9 `canFeature`), so
* followers of the blog actor and of every author receive the change.
*
* @since 9.0.1
*/
public static function schedule_all_profile_updates() {
foreach ( Actors::get_all_ids() as $user_id ) {
self::schedule_profile_update( $user_id );
}
}
/**
* Send a profile update to all followers. Gets hooked into all relevant options/meta etc.
*
* @param int $user_id The user ID to update (Could be 0 for Blog-User).
*/
public static function schedule_profile_update( $user_id ) {
if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
if ( \defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
return;
}
@ -148,7 +167,7 @@ class Actor {
return;
}
$actor->set_updated( gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, time() ) );
$actor->set_updated( \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, \time() ) );
add_to_outbox( $actor, 'Update', $user_id );
}

View File

@ -38,7 +38,7 @@ class Collection_Sync {
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,
\time() + MINUTE_IN_SECONDS,
"activitypub_{$type}_sync_reconcile",
array( $user_id, $actor_url, $params )
);
@ -70,7 +70,7 @@ class Collection_Sync {
$accepted = Following::get_by_authority( $user_id, $home_authority );
foreach ( $accepted as $following ) {
$key = array_search( $following->guid, $remote_followers, true );
$key = \array_search( $following->guid, $remote_followers, true );
if ( false === $key ) {
Following::reject( $following, $user_id );
} else {
@ -78,11 +78,11 @@ class Collection_Sync {
}
}
$remote_followers = array_values( $remote_followers ); // Reindex.
$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 );
$key = \array_search( $following->guid, $remote_followers, true );
if ( false === $key ) {
Following::reject( $following, $user_id );
} else {

View File

@ -40,11 +40,11 @@ class Comment {
* @param \WP_Comment $comment Comment object.
*/
public static function schedule_comment_activity( $new_status, $old_status, $comment ) {
if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
if ( \defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
return;
}
$comment = get_comment( $comment );
$comment = \get_comment( $comment );
// Federate only comments that are written by a registered user.
if ( ! $comment || ! $comment->user_id ) {
@ -65,7 +65,7 @@ class Comment {
$allowed_types[] = 'comment'; // Add core WordPress comment types.
// Check if comment type is in allowed list.
if ( ! in_array( $comment_type, $allowed_types, true ) ) {
if ( ! \in_array( $comment_type, $allowed_types, true ) ) {
return;
}
@ -78,7 +78,7 @@ class Comment {
$type = 'Create';
} elseif ( 'approved' === $new_status ) {
$type = 'Update';
\update_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', time(), true );
\update_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', \time(), true );
} elseif (
'trash' === $new_status ||
( 'delete' === $new_status && '' === $old_status ) || // Went through schedule_comment_delete_activity().

View File

@ -11,10 +11,10 @@ use Activitypub\Activity\Activity;
use Activitypub\Collection\Actors;
use function Activitypub\add_to_outbox;
use function Activitypub\get_content_visibility;
use function Activitypub\get_post_id;
use function Activitypub\get_wp_object_state;
use function Activitypub\is_post_disabled;
use function Activitypub\is_post_publicly_queryable;
/**
* Post scheduler class.
@ -55,7 +55,7 @@ class Post {
* @param \WP_Post $post_before Post object before the update.
*/
public static function triage( $post_id, $post, $update, $post_before ) {
if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
if ( \defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
return;
}
@ -64,12 +64,10 @@ class Post {
}
$object_status = get_wp_object_state( $post );
$is_queryable = is_post_publicly_queryable( $post );
// If the post is already soft-deleted, do not create any more activities.
if (
ACTIVITYPUB_OBJECT_STATE_DELETED === $object_status &&
in_array( get_content_visibility( $post ), array( ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE ), true )
) {
// If the post is already soft-deleted and still non-public, do not create any more activities.
if ( ACTIVITYPUB_OBJECT_STATE_DELETED === $object_status && ! $is_queryable ) {
return;
}
@ -78,8 +76,8 @@ class Post {
return;
}
$new_status = get_post_status( $post );
$old_status = $post_before ? get_post_status( $post_before ) : null;
$new_status = \get_post_status( $post );
$old_status = $post_before ? \get_post_status( $post_before ) : null;
switch ( $new_status ) {
case 'publish':
@ -90,17 +88,36 @@ class Post {
}
break;
case 'future':
/*
* A (re-)scheduled post is not a deletion: it becomes public again
* when it publishes, at which point the publish transition federates
* it. Treating `future` as a soft delete would fan out a Delete that
* remotely tombstones the object id — e.g. when a content edit reverts
* a future-dated published post back to `future` — after which the
* post can never re-federate. Emit nothing instead.
*/
$type = false;
break;
case 'draft':
case 'pending':
$type = ( 'publish' === $old_status ) ? 'Update' : false;
break;
case 'private':
case 'trash':
$type = ACTIVITYPUB_OBJECT_STATE_FEDERATED === $object_status ? 'Delete' : false;
break;
default:
$type = false;
/*
* Soft delete for federated posts (FEP-4f05).
*
* A previously-federated post transitioning to any non-public
* status (built-in or custom) emits a Delete so federated
* copies are torn down. Without this, draft/pending would
* broadcast a placeholder Update, private/trash would silently
* leave the federated copy stale, and a custom status would
* fall through without notifying followers at all.
*/
$type = ACTIVITYPUB_OBJECT_STATE_FEDERATED === $object_status && ! $is_queryable
? 'Delete'
: false;
}
// Do not send Activities if `$type` is not set or unknown.
@ -121,8 +138,20 @@ class Post {
$type = 'Create';
}
// If the post was federated before but is now local or private, it should be a Delete activity.
if ( ACTIVITYPUB_OBJECT_STATE_FEDERATED === $object_status && in_array( get_content_visibility( $post ), array( ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE ), true ) ) {
/*
* Resurrection: a soft-deleted post that is back in a publicly
* queryable state must emit Create, not Update. Remote followers
* either dropped the original Create on the Delete fan-out (so
* they need to learn about the post again) or had it cancelled
* before fanning out (so the supersession logic invalidates the
* pending Delete and Create is the correct re-introduction).
*/
if ( ACTIVITYPUB_OBJECT_STATE_DELETED === $object_status && 'Update' === $type && $is_queryable ) {
$type = 'Create';
}
// If the post was federated before but is now non-public, it should be a Delete activity.
if ( ACTIVITYPUB_OBJECT_STATE_FEDERATED === $object_status && ! $is_queryable ) {
$type = 'Delete';
}

View File

@ -9,6 +9,7 @@
namespace Activitypub\Scheduler;
use Activitypub\Collection\Actors;
use Activitypub\Mailer;
use Activitypub\Statistics as Statistics_Collector;
@ -320,7 +321,7 @@ class Statistics {
}
// Check user preference.
if ( $user_id > \Activitypub\Collection\Actors::BLOG_USER_ID ) {
if ( $user_id > Actors::BLOG_USER_ID ) {
if ( ! \get_user_option( $option_name, $user_id ) ) {
return false;
}