Shared Inbox (#617)

* init shared inbox

* try to get user id from activity

* some code formatting

* disable ACTIVITYPUB_SHARED_INBOX_FEATURE

* done!

* do not use the inbox-user at all

* fix user check!

* fix user check!
This commit is contained in:
Matthias Pfefferle 2023-12-22 17:45:27 +01:00 committed by GitHub
parent a9c65f55d6
commit a8078ce72b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 101 additions and 53 deletions

View file

@ -33,6 +33,7 @@ require_once __DIR__ . '/includes/functions.php';
\defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "<strong>[ap_title]</strong>\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" );
\defined( 'ACTIVITYPUB_AUTHORIZED_FETCH' ) || \define( 'ACTIVITYPUB_AUTHORIZED_FETCH', false );
\defined( 'ACTIVITYPUB_DISABLE_REWRITES' ) || \define( 'ACTIVITYPUB_DISABLE_REWRITES', false );
\defined( 'ACTIVITYPUB_SHARED_INBOX_FEATURE' ) || \define( 'ACTIVITYPUB_SHARED_INBOX_FEATURE', false );
\define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
\define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );

View file

@ -15,7 +15,12 @@ class Create {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_create', array( self::class, 'handle_create' ), 10, 3 );
\add_action(
'activitypub_inbox_create',
array( self::class, 'handle_create' ),
10,
3
);
}
/**

View file

@ -15,11 +15,24 @@ class Delete {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_delete', array( self::class, 'handle_delete' ), 10, 2 );
\add_action(
'activitypub_inbox_delete',
array( self::class, 'handle_delete' )
);
// defer signature verification for `Delete` requests.
\add_filter( 'activitypub_defer_signature_verification', array( self::class, 'defer_signature_verification' ), 10, 2 );
\add_filter(
'activitypub_defer_signature_verification',
array( self::class, 'defer_signature_verification' ),
10,
2
);
// side effect
\add_action( 'activitypub_delete_actor_interactions', array( self::class, 'delete_interactions' ), 10, 1 );
\add_action(
'activitypub_delete_actor_interactions',
array( self::class, 'delete_interactions' )
);
}
/**
@ -28,7 +41,7 @@ class Delete {
* @param array $activity The delete activity.
* @param int $user_id The ID of the user performing the delete activity.
*/
public static function handle_delete( $activity, $user_id ) {
public static function handle_delete( $activity ) {
$object_type = isset( $activity['object']['type'] ) ? $activity['object']['type'] : '';
switch ( $object_type ) {
@ -39,7 +52,7 @@ class Delete {
case 'Organization':
case 'Service':
case 'Application':
self::maybe_delete_follower( $user_id, $activity );
self::maybe_delete_follower( $activity );
break;
// Object and Link Types
// @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types

View file

@ -14,8 +14,17 @@ class Follow {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_follow', array( self::class, 'handle_follow' ), 10, 2 );
\add_action( 'activitypub_followers_post_follow', array( self::class, 'send_follow_response' ), 10, 4 );
\add_action(
'activitypub_inbox_follow',
array( self::class, 'handle_follow' )
);
\add_action(
'activitypub_followers_post_follow',
array( self::class, 'send_follow_response' ),
10,
4
);
}
/**
@ -24,11 +33,30 @@ class Follow {
* @param array $activity The activity object
* @param int $user_id The user ID
*/
public static function handle_follow( $activity, $user_id ) {
// save follower
$follower = Followers::add_follower( $user_id, $activity['actor'] );
public static function handle_follow( $activity ) {
$user = Users::get_by_resource( $activity['object'] );
do_action( 'activitypub_followers_post_follow', $activity['actor'], $activity, $user_id, $follower );
if ( ! $user || is_wp_error( $user ) ) {
// If we can not find a user,
// we can not initiate a follow process
return;
}
$user_id = $user->get__id();
// save follower
$follower = Followers::add_follower(
$user_id,
$activity['actor']
);
do_action(
'activitypub_followers_post_follow',
$activity['actor'],
$activity,
$user_id,
$follower
);
}
/**

View file

@ -1,6 +1,7 @@
<?php
namespace Activitypub\Handler;
use Activitypub\Collection\Users;
use Activitypub\Collection\Followers;
/**
@ -11,7 +12,10 @@ class Undo {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_undo', array( self::class, 'handle_undo' ), 10, 2 );
\add_action(
'activitypub_inbox_undo',
array( self::class, 'handle_undo' )
);
}
/**
@ -20,11 +24,23 @@ class Undo {
* @param array $activity The JSON "Undo" Activity
* @param int $user_id The ID of the ID of the WordPress User
*/
public static function handle_undo( $activity, $user_id ) {
public static function handle_undo( $activity ) {
if (
isset( $activity['object']['type'] ) &&
'Follow' === $activity['object']['type']
'Follow' === $activity['object']['type'] &&
isset( $activity['object']['object'] ) &&
filter_var( $activity['object']['object'], FILTER_VALIDATE_URL )
) {
$user = Users::get_by_resource( $activity['object']['object'] );
if ( ! $user || is_wp_error( $user ) ) {
// If we can not find a user,
// we can not initiate a follow process
return;
}
$user_id = $user->get__id();
Followers::remove_follower( $user_id, $activity['actor'] );
}
}

View file

@ -14,7 +14,10 @@ class Update {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_update', array( self::class, 'handle_update' ), 10, 2 );
\add_action(
'activitypub_inbox_update',
array( self::class, 'handle_update' )
);
}
/**
@ -23,7 +26,7 @@ class Update {
* @param array $array The activity-object
* @param int $user_id The id of the local blog-user
*/
public static function handle_update( $array, $user_id ) {
public static function handle_update( $array ) {
$object_type = isset( $array['object']['type'] ) ? $array['object']['type'] : '';
switch ( $object_type ) {
@ -45,7 +48,7 @@ class Update {
case 'Video':
case 'Event':
case 'Document':
self::update_interaction( $array, $user_id );
self::update_interaction( $array );
break;
// Minimal Activity
// @see https://www.w3.org/TR/activitystreams-core/#example-1
@ -62,7 +65,7 @@ class Update {
*
* @return void
*/
public static function update_interaction( $activity, $user_id ) {
public static function update_interaction( $activity ) {
$state = Interactions::update_comment( $activity );
$reaction = null;
@ -70,7 +73,7 @@ class Update {
$reaction = \get_comment( $state );
}
\do_action( 'activitypub_handled_update', $activity, $user_id, $state, $reaction );
\do_action( 'activitypub_handled_update', $activity, null, $state, $reaction );
}
/**

View file

@ -226,6 +226,18 @@ class User extends Actor {
return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) );
}
public function get_endpoints() {
$endpoints = null;
if ( ACTIVITYPUB_SHARED_INBOX_FEATURE ) {
$endpoints = array(
'sharedInbox' => get_rest_url_by_path( 'inbox' ),
);
}
return $endpoints;
}
/**
* Extend the User-Output with Attachments.
*

View file

@ -94,11 +94,8 @@ class Inbox {
$json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' );
$json->type = 'OrderedCollectionPage';
$json->partOf = get_rest_url_by_path( sprintf( 'users/%d/inbox', $user->get__id() ) ); // phpcs:ignore
$json->totalItems = 0; // phpcs:ignore
$json->orderedItems = array(); // phpcs:ignore
$json->first = $json->partOf; // phpcs:ignore
// filter output
@ -155,37 +152,10 @@ class Inbox {
$data = $request->get_json_params();
$activity = Activity::init_from_array( $data );
$type = $request->get_param( 'type' );
$users = self::get_recipients( $data );
$type = \strtolower( $type );
if ( ! $users ) {
return new WP_Error(
'rest_invalid_param',
\__( 'No recipients found', 'activitypub' ),
array(
'status' => 400,
'params' => array(
'to' => \__( 'Please check/validate "to" field', 'activitypub' ),
'bto' => \__( 'Please check/validate "bto" field', 'activitypub' ),
'cc' => \__( 'Please check/validate "cc" field', 'activitypub' ),
'bcc' => \__( 'Please check/validate "bcc" field', 'activitypub' ),
'audience' => \__( 'Please check/validate "audience" field', 'activitypub' ),
),
)
);
}
foreach ( $users as $user ) {
$user = User_Collection::get_by_various( $user );
if ( is_wp_error( $user ) ) {
continue;
}
$type = \strtolower( $type );
\do_action( 'activitypub_inbox', $data, $user->ID, $type, $activity );
\do_action( "activitypub_inbox_{$type}", $data, $user->ID, $activity );
}
\do_action( 'activitypub_inbox', $data, null, $type, $activity );
\do_action( "activitypub_inbox_{$type}", $data, null, $activity );
$rest_response = new WP_REST_Response( array(), 202 );
$rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );