Skip to content

Commit

Permalink
Merge branch 'update/dispatcher' into add/dispatch-logging
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwiebe authored Jan 22, 2025
2 parents 79f6b0e + 3382d7f commit be96f25
Show file tree
Hide file tree
Showing 15 changed files with 432 additions and 88 deletions.
4 changes: 2 additions & 2 deletions includes/class-activitypub.php
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ private static function register_post_types() {
Outbox::POST_TYPE,
'_activitypub_activity_actor',
array(
'type' => 'integer',
'type' => 'string',
'single' => true,
'sanitize_callback' => function ( $value ) {
$schema = array(
Expand Down Expand Up @@ -602,7 +602,7 @@ private static function register_post_types() {
'sanitize_callback' => function ( $value ) {
$schema = array(
'type' => 'string',
'enum' => array( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ),
'enum' => array( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ),
'default' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
);

Expand Down
82 changes: 62 additions & 20 deletions includes/class-dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

namespace Activitypub;

use Activitypub\Activity\Activity;
use Activitypub\Collection\Actors;
use Activitypub\Collection\Followers;
use Activitypub\Transformer\Factory;
use Activitypub\Transformer\Factory as Transformer_Factory;

/**
* ActivityPub Dispatcher Class.
Expand All @@ -23,10 +24,10 @@ class Dispatcher {
* Initialize the class, registering WordPress hooks.
*/
public static function init() {
\add_action( 'activitypub_process_outbox', array( self::class, 'process_outbox' ), 10, 1 );
\add_action( 'activitypub_process_outbox', array( self::class, 'process_outbox' ) );

// Default filters to add Inboxes to sent to.
\add_filter( 'activitypub_send_to_inboxes', array( self::class, 'add_inboxes_of_follower' ), 10, 2 );
\add_filter( 'activitypub_send_to_inboxes', array( self::class, 'add_inboxes_of_follower' ), 10, 3 );
\add_filter( 'activitypub_send_to_inboxes', array( self::class, 'add_inboxes_by_mentioned_actors' ), 10, 3 );
\add_filter( 'activitypub_send_to_inboxes', array( self::class, 'add_inboxes_of_replied_urls' ), 10, 3 );
\add_filter( 'activitypub_send_to_inboxes', array( self::class, 'maybe_add_inboxes_of_blog_user' ), 10, 3 );
Expand Down Expand Up @@ -61,7 +62,7 @@ public static function process_outbox( $id ) {
}

$type = \get_post_meta( $outbox_item->ID, '_activitypub_activity_type', true );
$transformer = Factory::get_transformer( $outbox_item->post_content );
$transformer = Transformer_Factory::get_transformer( $outbox_item->post_content );
$activity = $transformer->to_activity( $type );

self::send_activity_to_followers( $activity, $actor_id, $outbox_item );
Expand Down Expand Up @@ -123,19 +124,23 @@ private static function send_activity_to_followers( $activity, $actor_id, $outbo
\add_post_meta( $outbox_item->ID, 'activitypub_send_log', $to_log );
}

$outbox_item->post_status = 'publish';
\wp_update_post( $outbox_item );
\wp_publish_post( $outbox_item );
}

/**
* Default filter to add Inboxes of Followers.
*
* @param array $inboxes The list of Inboxes.
* @param int $actor_id The WordPress Actor-ID.
* @param array $inboxes The list of Inboxes.
* @param int $actor_id The WordPress Actor-ID.
* @param Activity $activity The ActivityPub Activity.
*
* @return array The filtered Inboxes
*/
public static function add_inboxes_of_follower( $inboxes, $actor_id ) {
public static function add_inboxes_of_follower( $inboxes, $actor_id, $activity ) {
if ( ! self::should_send_to_followers( $activity, $actor_id ) ) {
return $inboxes;
}

$follower_inboxes = Followers::get_inboxes( $actor_id );

return array_merge( $inboxes, $follower_inboxes );
Expand All @@ -144,9 +149,9 @@ public static function add_inboxes_of_follower( $inboxes, $actor_id ) {
/**
* Default filter to add Inboxes of Mentioned Actors
*
* @param array $inboxes The list of Inboxes.
* @param int $actor_id The WordPress Actor-ID.
* @param array $activity The ActivityPub Activity.
* @param array $inboxes The list of Inboxes.
* @param int $actor_id The WordPress Actor-ID.
* @param Activity $activity The ActivityPub Activity.
*
* @return array The filtered Inboxes.
*/
Expand Down Expand Up @@ -224,13 +229,17 @@ public static function add_inboxes_of_replied_urls( $inboxes, $actor_id, $activi
/**
* Adds Blog Actor inboxes to Updates so the Blog User's followers are notified of edits.
*
* @param array $inboxes The list of Inboxes.
* @param int $actor_id The WordPress Actor-ID.
* @param array $activity The ActivityPub Activity.
* @param array $inboxes The list of Inboxes.
* @param int $actor_id The WordPress Actor-ID.
* @param Activity $activity The ActivityPub Activity.
*
* @return array The filtered Inboxes
*/
public static function maybe_add_inboxes_of_blog_user( $inboxes, $actor_id, $activity ) {
if ( ! self::should_send_to_followers( $activity, $actor_id ) ) {
return $inboxes;
}

// Only if we're in both Blog and User modes.
if ( ACTIVITYPUB_ACTOR_AND_BLOG_MODE !== \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) {
return $inboxes;
Expand All @@ -239,15 +248,48 @@ public static function maybe_add_inboxes_of_blog_user( $inboxes, $actor_id, $act
if ( Actors::BLOG_USER_ID === $actor_id ) {
return $inboxes;
}
// Only if this is an Update.
if ( 'Update' !== $activity->get_type() ) {

// Only if this is an Update or Delete. Create handles its own Announce in dual user mode.
if ( ! in_array( $activity->get_type(), array( 'Update', 'Delete' ), true ) ) {
return $inboxes;
}

$blog_inboxes = Followers::get_inboxes( Actors::BLOG_USER_ID );
$inboxes = array_merge( $inboxes, $blog_inboxes );
$inboxes = array_unique( $inboxes );
// array_unique is done in `send_activity_to_followers()`, no need here.
return array_merge( $inboxes, $blog_inboxes );
}

return $inboxes;
/**
* Check if passed Activity is public.
*
* @param Activity $activity The Activity object.
* @param int $actor_id The Actor-ID.
*
* @return boolean True if public, false if not.
*/
protected static function should_send_to_followers( $activity, $actor_id ) {
// Check if follower endpoint is set.
$actor = Actors::get_by_id( $actor_id );

if ( ! $actor || is_wp_error( $actor ) ) {
return false;
}

// Check if follower endpoint is set.
$cc = $activity->get_cc() ?? array();
$to = $activity->get_to() ?? array();

$audience = array_merge( $cc, $to );

if (
// Check if activity is public.
in_array( 'https://www.w3.org/ns/activitystreams#Public', $audience, true ) ||
// ...or check if follower endpoint is set.
in_array( $actor->get_followers(), $audience, true )
) {
return true;
}

return false;
}
}
20 changes: 20 additions & 0 deletions includes/class-scheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public static function init() {
// Follower Cleanups.
\add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) );
\add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) );

\add_action( 'post_activitypub_add_to_outbox', array( self::class, 'schedule_outbox_activity_for_federation' ) );
}

/**
Expand Down Expand Up @@ -138,4 +140,22 @@ public static function cleanup_followers() {
}
}
}

/**
* Schedule the outbox item for federation.
*
* @param int $id The ID of the outbox item.
*/
public static function schedule_outbox_activity_for_federation( $id ) {
$hook = 'activitypub_process_outbox';
$args = array( $id );

if ( false === wp_next_scheduled( $hook, $args ) ) {
\wp_schedule_single_event(
\time() + 10,
$hook,
$args
);
}
}
}
6 changes: 4 additions & 2 deletions includes/collection/class-outbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@

/**
* ActivityPub Outbox Collection
*
* @link https://www.w3.org/TR/activitypub/#outbox
*/
class Outbox {
const POST_TYPE = 'ap_outbox';

/**
* Add an Item to the outbox.
*
* @param \Activitypub\Activity\Base_Object $activity_object The Activity-Object to add as JSON.
* @param \Activitypub\Activity\Base_Object $activity_object The object of the activity that will be added to the outbox.
* @param string $activity_type The activity type.
* @param int $user_id The user ID.
* @param int $user_id The real or imaginary user ID of the actor that published the activity that will be added to the outbox.
* @param string $content_visibility Optional. The visibility of the content. Default 'public'.
*
* @return false|int|\WP_Error The added item or an error.
Expand Down
1 change: 1 addition & 0 deletions includes/constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@
// Post visibility constants.
\define( 'ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC', '' );
\define( 'ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC', 'quiet_public' );
\define( 'ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE', 'private' );
\define( 'ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL', 'local' );
34 changes: 17 additions & 17 deletions includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,7 @@ function get_content_visibility( $post_id ) {
$_visibility = ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
$options = array(
ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC,
ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE,
ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL,
);

Expand Down Expand Up @@ -1554,43 +1555,42 @@ function is_self_ping( $id ) {
* Add an object to the outbox.
*
* @param mixed $data The object to add to the outbox.
* @param string $type The type of the Activity.
* @param string $activity_type The type of the Activity.
* @param integer $user_id The User-ID.
* @param string $content_visibility The visibility of the content.
*
* @return boolean|int The ID of the outbox item or false on failure.
*/
function add_to_outbox( $data, $type = 'Create', $user_id = 0, $content_visibility = ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC ) {
function add_to_outbox( $data, $activity_type = 'Create', $user_id = 0, $content_visibility = ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC ) {
$transformer = Transformer_Factory::get_transformer( $data );

if ( ! $transformer || is_wp_error( $transformer ) ) {
return false;
}

$activity = $transformer->to_object();
$activity_object = $transformer->to_object();

if ( ! $activity || is_wp_error( $activity ) ) {
if ( ! $activity_object || \is_wp_error( $activity_object ) ) {
return false;
}

set_wp_object_state( $data, 'federate' );

$id = Outbox::add( $activity, $type, $user_id, $content_visibility );
$outbox_activity_id = Outbox::add( $activity_object, $activity_type, $user_id, $content_visibility );

if ( ! $id ) {
if ( ! $outbox_activity_id ) {
return false;
}

$hook = 'activitypub_process_outbox';
$args = array( $id );

if ( false === wp_next_scheduled( $hook, $args ) ) {
\wp_schedule_single_event(
\time() + 10,
$hook,
$args
);
}
/**
* Action triggered after an object has been added to the outbox.
*
* @param int $outbox_activity_id The ID of the outbox item.
* @param \Activitypub\Activity\Base_Object $activity_object The activity object.
* @param int $user_id The User-ID.
* @param string $content_visibility The visibility of the content.
*/
\do_action( 'post_activitypub_add_to_outbox', $outbox_activity_id, $activity_object, $user_id, $content_visibility );

return $id;
return $outbox_activity_id;
}
2 changes: 1 addition & 1 deletion includes/rest/class-outbox-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public function get_items( $request ) {

$max_pages = \ceil( $response['totalItems'] / $request->get_param( 'per_page' ) );
$response['first'] = \add_query_arg( 'page', 1, $response['partOf'] );
$response['last'] = \add_query_arg( 'page', $max_pages, $response['partOf'] );
$response['last'] = \add_query_arg( 'page', \max( $max_pages, 1 ), $response['partOf'] );

if ( $max_pages > $page ) {
$response['next'] = \add_query_arg( 'page', $page + 1, $response['partOf'] );
Expand Down
25 changes: 13 additions & 12 deletions includes/scheduler/class-comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,7 @@ public static function init() {

// Comment transitions.
\add_action( 'transition_comment_status', array( self::class, 'schedule_comment_activity' ), 20, 3 );
\add_action(
'edit_comment',
function ( $comment_id ) {
self::schedule_comment_activity( 'approved', 'approved', $comment_id );
}
);
\add_action(
'wp_insert_comment',
function ( $comment_id ) {
self::schedule_comment_activity( 'approved', '', $comment_id );
}
);
\add_action( 'wp_insert_comment', array( self::class, 'schedule_comment_activity_on_insert' ), 10, 2 );
}

/**
Expand Down Expand Up @@ -84,4 +73,16 @@ public static function schedule_comment_activity( $new_status, $old_status, $com

add_to_outbox( $comment, $type, $comment->user_id );
}

/**
* Schedule Comment Activities on insert.
*
* @param int $comment_id Comment ID.
* @param \WP_Comment $comment Comment object.
*/
public static function schedule_comment_activity_on_insert( $comment_id, $comment ) {
if ( 1 === (int) $comment->comment_approved ) {
self::schedule_comment_activity( 'approved', '', $comment );
}
}
}
Loading

0 comments on commit be96f25

Please sign in to comment.