diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index f8abd38..6df83e7 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -13,7 +13,10 @@ class Activity_Dispatcher { * Initialize the class, registering WordPress hooks. */ public static function init() { - \add_action( 'activitypub_send_post_activity', array( '\Activitypub\Activity_Dispatcher', 'send_post_activity' ) ); + // legacy + \add_action( 'activitypub_send_post_activity', array( '\Activitypub\Activity_Dispatcher', 'send_create_activity' ) ); + + \add_action( 'activitypub_send_create_activity', array( '\Activitypub\Activity_Dispatcher', 'send_create_activity' ) ); \add_action( 'activitypub_send_update_activity', array( '\Activitypub\Activity_Dispatcher', 'send_update_activity' ) ); \add_action( 'activitypub_send_delete_activity', array( '\Activitypub\Activity_Dispatcher', 'send_delete_activity' ) ); } @@ -23,38 +26,8 @@ class Activity_Dispatcher { * * @param \Activitypub\Model\Post $activitypub_post */ - public static function send_post_activity( Model\Post $activitypub_post ) { - // get latest version of post - $user_id = $activitypub_post->get_post_author(); - - $activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_FULL ); - $activitypub_activity->from_post( $activitypub_post ); - - $inboxes = \Activitypub\get_follower_inboxes( $user_id ); - - $followers_url = \get_rest_url( null, '/activitypub/1.0/users/' . intval( $user_id ) . '/followers' ); - foreach ( $activitypub_activity->get_cc() as $cc ) { - if ( $cc === $followers_url ) { - continue; - } - $inbox = \Activitypub\get_inbox_by_actor( $cc ); - if ( ! $inbox || \is_wp_error( $inbox ) ) { - continue; - } - // init array if empty - if ( ! isset( $inboxes[ $inbox ] ) ) { - $inboxes[ $inbox ] = array(); - } - $inboxes[ $inbox ][] = $cc; - } - - foreach ( $inboxes as $inbox => $to ) { - $to = array_values( array_unique( $to ) ); - $activitypub_activity->set_to( $to ); - $activity = $activitypub_activity->to_json(); - - \Activitypub\safe_remote_post( $inbox, $activity, $user_id ); - } + public static function send_create_activity( Model\Post $activitypub_post ) { + self::send_activity( $activitypub_post, 'Create' ); } /** @@ -62,19 +35,8 @@ class Activity_Dispatcher { * * @param \Activitypub\Model\Post $activitypub_post */ - public static function send_update_activity( $activitypub_post ) { - // get latest version of post - $user_id = $activitypub_post->get_post_author(); - - $activitypub_activity = new \Activitypub\Model\Activity( 'Update', \Activitypub\Model\Activity::TYPE_FULL ); - $activitypub_activity->from_post( $activitypub_post ); - - foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) { - $activitypub_activity->set_to( $to ); - $activity = $activitypub_activity->to_json(); // phpcs:ignore - - \Activitypub\safe_remote_post( $inbox, $activity, $user_id ); - } + public static function send_update_activity( Model\Post $activitypub_post ) { + self::send_activity( $activitypub_post, 'Update' ); } /** @@ -82,16 +44,30 @@ class Activity_Dispatcher { * * @param \Activitypub\Model\Post $activitypub_post */ - public static function send_delete_activity( $activitypub_post ) { + public static function send_delete_activity( Model\Post $activitypub_post ) { + self::send_activity( $activitypub_post, 'Delete' ); + } + + /** + * Undocumented function + * + * @param [type] $activitypub_post + * @param [type] $activity_type + * @return void + */ + public static function send_activity( Model\Post $activitypub_post, $activity_type ) { // get latest version of post $user_id = $activitypub_post->get_post_author(); - $activitypub_activity = new \Activitypub\Model\Activity( 'Delete', \Activitypub\Model\Activity::TYPE_FULL ); + $activitypub_activity = new \Activitypub\Model\Activity( $activity_type ); $activitypub_activity->from_post( $activitypub_post ); - foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) { - $activitypub_activity->set_to( $to ); - $activity = $activitypub_activity->to_json(); // phpcs:ignore + $inboxes = \Activitypub\get_follower_inboxes( $user_id, $activitypub_activity->get_cc() ); + + foreach ( $inboxes as $inbox => $cc ) { + $cc = array_values( array_unique( $cc ) ); + $activitypub_activity->add_cc( $cc ); + $activity = $activitypub_activity->to_json(); \Activitypub\safe_remote_post( $inbox, $activity, $user_id ); } diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index c04aedc..4a60915 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -117,7 +117,7 @@ class Activitypub { $activitypub_post = new \Activitypub\Model\Post( $post ); if ( 'publish' === $new_status && 'publish' !== $old_status ) { - \wp_schedule_single_event( \time(), 'activitypub_send_post_activity', array( $activitypub_post ) ); + \wp_schedule_single_event( \time(), 'activitypub_send_create_activity', array( $activitypub_post ) ); } elseif ( 'publish' === $new_status ) { \wp_schedule_single_event( \time(), 'activitypub_send_update_activity', array( $activitypub_post ) ); } elseif ( 'trash' === $new_status ) { diff --git a/includes/functions.php b/includes/functions.php index 8f2e5d9..77508c5 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -219,8 +219,11 @@ function get_publickey_by_actor( $actor, $key_id ) { return new \WP_Error( 'activitypub_no_public_key', \__( 'No "Public-Key" found', 'activitypub' ), $metadata ); } -function get_follower_inboxes( $user_id ) { +function get_follower_inboxes( $user_id, $cc = array() ) { $followers = \Activitypub\Peer\Followers::get_followers( $user_id ); + $followers = array_merge( $followers, $cc ); + $followers = array_unique( $followers ); + $inboxes = array(); foreach ( $followers as $follower ) { diff --git a/includes/model/class-activity.php b/includes/model/class-activity.php index f865d6d..34f6146 100644 --- a/includes/model/class-activity.php +++ b/includes/model/class-activity.php @@ -9,24 +9,38 @@ namespace Activitypub\Model; * @see https://www.w3.org/TR/activitypub/ */ class Activity { - private $context = array( 'https://www.w3.org/ns/activitystreams' ); + private $context = array( + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', + array( + 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', + 'PropertyValue' => 'schema:PropertyValue', + 'schema' => 'http://schema.org#', + 'pt' => 'https://joinpeertube.org/ns#', + 'toot' => 'http://joinmastodon.org/ns#', + 'value' => 'schema:value', + 'Hashtag' => 'as:Hashtag', + 'featured' => array( + '@id' => 'toot:featured', + '@type' => '@id', + ), + 'featuredTags' => array( + '@id' => 'toot:featuredTags', + '@type' => '@id', + ), + ), + ); private $published = ''; private $id = ''; private $type = 'Create'; private $actor = ''; private $to = array( 'https://www.w3.org/ns/activitystreams#Public' ); - private $cc = array( 'https://www.w3.org/ns/activitystreams#Public' ); + private $cc = array(); private $object = null; - const TYPE_SIMPLE = 'simple'; - const TYPE_FULL = 'full'; - const TYPE_NONE = 'none'; - - public function __construct( $type = 'Create', $context = self::TYPE_SIMPLE ) { - if ( 'none' === $context ) { + public function __construct( $type = 'Create', $context = true ) { + if ( true !== $context ) { $this->context = null; - } elseif ( 'full' === $context ) { - $this->context = \Activitypub\get_context(); } $this->type = \ucfirst( $type ); @@ -43,6 +57,20 @@ class Activity { if ( \strncasecmp( $method, 'set', 3 ) === 0 ) { $this->$var = $params[0]; } + + if ( \strncasecmp( $method, 'add', 3 ) === 0 ) { + if ( ! is_array( $this->$var ) ) { + $this->$var = $params[0]; + } + + if ( is_array( $params[0] ) ) { + $this->$var = array_merge( $this->$var, $params[0] ); + } else { + array_push( $this->$var, $params[0] ); + } + + $this->$var = array_unique( $this->$var ); + } } public function from_post( Post $post ) { @@ -51,7 +79,8 @@ class Activity { if ( isset( $object['published'] ) ) { $this->published = $object['published']; } - $this->cc = array( \get_rest_url( null, '/activitypub/1.0/users/' . intval( $post->get_post_author() ) . '/followers' ) ); + + $this->add_to( \get_rest_url( null, '/activitypub/1.0/users/' . intval( $post->get_post_author() ) . '/followers' ) ); if ( isset( $this->object['attributedTo'] ) ) { $this->actor = $this->object['attributedTo']; @@ -59,7 +88,7 @@ class Activity { foreach ( $post->get_tags() as $tag ) { if ( 'Mention' === $tag['type'] ) { - $this->cc[] = $tag['href']; + $this->add_cc( $tag['href'] ); } } diff --git a/includes/model/class-post.php b/includes/model/class-post.php index d83384e..cfe49b6 100644 --- a/includes/model/class-post.php +++ b/includes/model/class-post.php @@ -104,6 +104,24 @@ class Post { 'cite' => array(), ); + /** + * List of audience + * + * Also used for visibility + * + * @var array + */ + private $to = array( 'https://www.w3.org/ns/activitystreams#Public' ); + + /** + * List of audience + * + * Also used for visibility + * + * @var array + */ + private $cc = array(); + /** * Constructor * @@ -111,6 +129,7 @@ class Post { */ public function __construct( $post ) { $this->post = \get_post( $post ); + $this->add_to( \get_rest_url( null, '/activitypub/1.0/users/' . intval( $this->get_post_author() ) . '/followers' ) ); } /** @@ -134,6 +153,20 @@ class Post { if ( \strncasecmp( $method, 'set', 3 ) === 0 ) { $this->$var = $params[0]; } + + if ( \strncasecmp( $method, 'add', 3 ) === 0 ) { + if ( ! is_array( $this->$var ) ) { + $this->$var = $params[0]; + } + + if ( is_array( $params[0] ) ) { + $this->$var = array_merge( $this->$var, $params[0] ); + } else { + array_push( $this->$var, $params[0] ); + } + + $this->$var = array_unique( $this->$var ); + } } /** @@ -155,8 +188,8 @@ class Post { 'contentMap' => array( \strstr( \get_locale(), '_', true ) => $this->get_content(), ), - 'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ), - 'cc' => array( 'https://www.w3.org/ns/activitystreams#Public' ), + 'to' => $this->get_to(), + 'cc' => $this->get_cc(), 'attachment' => $this->get_attachments(), 'tag' => $this->get_tags(), ); diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 5d6b6ce..86aefd4 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -356,7 +356,7 @@ class Inbox { $inbox = \Activitypub\get_inbox_by_actor( $object['actor'] ); // send "Accept" activity - $activity = new \Activitypub\Model\Activity( 'Accept', \Activitypub\Model\Activity::TYPE_SIMPLE ); + $activity = new \Activitypub\Model\Activity( 'Accept' ); $activity->set_object( $object ); $activity->set_actor( \get_author_posts_url( $user_id ) ); $activity->set_to( $object['actor'] ); diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index 615f06b..8749d11 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -102,7 +102,8 @@ class Outbox { foreach ( $posts as $post ) { $activitypub_post = new \Activitypub\Model\Post( $post ); - $activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_NONE ); + $activitypub_activity = new \Activitypub\Model\Activity( 'Create', false ); + $activitypub_activity->from_post( $activitypub_post ); $json->orderedItems[] = $activitypub_activity->to_array(); // phpcs:ignore } diff --git a/tests/test-class-activitypub-activity-dispatcher.php b/tests/test-class-activitypub-activity-dispatcher.php index dea7e38..0e503e5 100644 --- a/tests/test-class-activitypub-activity-dispatcher.php +++ b/tests/test-class-activitypub-activity-dispatcher.php @@ -28,7 +28,7 @@ class Test_Activitypub_Activity_Dispatcher extends ActivityPub_TestCase_Cache_HT add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 ); $activitypub_post = new \Activitypub\Model\Post( $post ); - \Activitypub\Activity_Dispatcher::send_post_activity( $activitypub_post ); + \Activitypub\Activity_Dispatcher::send_create_activity( $activitypub_post ); $this->assertNotEmpty( $activitypub_post->get_content() ); @@ -69,7 +69,7 @@ class Test_Activitypub_Activity_Dispatcher extends ActivityPub_TestCase_Cache_HT add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 ); $activitypub_post = new \Activitypub\Model\Post( $post ); - \Activitypub\Activity_Dispatcher::send_post_activity( $activitypub_post ); + \Activitypub\Activity_Dispatcher::send_create_activity( $activitypub_post ); $this->assertNotEmpty( $activitypub_post->get_content() ); diff --git a/tests/test-class-activitypub-activity.php b/tests/test-class-activitypub-activity.php index 254d860..7fe6551 100644 --- a/tests/test-class-activitypub-activity.php +++ b/tests/test-class-activitypub-activity.php @@ -19,10 +19,10 @@ class Test_Activitypub_Activity extends WP_UnitTestCase { $activitypub_post = new \Activitypub\Model\Post( $post ); - $activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_FULL ); + $activitypub_activity = new \Activitypub\Model\Activity( 'Create' ); $activitypub_activity->from_post( $activitypub_post ); - $this->assertContains( \get_rest_url( null, '/activitypub/1.0/users/1/followers' ), $activitypub_activity->get_cc() ); + $this->assertContains( \get_rest_url( null, '/activitypub/1.0/users/1/followers' ), $activitypub_activity->get_to() ); $this->assertContains( 'https://example.com/alex', $activitypub_activity->get_cc() ); remove_all_filters( 'activitypub_extract_mentions' );