From 359eabf67149448eee8f2086e9687078142b5537 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 3 Jul 2023 11:20:44 +0200 Subject: [PATCH] use collection instead of factory --- includes/activity/class-activity.php | 165 ++++++++++++++++++ includes/activity/class-actor.php | 8 + includes/class-activity-dispatcher.php | 2 +- includes/class-activitypub.php | 2 +- includes/class-http.php | 4 +- includes/class-scheduler.php | 3 +- includes/class-signature.php | 6 +- includes/class-webfinger.php | 2 +- .../class-users.php} | 4 +- includes/functions.php | 4 +- includes/model/class-application-user.php | 4 +- includes/model/class-blog-user.php | 4 +- includes/model/class-post.php | 4 +- includes/model/class-user.php | 4 +- includes/peer/class-users.php | 67 ------- includes/rest/class-followers.php | 4 +- includes/rest/class-following.php | 4 +- includes/rest/class-inbox.php | 6 +- includes/rest/class-outbox.php | 4 +- includes/rest/class-users.php | 7 +- includes/rest/class-webfinger.php | 4 +- includes/table/class-followers.php | 4 +- includes/transformer/class-wp-user.php | 76 ++++++++ templates/admin-header.php | 2 +- templates/author-json.php | 2 +- templates/settings.php | 2 +- templates/welcome.php | 4 +- ...typub-rest-post-signature-verification.php | 4 +- 28 files changed, 295 insertions(+), 111 deletions(-) create mode 100644 includes/activity/class-activity.php rename includes/{class-user-factory.php => collection/class-users.php} (98%) delete mode 100644 includes/peer/class-users.php create mode 100644 includes/transformer/class-wp-user.php diff --git a/includes/activity/class-activity.php b/includes/activity/class-activity.php new file mode 100644 index 0000000..592bbc1 --- /dev/null +++ b/includes/activity/class-activity.php @@ -0,0 +1,165 @@ + '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', + ), + ), + ); + + /** + * The object's unique global identifier + * + * @see https://www.w3.org/TR/activitypub/#obj-id + * + * @var string + */ + protected $id; + + /** + * @var string + */ + protected $type = 'Activity'; + + /** + * The context within which the object exists or an activity was + * performed. + * The notion of "context" used is intentionally vague. + * The intended function is to serve as a means of grouping objects + * and activities that share a common originating context or + * purpose. An example could be all activities relating to a common + * project or event. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context + * + * @var string + * | ObjectType + * | Link + * | null + */ + protected $context = self::CONTEXT; + + /** + * Describes the direct object of the activity. + * For instance, in the activity "John added a movie to his + * wishlist", the object of the activity is the movie added. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object-term + * + * @var string + * | ObjectType + * | Link + * | null + */ + protected $object; + + /** + * Describes one or more entities that either performed or are + * expected to perform the activity. + * Any single activity can have multiple actors. + * The actor MAY be specified using an indirect Link. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-actor + * + * @var string + * | \ActivityPhp\Type\Extended\AbstractActor + * | array + * | array + * | Link + */ + protected $actor; + + /** + * The indirect object, or target, of the activity. + * The precise meaning of the target is largely dependent on the + * type of action being described but will often be the object of + * the English preposition "to". + * For instance, in the activity "John added a movie to his + * wishlist", the target of the activity is John's wishlist. + * An activity can have more than one target. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-target + * + * @var string + * | ObjectType + * | array + * | Link + * | array + */ + protected $target; + + /** + * Describes the result of the activity. + * For instance, if a particular action results in the creation of + * a new resource, the result property can be used to describe + * that new resource. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-result + * + * @var string + * | ObjectType + * | Link + * | null + */ + protected $result; + + /** + * An indirect object of the activity from which the + * activity is directed. + * The precise meaning of the origin is the object of the English + * preposition "from". + * For instance, in the activity "John moved an item to List B + * from List A", the origin of the activity is "List A". + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-origin + * + * @var string + * | ObjectType + * | Link + * | null + */ + protected $origin; + + /** + * One or more objects used (or to be used) in the completion of an + * Activity. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-instrument + * + * @var string + * | ObjectType + * | Link + * | null + */ + protected $instrument; +} diff --git a/includes/activity/class-actor.php b/includes/activity/class-actor.php index 202783f..fabd653 100644 --- a/includes/activity/class-actor.php +++ b/includes/activity/class-actor.php @@ -7,6 +7,14 @@ namespace Activitypub\Activity; +/** + * \Activitypub\Activity\Actor is an implementation of + * one an Activity Streams Actor. + * + * Represents an individual actor. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types + */ class Actor extends Base_Object { /** * @var string diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 00ffd48..c7862c0 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -3,7 +3,7 @@ namespace Activitypub; use Activitypub\Model\Post; use Activitypub\Model\Activity; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; use Activitypub\Collection\Followers; use function Activitypub\safe_remote_post; diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index cd84dc1..3b4d68a 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -82,7 +82,7 @@ class Activitypub { $json_template = false; // check if user can publish posts - if ( \is_author() && ! User_Factory::get_by_id( \get_the_author_meta( 'ID' ) ) ) { + if ( \is_author() && ! Users::get_by_id( \get_the_author_meta( 'ID' ) ) ) { return $template; } diff --git a/includes/class-http.php b/includes/class-http.php index 240e5ea..dd19f9b 100644 --- a/includes/class-http.php +++ b/includes/class-http.php @@ -2,7 +2,7 @@ namespace Activitypub; use WP_Error; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; /** * ActivityPub HTTP Class @@ -63,7 +63,7 @@ class Http { */ public static function get( $url ) { $date = \gmdate( 'D, d M Y H:i:s T' ); - $signature = Signature::generate_signature( User_Factory::APPLICATION_USER_ID, 'get', $url, $date ); + $signature = Signature::generate_signature( Users::APPLICATION_USER_ID, 'get', $url, $date ); $wp_version = \get_bloginfo( 'version' ); $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) ); diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index ddb68e1..ed0dbda 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -3,6 +3,7 @@ namespace Activitypub; use Activitypub\Model\Post; +use Activitypub\Collection\Users; use Activitypub\Collection\Followers; /** @@ -82,7 +83,7 @@ class Scheduler { return; } - $activitypub_post = new Post( $post, Users::BLOG_USER_ID ); + $activitypub_post = new Post( $post ); \wp_schedule_single_event( \time(), diff --git a/includes/class-signature.php b/includes/class-signature.php index b9666cc..b7c98fc 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -5,7 +5,7 @@ use WP_Error; use DateTime; use DateTimeZone; use Activitypub\Model\User; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; /** * ActivityPub Signature Class @@ -106,7 +106,7 @@ class Signature { * @return string The signature. */ public static function generate_signature( $user_id, $http_method, $url, $date, $digest = null ) { - $user = User_Factory::get_by_id( $user_id ); + $user = Users::get_by_id( $user_id ); $key = $user->get__private_key(); $url_parts = \wp_parse_url( $url ); @@ -136,7 +136,7 @@ class Signature { \openssl_sign( $signed_string, $signature, $key, \OPENSSL_ALGO_SHA256 ); $signature = \base64_encode( $signature ); // phpcs:ignore - $user = User_Factory::get_by_id( $user_id ); + $user = Users::get_by_id( $user_id ); $key_id = $user->get_url() . '#main-key'; if ( ! empty( $digest ) ) { diff --git a/includes/class-webfinger.php b/includes/class-webfinger.php index 958f544..4a3a1a6 100644 --- a/includes/class-webfinger.php +++ b/includes/class-webfinger.php @@ -24,7 +24,7 @@ class Webfinger { return \get_webfinger_resource( $user_id, false ); } - $user = User_Factory::get_by_id( $user_id ); + $user = Users::get_by_id( $user_id ); if ( ! $user ) { return ''; } diff --git a/includes/class-user-factory.php b/includes/collection/class-users.php similarity index 98% rename from includes/class-user-factory.php rename to includes/collection/class-users.php index 8cc9f26..62b4264 100644 --- a/includes/class-user-factory.php +++ b/includes/collection/class-users.php @@ -1,5 +1,5 @@ get_user_id() ); + $user = Users::get_by_id( $this->get_user_id() ); return $user->get_url(); } diff --git a/includes/model/class-user.php b/includes/model/class-user.php index 10d10d6..c66e3d8 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -5,7 +5,7 @@ use WP_Query; use WP_Error; use Activitypub\Signature; use Activitypub\Model\User; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; use Activitypub\Activity\Actor; use function Activitypub\is_user_disabled; @@ -13,7 +13,7 @@ use function Activitypub\get_rest_url_by_path; class User extends Actor { /** - * The User-ID + * The local User-ID (WP_User). * * @var int */ diff --git a/includes/peer/class-users.php b/includes/peer/class-users.php deleted file mode 100644 index fd9c7b8..0000000 --- a/includes/peer/class-users.php +++ /dev/null @@ -1,67 +0,0 @@ -wp_rewrite_rules(); - - // not using rewrite rules, and 'author=N' method failed, so we're out of options - if ( empty( $rewrite ) ) { - return 0; - } - - // generate rewrite rule for the author url - $author_rewrite = $wp_rewrite->get_author_permastruct(); - $author_regexp = \str_replace( '%author%', '', $author_rewrite ); - - // match the rewrite rule with the passed url - if ( \preg_match( '/https?:\/\/(.+)' . \preg_quote( $author_regexp, '/' ) . '([^\/]+)/i', $url, $match ) ) { - $user = \get_user_by( 'slug', $match[2] ); - if ( $user ) { - return $user->ID; - } - } - - return 0; - } -} diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index 477393e..c359391 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -5,7 +5,7 @@ use WP_Error; use stdClass; use WP_REST_Server; use WP_REST_Response; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; use Activitypub\Collection\Followers as FollowerCollection; use function Activitypub\get_rest_url_by_path; @@ -52,7 +52,7 @@ class Followers { */ public static function get( $request ) { $user_id = $request->get_param( 'user_id' ); - $user = User_Factory::get_by_various( $user_id ); + $user = Users::get_by_various( $user_id ); if ( is_wp_error( $user ) ) { return $user; diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index 6f13482..10f4bed 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -1,7 +1,7 @@ get_param( 'user_id' ); - $user = User_Factory::get_by_various( $user_id ); + $user = Users::get_by_various( $user_id ); if ( is_wp_error( $user ) ) { return $user; diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index b8d75c9..eee740c 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -4,7 +4,7 @@ namespace Activitypub\Rest; use WP_Error; use WP_REST_Server; use WP_REST_Response; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; use Activitypub\Model\Activity; use function Activitypub\get_context; @@ -74,7 +74,7 @@ class Inbox { */ public static function user_inbox_get( $request ) { $user_id = $request->get_param( 'user_id' ); - $user = User_Factory::get_by_various( $user_id ); + $user = Users::get_by_various( $user_id ); if ( is_wp_error( $user ) ) { return $user; @@ -126,7 +126,7 @@ class Inbox { public static function user_inbox_post( $request ) { $user_id = $request->get_param( 'user_id' ); - $user = User_Factory::get_by_various( $user_id ); + $user = Users::get_by_various( $user_id ); if ( is_wp_error( $user ) ) { return $user; diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index 1dc05c7..3df982f 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -5,7 +5,7 @@ use stdClass; use WP_Error; use WP_REST_Server; use WP_REST_Response; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; use Activitypub\Model\Post; use Activitypub\Model\Activity; @@ -53,7 +53,7 @@ class Outbox { */ public static function user_outbox_get( $request ) { $user_id = $request->get_param( 'user_id' ); - $user = User_Factory::get_by_various( $user_id ); + $user = Users::get_by_various( $user_id ); if ( is_wp_error( $user ) ) { return $user; diff --git a/includes/rest/class-users.php b/includes/rest/class-users.php index 1dd08f8..dac7792 100644 --- a/includes/rest/class-users.php +++ b/includes/rest/class-users.php @@ -4,7 +4,8 @@ namespace Activitypub\Rest; use WP_Error; use WP_REST_Server; use WP_REST_Response; -use Activitypub\User_Factory; +use \Activitypub\Model\Activity; +use Activitypub\Collection\User_Collection; use function Activitypub\is_activitypub_request; @@ -50,7 +51,7 @@ class Users { */ public static function get( $request ) { $user_id = $request->get_param( 'user_id' ); - $user = User_Factory::get_by_various( $user_id ); + $user = User_Collection::get_by_various( $user_id ); if ( is_wp_error( $user ) ) { return $user; @@ -68,7 +69,7 @@ class Users { \do_action( 'activitypub_outbox_pre' ); $user->set_context( - \Activitypub\Model\Activity::CONTEXT + Activity::CONTEXT ); $json = $user->to_array(); diff --git a/includes/rest/class-webfinger.php b/includes/rest/class-webfinger.php index f124889..1dc79c9 100644 --- a/includes/rest/class-webfinger.php +++ b/includes/rest/class-webfinger.php @@ -3,7 +3,7 @@ namespace Activitypub\Rest; use WP_Error; use WP_REST_Response; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; /** * ActivityPub WebFinger REST-Class @@ -47,7 +47,7 @@ class Webfinger { */ public static function webfinger( $request ) { $resource = $request->get_param( 'resource' ); - $user = User_Factory::get_by_resource( $resource ); + $user = Users::get_by_resource( $resource ); if ( is_wp_error( $user ) ) { return $user; diff --git a/includes/table/class-followers.php b/includes/table/class-followers.php index f16a479..93d9456 100644 --- a/includes/table/class-followers.php +++ b/includes/table/class-followers.php @@ -2,7 +2,7 @@ namespace Activitypub\Table; use WP_List_Table; -use Activitypub\User_Factory; +use Activitypub\Collection\Users; use Activitypub\Collection\Followers as FollowerCollection; if ( ! \class_exists( '\WP_List_Table' ) ) { @@ -14,7 +14,7 @@ class Followers extends WP_List_Table { public function __construct() { if ( get_current_screen()->id === 'settings_page_activitypub' ) { - $this->user_id = User_Factory::BLOG_USER_ID; + $this->user_id = Users::BLOG_USER_ID; } else { $this->user_id = \get_current_user_id(); } diff --git a/includes/transformer/class-wp-user.php b/includes/transformer/class-wp-user.php new file mode 100644 index 0000000..083b119 --- /dev/null +++ b/includes/transformer/class-wp-user.php @@ -0,0 +1,76 @@ +wp_user = $wp_user; + } + + public function to_user() { + $wp_user = $this->wp_user; + if ( + is_user_disabled( $user->ID ) || + ! get_user_by( 'id', $user->ID ) + ) { + return new WP_Error( + 'activitypub_user_not_found', + \__( 'User not found', 'activitypub' ), + array( 'status' => 404 ) + ); + } + + $user = new User(); + + $user->setwp_user->ID( \esc_url( \get_author_posts_url( $wp_user->ID ) ) ); + $user->set_url( \esc_url( \get_author_posts_url( $wp_user->ID ) ) ); + $user->set_summary( $this->get_summary() ); + $user->set_name( \esc_attr( $wp_user->display_name ) ); + $user->set_preferred_username( \esc_attr( $wp_user->login ) ); + + $user->set_icon( $this->get_icon() ); + $user->set_image( $this->get_image() ); + + return $user; + } + + public function get_summary() { + $description = get_user_meta( $this->wp_user->ID, 'activitypub_user_description', true ); + if ( empty( $description ) ) { + $description = $this->wp_user->description; + } + return \wpautop( \wp_kses( $description, 'default' ) ); + } + + public function get_icon() { + $icon = \esc_url( + \get_avatar_url( + $this->wp_user->ID, + array( 'size' => 120 ) + ) + ); + + return array( + 'type' => 'Image', + 'url' => $icon, + ); + } + + public function get_image() { + if ( \has_header_image() ) { + $image = \esc_url( \get_header_image() ); + return array( + 'type' => 'Image', + 'url' => $image, + ); + } + + return null; + } +} diff --git a/templates/admin-header.php b/templates/admin-header.php index f2e7ed5..3b40468 100644 --- a/templates/admin-header.php +++ b/templates/admin-header.php @@ -12,7 +12,7 @@ - + diff --git a/templates/author-json.php b/templates/author-json.php index 3defd98..6c5f8ad 100644 --- a/templates/author-json.php +++ b/templates/author-json.php @@ -1,5 +1,5 @@ set_context( \Activitypub\Model\Activity::CONTEXT diff --git a/templates/settings.php b/templates/settings.php index 162ea02..b587b9e 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -31,7 +31,7 @@
- +

diff --git a/templates/welcome.php b/templates/welcome.php index 0bce61c..02e8be3 100644 --- a/templates/welcome.php +++ b/templates/welcome.php @@ -15,7 +15,7 @@

- +

@@ -44,7 +44,7 @@

ID ); + $user = \Activitypub\Collection\Users::get_by_id( wp_get_current_user()->ID ); echo wp_kses( \sprintf( // translators: diff --git a/tests/test-class-activitypub-rest-post-signature-verification.php b/tests/test-class-activitypub-rest-post-signature-verification.php index 3ee895e..f39927d 100644 --- a/tests/test-class-activitypub-rest-post-signature-verification.php +++ b/tests/test-class-activitypub-rest-post-signature-verification.php @@ -42,7 +42,7 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase { $signed_headers = $signature_block['headers']; $signed_data = Activitypub\Signature::get_signed_data( $signed_headers, $signature_block, $headers ); - $user = Activitypub\User_Factory::get_by_id( 1 ); + $user = Activitypub\Collection\Users::get_by_id( 1 ); $public_key = $user->get__public_key(); @@ -55,7 +55,7 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase { add_filter( 'pre_get_remote_metadata_by_actor', function( $json, $actor ) { - $user = Activitypub\User_Factory::get_by_id( 1 ); + $user = Activitypub\Collection\Users::get_by_id( 1 ); $public_key = $user->get__public_key(); // return ActivityPub Profile with signature return array(