From a5b3af1b3bfa03c4f1545e388258b8053ba95fa6 Mon Sep 17 00:00:00 2001 From: Alex Kirk Date: Fri, 16 Dec 2022 12:27:03 +0100 Subject: [PATCH] Move the friends parser to the Friends plugin --- activitypub.php | 54 -- .../class-friends-feed-parser-activitypub.php | 555 ------------------ tests/bootstrap.php | 6 - .../class-activitypub-testcase-cache-http.php | 17 +- ...-class-friends-feed-parser-activitypub.php | 313 ---------- tests/test-functions.php | 32 - 6 files changed, 1 insertion(+), 976 deletions(-) delete mode 100644 integration/class-friends-feed-parser-activitypub.php delete mode 100644 tests/test-class-friends-feed-parser-activitypub.php diff --git a/activitypub.php b/activitypub.php index b1f8ed4..0a3a950 100644 --- a/activitypub.php +++ b/activitypub.php @@ -147,57 +147,3 @@ function enable_buddypress_features() { \Activitypub\Integration\Buddypress::init(); } add_action( 'bp_include', '\Activitypub\enable_buddypress_features' ); - -add_action( - 'friends_load_parsers', - function( \Friends\Feed $friends_feed ) { - require_once __DIR__ . '/integration/class-friends-feed-parser-activitypub.php'; - $friends_feed->register_parser( Friends_Feed_Parser_ActivityPub::SLUG, new Friends_Feed_Parser_ActivityPub( $friends_feed ) ); - } -); - -/** - * Disable webfinger for known example domains. -*/ -add_filter( - 'pre_get_remote_metadata_by_actor', - function( $metadata, $actor ) { - if ( ! $metadata ) { - $username = null; - $domain = null; - if ( preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $actor, $m ) ) { - $username = $m[1]; - $domain = $m[2]; - } else { - $p = wp_parse_url( $actor ); - if ( $p ) { - if ( isset( $p['host'] ) ) { - $domain = $p['host']; - } - if ( isset( $p['path'] ) ) { - $path_parts = explode( '/', trim( $p['path'], '/' ) ); - $username = ltrim( array_pop( $path_parts ), '@' ); - } - } - } - if ( preg_match( '/[^a-zA-Z0-9.-]/', $domain ) ) {// invalid characters - return $metadata; - } - - if ( - rtrim( strtok( $domain, '.' ), '0123456789' ) === 'example' // classic example.org domain - || preg_match( '/(my|your|our)-(domain)/', $domain ) - || preg_match( '/(test)/', $domain ) - || in_array( $username, array( 'example' ), true ) - ) { - $metadata = array( - 'url' => sprintf( 'https://%s/users/%s/', $domain, $username ), - 'name' => $username, - ); - } - } - return $metadata; - }, - 9, - 2 -); diff --git a/integration/class-friends-feed-parser-activitypub.php b/integration/class-friends-feed-parser-activitypub.php deleted file mode 100644 index 594ae85..0000000 --- a/integration/class-friends-feed-parser-activitypub.php +++ /dev/null @@ -1,555 +0,0 @@ -friends_feed = $friends_feed; - - \add_action( 'activitypub_inbox', array( $this, 'handle_received_activity' ), 10, 3 ); - \add_action( 'friends_user_feed_activated', array( $this, 'queue_follow_user' ), 10 ); - \add_action( 'friends_user_feed_deactivated', array( $this, 'queue_unfollow_user' ), 10 ); - \add_action( 'friends_feed_parser_activitypub_follow', array( $this, 'follow_user' ), 10, 2 ); - \add_action( 'friends_feed_parser_activitypub_unfollow', array( $this, 'unfollow_user' ), 10, 2 ); - \add_filter( 'friends_rewrite_incoming_url', array( $this, 'friends_rewrite_incoming_url' ), 10, 2 ); - - \add_filter( 'friends_edit_friend_table_end', array( $this, 'activitypub_settings' ), 10 ); - \add_filter( 'friends_edit_friend_after_form_submit', array( $this, 'activitypub_save_settings' ), 10 ); - \add_filter( 'friends_modify_feed_item', array( $this, 'modify_incoming_item' ), 9, 3 ); - - \add_filter( 'the_content', array( $this, 'the_content' ), 99, 2 ); - \add_filter( 'activitypub_extract_mentions', array( $this, 'activitypub_extract_mentions' ), 10, 2 ); - } - - /** - * Allow logging a message via an action. - * @param string $message The message to log. - * @param array $objects Optional objects as meta data. - * @return void - */ - private function log( $message, $objects = array() ) { - do_action( 'friends_activitypub_log', $message, $objects ); - } - - /** - * Determines if this is a supported feed and to what degree we feel it's supported. - * - * @param string $url The url. - * @param string $mime_type The mime type. - * @param string $title The title. - * @param string|null $content The content, it can't be assumed that it's always available. - * - * @return int Return 0 if unsupported, a positive value representing the confidence for the feed, use 10 if you're reasonably confident. - */ - public function feed_support_confidence( $url, $mime_type, $title, $content = null ) { - if ( preg_match( '/^@?[^@]+@((?:[a-z0-9-]+\.)+[a-z]+)$/i', $url ) ) { - return 10; - } - - return 0; - } - - /** - * Format the feed title and autoselect the posts feed. - * - * @param array $feed_details The feed details. - * - * @return array The (potentially) modified feed details. - */ - public function update_feed_details( $feed_details ) { - $meta = \Activitypub\get_remote_metadata_by_actor( $feed_details['url'] ); - if ( ! $meta || is_wp_error( $meta ) ) { - return $meta; - } - - if ( isset( $meta['name'] ) ) { - $feed_details['title'] = $meta['name']; - } elseif ( isset( $meta['preferredUsername'] ) ) { - $feed_details['title'] = $meta['preferredUsername']; - } - - if ( isset( $meta['id'] ) ) { - $feed_details['url'] = $meta['id']; - } - - return $feed_details; - } - - /** - * Rewrite a Mastodon style URL @username@server to a URL via webfinger. - * - * @param string $url The URL to filter. - * @param string $incoming_url Potentially a mastodon identifier. - * - * @return ( description_of_the_return_value ) - */ - public function friends_rewrite_incoming_url( $url, $incoming_url ) { - if ( preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $incoming_url ) ) { - $resolved_url = \Activitypub\Webfinger::resolve( $incoming_url ); - if ( ! is_wp_error( $resolved_url ) ) { - return $resolved_url; - } - } - return $url; - } - - /** - * Discover the feeds available at the URL specified. - * - * @param string $content The content for the URL is already provided here. - * @param string $url The url to search. - * - * @return array A list of supported feeds at the URL. - */ - public function discover_available_feeds( $content, $url ) { - $discovered_feeds = array(); - - $meta = \Activitypub\get_remote_metadata_by_actor( $url ); - if ( $meta && ! is_wp_error( $meta ) ) { - $discovered_feeds[ $meta['id'] ] = array( - 'type' => 'application/activity+json', - 'rel' => 'self', - 'post-format' => 'status', - 'parser' => self::SLUG, - 'autoselect' => true, - ); - } - - return $discovered_feeds; - } - - /** - * Fetches a feed and returns the processed items. - * - * @param string $url The url. - * - * @return array An array of feed items. - */ - public function fetch_feed( $url ) { - // There is no feed to fetch, we'll receive items via ActivityPub. - return array(); - } - - /** - * Handles "Create" requests - * - * @param array $object The activity-object - * @param int $user_id The id of the local blog-user - * @param string $type The type of the activity. - */ - public function handle_received_activity( $object, $user_id, $type ) { - if ( ! in_array( - $type, - array( - // We don't need to handle 'Accept' types since it's handled by the ActivityPub plugin itself. - 'create', - 'announce', - ), - true - ) ) { - return false; - } - $actor_url = $object['actor']; - $user_feed = false; - if ( \wp_http_validate_url( $actor_url ) ) { - // Let's check if we follow this actor. If not it might be a different URL representation. - $user_feed = $this->friends_feed->get_user_feed_by_url( $actor_url ); - } - - if ( is_wp_error( $user_feed ) || ! \wp_http_validate_url( $actor_url ) ) { - $meta = \Activitypub\get_remote_metadata_by_actor( $actor_url ); - if ( ! $meta || ! isset( $meta['url'] ) ) { - $this->log( 'Received invalid meta for ' . $actor_url ); - return false; - } - - $actor_url = $meta['url']; - if ( ! \wp_http_validate_url( $actor_url ) ) { - $this->log( 'Received invalid meta url for ' . $actor_url ); - return false; - } - } - - $user_feed = $this->friends_feed->get_user_feed_by_url( $actor_url ); - if ( ! $user_feed || is_wp_error( $user_feed ) ) { - $this->log( 'We\'re not following ' . $actor_url ); - // We're not following this user. - return false; - } - - switch ( $type ) { - case 'create': - return $this->handle_incoming_post( $object['object'], $user_feed ); - case 'announce': - return $this->handle_incoming_announce( $object['object'], $user_feed, $user_id ); - - } - - return true; - } - - /** - * Map the Activity type to a post fomat. - * - * @param string $type The type. - * - * @return string The determined post format. - */ - private function map_type_to_post_format( $type ) { - return 'status'; - } - - /** - * We received a post for a feed, handle it. - * - * @param array $object The object from ActivityPub. - * @param \Friends\User_Feed $user_feed The user feed. - */ - private function handle_incoming_post( $object, \Friends\User_Feed $user_feed ) { - $permalink = $object['id']; - if ( isset( $object['url'] ) ) { - $permalink = $object['url']; - } - - $data = array( - 'permalink' => $permalink, - 'content' => $object['content'], - 'post_format' => $this->map_type_to_post_format( $object['type'] ), - 'date' => $object['published'], - ); - - if ( isset( $object['attributedTo'] ) ) { - $meta = \Activitypub\get_remote_metadata_by_actor( $object['attributedTo'] ); - $this->log( 'Attributed to ' . $object['attributedTo'], compact( 'meta' ) ); - if ( isset( $meta['name'] ) ) { - $data['author'] = $meta['name']; - } elseif ( isset( $meta['preferredUsername'] ) ) { - $data['author'] = $meta['preferredUsername']; - } - } - - if ( ! empty( $object['attachment'] ) ) { - foreach ( $object['attachment'] as $attachment ) { - if ( ! isset( $attachment['type'] ) || ! isset( $attachment['mediaType'] ) ) { - continue; - } - if ( 'Document' !== $attachment['type'] || strpos( $attachment['mediaType'], 'image/' ) !== 0 ) { - continue; - } - - $data['content'] .= PHP_EOL; - $data['content'] .= ''; - $data['content'] .= '

'; - $data['content'] .= ''; - } - $meta = \Activitypub\get_remote_metadata_by_actor( $object['attributedTo'] ); - $this->log( 'Attributed to ' . $object['attributedTo'], compact( 'meta' ) ); - if ( isset( $meta['name'] ) ) { - $data['author'] = $meta['name']; - } elseif ( isset( $meta['preferredUsername'] ) ) { - $data['author'] = $meta['preferredUsername']; - } - } - - $this->log( - 'Received feed item', - array( - 'url' => $permalink, - 'data' => $data, - ) - ); - $item = new \Friends\Feed_Item( $data ); - - $this->friends_feed->process_incoming_feed_items( array( $item ), $user_feed ); - - return true; - } - - /** - * We received an announced URL (boost) for a feed, handle it. - * - * @param array $url The announced URL. - * @param \Friends\User_Feed $user_feed The user feed. - */ - private function handle_incoming_announce( $url, \Friends\User_Feed $user_feed, $user_id ) { - if ( ! \wp_http_validate_url( $url ) ) { - $this->log( 'Received invalid announce', compact( 'url' ) ); - return false; - } - $this->log( 'Received announce for ' . $url ); - - $response = \Activitypub\safe_remote_get( $url, $user_id ); - if ( \is_wp_error( $response ) ) { - return $response; - } - $json = \wp_remote_retrieve_body( $response ); - $object = \json_decode( $json, true ); - if ( ! $object ) { - $this->log( 'Received invalid json', compact( 'json' ) ); - return false; - } - $this->log( 'Received response', compact( 'url', 'object' ) ); - - return $this->handle_incoming_post( $object, $user_feed ); - } - - /** - * Prepare to follow the user via a scheduled event. - * - * @param \Friends\User_Feed $user_feed The user feed. - * - * @return bool|WP_Error Whether the event was queued. - */ - public function queue_follow_user( \Friends\User_Feed $user_feed ) { - if ( self::SLUG !== $user_feed->get_parser() ) { - return; - } - - $args = array( $user_feed->get_url(), get_current_user_id() ); - - $unfollow_timestamp = wp_next_scheduled( 'friends_feed_parser_activitypub_unfollow', $args ); - if ( $unfollow_timestamp ) { - // If we just unfollowed, we don't want the event to potentially be executed after our follow event. - wp_unschedule_event( $unfollow_timestamp, $args ); - } - - if ( wp_next_scheduled( 'friends_feed_parser_activitypub_follow', $args ) ) { - return; - } - - return \wp_schedule_single_event( \time(), 'friends_feed_parser_activitypub_follow', $args ); - } - - /** - * Follow a user via ActivityPub at a URL. - * - * @param string $url The url. - * @param int $user_id The current user id. - */ - public function follow_user( $url, $user_id ) { - $meta = \Activitypub\get_remote_metadata_by_actor( $url ); - $to = $meta['id']; - $inbox = \Activitypub\get_inbox_by_actor( $to ); - $actor = \get_author_posts_url( $user_id ); - - $activity = new \Activitypub\Model\Activity( 'Follow', \Activitypub\Model\Activity::TYPE_SIMPLE ); - $activity->set_to( null ); - $activity->set_cc( null ); - $activity->set_actor( $actor ); - $activity->set_object( $to ); - $activity->set_id( $actor . '#follow-' . \preg_replace( '~^https?://~', '', $to ) ); - $activity = $activity->to_json(); - \Activitypub\safe_remote_post( $inbox, $activity, $user_id ); - } - - /** - * Prepare to unfollow the user via a scheduled event. - * - * @param \Friends\User_Feed $user_feed The user feed. - * - * @return bool|WP_Error Whether the event was queued. - */ - public function queue_unfollow_user( \Friends\User_Feed $user_feed ) { - if ( self::SLUG !== $user_feed->get_parser() ) { - return false; - } - - $args = array( $user_feed->get_url(), get_current_user_id() ); - - $follow_timestamp = wp_next_scheduled( 'friends_feed_parser_activitypub_follow', $args ); - if ( $follow_timestamp ) { - // If we just followed, we don't want the event to potentially be executed after our unfollow event. - wp_unschedule_event( $follow_timestamp, $args ); - } - - if ( wp_next_scheduled( 'friends_feed_parser_activitypub_unfollow', $args ) ) { - return true; - } - - return \wp_schedule_single_event( \time(), 'friends_feed_parser_activitypub_unfollow', $args ); - } - - /** - * Unfllow a user via ActivityPub at a URL. - * - * @param string $url The url. - * @param int $user_id The current user id. - */ - public function unfollow_user( $url, $user_id ) { - $meta = \Activitypub\get_remote_metadata_by_actor( $url ); - $to = $meta['id']; - $inbox = \Activitypub\get_inbox_by_actor( $to ); - $actor = \get_author_posts_url( $user_id ); - - $activity = new \Activitypub\Model\Activity( 'Undo', \Activitypub\Model\Activity::TYPE_SIMPLE ); - $activity->set_to( null ); - $activity->set_cc( null ); - $activity->set_actor( $actor ); - $activity->set_object( - array( - 'type' => 'Follow', - 'actor' => $actor, - 'object' => $to, - 'id' => $to, - ) - ); - $activity->set_id( $actor . '#unfollow-' . \preg_replace( '~^https?://~', '', $to ) ); - $activity = $activity->to_json(); - \Activitypub\safe_remote_post( $inbox, $activity, $user_id ); - } - - public function get_possible_mentions() { - static $users = null; - if ( ! method_exists( '\Friends\User_Feed', 'get_by_parser' ) ) { - return array(); - } - - if ( is_null( $users ) || ! apply_filters( 'activitypub_cache_possible_friend_mentions', true ) ) { - $feeds = \Friends\User_Feed::get_by_parser( 'activitypub' ); - $users = array(); - foreach ( $feeds as $feed ) { - $user = $feed->get_friend_user(); - $slug = sanitize_title( $user->user_nicename ); - $users[ '@' . $slug ] = $feed->get_url(); - } - } - return $users; - } - - /** - * Extract the mentions from the post_content. - * - * @param array $mentions The already found mentions. - * @param string $post_content The post content. - * @return mixed The discovered mentions. - */ - public function activitypub_extract_mentions( $mentions, $post_content ) { - $users = $this->get_possible_mentions(); - preg_match_all( '/@(?:[a-zA-Z0-9_-]+)/', $post_content, $matches ); - foreach ( $matches[0] as $match ) { - if ( isset( $users[ $match ] ) ) { - $mentions[ $match ] = $users[ $match ]; - } - } - return $mentions; - } - - - public function the_content( $the_content ) { - $protected_tags = array(); - $the_content = preg_replace_callback( - '#]+>.*?#i', - function( $m ) use ( &$protected_tags ) { - $c = count( $protected_tags ); - $protect = '!#!#PROTECT' . $c . '#!#!'; - $protected_tags[ $protect ] = $m[0]; - return $protect; - }, - $the_content - ); - - $the_content = \preg_replace_callback( '/@(?:[a-zA-Z0-9_-]+)/', array( $this, 'replace_with_links' ), $the_content ); - - $the_content = str_replace( array_keys( $protected_tags ), array_values( $protected_tags ), $the_content ); - - return $the_content; - } - - public function replace_with_links( $result ) { - $users = $this->get_possible_mentions(); - if ( isset( $users[ $result[0] ] ) ) { - return \Activitypub\Mention::replace_with_links( array( $users[ $result[0] ] ) ); - } - - return $result[0]; - } - - public function activitypub_save_settings( \Friends\User $friend ) { - if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'edit-friend-feeds-' . $friend->ID ) ) { - - if ( isset( $_POST['friends_show_replies'] ) && $_POST['friends_show_replies'] ) { - $friend->update_user_option( 'activitypub_friends_show_replies', '1' ); - } else { - $friend->delete_user_option( 'activitypub_friends_show_replies' ); - } - } - } - - public function activitypub_settings( \Friends\User $friend ) { - $has_activitypub_feed = false; - foreach ( $friend->get_active_feeds() as $feed ) { - if ( 'activitypub' === $feed->get_parser() ) { - $has_activitypub_feed = true; - } - } - - if ( ! $has_activitypub_feed ) { - return; - } - - ?> - - ActivityPub - -
-
- get_user_option( 'activitypub_friends_show_replies' ) ); ?> /> - -
-
-

- -

- - - get_parser() ) { - return $item; - } - - if ( ! $friend_user->get_user_option( 'activitypub_friends_show_replies' ) ) { - $plain_text_content = \wp_strip_all_tags( $item->post_content ); - - if ( preg_match( ' /^@(?:[a-zA-Z0-9_.-]+)/i', $plain_text_content, $m ) ) { - $users = $this->get_possible_mentions(); - if ( ! isset( $users[ $m[0] ] ) ) { - $item->_feed_rule_transform = array( - 'post_status' => 'trash', - ); - } - } - } - - return $item; - } -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index be9e470..9a4fd6b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -19,12 +19,6 @@ require_once $_tests_dir . '/includes/functions.php'; */ function _manually_load_plugin() { require \dirname( \dirname( __FILE__ ) ) . '/activitypub.php'; - - // Load the Friends plugin if available to test the integrations. - $friends_plugin = \dirname( \dirname( \dirname( __FILE__ ) ) ) . '/friends/friends.php'; - if ( file_exists( $friends_plugin ) ) { - require $friends_plugin; - } } \tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); diff --git a/tests/class-activitypub-testcase-cache-http.php b/tests/class-activitypub-testcase-cache-http.php index e6ede70..3277c03 100644 --- a/tests/class-activitypub-testcase-cache-http.php +++ b/tests/class-activitypub-testcase-cache-http.php @@ -19,30 +19,15 @@ class ActivityPub_TestCase_Cache_HTTP extends \WP_UnitTestCase { add_filter( 'pre_http_request', array( get_called_class(), 'pre_http_request' ), 10, 3 ); add_filter( 'http_response', array( get_called_class(), 'http_response' ), 10, 3 ); - add_filter( 'http_request_host_is_external', array( get_called_class(), 'http_request_host_is_external' ), 10, 2 ); - add_filter( 'http_request_args', array( get_called_class(), 'http_request_args' ), 10, 2 ); } public function tear_down() { remove_filter( 'pre_http_request', array( get_called_class(), 'pre_http_request' ) ); remove_filter( 'http_response', array( get_called_class(), 'http_response' ) ); - remove_filter( 'http_request_host_is_external', array( get_called_class(), 'http_request_host_is_external' ) ); - remove_filter( 'http_request_args', array( get_called_class(), 'http_request_args' ) ); parent::tear_down(); } - public static function http_request_host_is_external( $in, $host ) { - if ( in_array( $host, array( 'mastodon.local' ), true ) ) { - return true; - } - return $in; - } - public static function http_request_args( $args, $url ) { - if ( in_array( wp_parse_url( $url, PHP_URL_HOST ), array( 'mastodon.local' ), true ) ) { - $args['reject_unsafe_urls'] = false; - } - return $args; - } + public static function pre_http_request( $preempt, $request, $url ) { $p = wp_parse_url( $url ); $cache = __DIR__ . '/fixtures/' . sanitize_title( $p['host'] . '-' . $p['path'] ) . '.json'; diff --git a/tests/test-class-friends-feed-parser-activitypub.php b/tests/test-class-friends-feed-parser-activitypub.php deleted file mode 100644 index b4d5cf7..0000000 --- a/tests/test-class-friends-feed-parser-activitypub.php +++ /dev/null @@ -1,313 +0,0 @@ -friend_id ); - $now = time() - 10; - $status_id = 123; - - $posts = get_posts( - array( - 'post_type' => \Friends\Friends::CPT, - 'author' => $this->friend_id, - ) - ); - - $post_count = count( $posts ); - - // Let's post a new Note through the REST API. - $date = gmdate( \DATE_W3C, $now++ ); - $id = 'test' . $status_id; - $content = 'Test ' . $date . ' ' . wp_rand(); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/' . get_current_user_id() . '/inbox' ); - $request->set_param( 'type', 'Create' ); - $request->set_param( 'id', $id ); - $request->set_param( 'actor', $this->actor ); - - $attachment_url = 'https://mastodon.local/files/original/1234.png'; - $attachment_width = 400; - $attachment_height = 600; - $request->set_param( - 'object', - array( - 'type' => 'Note', - 'id' => $id, - 'attributedTo' => $this->actor, - 'content' => $content, - 'url' => 'https://mastodon.local/users/akirk/statuses/' . ( $status_id++ ), - 'published' => $date, - 'attachment' => array( - array( - 'type' => 'Document', - 'mediaType' => 'image/png', - 'url' => $attachment_url, - 'name' => '', - 'blurhash' => '', - 'width' => $attachment_width, - 'height' => $attachment_height, - - ), - ), - ) - ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 202, $response->get_status() ); - - $posts = get_posts( - array( - 'post_type' => \Friends\Friends::CPT, - 'author' => $this->friend_id, - ) - ); - - $this->assertEquals( $post_count + 1, count( $posts ) ); - $this->assertStringStartsWith( $content, $posts[0]->post_content ); - $this->assertStringContainsString( 'post_content ); - $this->assertEquals( $this->friend_id, $posts[0]->post_author ); - - // Do another test post, this time with a URL that has an @-id. - $date = gmdate( \DATE_W3C, $now++ ); - $id = 'test' . $status_id; - $content = 'Test ' . $date . ' ' . wp_rand(); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/' . get_current_user_id() . '/inbox' ); - $request->set_param( 'type', 'Create' ); - $request->set_param( 'id', $id ); - $request->set_param( 'actor', 'https://mastodon.local/@akirk' ); - $request->set_param( - 'object', - array( - 'type' => 'Note', - 'id' => $id, - 'attributedTo' => 'https://mastodon.local/@akirk', - 'content' => $content, - 'url' => 'https://mastodon.local/users/akirk/statuses/' . ( $status_id++ ), - 'published' => $date, - ) - ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 202, $response->get_status() ); - - $posts = get_posts( - array( - 'post_type' => \Friends\Friends::CPT, - 'author' => $this->friend_id, - ) - ); - - $this->assertEquals( $post_count + 2, count( $posts ) ); - $this->assertEquals( $content, $posts[0]->post_content ); - $this->assertEquals( $this->friend_id, $posts[0]->post_author ); - $this->assertEquals( $this->friend_name, get_post_meta( $posts[0]->ID, 'author', true ) ); - - delete_user_option( 'activitypub_friends_show_replies', $this->friend_id ); - - } - - public function test_incoming_mention_of_others() { - $now = time() - 10; - $status_id = 123; - - $posts = get_posts( - array( - 'post_type' => \Friends\Friends::CPT, - 'author' => $this->friend_id, - ) - ); - - $post_count = count( $posts ); - - // Let's post a new Note through the REST API. - $date = gmdate( \DATE_W3C, $now++ ); - $id = 'test' . $status_id; - $content = '@abc Test ' . $date . ' ' . wp_rand(); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/' . get_current_user_id() . '/inbox' ); - $request->set_param( 'type', 'Create' ); - $request->set_param( 'id', $id ); - $request->set_param( 'actor', $this->actor ); - - $request->set_param( - 'object', - array( - 'type' => 'Note', - 'id' => $id, - 'attributedTo' => $this->actor, - 'content' => $content, - 'url' => 'https://mastodon.local/users/akirk/statuses/' . ( $status_id++ ), - 'published' => $date, - ) - ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 202, $response->get_status() ); - - $posts = get_posts( - array( - 'post_type' => \Friends\Friends::CPT, - 'author' => $this->friend_id, - 'post_status' => 'trash', - ) - ); - - $this->assertEquals( $post_count + 1, count( $posts ) ); - $this->assertStringStartsWith( $content, $posts[0]->post_content ); - $this->assertEquals( $this->friend_id, $posts[0]->post_author ); - } - - public function test_incoming_announce() { - $now = time() - 10; - $status_id = 123; - - self::$users['https://notiz.blog/author/matthias-pfefferle/'] = array( - 'url' => 'https://notiz.blog/author/matthias-pfefferle/', - 'name' => 'Matthias Pfefferle', - ); - - $posts = get_posts( - array( - 'post_type' => \Friends\Friends::CPT, - 'author' => $this->friend_id, - ) - ); - - $post_count = count( $posts ); - - $date = gmdate( \DATE_W3C, $now++ ); - $id = 'test' . $status_id; - - $object = 'https://notiz.blog/2022/11/14/the-at-protocol/'; - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/' . get_current_user_id() . '/inbox' ); - $request->set_param( 'type', 'Announce' ); - $request->set_param( 'id', $id ); - $request->set_param( 'actor', $this->actor ); - $request->set_param( 'published', $date ); - $request->set_param( 'object', $object ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 202, $response->get_status() ); - - $p = wp_parse_url( $object ); - $cache = __DIR__ . '/fixtures/' . sanitize_title( $p['host'] . '-' . $p['path'] ) . '.json'; - $this->assertFileExists( $cache ); - - $object = json_decode( wp_remote_retrieve_body( json_decode( file_get_contents( $cache ), true ) ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents - - $posts = get_posts( - array( - 'post_type' => \Friends\Friends::CPT, - 'author' => $this->friend_id, - ) - ); - - $this->assertEquals( $post_count + 1, count( $posts ) ); - $this->assertStringContainsString( 'Dezentrale Netzwerke', $posts[0]->post_content ); - $this->assertEquals( $this->friend_id, $posts[0]->post_author ); - $this->assertEquals( 'Matthias Pfefferle', get_post_meta( $posts[0]->ID, 'author', true ) ); - } - - public function test_friend_mentions() { - add_filter( 'activitypub_cache_possible_friend_mentions', '__return_false' ); - $post = \wp_insert_post( - array( - 'post_author' => 1, - 'post_content' => '@' . sanitize_title( $this->friend_nicename ) . ' hello', - ) - ); - - $activitypub_post = new \Activitypub\Model\Post( $post ); - - $activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_FULL ); - $activitypub_activity->from_post( $activitypub_post ); - - $this->assertContains( - array( - 'type' => 'Mention', - 'href' => $this->actor, - 'name' => '@' . $this->friend_nicename, - ), - $activitypub_post->get_tags() - ); - - $this->assertContains( \get_rest_url( null, '/activitypub/1.0/users/1/followers' ), $activitypub_activity->get_cc() ); - $this->assertContains( $this->actor, $activitypub_activity->get_cc() ); - - remove_all_filters( 'activitypub_from_post_object' ); - remove_all_filters( 'activitypub_cache_possible_friend_mentions' ); - - \wp_trash_post( $post ); - - } - - public function set_up() { - if ( ! class_exists( '\Friends\Friends' ) ) { - return $this->markTestSkipped( 'The Friends plugin is not loaded.' ); - } - parent::set_up(); - - add_filter( 'pre_get_remote_metadata_by_actor', array( get_called_class(), 'pre_get_remote_metadata_by_actor' ), 10, 2 ); - - $user_id = $this->factory->user->create( - array( - 'role' => 'administrator', - ) - ); - wp_set_current_user( $user_id ); - - $this->friend_name = 'Alex Kirk'; - $this->actor = 'https://mastodon.local/users/akirk'; - - $user_feed = \Friends\User_Feed::get_by_url( $this->actor ); - if ( is_wp_error( $user_feed ) ) { - $this->friend_id = $this->factory->user->create( - array( - 'user_login' => 'akirk.blog', - 'display_name' => $this->friend_name, - 'nicename' => $this->friend_nicename, - 'role' => 'friend', - ) - ); - \Friends\User_Feed::save( - new \Friends\User( $this->friend_id ), - $this->actor, - array( - 'parser' => 'activitypub', - ) - ); - } else { - $this->friend_id = $user_feed->get_friend_user()->ID; - } - $userdata = get_userdata( $this->friend_id ); - $this->friend_nicename = $userdata->user_nicename; - - self::$users[ $this->actor ] = array( - 'url' => $this->actor, - 'name' => $this->friend_name, - ); - self::$users['https://mastodon.local/@akirk'] = self::$users[ $this->actor ]; - - _delete_all_posts(); - } - - public function tear_down() { - remove_filter( 'pre_get_remote_metadata_by_actor', array( get_called_class(), 'pre_get_remote_metadata_by_actor' ) ); - parent::tear_down(); - } - - public static function pre_get_remote_metadata_by_actor( $pre, $actor ) { - if ( isset( self::$users[ $actor ] ) ) { - return self::$users[ $actor ]; - } - return $pre; - } -} diff --git a/tests/test-functions.php b/tests/test-functions.php index 81fc68f..68140e0 100644 --- a/tests/test-functions.php +++ b/tests/test-functions.php @@ -1,41 +1,9 @@ assertTrue( false ); // should not be called. - } - public function test_get_remote_metadata_by_actor() { $metadata = \ActivityPub\get_remote_metadata_by_actor( 'pfefferle@notiz.blog' ); $this->assertEquals( 'https://notiz.blog/author/matthias-pfefferle/', $metadata['url'] ); $this->assertEquals( 'pfefferle', $metadata['preferredUsername'] ); $this->assertEquals( 'Matthias Pfefferle', $metadata['name'] ); } - /** - * @dataProvider example_actors - */ - public function test_get_example_metadata_by_actor( $actor, $domain, $username ) { - add_filter( 'pre_http_request', array( $this, 'invalid_http_response' ), 8, 3 ); - $metadata = \ActivityPub\get_remote_metadata_by_actor( $actor ); - $this->assertEquals( sprintf( 'https://%s/users/%s/', $domain, $username ), $metadata['url'], $actor ); - $this->assertEquals( $username, $metadata['name'], $actor ); - remove_filter( 'pre_http_request', array( $this, 'invalid_http_response' ), 8 ); - } - - public function example_actors() { - $actors = array(); - foreach ( array( 'user', 'test' ) as $username ) { - foreach ( array( 'example.org', 'example.net', 'example2.com', 'my-domain.com', 'your-domain.org', 'test.net' ) as $domain ) { - foreach ( array( '@', '' ) as $leading_at ) { - $actors[] = array( $leading_at . $username . '@' . $domain, $domain, $username ); - } - $actors[] = array( sprintf( 'https://%s/users/%s/', $domain, $username ), $domain, $username ); - $actors[] = array( sprintf( 'https://%s/users/%s', $domain, $username ), $domain, $username ); - $actors[] = array( sprintf( 'https://%s/@%s', $domain, $username ), $domain, $username ); - } - } - - $actors[] = array( 'example@abc.org', 'abc.org', 'example' ); - - return $actors; - } }