From 2b5f635ba3ae18c67de14edbac581fb77ea8928e Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Sat, 28 Sep 2024 12:53:21 -0500 Subject: [PATCH 01/47] Suggestion: Adopt WordPress PHP Coding Standards (#905) * Update phpcs rules * Automated style updates * Silence all other Warnings and Errors We'll fix them in follow ups. --- activitypub.php | 4 +- composer.json | 2 +- includes/activity/class-actor.php | 30 ++-- .../activity/extended-object/class-event.php | 9 +- includes/class-activitypub.php | 2 +- includes/class-admin.php | 50 +++---- includes/class-blocks.php | 22 +-- includes/class-comment.php | 18 +-- includes/class-hashtag.php | 5 +- includes/class-health-check.php | 20 +-- includes/class-http.php | 40 ++--- includes/class-link.php | 7 +- includes/class-notification.php | 4 +- includes/class-shortcodes.php | 10 +- includes/class-signature.php | 32 ++-- includes/class-webfinger.php | 4 +- includes/collection/class-extra-fields.php | 21 +-- includes/collection/class-followers.php | 26 ++-- includes/collection/class-interactions.php | 28 ++-- includes/collection/class-replies.php | 14 +- includes/collection/class-users.php | 12 +- includes/functions.php | 45 +++--- includes/handler/class-create.php | 2 +- includes/handler/class-undo.php | 2 +- includes/handler/class-update.php | 4 +- includes/model/class-application.php | 6 +- includes/model/class-blog.php | 34 ++--- includes/model/class-follower.php | 13 +- includes/model/class-user.php | 11 +- includes/rest/class-actors.php | 7 +- includes/rest/class-comment.php | 5 +- includes/rest/class-followers.php | 18 +-- includes/rest/class-following.php | 10 +- includes/rest/class-inbox.php | 26 ++-- includes/rest/class-nodeinfo.php | 48 +++--- includes/rest/class-outbox.php | 18 +-- includes/rest/class-webfinger.php | 6 +- includes/transformer/class-comment.php | 4 +- includes/transformer/class-post.php | 31 ++-- integration/class-buddypress.php | 12 +- integration/class-enable-mastodon-apps.php | 138 +++++++++--------- integration/class-jetpack.php | 4 +- integration/class-nodeinfo.php | 4 +- phpcs.xml | 83 +++-------- 44 files changed, 443 insertions(+), 448 deletions(-) diff --git a/activitypub.php b/activitypub.php index d2381957e..9b6be1f11 100644 --- a/activitypub.php +++ b/activitypub.php @@ -112,7 +112,7 @@ function ( $full_class ) { if ( strncmp( $full_class, $base, strlen( $base ) ) === 0 ) { $maybe_uppercase = str_replace( $base, '', $full_class ); - $class = strtolower( $maybe_uppercase ); + $class = strtolower( $maybe_uppercase ); // All classes should be capitalized. If this is instead looking for a lowercase method, we ignore that. if ( $maybe_uppercase === $class ) { return; @@ -144,7 +144,7 @@ function ( $full_class ) { * Add plugin settings link */ function plugin_settings_link( $actions ) { - $settings_link = array(); + $settings_link = array(); $settings_link[] = \sprintf( '%2s', \menu_page_url( 'activitypub', false ), diff --git a/composer.json b/composer.json index 2442e31a2..9033833df 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "vendor/bin/phpunit" ], "lint": [ - "vendor/bin/phpcs -n -q" + "vendor/bin/phpcs" ], "lint:fix": [ "vendor/bin/phpcbf" diff --git a/includes/activity/class-actor.php b/includes/activity/class-actor.php index 80dc710b3..8cfdf915b 100644 --- a/includes/activity/class-actor.php +++ b/includes/activity/class-actor.php @@ -22,28 +22,28 @@ class Actor extends Base_Object { 'https://w3id.org/security/v1', 'https://purl.archive.org/socialweb/webfinger', array( - 'schema' => 'http://schema.org#', - 'toot' => 'http://joinmastodon.org/ns#', - 'lemmy' => 'https://join-lemmy.org/ns#', + 'schema' => 'http://schema.org#', + 'toot' => 'http://joinmastodon.org/ns#', + 'lemmy' => 'https://join-lemmy.org/ns#', 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', - 'PropertyValue' => 'schema:PropertyValue', - 'value' => 'schema:value', - 'Hashtag' => 'as:Hashtag', - 'featured' => array( - '@id' => 'toot:featured', + 'PropertyValue' => 'schema:PropertyValue', + 'value' => 'schema:value', + 'Hashtag' => 'as:Hashtag', + 'featured' => array( + '@id' => 'toot:featured', '@type' => '@id', ), - 'featuredTags' => array( - '@id' => 'toot:featuredTags', + 'featuredTags' => array( + '@id' => 'toot:featuredTags', '@type' => '@id', ), - 'moderators' => array( - '@id' => 'lemmy:moderators', + 'moderators' => array( + '@id' => 'lemmy:moderators', '@type' => '@id', ), - 'postingRestrictedToMods' => 'lemmy:postingRestrictedToMods', - 'discoverable' => 'toot:discoverable', - 'indexable' => 'toot:indexable', + 'postingRestrictedToMods' => 'lemmy:postingRestrictedToMods', + 'discoverable' => 'toot:discoverable', + 'indexable' => 'toot:indexable', ), ); diff --git a/includes/activity/extended-object/class-event.php b/includes/activity/extended-object/class-event.php index b0adb84f0..1a57b7454 100644 --- a/includes/activity/extended-object/class-event.php +++ b/includes/activity/extended-object/class-event.php @@ -51,6 +51,7 @@ class Event extends Base_Object { /** * Mobilizon compatible values for repliesModertaionOption. + * * @var array */ const REPLIES_MODERATION_OPTION_TYPES = array( 'allow_all', 'closed' ); @@ -62,6 +63,7 @@ class Event extends Base_Object { /** * Allowed values for ical VEVENT STATUS. + * * @var array */ const ICAL_EVENT_STATUS_TYPES = array( 'TENTATIVE', 'CONFIRMED', 'CANCELLED' ); @@ -70,6 +72,7 @@ class Event extends Base_Object { * Default event categories. * * These values currently reflect the default set as proposed by Mobilizon to maximize interoperability. + * * @var array */ const DEFAULT_EVENT_CATEGORIES = array( @@ -253,7 +256,7 @@ public function set_timezone( $timezone ) { public function set_replies_moderation_option( $type ) { if ( in_array( $type, self::REPLIES_MODERATION_OPTION_TYPES, true ) ) { $this->replies_moderation_option = $type; - $this->comments_enabled = ( 'allow_all' === $type ) ? true : false; + $this->comments_enabled = ( 'allow_all' === $type ) ? true : false; } else { _doing_it_wrong( __METHOD__, @@ -272,7 +275,7 @@ public function set_replies_moderation_option( $type ) { */ public function set_comments_enabled( $comments_enabled ) { if ( is_bool( $comments_enabled ) ) { - $this->comments_enabled = $comments_enabled; + $this->comments_enabled = $comments_enabled; $this->replies_moderation_option = $comments_enabled ? 'allow_all' : 'closed'; } else { _doing_it_wrong( @@ -332,7 +335,7 @@ public function set_category( $category, $mobilizon_compatibilty = true ) { public function set_external_participation_url( $url ) { if ( preg_match( '/^https?:\/\/.*/i', $url ) ) { $this->external_participation_url = $url; - $this->join_mode = 'external'; + $this->join_mode = 'external'; } return $this; diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 7117dd6bb..78e3284cc 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -508,7 +508,7 @@ private static function register_post_types() { // Both User and Blog Extra Fields types have the same args. $args = array( - 'labels' => array( + 'labels' => array( 'name' => _x( 'Extra fields', 'post_type plural name', 'activitypub' ), 'singular_name' => _x( 'Extra field', 'post_type single name', 'activitypub' ), 'add_new' => __( 'Add new', 'activitypub' ), diff --git a/includes/class-admin.php b/includes/class-admin.php index fc58417d2..353b75fbc 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -193,8 +193,8 @@ public static function register_settings() { 'activitypub', 'activitypub_post_content_type', array( - 'type' => 'string', - 'description' => \__( 'Use title and link, summary, full or custom content', 'activitypub' ), + 'type' => 'string', + 'description' => \__( 'Use title and link, summary, full or custom content', 'activitypub' ), 'show_in_rest' => array( 'schema' => array( 'enum' => array( @@ -204,34 +204,34 @@ public static function register_settings() { ), ), ), - 'default' => 'content', + 'default' => 'content', ) ); \register_setting( 'activitypub', 'activitypub_custom_post_content', array( - 'type' => 'string', - 'description' => \__( 'Define your own custom post template', 'activitypub' ), + 'type' => 'string', + 'description' => \__( 'Define your own custom post template', 'activitypub' ), 'show_in_rest' => true, - 'default' => ACTIVITYPUB_CUSTOM_POST_CONTENT, + 'default' => ACTIVITYPUB_CUSTOM_POST_CONTENT, ) ); \register_setting( 'activitypub', 'activitypub_max_image_attachments', array( - 'type' => 'integer', + 'type' => 'integer', 'description' => \__( 'Number of images to attach to posts.', 'activitypub' ), - 'default' => ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS, + 'default' => ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS, ) ); \register_setting( 'activitypub', 'activitypub_object_type', array( - 'type' => 'string', - 'description' => \__( 'The Activity-Object-Type', 'activitypub' ), + 'type' => 'string', + 'description' => \__( 'The Activity-Object-Type', 'activitypub' ), 'show_in_rest' => array( 'schema' => array( 'enum' => array( @@ -240,25 +240,25 @@ public static function register_settings() { ), ), ), - 'default' => 'note', + 'default' => 'note', ) ); \register_setting( 'activitypub', 'activitypub_use_hashtags', array( - 'type' => 'boolean', + 'type' => 'boolean', 'description' => \__( 'Add hashtags in the content as native tags and replace the #tag with the tag-link', 'activitypub' ), - 'default' => '0', + 'default' => '0', ) ); \register_setting( 'activitypub', 'activitypub_use_opengraph', array( - 'type' => 'boolean', + 'type' => 'boolean', 'description' => \__( 'Automatically add "fediverse:creator" OpenGraph tags for Authors and the Blog-User.', 'activitypub' ), - 'default' => '1', + 'default' => '1', ) ); \register_setting( @@ -275,18 +275,18 @@ public static function register_settings() { 'activitypub', 'activitypub_enable_users', array( - 'type' => 'boolean', + 'type' => 'boolean', 'description' => \__( 'Every Author on this Blog (with the publish_posts capability) gets his own ActivityPub enabled Profile.', 'activitypub' ), - 'default' => '1', + 'default' => '1', ) ); \register_setting( 'activitypub', 'activitypub_enable_blog_user', array( - 'type' => 'boolean', + 'type' => 'boolean', 'description' => \__( 'Your Blog becomes an ActivityPub compatible Profile.', 'activitypub' ), - 'default' => '0', + 'default' => '0', ) ); @@ -295,10 +295,10 @@ public static function register_settings() { 'activitypub_blog', 'activitypub_blog_description', array( - 'type' => 'string', - 'description' => \esc_html__( 'The Description of the Blog-User', 'activitypub' ), + 'type' => 'string', + 'description' => \esc_html__( 'The Description of the Blog-User', 'activitypub' ), 'show_in_rest' => true, - 'default' => '', + 'default' => '', ) ); \register_setting( @@ -350,9 +350,9 @@ public static function register_settings() { 'activitypub_blog', 'activitypub_header_image', array( - 'type' => 'integer', + 'type' => 'integer', 'description' => \__( 'The Attachment-ID of the Sites Header-Image', 'activitypub' ), - 'default' => null, + 'default' => null, ) ); } @@ -665,7 +665,7 @@ public static function manage_posts_custom_column( $column_name, $post_id ) { * @return array The extended bulk options. */ public static function user_bulk_options( $actions ) { - $actions['add_activitypub_cap'] = __( 'Enable for ActivityPub', 'activitypub' ); + $actions['add_activitypub_cap'] = __( 'Enable for ActivityPub', 'activitypub' ); $actions['remove_activitypub_cap'] = __( 'Disable for ActivityPub', 'activitypub' ); return $actions; diff --git a/includes/class-blocks.php b/includes/class-blocks.php index f2fea4089..b87bd5086 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -62,17 +62,17 @@ public static function handle_in_reply_to_get_param() { } public static function add_data() { - $context = is_admin() ? 'editor' : 'view'; + $context = is_admin() ? 'editor' : 'view'; $followers_handle = 'activitypub-followers-' . $context . '-script'; $follow_me_handle = 'activitypub-follow-me-' . $context . '-script'; - $data = array( + $data = array( 'namespace' => ACTIVITYPUB_REST_NAMESPACE, - 'enabled' => array( - 'site' => ! is_user_type_disabled( 'blog' ), + 'enabled' => array( + 'site' => ! is_user_type_disabled( 'blog' ), 'users' => ! is_user_type_disabled( 'user' ), ), ); - $js = sprintf( 'var _activityPubOptions = %s;', wp_json_encode( $data ) ); + $js = sprintf( 'var _activityPubOptions = %s;', wp_json_encode( $data ) ); \wp_add_inline_script( $followers_handle, $js, 'before' ); \wp_add_inline_script( $follow_me_handle, $js, 'before' ); } @@ -152,6 +152,7 @@ private static function get_user_id( $user_string ) { /** * Filter an array by a list of keys. + * * @param array $array The array to filter. * @param array $keys The keys to keep. * @return array The filtered array. @@ -162,12 +163,13 @@ protected static function filter_array_by_keys( $array, $keys ) { /** * Render the follow me block. + * * @param array $attrs The block attributes. * @return string The HTML to render. */ public static function render_follow_me_block( $attrs ) { $user_id = self::get_user_id( $attrs['selectedUser'] ); - $user = User_Collection::get_by_id( $user_id ); + $user = User_Collection::get_by_id( $user_id ); if ( is_wp_error( $user ) ) { if ( 'inherit' === $attrs['selectedUser'] ) { // If the user is 'inherit' and we couldn't determine the user, don't render anything. @@ -204,10 +206,10 @@ public static function render_follower_block( $attrs ) { return ''; } - $per_page = absint( $attrs['per_page'] ); + $per_page = absint( $attrs['per_page'] ); $follower_data = Followers::get_followers_with_count( $followee_user_id, $per_page ); - $attrs['followerData']['total'] = $follower_data['total']; + $attrs['followerData']['total'] = $follower_data['total']; $attrs['followerData']['followers'] = array_map( function ( $follower ) { return self::filter_array_by_keys( @@ -217,7 +219,7 @@ function ( $follower ) { }, $follower_data['followers'] ); - $wrapper_attributes = get_block_wrapper_attributes( + $wrapper_attributes = get_block_wrapper_attributes( array( 'aria-label' => __( 'Fediverse Followers', 'activitypub' ), 'class' => 'activitypub-follower-block', @@ -261,7 +263,7 @@ public static function render_reply_block( $attrs ) { public static function render_follower( $follower ) { $external_svg = ''; - $template = + $template = ' diff --git a/includes/class-comment.php b/includes/class-comment.php index 867e1fa22..2d869f33e 100644 --- a/includes/class-comment.php +++ b/includes/class-comment.php @@ -54,7 +54,7 @@ public static function comment_reply_link( $link, $args, $comment ) { $attrs = array( 'selectedComment' => self::generate_id( $comment ), - 'commentId' => $comment->comment_ID, + 'commentId' => $comment->comment_ID, ); $div = sprintf( @@ -78,7 +78,7 @@ public static function comment_reply_link( $link, $args, $comment ) { */ private static function create_fediverse_reply_link( $link, $args ) { $str_to_replace = sprintf( '>%s<', $args['reply_text'] ); - $replace_with = sprintf( + $replace_with = sprintf( ' title="%s">%s<', esc_attr__( 'This comment was received from the fediverse and your reply will be sent to the original author', 'activitypub' ), esc_html__( 'Reply with federation', 'activitypub' ) @@ -343,8 +343,8 @@ public static function comment_class( $classes, $css_class, $comment_id ) { /** * Gets the public comment id via the WordPress comments meta. * - * @param int $wp_comment_id The internal WordPress comment ID. - * @param bool $fallback Whether the code should fall back to `source_url` if `source_id` is not set. + * @param int $wp_comment_id The internal WordPress comment ID. + * @param bool $fallback Whether the code should fall back to `source_url` if `source_id` is not set. * * @return string|null The ActivityPub id/url of the comment. */ @@ -363,8 +363,8 @@ public static function get_source_id( $wp_comment_id, $fallback = true ) { /** * Gets the public comment url via the WordPress comments meta. * - * @param int $wp_comment_id The internal WordPress comment ID. - * @param bool $fallback Whether the code should fall back to `source_id` if `source_url` is not set. + * @param int $wp_comment_id The internal WordPress comment ID. + * @param bool $fallback Whether the code should fall back to `source_id` if `source_url` is not set. * * @return string|null The ActivityPub id/url of the comment. */ @@ -383,7 +383,7 @@ public static function get_source_url( $wp_comment_id, $fallback = true ) { /** * Link remote comments to source url. * - * @param string $comment_link + * @param string $comment_link * @param object|WP_Comment $comment * * @return string $url @@ -430,7 +430,7 @@ public static function generate_id( $comment ) { private static function post_has_remote_comments( $post_id ) { $comments = \get_comments( array( - 'post_id' => $post_id, + 'post_id' => $post_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'meta_query' => array( 'relation' => 'AND', @@ -496,7 +496,7 @@ public static function enqueue_scripts() { \wp_enqueue_style( $handle, \plugins_url( 'build/remote-reply/style-index.css', __DIR__ ), - [ 'wp-components' ], + array( 'wp-components' ), $assets['version'] ); } diff --git a/includes/class-hashtag.php b/includes/class-hashtag.php index cb1a5e1be..7344963dc 100644 --- a/includes/class-hashtag.php +++ b/includes/class-hashtag.php @@ -28,7 +28,8 @@ public static function init() { * @return array the activity object array */ public static function filter_activity_object( $object_array ) { - /* Removed until this is merged: https://github.com/mastodon/mastodon/pull/28629 + /* + Removed until this is merged: https://github.com/mastodon/mastodon/pull/28629 if ( ! empty( $object_array['summary'] ) ) { $object_array['summary'] = self::the_content( $object_array['summary'] ); } @@ -85,7 +86,7 @@ public static function the_content( $the_content ) { * @return string the final string */ public static function replace_with_links( $result ) { - $tag = $result[1]; + $tag = $result[1]; $tag_object = \get_term_by( 'name', $tag, 'post_tag' ); if ( ! $tag_object ) { $tag_object = \get_term_by( 'name', $tag, 'category' ); diff --git a/includes/class-health-check.php b/includes/class-health-check.php index 8a6dbf368..7e782f3d0 100644 --- a/includes/class-health-check.php +++ b/includes/class-health-check.php @@ -112,7 +112,7 @@ public static function test_system_cron() { '

%s

', \__( 'Enhance your WordPress site’s performance and mitigate potential heavy loads caused by plugins like ActivityPub by setting up a system cron job to run WP Cron. This ensures scheduled tasks are executed consistently and reduces the reliance on website traffic for trigger events.', 'activitypub' ) ); - $result['actions'] .= sprintf( + $result['actions'] .= sprintf( '

%s %s

', __( 'https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/', 'activitypub' ), __( 'Learn how to hook the WP-Cron into the System Task Scheduler.', 'activitypub' ), @@ -167,8 +167,8 @@ public static function test_webfinger() { * @return boolean|WP_Error */ public static function is_author_url_accessible() { - $user = \wp_get_current_user(); - $author_url = \get_author_posts_url( $user->ID ); + $user = \wp_get_current_user(); + $author_url = \get_author_posts_url( $user->ID ); $reference_author_url = self::get_author_posts_url( $user->ID, $user->user_nicename ); // check for "author" in URL @@ -190,7 +190,7 @@ public static function is_author_url_accessible() { $response = \wp_remote_get( $author_url, array( - 'headers' => array( 'Accept' => 'application/activity+json' ), + 'headers' => array( 'Accept' => 'application/activity+json' ), 'redirection' => 0, ) ); @@ -252,7 +252,7 @@ public static function is_author_url_accessible() { * @return boolean|WP_Error */ public static function is_webfinger_endpoint_accessible() { - $user = Users::get_by_id( Users::APPLICATION_USER_ID ); + $user = Users::get_by_id( Users::APPLICATION_USER_ID ); $resource = $user->get_webfinger(); $url = Webfinger::resolve( $resource ); @@ -277,7 +277,7 @@ public static function is_webfinger_endpoint_accessible() { ); $health_messages = array( - 'webfinger_url_not_accessible' => \sprintf( + 'webfinger_url_not_accessible' => \sprintf( $not_accessible, $url->get_error_data()['data'] ), @@ -287,7 +287,7 @@ public static function is_webfinger_endpoint_accessible() { $url->get_error_data()['data'] ), ); - $message = null; + $message = null; if ( isset( $health_messages[ $url->get_error_code() ] ) ) { $message = $health_messages[ $url->get_error_code() ]; } @@ -314,7 +314,7 @@ public static function is_webfinger_endpoint_accessible() { public static function get_author_posts_url( $author_id, $author_nicename = '' ) { global $wp_rewrite; $auth_id = (int) $author_id; - $link = $wp_rewrite->get_author_permastruct(); + $link = $wp_rewrite->get_author_permastruct(); if ( empty( $link ) ) { $file = home_url( '/' ); @@ -343,12 +343,12 @@ public static function debug_information( $info ) { $info['activitypub'] = array( 'label' => __( 'ActivityPub', 'activitypub' ), 'fields' => array( - 'webfinger' => array( + 'webfinger' => array( 'label' => __( 'WebFinger Resource', 'activitypub' ), 'value' => Webfinger::get_user_resource( wp_get_current_user()->ID ), 'private' => true, ), - 'author_url' => array( + 'author_url' => array( 'label' => __( 'Author URL', 'activitypub' ), 'value' => get_author_posts_url( wp_get_current_user()->ID ), 'private' => true, diff --git a/includes/class-http.php b/includes/class-http.php index 2a8ce7d0d..7cc5f9ae6 100644 --- a/includes/class-http.php +++ b/includes/class-http.php @@ -24,8 +24,8 @@ class Http { public static function post( $url, $body, $user_id ) { \do_action( 'activitypub_pre_http_post', $url, $body, $user_id ); - $date = \gmdate( 'D, d M Y H:i:s T' ); - $digest = Signature::generate_digest( $body ); + $date = \gmdate( 'D, d M Y H:i:s T' ); + $digest = Signature::generate_digest( $body ); $signature = Signature::generate_signature( $user_id, 'post', $url, $date, $digest ); $wp_version = get_masked_wp_version(); @@ -36,19 +36,19 @@ public static function post( $url, $body, $user_id ) { * @param string $user_agent The user agent string. */ $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) ); - $args = array( - 'timeout' => 100, + $args = array( + 'timeout' => 100, 'limit_response_size' => 1048576, - 'redirection' => 3, - 'user-agent' => "$user_agent; ActivityPub", - 'headers' => array( - 'Accept' => 'application/activity+json', + 'redirection' => 3, + 'user-agent' => "$user_agent; ActivityPub", + 'headers' => array( + 'Accept' => 'application/activity+json', 'Content-Type' => 'application/activity+json', - 'Digest' => $digest, - 'Signature' => $signature, - 'Date' => $date, + 'Digest' => $digest, + 'Signature' => $signature, + 'Date' => $date, ), - 'body' => $body, + 'body' => $body, ); $response = \wp_safe_remote_post( $url, $args ); @@ -86,7 +86,7 @@ public static function get( $url, $cached = false ) { } } - $date = \gmdate( 'D, d M Y H:i:s T' ); + $date = \gmdate( 'D, d M Y H:i:s T' ); $signature = Signature::generate_signature( Users::APPLICATION_USER_ID, 'get', $url, $date ); $wp_version = get_masked_wp_version(); @@ -99,15 +99,15 @@ public static function get( $url, $cached = false ) { $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) ); $args = array( - 'timeout' => apply_filters( 'activitypub_remote_get_timeout', 100 ), + 'timeout' => apply_filters( 'activitypub_remote_get_timeout', 100 ), 'limit_response_size' => 1048576, - 'redirection' => 3, - 'user-agent' => "$user_agent; ActivityPub", - 'headers' => array( - 'Accept' => 'application/activity+json', + 'redirection' => 3, + 'user-agent' => "$user_agent; ActivityPub", + 'headers' => array( + 'Accept' => 'application/activity+json', 'Content-Type' => 'application/activity+json', - 'Signature' => $signature, - 'Date' => $date, + 'Signature' => $signature, + 'Date' => $date, ), ); diff --git a/includes/class-link.php b/includes/class-link.php index dafea7ae1..94b63cefd 100644 --- a/includes/class-link.php +++ b/includes/class-link.php @@ -24,7 +24,8 @@ public static function init() { * @return array the activity object array */ public static function filter_activity_object( $object_array ) { - /* Removed until this is merged: https://github.com/mastodon/mastodon/pull/28629 + /* + Removed until this is merged: https://github.com/mastodon/mastodon/pull/28629 if ( ! empty( $object_array['summary'] ) ) { $object_array['summary'] = self::the_content( $object_array['summary'] ); } @@ -82,7 +83,7 @@ public static function replace_with_links( $result ) { $text_url = $parsed_url['host']; if ( 'www.' === substr( $text_url, 0, 4 ) ) { - $text_url = substr( $text_url, 4 ); + $text_url = substr( $text_url, 4 ); $invisible_prefix .= 'www.'; } if ( ! empty( $parsed_url['port'] ) ) { @@ -98,7 +99,7 @@ public static function replace_with_links( $result ) { $text_url .= '#' . $parsed_url['fragment']; } - $display = \substr( $text_url, 0, 30 ); + $display = \substr( $text_url, 0, 30 ); $invisible_suffix = \substr( $text_url, 30 ); $display_class = ''; diff --git a/includes/class-notification.php b/includes/class-notification.php index 002d52d35..3aed0207f 100644 --- a/includes/class-notification.php +++ b/includes/class-notification.php @@ -43,8 +43,8 @@ class Notification { * @param int $target The WordPress User-Id. */ public function __construct( $type, $actor, $object, $target ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound - $this->type = $type; - $this->actor = $actor; + $this->type = $type; + $this->actor = $actor; $this->object = $object; $this->target = $target; } diff --git a/includes/class-shortcodes.php b/includes/class-shortcodes.php index 16be96ab9..90a1bcca6 100644 --- a/includes/class-shortcodes.php +++ b/includes/class-shortcodes.php @@ -332,7 +332,7 @@ public static function author( $atts, $content, $tag ) { } $author_id = \get_post_field( 'post_author', $item->ID ); - $name = \get_the_author_meta( 'display_name', $author_id ); + $name = \get_the_author_meta( 'display_name', $author_id ); if ( ! $name ) { return ''; @@ -358,7 +358,7 @@ public static function authorurl( $atts, $content, $tag ) { } $author_id = \get_post_field( 'post_author', $item->ID ); - $url = \get_the_author_meta( 'user_url', $author_id ); + $url = \get_the_author_meta( 'user_url', $author_id ); if ( ! $url ) { return ''; @@ -422,7 +422,7 @@ public static function date( $atts, $content, $tag ) { return ''; } - $datetime = \get_post_datetime( $item ); + $datetime = \get_post_datetime( $item ); $dateformat = \get_option( 'date_format' ); $timeformat = \get_option( 'time_format' ); @@ -451,7 +451,7 @@ public static function time( $atts, $content, $tag ) { return ''; } - $datetime = \get_post_datetime( $item ); + $datetime = \get_post_datetime( $item ); $dateformat = \get_option( 'date_format' ); $timeformat = \get_option( 'time_format' ); @@ -480,7 +480,7 @@ public static function datetime( $atts, $content, $tag ) { return ''; } - $datetime = \get_post_datetime( $item ); + $datetime = \get_post_datetime( $item ); $dateformat = \get_option( 'date_format' ); $timeformat = \get_option( 'time_format' ); diff --git a/includes/class-signature.php b/includes/class-signature.php index 6f8880a15..07831583e 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -60,7 +60,7 @@ public static function get_private_key_for( $user_id, $force = false ) { */ public static function get_keypair_for( $user_id ) { $option_key = self::get_signature_options_key_for( $user_id ); - $key_pair = \get_option( $option_key ); + $key_pair = \get_option( $option_key ); if ( ! $key_pair ) { $key_pair = self::generate_key_pair_for( $user_id ); @@ -78,7 +78,7 @@ public static function get_keypair_for( $user_id ) { */ protected static function generate_key_pair_for( $user_id ) { $option_key = self::get_signature_options_key_for( $user_id ); - $key_pair = self::check_legacy_key_pair_for( $user_id ); + $key_pair = self::check_legacy_key_pair_for( $user_id ); if ( $key_pair ) { \add_option( $option_key, $key_pair ); @@ -87,14 +87,14 @@ protected static function generate_key_pair_for( $user_id ) { } $config = array( - 'digest_alg' => 'sha512', + 'digest_alg' => 'sha512', 'private_key_bits' => 2048, 'private_key_type' => \OPENSSL_KEYTYPE_RSA, ); - $key = \openssl_pkey_new( $config ); + $key = \openssl_pkey_new( $config ); $priv_key = null; - $detail = array(); + $detail = array(); if ( $key ) { \openssl_pkey_export( $key, $priv_key ); @@ -152,15 +152,15 @@ protected static function get_signature_options_key_for( $user_id ) { protected static function check_legacy_key_pair_for( $user_id ) { switch ( $user_id ) { case 0: - $public_key = \get_option( 'activitypub_blog_user_public_key' ); + $public_key = \get_option( 'activitypub_blog_user_public_key' ); $private_key = \get_option( 'activitypub_blog_user_private_key' ); break; case -1: - $public_key = \get_option( 'activitypub_application_user_public_key' ); + $public_key = \get_option( 'activitypub_application_user_public_key' ); $private_key = \get_option( 'activitypub_application_user_private_key' ); break; default: - $public_key = \get_user_meta( $user_id, 'magic_sig_public_key', true ); + $public_key = \get_user_meta( $user_id, 'magic_sig_public_key', true ); $private_key = \get_user_meta( $user_id, 'magic_sig_private_key', true ); break; } @@ -253,11 +253,11 @@ public static function verify_http_signature( $request ) { $route = '/' . $path . $route; } - $headers = $request->get_headers(); + $headers = $request->get_headers(); $headers['(request-target)'][0] = strtolower( $request->get_method() ) . ' ' . $route; } else { - $request = self::format_server_request( $request ); - $headers = $request['headers']; // $_SERVER array + $request = self::format_server_request( $request ); + $headers = $request['headers']; // $_SERVER array $headers['(request-target)'][0] = strtolower( $headers['request_method'][0] ) . ' ' . $headers['request_uri'][0]; } @@ -359,7 +359,7 @@ public static function get_signature_algorithm( $signature_block ) { if ( $signature_block['algorithm'] ) { switch ( $signature_block['algorithm'] ) { case 'rsa-sha-512': - return 'sha512'; //hs2019 https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12 + return 'sha512'; // hs2019 https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12 default: return 'sha256'; } @@ -375,8 +375,8 @@ public static function get_signature_algorithm( $signature_block ) { * @return array signature parts */ public static function parse_signature_header( $signature ) { - $parsed_header = array(); - $matches = array(); + $parsed_header = array(); + $matches = array(); if ( \preg_match( '/keyId="(.*?)"/ism', $signature, $matches ) ) { $parsed_header['keyId'] = trim( $matches[1] ); @@ -459,7 +459,7 @@ public static function get_signed_data( $signed_headers, $signature_block, $head $d->setTimeZone( new DateTimeZone( 'UTC' ) ); $c = $d->format( 'U' ); - $dplus = time() + ( 3 * HOUR_IN_SECONDS ); + $dplus = time() + ( 3 * HOUR_IN_SECONDS ); $dminus = time() - ( 3 * HOUR_IN_SECONDS ); if ( $c > $dplus || $c < $dminus ) { @@ -499,7 +499,7 @@ public static function format_server_request( $server ) { if ( 'REQUEST_URI' === $req_param ) { $request['headers']['route'][] = $param_val; } else { - $header_key = str_replace( + $header_key = str_replace( 'http_', '', $req_param diff --git a/includes/class-webfinger.php b/includes/class-webfinger.php index f87f7ba21..f26de250d 100644 --- a/includes/class-webfinger.php +++ b/includes/class-webfinger.php @@ -142,10 +142,10 @@ public static function get_identifier_and_host( $url ) { if ( ! preg_match( '/^([a-zA-Z+]+):/', $url, $match ) ) { $identifier = 'acct:' . $url; - $scheme = 'acct'; + $scheme = 'acct'; } else { $identifier = $url; - $scheme = $match[1]; + $scheme = $match[1]; } $host = null; diff --git a/includes/collection/class-extra-fields.php b/includes/collection/class-extra-fields.php index 608cfbdab..25362e9f5 100644 --- a/includes/collection/class-extra-fields.php +++ b/includes/collection/class-extra-fields.php @@ -21,9 +21,9 @@ class Extra_Fields { * @return \WP_Post[] The extra fields. */ public static function get_actor_fields( $user_id ) { - $is_blog = self::is_blog( $user_id ); + $is_blog = self::is_blog( $user_id ); $post_type = $is_blog ? self::BLOG_POST_TYPE : self::USER_POST_TYPE; - $args = array( + $args = array( 'post_type' => $post_type, 'nopaging' => true, 'orderby' => 'menu_order', @@ -33,7 +33,7 @@ public static function get_actor_fields( $user_id ) { $args['author'] = $user_id; } - $query = new \WP_Query( $args ); + $query = new \WP_Query( $args ); $fields = $query->posts ?? array(); return apply_filters( 'activitypub_get_actor_extra_fields', $fields, $user_id ); @@ -75,10 +75,10 @@ function ( $rel ) { ); foreach ( $fields as $post ) { - $content = self::get_formatted_content( $post ); + $content = self::get_formatted_content( $post ); $attachments[] = array( - 'type' => 'PropertyValue', - 'name' => \get_the_title( $post ), + 'type' => 'PropertyValue', + 'name' => \get_the_title( $post ), 'value' => \html_entity_decode( $content, \ENT_QUOTES, @@ -104,7 +104,7 @@ function ( $rel ) { 'type' => 'Link', 'name' => \get_the_title( $post ), 'href' => \esc_url( $tags->get_attribute( 'href' ) ), - 'rel' => explode( ' ', $tags->get_attribute( 'rel' ) ), + 'rel' => explode( ' ', $tags->get_attribute( 'rel' ) ), ); $link_added = true; @@ -176,7 +176,7 @@ public static function default_actor_extra_fields( $extra_fields, $user_id ) { return $extra_fields; } - $is_blog = self::is_blog( $user_id ); + $is_blog = self::is_blog( $user_id ); $already_migrated = $is_blog ? \get_option( 'activitypub_default_extra_fields' ) : \get_user_meta( $user_id, 'activitypub_default_extra_fields', true ); @@ -199,7 +199,7 @@ function ( $rel ) { ); if ( ! $is_blog ) { - $author_url = \get_the_author_meta( 'user_url', $user_id ); + $author_url = \get_the_author_meta( 'user_url', $user_id ); $author_posts_url = \get_author_posts_url( $user_id ); $defaults[ \__( 'Profile', 'activitypub' ) ] = $author_posts_url; @@ -226,7 +226,7 @@ function ( $rel ) { 'menu_order' => $menu_order, ); - $menu_order += 10; + $menu_order += 10; $extra_field_id = wp_insert_post( $extra_field ); $extra_fields[] = get_post( $extra_field_id ); } @@ -247,6 +247,7 @@ public static function make_paragraph_block( $content ) { /** * Checks if the user is the blog user. + * * @param int $user_id The user ID. * @return bool True if the user is the blog user, otherwise false. */ diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index fdd80daa7..2df376c17 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -17,7 +17,7 @@ * @author Matthias Pfefferle */ class Followers { - const POST_TYPE = 'ap_follower'; + const POST_TYPE = 'ap_follower'; const CACHE_KEY_INBOXES = 'follower_inboxes_%s'; /** @@ -139,10 +139,10 @@ public static function get_follower_by_actor( $actor ) { /** * Get the Followers of a given user * - * @param int $user_id The ID of the WordPress User. - * @param int $number Maximum number of results to return. - * @param int $page Page number. - * @param array $args The WP_Query arguments. + * @param int $user_id The ID of the WordPress User. + * @param int $number Maximum number of results to return. + * @param int $page Page number. + * @param array $args The WP_Query arguments. * @return array List of `Follower` objects. */ public static function get_followers( $user_id, $number = -1, $page = null, $args = array() ) { @@ -153,10 +153,10 @@ public static function get_followers( $user_id, $number = -1, $page = null, $arg /** * Get the Followers of a given user, along with a total count for pagination purposes. * - * @param int $user_id The ID of the WordPress User. - * @param int $number Maximum number of results to return. - * @param int $page Page number. - * @param array $args The WP_Query arguments. + * @param int $user_id The ID of the WordPress User. + * @param int $number Maximum number of results to return. + * @param int $page Page number. + * @param array $args The WP_Query arguments. * * @return array * followers List of `Follower` objects. @@ -178,9 +178,9 @@ public static function get_followers_with_count( $user_id, $number = -1, $page = ), ); - $args = wp_parse_args( $args, $defaults ); - $query = new WP_Query( $args ); - $total = $query->found_posts; + $args = wp_parse_args( $args, $defaults ); + $query = new WP_Query( $args ); + $total = $query->found_posts; $followers = array_map( function ( $post ) { return Follower::init_from_cpt( $post ); @@ -259,7 +259,7 @@ public static function count_followers( $user_id ) { */ public static function get_inboxes( $user_id ) { $cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id ); - $inboxes = wp_cache_get( $cache_key, 'activitypub' ); + $inboxes = wp_cache_get( $cache_key, 'activitypub' ); if ( $inboxes ) { return $inboxes; diff --git a/includes/collection/class-interactions.php b/includes/collection/class-interactions.php index 25df63d3a..00e1f579d 100644 --- a/includes/collection/class-interactions.php +++ b/includes/collection/class-interactions.php @@ -62,7 +62,7 @@ public static function add_comment( $activity ) { public static function update_comment( $activity ) { $meta = get_remote_metadata_by_actor( $activity['actor'] ); - //Determine comment_ID + // Determine comment_ID $comment = object_id_to_comment( \esc_url_raw( $activity['object']['id'] ) ); $commentdata = \get_comment( $comment, ARRAY_A ); @@ -70,8 +70,8 @@ public static function update_comment( $activity ) { return false; } - //found a local comment id - $commentdata['comment_author'] = \esc_attr( $meta['name'] ? $meta['name'] : $meta['preferredUsername'] ); + // found a local comment id + $commentdata['comment_author'] = \esc_attr( $meta['name'] ? $meta['name'] : $meta['preferredUsername'] ); $commentdata['comment_content'] = \addslashes( $activity['object']['content'] ); return self::persist( $commentdata, self::UPDATE ); @@ -80,7 +80,7 @@ public static function update_comment( $activity ) { /** * Adds an incoming Like, Announce, ... as a comment to a post. * - * @param array $activity Activity array. + * @param array $activity Activity array. * * @return array|false Comment data or `false` on failure. */ @@ -173,14 +173,14 @@ public static function get_interactions_by_actor( $actor ) { $actor = object_to_uri( $meta['url'] ); } - $args = array( + $args = array( 'nopaging' => true, 'author_url' => $actor, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'meta_query' => array( array( - 'key' => 'protocol', - 'value' => 'activitypub', + 'key' => 'protocol', + 'value' => 'activitypub', 'compare' => '=', ), ), @@ -252,14 +252,14 @@ public static function activity_to_comment( $activity ) { } $commentdata = array( - 'comment_author' => \esc_attr( $comment_author ), - 'comment_author_url' => \esc_url_raw( $url ), - 'comment_content' => $comment_content, - 'comment_type' => 'comment', + 'comment_author' => \esc_attr( $comment_author ), + 'comment_author_url' => \esc_url_raw( $url ), + 'comment_content' => $comment_content, + 'comment_type' => 'comment', 'comment_author_email' => '', - 'comment_meta' => array( - 'source_id' => \esc_url_raw( object_to_uri( $activity['object'] ) ), - 'protocol' => 'activitypub', + 'comment_meta' => array( + 'source_id' => \esc_url_raw( object_to_uri( $activity['object'] ) ), + 'protocol' => 'activitypub', ), ); diff --git a/includes/collection/class-replies.php b/includes/collection/class-replies.php index ade818362..11c170812 100644 --- a/includes/collection/class-replies.php +++ b/includes/collection/class-replies.php @@ -21,13 +21,13 @@ class Replies { */ private static function build_args( $wp_object ) { $args = array( - 'status' => 'approve', + 'status' => 'approve', 'orderby' => 'comment_date_gmt', - 'order' => 'ASC', + 'order' => 'ASC', ); if ( $wp_object instanceof WP_Post ) { - $args['parent'] = 0; // TODO: maybe this is unnecessary. + $args['parent'] = 0; // TODO: maybe this is unnecessary. $args['post_id'] = $wp_object->ID; } elseif ( $wp_object instanceof WP_Comment ) { $args['parent'] = $wp_object->comment_ID; @@ -48,7 +48,7 @@ private static function build_args( $wp_object ) { private static function add_pagination_args( $args, $page, $comments_per_page ) { $args['number'] = $comments_per_page; - $offset = intval( $page ) * $comments_per_page; + $offset = intval( $page ) * $comments_per_page; $args['offset'] = $offset; return $args; @@ -76,7 +76,7 @@ private static function get_id( $wp_object ) { * Get the replies collection. * * @param WP_Post|WP_Comment $wp_object - * @param int $page + * @param int $page * * @return array An associative array containing the replies collection without JSON-LD context. */ @@ -88,8 +88,8 @@ public static function get_collection( $wp_object ) { } $replies = array( - 'id' => $id, - 'type' => 'Collection', + 'id' => $id, + 'type' => 'Collection', ); $replies['first'] = self::get_collection_page( $wp_object, 0, $replies['id'] ); diff --git a/includes/collection/class-users.php b/includes/collection/class-users.php index d0672a042..d6e70cec5 100644 --- a/includes/collection/class-users.php +++ b/includes/collection/class-users.php @@ -88,12 +88,12 @@ public static function get_by_username( $username ) { // check for 'activitypub_username' meta $user = new WP_User_Query( array( - 'count_total' => false, - 'number' => 1, - 'hide_empty' => true, - 'fields' => 'ID', + 'count_total' => false, + 'number' => 1, + 'hide_empty' => true, + 'fields' => 'ID', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query - 'meta_query' => array( + 'meta_query' => array( 'relation' => 'OR', array( 'key' => 'activitypub_user_identifier', @@ -144,7 +144,7 @@ public static function get_by_resource( $resource ) { $resource = object_to_uri( $resource ); $scheme = 'acct'; - $match = array(); + $match = array(); // try to extract the scheme and the host if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $resource, $match ) ) { // extract the scheme diff --git a/includes/functions.php b/includes/functions.php index 767bf7215..546f2b0c1 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -64,7 +64,10 @@ function get_remote_metadata_by_actor( $actor, $cached = true ) { return new WP_Error( 'activitypub_no_valid_actor_identifier', \__( 'The "actor" identifier is not valid', 'activitypub' ), - array( 'status' => 404, 'actor' => $actor ) + array( + 'status' => 404, + 'actor' => $actor, + ) ); } } @@ -77,7 +80,10 @@ function get_remote_metadata_by_actor( $actor, $cached = true ) { return new WP_Error( 'activitypub_no_valid_actor_identifier', \__( 'The "actor" identifier is not valid', 'activitypub' ), - array( 'status' => 404, 'actor' => $actor ) + array( + 'status' => 404, + 'actor' => $actor, + ) ); } @@ -100,7 +106,10 @@ function get_remote_metadata_by_actor( $actor, $cached = true ) { $metadata = new WP_Error( 'activitypub_no_valid_actor_url', \__( 'The "actor" is no valid URL', 'activitypub' ), - array( 'status' => 400, 'actor' => $actor ) + array( + 'status' => 400, + 'actor' => $actor, + ) ); return $metadata; } @@ -118,7 +127,10 @@ function get_remote_metadata_by_actor( $actor, $cached = true ) { $metadata = new WP_Error( 'activitypub_invalid_json', \__( 'No valid JSON data', 'activitypub' ), - array( 'status' => 400, 'actor' => $actor ) + array( + 'status' => 400, + 'actor' => $actor, + ) ); return $metadata; } @@ -185,7 +197,7 @@ function url_to_authorid( $url ) { // generate rewrite rule for the author url $author_rewrite = $wp_rewrite->get_author_permastruct(); - $author_regexp = \str_replace( '%author%', '', $author_rewrite ); + $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 ) ) { @@ -248,7 +260,7 @@ function is_tombstone( $wp_error ) { */ function get_rest_url_by_path( $path = '' ) { // we'll handle the leading slash. - $path = ltrim( $path, '/' ); + $path = ltrim( $path, '/' ); $namespaced_path = sprintf( '/%s/%s', ACTIVITYPUB_REST_NAMESPACE, $path ); return \get_rest_url( null, $namespaced_path ); } @@ -631,9 +643,9 @@ function is_activity_public( $data ) { */ function get_active_users( $duration = 1 ) { - $duration = intval( $duration ); + $duration = intval( $duration ); $transient_key = sprintf( 'monthly_active_users_%d', $duration ); - $count = get_transient( $transient_key ); + $count = get_transient( $transient_key ); if ( false === $count ) { global $wpdb; @@ -924,8 +936,8 @@ function ( $enclosure ) { } return array( - 'url' => $attributes[0], - 'length' => isset( $attributes[1] ) ? trim( $attributes[1] ) : null, + 'url' => $attributes[0], + 'length' => isset( $attributes[1] ) ? trim( $attributes[1] ) : null, 'mediaType' => isset( $attributes[2] ) ? trim( $attributes[2] ) : null, ); }, @@ -961,7 +973,7 @@ function get_comment_ancestors( $comment ) { // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition while ( $id > 0 ) { - $ancestor = \get_comment( $id ); + $ancestor = \get_comment( $id ); $parent_id = (int) $ancestor->comment_parent; // Loop detection: If the ancestor has been seen before, break. @@ -1019,7 +1031,6 @@ function custom_large_numbers( $formatted, $number, $decimals ) { /** * Registers a ActivityPub comment type. * - * * @param string $comment_type Key for comment type. * @param array $args Arguments. * @@ -1040,7 +1051,6 @@ function register_comment_type( $comment_type, $args = array() ) { /** * Fires after a ActivityPub comment type is registered. * - * * @param string $comment_type Comment type. * @param array $args Arguments used to register the comment type. */ @@ -1102,8 +1112,8 @@ function enrich_content_data( $content, $regex, $regex_callback ) { if ( mb_strlen( $content ) > MB_IN_BYTES ) { return $content; } - $tag_stack = array(); - $protected_tags = array( + $tag_stack = array(); + $protected_tags = array( 'pre', 'code', 'textarea', @@ -1111,7 +1121,7 @@ function enrich_content_data( $content, $regex, $regex_callback ) { 'a', ); $content_with_links = ''; - $in_protected_tag = false; + $in_protected_tag = false; foreach ( wp_html_split( $content ) as $chunk ) { if ( preg_match( '#^$#i', $chunk, $m ) ) { $content_with_links .= $chunk; @@ -1210,7 +1220,8 @@ function generate_post_summary( $post, $length = 500 ) { $content = $content[0] . ' ' . $excerpt_more; } - /* Removed until this is merged: https://github.com/mastodon/mastodon/pull/28629 + /* + Removed until this is merged: https://github.com/mastodon/mastodon/pull/28629 return \apply_filters( 'the_excerpt', $content ); */ return $content; diff --git a/includes/handler/class-create.php b/includes/handler/class-create.php index 1bbb137d3..1e48c6097 100644 --- a/includes/handler/class-create.php +++ b/includes/handler/class-create.php @@ -70,7 +70,7 @@ public static function handle_create( $array, $user_id, $object = null ) { * @param bool $valid The validation state * @param string $param The object parameter * @param \WP_REST_Request $request The request object - * @param array $array The activity-object + * @param array $array The activity-object * * @return bool The validation state: true if valid, false if not */ diff --git a/includes/handler/class-undo.php b/includes/handler/class-undo.php index 0fb7cfb10..c43f60444 100644 --- a/includes/handler/class-undo.php +++ b/includes/handler/class-undo.php @@ -40,7 +40,7 @@ public static function handle_undo( $activity ) { // Handle "Unfollow" requests if ( 'Follow' === $type ) { $user_id = object_to_uri( $activity['object']['object'] ); - $user = Users::get_by_resource( $user_id ); + $user = Users::get_by_resource( $user_id ); if ( ! $user || is_wp_error( $user ) ) { // If we can not find a user, diff --git a/includes/handler/class-update.php b/includes/handler/class-update.php index 0542d5606..426d8ec53 100644 --- a/includes/handler/class-update.php +++ b/includes/handler/class-update.php @@ -23,8 +23,8 @@ public static function init() { /** * Handle "Update" requests * - * @param array $array The activity-object - * @param int $user_id The id of the local blog-user + * @param array $array The activity-object + * @param int $user_id The id of the local blog-user */ public static function handle_update( $array ) { $object_type = isset( $array['object']['type'] ) ? $array['object']['type'] : ''; diff --git a/includes/model/class-application.php b/includes/model/class-application.php index 35c57d647..a0bda4310 100644 --- a/includes/model/class-application.php +++ b/includes/model/class-application.php @@ -81,7 +81,7 @@ public function get_preferred_username() { return $this->get_name(); } - /** + /** * Get the User-Icon. * * @return array The User-Icon. @@ -178,8 +178,8 @@ public function get_webfinger() { public function get_public_key() { return array( - 'id' => $this->get_id() . '#main-key', - 'owner' => $this->get_id(), + 'id' => $this->get_id() . '#main-key', + 'owner' => $this->get_id(), 'publicKeyPem' => Signature::get_public_key_for( Users::APPLICATION_USER_ID ), ); } diff --git a/includes/model/class-blog.php b/includes/model/class-blog.php index 65e90452a..aff08831c 100644 --- a/includes/model/class-blog.php +++ b/includes/model/class-blog.php @@ -301,8 +301,8 @@ public function get_attributed_to() { public function get_public_key() { return array( - 'id' => $this->get_id() . '#main-key', - 'owner' => $this->get_id(), + 'id' => $this->get_id() . '#main-key', + 'owner' => $this->get_id(), 'publicKeyPem' => Signature::get_public_key_for( $this->get__id() ), ); } @@ -400,21 +400,21 @@ public function update_name( $value ) { } /** - * Update the User-Description. - * - * @param mixed $value The new value. - * @return bool True if the attribute was updated, false otherwise. - */ + * Update the User-Description. + * + * @param mixed $value The new value. + * @return bool True if the attribute was updated, false otherwise. + */ public function update_summary( $value ) { return \update_option( 'blogdescription', $value ); } /** - * Update the User-Icon. - * - * @param mixed $value The new value. - * @return bool True if the attribute was updated, false otherwise. - */ + * Update the User-Icon. + * + * @param mixed $value The new value. + * @return bool True if the attribute was updated, false otherwise. + */ public function update_icon( $value ) { if ( ! wp_attachment_is_image( $value ) ) { return false; @@ -423,11 +423,11 @@ public function update_icon( $value ) { } /** - * Update the User-Header-Image. - * - * @param mixed $value The new value. - * @return bool True if the attribute was updated, false otherwise. - */ + * Update the User-Header-Image. + * + * @param mixed $value The new value. + * @return bool True if the attribute was updated, false otherwise. + */ public function update_header( $value ) { if ( ! wp_attachment_is_image( $value ) ) { return false; diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 4590ea49f..92ef1717f 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -184,7 +184,7 @@ public function save() { $args['post_date_gmt'] = $post->post_date_gmt; } - $post_id = wp_insert_post( $args ); + $post_id = wp_insert_post( $args ); $this->_id = $post_id; return $post_id; @@ -205,6 +205,7 @@ public function upsert() { * Beware that this os deleting a Follower for ALL users!!! * * To delete only the User connection (unfollow) + * * @see \Activitypub\Rest\Followers::remove_follower() * * @return void @@ -219,8 +220,8 @@ public function delete() { * @return void */ protected function get_post_meta_input() { - $meta_input = array(); - $meta_input['activitypub_inbox'] = $this->get_shared_inbox(); + $meta_input = array(); + $meta_input['activitypub_inbox'] = $this->get_shared_inbox(); $meta_input['activitypub_actor_json'] = $this->to_json(); return $meta_input; @@ -239,9 +240,9 @@ public function get_icon() { } return array( - 'type' => 'Image', + 'type' => 'Image', 'mediaType' => 'image/jpeg', - 'url' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg', + 'url' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg', ); } @@ -339,7 +340,7 @@ public function get_shared_inbox() { */ public static function init_from_cpt( $post ) { $actor_json = get_post_meta( $post->ID, 'activitypub_actor_json', true ); - $object = self::init_from_json( $actor_json ); + $object = self::init_from_json( $actor_json ); $object->set__id( $post->ID ); $object->set_id( $post->guid ); $object->set_name( $post->post_title ); diff --git a/includes/model/class-user.php b/includes/model/class-user.php index c90e1b349..7849b9ab7 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -76,7 +76,7 @@ public static function from_wp_user( $user_id ) { ); } - $object = new static(); + $object = new static(); $object->_id = $user_id; return $object; @@ -185,8 +185,8 @@ public function get_published() { public function get_public_key() { return array( - 'id' => $this->get_id() . '#main-key', - 'owner' => $this->get_id(), + 'id' => $this->get_id() . '#main-key', + 'owner' => $this->get_id(), 'publicKeyPem' => Signature::get_public_key_for( $this->get__id() ), ); } @@ -295,7 +295,10 @@ public function get_indexable() { * @return bool True if the attribute was updated, false otherwise. */ public function update_name( $value ) { - $userdata = [ 'ID' => $this->_id, 'display_name' => $value ]; + $userdata = array( + 'ID' => $this->_id, + 'display_name' => $value, + ); return \wp_update_user( $userdata ); } diff --git a/includes/rest/class-actors.php b/includes/rest/class-actors.php index a21a7e5de..f7bfc45a5 100644 --- a/includes/rest/class-actors.php +++ b/includes/rest/class-actors.php @@ -65,7 +65,7 @@ public static function register_routes() { /** * Handle GET request * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * * @return WP_REST_Response */ @@ -127,7 +127,10 @@ public static function remote_follow_get( WP_REST_Request $request ) { $url = str_replace( '{uri}', $resource, $template ); return new WP_REST_Response( - array( 'url' => $url, 'template' => $template ), + array( + 'url' => $url, + 'template' => $template, + ), 200 ); } diff --git a/includes/rest/class-comment.php b/includes/rest/class-comment.php index c9f911b46..cb9e11260 100644 --- a/includes/rest/class-comment.php +++ b/includes/rest/class-comment.php @@ -84,7 +84,10 @@ public static function remote_reply_get( WP_REST_Request $request ) { $url = str_replace( '{uri}', $resource, $template ); return new WP_REST_Response( - array( 'url' => $url, 'template' => $template ), + array( + 'url' => $url, + 'template' => $template, + ), 200 ); } diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index ca882cf3e..0543a199a 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -47,7 +47,7 @@ public static function register_routes() { /** * Handle GET request * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * * @return WP_REST_Response */ @@ -74,10 +74,10 @@ public static function get( $request ) { $json->{'@context'} = \Activitypub\get_context(); - $json->id = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) ); + $json->id = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) ); $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); - $json->actor = $user->get_id(); - $json->type = 'OrderedCollectionPage'; + $json->actor = $user->get_id(); + $json->type = 'OrderedCollectionPage'; $json->totalItems = $data['total']; // phpcs:ignore $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) ); // phpcs:ignore @@ -119,12 +119,12 @@ public static function request_parameters() { $params = array(); $params['page'] = array( - 'type' => 'integer', + 'type' => 'integer', 'default' => 1, ); $params['per_page'] = array( - 'type' => 'integer', + 'type' => 'integer', 'default' => 20, ); @@ -136,13 +136,13 @@ public static function request_parameters() { $params['user_id'] = array( 'required' => true, - 'type' => 'string', + 'type' => 'string', ); $params['context'] = array( - 'type' => 'string', + 'type' => 'string', 'default' => 'simple', - 'enum' => array( 'simple', 'full' ), + 'enum' => array( 'simple', 'full' ), ); return $params; diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index 4e077279c..e8e0ca823 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -46,7 +46,7 @@ public static function register_routes() { /** * Handle GET request * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * * @return WP_REST_Response */ @@ -67,10 +67,10 @@ public static function get( $request ) { $json->{'@context'} = \Activitypub\get_context(); - $json->id = get_rest_url_by_path( sprintf( 'actors/%d/following', $user->get__id() ) ); + $json->id = get_rest_url_by_path( sprintf( 'actors/%d/following', $user->get__id() ) ); $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); - $json->actor = $user->get_id(); - $json->type = 'OrderedCollectionPage'; + $json->actor = $user->get_id(); + $json->type = 'OrderedCollectionPage'; $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/following', $user->get__id() ) ); // phpcs:ignore @@ -101,7 +101,7 @@ public static function request_parameters() { $params['user_id'] = array( 'required' => true, - 'type' => 'string', + 'type' => 'string', ); return $params; diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index a5a7e03c5..b09bac837 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -69,7 +69,7 @@ public static function register_routes() { /** * Renders the user-inbox * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * @return WP_REST_Response */ public static function user_inbox_get( $request ) { @@ -90,9 +90,9 @@ public static function user_inbox_get( $request ) { $json = new \stdClass(); $json->{'@context'} = get_context(); - $json->id = get_rest_url_by_path( sprintf( 'actors/%d/inbox', $user->get__id() ) ); - $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); - $json->type = 'OrderedCollectionPage'; + $json->id = get_rest_url_by_path( sprintf( 'actors/%d/inbox', $user->get__id() ) ); + $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); + $json->type = 'OrderedCollectionPage'; $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/inbox', $user->get__id() ) ); // phpcs:ignore $json->totalItems = 0; // phpcs:ignore $json->orderedItems = array(); // phpcs:ignore @@ -115,7 +115,7 @@ public static function user_inbox_get( $request ) { /** * Handles user-inbox requests * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * * @return WP_REST_Response */ @@ -144,7 +144,7 @@ public static function user_inbox_post( $request ) { /** * The shared inbox * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * * @return WP_REST_Response */ @@ -177,7 +177,7 @@ public static function user_inbox_get_parameters() { $params['user_id'] = array( 'required' => true, - 'type' => 'string', + 'type' => 'string', ); return $params; @@ -193,16 +193,16 @@ public static function user_inbox_post_parameters() { $params['user_id'] = array( 'required' => true, - 'type' => 'string', + 'type' => 'string', ); $params['id'] = array( - 'required' => true, + 'required' => true, 'sanitize_callback' => 'esc_url_raw', ); $params['actor'] = array( - 'required' => true, + 'required' => true, // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable 'sanitize_callback' => function ( $param, $request, $key ) { return object_to_uri( $param ); @@ -214,7 +214,7 @@ public static function user_inbox_post_parameters() { ); $params['object'] = array( - 'required' => true, + 'required' => true, 'validate_callback' => function ( $param, $request, $key ) { return apply_filters( 'activitypub_validate_object', true, $param, $request, $key ); }, @@ -232,7 +232,7 @@ public static function shared_inbox_post_parameters() { $params = self::user_inbox_post_parameters(); $params['to'] = array( - 'required' => false, + 'required' => false, // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable 'sanitize_callback' => function ( $param, $request, $key ) { if ( \is_string( $param ) ) { @@ -277,7 +277,7 @@ public static function shared_inbox_post_parameters() { */ public static function get_recipients( $data ) { $recipients = extract_recipients_from_activity( $data ); - $users = array(); + $users = array(); foreach ( $recipients as $recipient ) { $user_id = url_to_authorid( $recipient ); diff --git a/includes/rest/class-nodeinfo.php b/includes/rest/class-nodeinfo.php index 02b89b6c6..b8af823c3 100644 --- a/includes/rest/class-nodeinfo.php +++ b/includes/rest/class-nodeinfo.php @@ -67,7 +67,7 @@ public static function register_routes() { /** * Render NodeInfo file * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * * @return WP_REST_Response */ @@ -79,37 +79,37 @@ public static function nodeinfo( $request ) { $nodeinfo = array(); - $nodeinfo['version'] = '2.0'; + $nodeinfo['version'] = '2.0'; $nodeinfo['software'] = array( - 'name' => 'wordpress', + 'name' => 'wordpress', 'version' => get_masked_wp_version(), ); - $posts = \wp_count_posts(); + $posts = \wp_count_posts(); $comments = \wp_count_comments(); $nodeinfo['usage'] = array( - 'users' => array( + 'users' => array( 'total' => get_total_users(), 'activeMonth' => get_active_users( '1 month ago' ), 'activeHalfyear' => get_active_users( '6 month ago' ), ), - 'localPosts' => (int) $posts->publish, + 'localPosts' => (int) $posts->publish, 'localComments' => (int) $comments->approved, ); $nodeinfo['openRegistrations'] = false; - $nodeinfo['protocols'] = array( 'activitypub' ); + $nodeinfo['protocols'] = array( 'activitypub' ); $nodeinfo['services'] = array( - 'inbound' => array(), + 'inbound' => array(), 'outbound' => array(), ); $nodeinfo['metadata'] = array( - 'nodeName' => \get_bloginfo( 'name' ), + 'nodeName' => \get_bloginfo( 'name' ), 'nodeDescription' => \get_bloginfo( 'description' ), - 'nodeIcon' => \get_site_icon_url(), + 'nodeIcon' => \get_site_icon_url(), ); return new WP_REST_Response( $nodeinfo, 200 ); @@ -118,7 +118,7 @@ public static function nodeinfo( $request ) { /** * Render NodeInfo file * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * * @return WP_REST_Response */ @@ -131,31 +131,31 @@ public static function nodeinfo2( $request ) { $nodeinfo = array(); $nodeinfo['version'] = '2.0'; - $nodeinfo['server'] = array( - 'baseUrl' => \home_url( '/' ), - 'name' => \get_bloginfo( 'name' ), + $nodeinfo['server'] = array( + 'baseUrl' => \home_url( '/' ), + 'name' => \get_bloginfo( 'name' ), 'software' => 'wordpress', - 'version' => get_masked_wp_version(), + 'version' => get_masked_wp_version(), ); - $posts = \wp_count_posts(); + $posts = \wp_count_posts(); $comments = \wp_count_comments(); $nodeinfo['usage'] = array( - 'users' => array( + 'users' => array( 'total' => get_total_users(), 'activeMonth' => get_active_users( 1 ), 'activeHalfyear' => get_active_users( 6 ), ), - 'localPosts' => (int) $posts->publish, + 'localPosts' => (int) $posts->publish, 'localComments' => (int) $comments->approved, ); $nodeinfo['openRegistrations'] = false; - $nodeinfo['protocols'] = array( 'activitypub' ); + $nodeinfo['protocols'] = array( 'activitypub' ); $nodeinfo['services'] = array( - 'inbound' => array(), + 'inbound' => array(), 'outbound' => array(), ); @@ -165,19 +165,19 @@ public static function nodeinfo2( $request ) { /** * Render NodeInfo discovery file * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * * @return WP_REST_Response */ public static function discovery( $request ) { - $discovery = array(); + $discovery = array(); $discovery['links'] = array( array( - 'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', + 'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', 'href' => get_rest_url_by_path( 'nodeinfo' ), ), array( - 'rel' => 'https://www.w3.org/ns/activitystreams#Application', + 'rel' => 'https://www.w3.org/ns/activitystreams#Application', 'href' => get_rest_url_by_path( 'application' ), ), ); diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index e0670301c..e6656e89c 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -49,7 +49,7 @@ public static function register_routes() { /** * Renders the user-outbox * - * @param WP_REST_Request $request + * @param WP_REST_Request $request * @return WP_REST_Response */ public static function user_outbox_get( $request ) { @@ -72,19 +72,19 @@ public static function user_outbox_get( $request ) { $json = new stdClass(); $json->{'@context'} = get_context(); - $json->id = get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) ); - $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); - $json->actor = $user->get_id(); - $json->type = 'OrderedCollectionPage'; + $json->id = get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) ); + $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); + $json->actor = $user->get_id(); + $json->type = 'OrderedCollectionPage'; $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) ); // phpcs:ignore $json->totalItems = 0; // phpcs:ignore if ( $user_id > 0 ) { - $count_posts = \count_user_posts( $user_id, $post_types, true ); + $count_posts = \count_user_posts( $user_id, $post_types, true ); $json->totalItems = \intval( $count_posts ); // phpcs:ignore } else { foreach ( $post_types as $post_type ) { - $count_posts = \wp_count_posts( $post_type ); + $count_posts = \wp_count_posts( $post_type ); $json->totalItems += \intval( $count_posts->publish ); // phpcs:ignore } } @@ -148,13 +148,13 @@ public static function request_parameters() { $params = array(); $params['page'] = array( - 'type' => 'integer', + 'type' => 'integer', 'default' => 1, ); $params['user_id'] = array( 'required' => true, - 'type' => 'string', + 'type' => 'string', ); return $params; diff --git a/includes/rest/class-webfinger.php b/includes/rest/class-webfinger.php index 33aed4651..b6bba3d57 100644 --- a/includes/rest/class-webfinger.php +++ b/includes/rest/class-webfinger.php @@ -73,7 +73,7 @@ public static function webfinger( $request ) { $code, array( 'Access-Control-Allow-Origin' => '*', - 'Content-Type' => 'application/jrd+json; charset=' . get_option( 'blog_charset' ), + 'Content-Type' => 'application/jrd+json; charset=' . get_option( 'blog_charset' ), ) ); } @@ -88,8 +88,8 @@ public static function request_parameters() { $params['resource'] = array( 'required' => true, - 'type' => 'string', - 'pattern' => '^(acct:)|^(https?://)(.+)$', + 'type' => 'string', + 'pattern' => '^(acct:)|^(https?://)(.+)$', ); return $params; diff --git a/includes/transformer/class-comment.php b/includes/transformer/class-comment.php index 5b53c5e5d..930f8fe52 100644 --- a/includes/transformer/class-comment.php +++ b/includes/transformer/class-comment.php @@ -191,7 +191,7 @@ protected function get_tag() { $mentions = $this->get_mentions(); if ( $mentions ) { foreach ( $mentions as $mention => $url ) { - $tag = array( + $tag = array( 'type' => 'Mention', 'href' => \esc_url( $url ), 'name' => \esc_html( $mention ), @@ -255,7 +255,7 @@ public function extract_reply_context( $mentions ) { if ( $comment && ! empty( $comment->comment_author_url ) ) { $acct = Webfinger::uri_to_acct( $comment->comment_author_url ); if ( $acct && ! is_wp_error( $acct ) ) { - $acct = str_replace( 'acct:', '@', $acct ); + $acct = str_replace( 'acct:', '@', $acct ); $mentions[ $acct ] = $comment->comment_author_url; } } diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index a80507f04..5f38dca96 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -62,7 +62,7 @@ public function change_wp_user_id( $user_id ) { * @return \Activitypub\Activity\Base_Object The ActivityPub Object */ public function to_object() { - $post = $this->wp_object; + $post = $this->wp_object; $object = parent::to_object(); $published = \strtotime( $post->post_date_gmt ); @@ -109,7 +109,7 @@ protected function get_actor_object() { return $this->actor_object; } - $blog_user = new Blog(); + $blog_user = new Blog(); $this->actor_object = $blog_user; if ( is_single_user() ) { @@ -299,16 +299,17 @@ protected function get_block_attachments( $media, $max_media ) { } $blocks = \parse_blocks( $this->wp_object->post_content ); - $media = self::get_media_from_blocks( $blocks, $media ); + $media = self::get_media_from_blocks( $blocks, $media ); return $media; } /** * Recursively get media IDs from blocks. + * * @param array $blocks The blocks to search for media IDs * @param array $media The media IDs to append new IDs to - * @param int $max_media The maximum number of media to return. + * @param int $max_media The maximum number of media to return. * * @return array The image IDs. */ @@ -443,14 +444,14 @@ protected function get_classic_editor_image_embeds( $max_images ) { if ( 0 === $img_id ) { $count = 0; - $src = preg_replace( '/-(?:\d+x\d+)(\.[a-zA-Z]+)$/', '$1', $src, 1, $count ); + $src = preg_replace( '/-(?:\d+x\d+)(\.[a-zA-Z]+)$/', '$1', $src, 1, $count ); if ( $count > 0 ) { $img_id = \attachment_url_to_postid( $src ); } } if ( 0 === $img_id ) { - $src = preg_replace( '/(\.[a-zA-Z]+)$/', '-scaled$1', $src ); + $src = preg_replace( '/(\.[a-zA-Z]+)$/', '-scaled$1', $src ); $img_id = \attachment_url_to_postid( $src ); } @@ -484,12 +485,12 @@ protected function get_classic_editor_image_attachments( $max_images ) { $images = array(); $query = new \WP_Query( array( - 'post_parent' => $this->wp_object->ID, - 'post_status' => 'inherit', - 'post_type' => 'attachment', + 'post_parent' => $this->wp_object->ID, + 'post_status' => 'inherit', + 'post_type' => 'attachment', 'post_mime_type' => 'image', - 'order' => 'ASC', - 'orderby' => 'menu_order ID', + 'order' => 'ASC', + 'orderby' => 'menu_order ID', 'posts_per_page' => $max_images, ) ); @@ -584,10 +585,10 @@ public static function wp_attachment_to_activity_attachment( $media ) { 'url' => \esc_url( \wp_get_attachment_url( $id ) ), 'name' => \esc_attr( \get_the_title( $id ) ), ); - $meta = wp_get_attachment_metadata( $id ); + $meta = wp_get_attachment_metadata( $id ); // height and width for videos if ( isset( $meta['width'] ) && isset( $meta['height'] ) ) { - $attachment['width'] = \esc_attr( $meta['width'] ); + $attachment['width'] = \esc_attr( $meta['width'] ); $attachment['height'] = \esc_attr( $meta['height'] ); } // @todo: add `icon` support for audio/video attachments. Maybe use post thumbnail? @@ -723,7 +724,7 @@ protected function get_tag() { $post_tags = \get_the_tags( $this->wp_object->ID ); if ( $post_tags ) { foreach ( $post_tags as $post_tag ) { - $tag = array( + $tag = array( 'type' => 'Hashtag', 'href' => \esc_url( \get_tag_link( $post_tag->term_id ) ), 'name' => esc_hashtag( $post_tag->name ), @@ -735,7 +736,7 @@ protected function get_tag() { $mentions = $this->get_mentions(); if ( $mentions ) { foreach ( $mentions as $mention => $url ) { - $tag = array( + $tag = array( 'type' => 'Mention', 'href' => \esc_url( $url ), 'name' => \esc_html( $mention ), diff --git a/integration/class-buddypress.php b/integration/class-buddypress.php index 8d71c1d23..3d5da3e5f 100644 --- a/integration/class-buddypress.php +++ b/integration/class-buddypress.php @@ -15,7 +15,7 @@ public static function init() { } public static function add_user_metadata( $object, $author_id ) { - $object->url = bp_core_get_user_domain( $author_id ); //add BP member profile URL as user URL + $object->url = bp_core_get_user_domain( $author_id ); // add BP member profile URL as user URL // add BuddyPress' cover_image instead of WordPress' header_image $cover_image_url = bp_attachments_get_attachment( 'url', array( 'item_id' => $author_id ) ); @@ -29,8 +29,8 @@ public static function add_user_metadata( $object, $author_id ) { // change profile URL to BuddyPress' profile URL $object->attachment['profile_url'] = array( - 'type' => 'PropertyValue', - 'name' => \__( 'Profile', 'activitypub' ), + 'type' => 'PropertyValue', + 'name' => \__( 'Profile', 'activitypub' ), 'value' => \html_entity_decode( sprintf( '%s', @@ -45,7 +45,7 @@ public static function add_user_metadata( $object, $author_id ) { // replace blog URL on multisite if ( is_multisite() ) { - $user_blogs = get_blogs_of_user( $author_id ); //get sites of user to send as AP metadata + $user_blogs = get_blogs_of_user( $author_id ); // get sites of user to send as AP metadata if ( ! empty( $user_blogs ) ) { unset( $object->attachment['blog_url'] ); @@ -53,8 +53,8 @@ public static function add_user_metadata( $object, $author_id ) { foreach ( $user_blogs as $blog ) { if ( 1 !== $blog->userblog_id ) { $object->attachment[] = array( - 'type' => 'PropertyValue', - 'name' => $blog->blogname, + 'type' => 'PropertyValue', + 'name' => $blog->blogname, 'value' => \html_entity_decode( sprintf( '%s', diff --git a/integration/class-enable-mastodon-apps.php b/integration/class-enable-mastodon-apps.php index cd0869cea..c04b68fa7 100644 --- a/integration/class-enable-mastodon-apps.php +++ b/integration/class-enable-mastodon-apps.php @@ -117,8 +117,8 @@ private static function get_extra_fields( $user_id ) { foreach ( $fields as $field ) { $ret[] = array( - 'name' => $field->post_title, - 'value' => Extra_Fields::get_formatted_content( $field ), + 'name' => $field->post_title, + 'value' => Extra_Fields::get_formatted_content( $field ), ); } @@ -190,29 +190,29 @@ function ( $item ) { $acct = $item->get_url(); } - $account = new Account(); - $account->id = \strval( $item->get__id() ); - $account->username = $item->get_preferred_username(); - $account->acct = $acct; - $account->display_name = $item->get_name(); - $account->url = $item->get_url(); - $account->avatar = $item->get_icon_url(); - $account->avatar_static = $item->get_icon_url(); - $account->created_at = new DateTime( $item->get_published() ); - $account->last_status_at = new DateTime( $item->get_published() ); - $account->note = $item->get_summary(); - $account->header = $item->get_image_url(); - $account->header_static = $item->get_image_url(); + $account = new Account(); + $account->id = \strval( $item->get__id() ); + $account->username = $item->get_preferred_username(); + $account->acct = $acct; + $account->display_name = $item->get_name(); + $account->url = $item->get_url(); + $account->avatar = $item->get_icon_url(); + $account->avatar_static = $item->get_icon_url(); + $account->created_at = new DateTime( $item->get_published() ); + $account->last_status_at = new DateTime( $item->get_published() ); + $account->note = $item->get_summary(); + $account->header = $item->get_image_url(); + $account->header_static = $item->get_image_url(); $account->followers_count = 0; $account->following_count = 0; - $account->statuses_count = 0; - $account->bot = false; - $account->locked = false; - $account->group = false; - $account->discoverable = false; - $account->noindex = false; - $account->fields = array(); - $account->emojis = array(); + $account->statuses_count = 0; + $account->bot = false; + $account->locked = false; + $account->group = false; + $account->discoverable = false; + $account->noindex = false; + $account->fields = array(); + $account->emojis = array(); return $account; }, @@ -260,7 +260,7 @@ public static function api_account_external( $user_data, $user_id ) { public static function api_account_internal( $user_data, $user_id ) { $user_id_to_use = self::maybe_map_user_to_blog( $user_id ); - $user = Users::get_by_id( $user_id_to_use ); + $user = Users::get_by_id( $user_id_to_use ); if ( ! $user || is_wp_error( $user ) ) { return $user_data; @@ -269,21 +269,21 @@ public static function api_account_internal( $user_data, $user_id ) { // convert user to account. $account = new Account(); // even if we have a blog user, maintain the provided user_id so as not to confuse clients - $account->id = (int) $user_id; - $account->username = $user->get_preferred_username(); - $account->acct = $account->username; - $account->display_name = $user->get_name(); - $account->note = $user->get_summary(); + $account->id = (int) $user_id; + $account->username = $user->get_preferred_username(); + $account->acct = $account->username; + $account->display_name = $user->get_name(); + $account->note = $user->get_summary(); $account->source['note'] = wp_strip_all_tags( $account->note, true ); - $account->url = $user->get_url(); + $account->url = $user->get_url(); - $icon = $user->get_icon(); - $account->avatar = $icon['url']; + $icon = $user->get_icon(); + $account->avatar = $icon['url']; $account->avatar_static = $account->avatar; $header = $user->get_image(); if ( $header ) { - $account->header = $header['url']; + $account->header = $header['url']; $account->header_static = $account->header; } @@ -291,13 +291,13 @@ public static function api_account_internal( $user_data, $user_id ) { $post_types = \get_option( 'activitypub_support_post_types', array( 'post' ) ); $query_args = array( - 'post_type' => $post_types, + 'post_type' => $post_types, 'posts_per_page' => 1, ); if ( $user_id > 0 ) { $query_args['author'] = $user_id; } - $posts = \get_posts( $query_args ); + $posts = \get_posts( $query_args ); $account->last_status_at = ! empty( $posts ) ? new DateTime( $posts[0]->post_date_gmt ) : $account->created_at; $account->fields = self::get_extra_fields( $user_id_to_use ); @@ -331,11 +331,11 @@ private static function get_account_for_actor( $uri ) { $acct = substr( $acct, 5 ); } - $account->id = $acct; - $account->username = $acct; - $account->acct = $acct; - $account->display_name = $data['name']; - $account->url = $uri; + $account->id = $acct; + $account->username = $acct; + $account->acct = $acct; + $account->display_name = $data['name']; + $account->url = $uri; if ( ! empty( $data['summary'] ) ) { $account->note = $data['summary']; @@ -412,20 +412,20 @@ public static function api_search( $search_data, $request ) { $acct = $follower->get_url(); } - $account = new Account(); - $account->id = \strval( $follower->get__id() ); - $account->username = $follower->get_preferred_username(); - $account->acct = $acct; - $account->display_name = $follower->get_name(); - $account->url = $follower->get_url(); - $account->uri = $follower->get_id(); - $account->avatar = $follower->get_icon_url(); - $account->avatar_static = $follower->get_icon_url(); - $account->created_at = new DateTime( $follower->get_published() ); + $account = new Account(); + $account->id = \strval( $follower->get__id() ); + $account->username = $follower->get_preferred_username(); + $account->acct = $acct; + $account->display_name = $follower->get_name(); + $account->url = $follower->get_url(); + $account->uri = $follower->get_id(); + $account->avatar = $follower->get_icon_url(); + $account->avatar_static = $follower->get_icon_url(); + $account->created_at = new DateTime( $follower->get_published() ); $account->last_status_at = new DateTime( $follower->get_published() ); - $account->note = $follower->get_summary(); - $account->header = $follower->get_image_url(); - $account->header_static = $follower->get_image_url(); + $account->note = $follower->get_summary(); + $account->header = $follower->get_image_url(); + $account->header_static = $follower->get_image_url(); $search_data['accounts'][] = $account; } @@ -456,7 +456,7 @@ private static function activity_to_status( $item, $account ) { return null; } - $status = new Status(); + $status = new Status(); $status->id = $object['id']; $status->created_at = new DateTime( $object['published'] ); $status->content = $object['content']; @@ -480,20 +480,20 @@ private static function activity_to_status( $item, $account ) { $status->media_attachments = array_map( function ( $attachment ) { $default_attachment = array( - 'url' => null, + 'url' => null, 'mediaType' => null, - 'name' => null, - 'width' => 0, - 'height' => 0, - 'blurhash' => null, + 'name' => null, + 'width' => 0, + 'height' => 0, + 'blurhash' => null, ); $attachment = array_merge( $default_attachment, $attachment ); - $media_attachment = new Media_Attachment(); - $media_attachment->id = $attachment['url']; - $media_attachment->type = strtok( $attachment['mediaType'], '/' ); - $media_attachment->url = $attachment['url']; + $media_attachment = new Media_Attachment(); + $media_attachment->id = $attachment['url']; + $media_attachment->type = strtok( $attachment['mediaType'], '/' ); + $media_attachment->url = $attachment['url']; $media_attachment->preview_url = $attachment['url']; $media_attachment->description = $attachment['name']; if ( $attachment['blurhash'] ) { @@ -545,8 +545,8 @@ public static function api_statuses_external( $statuses, $args ) { $limit = 40; } $activitypub_statuses = array(); - $url = $outbox['first']; - $tries = 0; + $url = $outbox['first']; + $tries = 0; while ( $url ) { if ( ++$tries > 3 ) { break; @@ -557,7 +557,7 @@ public static function api_statuses_external( $statuses, $args ) { return $statuses; } - $new_statuses = array_map( + $new_statuses = array_map( function ( $item ) use ( $account, $args ) { if ( $args['exclude_replies'] ) { if ( isset( $item['object']['inReplyTo'] ) && $item['object']['inReplyTo'] ) { @@ -569,7 +569,7 @@ function ( $item ) use ( $account, $args ) { $posts['orderedItems'] ); $activitypub_statuses = array_merge( $activitypub_statuses, array_filter( $new_statuses ) ); - $url = $posts['next']; + $url = $posts['next']; if ( count( $activitypub_statuses ) >= $limit ) { break; @@ -586,7 +586,7 @@ public static function api_get_replies( $context, $post_id, $url ) { } $replies_url = $meta['replies']['first']['next']; - $replies = Http::get_remote_object( $replies_url, true ); + $replies = Http::get_remote_object( $replies_url, true ); if ( is_wp_error( $replies ) || ! isset( $replies['items'] ) ) { return $context; } @@ -602,7 +602,7 @@ public static function api_get_replies( $context, $post_id, $url ) { } $account = self::get_account_for_actor( $status['attributedTo'] ); - $status = self::activity_to_status( $status, $account ); + $status = self::activity_to_status( $status, $account ); if ( $status ) { $context['descendants'][ $status->id ] = $status; } diff --git a/integration/class-jetpack.php b/integration/class-jetpack.php index b145476a3..d3662364a 100644 --- a/integration/class-jetpack.php +++ b/integration/class-jetpack.php @@ -11,11 +11,11 @@ public static function add_sync_meta( $whitelist ) { if ( ! is_array( $whitelist ) ) { return $whitelist; } - $activitypub_meta_keys = [ + $activitypub_meta_keys = array( 'activitypub_user_id', 'activitypub_inbox', 'activitypub_actor_json', - ]; + ); return \array_merge( $whitelist, $activitypub_meta_keys ); } } diff --git a/integration/class-nodeinfo.php b/integration/class-nodeinfo.php index 02f3a643e..37c32b876 100644 --- a/integration/class-nodeinfo.php +++ b/integration/class-nodeinfo.php @@ -25,7 +25,7 @@ public static function init() { * Extend NodeInfo data * * @param array $nodeinfo NodeInfo data - * @param string The NodeInfo Version + * @param string $version The NodeInfo Version * * @return array The extended array */ @@ -74,7 +74,7 @@ public static function add_nodeinfo2_data( $nodeinfo ) { */ public static function add_wellknown_nodeinfo_data( $data ) { $data['links'][] = array( - 'rel' => 'https://www.w3.org/ns/activitystreams#Application', + 'rel' => 'https://www.w3.org/ns/activitystreams#Application', 'href' => get_rest_url_by_path( 'application' ), ); diff --git a/phpcs.xml b/phpcs.xml index e3d7f5071..9284b5153 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -5,74 +5,39 @@ ./includes/ ./integration/ ./build/ + *\.(inc|css|js|svg) */vendor/* */node_modules/* *.asset.php + + + + + + - - - - - - - - - - - - - + - - - - - error - - - - - - - - - error - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - warning - - - **/*.asset.php - + + + + + + + + + + + + + + + + From 7a19d7377f6db170e4dd5d8056948f5214ef5df9 Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Sun, 29 Sep 2024 16:11:21 -0500 Subject: [PATCH 02/47] PHPCS: Enable MissingShort Sniff (#910) --- includes/activity/class-activity.php | 2 ++ includes/activity/class-actor.php | 2 ++ includes/activity/class-base-object.php | 2 ++ .../activity/extended-object/class-event.php | 32 +++++++++++++++++-- .../activity/extended-object/class-place.php | 9 +++++- includes/transformer/class-factory.php | 4 ++- phpcs.xml | 1 - 7 files changed, 46 insertions(+), 6 deletions(-) diff --git a/includes/activity/class-activity.php b/includes/activity/class-activity.php index e6e3ecbb4..4af3771ee 100644 --- a/includes/activity/class-activity.php +++ b/includes/activity/class-activity.php @@ -22,6 +22,8 @@ class Activity extends Base_Object { ); /** + * The type of the object. + * * @var string */ protected $type = 'Activity'; diff --git a/includes/activity/class-actor.php b/includes/activity/class-actor.php index 8cfdf915b..6ce832bc7 100644 --- a/includes/activity/class-actor.php +++ b/includes/activity/class-actor.php @@ -48,6 +48,8 @@ class Actor extends Base_Object { ); /** + * The type of the object. + * * @var string */ protected $type; diff --git a/includes/activity/class-base-object.php b/includes/activity/class-base-object.php index 112ad4b4c..bdafb319f 100644 --- a/includes/activity/class-base-object.php +++ b/includes/activity/class-base-object.php @@ -45,6 +45,8 @@ class Base_Object { protected $id; /** + * The type of the object. + * * @var string */ protected $type = 'Object'; diff --git a/includes/activity/extended-object/class-event.php b/includes/activity/extended-object/class-event.php index 1a57b7454..4523405e7 100644 --- a/includes/activity/extended-object/class-event.php +++ b/includes/activity/extended-object/class-event.php @@ -118,6 +118,8 @@ class Event extends Base_Object { /** * The Title of the event. + * + * @var string */ protected $name; @@ -145,12 +147,16 @@ class Event extends Base_Object { protected $comments_enabled; /** + * Timezone of the event. + * * @context https://joinmobilizon.org/ns#timezone * @var string */ protected $timezone; /** + * Moderation option for replies. + * * @context https://joinmobilizon.org/ns#repliesModerationOption * @see https://docs.joinmobilizon.org/contribute/activity_pub/#repliesmoderation * @var string @@ -158,6 +164,8 @@ class Event extends Base_Object { protected $replies_moderation_option; /** + * Whether anonymous participation is enabled. + * * @context https://joinmobilizon.org/ns#anonymousParticipationEnabled * @see https://docs.joinmobilizon.org/contribute/activity_pub/#anonymousparticipationenabled * @var bool @@ -165,24 +173,32 @@ class Event extends Base_Object { protected $anonymous_participation_enabled; /** + * The event's category. + * * @context https://schema.org/category - * @var enum + * @var string */ protected $category; /** + * Language of the event. + * * @context https://schema.org/inLanguage - * @var + * @var string */ protected $in_language; /** + * Whether the event is online. + * * @context https://joinmobilizon.org/ns#isOnline * @var bool */ protected $is_online; /** + * The event's status. + * * @context https://www.w3.org/2002/12/cal/ical#status * @var enum */ @@ -199,25 +215,33 @@ class Event extends Base_Object { protected $actor; /** + * The external participation URL. + * * @context https://joinmobilizon.org/ns#externalParticipationUrl * @var string */ protected $external_participation_url; /** + * Indicator of how new members may be able to join. + * * @context https://joinmobilizon.org/ns#joinMode * @see https://docs.joinmobilizon.org/contribute/activity_pub/#joinmode - * @var + * @var string */ protected $join_mode; /** + * The participant count of the event. + * * @context https://joinmobilizon.org/ns#participantCount * @var int */ protected $participant_count; /** + * How many places there can be for an event. + * * @context https://schema.org/maximumAttendeeCapacity * @see https://docs.joinmobilizon.org/contribute/activity_pub/#maximumattendeecapacity * @var int @@ -225,6 +249,8 @@ class Event extends Base_Object { protected $maximum_attendee_capacity; /** + * The number of attendee places for an event that remain unallocated. + * * @context https://schema.org/remainingAttendeeCapacity * @see https://docs.joinmobilizon.org/contribute/activity_pub/#remainignattendeecapacity * @var int diff --git a/includes/activity/extended-object/class-place.php b/includes/activity/extended-object/class-place.php index f84013dfd..4efe66561 100644 --- a/includes/activity/extended-object/class-place.php +++ b/includes/activity/extended-object/class-place.php @@ -63,19 +63,26 @@ class Place extends Base_Object { protected $longitude; /** + * The radius from the given latitude and longitude for a Place. + * * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-radius * @var float */ protected $radius; /** + * Specifies the measurement units for the `radius` and `altitude` properties. + * * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-units * @var string */ protected $units; /** - * @var Postal_Address|string + * The address of the place. + * + * @see https://schema.org/PostalAddress + * @var array|string */ protected $address; diff --git a/includes/transformer/class-factory.php b/includes/transformer/class-factory.php index b21de7f92..0b026c2b5 100644 --- a/includes/transformer/class-factory.php +++ b/includes/transformer/class-factory.php @@ -12,7 +12,9 @@ */ class Factory { /** - * @param mixed $object The object to transform + * Get the transformer for a given object. + * + * @param mixed $object The object to transform. * @return \Activitypub\Transformer|\WP_Error The transformer to use, or an error. */ public static function get_transformer( $object ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound diff --git a/phpcs.xml b/phpcs.xml index 9284b5153..5aa9c0b25 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -24,7 +24,6 @@ - From f84683be1a0f41d57ae99957842be2252130a155 Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Sun, 29 Sep 2024 16:12:01 -0500 Subject: [PATCH 03/47] PHPCS: Enable NonceVerification sniff (#908) --- includes/class-blocks.php | 1 + phpcs.xml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-blocks.php b/includes/class-blocks.php index b87bd5086..63f532d53 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -52,6 +52,7 @@ public static function enqueue_editor_assets() { */ public static function handle_in_reply_to_get_param() { // only load the script if the in_reply_to GET param is set, action happens there, not here. + // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! isset( $_GET['in_reply_to'] ) ) { return; } diff --git a/phpcs.xml b/phpcs.xml index 5aa9c0b25..b6b245835 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -29,7 +29,6 @@ - From 91a3799a4d122b95ee5b91025c15116917eb54df Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Sun, 29 Sep 2024 16:12:31 -0500 Subject: [PATCH 04/47] PHPCS: Enable NotPrepared check (#906) --- includes/functions.php | 11 ++++++++--- phpcs.xml | 1 - 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 546f2b0c1..2ad653c7e 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -649,9 +649,14 @@ function get_active_users( $duration = 1 ) { if ( false === $count ) { global $wpdb; - $query = "SELECT COUNT( DISTINCT post_author ) FROM {$wpdb->posts} WHERE post_type = 'post' AND post_status = 'publish' AND post_date <= DATE_SUB( NOW(), INTERVAL %d MONTH )"; - $query = $wpdb->prepare( $query, $duration ); - $count = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $count = $wpdb->get_var( + $wpdb->prepare( + "SELECT COUNT( DISTINCT post_author ) FROM {$wpdb->posts} WHERE post_type = 'post' AND post_status = 'publish' AND post_date <= DATE_SUB( NOW(), INTERVAL %d MONTH )", + $duration + ) + ); set_transient( $transient_key, $count, DAY_IN_SECONDS ); } diff --git a/phpcs.xml b/phpcs.xml index b6b245835..0bb957b12 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -30,7 +30,6 @@ - From d7bc9c78a0d1c092eee88687fbc56b045694b5dc Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 30 Sep 2024 09:24:11 +0200 Subject: [PATCH 05/47] fixed fatal error if remote-object is WP_Error --- includes/class-activity-dispatcher.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 6471925d5..08dc2154d 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -292,7 +292,11 @@ public static function add_inboxes_of_replied_urls( $inboxes, $user_id, $activit foreach ( $in_reply_to as $url ) { $object = Http::get_remote_object( $url ); - if ( ! $object || empty( $object['attributedTo'] ) ) { + if ( + ! $object || + \is_wp_error( $object ) || + empty( $object['attributedTo'] ) + ) { continue; } From f40905dd859a15b746aa6e883f39e94d0146754b Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Mon, 30 Sep 2024 05:06:59 -0500 Subject: [PATCH 06/47] PHPCS: Enable GetMetaSingle sniff (#907) --- includes/collection/class-followers.php | 2 +- includes/functions.php | 2 +- includes/model/class-follower.php | 2 +- phpcs.xml | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 2df376c17..8ea700b5b 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -48,7 +48,7 @@ public static function add_follower( $user_id, $actor ) { return $id; } - $post_meta = get_post_meta( $id, 'activitypub_user_id' ); + $post_meta = get_post_meta( $id, 'activitypub_user_id', false ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict if ( is_array( $post_meta ) && ! in_array( $user_id, $post_meta ) ) { diff --git a/includes/functions.php b/includes/functions.php index 2ad653c7e..8a2934a62 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -926,7 +926,7 @@ function get_masked_wp_version() { * @return array The enclosures. */ function get_enclosures( $post_id ) { - $enclosures = get_post_meta( $post_id, 'enclosure' ); + $enclosures = get_post_meta( $post_id, 'enclosure', false ); if ( ! $enclosures ) { return array(); diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 92ef1717f..21565911f 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -31,7 +31,7 @@ class Follower extends Actor { * @return mixed */ public function get_errors() { - return get_post_meta( $this->_id, 'activitypub_errors' ); + return get_post_meta( $this->_id, 'activitypub_errors', false ); } /** diff --git a/phpcs.xml b/phpcs.xml index 0bb957b12..2ac2d8a51 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -29,7 +29,6 @@ - From cefed604cec81ddf2ae5511c55c30c5771def9b6 Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Mon, 30 Sep 2024 05:07:52 -0500 Subject: [PATCH 07/47] PHPCS: Enable LongNotCapital sniff (#913) --- includes/class-scheduler.php | 2 +- phpcs.xml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 631da8d77..a87c381ae 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -176,7 +176,7 @@ public static function schedule_post_activity( $new_status, $old_status, $post ) /** * Schedule Comment Activities * - * transition_comment_status() + * @see transition_comment_status() * * @param string $new_status New comment status. * @param string $old_status Old comment status. diff --git a/phpcs.xml b/phpcs.xml index 2ac2d8a51..20f911615 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -25,7 +25,6 @@ - From 711217715c04e467ec097ec2da621d6bdbe75a08 Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Mon, 30 Sep 2024 05:08:18 -0500 Subject: [PATCH 08/47] PHPCS: Enable CommentedOutCode sniff (#914) --- includes/class-hashtag.php | 2 +- includes/class-link.php | 2 +- includes/collection/class-interactions.php | 2 +- phpcs.xml | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/includes/class-hashtag.php b/includes/class-hashtag.php index 7344963dc..a7b853eff 100644 --- a/includes/class-hashtag.php +++ b/includes/class-hashtag.php @@ -28,7 +28,7 @@ public static function init() { * @return array the activity object array */ public static function filter_activity_object( $object_array ) { - /* + /* phpcs:ignore Squiz.PHP.CommentedOutCode.Found Removed until this is merged: https://github.com/mastodon/mastodon/pull/28629 if ( ! empty( $object_array['summary'] ) ) { $object_array['summary'] = self::the_content( $object_array['summary'] ); diff --git a/includes/class-link.php b/includes/class-link.php index 94b63cefd..2e48d1be4 100644 --- a/includes/class-link.php +++ b/includes/class-link.php @@ -24,7 +24,7 @@ public static function init() { * @return array the activity object array */ public static function filter_activity_object( $object_array ) { - /* + /* phpcs:ignore Squiz.PHP.CommentedOutCode.Found Removed until this is merged: https://github.com/mastodon/mastodon/pull/28629 if ( ! empty( $object_array['summary'] ) ) { $object_array['summary'] = self::the_content( $object_array['summary'] ); diff --git a/includes/collection/class-interactions.php b/includes/collection/class-interactions.php index 00e1f579d..802070a6f 100644 --- a/includes/collection/class-interactions.php +++ b/includes/collection/class-interactions.php @@ -310,7 +310,7 @@ function () { if ( 1 === $state ) { return $commentdata; } else { - return $state; // Either `WP_Comment`, `false` or a `WP_Error` instance or `0` or `1`! + return $state; // Either WP_Comment, false, a WP_Error, 0, or 1! } } } diff --git a/phpcs.xml b/phpcs.xml index 20f911615..f961b5841 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -26,7 +26,6 @@ - From ad1bcbd475c91a7ef1367d349db1f1c259e406dc Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 2 Oct 2024 11:35:08 +0200 Subject: [PATCH 09/47] version bump --- CHANGELOG.md | 12 ++++++++++++ README.md | 8 +++++++- activitypub.php | 4 ++-- readme.txt | 8 +++++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a493d9f..034397084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.3.2] - 2024-10-02 + +### Fixed + +* Keep priority of Icons +* Fatal error if remote-object is `WP_Error` + +### Improved + +* Adopt WordPress PHP Coding Standards + ## [3.3.1] - 2024-09-26 ### Fixed @@ -947,6 +958,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * initial +[3.3.2]: https://github.com/Automattic/wordpress-activitypub/compare/3.3.1...3.3.2 [3.3.1]: https://github.com/Automattic/wordpress-activitypub/compare/3.3.0...3.3.1 [3.3.0]: https://github.com/Automattic/wordpress-activitypub/compare/3.2.5...3.3.0 [3.2.5]: https://github.com/Automattic/wordpress-activitypub/compare/3.2.4...3.2.5 diff --git a/README.md b/README.md index 5a65593ba..1aebc86b2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ **Tags:** OStatus, fediverse, activitypub, activitystream **Requires at least:** 5.5 **Tested up to:** 6.6 -**Stable tag:** 3.3.1 +**Stable tag:** 3.3.2 **Requires PHP:** 7.0 **License:** MIT **License URI:** http://opensource.org/licenses/MIT @@ -150,6 +150,12 @@ For reasons of data protection, it is not possible to see the followers of other ## Changelog ## +### 3.3.2 ### + +* Fixed: Keep priority of Icons +* Fixed: Fatal error if remote-object is `WP_Error` +* Improved: Adopt WordPress PHP Coding Standards + ### 3.3.1 ### * Fixed: PHP Warnings diff --git a/activitypub.php b/activitypub.php index 9b6be1f11..2cb7ad576 100644 --- a/activitypub.php +++ b/activitypub.php @@ -3,7 +3,7 @@ * Plugin Name: ActivityPub * Plugin URI: https://github.com/pfefferle/wordpress-activitypub/ * Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. - * Version: 3.3.1 + * Version: 3.3.2 * Author: Matthias Pfefferle & Automattic * Author URI: https://automattic.com/ * License: MIT @@ -23,7 +23,7 @@ require_once __DIR__ . '/includes/compat.php'; require_once __DIR__ . '/includes/functions.php'; -\define( 'ACTIVITYPUB_PLUGIN_VERSION', '3.3.1' ); +\define( 'ACTIVITYPUB_PLUGIN_VERSION', '3.3.2' ); /** * Initialize the plugin constants. diff --git a/readme.txt b/readme.txt index 1950e746b..72f72284b 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nur Tags: OStatus, fediverse, activitypub, activitystream Requires at least: 5.5 Tested up to: 6.6 -Stable tag: 3.3.1 +Stable tag: 3.3.2 Requires PHP: 7.0 License: MIT License URI: http://opensource.org/licenses/MIT @@ -148,6 +148,12 @@ For reasons of data protection, it is not possible to see the followers of other == Changelog == += 3.3.2 = + +* Fixed: Keep priority of Icons +* Fixed: Fatal error if remote-object is `WP_Error` +* Improved: Adopt WordPress PHP Coding Standards + = 3.3.1 = * Fixed: PHP Warnings From a2d6b9dc8efa276b9673172968c2777228e2337d Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 2 Oct 2024 18:46:52 +0200 Subject: [PATCH 10/47] fix #871 (#920) --- includes/class-activity-dispatcher.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 08dc2154d..484af40e5 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -144,7 +144,6 @@ public static function send_profile_update( $user_id ) { // build the update $activity = new Activity(); - $activity->set_id( $user->get_url() . '#update' ); $activity->set_type( 'Update' ); $activity->set_actor( $user->get_url() ); $activity->set_object( $user->get_url() ); From 1af17c4b1d8635b9b3710ba82a2f9c4a922efe2e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 2 Oct 2024 18:47:20 +0200 Subject: [PATCH 11/47] Prepare multi-lang support (#919) This PR adds Maps for "name" (title) and "summary". --- includes/transformer/class-post.php | 106 ++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 23 deletions(-) diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 5f38dca96..ca996cf89 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -65,33 +65,11 @@ public function to_object() { $post = $this->wp_object; $object = parent::to_object(); - $published = \strtotime( $post->post_date_gmt ); - - $object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) ); - - $updated = \strtotime( $post->post_modified_gmt ); - - if ( $updated > $published ) { - $object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) ); - } - - $object->set_content_map( - array( - $this->get_locale() => $this->get_content(), - ) - ); - - $object->set_to( - array( - 'https://www.w3.org/ns/activitystreams#Public', - $this->get_actor_object()->get_followers(), - ) - ); - $content_warning = get_content_warning( $post ); if ( ! empty( $content_warning ) ) { $object->set_sensitive( true ); $object->set_summary( $content_warning ); + $object->set_summary_map( null ); } return $object; @@ -934,6 +912,88 @@ public function get_in_reply_to() { return null; } + /** + * Returns the recipient of the post. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-to + * + * @return array The recipient URLs of the post. + */ + public function get_to() { + return array( + 'https://www.w3.org/ns/activitystreams#Public', + $this->get_actor_object()->get_followers(), + ); + } + + /** + * Returns the published date of the post. + * + * @return string The published date of the post. + */ + public function get_published() { + $published = \strtotime( $this->wp_object->post_date_gmt ); + + return \gmdate( 'Y-m-d\TH:i:s\Z', $published ); + } + + /** + * Returns the updated date of the post. + * + * @return string|null The updated date of the post. + */ + public function get_updated() { + $published = \strtotime( $this->wp_object->post_date_gmt ); + $updated = \strtotime( $this->wp_object->post_modified_gmt ); + + if ( $updated > $published ) { + return \gmdate( 'Y-m-d\TH:i:s\Z', $updated ); + } + + return null; + } + + /** + * Returns the content map for the post. + * + * @return array The content map for the post. + */ + public function get_content_map() { + return array( + $this->get_locale() => $this->get_content(), + ); + } + + /** + * Returns the name map for the post. + * + * @return array The name map for the post. + */ + public function get_name_map() { + if ( ! $this->get_name() ) { + return null; + } + + return array( + $this->get_locale() => $this->get_name(), + ); + } + + /** + * Returns the summary map for the post. + * + * @return array The summary map for the post. + */ + public function get_summary_map() { + if ( ! $this->get_summary() ) { + return null; + } + + return array( + $this->get_locale() => $this->get_summary(), + ); + } + /** * Transform Embed blocks to block level link. * From e68e62305105baeb806cd49fb015b10e0b4f445e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= <99024746+Menrath@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:03:17 +0200 Subject: [PATCH 12/47] fix phpdoc return type for transformer factory's get_transformer (#921) --- includes/transformer/class-factory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/transformer/class-factory.php b/includes/transformer/class-factory.php index 0b026c2b5..2824072c5 100644 --- a/includes/transformer/class-factory.php +++ b/includes/transformer/class-factory.php @@ -15,7 +15,7 @@ class Factory { * Get the transformer for a given object. * * @param mixed $object The object to transform. - * @return \Activitypub\Transformer|\WP_Error The transformer to use, or an error. + * @return \Activitypub\Transformer\Base|\WP_Error The transformer to use, or an error. */ public static function get_transformer( $object ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound if ( ! \is_object( $object ) ) { From a2e7729d4d646816f8bc66dbdf8ec8b39dc448ec Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Thu, 3 Oct 2024 10:19:06 -0500 Subject: [PATCH 13/47] PHPCS: Enable NoReservedKeywordParameterNames sniff (#909) * PHPCS: Enable NoReservedKeywordParameterNames sniff * Fix parameter doc Co-authored-by: Matthias Pfefferle * Rename to $data since the type is ambiguous Props @pfefferle. * Name it $uri for consistency Props @pfefferle. --------- Co-authored-by: Matthias Pfefferle --- includes/activity/class-activity.php | 40 ++++++++++++------------- includes/activity/class-base-object.php | 18 +++++------ includes/class-blocks.php | 6 ++-- includes/collection/class-users.php | 24 +++++++-------- includes/functions.php | 12 ++++---- includes/handler/class-create.php | 18 +++++------ includes/handler/class-follow.php | 16 +++++----- includes/handler/class-update.php | 11 ++++--- includes/rest/class-following.php | 12 ++++---- integration/class-buddypress.php | 14 ++++----- integration/class-webfinger.php | 31 ++++++++++--------- phpcs.xml | 1 - 12 files changed, 100 insertions(+), 103 deletions(-) diff --git a/includes/activity/class-activity.php b/includes/activity/class-activity.php index 4af3771ee..f8db1945e 100644 --- a/includes/activity/class-activity.php +++ b/includes/activity/class-activity.php @@ -145,49 +145,49 @@ class Activity extends Base_Object { * * @see https://www.w3.org/TR/activitypub/#object-without-create * - * @param string|Base_Objectr|Link|null $object + * @param array|string|Base_Object|Link|null $data Activity object. * * @return void */ - public function set_object( $object ) { + public function set_object( $data ) { // convert array to object - if ( is_array( $object ) ) { - $object = self::init_from_array( $object ); + if ( is_array( $data ) ) { + $data = self::init_from_array( $data ); } // set object - $this->set( 'object', $object ); + $this->set( 'object', $data ); - if ( ! is_object( $object ) ) { + if ( ! is_object( $data ) ) { return; } foreach ( array( 'to', 'bto', 'cc', 'bcc', 'audience' ) as $i ) { - $this->set( $i, $object->get( $i ) ); + $this->set( $i, $data->get( $i ) ); } - if ( $object->get_published() && ! $this->get_published() ) { - $this->set( 'published', $object->get_published() ); + if ( $data->get_published() && ! $this->get_published() ) { + $this->set( 'published', $data->get_published() ); } - if ( $object->get_updated() && ! $this->get_updated() ) { - $this->set( 'updated', $object->get_updated() ); + if ( $data->get_updated() && ! $this->get_updated() ) { + $this->set( 'updated', $data->get_updated() ); } - if ( $object->get_attributed_to() && ! $this->get_actor() ) { - $this->set( 'actor', $object->get_attributed_to() ); + if ( $data->get_attributed_to() && ! $this->get_actor() ) { + $this->set( 'actor', $data->get_attributed_to() ); } - if ( $object->get_in_reply_to() ) { - $this->set( 'in_reply_to', $object->get_in_reply_to() ); + if ( $data->get_in_reply_to() ) { + $this->set( 'in_reply_to', $data->get_in_reply_to() ); } - if ( $object->get_id() && ! $this->get_id() ) { - $id = strtok( $object->get_id(), '#' ); - if ( $object->get_updated() ) { - $updated = $object->get_updated(); + if ( $data->get_id() && ! $this->get_id() ) { + $id = strtok( $data->get_id(), '#' ); + if ( $data->get_updated() ) { + $updated = $data->get_updated(); } else { - $updated = $object->get_published(); + $updated = $data->get_published(); } $this->set( 'id', $id . '#activity-' . strtolower( $this->get_type() ) . '-' . $updated ); } diff --git a/includes/activity/class-base-object.php b/includes/activity/class-base-object.php index bdafb319f..ce1799369 100644 --- a/includes/activity/class-base-object.php +++ b/includes/activity/class-base-object.php @@ -592,20 +592,20 @@ public static function init_from_json( $json ) { } /** - * Convert JSON input to an array. + * Convert input array to a Base_Object. * - * @return string The object array. + * @param array $data The object array. * - * @return \Activitypub\Activity\Base_Object An Object built from the JSON string. + * @return Base_Object|WP_Error An Object built from the input array or WP_Error when it's not an array. */ - public static function init_from_array( $array ) { - if ( ! is_array( $array ) ) { + public static function init_from_array( $data ) { + if ( ! is_array( $data ) ) { return new WP_Error( 'invalid_array', __( 'Invalid array', 'activitypub' ), array( 'status' => 404 ) ); } $object = new static(); - foreach ( $array as $key => $value ) { + foreach ( $data as $key => $value ) { $key = camel_to_snake_case( $key ); call_user_func( array( $object, 'set_' . $key ), $value ); } @@ -627,10 +627,10 @@ public function from_json( $json ) { /** * Convert JSON input to an array and pre-fill the object. * - * @param array $array The array. + * @param array $data The array. */ - public function from_array( $array ) { - foreach ( $array as $key => $value ) { + public function from_array( $data ) { + foreach ( $data as $key => $value ) { if ( $value ) { $key = camel_to_snake_case( $key ); call_user_func( array( $this, 'set_' . $key ), $value ); diff --git a/includes/class-blocks.php b/includes/class-blocks.php index 63f532d53..cf7261bc8 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -154,12 +154,12 @@ private static function get_user_id( $user_string ) { /** * Filter an array by a list of keys. * - * @param array $array The array to filter. + * @param array $data The array to filter. * @param array $keys The keys to keep. * @return array The filtered array. */ - protected static function filter_array_by_keys( $array, $keys ) { - return array_intersect_key( $array, array_flip( $keys ) ); + protected static function filter_array_by_keys( $data, $keys ) { + return array_intersect_key( $data, array_flip( $keys ) ); } /** diff --git a/includes/collection/class-users.php b/includes/collection/class-users.php index d6e70cec5..06fe6e8eb 100644 --- a/includes/collection/class-users.php +++ b/includes/collection/class-users.php @@ -136,17 +136,17 @@ public static function get_by_username( $username ) { /** * Get the User by resource. * - * @param string $resource The User-Resource. + * @param string $uri The User-Resource. * * @return \Acitvitypub\Model\User The User. */ - public static function get_by_resource( $resource ) { - $resource = object_to_uri( $resource ); + public static function get_by_resource( $uri ) { + $uri = object_to_uri( $uri ); $scheme = 'acct'; $match = array(); // try to extract the scheme and the host - if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $resource, $match ) ) { + if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $uri, $match ) ) { // extract the scheme $scheme = \esc_attr( $match[1] ); } @@ -155,7 +155,7 @@ public static function get_by_resource( $resource ) { // check for http(s) URIs case 'http': case 'https': - $resource_path = \wp_parse_url( $resource, PHP_URL_PATH ); + $resource_path = \wp_parse_url( $uri, PHP_URL_PATH ); if ( $resource_path ) { $blog_path = \wp_parse_url( \home_url(), PHP_URL_PATH ); @@ -176,7 +176,7 @@ public static function get_by_resource( $resource ) { } // check for http(s)://blog.example.com/author/username - $user_id = url_to_authorid( $resource ); + $user_id = url_to_authorid( $uri ); if ( $user_id ) { return self::get_by_id( $user_id ); @@ -184,8 +184,8 @@ public static function get_by_resource( $resource ) { // check for http(s)://blog.example.com/ if ( - normalize_url( site_url() ) === normalize_url( $resource ) || - normalize_url( home_url() ) === normalize_url( $resource ) + normalize_url( site_url() ) === normalize_url( $uri ) || + normalize_url( home_url() ) === normalize_url( $uri ) ) { return self::get_by_id( self::BLOG_USER_ID ); } @@ -197,9 +197,9 @@ public static function get_by_resource( $resource ) { ); // check for acct URIs case 'acct': - $resource = \str_replace( 'acct:', '', $resource ); - $identifier = \substr( $resource, 0, \strrpos( $resource, '@' ) ); - $host = normalize_host( \substr( \strrchr( $resource, '@' ), 1 ) ); + $uri = \str_replace( 'acct:', '', $uri ); + $identifier = \substr( $uri, 0, \strrpos( $uri, '@' ) ); + $host = normalize_host( \substr( \strrchr( $uri, '@' ), 1 ) ); $blog_host = normalize_host( \wp_parse_url( \home_url( '/' ), \PHP_URL_HOST ) ); if ( $blog_host !== $host ) { @@ -228,7 +228,7 @@ public static function get_by_resource( $resource ) { /** * Get the User by resource. * - * @param string $resource The User-Resource. + * @param string $id The User-Resource. * * @return \Acitvitypub\Model\User The User. */ diff --git a/includes/functions.php b/includes/functions.php index 8a2934a62..a4e873129 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -292,13 +292,13 @@ function snake_to_camel_case( $string ) { /** * Escapes a Tag, to be used as a hashtag. * - * @param string $string The string to escape. + * @param string $input The string to escape. * * @return string The escaped hastag. */ -function esc_hashtag( $string ) { +function esc_hashtag( $input ) { - $hashtag = \wp_specialchars_decode( $string, ENT_QUOTES ); + $hashtag = \wp_specialchars_decode( $input, ENT_QUOTES ); // Remove all characters that are not letters, numbers, or underscores. $hashtag = \preg_replace( '/emoji-regex(*SKIP)(?!)|[^\p{L}\p{Nd}_]+/u', '_', $hashtag ); @@ -306,7 +306,7 @@ function esc_hashtag( $string ) { $hashtag = preg_replace_callback( '/_(.)/', function ( $matches ) { - return '' . strtoupper( $matches[1] ); + return strtoupper( $matches[1] ); }, $hashtag ); @@ -319,9 +319,9 @@ function ( $matches ) { * Allow defining your own custom hashtag generation rules. * * @param string $hashtag The hashtag to be returned. - * @param string $string The original string. + * @param string $input The original string. */ - $hashtag = apply_filters( 'activitypub_esc_hashtag', $hashtag, $string ); + $hashtag = apply_filters( 'activitypub_esc_hashtag', $hashtag, $input ); return esc_html( $hashtag ); } diff --git a/includes/handler/class-create.php b/includes/handler/class-create.php index 1e48c6097..35200ee3a 100644 --- a/includes/handler/class-create.php +++ b/includes/handler/class-create.php @@ -33,35 +33,35 @@ public static function init() { /** * Handles "Create" requests * - * @param array $array The activity-object - * @param int $user_id The id of the local blog-user - * @param Activitypub\Activity $object The activity object + * @param array $activity The activity-object + * @param int $user_id The id of the local blog-user + * @param Activitypub\Activity $activity_object The activity object * * @return void */ - public static function handle_create( $array, $user_id, $object = null ) { + public static function handle_create( $activity, $user_id, $activity_object = null ) { // check if Activity is public or not - if ( ! is_activity_public( $array ) ) { + if ( ! is_activity_public( $activity ) ) { // @todo maybe send email return; } - $check_dupe = object_id_to_comment( $array['object']['id'] ); + $check_dupe = object_id_to_comment( $activity['object']['id'] ); // if comment exists, call update action if ( $check_dupe ) { - \do_action( 'activitypub_inbox_update', $array, $user_id, $object ); + \do_action( 'activitypub_inbox_update', $activity, $user_id, $activity_object ); return; } - $state = Interactions::add_comment( $array ); + $state = Interactions::add_comment( $activity ); $reaction = null; if ( $state && ! \is_wp_error( $state ) ) { $reaction = \get_comment( $state ); } - \do_action( 'activitypub_handled_create', $array, $user_id, $state, $reaction ); + \do_action( 'activitypub_handled_create', $activity, $user_id, $state, $reaction ); } /** diff --git a/includes/handler/class-follow.php b/includes/handler/class-follow.php index ed29dadf3..c22a9362d 100644 --- a/includes/handler/class-follow.php +++ b/includes/handler/class-follow.php @@ -72,14 +72,14 @@ public static function handle_follow( $activity ) { /** * Send Accept response * - * @param string $actor The Actor URL - * @param array $object The Activity object - * @param int $user_id The ID of the WordPress User - * @param Activitypub\Model\Follower $follower The Follower object + * @param string $actor The Actor URL + * @param array $activity_object The Activity object + * @param int $user_id The ID of the WordPress User + * @param Activitypub\Model\Follower $follower The Follower object * * @return void */ - public static function send_follow_response( $actor, $object, $user_id, $follower ) { + public static function send_follow_response( $actor, $activity_object, $user_id, $follower ) { if ( \is_wp_error( $follower ) ) { // it is not even possible to send a "Reject" because // we can not get the Remote-Inbox @@ -87,8 +87,8 @@ public static function send_follow_response( $actor, $object, $user_id, $followe } // only send minimal data - $object = array_intersect_key( - $object, + $activity_object = array_intersect_key( + $activity_object, array_flip( array( 'id', @@ -107,7 +107,7 @@ public static function send_follow_response( $actor, $object, $user_id, $followe // send "Accept" activity $activity = new Activity(); $activity->set_type( 'Accept' ); - $activity->set_object( $object ); + $activity->set_object( $activity_object ); $activity->set_actor( $user->get_id() ); $activity->set_to( $actor ); $activity->set_id( $user->get_id() . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) . '-' . \time() ); diff --git a/includes/handler/class-update.php b/includes/handler/class-update.php index 426d8ec53..83f1390f8 100644 --- a/includes/handler/class-update.php +++ b/includes/handler/class-update.php @@ -23,11 +23,10 @@ public static function init() { /** * Handle "Update" requests * - * @param array $array The activity-object - * @param int $user_id The id of the local blog-user + * @param array $activity The activity-object */ - public static function handle_update( $array ) { - $object_type = isset( $array['object']['type'] ) ? $array['object']['type'] : ''; + public static function handle_update( $activity ) { + $object_type = isset( $activity['object']['type'] ) ? $activity['object']['type'] : ''; switch ( $object_type ) { // Actor Types @@ -37,7 +36,7 @@ public static function handle_update( $array ) { case 'Organization': case 'Service': case 'Application': - self::update_actor( $array ); + self::update_actor( $activity ); break; // Object and Link Types // @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types @@ -48,7 +47,7 @@ public static function handle_update( $array ) { case 'Video': case 'Event': case 'Document': - self::update_interaction( $array ); + self::update_interaction( $activity ); break; // Minimal Activity // @see https://www.w3.org/TR/activitystreams-core/#example-1 diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index e8e0ca823..e3e22cc5a 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -111,22 +111,22 @@ public static function request_parameters() { * Add the Blog Authors to the following list of the Blog Actor * if Blog not in single mode. * - * @param array $array The array of following urls. - * @param User $user The user object. + * @param array $follow_list The array of following urls. + * @param \Activitypub\Model\User $user The user object. * * @return array The array of following urls. */ - public static function default_following( $array, $user ) { + public static function default_following( $follow_list, $user ) { if ( 0 !== $user->get__id() || is_single_user() ) { - return $array; + return $follow_list; } $users = User_Collection::get_collection(); foreach ( $users as $user ) { - $array[] = $user->get_url(); + $follow_list[] = $user->get_url(); } - return $array; + return $follow_list; } } diff --git a/integration/class-buddypress.php b/integration/class-buddypress.php index 3d5da3e5f..14afbe4db 100644 --- a/integration/class-buddypress.php +++ b/integration/class-buddypress.php @@ -14,21 +14,21 @@ public static function init() { \add_filter( 'activitypub_json_author_array', array( self::class, 'add_user_metadata' ), 11, 2 ); } - public static function add_user_metadata( $object, $author_id ) { - $object->url = bp_core_get_user_domain( $author_id ); // add BP member profile URL as user URL + public static function add_user_metadata( $author, $author_id ) { + $author->url = bp_core_get_user_domain( $author_id ); // add BP member profile URL as user URL // add BuddyPress' cover_image instead of WordPress' header_image $cover_image_url = bp_attachments_get_attachment( 'url', array( 'item_id' => $author_id ) ); if ( $cover_image_url ) { - $object->image = array( + $author->image = array( 'type' => 'Image', 'url' => $cover_image_url, ); } // change profile URL to BuddyPress' profile URL - $object->attachment['profile_url'] = array( + $author->attachment['profile_url'] = array( 'type' => 'PropertyValue', 'name' => \__( 'Profile', 'activitypub' ), 'value' => \html_entity_decode( @@ -48,11 +48,11 @@ public static function add_user_metadata( $object, $author_id ) { $user_blogs = get_blogs_of_user( $author_id ); // get sites of user to send as AP metadata if ( ! empty( $user_blogs ) ) { - unset( $object->attachment['blog_url'] ); + unset( $author->attachment['blog_url'] ); foreach ( $user_blogs as $blog ) { if ( 1 !== $blog->userblog_id ) { - $object->attachment[] = array( + $author->attachment[] = array( 'type' => 'PropertyValue', 'name' => $blog->blogname, 'value' => \html_entity_decode( @@ -71,6 +71,6 @@ public static function add_user_metadata( $object, $author_id ) { } } - return $object; + return $author; } } diff --git a/integration/class-webfinger.php b/integration/class-webfinger.php index 2ad4f281e..f566e523a 100644 --- a/integration/class-webfinger.php +++ b/integration/class-webfinger.php @@ -23,49 +23,48 @@ public static function init() { /** * Add WebFinger discovery links * - * @param array $array the jrd array - * @param string $resource the WebFinger resource - * @param WP_User $user the WordPress user + * @param array $jrd the jrd array + * @param string $uri the WebFinger resource + * @param WP_User $user the WordPress user * * @return array the jrd array */ - public static function add_user_discovery( $array, $resource, $user ) { + public static function add_user_discovery( $jrd, $uri, $user ) { $user = User_Collection::get_by_id( $user->ID ); if ( ! $user || is_wp_error( $user ) ) { - return $array; + return $jrd; } - $array['subject'] = sprintf( 'acct:%s', $user->get_webfinger() ); + $jrd['subject'] = sprintf( 'acct:%s', $user->get_webfinger() ); - $array['aliases'][] = $user->get_url(); - $array['aliases'][] = $user->get_alternate_url(); + $jrd['aliases'][] = $user->get_url(); + $jrd['aliases'][] = $user->get_alternate_url(); - $array['links'][] = array( + $jrd['links'][] = array( 'rel' => 'self', 'type' => 'application/activity+json', 'href' => $user->get_url(), ); - $array['links'][] = array( + $jrd['links'][] = array( 'rel' => 'http://ostatus.org/schema/1.0/subscribe', 'template' => get_rest_url_by_path( 'interactions?uri={uri}' ), ); - return $array; + return $jrd; } /** * Add WebFinger discovery links * - * @param array $array the jrd array - * @param string $resource the WebFinger resource - * @param WP_User $user the WordPress user + * @param array $jrd the jrd array + * @param string $uri the WebFinger resource * * @return array the jrd array */ - public static function add_pseudo_user_discovery( $array, $resource ) { - $user = User_Collection::get_by_resource( $resource ); + public static function add_pseudo_user_discovery( $jrd, $uri ) { + $user = User_Collection::get_by_resource( $uri ); if ( \is_wp_error( $user ) ) { return $user; diff --git a/phpcs.xml b/phpcs.xml index f961b5841..8a80286d8 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -26,7 +26,6 @@ - From 204afdfb9d4745ecc4ae5561df498a363ac8d8c3 Mon Sep 17 00:00:00 2001 From: Jeremy Herve Date: Fri, 4 Oct 2024 09:20:53 +0200 Subject: [PATCH 14/47] Issue templates: add new issue type option (#924) This was added as an option as part of this set of changes to GitHub issues: https://github.blog/changelog/2024-10-01-evolving-github-issues-public-preview/ Related post: pdWQjU-WT-p2 --- .github/ISSUE_TEMPLATE/bug_report.yml | 3 ++- .github/ISSUE_TEMPLATE/feature_request.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 3c72a8601..c5c3d27ce 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,7 @@ name: Bug Report description: Helps us improve our product! -labels: "Needs triage, [Type] Bug" +labels: [ 'Needs triage', '[Type] Bug' ] +type: 'Bug' body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 2fc3601d6..a4017ae9f 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -2,6 +2,7 @@ name: Feature Request description: Suggest an idea for the ActivityPub plugin! title: "Feature Request:" labels: ["[Type] Feature Request"] +type: 'Enhancement' body: - type: markdown attributes: From 2538a2f1b7827ec61a4269e98ebfa1a18a617b50 Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Fri, 4 Oct 2024 11:28:31 -0500 Subject: [PATCH 15/47] PHPCS: Enable UnusedFunctionParameter sniff (#911) * PHPCS: Enable UnusedFunctionParameter sniff * Add unit test for at_title shortcode * Remove unused variables in at_title shortcode * Remove remaining unused function parameters. * PHPCS: Enable UnusedVariable sniff (#912) --------- Co-authored-by: Matthias Pfefferle --- includes/class-shortcodes.php | 62 ++++----------------- includes/rest/class-collection.php | 4 +- includes/rest/class-inbox.php | 2 - includes/rest/class-nodeinfo.php | 12 +--- phpcs.xml | 5 +- tests/test-class-activitypub-shortcodes.php | 26 +++++++++ 6 files changed, 41 insertions(+), 70 deletions(-) diff --git a/includes/class-shortcodes.php b/includes/class-shortcodes.php index 90a1bcca6..06bb796ed 100644 --- a/includes/class-shortcodes.php +++ b/includes/class-shortcodes.php @@ -65,13 +65,9 @@ public static function hashtags( $atts, $content, $tag ) { /** * Generates output for the 'ap_title' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The post title. */ - public static function title( $atts, $content, $tag ) { + public static function title() { $item = self::get_item(); if ( ! $item ) { @@ -283,13 +279,9 @@ public static function image( $atts, $content, $tag ) { /** * Generates output for the 'ap_hashcats' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The post categories as hashtags. */ - public static function hashcats( $atts, $content, $tag ) { + public static function hashcats() { $item = self::get_item(); if ( ! $item ) { @@ -318,13 +310,9 @@ public static function hashcats( $atts, $content, $tag ) { /** * Generates output for the 'ap_author' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The author name. */ - public static function author( $atts, $content, $tag ) { + public static function author() { $item = self::get_item(); if ( ! $item ) { @@ -344,13 +332,9 @@ public static function author( $atts, $content, $tag ) { /** * Generates output for the 'ap_authorurl' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The author URL. */ - public static function authorurl( $atts, $content, $tag ) { + public static function authorurl() { $item = self::get_item(); if ( ! $item ) { @@ -370,52 +354,36 @@ public static function authorurl( $atts, $content, $tag ) { /** * Generates output for the 'ap_blogurl' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The site URL. */ - public static function blogurl( $atts, $content, $tag ) { + public static function blogurl() { return \esc_url( \get_bloginfo( 'url' ) ); } /** * Generates output for the 'ap_blogname' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string */ - public static function blogname( $atts, $content, $tag ) { + public static function blogname() { return \wp_strip_all_tags( \get_bloginfo( 'name' ) ); } /** * Generates output for the 'ap_blogdesc' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The site description. */ - public static function blogdesc( $atts, $content, $tag ) { + public static function blogdesc() { return \wp_strip_all_tags( \get_bloginfo( 'description' ) ); } /** * Generates output for the 'ap_date' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The post date. */ - public static function date( $atts, $content, $tag ) { + public static function date() { $item = self::get_item(); if ( ! $item ) { @@ -424,7 +392,6 @@ public static function date( $atts, $content, $tag ) { $datetime = \get_post_datetime( $item ); $dateformat = \get_option( 'date_format' ); - $timeformat = \get_option( 'time_format' ); $date = $datetime->format( $dateformat ); @@ -438,13 +405,9 @@ public static function date( $atts, $content, $tag ) { /** * Generates output for the 'ap_time' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The post time. */ - public static function time( $atts, $content, $tag ) { + public static function time() { $item = self::get_item(); if ( ! $item ) { @@ -452,7 +415,6 @@ public static function time( $atts, $content, $tag ) { } $datetime = \get_post_datetime( $item ); - $dateformat = \get_option( 'date_format' ); $timeformat = \get_option( 'time_format' ); $date = $datetime->format( $timeformat ); @@ -467,13 +429,9 @@ public static function time( $atts, $content, $tag ) { /** * Generates output for the 'ap_datetime' Shortcode * - * @param array $atts The Shortcode attributes. - * @param string $content The ActivityPub post-content. - * @param string $tag The tag/name of the Shortcode. - * * @return string The post date/time. */ - public static function datetime( $atts, $content, $tag ) { + public static function datetime() { $item = self::get_item(); if ( ! $item ) { diff --git a/includes/rest/class-collection.php b/includes/rest/class-collection.php index aa7a3bbf6..c15ddd7d9 100644 --- a/includes/rest/class-collection.php +++ b/includes/rest/class-collection.php @@ -256,11 +256,9 @@ public static function featured_get( $request ) { /** * Moderators endpoint * - * @param WP_REST_Request $request The request object. - * * @return WP_REST_Response The response object. */ - public static function moderators_get( $request ) { + public static function moderators_get() { $response = array( '@context' => Actor::JSON_LD_CONTEXT, 'id' => get_rest_url_by_path( 'collections/moderators' ), diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index b09bac837..9ade01ead 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -80,8 +80,6 @@ public static function user_inbox_get( $request ) { return $user; } - $page = $request->get_param( 'page', 0 ); - /* * Action triggerd prior to the ActivityPub profile being created and sent to the client */ diff --git a/includes/rest/class-nodeinfo.php b/includes/rest/class-nodeinfo.php index b8af823c3..1ec5d502e 100644 --- a/includes/rest/class-nodeinfo.php +++ b/includes/rest/class-nodeinfo.php @@ -67,11 +67,9 @@ public static function register_routes() { /** * Render NodeInfo file * - * @param WP_REST_Request $request - * * @return WP_REST_Response */ - public static function nodeinfo( $request ) { + public static function nodeinfo() { /* * Action triggerd prior to the ActivityPub profile being created and sent to the client */ @@ -118,11 +116,9 @@ public static function nodeinfo( $request ) { /** * Render NodeInfo file * - * @param WP_REST_Request $request - * * @return WP_REST_Response */ - public static function nodeinfo2( $request ) { + public static function nodeinfo2() { /* * Action triggerd prior to the ActivityPub profile being created and sent to the client */ @@ -165,11 +161,9 @@ public static function nodeinfo2( $request ) { /** * Render NodeInfo discovery file * - * @param WP_REST_Request $request - * * @return WP_REST_Response */ - public static function discovery( $request ) { + public static function discovery() { $discovery = array(); $discovery['links'] = array( array( diff --git a/phpcs.xml b/phpcs.xml index 8a80286d8..0351ace24 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -24,12 +24,9 @@ - - - - + diff --git a/tests/test-class-activitypub-shortcodes.php b/tests/test-class-activitypub-shortcodes.php index 2db2f2bb1..92bfae174 100644 --- a/tests/test-class-activitypub-shortcodes.php +++ b/tests/test-class-activitypub-shortcodes.php @@ -92,4 +92,30 @@ public function test_excerpt() { $this->assertEquals( "

Lorem ipsum dolor […]

\n", $content ); Shortcodes::unregister(); } + + /** + * Tests 'ap_title' shortcode. + * + * @covers Activitypub\Shortcodes::title + */ + public function test_title() { + Shortcodes::register(); + global $post; + + $post = self::factory()->post->create_and_get( + array( + 'post_title' => 'Test title for shortcode', + ) + ); + + $content = '[ap_title]'; + + // Fill in the shortcodes. + setup_postdata( $post ); + $content = do_shortcode( $content ); + wp_reset_postdata(); + Shortcodes::unregister(); + + $this->assertEquals( 'Test title for shortcode', $content ); + } } From 9b0d5857c19e942f6ccd7af282a9d34b0225ac38 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 7 Oct 2024 10:03:40 +0200 Subject: [PATCH 16/47] check for WP_Error --- includes/class-activity-dispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 484af40e5..a458f9fba 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -302,7 +302,7 @@ public static function add_inboxes_of_replied_urls( $inboxes, $user_id, $activit $actor = object_to_uri( $object['attributedTo'] ); $actor = Http::get_remote_object( $actor ); - if ( ! $actor ) { + if ( ! $actor || \is_wp_error( $actor ) ) { continue; } From 465d84b68adc043391a9111b2deac271ade6edbe Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Mon, 7 Oct 2024 03:33:48 -0500 Subject: [PATCH 17/47] Display warnings and show output (#927) --- .github/workflows/phpcs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml index b7392e165..9707c9124 100644 --- a/.github/workflows/phpcs.yml +++ b/.github/workflows/phpcs.yml @@ -28,4 +28,4 @@ jobs: - name: Install dependencies run: composer install --prefer-dist --no-progress - name: Detect coding standard violations - run: ./vendor/bin/phpcs -n -q + run: ./vendor/bin/phpcs From 06250909896e7bd3a6ab35aca90b5cfe63602ec4 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Mon, 7 Oct 2024 06:25:32 -0500 Subject: [PATCH 18/47] Fix link regex (#922) * Fix link regex * add some tests --------- Co-authored-by: Matthias Pfefferle --- activitypub.php | 2 +- includes/class-link.php | 2 +- tests/test-class-activitypub-link.php | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/activitypub.php b/activitypub.php index 2cb7ad576..157edebef 100644 --- a/activitypub.php +++ b/activitypub.php @@ -34,7 +34,7 @@ \defined( 'ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS' ) || \define( 'ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS', 3 ); \defined( 'ACTIVITYPUB_HASHTAGS_REGEXP' ) || \define( 'ACTIVITYPUB_HASHTAGS_REGEXP', '(?:(?<=\s)|(?<=

)|(?<=
)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))' ); \defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9\._-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' ); -\defined( 'ACTIVITYPUB_URL_REGEXP' ) || \define( 'ACTIVITYPUB_URL_REGEXP', '(www.|http:|https:)+[^\s]+[\w\/]' ); +\defined( 'ACTIVITYPUB_URL_REGEXP' ) || \define( 'ACTIVITYPUB_URL_REGEXP', '(https?:|www\.)\S+[\w\/]' ); \defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "

[ap_title]

\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 ); diff --git a/includes/class-link.php b/includes/class-link.php index 2e48d1be4..52a814000 100644 --- a/includes/class-link.php +++ b/includes/class-link.php @@ -62,7 +62,7 @@ public static function replace_with_links( $result ) { $result[0] = 'https://' . $result[0]; } $parsed_url = \wp_parse_url( html_entity_decode( $result[0] ) ); - if ( ! $parsed_url ) { + if ( ! $parsed_url || empty( $parsed_url['host'] ) ) { return $result[0]; } diff --git a/tests/test-class-activitypub-link.php b/tests/test-class-activitypub-link.php index e966ce895..bfe124db5 100644 --- a/tests/test-class-activitypub-link.php +++ b/tests/test-class-activitypub-link.php @@ -37,6 +37,8 @@ public function the_content_provider() { array( 'hello https://www.test.de test', 'hello test.de test' ), array( 'hello www.test.de test', 'hello test.de test' ), array( 'hello https://test:test@test.de test', 'hello test.de test' ), + array( 'wwwfoocom', 'wwwfoocom' ), + array( 'www.foo.com', 'foo.com' ), array( $code, $code ), array( $style, $style ), array( $textarea, $textarea ), From 00a57c39a4e844949bc4f64a45714d1f21d9645d Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Mon, 7 Oct 2024 06:25:51 -0500 Subject: [PATCH 19/47] PHPCS: Enable Commenting sniff (#925) * PHPCS: Enable Commenting sniff * Apply suggestions from code review Co-authored-by: Matthias Pfefferle * Fix param alignment. --------- Co-authored-by: Matthias Pfefferle --- activitypub.php | 26 +- includes/activity/class-activity.php | 10 +- includes/activity/class-actor.php | 2 + includes/activity/class-base-object.php | 50 +++- .../activity/extended-object/class-event.php | 38 ++- .../activity/extended-object/class-place.php | 5 + includes/class-activity-dispatcher.php | 124 ++++---- includes/class-activitypub.php | 123 ++++---- includes/class-admin.php | 127 ++++---- includes/class-blocks.php | 63 +++- includes/class-cli.php | 51 ++-- includes/class-comment.php | 106 ++++--- includes/class-debug.php | 46 ++- includes/class-handler.php | 13 +- includes/class-hashtag.php | 56 ++-- includes/class-health-check.php | 62 ++-- includes/class-http.php | 62 +++- includes/class-link.php | 43 +-- includes/class-mention.php | 50 ++-- includes/class-migration.php | 88 +++--- includes/class-notification.php | 5 + includes/class-scheduler.php | 85 +++--- includes/class-shortcodes.php | 58 ++-- includes/class-signature.php | 73 +++-- includes/class-webfinger.php | 54 ++-- includes/collection/class-extra-fields.php | 40 ++- includes/collection/class-followers.php | 87 +++--- includes/collection/class-interactions.php | 59 ++-- includes/collection/class-replies.php | 15 +- includes/collection/class-users.php | 53 ++-- includes/compat.php | 27 +- includes/debug.php | 17 +- includes/functions.php | 283 +++++++++++------- includes/handler/class-announce.php | 28 +- includes/handler/class-create.php | 54 ++-- includes/handler/class-delete.php | 61 ++-- includes/handler/class-follow.php | 43 +-- includes/handler/class-like.php | 18 +- includes/handler/class-undo.php | 22 +- includes/handler/class-update.php | 51 ++-- includes/help.php | 11 +- includes/model/class-application.php | 65 +++- includes/model/class-blog.php | 111 +++++-- includes/model/class-follower.php | 59 ++-- includes/model/class-user.php | 118 ++++++-- includes/rest/class-actors.php | 32 +- includes/rest/class-collection.php | 36 ++- includes/rest/class-comment.php | 16 +- includes/rest/class-followers.php | 58 ++-- includes/rest/class-following.php | 42 ++- includes/rest/class-inbox.php | 127 +++++--- includes/rest/class-interaction.php | 27 +- includes/rest/class-nodeinfo.php | 28 +- includes/rest/class-outbox.php | 55 ++-- includes/rest/class-server.php | 41 +-- includes/rest/class-webfinger.php | 35 ++- includes/table/class-followers.php | 96 +++++- includes/transformer/class-attachment.php | 11 +- includes/transformer/class-base.php | 40 +-- includes/transformer/class-comment.php | 66 ++-- includes/transformer/class-factory.php | 14 +- includes/transformer/class-post.php | 118 +++++--- integration/class-buddypress.php | 28 +- integration/class-enable-mastodon-apps.php | 122 ++++++-- integration/class-jetpack.php | 27 +- integration/class-nodeinfo.php | 32 +- integration/class-opengraph.php | 24 +- .../class-seriously-simple-podcasting.php | 6 + integration/class-stream-connector.php | 26 +- integration/class-webfinger.php | 27 +- integration/load.php | 5 + phpcs.xml | 4 +- templates/admin-header.php | 7 +- 73 files changed, 2362 insertions(+), 1430 deletions(-) diff --git a/activitypub.php b/activitypub.php index 157edebef..69ecbaaf3 100644 --- a/activitypub.php +++ b/activitypub.php @@ -11,15 +11,14 @@ * Requires PHP: 7.0 * Text Domain: activitypub * Domain Path: /languages + * + * @package Activitypub */ namespace Activitypub; use WP_CLI; -use function Activitypub\is_blog_public; -use function Activitypub\site_supports_blocks; - require_once __DIR__ . '/includes/compat.php'; require_once __DIR__ . '/includes/functions.php'; @@ -39,7 +38,7 @@ \defined( 'ACTIVITYPUB_AUTHORIZED_FETCH' ) || \define( 'ACTIVITYPUB_AUTHORIZED_FETCH', false ); \defined( 'ACTIVITYPUB_DISABLE_REWRITES' ) || \define( 'ACTIVITYPUB_DISABLE_REWRITES', false ); \defined( 'ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS' ) || \define( 'ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS', false ); -// Disable reactions like `Like` and `Announce` by default +// Disable reactions like `Like` and `Announce` by default. \defined( 'ACTIVITYPUB_DISABLE_REACTIONS' ) || \define( 'ACTIVITYPUB_DISABLE_REACTIONS', true ); \defined( 'ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS' ) || \define( 'ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS', false ); \defined( 'ACTIVITYPUB_SHARED_INBOX_FEATURE' ) || \define( 'ACTIVITYPUB_SHARED_INBOX_FEATURE', false ); @@ -66,7 +65,7 @@ function rest_init() { Rest\Collection::init(); Rest\Interaction::init(); - // load NodeInfo endpoints only if blog is public + // Load NodeInfo endpoints only if blog is public. if ( is_blog_public() ) { Rest\NodeInfo::init(); } @@ -103,7 +102,7 @@ function plugin_init() { /** - * Class Autoloader + * Class Autoloader. */ \spl_autoload_register( function ( $full_class ) { @@ -131,7 +130,7 @@ function ( $full_class ) { if ( file_exists( $file ) && is_readable( $file ) ) { require_once $file; } else { - // translators: %s is the class name + // translators: %s is the class name. $message = sprintf( esc_html__( 'Required class not found or not readable: %s', 'activitypub' ), esc_html( $full_class ) ); Debug::write_log( $message ); \wp_die( $message ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped @@ -141,7 +140,9 @@ function ( $full_class ) { ); /** - * Add plugin settings link + * Add plugin settings link. + * + * @param array $actions The current actions. */ function plugin_settings_link( $actions ) { $settings_link = array(); @@ -179,13 +180,14 @@ function plugin_settings_link( $actions ) { ) ); -// Load integrations +// Load integrations. require_once __DIR__ . '/integration/load.php'; /** - * `get_plugin_data` wrapper + * `get_plugin_data` wrapper. * - * @return array The plugin metadata array + * @param array $default_headers Optional. The default plugin headers. Default empty array. + * @return array The plugin metadata array. */ function get_plugin_meta( $default_headers = array() ) { if ( ! $default_headers ) { @@ -221,7 +223,7 @@ function get_plugin_version() { return $meta['Version']; } -// Check for CLI env, to add the CLI commands +// Check for CLI env, to add the CLI commands. if ( defined( 'WP_CLI' ) && WP_CLI ) { WP_CLI::add_command( 'activitypub', diff --git a/includes/activity/class-activity.php b/includes/activity/class-activity.php index f8db1945e..b8418315e 100644 --- a/includes/activity/class-activity.php +++ b/includes/activity/class-activity.php @@ -3,11 +3,13 @@ * Inspired by the PHP ActivityPub Library by @Landrok * * @link https://github.com/landrok/activitypub + * + * @package Activitypub */ namespace Activitypub\Activity; -use Activitypub\Activity\Base_Object; +use Activitypub\Link; /** * \Activitypub\Activity\Activity implements the common @@ -150,12 +152,12 @@ class Activity extends Base_Object { * @return void */ public function set_object( $data ) { - // convert array to object + // Convert array to object. if ( is_array( $data ) ) { $data = self::init_from_array( $data ); } - // set object + // Set object. $this->set( 'object', $data ); if ( ! is_object( $data ) ) { @@ -202,7 +204,7 @@ public function get_json_ld_context() { if ( $this->object instanceof Base_Object ) { $class = get_class( $this->object ); if ( $class && $class::JSON_LD_CONTEXT ) { - // Without php 5.6 support this could be just: 'return $this->object::JSON_LD_CONTEXT;' + // Without php 5.6 support this could be just: 'return $this->object::JSON_LD_CONTEXT;'. return $class::JSON_LD_CONTEXT; } } diff --git a/includes/activity/class-actor.php b/includes/activity/class-actor.php index 6ce832bc7..ddbef8eaa 100644 --- a/includes/activity/class-actor.php +++ b/includes/activity/class-actor.php @@ -3,6 +3,8 @@ * Inspired by the PHP ActivityPub Library by @Landrok * * @link https://github.com/landrok/activitypub + * + * @package Activitypub */ namespace Activitypub\Activity; diff --git a/includes/activity/class-base-object.php b/includes/activity/class-base-object.php index ce1799369..ab765174f 100644 --- a/includes/activity/class-base-object.php +++ b/includes/activity/class-base-object.php @@ -3,6 +3,8 @@ * Inspired by the PHP ActivityPub Library by @Landrok * * @link https://github.com/landrok/activitypub + * + * @package Activitypub */ namespace Activitypub\Activity; @@ -431,7 +433,7 @@ class Base_Object { * * @see https://www.w3.org/TR/activitypub/#source-property * - * @var ObjectType + * @var array */ protected $source; @@ -460,12 +462,10 @@ class Base_Object { protected $sensitive = false; /** - * Magic function to implement getter and setter + * Magic function to implement getter and setter. * * @param string $method The method name. * @param string $params The method params. - * - * @return void */ public function __call( $method, $params ) { $var = \strtolower( \substr( $method, 4 ) ); @@ -577,9 +577,9 @@ public function add( $key, $value ) { /** * Convert JSON input to an array. * - * @return string The JSON string. + * @param string $json The JSON string. * - * @return \Activitypub\Activity\Base_Object An Object built from the JSON string. + * @return Base_Object An Object built from the JSON string. */ public static function init_from_json( $json ) { $array = \json_decode( $json, true ); @@ -653,12 +653,12 @@ public function to_array( $include_json_ld_context = true ) { $vars = get_object_vars( $this ); foreach ( $vars as $key => $value ) { - // ignotre all _prefixed keys. + // Ignore all _prefixed keys. if ( '_' === substr( $key, 0, 1 ) ) { continue; } - // if value is empty, try to get it from a getter. + // If value is empty, try to get it from a getter. if ( ! $value ) { $value = call_user_func( array( $this, 'get_' . $key ) ); } @@ -667,7 +667,7 @@ public function to_array( $include_json_ld_context = true ) { $value = $value->to_array( false ); } - // if value is still empty, ignore it for the array and continue. + // If value is still empty, ignore it for the array and continue. if ( isset( $value ) ) { $array[ snake_to_camel_case( $key ) ] = $value; } @@ -681,10 +681,28 @@ public function to_array( $include_json_ld_context = true ) { $class = new ReflectionClass( $this ); $class = strtolower( $class->getShortName() ); + /** + * Filter the array of the ActivityPub object. + * + * @param array $array The array of the ActivityPub object. + * @param string $class The class of the ActivityPub object. + * @param int $id The ID of the ActivityPub object. + * @param Base_Object $object The ActivityPub object. + * + * @return array The filtered array of the ActivityPub object. + */ $array = \apply_filters( 'activitypub_activity_object_array', $array, $class, $this->id, $this ); - $array = \apply_filters( "activitypub_activity_{$class}_object_array", $array, $this->id, $this ); - return $array; + /** + * Filter the array of the ActivityPub object by class. + * + * @param array $array The array of the ActivityPub object. + * @param int $id The ID of the ActivityPub object. + * @param Base_Object $object The ActivityPub object. + * + * @return array The filtered array of the ActivityPub object. + */ + return \apply_filters( "activitypub_activity_{$class}_object_array", $array, $this->id, $this ); } /** @@ -698,11 +716,11 @@ public function to_json( $include_json_ld_context = true ) { $array = $this->to_array( $include_json_ld_context ); $options = \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT; - /* - * Options to be passed to json_encode() - * - * @param int $options The current options flags - */ + /** + * Options to be passed to json_encode() + * + * @param int $options The current options flags. + */ $options = \apply_filters( 'activitypub_json_encode_options', $options ); return \wp_json_encode( $array, $options ); diff --git a/includes/activity/extended-object/class-event.php b/includes/activity/extended-object/class-event.php index 4523405e7..9ddb46ffd 100644 --- a/includes/activity/extended-object/class-event.php +++ b/includes/activity/extended-object/class-event.php @@ -19,8 +19,8 @@ class Event extends Base_Object { // Human friendly minimal context for full Mobilizon compatible ActivityPub events. const JSON_LD_CONTEXT = array( - 'https://schema.org/', // The base context is schema.org, cause it is used a lot. - 'https://www.w3.org/ns/activitystreams', // The ActivityStreams context overrides everyting also defined in schema.org. + 'https://schema.org/', // The base context is schema.org, because it is used a lot. + 'https://www.w3.org/ns/activitystreams', // The ActivityStreams context overrides everything also defined in schema.org. array( // The keys here override/extend the context even more. 'pt' => 'https://joinpeertube.org/ns#', 'mz' => 'https://joinmobilizon.org/ns#', @@ -59,7 +59,7 @@ class Event extends Base_Object { /** * Mobilizon compatible values for joinModeTypes. */ - const JOIN_MODE_TYPES = array( 'free', 'restricted', 'external' ); // and 'invite', but not used by mobilizon atm + const JOIN_MODE_TYPES = array( 'free', 'restricted', 'external' ); // and 'invite', but not used by mobilizon atm. /** * Allowed values for ical VEVENT STATUS. @@ -109,8 +109,7 @@ class Event extends Base_Object { ); /** - * Event is an implementation of one of the - * Activity Streams + * Event is an implementation of one of the Activity Streams. * * @var string */ @@ -124,7 +123,7 @@ class Event extends Base_Object { protected $name; /** - * The events contacts + * The events contacts. * * @context { * '@id' => 'mz:contacts', @@ -200,7 +199,7 @@ class Event extends Base_Object { * The event's status. * * @context https://www.w3.org/2002/12/cal/ical#status - * @var enum + * @var string */ protected $status; @@ -263,6 +262,7 @@ class Event extends Base_Object { * The passed timezone is only set when it is a valid one, otherwise the site's timezone is used. * * @param string $timezone The timezone string to be set, e.g. 'Europe/Berlin'. + * @return Event */ public function set_timezone( $timezone ) { if ( in_array( $timezone, timezone_identifiers_list(), true ) ) { @@ -275,9 +275,11 @@ public function set_timezone( $timezone ) { } /** - * Custom setter for repliesModerationOption which also directy sets commentsEnabled accordingly. + * Custom setter for repliesModerationOption which also directly sets commentsEnabled accordingly. + * + * @param string $type The type of the replies moderation option. * - * @param string $type + * @return Event */ public function set_replies_moderation_option( $type ) { if ( in_array( $type, self::REPLIES_MODERATION_OPTION_TYPES, true ) ) { @@ -297,7 +299,9 @@ public function set_replies_moderation_option( $type ) { /** * Custom setter for commentsEnabled which also directly sets repliesModerationOption accordingly. * - * @param bool $comments_enabled + * @param bool $comments_enabled Whether comments are enabled. + * + * @return Event */ public function set_comments_enabled( $comments_enabled ) { if ( is_bool( $comments_enabled ) ) { @@ -317,7 +321,9 @@ public function set_comments_enabled( $comments_enabled ) { /** * Custom setter for the ical status that checks whether the status is an ical event status. * - * @param string $status + * @param string $status The status of the event. + * + * @return Event */ public function set_status( $status ) { if ( in_array( $status, self::ICAL_EVENT_STATUS_TYPES, true ) ) { @@ -338,8 +344,10 @@ public function set_status( $status ) { * * Falls back to Mobilizons default category. * - * @param string $category - * @param bool $mobilizon_compatibilty Whether the category must be compatibly with Mobilizon. + * @param string $category The category of the event. + * @param bool $mobilizon_compatibilty Optional. Whether the category must be compatibly with Mobilizon. Default true. + * + * @return Event */ public function set_category( $category, $mobilizon_compatibilty = true ) { if ( $mobilizon_compatibilty ) { @@ -356,7 +364,9 @@ public function set_category( $category, $mobilizon_compatibilty = true ) { * * Automatically sets the joinMode to true if called. * - * @param string $url + * @param string $url The URL for external participation. + * + * @return Event */ public function set_external_participation_url( $url ) { if ( preg_match( '/^https?:\/\/.*/i', $url ) ) { diff --git a/includes/activity/extended-object/class-place.php b/includes/activity/extended-object/class-place.php index 4efe66561..8b66cdcd0 100644 --- a/includes/activity/extended-object/class-place.php +++ b/includes/activity/extended-object/class-place.php @@ -86,6 +86,11 @@ class Place extends Base_Object { */ protected $address; + /** + * Set the address of the place. + * + * @param array|string $address The address of the place. + */ public function set_address( $address ) { if ( is_string( $address ) || is_array( $address ) ) { $this->address = $address; diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index a458f9fba..17281a334 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -1,24 +1,21 @@ set_type( 'Update' ); $activity->set_actor( $user->get_url() ); $activity->set_object( $user->get_url() ); $activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' ); - // send the update + // Send the update. self::send_activity_to_followers( $activity, $user_id, $user ); } /** * Send an Activity to all followers and mentioned users. * - * @param Activity $activity The ActivityPub Activity. - * @param int $user_id The user ID. - * @param WP_User|WP_Post|WP_Comment $wp_object The WordPress object. - * - * @return void + * @param Activity $activity The ActivityPub Activity. + * @param int $user_id The user ID. + * @param \WP_User|WP_Post|WP_Comment $wp_object The WordPress object. */ private static function send_activity_to_followers( $activity, $user_id, $wp_object ) { - // check if the Activity should be send to the followers + /** + * Filter to prevent sending an Activity to followers. + * + * @param bool $send_activity_to_followers Whether to send the Activity to followers. + * @param Activity $activity The ActivityPub Activity. + * @param int $user_id The user ID. + * @param \WP_User|WP_Post|WP_Comment $wp_object The WordPress object. + */ if ( ! apply_filters( 'activitypub_send_activity_to_followers', true, $activity, $user_id, $wp_object ) ) { return; } + /** + * Filter to modify the Activity before sending it to followers. + * + * @param Activity $activity The ActivityPub Activity. + * @param int $user_id The user ID. + * @param \WP_User|WP_Post|WP_Comment $wp_object The WordPress object. + */ $inboxes = apply_filters( 'activitypub_send_to_inboxes', array(), $user_id, $activity ); $inboxes = array_unique( $inboxes ); @@ -189,8 +191,6 @@ private static function send_activity_to_followers( $activity, $user_id, $wp_obj * * @param int $id The WordPress Post ID. * @param string $type The Activity-Type. - * - * @return void */ public static function send_post( $id, $type ) { $post = get_post( $id ); @@ -199,14 +199,20 @@ public static function send_post( $id, $type ) { return; } + /** + * Action to send an Activity for a Post. + * + * @param WP_Post $post The WordPress Post. + * @param string $type The Activity-Type. + */ do_action( 'activitypub_send_activity', $post, $type ); - do_action( - sprintf( - 'activitypub_send_%s_activity', - \strtolower( $type ) - ), - $post - ); + + /** + * Action to send a specific Activity for a Post. + * + * @param WP_Post $post The WordPress Post. + */ + do_action( sprintf( 'activitypub_send_%s_activity', \strtolower( $type ) ), $post ); } /** @@ -214,8 +220,6 @@ public static function send_post( $id, $type ) { * * @param int $id The WordPress Comment ID. * @param string $type The Activity-Type. - * - * @return void */ public static function send_comment( $id, $type ) { $comment = get_comment( $id ); @@ -224,21 +228,27 @@ public static function send_comment( $id, $type ) { return; } + /** + * Action to send an Activity for a Comment. + * + * @param WP_Comment $comment The WordPress Comment. + * @param string $type The Activity-Type. + */ do_action( 'activitypub_send_activity', $comment, $type ); - do_action( - sprintf( - 'activitypub_send_%s_activity', - \strtolower( $type ) - ), - $comment - ); + + /** + * Action to send a specific Activity for a Comment. + * + * @param WP_Comment $comment The WordPress Comment. + */ + do_action( sprintf( 'activitypub_send_%s_activity', \strtolower( $type ) ), $comment ); } /** - * Default filter to add Inboxes of Followers + * Default filter to add Inboxes of Followers. * - * @param array $inboxes The list of Inboxes - * @param int $user_id The WordPress User-ID + * @param array $inboxes The list of Inboxes. + * @param int $user_id The WordPress User-ID. * * @return array The filtered Inboxes */ @@ -251,11 +261,11 @@ public static function add_inboxes_of_follower( $inboxes, $user_id ) { /** * Default filter to add Inboxes of Mentioned Actors * - * @param array $inboxes The list of Inboxes - * @param int $user_id The WordPress User-ID - * @param array $activity The ActivityPub Activity + * @param array $inboxes The list of Inboxes. + * @param int $user_id The WordPress User-ID. + * @param array $activity The ActivityPub Activity. * - * @return array The filtered Inboxes + * @return array The filtered Inboxes. */ public static function add_inboxes_by_mentioned_actors( $inboxes, $user_id, $activity ) { $cc = $activity->get_cc(); @@ -271,9 +281,9 @@ public static function add_inboxes_by_mentioned_actors( $inboxes, $user_id, $act /** * Default filter to add Inboxes of Posts that are set as `in-reply-to` * - * @param array $inboxes The list of Inboxes - * @param int $user_id The WordPress User-ID - * @param array $activity The ActivityPub Activity + * @param array $inboxes The list of Inboxes. + * @param int $user_id The WordPress User-ID. + * @param array $activity The ActivityPub Activity. * * @return array The filtered Inboxes */ diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 78e3284cc..c85a5fb17 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -1,22 +1,18 @@ ; rel="alternate"; type="application/activity+json"' ); @@ -199,21 +184,21 @@ public static function template_redirect() { $comment_id = get_query_var( 'c', null ); - // check if it seems to be a comment + // Check if it seems to be a comment. if ( ! $comment_id ) { return; } $comment = get_comment( $comment_id ); - // load a 404 page if `c` is set but not valid + // Load a 404 page if `c` is set but not valid. if ( ! $comment ) { global $wp_query; $wp_query->set_404(); return; } - // stop if it's not an ActivityPub comment + // Stop if it's not an ActivityPub comment. if ( is_activitypub_request() && ! is_local_comment( $comment ) ) { return; } @@ -224,6 +209,10 @@ public static function template_redirect() { /** * Add the 'activitypub' query variable so WordPress won't mangle it. + * + * @param array $vars The query variables. + * + * @return array The query variables. */ public static function add_query_vars( $vars ) { $vars[] = 'activitypub'; @@ -286,9 +275,9 @@ public static function pre_get_avatar_data( $args, $id_or_email ) { /** * Function to retrieve Avatar URL if stored in meta. * - * @param int|WP_Comment $comment + * @param int|\WP_Comment $comment The comment ID or object. * - * @return string $url + * @return string The Avatar URL. */ public static function get_avatar_url( $comment ) { if ( \is_numeric( $comment ) ) { @@ -301,8 +290,6 @@ public static function get_avatar_url( $comment ) { * Store permalink in meta, to send delete Activity. * * @param string $post_id The Post ID. - * - * @return void */ public static function trash_post( $post_id ) { \add_post_meta( @@ -314,22 +301,22 @@ public static function trash_post( $post_id ) { } /** - * Delete permalink from meta + * Delete permalink from meta. * - * @param string $post_id The Post ID - * - * @return void + * @param string $post_id The Post ID. */ public static function untrash_post( $post_id ) { \delete_post_meta( $post_id, 'activitypub_canonical_url' ); } /** - * Add rewrite rules + * Add rewrite rules. */ public static function add_rewrite_rules() { - // If another system needs to take precedence over the ActivityPub rewrite rules, - // they can define their own and will manually call the appropriate functions as required. + /* + * If another system needs to take precedence over the ActivityPub rewrite rules, + * they can define their own and will manually call the appropriate functions as required. + */ if ( ACTIVITYPUB_DISABLE_REWRITES ) { return; } @@ -365,7 +352,7 @@ public static function add_rewrite_rules() { } /** - * Flush rewrite rules; + * Flush rewrite rules. */ public static function flush_rewrite_rules() { self::add_rewrite_rules(); @@ -373,9 +360,7 @@ public static function flush_rewrite_rules() { } /** - * Adds metabox on wp-admin/tools.php - * - * @return void + * Adds metabox on wp-admin/tools.php. */ public static function tool_box() { if ( \current_user_can( 'edit_posts' ) ) { @@ -384,12 +369,10 @@ public static function tool_box() { } /** - * Theme compatibility stuff - * - * @return void + * Theme compatibility stuff. */ public static function theme_compat() { - // We assume that you want to use Post-Formats when enabling the setting + // We assume that you want to use Post-Formats when enabling the setting. if ( 'wordpress-post-format' === \get_option( 'activitypub_object_type', ACTIVITYPUB_DEFAULT_OBJECT_TYPE ) ) { if ( ! get_theme_support( 'post-formats' ) ) { // Add support for the Aside, Gallery Post Formats... @@ -408,11 +391,9 @@ public static function theme_compat() { } /** - * Display plugin upgrade notice to users - * - * @param array $data The plugin data + * Display plugin upgrade notice to users. * - * @return void + * @param array $data The plugin data. */ public static function plugin_update_message( $data ) { if ( ! isset( $data['upgrade_notice'] ) ) { @@ -434,9 +415,7 @@ public static function plugin_update_message( $data ) { } /** - * Register the "Followers" Taxonomy - * - * @return void + * Register the "Followers" Taxonomy. */ private static function register_post_types() { \register_post_type( @@ -542,9 +521,7 @@ private static function register_post_types() { /** * Add the 'activitypub' capability to users who can publish posts. * - * @param int $user_id User ID. - * - * @param array $userdata The raw array of data passed to wp_insert_user(). + * @param int $user_id User ID. */ public static function user_register( $user_id ) { if ( \user_can( $user_id, 'publish_posts' ) ) { diff --git a/includes/class-admin.php b/includes/class-admin.php index 353b75fbc..2d1d86cd9 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -1,25 +1,25 @@ @@ -140,7 +136,7 @@ private static function show_admin_notice( $admin_notice, $level ) { } /** - * Load settings page + * Load settings page. */ public static function settings_page() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended @@ -179,7 +175,7 @@ public static function settings_page() { * Load user settings page */ public static function followers_list_page() { - // user has to be able to publish posts + // User has to be able to publish posts. if ( ! is_user_disabled( get_current_user_id() ) ) { \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/user-followers-list.php' ); } @@ -290,7 +286,7 @@ public static function register_settings() { ) ); - // Blog-User Settings + // Blog-User Settings. \register_setting( 'activitypub_blog', 'activitypub_blog_description', @@ -310,7 +306,7 @@ public static function register_settings() { 'show_in_rest' => true, 'default' => Blog::get_default_username(), 'sanitize_callback' => function ( $value ) { - // hack to allow dots in the username + // Hack to allow dots in the username. $parts = explode( '.', $value ); $sanitized = array(); @@ -320,7 +316,7 @@ public static function register_settings() { $sanitized = implode( '.', $sanitized ); - // check for login or nicename. + // Check for login or nicename. $user = new WP_User_Query( array( 'search' => $sanitized, @@ -357,14 +353,25 @@ public static function register_settings() { ); } + /** + * Adds the ActivityPub settings to the Help tab. + */ public static function add_settings_help_tab() { require_once ACTIVITYPUB_PLUGIN_DIR . 'includes/help.php'; } + /** + * Adds the follower list to the Help tab. + */ public static function add_followers_list_help_tab() { - // todo + // todo. } + /** + * Add the profile. + * + * @param \WP_User $user The user object. + */ public static function add_profile( $user ) { $description = \get_user_option( 'activitypub_description', $user->ID ); @@ -381,25 +388,25 @@ public static function add_profile( $user ) { } /** - * Save the user settings + * Save the user settings. * - * Habdles the saving of the ActivityPub settings. + * Handles the saving of the ActivityPub settings. * * @param int $user_id The user ID. - * - * @return void */ public static function save_user_settings( $user_id ) { if ( ! isset( $_REQUEST['_apnonce'] ) ) { - return false; + return; } + $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_apnonce'] ) ); if ( ! wp_verify_nonce( $nonce, 'activitypub-user-settings' ) || ! current_user_can( 'edit_user', $user_id ) ) { - return false; + return; } + $description = ! empty( $_POST['activitypub_description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['activitypub_description'] ) ) : false; if ( $description ) { \update_user_option( $user_id, 'activitypub_description', $description ); @@ -415,6 +422,11 @@ public static function save_user_settings( $user_id ) { } } + /** + * Enqueue the admin scripts and styles. + * + * @param string $hook_suffix The current page. + */ public static function enqueue_scripts( $hook_suffix ) { wp_register_script( 'activitypub-header-image', @@ -463,11 +475,9 @@ public static function enqueue_scripts( $hook_suffix ) { } /** - * Hook into the edit_comment functionality + * Hook into the edit_comment functionality. * - * * Disable the edit_comment capability for federated comments. - * - * @return void + * Disables the edit_comment capability for federated comments. */ public static function edit_comment() { // Disable the edit_comment capability for federated comments. @@ -489,6 +499,11 @@ function ( $allcaps, $caps, $arg ) { ); } + /** + * Hook into the edit_post functionality. + * + * Disables the edit_post capability for federated posts. + */ public static function edit_post() { // Disable the edit_post capability for federated posts. \add_filter( @@ -516,9 +531,7 @@ function ( $allcaps, $caps, $arg ) { } /** - * Add ActivityPub specific actions/filters to the post list view - * - * @return void + * Add ActivityPub specific actions/filters to the post list view. */ public static function list_posts() { // Show only the user's extra fields. @@ -546,6 +559,14 @@ function ( $views ) { ); } + /** + * Comment row actions. + * + * @param array $actions The existing actions. + * @param int|\WP_Comment $comment The comment object or ID. + * + * @return array The modified actions. + */ public static function comment_row_actions( $actions, $comment ) { if ( was_comment_received( $comment ) ) { unset( $actions['edit'] ); @@ -556,7 +577,7 @@ public static function comment_row_actions( $actions, $comment ) { } /** - * Add a column "activitypub" + * Add a column "activitypub". * * This column shows if the user has the capability to use ActivityPub. * @@ -570,9 +591,11 @@ public static function manage_users_columns( $columns ) { } /** - * Add "comment-type" and "protocol" as column in WP-Admin + * Add "comment-type" and "protocol" as column in WP-Admin. + * + * @param array $columns The list of column names. * - * @param array $columns the list of column names + * @return array The extended list of column names. */ public static function manage_comment_columns( $columns ) { $columns['comment_type'] = esc_attr__( 'Comment-Type', 'activitypub' ); @@ -582,10 +605,12 @@ public static function manage_comment_columns( $columns ) { } /** - * Add "post_content" as column for Extra-Fields in WP-Admin + * Add "post_content" as column for Extra-Fields in WP-Admin. * - * @param array $columns Tthe list of column names. + * @param array $columns The list of column names. * @param string $post_type The post type. + * + * @return array The extended list of column names. */ public static function manage_post_columns( $columns, $post_type ) { if ( Extra_Fields::is_extra_fields_post_type( $post_type ) ) { @@ -598,10 +623,10 @@ public static function manage_post_columns( $columns, $post_type ) { } /** - * Add "comment-type" and "protocol" as column in WP-Admin + * Add "comment-type" and "protocol" as column in WP-Admin. * - * @param array $column The column to implement - * @param int $comment_id The comment id + * @param array $column The column to implement. + * @param int $comment_id The comment id. */ public static function manage_comments_custom_column( $column, $comment_id ) { if ( 'comment_type' === $column && ! defined( 'WEBMENTION_PLUGIN_DIR' ) ) { @@ -639,7 +664,7 @@ public static function manage_users_custom_column( $output, $column_name, $user_ } /** - * Add a column "extra_field_content" to the post list view + * Add a column "extra_field_content" to the post list view. * * @param string $column_name The column name. * @param int $post_id The post ID. @@ -647,8 +672,6 @@ public static function manage_users_custom_column( $output, $column_name, $user_ * @return void */ public static function manage_posts_custom_column( $column_name, $post_id ) { - $post = get_post( $post_id ); - if ( 'extra_field_content' === $column_name ) { $post = get_post( $post_id ); if ( Extra_Fields::is_extra_fields_post_type( $post->post_type ) ) { @@ -658,7 +681,7 @@ public static function manage_posts_custom_column( $column_name, $post_id ) { } /** - * Add options to the Bulk dropdown on the users page + * Add options to the Bulk dropdown on the users page. * * @param array $actions The existing bulk options. * @@ -672,7 +695,7 @@ public static function user_bulk_options( $actions ) { } /** - * Handle bulk activitypub requests + * Handle bulk activitypub requests. * * * `add_activitypub_cap` - Add the activitypub capability to the selected users. * * `remove_activitypub_cap` - Remove the activitypub capability from the selected users. @@ -707,7 +730,7 @@ public static function handle_bulk_request( $sendback, $action, $users ) { } /** - * Add ActivityPub infos to the dashboard glance items + * Add ActivityPub infos to the dashboard glance items. * * @param array $items The existing glance items. * @@ -718,7 +741,7 @@ public static function dashboard_glance_items( $items ) { if ( ! is_user_disabled( get_current_user_id() ) ) { $follower_count = sprintf( - // translators: %s: number of followers + // translators: %s: number of followers. _n( '%s Follower', '%s Followers', @@ -737,7 +760,7 @@ public static function dashboard_glance_items( $items ) { if ( ! is_user_type_disabled( 'blog' ) && current_user_can( 'manage_options' ) ) { $follower_count = sprintf( - // translators: %s: number of followers + // translators: %s: number of followers. _n( '%s Follower (Blog)', '%s Followers (Blog)', @@ -754,7 +777,7 @@ public static function dashboard_glance_items( $items ) { ); } - \remove_filter( 'number_format_i18n', '\Activitypub\custom_large_numbers', 10, 3 ); + \remove_filter( 'number_format_i18n', '\Activitypub\custom_large_numbers' ); return $items; } diff --git a/includes/class-blocks.php b/includes/class-blocks.php index cf7261bc8..c42d2fc6a 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -1,24 +1,37 @@ post_type, $ap_post_types, true ) ) { @@ -51,7 +67,7 @@ public static function enqueue_editor_assets() { * Enqueue the reply handle script if the in_reply_to GET param is set. */ public static function handle_in_reply_to_get_param() { - // only load the script if the in_reply_to GET param is set, action happens there, not here. + // Only load the script if the in_reply_to GET param is set, action happens there, not here. // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! isset( $_GET['in_reply_to'] ) ) { return; @@ -62,6 +78,9 @@ public static function handle_in_reply_to_get_param() { wp_enqueue_script( 'activitypub-reply-intent', $plugin_url, $asset_data['dependencies'], $asset_data['version'], true ); } + /** + * Add data to the block editor. + */ public static function add_data() { $context = is_admin() ? 'editor' : 'view'; $followers_handle = 'activitypub-followers-' . $context . '-script'; @@ -78,6 +97,9 @@ public static function add_data() { \wp_add_inline_script( $follow_me_handle, $js, 'before' ); } + /** + * Register the blocks. + */ public static function register_blocks() { \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/followers', @@ -110,7 +132,7 @@ private static function get_user_id( $user_string ) { return absint( $user_string ); } - // if the user string is 'site', return the Blog User ID. + // If the user string is 'site', return the Blog User ID. if ( 'site' === $user_string ) { return User_Collection::BLOG_USER_ID; } @@ -125,7 +147,7 @@ private static function get_user_id( $user_string ) { return User_Collection::BLOG_USER_ID; } - // If we're in a loop, use the post author + // If we're in a loop, use the post author. $author_id = get_the_author_meta( 'ID' ); if ( $author_id ) { return $author_id; @@ -197,11 +219,19 @@ public static function render_follow_me_block( $attrs ) { return '
'; } + /** + * Render the follower block. + * + * @param array $attrs The block attributes. + * + * @return string The HTML to render. + */ public static function render_follower_block( $attrs ) { $followee_user_id = self::get_user_id( $attrs['selectedUser'] ); if ( is_null( $followee_user_id ) ) { return ''; } + $user = User_Collection::get_by_id( $followee_user_id ); if ( is_wp_error( $user ) ) { return ''; @@ -246,9 +276,15 @@ function ( $follower ) { * * @param array $attrs The block attributes. * - * @return void + * @return string The HTML to render. */ public static function render_reply_block( $attrs ) { + /** + * Filter the reply block. + * + * @param string $html The HTML to render. + * @param array $attrs The block attributes. + */ return apply_filters( 'activitypub_reply_block', sprintf( @@ -262,6 +298,13 @@ public static function render_reply_block( $attrs ) { ); } + /** + * Render a follower. + * + * @param \Activitypub\Model\Follower $follower The follower to render. + * + * @return string The HTML to render. + */ public static function render_follower( $follower ) { $external_svg = ''; $template = diff --git a/includes/class-cli.php b/includes/class-cli.php index b30cdb9f6..64b4b8ab4 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -1,58 +1,61 @@ * - * @param array|null $args The arguments. - * @param array|null $assoc_args The associative arguments. - * - * @return void + * @param array|null $args The arguments. */ - public function post( $args, $assoc_args ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + public function post( $args ) { $post = get_post( $args[1] ); if ( ! $post ) { @@ -170,12 +170,9 @@ public function post( $args, $assoc_args ) { // phpcs:ignore VariableAnalysis.Co * * @synopsis * - * @param array|null $args The arguments. - * @param array|null $assoc_args The associative arguments. - * - * @return void + * @param array|null $args The arguments. */ - public function comment( $args, $assoc_args ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + public function comment( $args ) { $comment = get_comment( $args[1] ); if ( ! $comment ) { diff --git a/includes/class-comment.php b/includes/class-comment.php index 2d869f33e..525a35438 100644 --- a/includes/class-comment.php +++ b/includes/class-comment.php @@ -1,22 +1,24 @@ user_id; - // comments without user can't be federated + // Comments without user can't be federated. if ( ! $user_id ) { return false; } if ( is_single_user() && \user_can( $user_id, 'publish_posts' ) ) { - // On a single user site, comments by users with the `publish_posts` capability will be federated as the blog user + // On a single user site, comments by users with the `publish_posts` capability will be federated as the blog user. $user_id = Users::BLOG_USER_ID; } $is_user_disabled = is_user_disabled( $user_id ); - // user is disabled for federation + // User is disabled for federation. if ( $is_user_disabled ) { return false; } - // it is a comment to the post and can be federated + // It is a comment to the post and can be federated. if ( empty( $comment->comment_parent ) ) { return true; } - // check if parent comment is federated + // Check if parent comment is federated. $parent_comment = \get_comment( $comment->comment_parent ); return ! self::is_local( $parent_comment ); @@ -269,18 +272,18 @@ public static function object_id_to_comment( $id ) { /** * Verify if URL is a local comment, or if it is a previously received - * remote comment (For threading comments locally) + * remote comment (For threading comments locally). * * @param string $url The URL to check. * - * @return int comment_ID or null if not found + * @return string|null Comment ID or null if not found. */ public static function url_to_commentid( $url ) { if ( ! $url || ! filter_var( $url, \FILTER_VALIDATE_URL ) ) { return null; } - // check for local comment + // Check for local comment. if ( \wp_parse_url( \home_url(), \PHP_URL_HOST ) === \wp_parse_url( $url, \PHP_URL_HOST ) ) { $query = \wp_parse_url( $url, \PHP_URL_QUERY ); @@ -332,7 +335,7 @@ public static function url_to_commentid( $url ) { * @return string[] An array of classes. */ public static function comment_class( $classes, $css_class, $comment_id ) { - // check if ActivityPub comment + // Check if ActivityPub comment. if ( 'activitypub' === get_comment_meta( $comment_id, 'protocol', true ) ) { $classes[] = 'activitypub-comment'; } @@ -383,8 +386,8 @@ public static function get_source_url( $wp_comment_id, $fallback = true ) { /** * Link remote comments to source url. * - * @param string $comment_link - * @param object|WP_Comment $comment + * @param string $comment_link The comment link. + * @param object|\WP_Comment $comment The comment object. * * @return string $url */ @@ -402,21 +405,21 @@ public static function remote_comment_link( $comment_link, $comment ) { /** * Generates an ActivityPub URI for a comment * - * @param WP_Comment|int $comment A comment object or comment ID + * @param \WP_Comment|int $comment A comment object or comment ID. * * @return string ActivityPub URI for comment */ public static function generate_id( $comment ) { $comment = \get_comment( $comment ); - // show external comment ID if it exists + // Show external comment ID if it exists. $public_comment_link = self::get_source_id( $comment->comment_ID ); if ( $public_comment_link ) { return $public_comment_link; } - // generate URI based on comment ID + // Generate URI based on comment ID. return \add_query_arg( 'c', $comment->comment_ID, \trailingslashit( \home_url() ) ); } @@ -455,22 +458,22 @@ private static function post_has_remote_comments( $post_id ) { */ public static function enqueue_scripts() { if ( ! \is_singular() || \is_user_logged_in() ) { - // only on single pages, only for logged out users + // Only on single pages, only for logged-out users. return; } if ( ! \post_type_supports( \get_post_type(), 'activitypub' ) ) { - // post type does not support ActivityPub + // Post type does not support ActivityPub. return; } if ( ! \comments_open() || ! \get_comments_number() ) { - // no comments, no need to load the script + // No comments, no need to load the script. return; } if ( ! self::post_has_remote_comments( \get_the_ID() ) ) { - // no remote comments, no need to load the script + // No remote comments, no need to load the script. return; } @@ -514,9 +517,9 @@ public static function get_comment_types() { } /** - * Is this a registered comment type + * Is this a registered comment type. * - * @param string $slug The name of the type + * @param string $slug The name of the type. * @return boolean True if registered. */ public static function is_registered_comment_type( $slug ) { @@ -529,18 +532,18 @@ public static function is_registered_comment_type( $slug ) { /** * Return the registered custom comment types names. * - * @return array The registered custom comment type names + * @return array The registered custom comment type names. */ public static function get_comment_type_names() { return array_values( wp_list_pluck( self::get_comment_types(), 'type' ) ); } /** - * Get a comment type + * Get a comment type. * - * @param string $type The comment type + * @param string $type The comment type. * - * @return array The comment type + * @return array The comment type. */ public static function get_comment_type( $type ) { $type = strtolower( $type ); @@ -553,16 +556,21 @@ public static function get_comment_type( $type ) { $type_array = array(); } + /** + * Filter the comment type. + * + * @param array $type_array The comment type. + */ return apply_filters( "activitypub_comment_type_{$type}", $type_array ); } /** - * Get a comment type attribute + * Get a comment type attribute. * - * @param string $type The comment type - * @param string $attr The attribute to get + * @param string $type The comment type. + * @param string $attr The attribute to get. * - * @return mixed The value of the attribute + * @return mixed The value of the attribute. */ public static function get_comment_type_attr( $type, $attr ) { $type_array = self::get_comment_type( $type ); @@ -573,15 +581,17 @@ public static function get_comment_type_attr( $type, $attr ) { $value = ''; } + /** + * Filter the comment type attribute. + * + * @param mixed $value The value of the attribute. + * @param string $type The comment type. + */ return apply_filters( "activitypub_comment_type_{$attr}", $value, $type ); } - - /** - * Register the comment types used by the ActivityPub plugin - * - * @return void + * Register the comment types used by the ActivityPub plugin. */ public static function register_comment_types() { register_comment_type( @@ -593,7 +603,7 @@ public static function register_comment_types() { 'icon' => '♻️', 'class' => 'p-repost', 'type' => 'repost', - // translators: %1$s username, %2$s opject format (post, audio, ...), %3$s URL, %4$s domain + // translators: %1$s username, %2$s object format (post, audio, ...), %3$s URL, %4$s domain. 'excerpt' => __( '… reposted this!', 'activitypub' ), ) ); @@ -607,16 +617,16 @@ public static function register_comment_types() { 'icon' => '👍', 'class' => 'p-like', 'type' => 'like', - // translators: %1$s username, %2$s opject format (post, audio, ...), %3$s URL, %4$s domain + // translators: %1$s username, %2$s object format (post, audio, ...), %3$s URL, %4$s domain. 'excerpt' => __( '… liked this!', 'activitypub' ), ) ); } /** - * Show avatars on Activities if set + * Show avatars on Activities if set. * - * @param array $types list of avatar enabled comment types + * @param array $types List of avatar enabled comment types. * * @return array show avatars on Activities */ @@ -634,7 +644,7 @@ public static function get_avatar_comment_types( $types ) { * * @see https://github.com/janboddez/indieblocks/blob/a2d59de358031056a649ee47a1332ce9e39d4ce2/includes/functions.php#L423-L432 * - * @param WP_Comment_Query $query Comment count. + * @param WP_Comment_Query $query Comment count. */ public static function comment_query( $query ) { if ( ! $query instanceof WP_Comment_Query ) { diff --git a/includes/class-debug.php b/includes/class-debug.php index bfd393eb2..ebca05349 100644 --- a/includes/class-debug.php +++ b/includes/class-debug.php @@ -1,46 +1,66 @@ post_content, $match ) ) { @@ -64,25 +66,23 @@ public static function insert_post( $id, $post ) { $tags = \implode( ', ', $tags ); \wp_add_post_tags( $post->ID, $tags ); - - return $id; } /** - * Filter to replace the #tags in the content with links + * Filter to replace the #tags in the content with links. * - * @param string $the_content the post-content + * @param string $the_content The post content. * - * @return string the filtered post-content + * @return string The filtered post content. */ public static function the_content( $the_content ) { return enrich_content_data( $the_content, '/' . ACTIVITYPUB_HASHTAGS_REGEXP . '/i', array( self::class, 'replace_with_links' ) ); } /** - * A callback for preg_replace to build the term links + * A callback for preg_replace to build the term links. * - * @param array $result the preg_match results + * @param array $result The preg_match results. * @return string the final string */ public static function replace_with_links( $result ) { @@ -94,7 +94,7 @@ public static function replace_with_links( $result ) { if ( $tag_object ) { $link = \get_term_link( $tag_object, 'post_tag' ); - return \sprintf( '', $link, $tag ); + return \sprintf( '', esc_url( $link ), $tag ); } return '#' . $tag; diff --git a/includes/class-health-check.php b/includes/class-health-check.php index 7e782f3d0..65e6a9e4d 100644 --- a/includes/class-health-check.php +++ b/includes/class-health-check.php @@ -1,31 +1,37 @@ %s %s

', - __( 'https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/', 'activitypub' ), + esc_url( __( 'https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/', 'activitypub' ) ), __( 'Learn how to hook the WP-Cron into the System Task Scheduler.', 'activitypub' ), /* translators: Hidden accessibility text. */ __( '(opens in a new tab)', 'activitypub' ) @@ -124,7 +130,7 @@ public static function test_system_cron() { } /** - * WebFinger tests + * WebFinger tests. * * @return array */ @@ -162,21 +168,21 @@ public static function test_webfinger() { } /** - * Check if `author_posts_url` is accessible and that request returns correct JSON + * Check if `author_posts_url` is accessible and that request returns correct JSON. * - * @return boolean|WP_Error + * @return bool|WP_Error True if the author URL is accessible, WP_Error otherwise. */ public static function is_author_url_accessible() { $user = \wp_get_current_user(); $author_url = \get_author_posts_url( $user->ID ); $reference_author_url = self::get_author_posts_url( $user->ID, $user->user_nicename ); - // check for "author" in URL + // Check for "author" in URL. if ( $author_url !== $reference_author_url ) { return new WP_Error( 'author_url_not_accessible', \sprintf( - // translators: %s: Author URL + // translators: %s: Author URL. \__( 'Your author URL %s was replaced, this is often done by plugins.', 'activitypub' @@ -186,7 +192,7 @@ public static function is_author_url_accessible() { ); } - // try to access author URL + // Try to access author URL. $response = \wp_remote_get( $author_url, array( @@ -199,7 +205,7 @@ public static function is_author_url_accessible() { return new WP_Error( 'author_url_not_accessible', \sprintf( - // translators: %s: Author URL + // translators: %s: Author URL. \__( 'Your author URL %s is not accessible. Please check your WordPress setup or permalink structure. If the setup seems fine, maybe check if a plugin might restrict the access.', 'activitypub' @@ -211,12 +217,12 @@ public static function is_author_url_accessible() { $response_code = \wp_remote_retrieve_response_code( $response ); - // check for redirects + // Check for redirects. if ( \in_array( $response_code, array( 301, 302, 307, 308 ), true ) ) { return new WP_Error( 'author_url_not_accessible', \sprintf( - // translators: %s: Author URL + // translators: %s: Author URL. \__( 'Your author URL %s is redirecting to another page, this is often done by SEO plugins like "Yoast SEO".', 'activitypub' @@ -226,14 +232,14 @@ public static function is_author_url_accessible() { ); } - // check if response is JSON + // Check if response is JSON. $body = \wp_remote_retrieve_body( $response ); if ( ! \is_string( $body ) || ! \is_array( \json_decode( $body, true ) ) ) { return new WP_Error( 'author_url_not_accessible', \sprintf( - // translators: %s: Author URL + // translators: %s: Author URL. \__( 'Your author URL %s does not return valid JSON for application/activity+json. Please check if your hosting supports alternate Accept headers.', 'activitypub' @@ -260,7 +266,7 @@ public static function is_webfinger_endpoint_accessible() { $allowed = array( 'code' => array() ); $not_accessible = wp_kses( - // translators: %s: Author URL + // translators: %s: Author URL. \__( 'Your WebFinger endpoint %s is not accessible. Please check your WordPress setup or permalink structure.', 'activitypub' @@ -268,7 +274,7 @@ public static function is_webfinger_endpoint_accessible() { $allowed ); $invalid_response = wp_kses( - // translators: %s: Author URL + // translators: %s: Author URL. \__( 'Your WebFinger endpoint %s does not return valid JSON for application/jrd+json.', 'activitypub' @@ -282,7 +288,7 @@ public static function is_webfinger_endpoint_accessible() { $url->get_error_data()['data'] ), 'webfinger_url_invalid_response' => \sprintf( - // translators: %s: Author URL + // translators: %s: Author URL. $invalid_response, $url->get_error_data()['data'] ), @@ -291,6 +297,7 @@ public static function is_webfinger_endpoint_accessible() { if ( isset( $health_messages[ $url->get_error_code() ] ) ) { $message = $health_messages[ $url->get_error_code() ]; } + return new WP_Error( $url->get_error_code(), $message, @@ -304,7 +311,7 @@ public static function is_webfinger_endpoint_accessible() { /** * Retrieve the URL to the author page for the user with the ID provided. * - * @global WP_Rewrite $wp_rewrite WordPress rewrite component. + * @global \WP_Rewrite $wp_rewrite WordPress rewrite component. * * @param int $author_id Author ID. * @param string $author_nicename Optional. The author's nicename (slug). Default empty. @@ -313,6 +320,7 @@ public static function is_webfinger_endpoint_accessible() { */ public static function get_author_posts_url( $author_id, $author_nicename = '' ) { global $wp_rewrite; + $auth_id = (int) $author_id; $link = $wp_rewrite->get_author_permastruct(); diff --git a/includes/class-http.php b/includes/class-http.php index 7cc5f9ae6..133a34691 100644 --- a/includes/class-http.php +++ b/includes/class-http.php @@ -1,11 +1,15 @@ $code ) ); } + /** + * Action to save the response of the remote POST request. + * + * @param array|WP_Error $response The response of the remote POST request. + * @param string $url The URL endpoint. + * @param string $body The Post Body. + * @param int $user_id The WordPress User-ID. + */ \do_action( 'activitypub_safe_remote_post_response', $response, $url, $body, $user_id ); return $response; } /** - * Send a GET Request with the needed HTTP Headers + * Send a GET Request with the needed HTTP Headers. * - * @param string $url The URL endpoint - * @param bool|int $cached If the result should be cached, or its duration. Default: 1hr. + * @param string $url The URL endpoint. + * @param bool|int $cached Optional. Whether the result should be cached, or its duration. Default false. * - * @return array|WP_Error The GET Response or an WP_ERROR + * @return array|WP_Error The GET Response or a WP_Error. */ public static function get( $url, $cached = false ) { \do_action( 'activitypub_pre_http_get', $url ); @@ -80,6 +92,12 @@ public static function get( $url, $cached = false ) { $response = \get_transient( $transient_key ); if ( $response ) { + /** + * Action to save the response of the remote GET request. + * + * @param array|WP_Error $response The response of the remote GET request. + * @param string $url The URL endpoint. + */ \do_action( 'activitypub_safe_remote_get_response', $response, $url ); return $response; @@ -118,6 +136,12 @@ public static function get( $url, $cached = false ) { $response = new WP_Error( $code, __( 'Failed HTTP Request', 'activitypub' ), array( 'status' => $code ) ); } + /** + * Action to save the response of the remote GET request. + * + * @param array|WP_Error $response The response of the remote GET request. + * @param string $url The URL endpoint. + */ \do_action( 'activitypub_safe_remote_get_response', $response, $url ); if ( $cached ) { @@ -139,6 +163,11 @@ public static function get( $url, $cached = false ) { * @return bool True if the URL is a tombstone. */ public static function is_tombstone( $url ) { + /** + * Action before checking if the URL is a tombstone. + * + * @param string $url The URL to check. + */ \do_action( 'activitypub_pre_http_is_tombstone', $url ); $response = \wp_safe_remote_get( $url ); @@ -151,15 +180,22 @@ public static function is_tombstone( $url ) { return false; } + /** + * Generate a cache key for the URL. + * + * @param string $url The URL to generate the cache key for. + * + * @return string The cache key. + */ public static function generate_cache_key( $url ) { return 'activitypub_http_' . \md5( $url ); } /** - * Requests the Data from the Object-URL or Object-Array + * Requests the Data from the Object-URL or Object-Array. * * @param array|string $url_or_object The Object or the Object URL. - * @param bool $cached If the result should be cached. + * @param bool $cached Optional. Whether the result should be cached. Default true. * * @return array|WP_Error The Object data as array or WP_Error on failure. */ @@ -204,7 +240,7 @@ public static function get_remote_object( $url_or_object, $cached = true ) { $transient_key = self::generate_cache_key( $url ); - // only check the cache if needed. + // Only check the cache if needed. if ( $cached ) { $data = \get_transient( $transient_key ); diff --git a/includes/class-link.php b/includes/class-link.php index 52a814000..3c53f3bb8 100644 --- a/includes/class-link.php +++ b/includes/class-link.php @@ -1,61 +1,66 @@ 'ID' ) ) as $user_id ) { $followers = get_user_meta( $user_id, 'activitypub_followers', true ); @@ -210,9 +208,7 @@ public static function migrate_from_0_17() { } /** - * Clear the cache after updating to 1.3.0 - * - * @return void + * Clear the cache after updating to 1.3.0. */ private static function migrate_from_1_2_0() { $user_ids = \get_users( @@ -228,9 +224,7 @@ private static function migrate_from_1_2_0() { } /** - * Unschedule Hooks after updating to 2.0.0 - * - * @return void + * Unschedule Hooks after updating to 2.0.0. */ private static function migrate_from_2_0_0() { wp_clear_scheduled_hook( 'activitypub_send_post_activity' ); @@ -249,19 +243,15 @@ private static function migrate_from_2_0_0() { /** * Add the ActivityPub capability to all users that can publish posts - * Delete old meta to store followers - * - * @return void + * Delete old meta to store followers. */ private static function migrate_from_2_2_0() { - // add the ActivityPub capability to all users that can publish posts + // Add the ActivityPub capability to all users that can publish posts. self::add_activitypub_capability(); } /** - * Rename DB fields - * - * @return void + * Rename DB fields. */ private static function migrate_from_2_6_0() { wp_cache_flush(); @@ -273,30 +263,26 @@ private static function migrate_from_2_6_0() { } /** - * Set the defaults needed for the plugin to work + * Set the defaults needed for the plugin to work. * - * * Add the ActivityPub capability to all users that can publish posts - * - * @return void + * Add the ActivityPub capability to all users that can publish posts. */ public static function add_default_settings() { self::add_activitypub_capability(); } /** - * Add the ActivityPub capability to all users that can publish posts - * - * @return void + * Add the ActivityPub capability to all users that can publish posts. */ private static function add_activitypub_capability() { - // get all WP_User objects that can publish posts + // Get all WP_User objects that can publish posts. $users = \get_users( array( 'capability__in' => array( 'publish_posts' ), ) ); - // add ActivityPub capability to all users that can publish posts + // Add ActivityPub capability to all users that can publish posts. foreach ( $users as $user ) { $user->add_cap( 'activitypub' ); } @@ -305,16 +291,16 @@ private static function add_activitypub_capability() { /** * Rename meta keys. * - * @param string $old The old commentmeta key - * @param string $new The new commentmeta key + * @param string $old_key The old comment meta key. + * @param string $new_key The new comment meta key. */ - private static function update_usermeta_key( $old, $new ) { // phpcs:ignore + private static function update_usermeta_key( $old_key, $new_key ) { global $wpdb; - $wpdb->update( // phpcs:ignore + $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery $wpdb->usermeta, - array( 'meta_key' => $new ), // phpcs:ignore - array( 'meta_key' => $old ), // phpcs:ignore + array( 'meta_key' => $new_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + array( 'meta_key' => $old_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key array( '%s' ), array( '%s' ) ); @@ -323,16 +309,16 @@ private static function update_usermeta_key( $old, $new ) { // phpcs:ignore /** * Rename option keys. * - * @param string $old The old option key - * @param string $new The new option key + * @param string $old_key The old option key. + * @param string $new_key The new option key. */ - private static function update_options_key( $old, $new ) { // phpcs:ignore + private static function update_options_key( $old_key, $new_key ) { global $wpdb; - $wpdb->update( // phpcs:ignore + $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery $wpdb->options, - array( 'option_name' => $new ), // phpcs:ignore - array( 'option_name' => $old ), // phpcs:ignore + array( 'option_name' => $new_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + array( 'option_name' => $old_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key array( '%s' ), array( '%s' ) ); diff --git a/includes/class-notification.php b/includes/class-notification.php index 3aed0207f..bab72774c 100644 --- a/includes/class-notification.php +++ b/includes/class-notification.php @@ -1,4 +1,9 @@ user_id ) { return; } @@ -211,7 +207,7 @@ public static function schedule_comment_activity( $new_status, $old_status, $com return; } - // check if comment should be federated or not + // Check if comment should be federated or not. if ( ! should_comment_be_federated( $comment ) ) { return; } @@ -226,9 +222,7 @@ public static function schedule_comment_activity( $new_status, $old_status, $com } /** - * Update followers - * - * @return void + * Update followers. */ public static function update_followers() { $number = 5; @@ -237,6 +231,11 @@ public static function update_followers() { $number = 50; } + /** + * Filter the number of followers to update. + * + * @param int $number The number of followers to update. + */ $number = apply_filters( 'activitypub_update_followers_number', $number ); $followers = Followers::get_outdated_followers( $number ); @@ -253,9 +252,7 @@ public static function update_followers() { } /** - * Cleanup followers - * - * @return void + * Cleanup followers. */ public static function cleanup_followers() { $number = 5; @@ -264,6 +261,11 @@ public static function cleanup_followers() { $number = 50; } + /** + * Filter the number of followers to clean up. + * + * @param int $number The number of followers to clean up. + */ $number = apply_filters( 'activitypub_update_followers_number', $number ); $followers = Followers::get_faulty_followers( $number ); @@ -292,18 +294,17 @@ public static function cleanup_followers() { /** * Send a profile update when relevant user meta is updated. * - * @param int $meta_id Meta ID being updated. - * @param int $user_id User ID being updated. + * @param int $meta_id Meta ID being updated. + * @param int $user_id User ID being updated. * @param string $meta_key Meta key being updated. - * - * @return void */ public static function user_meta_update( $meta_id, $user_id, $meta_key ) { - // don't bother if the user can't publish + // Don't bother if the user can't publish. if ( ! \user_can( $user_id, 'activitypub' ) ) { return; } - // the user meta fields that affect a profile. + + // The user meta fields that affect a profile. $fields = array( 'activitypub_description', 'activitypub_header_image', @@ -319,12 +320,10 @@ public static function user_meta_update( $meta_id, $user_id, $meta_key ) { /** * Send a profile update when a user is updated. * - * @param int $user_id User ID being updated. - * - * @return void + * @param int $user_id User ID being updated. */ public static function user_update( $user_id ) { - // don't bother if the user can't publish + // Don't bother if the user can't publish. if ( ! \user_can( $user_id, 'activitypub' ) ) { return; } @@ -335,7 +334,7 @@ public static function user_update( $user_id ) { /** * Theme mods only have a dynamic filter so we fudge it like this. * - * @param mixed $value + * @param mixed $value Optional. The value to be updated. Default null. * * @return mixed */ diff --git a/includes/class-shortcodes.php b/includes/class-shortcodes.php index 06bb796ed..eb9c5135b 100644 --- a/includes/class-shortcodes.php +++ b/includes/class-shortcodes.php @@ -1,12 +1,18 @@ post_type ) { - // get title of attachment with fallback to alt text. + // Get title of attachment with fallback to alt text. $content = wp_get_attachment_caption( $item->ID ); if ( empty( $content ) ) { $content = get_post_meta( $item->ID, '_wp_attachment_image_alt', true ); @@ -154,7 +156,7 @@ public static function content( $atts, $content, $tag ) { $content = wp_filter_content_tags( $content ); } - // replace script and style elements + // Replace script and style elements. $content = \preg_replace( '@<(script|style)[^>]*?>.*?@si', '', $content ); $content = strip_shortcodes( $content ); $content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) ); @@ -166,7 +168,7 @@ public static function content( $atts, $content, $tag ) { } /** - * Generates output for the 'ap_permalink' Shortcode + * Generates output for the 'ap_permalink' Shortcode. * * @param array $atts The Shortcode attributes. * @param string $content The ActivityPub post-content. @@ -200,7 +202,7 @@ public static function permalink( $atts, $content, $tag ) { } /** - * Generates output for the 'ap_shortlink' Shortcode + * Generates output for the 'ap_shortlink' Shortcode. * * @param array $atts The Shortcode attributes. * @param string $content The ActivityPub post-content. @@ -234,7 +236,7 @@ public static function shortlink( $atts, $content, $tag ) { } /** - * Generates output for the 'ap_image' Shortcode + * Generates output for the 'ap_image' Shortcode. * * @param array $atts The Shortcode attributes. * @param string $content The ActivityPub post-content. @@ -277,7 +279,7 @@ public static function image( $atts, $content, $tag ) { } /** - * Generates output for the 'ap_hashcats' Shortcode + * Generates output for the 'ap_hashcats' Shortcode. * * @return string The post categories as hashtags. */ @@ -308,7 +310,7 @@ public static function hashcats() { } /** - * Generates output for the 'ap_author' Shortcode + * Generates output for the 'ap_author' Shortcode. * * @return string The author name. */ @@ -330,7 +332,7 @@ public static function author() { } /** - * Generates output for the 'ap_authorurl' Shortcode + * Generates output for the 'ap_authorurl' Shortcode. * * @return string The author URL. */ @@ -352,7 +354,7 @@ public static function authorurl() { } /** - * Generates output for the 'ap_blogurl' Shortcode + * Generates output for the 'ap_blogurl' Shortcode. * * @return string The site URL. */ @@ -361,7 +363,7 @@ public static function blogurl() { } /** - * Generates output for the 'ap_blogname' Shortcode + * Generates output for the 'ap_blogname' Shortcode. * * @return string */ @@ -370,7 +372,7 @@ public static function blogname() { } /** - * Generates output for the 'ap_blogdesc' Shortcode + * Generates output for the 'ap_blogdesc' Shortcode. * * @return string The site description. */ @@ -379,7 +381,7 @@ public static function blogdesc() { } /** - * Generates output for the 'ap_date' Shortcode + * Generates output for the 'ap_date' Shortcode. * * @return string The post date. */ @@ -403,7 +405,7 @@ public static function date() { } /** - * Generates output for the 'ap_time' Shortcode + * Generates output for the 'ap_time' Shortcode. * * @return string The post time. */ @@ -427,7 +429,7 @@ public static function time() { } /** - * Generates output for the 'ap_datetime' Shortcode + * Generates output for the 'ap_datetime' Shortcode. * * @return string The post date/time. */ @@ -457,7 +459,7 @@ public static function datetime() { * Checks if item (WP_Post) is "public", a supported post type * and not password protected. * - * @return null|WP_Post The WordPress item. + * @return null|\WP_Post The WordPress item. */ protected static function get_item() { $post = \get_post(); diff --git a/includes/class-signature.php b/includes/class-signature.php index 07831583e..aa2a109bd 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -1,4 +1,10 @@ $detail['key'], ); - // persist keys + // Persist keys. \add_option( $option_key, $key_pair ); return $key_pair; @@ -135,7 +141,7 @@ protected static function get_signature_options_key_for( $user_id ) { if ( $user_id > 0 ) { $user = \get_userdata( $user_id ); - // sanatize username because it could include spaces and special chars + // Sanitize username because it could include spaces and special chars. $id = sanitize_title( $user->user_login ); } @@ -176,13 +182,13 @@ protected static function check_legacy_key_pair_for( $user_id ) { } /** - * Generates the Signature for a HTTP Request + * Generates the Signature for an HTTP Request. * * @param int $user_id The WordPress User ID. * @param string $http_method The HTTP method. * @param string $url The URL to send the request to. * @param string $date The date the request is sent. - * @param string $digest The digest of the request body. + * @param string $digest Optional. The digest of the request body. Default null. * * @return string The signature. */ @@ -195,12 +201,12 @@ public static function generate_signature( $user_id, $http_method, $url, $date, $host = $url_parts['host']; $path = '/'; - // add path + // Add path. if ( ! empty( $url_parts['path'] ) ) { $path = $url_parts['path']; } - // add query + // Add query. if ( ! empty( $url_parts['query'] ) ) { $path .= '?' . $url_parts['query']; } @@ -231,18 +237,18 @@ public static function generate_signature( $user_id, $http_method, $url, $date, * * @param WP_REST_Request|array $request The request object or $_SERVER array. * - * @return mixed A boolean or WP_Error. + * @return bool|WP_Error A boolean or WP_Error. */ public static function verify_http_signature( $request ) { - if ( is_object( $request ) ) { // REST Request object - // check if route starts with "index.php" + if ( is_object( $request ) ) { // REST Request object. + // Check if route starts with "index.php". if ( str_starts_with( $request->get_route(), '/index.php' ) || ! rest_get_url_prefix() ) { $route = $request->get_route(); } else { $route = '/' . rest_get_url_prefix() . '/' . ltrim( $request->get_route(), '/' ); } - // fix route for subdirectory installs + // Fix route for subdirectory installs. $path = \wp_parse_url( \get_home_url(), PHP_URL_PATH ); if ( \is_string( $path ) ) { @@ -323,14 +329,14 @@ public static function verify_http_signature( $request ) { } /** - * Get public key from key_id + * Get public key from key_id. * * @param string $key_id The URL to the public key. * * @return WP_Error|string The public key or WP_Error. */ - public static function get_remote_key( $key_id ) { // phpcs:ignore - $actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) ); // phpcs:ignore + public static function get_remote_key( $key_id ) { + $actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) ); if ( \is_wp_error( $actor ) ) { return new WP_Error( 'activitypub_no_remote_profile_found', @@ -349,9 +355,9 @@ public static function get_remote_key( $key_id ) { // phpcs:ignore } /** - * Gets the signature algorithm from the signature header + * Gets the signature algorithm from the signature header. * - * @param array $signature_block + * @param array $signature_block The signature block. * * @return string The signature algorithm. */ @@ -359,7 +365,7 @@ public static function get_signature_algorithm( $signature_block ) { if ( $signature_block['algorithm'] ) { switch ( $signature_block['algorithm'] ) { case 'rsa-sha-512': - return 'sha512'; // hs2019 https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12 + return 'sha512'; // hs2019 https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12. default: return 'sha256'; } @@ -368,11 +374,11 @@ public static function get_signature_algorithm( $signature_block ) { } /** - * Parses the Signature header + * Parses the Signature header. * * @param string $signature The signature header. * - * @return array signature parts + * @return array Signature parts. */ public static function parse_signature_header( $signature ) { $parsed_header = array(); @@ -405,16 +411,17 @@ public static function parse_signature_header( $signature ) { } /** - * Gets the header data from the included pseudo headers + * Gets the header data from the included pseudo headers. * * @param array $signed_headers The signed headers. - * @param array $signature_block (pseudo-headers) - * @param array $headers (http headers) + * @param array $signature_block The signature block. + * @param array $headers The HTTP headers. * * @return string signed headers for comparison */ public static function get_signed_data( $signed_headers, $signature_block, $headers ) { $signed_data = ''; + // This also verifies time-based values by returning false if any of these are out of range. foreach ( $signed_headers as $header ) { if ( 'host' === $header ) { @@ -433,7 +440,7 @@ public static function get_signed_data( $signed_headers, $signature_block, $head } if ( '(created)' === $header ) { if ( ! empty( $signature_block['(created)'] ) && \intval( $signature_block['(created)'] ) > \time() ) { - // created in future + // Created in the future. return false; } @@ -444,7 +451,7 @@ public static function get_signed_data( $signed_headers, $signature_block, $head } if ( '(expires)' === $header ) { if ( ! empty( $signature_block['(expires)'] ) && \intval( $signature_block['(expires)'] ) < \time() ) { - // expired in past + // Expired in the past. return false; } @@ -454,7 +461,7 @@ public static function get_signed_data( $signed_headers, $signature_block, $head } } if ( 'date' === $header ) { - // allow a bit of leeway for misconfigured clocks. + // Allow a bit of leeway for misconfigured clocks. $d = new DateTime( $headers[ $header ][0] ); $d->setTimeZone( new DateTimeZone( 'UTC' ) ); $c = $d->format( 'U' ); @@ -463,7 +470,7 @@ public static function get_signed_data( $signed_headers, $signature_block, $head $dminus = time() - ( 3 * HOUR_IN_SECONDS ); if ( $c > $dplus || $c < $dminus ) { - // time out of range + // Time out of range. return false; } } @@ -473,22 +480,22 @@ public static function get_signed_data( $signed_headers, $signature_block, $head } /** - * Generates the digest for a HTTP Request + * Generates the digest for an HTTP Request. * * @param string $body The body of the request. * * @return string The digest. */ public static function generate_digest( $body ) { - $digest = \base64_encode( \hash( 'sha256', $body, true ) ); // phpcs:ignore + $digest = \base64_encode( \hash( 'sha256', $body, true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode return "SHA-256=$digest"; } /** * Formats the $_SERVER to resemble the WP_REST_REQUEST array, - * for use with verify_http_signature() + * for use with verify_http_signature(). * - * @param array $_SERVER The $_SERVER array. + * @param array $server The $_SERVER array. * * @return array $request The formatted request array. */ diff --git a/includes/class-webfinger.php b/includes/class-webfinger.php index f26de250d..f6a189af3 100644 --- a/includes/class-webfinger.php +++ b/includes/class-webfinger.php @@ -1,11 +1,17 @@ @ + * Transform a URI to an acct @. * - * @param string $uri The URI (acct:, mailto:, http:, https:) + * @param string $uri The URI (acct:, mailto:, http:, https:). * - * @return string|WP_Error Error or acct URI + * @return string|WP_Error Error or acct URI. */ public static function uri_to_acct( $uri ) { $data = self::get_data( $uri ); @@ -89,7 +95,7 @@ public static function uri_to_acct( $uri ) { return $data; } - // check if subject is an acct URI + // Check if subject is an acct URI. if ( isset( $data['subject'] ) && \str_starts_with( $data['subject'], 'acct:' ) @@ -97,7 +103,7 @@ public static function uri_to_acct( $uri ) { return $data['subject']; } - // search for an acct URI in the aliases + // Search for an acct URI in the aliases. if ( isset( $data['aliases'] ) ) { foreach ( $data['aliases'] as $alias ) { if ( \str_starts_with( $alias, 'acct:' ) ) { @@ -120,10 +126,9 @@ public static function uri_to_acct( $uri ) { * Convert a URI string to an identifier and its host. * Automatically adds acct: if it's missing. * - * @param string $url The URI (acct:, mailto:, http:, https:) + * @param string $url The URI (acct:, mailto:, http:, https:). * - * @return WP_Error|array Error reaction or array with - * identifier and host as values + * @return WP_Error|array Error reaction or array with identifier and host as values. */ public static function get_identifier_and_host( $url ) { if ( ! $url ) { @@ -137,7 +142,7 @@ public static function get_identifier_and_host( $url ) { ); } - // remove leading @ + // Remove leading @. $url = ltrim( $url, '@' ); if ( ! preg_match( '/^([a-zA-Z+]+):/', $url, $match ) ) { @@ -178,12 +183,11 @@ public static function get_identifier_and_host( $url ) { } /** - * Get the WebFinger data for a given URI + * Get the WebFinger data for a given URI. * - * @param string $uri The Identifier: @ or URI + * @param string $uri The Identifier: @ or URI. * - * @return WP_Error|array Error reaction or array with - * identifier and host as values + * @return WP_Error|array Error reaction or array with identifier and host as values. */ public static function get_data( $uri ) { $identifier_and_host = self::get_identifier_and_host( $uri ); @@ -234,7 +238,9 @@ public static function get_data( $uri ) { } /** - * Get the Remote-Follow endpoint for a given URI + * Get the Remote-Follow endpoint for a given URI. + * + * @param string $uri The WebFinger Resource URI. * * @return string|WP_Error Error or the Remote-Follow endpoint URI. */ @@ -273,11 +279,11 @@ public static function get_remote_follow_endpoint( $uri ) { } /** - * Generate a cache key for a given URI + * Generate a cache key for a given URI. * - * @param string $uri A WebFinger Resource URI + * @param string $uri A WebFinger Resource URI. * - * @return string The cache key + * @return string The cache key. */ public static function generate_cache_key( $uri ) { $uri = ltrim( $uri, '@' ); diff --git a/includes/collection/class-extra-fields.php b/includes/collection/class-extra-fields.php index 25362e9f5..320ed478a 100644 --- a/includes/collection/class-extra-fields.php +++ b/includes/collection/class-extra-fields.php @@ -1,13 +1,19 @@ ]*?>.*?@si', '', $content ); $content = \strip_shortcodes( $content ); $content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) ); - $content = \apply_filters( 'activitypub_extra_field_content', $content, $post ); - return $content; + /** + * Filters the content of an extra field. + * + * @param string $content The content. + * @param \WP_Post $post The post. + */ + return \apply_filters( 'activitypub_extra_field_content', $content, $post ); } /** - * Transforms the Extra Fields (Cutom Post Types) to ActivityPub Actor-Attachments. + * Transforms the Extra Fields (Custom Post Types) to ActivityPub Actor-Attachments. * * @param \WP_Post[] $fields The extra fields. * @@ -88,7 +107,7 @@ function ( $rel ) { $link_added = false; - // Add support for FEP-fb2a, for more information see FEDERATION.md + // Add support for FEP-fb2a, for more information see FEDERATION.md. $link_content = \trim( \strip_tags( $content, '' ) ); if ( \stripos( $link_content, 'get_posts() ); + return compact( 'followers', 'total' ); } /** - * Get all Followers - * - * @param array $args The WP_Query arguments. + * Get all Followers. * * @return array The Term list of Followers. */ @@ -219,7 +225,7 @@ public static function get_all_followers() { /** * Count the total number of followers * - * @param int $user_id The ID of the WordPress User + * @param int $user_id The ID of the WordPress User. * * @return int The number of Followers */ @@ -251,11 +257,11 @@ public static function count_followers( $user_id ) { } /** - * Returns all Inboxes fo a Users Followers + * Returns all Inboxes for a Users Followers. * - * @param int $user_id The ID of the WordPress User + * @param int $user_id The ID of the WordPress User. * - * @return array The list of Inboxes + * @return array The list of Inboxes. */ public static function get_inboxes( $user_id ) { $cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id ); @@ -265,7 +271,7 @@ public static function get_inboxes( $user_id ) { return $inboxes; } - // get all Followers of a ID of the WordPress User + // Get all Followers of a ID of the WordPress User. $posts = new WP_Query( array( 'nopaging' => true, @@ -316,13 +322,12 @@ public static function get_inboxes( $user_id ) { } /** - * Get all Followers that have not been updated for a given time + * Get all Followers that have not been updated for a given time. * - * @param enum $output The output format, supported ARRAY_N, OBJECT and ACTIVITYPUB_OBJECT. - * @param int $number Limits the result. - * @param int $older_than The time in seconds. + * @param int $number Optional. Limits the result. Default 50. + * @param int $older_than Optional. The time in seconds. Default 86400 (1 day). * - * @return mixed The Term list of Followers, the format depends on $output. + * @return array The Term list of Followers. */ public static function get_outdated_followers( $number = 50, $older_than = 86400 ) { $args = array( @@ -330,7 +335,7 @@ public static function get_outdated_followers( $number = 50, $older_than = 86400 'posts_per_page' => $number, 'orderby' => 'modified', 'order' => 'ASC', - 'post_status' => 'any', // 'any' includes 'trash + 'post_status' => 'any', // 'any' includes 'trash'. 'date_query' => array( array( 'column' => 'post_modified_gmt', @@ -343,19 +348,18 @@ public static function get_outdated_followers( $number = 50, $older_than = 86400 $items = array(); foreach ( $posts->get_posts() as $follower ) { - $items[] = Follower::init_from_cpt( $follower ); // phpcs:ignore + $items[] = Follower::init_from_cpt( $follower ); } return $items; } /** - * Get all Followers that had errors + * Get all Followers that had errors. * - * @param enum $output The output format, supported ARRAY_N, OBJECT and ACTIVITYPUB_OBJECT - * @param integer $number The number of Followers to return. + * @param int $number Optional. The number of Followers to return. Default 20. * - * @return mixed The Term list of Followers, the format depends on $output. + * @return array The Term list of Followers. */ public static function get_faulty_followers( $number = 20 ) { $args = array( @@ -393,7 +397,7 @@ public static function get_faulty_followers( $number = 20 ) { $items = array(); foreach ( $posts->get_posts() as $follower ) { - $items[] = Follower::init_from_cpt( $follower ); // phpcs:ignore + $items[] = Follower::init_from_cpt( $follower ); } return $items; @@ -403,8 +407,7 @@ public static function get_faulty_followers( $number = 20 ) { * This function is used to store errors that occur when * sending an ActivityPub message to a Follower. * - * The error will be stored in the - * post meta. + * The error will be stored in post meta. * * @param int $post_id The ID of the WordPress Custom-Post-Type. * @param mixed $error The error message. Can be a string or a WP_Error. diff --git a/includes/collection/class-interactions.php b/includes/collection/class-interactions.php index 802070a6f..36262e862 100644 --- a/includes/collection/class-interactions.php +++ b/includes/collection/class-interactions.php @@ -1,7 +1,12 @@ comment_post_ID; } - // not a reply to a post or comment + // Not a reply to a post or comment. if ( ! $comment_post_id ) { return false; } @@ -53,16 +58,16 @@ public static function add_comment( $activity ) { } /** - * Update a comment + * Update a comment. * - * @param array $activity The activity-object + * @param array $activity The activity object. * - * @return array|string|int|\WP_Error|false The commentdata or false on failure + * @return array|string|int|\WP_Error|false The comment data or false on failure. */ public static function update_comment( $activity ) { $meta = get_remote_metadata_by_actor( $activity['actor'] ); - // Determine comment_ID + // Determine comment_ID. $comment = object_id_to_comment( \esc_url_raw( $activity['object']['id'] ) ); $commentdata = \get_comment( $comment, ARRAY_A ); @@ -70,7 +75,7 @@ public static function update_comment( $activity ) { return false; } - // found a local comment id + // Found a local comment id. $commentdata['comment_author'] = \esc_attr( $meta['name'] ? $meta['name'] : $meta['preferredUsername'] ); $commentdata['comment_content'] = \addslashes( $activity['object']['content'] ); @@ -126,7 +131,7 @@ public static function add_reaction( $activity ) { /** * Get interaction(s) for a given URL/ID. * - * @param strin $url The URL/ID to get interactions for. + * @param string $url The URL/ID to get interactions for. * * @return array The interactions as WP_Comment objects. */ @@ -168,7 +173,7 @@ public static function get_interaction_by_id( $url ) { public static function get_interactions_by_actor( $actor ) { $meta = get_remote_metadata_by_actor( $actor ); - // get URL, because $actor seems to be the ID + // Get URL, because $actor seems to be the ID. if ( $meta && ! is_wp_error( $meta ) && isset( $meta['url'] ) ) { $actor = object_to_uri( $meta['url'] ); } @@ -193,7 +198,7 @@ public static function get_interactions_by_actor( $actor ) { * Adds line breaks to the list of allowed comment tags. * * @param array $allowed_tags Allowed HTML tags. - * @param string $context Context. + * @param string $context Optional. Context. Default empty. * * @return array Filtered tag list. */ @@ -218,21 +223,21 @@ public static function allowed_comment_html( $allowed_tags, $context = '' ) { /** * Convert an Activity to a WP_Comment * - * @param array $activity The Activity array + * @param array $activity The Activity array. * - * @return array|false The commentdata or false on failure + * @return array|false The comment data or false on failure. */ public static function activity_to_comment( $activity ) { $comment_content = null; $actor = object_to_uri( $activity['actor'] ); $actor = get_remote_metadata_by_actor( $actor ); - // check Actor-Meta + // Check Actor-Meta. if ( ! $actor || is_wp_error( $actor ) ) { return false; } - // check Actor-Name + // Check Actor-Name. if ( isset( $actor['name'] ) ) { $comment_author = $actor['name']; } elseif ( isset( $actor['preferredUsername'] ) ) { @@ -275,19 +280,19 @@ public static function activity_to_comment( $activity ) { } /** - * Persist a comment + * Persist a comment. * - * @param array $commentdata The commentdata array - * @param string $action Either 'insert' or 'update' + * @param array $commentdata The commentdata array. + * @param string $action Optional. Either 'insert' or 'update'. Default 'insert'. * - * @return array|string|int|\WP_Error|false The commentdata or false on failure + * @return array|string|int|\WP_Error|false The comment data or false on failure */ public static function persist( $commentdata, $action = self::INSERT ) { - // disable flood control + // Disable flood control. \remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 ); - // do not require email for AP entries + // Do not require email for AP entries. \add_filter( 'pre_option_require_name_email', '__return_false' ); - // No nonce possible for this submission route + // No nonce possible for this submission route. \add_filter( 'akismet_comment_nonce', function () { @@ -304,7 +309,7 @@ function () { \remove_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10 ); \remove_filter( 'pre_option_require_name_email', '__return_false' ); - // re-add flood control + // Restore flood control. \add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 ); if ( 1 === $state ) { diff --git a/includes/collection/class-replies.php b/includes/collection/class-replies.php index 11c170812..34a5aa02b 100644 --- a/includes/collection/class-replies.php +++ b/includes/collection/class-replies.php @@ -1,4 +1,10 @@ false, @@ -110,7 +119,7 @@ public static function get_by_username( $username ) { $username = str_replace( array( '*', '%' ), '', $username ); - // check for login or nicename. + // Check for login or nicename. $user = new WP_User_Query( array( 'count_total' => false, @@ -138,21 +147,21 @@ public static function get_by_username( $username ) { * * @param string $uri The User-Resource. * - * @return \Acitvitypub\Model\User The User. + * @return User|WP_Error The User or WP_Error if user not found. */ public static function get_by_resource( $uri ) { $uri = object_to_uri( $uri ); $scheme = 'acct'; $match = array(); - // try to extract the scheme and the host + // Try to extract the scheme and the host. if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $uri, $match ) ) { - // extract the scheme + // Extract the scheme. $scheme = \esc_attr( $match[1] ); } switch ( $scheme ) { - // check for http(s) URIs + // Check for http(s) URIs. case 'http': case 'https': $resource_path = \wp_parse_url( $uri, PHP_URL_PATH ); @@ -166,7 +175,7 @@ public static function get_by_resource( $uri ) { $resource_path = \trim( $resource_path, '/' ); - // check for http(s)://blog.example.com/@username + // Check for http(s)://blog.example.com/@username. if ( str_starts_with( $resource_path, '@' ) ) { $identifier = \str_replace( '@', '', $resource_path ); $identifier = \trim( $identifier, '/' ); @@ -175,14 +184,14 @@ public static function get_by_resource( $uri ) { } } - // check for http(s)://blog.example.com/author/username + // Check for http(s)://blog.example.com/author/username. $user_id = url_to_authorid( $uri ); if ( $user_id ) { return self::get_by_id( $user_id ); } - // check for http(s)://blog.example.com/ + // Check for http(s)://blog.example.com/. if ( normalize_url( site_url() ) === normalize_url( $uri ) || normalize_url( home_url() ) === normalize_url( $uri ) @@ -195,7 +204,7 @@ public static function get_by_resource( $uri ) { \__( 'User not found', 'activitypub' ), array( 'status' => 404 ) ); - // check for acct URIs + // Check for acct URIs. case 'acct': $uri = \str_replace( 'acct:', '', $uri ); $identifier = \substr( $uri, 0, \strrpos( $uri, '@' ) ); @@ -210,7 +219,7 @@ public static function get_by_resource( $uri ) { ); } - // prepare wildcards https://github.com/mastodon/mastodon/issues/22213 + // Prepare wildcards https://github.com/mastodon/mastodon/issues/22213. if ( in_array( $identifier, array( '_', '*', '' ), true ) ) { return self::get_by_id( self::BLOG_USER_ID ); } @@ -230,7 +239,7 @@ public static function get_by_resource( $uri ) { * * @param string $id The User-Resource. * - * @return \Acitvitypub\Model\User The User. + * @return User|Blog|Application|WP_Error The User or WP_Error if user not found. */ public static function get_by_various( $id ) { $user = null; @@ -238,11 +247,11 @@ public static function get_by_various( $id ) { if ( is_numeric( $id ) ) { $user = self::get_by_id( $id ); } elseif ( - // is URL + // Is URL. filter_var( $id, FILTER_VALIDATE_URL ) || - // is acct + // Is acct. str_starts_with( $id, 'acct:' ) || - // is email + // Is email. filter_var( $id, FILTER_VALIDATE_EMAIL ) ) { $user = self::get_by_resource( $id ); diff --git a/includes/compat.php b/includes/compat.php index 279591313..fa8627b71 100644 --- a/includes/compat.php +++ b/includes/compat.php @@ -1,6 +1,8 @@ $v ) { + foreach ( $input as $k => $v ) { if ( ++$next_key !== $k ) { return false; } diff --git a/includes/debug.php b/includes/debug.php index daa66a774..b289c80d2 100644 --- a/includes/debug.php +++ b/includes/debug.php @@ -1,17 +1,22 @@ wp_rewrite_rules(); - // not using rewrite rules, and 'author=N' method failed, so we're out of options + // 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 + // 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 + // 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 ) { @@ -211,10 +228,9 @@ function url_to_authorid( $url ) { } /** - * Verify if url is a wp_ap_comment, - * Or if it is a previously received remote comment + * Verify that url is a wp_ap_comment or a previously received remote comment. * - * @return int comment_id + * @return int|bool Comment ID or false if not found. */ function is_comment() { $comment_id = get_query_var( 'c', null ); @@ -231,13 +247,13 @@ function is_comment() { } /** - * Check for Tombstone Objects + * Check for Tombstone Objects. * * @see https://www.w3.org/TR/activitypub/#delete-activity-outbox * - * @param WP_Error $wp_error A WP_Error-Response of an HTTP-Request + * @param WP_Error $wp_error A WP_Error-Response of an HTTP-Request. * - * @return boolean true if HTTP-Code is 410 or 404 + * @return boolean True if HTTP-Code is 410 or 404. */ function is_tombstone( $wp_error ) { if ( ! is_wp_error( $wp_error ) ) { @@ -254,12 +270,12 @@ function is_tombstone( $wp_error ) { /** * Get the REST URL relative to this plugin's namespace. * - * @param string $path Optional. REST route path. Otherwise this plugin's namespaced root. + * @param string $path Optional. REST route path. Default ''. * * @return string REST URL relative to this plugin's namespace. */ function get_rest_url_by_path( $path = '' ) { - // we'll handle the leading slash. + // We'll handle the leading slash. $path = ltrim( $path, '/' ); $namespaced_path = sprintf( '/%s/%s', ACTIVITYPUB_REST_NAMESPACE, $path ); return \get_rest_url( null, $namespaced_path ); @@ -268,25 +284,23 @@ function get_rest_url_by_path( $path = '' ) { /** * Convert a string from camelCase to snake_case. * - * @param string $string The string to convert. + * @param string $input The string to convert. * * @return string The converted string. */ -// phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.stringFound -function camel_to_snake_case( $string ) { - return strtolower( preg_replace( '/(?query_vars['activitypub'] ) ) { return true; } @@ -391,7 +403,7 @@ function is_activitypub_request() { /** * This function checks if a user is disabled for ActivityPub. * - * @param int $user_id The User-ID. + * @param int $user_id The user ID. * * @return boolean True if the user is disabled, false otherwise. */ @@ -433,6 +445,12 @@ function is_user_disabled( $user_id ) { break; } + /** + * Allow plugins to disable users for ActivityPub. + * + * @param boolean $return True if the user is disabled, false otherwise. + * @param int $user_id The User-ID. + */ return apply_filters( 'activitypub_is_user_disabled', $return, $user_id ); } @@ -442,7 +460,7 @@ function is_user_disabled( $user_id ) { * This function is used to check if the 'blog' or 'user' * type is disabled for ActivityPub. * - * @param enum $type Can be 'blog' or 'user'. + * @param string $type User type. 'blog' or 'user'. * * @return boolean True if the user type is disabled, false otherwise. */ @@ -497,6 +515,12 @@ function is_user_type_disabled( $type ) { break; } + /** + * Allow plugins to disable user types for ActivityPub. + * + * @param boolean $return True if the user type is disabled, false otherwise. + * @param string $type The User-Type. + */ return apply_filters( 'activitypub_is_user_type_disabled', $return, $type ); } @@ -551,20 +575,25 @@ function is_json( $data ) { } /** - * Check if a blog is public based on the `blog_public` option + * Check whther a blog is public based on the `blog_public` option. * - * @return bollean True if public, false if not + * @return bool True if public, false if not */ function is_blog_public() { + /** + * Filter whether the blog is public. + * + * @param bool $public Whether the blog is public. + */ return (bool) apply_filters( 'activitypub_is_blog_public', \get_option( 'blog_public', 1 ) ); } /** - * Sanitize a URL + * Sanitize a URL. * - * @param string $value The URL to sanitize + * @param string $value The URL to sanitize. * - * @return string|null The sanitized URL or null if invalid + * @return string|null The sanitized URL or null if invalid. */ function sanitize_url( $value ) { if ( filter_var( $value, FILTER_VALIDATE_URL ) === false ) { @@ -575,11 +604,11 @@ function sanitize_url( $value ) { } /** - * Extract recipient URLs from Activity object + * Extract recipient URLs from Activity object. * - * @param array $data + * @param array $data The Activity object as array. * - * @return array The list of user URLs + * @return array The list of user URLs. */ function extract_recipients_from_activity( $data ) { $recipient_items = array(); @@ -606,10 +635,10 @@ function extract_recipients_from_activity( $data ) { $recipients = array(); - // flatten array + // Flatten array. foreach ( $recipient_items as $recipient ) { if ( is_array( $recipient ) ) { - // check if recipient is an object + // Check if recipient is an object. if ( array_key_exists( 'id', $recipient ) ) { $recipients[] = $recipient['id']; } @@ -622,11 +651,11 @@ function extract_recipients_from_activity( $data ) { } /** - * Check if passed Activity is Public + * Check if passed Activity is Public. * - * @param array $data The Activity object as array + * @param array $data The Activity object as array. * - * @return boolean True if public, false if not + * @return boolean True if public, false if not. */ function is_activity_public( $data ) { $recipients = extract_recipients_from_activity( $data ); @@ -635,11 +664,11 @@ function is_activity_public( $data ) { } /** - * Get active users based on a given duration + * Get active users based on a given duration. * - * @param int $duration The duration to check in month(s) + * @param int $duration Optional. The duration to check in month(s). Default 1. * - * @return int The number of active users + * @return int The number of active users. */ function get_active_users( $duration = 1 ) { @@ -650,7 +679,7 @@ function get_active_users( $duration = 1 ) { if ( false === $count ) { global $wpdb; - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:ignore WordPress.DB.DirectDatabaseQuery $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( DISTINCT post_author ) FROM {$wpdb->posts} WHERE post_type = 'post' AND post_status = 'publish' AND post_date <= DATE_SUB( NOW(), INTERVAL %d MONTH )", @@ -661,32 +690,32 @@ function get_active_users( $duration = 1 ) { set_transient( $transient_key, $count, DAY_IN_SECONDS ); } - // if 0 authors where active + // If 0 authors where active. if ( 0 === $count ) { return 0; } - // if single user mode + // If single user mode. if ( is_single_user() ) { return 1; } - // if blog user is disabled + // If blog user is disabled. if ( is_user_disabled( Users::BLOG_USER_ID ) ) { return (int) $count; } - // also count blog user + // Also count blog user. return (int) $count + 1; } /** - * Get the total number of users + * Get the total number of users. * - * @return int The total number of users + * @return int The total number of users. */ function get_total_users() { - // if single user mode + // If single user mode. if ( is_single_user() ) { return 1; } @@ -703,7 +732,7 @@ function get_total_users() { $users = 1; } - // if blog user is disabled + // If blog user is disabled. if ( is_user_disabled( Users::BLOG_USER_ID ) ) { return (int) $users; } @@ -716,65 +745,66 @@ function get_total_users() { * * @param string $id ActivityPub object ID (usually a URL) to check. * - * @return int|boolean Comment ID, or false on failure. + * @return \WP_Comment|boolean Comment, or false on failure. */ function object_id_to_comment( $id ) { return Comment::object_id_to_comment( $id ); } /** - * Verify if URL is a local comment, - * Or if it is a previously received remote comment + * Verify that URL is a local comment or a previously received remote comment. * (For threading comments locally) * * @param string $url The URL to check. * - * @return int comment_ID or null if not found + * @return string|null Comment ID or null if not found */ function url_to_commentid( $url ) { return Comment::url_to_commentid( $url ); } /** - * Get the URI of an ActivityPub object + * Get the URI of an ActivityPub object. * - * @param array $object The ActivityPub object + * @param array|string $data The ActivityPub object. * * @return string The URI of the ActivityPub object */ -function object_to_uri( $object ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound - // check if it is already simple - if ( ! $object || is_string( $object ) ) { - return $object; +function object_to_uri( $data ) { + // Check whether it is already simple. + if ( ! $data || is_string( $data ) ) { + return $data; } - // check if it is a list, then take first item - // this plugin does not support collections - if ( array_is_list( $object ) ) { - $object = $object[0]; + /* + * Check if it is a list, then take first item. + * This plugin does not support collections. + */ + if ( array_is_list( $data ) ) { + $data = $data[0]; } - // check if it is simplified now - if ( is_string( $object ) ) { - return $object; + // Check if it is simplified now. + if ( is_string( $data ) ) { + return $data; } $type = 'Object'; - if ( isset( $object['type'] ) ) { - $type = $object['type']; + if ( isset( $data['type'] ) ) { + $type = $data['type']; } - // return part of Object that makes most sense + // Return part of Object that makes most sense. switch ( $type ) { case 'Link': - $object = $object['href']; + $data = $data['href']; break; default: - $object = $object['id']; + $data = $data['id']; break; } - return $object; + return $data; } /** @@ -836,9 +866,8 @@ function is_local_comment( $comment ) { /** * Mark a WordPress object as federated. * - * @param WP_Comment|WP_Post|mixed $wp_object - * - * @return void + * @param \WP_Comment|\WP_Post $wp_object The WordPress object. + * @param string $state The state of the object. */ function set_wp_object_state( $wp_object, $state ) { $meta_key = 'activitypub_status'; @@ -848,6 +877,12 @@ function set_wp_object_state( $wp_object, $state ) { } elseif ( $wp_object instanceof \WP_Comment ) { \update_comment_meta( $wp_object->comment_ID, $meta_key, $state ); } else { + /** + * Allow plugins to mark WordPress objects as federated. + * + * @param \WP_Comment|\WP_Post $wp_object The WordPress object. + * @param string $state The state of the object. + */ \apply_filters( 'activitypub_mark_wp_object_as_federated', $wp_object ); } } @@ -855,7 +890,7 @@ function set_wp_object_state( $wp_object, $state ) { /** * Get the federation state of a WordPress object. * - * @param WP_Comment|WP_Post|mixed $wp_object + * @param \WP_Comment|\WP_Post $wp_object The WordPress object. * * @return string|false The state of the object or false if not found. */ @@ -867,6 +902,11 @@ function get_wp_object_state( $wp_object ) { } elseif ( $wp_object instanceof \WP_Comment ) { return \get_comment_meta( $wp_object->comment_ID, $meta_key, true ); } else { + /** + * Allow plugins to get the federation state of a WordPress object. + * + * @param \WP_Comment|\WP_Post $wp_object The WordPress object. + */ return \apply_filters( 'activitypub_get_wp_object_state', false, $wp_object ); } } @@ -876,7 +916,7 @@ function get_wp_object_state( $wp_object ) { * * Set some default descriptions for the default post types. * - * @param WP_Post_Type $post_type The post type object. + * @param \WP_Post_Type $post_type The post type object. * * @return string The description of the post type. */ @@ -899,6 +939,12 @@ function get_post_type_description( $post_type ) { } } + /** + * Allow plugins to get the description of a post type. + * + * @param string $description The description of the post type. + * @param \WP_Post_Type $post_type The post type object. + */ return apply_filters( 'activitypub_post_type_description', $description, $post_type->name, $post_type ); } @@ -908,9 +954,9 @@ function get_post_type_description( $post_type ) { * @return string The masked version. */ function get_masked_wp_version() { - // only show the major and minor version + // Only show the major and minor version. $version = get_bloginfo( 'version' ); - // strip the RC or beta part + // Strip the RC or beta part. $version = preg_replace( '/-.*$/', '', $version ); $version = explode( '.', $version ); $version = array_slice( $version, 0, 2 ); @@ -959,9 +1005,9 @@ function ( $enclosure ) { * * @see https://developer.wordpress.org/reference/functions/get_post_ancestors/ * - * @param int|WP_Comment $comment Comment ID or comment object. + * @param int|\WP_Comment $comment Comment ID or comment object. * - * @return WP_Comment[] Array of ancestor comments or empty array if there are none. + * @return \WP_Comment[] Array of ancestor comments or empty array if there are none. */ function get_comment_ancestors( $comment ) { $comment = \get_comment( $comment ); @@ -976,7 +1022,6 @@ function get_comment_ancestors( $comment ) { $id = (int) $comment->comment_parent; $ancestors[] = $id; - // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition while ( $id > 0 ) { $ancestor = \get_comment( $id ); $parent_id = (int) $ancestor->comment_parent; @@ -1019,13 +1064,13 @@ function custom_large_numbers( $formatted, $number, $decimals ) { $thousands_sep = $wp_locale->number_format['thousands_sep']; } - if ( $number < 1000 ) { // any number less than a Thousand. + if ( $number < 1000 ) { // Any number less than a Thousand. return \number_format( $number, $decimals, $decimal_point, $thousands_sep ); - } elseif ( $number < 1000000 ) { // any number less than a million + } elseif ( $number < 1000000 ) { // Any number less than a million. return \number_format( $number / 1000, $decimals, $decimal_point, $thousands_sep ) . 'K'; - } elseif ( $number < 1000000000 ) { // any number less than a billion + } elseif ( $number < 1000000000 ) { // Any number less than a billion. return \number_format( $number / 1000000, $decimals, $decimal_point, $thousands_sep ) . 'M'; - } else { // at least a billion + } else { // At least a billion. return \number_format( $number / 1000000000, $decimals, $decimal_point, $thousands_sep ) . 'B'; } @@ -1037,7 +1082,7 @@ function custom_large_numbers( $formatted, $number, $decimals ) { * Registers a ActivityPub comment type. * * @param string $comment_type Key for comment type. - * @param array $args Arguments. + * @param array $args Optional. Array of arguments for registering a comment type. Default empty array. * * @return array The registered Activitypub comment type. */ @@ -1106,14 +1151,14 @@ function get_reply_intent_uri() { /** * Replace content with links, mentions or hashtags by Regex callback and not affect protected tags. * - * @param $content string The content that should be changed - * @param $regex string The regex to use - * @param $regex_callback callable Callback for replacement logic + * @param string $content The content that should be changed. + * @param string $regex The regex to use. + * @param callable $regex_callback Callback for replacement logic. * * @return string The content with links, mentions, hashtags, etc. */ function enrich_content_data( $content, $regex, $regex_callback ) { - // small protection against execution timeouts: limit to 1 MB + // Small protection against execution timeouts: limit to 1 MB. if ( mb_strlen( $content ) > MB_IN_BYTES ) { return $content; } @@ -1178,10 +1223,10 @@ function enrich_content_data( $content, $regex, $regex_callback ) { * 2. The first part of the post content if it contains the tag. * 3. An excerpt of the post content if it is longer than the specified length. * - * @param int|WP_Post $post The post ID or post object. - * @param integer $length The maximum length of the summary. - * Default is 500. It will ne ignored if the post excerpt - * and the content above the tag. + * @param int|\WP_Post $post The post ID or post object. + * @param integer $length The maximum length of the summary. + * Default is 500. It will be ignored if the post excerpt + * and the content above the tag. * * @return string The generated post summary. */ @@ -1195,12 +1240,22 @@ function generate_post_summary( $post, $length = 500 ) { $content = \sanitize_post_field( 'post_excerpt', $post->post_excerpt, $post->ID ); if ( $content ) { + /** + * Filters the post excerpt. + * + * @param string $content The post excerpt. + */ return \apply_filters( 'the_excerpt', $content ); } $content = \sanitize_post_field( 'post_content', $post->post_content, $post->ID ); $content_parts = \get_extended( $content ); + /** + * Filters the excerpt more value. + * + * @param string $excerpt_more The excerpt more. + */ $excerpt_more = \apply_filters( 'activitypub_excerpt_more', '[…]' ); $length = $length - strlen( $excerpt_more ); @@ -1235,7 +1290,7 @@ function generate_post_summary( $post, $length = 500 ) { /** * Get the content warning of a post. * - * @param int $post_id The post ID. + * @param int|\WP_Post $post_id The post ID or post object. * * @return string|false The content warning or false if not found. */ diff --git a/includes/handler/class-announce.php b/includes/handler/class-announce.php index e9e4ff7ef..b0aa90fd5 100644 --- a/includes/handler/class-announce.php +++ b/includes/handler/class-announce.php @@ -1,4 +1,10 @@ get_json_params(); diff --git a/includes/handler/class-delete.php b/includes/handler/class-delete.php index 3cc732bfc..5b514dfab 100644 --- a/includes/handler/class-delete.php +++ b/includes/handler/class-delete.php @@ -1,7 +1,12 @@ delete(); self::maybe_delete_interactions( $activity ); @@ -110,7 +129,7 @@ public static function maybe_delete_follower( $activity ) { * @param array $activity The delete activity. */ public static function maybe_delete_interactions( $activity ) { - // verify if Actor is deleted. + // Verify that Actor is deleted. if ( Http::is_tombstone( $activity['actor'] ) ) { \wp_schedule_single_event( \time(), @@ -123,7 +142,7 @@ public static function maybe_delete_interactions( $activity ) { /** * Delete comments from an Actor. * - * @param array $comments The comments to delete. + * @param array $actor The actor whose comments to delete. */ public static function delete_interactions( $actor ) { $comments = Interactions::get_interactions_by_actor( $actor ); @@ -139,8 +158,6 @@ public static function delete_interactions( $actor ) { * Delete a Reaction if URL is a Tombstone. * * @param array $activity The delete activity. - * - * @return void */ public static function maybe_delete_interaction( $activity ) { if ( is_array( $activity['object'] ) ) { diff --git a/includes/handler/class-follow.php b/includes/handler/class-follow.php index c22a9362d..fb94ba6d1 100644 --- a/includes/handler/class-follow.php +++ b/includes/handler/class-follow.php @@ -1,4 +1,10 @@ get__id(); - // save follower + // Save follower. $follower = Followers::add_follower( $user_id, $activity['actor'] @@ -59,7 +63,7 @@ public static function handle_follow( $activity ) { $follower ); - // send notification + // Send notification. $notification = new Notification( 'follow', $activity['actor'], @@ -70,23 +74,20 @@ public static function handle_follow( $activity ) { } /** - * Send Accept response - * - * @param string $actor The Actor URL - * @param array $activity_object The Activity object - * @param int $user_id The ID of the WordPress User - * @param Activitypub\Model\Follower $follower The Follower object + * Send Accept response. * - * @return void + * @param string $actor The Actor URL. + * @param array $activity_object The Activity object. + * @param int $user_id The ID of the WordPress User. + * @param \Activitypub\Model\Follower $follower The Follower object. */ public static function send_follow_response( $actor, $activity_object, $user_id, $follower ) { if ( \is_wp_error( $follower ) ) { - // it is not even possible to send a "Reject" because - // we can not get the Remote-Inbox + // Impossible to send a "Reject" because we can not get the Remote-Inbox. return; } - // only send minimal data + // Only send minimal data. $activity_object = array_intersect_key( $activity_object, array_flip( @@ -101,10 +102,10 @@ public static function send_follow_response( $actor, $activity_object, $user_id, $user = Users::get_by_id( $user_id ); - // get inbox + // Get inbox. $inbox = $follower->get_shared_inbox(); - // send "Accept" activity + // Send "Accept" activity. $activity = new Activity(); $activity->set_type( 'Accept' ); $activity->set_object( $activity_object ); diff --git a/includes/handler/class-like.php b/includes/handler/class-like.php index e395d2cf7..94facde46 100644 --- a/includes/handler/class-like.php +++ b/includes/handler/class-like.php @@ -1,4 +1,10 @@ add_help_tab( array( @@ -54,17 +59,17 @@ '

' . \__( 'Fediverse', 'activitypub' ) . '

' . '

' . \__( 'The Fediverse is a new word made of two words: "federation" + "universe"', 'activitypub' ) . '

' . '

' . \__( 'It is a federated social network running on free open software on a myriad of computers across the globe. Many independent servers are interconnected and allow people to interact with one another. There\'s no one central site: you choose a server to register. This ensures some decentralization and sovereignty of data. Fediverse (also called Fedi) has no built-in advertisements, no tricky algorithms, no one big corporation dictating the rules. Instead we have small cozy communities of like-minded people. Welcome!', 'activitypub' ) . '

' . - '

' . \__( 'For more informations please visit fediverse.party', 'activitypub' ) . '

' . + '

' . \__( 'For more information please visit fediverse.party', 'activitypub' ) . '

' . '

' . \__( 'ActivityPub', 'activitypub' ) . '

' . '

' . \__( 'ActivityPub is a decentralized social networking protocol based on the ActivityStreams 2.0 data format. ActivityPub is an official W3C recommended standard published by the W3C Social Web Working Group. It provides a client to server API for creating, updating and deleting content, as well as a federated server to server API for delivering notifications and subscribing to content.', 'activitypub' ) . '

' . '

' . \__( 'WebFinger', 'activitypub' ) . '

' . '

' . \__( 'WebFinger is used to discover information about people or other entities on the Internet that are identified by a URI using standard Hypertext Transfer Protocol (HTTP) methods over a secure transport. A WebFinger resource returns a JavaScript Object Notation (JSON) object describing the entity that is queried. The JSON object is referred to as the JSON Resource Descriptor (JRD).', 'activitypub' ) . '

' . '

' . \__( 'For a person, the type of information that might be discoverable via WebFinger includes a personal profile address, identity service, telephone number, or preferred avatar. For other entities on the Internet, a WebFinger resource might return JRDs containing link relations that enable a client to discover, for example, that a printer can print in color on A4 paper, the physical location of a server, or other static information.', 'activitypub' ) . '

' . '

' . \__( 'On Mastodon [and other Plattforms], user profiles can be hosted either locally on the same website as yours, or remotely on a completely different website. The same username may be used on a different domain. Therefore, a Mastodon user\'s full mention consists of both the username and the domain, in the form @username@domain. In practical terms, @user@example.com is not the same as @user@example.org. If the domain is not included, Mastodon will try to find a local user named @username. However, in order to deliver to someone over ActivityPub, the @username@domain mention is not enough – mentions must be translated to an HTTPS URI first, so that the remote actor\'s inbox and outbox can be found. (This paragraph is copied from the Mastodon Documentation)', 'activitypub' ) . '

' . - '

' . \__( 'For more informations please visit webfinger.net', 'activitypub' ) . '

' . + '

' . \__( 'For more information please visit webfinger.net', 'activitypub' ) . '

' . '

' . \__( 'NodeInfo', 'activitypub' ) . '

' . '

' . \__( 'NodeInfo is an effort to create a standardized way of exposing metadata about a server running one of the distributed social networks. The two key goals are being able to get better insights into the user base of distributed social networking and the ability to build tools that allow users to choose the best fitting software and server for their needs.', 'activitypub' ) . '

' . - '

' . \__( 'For more informations please visit nodeinfo.diaspora.software', 'activitypub' ) . '

', + '

' . \__( 'For more information please visit nodeinfo.diaspora.software', 'activitypub' ) . '

', ) ); diff --git a/includes/model/class-application.php b/includes/model/class-application.php index a0bda4310..b7d38af24 100644 --- a/includes/model/class-application.php +++ b/includes/model/class-application.php @@ -1,4 +1,10 @@ + * @var string */ protected $webfinger; + /** + * Returns the type of the object. + * + * @return string The type of the object. + */ public function get_type() { return 'Application'; } + /** + * Returns whether the Application manually approves followers. + * + * @return true Whether the Application manually approves followers. + */ public function get_manually_approves_followers() { return true; } + /** + * Returns the ID of the Application. + * + * @return string The ID of the Application. + */ public function get_id() { return get_rest_url_by_path( 'application' ); } @@ -73,10 +97,20 @@ public function get_alternate_url() { return $this->get_url(); } + /** + * Get the Username. + * + * @return string The Username. + */ public function get_name() { return 'application'; } + /** + * Get the preferred username. + * + * @return string The preferred username. + */ public function get_preferred_username() { return $this->get_name(); } @@ -87,10 +121,10 @@ public function get_preferred_username() { * @return array The User-Icon. */ public function get_icon() { - // try site icon first + // Try site icon first. $icon_id = get_option( 'site_icon' ); - // try custom logo second + // Try custom logo second. if ( ! $icon_id ) { $icon_id = get_theme_mod( 'custom_logo' ); } @@ -105,7 +139,7 @@ public function get_icon() { } if ( ! $icon_url ) { - // fallback to default icon + // Fallback to default icon. $icon_url = plugins_url( '/assets/img/wp-logo.png', ACTIVITYPUB_PLUGIN_FILE ); } @@ -131,6 +165,11 @@ public function get_header_image() { return null; } + /** + * Get the first published date. + * + * @return string The published date. + */ public function get_published() { $first_post = new WP_Query( array( @@ -176,6 +215,11 @@ public function get_webfinger() { return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST ); } + /** + * Returns the public key. + * + * @return array The public key. + */ public function get_public_key() { return array( 'id' => $this->get_id() . '#main-key', @@ -185,9 +229,9 @@ public function get_public_key() { } /** - * Get the User-Description. + * Get the User description. * - * @return string The User-Description. + * @return string The User description. */ public function get_summary() { return \wpautop( @@ -198,6 +242,11 @@ public function get_summary() { ); } + /** + * Returns the canonical URL of the object. + * + * @return string|null The canonical URL of the object. + */ public function get_canonical_url() { return \home_url(); } diff --git a/includes/model/class-blog.php b/includes/model/class-blog.php index aff08831c..3cbd6f91e 100644 --- a/includes/model/class-blog.php +++ b/includes/model/class-blog.php @@ -1,8 +1,13 @@ + * @var string */ protected $webfinger; /** - * If the User is discoverable. + * Whether the User is discoverable. * * @see https://docs.joinmastodon.org/spec/activitypub/#discoverable * @@ -74,7 +81,7 @@ class Blog extends Actor { protected $discoverable; /** - * Restrict posting to mods + * Restrict posting to mods. * * @see https://join-lemmy.org/docs/contributors/05-federation.html * @@ -82,18 +89,28 @@ class Blog extends Actor { */ protected $posting_restricted_to_mods; + /** + * Whether the User manually approves followers. + * + * @return false + */ public function get_manually_approves_followers() { return false; } + /** + * Whether the User is discoverable. + * + * @return boolean + */ public function get_discoverable() { return true; } /** - * Get the User-ID. + * Get the User ID. * - * @return string The User-ID. + * @return string The User ID. */ public function get_id() { return $this->get_url(); @@ -115,9 +132,9 @@ public function get_type() { } /** - * Get the User-Name. + * Get the Username. * - * @return string The User-Name. + * @return string The Username. */ public function get_name() { return \wp_strip_all_tags( @@ -130,9 +147,9 @@ public function get_name() { } /** - * Get the User-Description. + * Get the User description. * - * @return string The User-Description. + * @return string The User description. */ public function get_summary() { $summary = \get_option( 'activitypub_blog_description', null ); @@ -150,9 +167,9 @@ public function get_summary() { } /** - * Get the User-Url. + * Get the User url. * - * @return string The User-Url. + * @return string The User url. */ public function get_url() { return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() ); @@ -173,12 +190,12 @@ public function get_alternate_url() { * @return string The auto-generated Username. */ public static function get_default_username() { - // check if domain host has a subdomain + // Check if domain host has a subdomain. $host = \wp_parse_url( \get_home_url(), \PHP_URL_HOST ); $host = \preg_replace( '/^www\./i', '', $host ); /** - * Filter the default blog username. + * Filters the default blog username. * * @param string $host The default username. */ @@ -186,9 +203,9 @@ public static function get_default_username() { } /** - * Get the preferred User-Name. + * Get the preferred Username. * - * @return string The User-Name. + * @return string The Username. */ public function get_preferred_username() { $username = \get_option( 'activitypub_blog_identifier' ); @@ -201,15 +218,15 @@ public function get_preferred_username() { } /** - * Get the User-Icon. + * Get the User icon. * - * @return array The User-Icon. + * @return array The User icon. */ public function get_icon() { - // try site_logo, falling back to site_icon, first + // Try site_logo, falling back to site_icon, first. $icon_id = get_option( 'site_icon' ); - // try custom logo second + // Try custom logo second. if ( ! $icon_id ) { $icon_id = get_theme_mod( 'custom_logo' ); } @@ -224,7 +241,7 @@ public function get_icon() { } if ( ! $icon_url ) { - // fallback to default icon + // Fallback to default icon. $icon_url = plugins_url( '/assets/img/wp-logo.png', ACTIVITYPUB_PLUGIN_FILE ); } @@ -261,6 +278,11 @@ public function get_image() { return null; } + /** + * Get the published date. + * + * @return string The published date. + */ public function get_published() { $first_post = new WP_Query( array( @@ -279,10 +301,20 @@ public function get_published() { return \gmdate( 'Y-m-d\TH:i:s\Z', $time ); } + /** + * Get the canonical URL. + * + * @return string|null The canonical URL. + */ public function get_canonical_url() { return \home_url(); } + /** + * Get the Moderators endpoint. + * + * @return string|null The Moderators endpoint. + */ public function get_moderators() { if ( is_single_user() || 'Group' !== $this->get_type() ) { return null; @@ -291,6 +323,11 @@ public function get_moderators() { return get_rest_url_by_path( 'collections/moderators' ); } + /** + * Get attributedTo value. + * + * @return string|null The attributedTo value. + */ public function get_attributed_to() { if ( is_single_user() || 'Group' !== $this->get_type() ) { return null; @@ -299,6 +336,11 @@ public function get_attributed_to() { return get_rest_url_by_path( 'collections/moderators' ); } + /** + * Get the public key information. + * + * @return array The public key. + */ public function get_public_key() { return array( 'id' => $this->get_id() . '#main-key', @@ -307,6 +349,11 @@ public function get_public_key() { ); } + /** + * Returns whether posting is restricted to mods. + * + * @return bool|null True if posting is restricted to mods, null if not applicable. + */ public function get_posting_restricted_to_mods() { if ( 'Group' === $this->get_type() ) { return true; @@ -351,6 +398,11 @@ public function get_following() { return get_rest_url_by_path( sprintf( 'actors/%d/following', $this->get__id() ) ); } + /** + * Returns endpoints. + * + * @return array|null The endpoints. + */ public function get_endpoints() { $endpoints = null; @@ -381,6 +433,11 @@ public function get_featured() { return get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $this->get__id() ) ); } + /** + * Returns whether the site is indexable. + * + * @return bool Whether the site is indexable. + */ public function get_indexable() { if ( is_blog_public() ) { return true; @@ -390,7 +447,7 @@ public function get_indexable() { } /** - * Update the User-Name. + * Update the Username. * * @param mixed $value The new value. * @return bool True if the attribute was updated, false otherwise. @@ -400,7 +457,7 @@ public function update_name( $value ) { } /** - * Update the User-Description. + * Update the User description. * * @param mixed $value The new value. * @return bool True if the attribute was updated, false otherwise. @@ -410,7 +467,7 @@ public function update_summary( $value ) { } /** - * Update the User-Icon. + * Update the User icon. * * @param mixed $value The new value. * @return bool True if the attribute was updated, false otherwise. @@ -436,11 +493,11 @@ public function update_header( $value ) { } /** - * Get the User - Hashtags . + * Get the User - Hashtags. * * @see https://docs.joinmastodon.org/spec/activitypub/#Hashtag * - * @return array The User - Hashtags . + * @return array The User - Hashtags. */ public function get_tag() { $hashtags = array(); diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 21565911f..af30303da 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -1,13 +1,18 @@ summary ) ) { @@ -51,7 +56,7 @@ public function get_summary() { * Getter for URL attribute. * * Falls back to ID, if no URL is set. This is relevant for - * Plattforms like Lemmy, where the ID is the URL. + * Platforms like Lemmy, where the ID is the URL. * * @return string The URL. */ @@ -65,8 +70,6 @@ public function get_url() { /** * Reset (delete) all errors. - * - * @return void */ public function reset_errors() { delete_post_meta( $this->_id, 'activitypub_errors' ); @@ -103,21 +106,19 @@ public function get_latest_error_message() { } /** - * Update the current Follower-Object. - * - * @return void + * Update the current Follower object. */ public function update() { $this->save(); } /** - * Validate the current Follower-Object. + * Validate the current Follower object. * * @return boolean True if the verification was successful. */ public function is_valid() { - // the minimum required attributes + // The minimum required attributes. $required_attributes = array( 'id', 'preferredUsername', @@ -136,9 +137,9 @@ public function is_valid() { } /** - * Save the current Follower-Object. + * Save the current Follower object. * - * @return int|WP_Error The Post-ID or an WP_Error. + * @return int|WP_Error The post ID or an WP_Error. */ public function save() { if ( ! $this->is_valid() ) { @@ -177,8 +178,7 @@ public function save() { ); if ( ! empty( $post_id ) ) { - // If this is an update, prevent the "followed" date from being - // overwritten by the current date. + // If this is an update, prevent the "followed" date from being overwritten by the current date. $post = get_post( $post_id ); $args['post_date'] = $post->post_date; $args['post_date_gmt'] = $post->post_date_gmt; @@ -191,24 +191,22 @@ public function save() { } /** - * Upsert the current Follower-Object. + * Upsert the current Follower object. * - * @return int|WP_Error The Post-ID or an WP_Error. + * @return int|WP_Error The post ID or an WP_Error. */ public function upsert() { return $this->save(); } /** - * Delete the current Follower-Object. + * Delete the current Follower object. * * Beware that this os deleting a Follower for ALL users!!! * * To delete only the User connection (unfollow) * * @see \Activitypub\Rest\Followers::remove_follower() - * - * @return void */ public function delete() { wp_delete_post( $this->_id ); @@ -216,8 +214,6 @@ public function delete() { /** * Update the post meta. - * - * @return void */ protected function get_post_meta_input() { $meta_input = array(); @@ -279,7 +275,7 @@ public function get_preferred_username() { } /** - * Get the Icon URL (Avatar) + * Get the Icon URL (Avatar). * * @return string The URL to the Avatar. */ @@ -298,7 +294,7 @@ public function get_icon_url() { } /** - * Get the Icon URL (Avatar) + * Get the Icon URL (Avatar). * * @return string The URL to the Avatar. */ @@ -334,9 +330,8 @@ public function get_shared_inbox() { /** * Convert a Custom-Post-Type input to an Activitypub\Model\Follower. * - * @return string The JSON string. - * - * @return array Activitypub\Model\Follower + * @param \WP_Post $post The post object. + * @return \Activitypub\Activity\Base_Object|WP_Error */ public static function init_from_cpt( $post ) { $actor_json = get_post_meta( $post->ID, 'activitypub_actor_json', true ); @@ -371,10 +366,10 @@ protected function extract_name_from_uri() { if ( $path ) { if ( \strpos( $name, '@' ) !== false ) { - // expected: https://example.com/@user (default URL pattern) + // Expected: https://example.com/@user (default URL pattern). $name = \preg_replace( '|^/@?|', '', $path ); } else { - // expected: https://example.com/users/user (default ID pattern) + // Expected: https://example.com/users/user (default ID pattern). $parts = \explode( '/', $path ); $name = \array_pop( $parts ); } @@ -384,7 +379,7 @@ protected function extract_name_from_uri() { \strpos( $name, 'acct' ) === 0 || \strpos( $name, '@' ) === 0 ) { - // expected: user@example.com or acct:user@example (WebFinger) + // Expected: user@example.com or acct:user@example (WebFinger). $name = \ltrim( $name, '@' ); $name = \ltrim( $name, 'acct:' ); $parts = \explode( '@', $name ); diff --git a/includes/model/class-user.php b/includes/model/class-user.php index 7849b9ab7..997c52104 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -1,19 +1,24 @@ + * @var string */ protected $webfinger; + /** + * The type of the object. + * + * @return string The type of the object. + */ public function get_type() { return 'Person'; } + /** + * Generate a User object from a WP_User. + * + * @param int $user_id The user ID. + * + * @return WP_Error|User The User object or WP_Error if user not found. + */ public static function from_wp_user( $user_id ) { if ( is_user_disabled( $user_id ) ) { return new WP_Error( @@ -83,27 +100,27 @@ public static function from_wp_user( $user_id ) { } /** - * Get the User-ID. + * Get the user ID. * - * @return string The User-ID. + * @return string The user ID. */ public function get_id() { return $this->get_url(); } /** - * Get the User-Name. + * Get the Username. * - * @return string The User-Name. + * @return string The Username. */ public function get_name() { return \esc_attr( \get_the_author_meta( 'display_name', $this->_id ) ); } /** - * Get the User-Description. + * Get the User description. * - * @return string The User-Description. + * @return string The User description. */ public function get_summary() { $description = get_user_option( 'activitypub_description', $this->_id ); @@ -114,27 +131,37 @@ public function get_summary() { } /** - * Get the User-Url. + * Get the User url. * - * @return string The User-Url. + * @return string The User url. */ public function get_url() { return \esc_url( \get_author_posts_url( $this->_id ) ); } /** - * Returns the User-URL with @-Prefix for the username. + * Returns the User URL with @-Prefix for the username. * - * @return string The User-URL with @-Prefix for the username. + * @return string The User URL with @-Prefix for the username. */ public function get_alternate_url() { return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() ); } + /** + * Get the preferred username. + * + * @return string The preferred username. + */ public function get_preferred_username() { return \esc_attr( \get_the_author_meta( 'login', $this->_id ) ); } + /** + * Get the User icon. + * + * @return array The User icon. + */ public function get_icon() { $icon = \get_user_option( 'activitypub_icon', $this->_id ); if ( wp_attachment_is_image( $icon ) ) { @@ -157,6 +184,11 @@ public function get_icon() { ); } + /** + * Returns the header image. + * + * @return array|null The header image. + */ public function get_image() { $header_image = get_user_option( 'activitypub_header_image', $this->_id ); $image_url = null; @@ -179,10 +211,20 @@ public function get_image() { return null; } + /** + * Returns the date the user was created. + * + * @return false|string The date the user was created. + */ public function get_published() { return \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( \get_the_author_meta( 'registered', $this->_id ) ) ); } + /** + * Returns the public key. + * + * @return array The public key. + */ public function get_public_key() { return array( 'id' => $this->get_id() . '#main-key', @@ -236,6 +278,11 @@ public function get_featured() { return get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $this->get__id() ) ); } + /** + * Returns the endpoints. + * + * @return array|null The endpoints. + */ public function get_endpoints() { $endpoints = null; @@ -267,18 +314,38 @@ public function get_webfinger() { return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST ); } + /** + * Returns the canonical URL. + * + * @return string The canonical URL. + */ public function get_canonical_url() { return $this->get_url(); } + /** + * Returns the streams. + * + * @return null The streams. + */ public function get_streams() { return null; } + /** + * Returns the tag. + * + * @return array The tag. + */ public function get_tag() { return array(); } + /** + * Returns the indexable state. + * + * @return bool Whether the user is indexable. + */ public function get_indexable() { if ( is_blog_public() ) { return true; @@ -287,12 +354,11 @@ public function get_indexable() { } } - /** - * Update the User-Name. + * Update the username. * - * @param mixed $value The new value. - * @return bool True if the attribute was updated, false otherwise. + * @param string $value The new value. + * @return int|WP_Error The updated user ID or WP_Error on failure. */ public function update_name( $value ) { $userdata = array( @@ -303,9 +369,9 @@ public function update_name( $value ) { } /** - * Update the User-Description. + * Update the User description. * - * @param mixed $value The new value. + * @param string $value The new value. * @return bool True if the attribute was updated, false otherwise. */ public function update_summary( $value ) { @@ -313,9 +379,9 @@ public function update_summary( $value ) { } /** - * Update the User-Icon. + * Update the User icon. * - * @param mixed $value The new value. Should be an attachment ID. + * @param int $value The new value. Should be an attachment ID. * @return bool True if the attribute was updated, false otherwise. */ public function update_icon( $value ) { @@ -328,7 +394,7 @@ public function update_icon( $value ) { /** * Update the User-Header-Image. * - * @param mixed $value The new value. Should be an attachment ID. + * @param int $value The new value. Should be an attachment ID. * @return bool True if the attribute was updated, false otherwise. */ public function update_header( $value ) { diff --git a/includes/rest/class-actors.php b/includes/rest/class-actors.php index f7bfc45a5..60f03d298 100644 --- a/includes/rest/class-actors.php +++ b/includes/rest/class-actors.php @@ -1,18 +1,22 @@ get_param( 'user_id' ); @@ -79,15 +83,15 @@ public static function get( $request ) { $link_header = sprintf( '<%1$s>; rel="alternate"; type="application/activity+json"', $user->get_id() ); - // redirect to canonical URL if it is not an ActivityPub request + // Redirect to canonical URL if it is not an ActivityPub request. if ( ! is_activitypub_request() ) { header( 'Link: ' . $link_header ); header( 'Location: ' . $user->get_canonical_url(), true, 301 ); exit; } - /* - * Action triggerd prior to the ActivityPub profile being created and sent to the client + /** + * Action triggered prior to the ActivityPub profile being created and sent to the client. */ \do_action( 'activitypub_rest_users_pre' ); @@ -102,11 +106,11 @@ public static function get( $request ) { /** - * Endpoint for remote follow UI/Block + * Endpoint for remote follow UI/Block. * * @param WP_REST_Request $request The request object. * - * @return void|string The URL to the remote follow page + * @return WP_REST_Response|\WP_Error The response object or WP_Error. */ public static function remote_follow_get( WP_REST_Request $request ) { $resource = $request->get_param( 'resource' ); @@ -136,9 +140,9 @@ public static function remote_follow_get( WP_REST_Request $request ) { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array List of parameters, */ public static function request_parameters() { $params = array(); diff --git a/includes/rest/class-collection.php b/includes/rest/class-collection.php index c15ddd7d9..02cc99bf9 100644 --- a/includes/rest/class-collection.php +++ b/includes/rest/class-collection.php @@ -1,4 +1,10 @@ get_param( 'type' ); @@ -144,9 +150,9 @@ public static function replies_get( $request ) { /** * The Featured Tags endpoint * - * @param WP_REST_Request $request The request object. + * @param \WP_REST_Request $request The request object. * - * @return WP_REST_Response The response object. + * @return WP_REST_Response|\WP_Error The response object or WP_Error. */ public static function tags_get( $request ) { $user_id = $request->get_param( 'user_id' ); @@ -196,9 +202,9 @@ public static function tags_get( $request ) { /** * Featured posts endpoint * - * @param WP_REST_Request $request The request object. + * @param \WP_REST_Request $request The request object. * - * @return WP_REST_Response The response object. + * @return WP_REST_Response|\WP_Error The response object or WP_Error. */ public static function featured_get( $request ) { $user_id = $request->get_param( 'user_id' ); @@ -254,7 +260,7 @@ public static function featured_get( $request ) { } /** - * Moderators endpoint + * Moderators endpoint. * * @return WP_REST_Response The response object. */ @@ -279,9 +285,9 @@ public static function moderators_get() { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array List of parameters. */ public static function request_parameters() { $params = array(); @@ -295,9 +301,9 @@ public static function request_parameters() { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array list of parameters. */ public static function request_parameters_for_replies() { $params = array(); diff --git a/includes/rest/class-comment.php b/includes/rest/class-comment.php index cb9e11260..095c85892 100644 --- a/includes/rest/class-comment.php +++ b/includes/rest/class-comment.php @@ -1,4 +1,10 @@ get_param( 'resource' ); diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index 0543a199a..fe97548ee 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -1,7 +1,12 @@ get_param( 'user_id' ); @@ -64,36 +69,34 @@ public static function get( $request ) { $page = (int) $request->get_param( 'page' ); $context = $request->get_param( 'context' ); - /* - * Action triggerd prior to the ActivityPub profile being created and sent to the client + /** + * Action triggered prior to the ActivityPub profile being created and sent to the client */ \do_action( 'activitypub_rest_followers_pre' ); $data = Follower_Collection::get_followers_with_count( $user_id, $per_page, $page, array( 'order' => ucwords( $order ) ) ); $json = new stdClass(); + // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $json->{'@context'} = \Activitypub\get_context(); - - $json->id = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) ); - $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); - $json->actor = $user->get_id(); - $json->type = 'OrderedCollectionPage'; - - $json->totalItems = $data['total']; // phpcs:ignore - $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) ); // phpcs:ignore - - $json->first = \add_query_arg( 'page', 1, $json->partOf ); // phpcs:ignore - $json->last = \add_query_arg( 'page', \ceil ( $json->totalItems / $per_page ), $json->partOf ); // phpcs:ignore - - if ( $page && ( ( \ceil ( $json->totalItems / $per_page ) ) > $page ) ) { // phpcs:ignore - $json->next = \add_query_arg( 'page', $page + 1, $json->partOf ); // phpcs:ignore + $json->id = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) ); + $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); + $json->actor = $user->get_id(); + $json->type = 'OrderedCollectionPage'; + $json->totalItems = $data['total']; + $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/followers', $user->get__id() ) ); + + $json->first = \add_query_arg( 'page', 1, $json->partOf ); + $json->last = \add_query_arg( 'page', \ceil( $json->totalItems / $per_page ), $json->partOf ); + + if ( $page && ( ( \ceil( $json->totalItems / $per_page ) ) > $page ) ) { + $json->next = \add_query_arg( 'page', $page + 1, $json->partOf ); } - if ( $page && ( $page > 1 ) ) { // phpcs:ignore - $json->prev = \add_query_arg( 'page', $page - 1, $json->partOf ); // phpcs:ignore + if ( $page && ( $page > 1 ) ) { + $json->prev = \add_query_arg( 'page', $page - 1, $json->partOf ); } - // phpcs:ignore $json->orderedItems = array_map( function ( $item ) use ( $context ) { if ( 'full' === $context ) { @@ -103,6 +106,7 @@ function ( $item ) use ( $context ) { }, $data['followers'] ); + // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $rest_response = new WP_REST_Response( $json, 200 ); $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); @@ -111,9 +115,9 @@ function ( $item ) use ( $context ) { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array List of parameters. */ public static function request_parameters() { $params = array(); diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index e3e22cc5a..bf546e96d 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -1,4 +1,10 @@ get_param( 'user_id' ); @@ -58,8 +64,8 @@ public static function get( $request ) { return $user; } - /* - * Action triggerd prior to the ActivityPub profile being created and sent to the client + /** + * Action triggered prior to the ActivityPub profile being created and sent to the client. */ \do_action( 'activitypub_rest_following_pre' ); @@ -67,19 +73,25 @@ public static function get( $request ) { $json->{'@context'} = \Activitypub\get_context(); + // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $json->id = get_rest_url_by_path( sprintf( 'actors/%d/following', $user->get__id() ) ); $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); $json->actor = $user->get_id(); $json->type = 'OrderedCollectionPage'; + $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/following', $user->get__id() ) ); - $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/following', $user->get__id() ) ); // phpcs:ignore - - $items = apply_filters( 'activitypub_rest_following', array(), $user ); // phpcs:ignore - - $json->totalItems = is_countable( $items ) ? count( $items ) : 0; // phpcs:ignore - $json->orderedItems = $items; // phpcs:ignore + /** + * Filter the list of following urls. + * + * @param array $items The array of following urls. + * @param \Activitypub\Model\User $user The user object. + */ + $items = apply_filters( 'activitypub_rest_following', array(), $user ); - $json->first = $json->partOf; // phpcs:ignore + $json->totalItems = is_countable( $items ) ? count( $items ) : 0; + $json->orderedItems = $items; + $json->first = $json->partOf; + // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $rest_response = new WP_REST_Response( $json, 200 ); $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); @@ -88,9 +100,9 @@ public static function get( $request ) { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array List of parameters. */ public static function request_parameters() { $params = array(); diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 9ade01ead..d4a104f6c 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -1,7 +1,12 @@ get_param( 'user_id' ); @@ -80,27 +85,33 @@ public static function user_inbox_get( $request ) { return $user; } - /* - * Action triggerd prior to the ActivityPub profile being created and sent to the client + /** + * Action triggered prior to the ActivityPub profile being created and sent to the client. */ \do_action( 'activitypub_rest_inbox_pre' ); $json = new \stdClass(); + // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $json->{'@context'} = get_context(); $json->id = get_rest_url_by_path( sprintf( 'actors/%d/inbox', $user->get__id() ) ); $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); $json->type = 'OrderedCollectionPage'; - $json->partOf = get_rest_url_by_path( sprintf( 'actors/%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 + $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/inbox', $user->get__id() ) ); + $json->totalItems = 0; + $json->orderedItems = array(); + $json->first = $json->partOf; + // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + + /** + * Filter the ActivityPub inbox array. + * + * @param array $json The ActivityPub inbox array. + */ $json = \apply_filters( 'activitypub_rest_inbox_array', $json ); - /* - * Action triggerd after the ActivityPub profile has been created and sent to the client + /** + * Action triggered after the ActivityPub profile has been created and sent to the client. */ \do_action( 'activitypub_inbox_post' ); @@ -111,11 +122,11 @@ public static function user_inbox_get( $request ) { } /** - * Handles user-inbox requests + * Handles user-inbox requests. * - * @param WP_REST_Request $request + * @param \WP_REST_Request $request The request object. * - * @return WP_REST_Response + * @return WP_REST_Response|\WP_Error The response object or WP_Error. */ public static function user_inbox_post( $request ) { $user_id = $request->get_param( 'user_id' ); @@ -130,7 +141,23 @@ public static function user_inbox_post( $request ) { $type = $request->get_param( 'type' ); $type = \strtolower( $type ); + /** + * ActivityPub inbox action. + * + * @param array $data The data array. + * @param int|null $user_id The user ID. + * @param string $type The type of the activity. + * @param Activity $activity The Activity object. + */ \do_action( 'activitypub_inbox', $data, $user->get__id(), $type, $activity ); + + /** + * ActivityPub inbox action for specific activity types. + * + * @param array $data The data array. + * @param int|null $user_id The user ID. + * @param Activity $activity The Activity object. + */ \do_action( "activitypub_inbox_{$type}", $data, $user->get__id(), $activity ); $rest_response = new WP_REST_Response( array(), 202 ); @@ -140,9 +167,9 @@ public static function user_inbox_post( $request ) { } /** - * The shared inbox + * The shared inbox. * - * @param WP_REST_Request $request + * @param \WP_REST_Request $request The request object. * * @return WP_REST_Response */ @@ -152,7 +179,23 @@ public static function shared_inbox_post( $request ) { $type = $request->get_param( 'type' ); $type = \strtolower( $type ); + /** + * ActivityPub inbox action. + * + * @param array $data The data array. + * @param int|null $user_id The user ID. + * @param string $type The type of the activity. + * @param Activity $activity The Activity object. + */ \do_action( 'activitypub_inbox', $data, null, $type, $activity ); + + /** + * ActivityPub inbox action for specific activity types. + * + * @param array $data The data array. + * @param int|null $user_id The user ID. + * @param Activity $activity The Activity object. + */ \do_action( "activitypub_inbox_{$type}", $data, null, $activity ); $rest_response = new WP_REST_Response( array(), 202 ); @@ -162,9 +205,9 @@ public static function shared_inbox_post( $request ) { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array List of parameters. */ public static function user_inbox_get_parameters() { $params = array(); @@ -182,9 +225,9 @@ public static function user_inbox_get_parameters() { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array List of parameters. */ public static function user_inbox_post_parameters() { $params = array(); @@ -201,10 +244,7 @@ public static function user_inbox_post_parameters() { $params['actor'] = array( 'required' => true, - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - 'sanitize_callback' => function ( $param, $request, $key ) { - return object_to_uri( $param ); - }, + 'sanitize_callback' => 'object_to_uri', ); $params['type'] = array( @@ -214,6 +254,14 @@ public static function user_inbox_post_parameters() { $params['object'] = array( 'required' => true, 'validate_callback' => function ( $param, $request, $key ) { + /** + * Filter the ActivityPub object validation. + * + * @param bool $validate The validation result. + * @param array $param The object data. + * @param object $request The request object. + * @param string $key The key. + */ return apply_filters( 'activitypub_validate_object', true, $param, $request, $key ); }, ); @@ -222,17 +270,16 @@ public static function user_inbox_post_parameters() { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array List of parameters. */ public static function shared_inbox_post_parameters() { $params = self::user_inbox_post_parameters(); $params['to'] = array( 'required' => false, - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - 'sanitize_callback' => function ( $param, $request, $key ) { + 'sanitize_callback' => function ( $param ) { if ( \is_string( $param ) ) { $param = array( $param ); } @@ -242,8 +289,7 @@ public static function shared_inbox_post_parameters() { ); $params['cc'] = array( - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - 'sanitize_callback' => function ( $param, $request, $key ) { + 'sanitize_callback' => function ( $param ) { if ( \is_string( $param ) ) { $param = array( $param ); } @@ -253,8 +299,7 @@ public static function shared_inbox_post_parameters() { ); $params['bcc'] = array( - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - 'sanitize_callback' => function ( $param, $request, $key ) { + 'sanitize_callback' => function ( $param ) { if ( \is_string( $param ) ) { $param = array( $param ); } @@ -267,11 +312,11 @@ public static function shared_inbox_post_parameters() { } /** - * Get local user recipients + * Get local user recipients. * - * @param array $data + * @param array $data The data array. * - * @return array The list of local users + * @return array The list of local users. */ public static function get_recipients( $data ) { $recipients = extract_recipients_from_activity( $data ); diff --git a/includes/rest/class-interaction.php b/includes/rest/class-interaction.php index 011f13030..8bf78267e 100644 --- a/includes/rest/class-interaction.php +++ b/includes/rest/class-interaction.php @@ -1,12 +1,21 @@ get_param( 'uri' ); @@ -78,10 +87,16 @@ public static function get( $request ) { $redirect_url = \apply_filters( 'activitypub_interactions_reply_url', $redirect_url, $uri, $object ); } - // generic Interaction hook + /** + * Filter the redirect URL. + * + * @param string $redirect_url The URL to redirect to. + * @param string $uri The URI of the object. + * @param array $object The object. + */ $redirect_url = \apply_filters( 'activitypub_interactions_url', $redirect_url, $uri, $object ); - // check if hook is implemented + // Check if hook is implemented. if ( ! $redirect_url ) { \wp_die( esc_html__( diff --git a/includes/rest/class-nodeinfo.php b/includes/rest/class-nodeinfo.php index 1ec5d502e..3eb5fc007 100644 --- a/includes/rest/class-nodeinfo.php +++ b/includes/rest/class-nodeinfo.php @@ -1,4 +1,10 @@ get_param( 'user_id' ); @@ -64,41 +69,43 @@ public static function user_outbox_get( $request ) { $page = $request->get_param( 'page', 1 ); - /* - * Action triggerd prior to the ActivityPub profile being created and sent to the client + /** + * Action triggered prior to the ActivityPub profile being created and sent to the client. */ \do_action( 'activitypub_rest_outbox_pre' ); $json = new stdClass(); + // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $json->{'@context'} = get_context(); $json->id = get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) ); $json->generator = 'http://wordpress.org/?v=' . get_masked_wp_version(); $json->actor = $user->get_id(); $json->type = 'OrderedCollectionPage'; - $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) ); // phpcs:ignore - $json->totalItems = 0; // phpcs:ignore + $json->partOf = get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) ); + $json->totalItems = 0; if ( $user_id > 0 ) { $count_posts = \count_user_posts( $user_id, $post_types, true ); - $json->totalItems = \intval( $count_posts ); // phpcs:ignore + $json->totalItems = \intval( $count_posts ); } else { foreach ( $post_types as $post_type ) { $count_posts = \wp_count_posts( $post_type ); - $json->totalItems += \intval( $count_posts->publish ); // phpcs:ignore + $json->totalItems += \intval( $count_posts->publish ); } } - $json->first = \add_query_arg( 'page', 1, $json->partOf ); // phpcs:ignore - $json->last = \add_query_arg( 'page', \ceil ( $json->totalItems / 10 ), $json->partOf ); // phpcs:ignore + $json->first = \add_query_arg( 'page', 1, $json->partOf ); + $json->last = \add_query_arg( 'page', \ceil( $json->totalItems / 10 ), $json->partOf ); - if ( $page && ( ( \ceil ( $json->totalItems / 10 ) ) > $page ) ) { // phpcs:ignore - $json->next = \add_query_arg( 'page', $page + 1, $json->partOf ); // phpcs:ignore + if ( $page && ( ( \ceil( $json->totalItems / 10 ) ) > $page ) ) { + $json->next = \add_query_arg( 'page', $page + 1, $json->partOf ); } if ( $page && ( $page > 1 ) ) { // phpcs:ignore - $json->prev = \add_query_arg( 'page', $page - 1, $json->partOf ); // phpcs:ignore + $json->prev = \add_query_arg( 'page', $page - 1, $json->partOf ); } + // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase if ( $page ) { $posts = \get_posts( @@ -121,15 +128,19 @@ public static function user_outbox_get( $request ) { $activity = new Activity(); $activity->set_type( 'Create' ); $activity->set_object( $post ); - $json->orderedItems[] = $activity->to_array( false ); // phpcs:ignore + $json->orderedItems[] = $activity->to_array( false ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase } } - // filter output + /** + * Filter the ActivityPub outbox array. + * + * @param array $json The ActivityPub outbox array. + */ $json = \apply_filters( 'activitypub_rest_outbox_array', $json ); - /* - * Action triggerd after the ActivityPub profile has been created and sent to the client + /** + * Action triggered after the ActivityPub profile has been created and sent to the client */ \do_action( 'activitypub_outbox_post' ); @@ -140,9 +151,9 @@ public static function user_outbox_get( $request ) { } /** - * The supported parameters + * The supported parameters. * - * @return array list of parameters + * @return array List of parameters. */ public static function request_parameters() { $params = array(); diff --git a/includes/rest/class-server.php b/includes/rest/class-server.php index a910e367f..aac34982b 100644 --- a/includes/rest/class-server.php +++ b/includes/rest/class-server.php @@ -1,14 +1,19 @@ get_route(); - // check if it is an activitypub request and exclude webfinger and nodeinfo endpoints + // Check if it is an activitypub request and exclude webfinger and nodeinfo endpoints. if ( ! \str_starts_with( $route, '/' . ACTIVITYPUB_REST_NAMESPACE ) || \str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'webfinger' ) || @@ -95,13 +100,13 @@ public static function authorize_activitypub_requests( $response, $handler, $req } /** - * Filter to defer signature verification + * Filter to defer signature verification. * * Skip signature verification for debugging purposes or to reduce load for * certain Activity-Types, like "Delete". * - * @param bool $defer Whether to defer signature verification. - * @param WP_REST_Request $request The request used to generate the response. + * @param bool $defer Whether to defer signature verification. + * @param \WP_REST_Request $request The request used to generate the response. * * @return bool Whether to defer signature verification. */ @@ -112,9 +117,9 @@ public static function authorize_activitypub_requests( $response, $handler, $req } if ( - // POST-Requests are always signed + // POST-Requests are always signed. 'GET' !== $request->get_method() || - // GET-Requests only require a signature in secure mode + // GET-Requests only require a signature in secure mode. ( 'GET' === $request->get_method() && ACTIVITYPUB_AUTHORIZED_FETCH ) ) { $verified_request = Signature::verify_http_signature( $request ); @@ -133,10 +138,10 @@ public static function authorize_activitypub_requests( $response, $handler, $req /** * Callback function to validate incoming ActivityPub requests * - * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. - * Usually a WP_REST_Response or WP_Error. - * @param array $handler Route handler used for the request. - * @param WP_REST_Request $request Request used to generate the response. + * @param WP_REST_Response|\WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. + * Usually a WP_REST_Response or WP_Error. + * @param array $handler Route handler used for the request. + * @param \WP_REST_Request $request Request used to generate the response. * * @return mixed|WP_Error The response, error, or modified response. */ @@ -156,7 +161,7 @@ public static function validate_activitypub_requests( $response, $handler, $requ $params = $request->get_json_params(); - // Type is required for ActivityPub requests, so it fail later in the process + // Type is required for ActivityPub requests, so it fail later in the process. if ( ! isset( $params['type'] ) ) { return $response; } diff --git a/includes/rest/class-webfinger.php b/includes/rest/class-webfinger.php index b6bba3d57..868ed50d2 100644 --- a/includes/rest/class-webfinger.php +++ b/includes/rest/class-webfinger.php @@ -1,11 +1,16 @@ id === 'settings_page_activitypub' ) { $this->user_id = Users::BLOG_USER_ID; @@ -30,6 +47,11 @@ public function __construct() { ); } + /** + * Get columns. + * + * @return array + */ public function get_columns() { return array( 'cb' => '', @@ -42,16 +64,22 @@ public function get_columns() { ); } + /** + * Returns sortable columns. + * + * @return array + */ public function get_sortable_columns() { - $sortable_columns = array( + return array( 'post_title' => array( 'post_title', true ), 'modified' => array( 'modified', false ), 'published' => array( 'published', false ), ); - - return $sortable_columns; } + /** + * Prepare items. + */ public function prepare_items() { $columns = $this->get_columns(); $hidden = array(); @@ -113,12 +141,24 @@ public function prepare_items() { } } + /** + * Returns bulk actions. + * + * @return array + */ public function get_bulk_actions() { return array( 'delete' => __( 'Delete', 'activitypub' ), ); } + /** + * Column default. + * + * @param array $item Item. + * @param string $column_name Column name. + * @return string + */ public function column_default( $item, $column_name ) { if ( ! array_key_exists( $column_name, $item ) ) { return __( 'None', 'activitypub' ); @@ -126,6 +166,12 @@ public function column_default( $item, $column_name ) { return $item[ $column_name ]; } + /** + * Column avatar. + * + * @param array $item Item. + * @return string + */ public function column_avatar( $item ) { return sprintf( '', @@ -133,45 +179,63 @@ public function column_avatar( $item ) { ); } + /** + * Column url. + * + * @param array $item Item. + * @return string + */ public function column_url( $item ) { return sprintf( '%s', - $item['url'], + esc_url( $item['url'] ), $item['url'] ); } + /** + * Column cb. + * + * @param array $item Item. + * @return string + */ public function column_cb( $item ) { return sprintf( '', esc_attr( $item['identifier'] ) ); } + /** + * Process action. + */ public function process_action() { if ( ! isset( $_REQUEST['followers'] ) || ! isset( $_REQUEST['_wpnonce'] ) ) { - return false; + return; } $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ); if ( ! wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) { - return false; + return; } if ( ! current_user_can( 'edit_user', $this->user_id ) ) { - return false; + return; } $followers = $_REQUEST['followers']; // phpcs:ignore - switch ( $this->current_action() ) { - case 'delete': - if ( ! is_array( $followers ) ) { - $followers = array( $followers ); - } - foreach ( $followers as $follower ) { - FollowerCollection::remove_follower( $this->user_id, $follower ); - } - break; + if ( $this->current_action() === 'delete' ) { + if ( ! is_array( $followers ) ) { + $followers = array( $followers ); + } + foreach ( $followers as $follower ) { + FollowerCollection::remove_follower( $this->user_id, $follower ); + } } } + /** + * Returns user count. + * + * @return int + */ public function get_user_count() { return FollowerCollection::count_followers( $this->user_id ); } diff --git a/includes/transformer/class-attachment.php b/includes/transformer/class-attachment.php index 2fc46350e..98aaf8bf4 100644 --- a/includes/transformer/class-attachment.php +++ b/includes/transformer/class-attachment.php @@ -1,10 +1,14 @@ wp_object->ID ); $media_type = preg_replace( '/(\/[a-zA-Z]+)/i', '', $mime_type ); + $type = ''; switch ( $media_type ) { case 'audio': diff --git a/includes/transformer/class-base.php b/includes/transformer/class-base.php index f33e56428..6d4c202a6 100644 --- a/includes/transformer/class-base.php +++ b/includes/transformer/class-base.php @@ -1,4 +1,10 @@ wp_object = $wp_object; @@ -50,9 +55,9 @@ public function __construct( $wp_object ) { /** * Transform all properties with available get(ter) functions. * - * @param Base_Object|object $object + * @param Base_Object|object $activitypub_object The ActivityPub Object. * - * @return Base_Object|object $object + * @return Base_Object|object */ protected function transform_object_properties( $activitypub_object ) { $vars = $activitypub_object->get_object_var_keys(); @@ -76,13 +81,12 @@ protected function transform_object_properties( $activitypub_object ) { /** * Transform the WordPress Object into an ActivityPub Object. * - * @return Activitypub\Activity\Base_Object + * @return Base_Object|object The ActivityPub Object. */ public function to_object() { $activitypub_object = new Base_Object(); - $activitypub_object = $this->transform_object_properties( $activitypub_object ); - return $activitypub_object; + return $this->transform_object_properties( $activitypub_object ); } /** @@ -90,7 +94,7 @@ public function to_object() { * * @param string $type The Activity-Type. * - * @return \Activitypub\Activity\Activity The Activity. + * @return Activity The Activity. */ public function to_activity( $type ) { $object = $this->to_object(); @@ -101,7 +105,7 @@ public function to_activity( $type ) { // Pre-fill the Activity with data (for example cc and to). $activity->set_object( $object ); - // Use simple Object (only ID-URI) for Like and Announce + // Use simple Object (only ID-URI) for Like and Announce. if ( in_array( $type, array( 'Like', 'Announce' ), true ) ) { $activity->set_object( $object->get_id() ); } @@ -109,27 +113,27 @@ public function to_activity( $type ) { return $activity; } + /** + * Get the ID of the WordPress Object. + */ abstract protected function get_id(); /** * Get the replies Collection. */ public function get_replies() { - $replies = Replies::get_collection( $this->wp_object, $this->get_id() ); - return $replies; + return Replies::get_collection( $this->wp_object ); } /** * Returns the ID of the WordPress Object. - * - * @return int The ID of the WordPress Object */ abstract public function get_wp_user_id(); /** * Change the User-ID of the WordPress Post. * - * @return int The User-ID of the WordPress Post + * @param int $user_id The new user ID. */ abstract public function change_wp_user_id( $user_id ); } diff --git a/includes/transformer/class-comment.php b/includes/transformer/class-comment.php index 930f8fe52..7f0d3f685 100644 --- a/includes/transformer/class-comment.php +++ b/includes/transformer/class-comment.php @@ -1,21 +1,23 @@ wp_object->user_id = $user_id; } /** - * Transforms the WP_Comment object to an ActivityPub Object + * Transforms the WP_Comment object to an ActivityPub Object. * * @see \Activitypub\Activity\Base_Object * - * @return \Activitypub\Activity\Base_Object The ActivityPub Object + * @return \Activitypub\Activity\Base_Object The ActivityPub Object. */ public function to_object() { $comment = $this->wp_object; @@ -106,28 +108,41 @@ protected function get_attributed_to() { * @return string The content. */ protected function get_content() { - // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $comment = $this->wp_object; $content = $comment->comment_content; + /** + * Filter the content of the comment. + * + * @param string $content The content of the comment. + * @param \WP_Comment $comment The comment object. + * @param array $args The arguments. + * + * @return string The filtered content of the comment. + */ $content = \apply_filters( 'comment_text', $content, $comment, array() ); $content = \preg_replace( '/[\n\r\t]/', '', $content ); $content = \trim( $content ); - $content = \apply_filters( 'activitypub_the_content', $content, $comment ); - return $content; + /** + * Filter the content of the comment. + * + * @param string $content The content of the comment. + * @param \WP_Comment $comment The comment object. + * + * @return string The filtered content of the comment. + */ + return \apply_filters( 'activitypub_the_content', $content, $comment ); } /** * Returns the in-reply-to for the ActivityPub Item. * - * @return int The URL of the in-reply-to. + * @return false|string|null The URL of the in-reply-to. */ protected function get_in_reply_to() { - $comment = $this->wp_object; - + $comment = $this->wp_object; $parent_comment = null; - $in_reply_to = null; if ( $comment->comment_parent ) { $parent_comment = \get_comment( $comment->comment_parent ); @@ -211,6 +226,15 @@ protected function get_tag() { protected function get_mentions() { \add_filter( 'activitypub_extract_mentions', array( $this, 'extract_reply_context' ) ); + /** + * Filter the mentions in the comment. + * + * @param array $mentions The list of mentions. + * @param string $content The content of the comment. + * @param \WP_Comment $comment The comment object. + * + * @return array The filtered list of mentions. + */ return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->comment_content, $this->wp_object ); } @@ -222,7 +246,7 @@ protected function get_mentions() { protected function get_comment_ancestors() { $ancestors = get_comment_ancestors( $this->wp_object ); - // Now that we have the full tree of ancestors, only return the ones received from the fediverse + // Now that we have the full tree of ancestors, only return the ones received from the fediverse. return array_filter( $ancestors, function ( $comment_id ) { @@ -235,12 +259,12 @@ function ( $comment_id ) { * Collect all other Users that participated in this comment-thread * to send them a notification about the new reply. * - * @param array $mentions The already mentioned ActivityPub users + * @param array $mentions The already mentioned ActivityPub users. * * @return array The list of all Repliers. */ public function extract_reply_context( $mentions ) { - // Check if `$this->wp_object` is a WP_Comment + // Check if `$this->wp_object` is a WP_Comment. if ( 'WP_Comment' !== get_class( $this->wp_object ) ) { return $mentions; } @@ -276,9 +300,9 @@ public function get_locale() { /** * Filter the locale of the comment. * - * @param string $lang The locale of the comment. - * @param int $comment_id The comment ID. - * @param WP_Post $post The comment object. + * @param string $lang The locale of the comment. + * @param int $comment_id The comment ID. + * @param \WP_Post $post The comment object. * * @return string The filtered locale of the comment. */ diff --git a/includes/transformer/class-factory.php b/includes/transformer/class-factory.php index 2824072c5..e8e597966 100644 --- a/includes/transformer/class-factory.php +++ b/includes/transformer/class-factory.php @@ -1,11 +1,13 @@ post_type ) { diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index ca996cf89..85a8b809e 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -1,23 +1,26 @@ wp_object->post_author = $user_id; @@ -80,7 +85,7 @@ public function to_object() { * * If `single_user` mode is enabled, the Blog-User is returned. * - * @return Activitypub\Activity\Actor The User-Object. + * @return \Activitypub\Activity\Actor The User-Object. */ protected function get_actor_object() { if ( $this->actor_object ) { @@ -126,7 +131,7 @@ public function get_url() { $permalink = \get_post_meta( $post->ID, 'activitypub_canonical_url', true ); break; case 'draft': - // get_sample_permalink is in wp-admin, not always loaded + // Get_sample_permalink is in wp-admin, not always loaded. if ( ! \function_exists( '\get_sample_permalink' ) ) { require_once ABSPATH . 'wp-admin/includes/post.php'; } @@ -179,7 +184,7 @@ protected function get_attachment() { ); $id = $this->wp_object->ID; - // list post thumbnail first if this post has one + // List post thumbnail first if this post has one. if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) { $media['image'][] = array( 'id' => \get_post_thumbnail_id( $id ) ); } @@ -207,17 +212,17 @@ protected function get_attachment() { */ $media = \apply_filters( 'activitypub_attachment_ids', $media, $this->wp_object ); - $attchments = \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $media ) ); + $attachments = \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $media ) ); /** * Filter the attachments for a post. * - * @param array $attchments The attachments. + * @param array $attachments The attachments. * @param WP_Post $this->wp_object The post object. * * @return array The filtered attachments. */ - return \apply_filters( 'activitypub_attachments', $attchments, $this->wp_object ); + return \apply_filters( 'activitypub_attachments', $attachments, $this->wp_object ); } /** @@ -235,7 +240,7 @@ public function get_enclosures( $media ) { } foreach ( $enclosures as $enclosure ) { - // check if URL is an attachment + // Check if URL is an attachment. $attachment_id = \attachment_url_to_postid( $enclosure['url'] ); if ( $attachment_id ) { $enclosure['id'] = $attachment_id; @@ -271,29 +276,27 @@ public function get_enclosures( $media ) { * @return array The attachments. */ protected function get_block_attachments( $media, $max_media ) { - // max media can't be negative or zero + // Max media can't be negative or zero. if ( $max_media <= 0 ) { return array(); } $blocks = \parse_blocks( $this->wp_object->post_content ); - $media = self::get_media_from_blocks( $blocks, $media ); - return $media; + return self::get_media_from_blocks( $blocks, $media ); } /** * Recursively get media IDs from blocks. * - * @param array $blocks The blocks to search for media IDs - * @param array $media The media IDs to append new IDs to - * @param int $max_media The maximum number of media to return. + * @param array $blocks The blocks to search for media IDs. + * @param array $media The media IDs to append new IDs to. * * @return array The image IDs. */ protected static function get_media_from_blocks( $blocks, $media ) { foreach ( $blocks as $block ) { - // recurse into inner blocks + // Recurse into inner blocks. if ( ! empty( $block['innerBlocks'] ) ) { $media = self::get_media_from_blocks( $block['innerBlocks'], $media ); } @@ -364,7 +367,7 @@ function ( $id ) { * @return array The attachments. */ protected function get_classic_editor_images( $media, $max_images ) { - // max images can't be negative or zero + // Max images can't be negative or zero. if ( $max_images <= 0 ) { return array(); } @@ -388,12 +391,12 @@ protected function get_classic_editor_images( $media, $max_images ) { * @return array The attachments. */ protected function get_classic_editor_image_embeds( $max_images ) { - // if someone calls that function directly, bail + // If someone calls that function directly, bail. if ( ! \class_exists( '\WP_HTML_Tag_Processor' ) ) { return array(); } - // max images can't be negative or zero + // Max images can't be negative or zero. if ( $max_images <= 0 ) { return array(); } @@ -403,20 +406,21 @@ protected function get_classic_editor_image_embeds( $max_images ) { $content = \get_post_field( 'post_content', $this->wp_object ); $tags = new \WP_HTML_Tag_Processor( $content ); - // This linter warning is a false positive - we have to - // re-count each time here as we modify $images. + // This linter warning is a false positive - we have to re-count each time here as we modify $images. // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found while ( $tags->next_tag( 'img' ) && ( \count( $images ) <= $max_images ) ) { $src = $tags->get_attribute( 'src' ); - // If the img source is in our uploads dir, get the - // associated ID. Note: if there's a -500x500 - // type suffix, we remove it, but we try the original - // first in case the original image is actually called - // that. Likewise, we try adding the -scaled suffix for - // the case that this is a small version of an image - // that was big enough to get scaled down on upload: - // https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/ + /* + * If the img source is in our uploads dir, get the + * associated ID. Note: if there's a -500x500 + * type suffix, we remove it, but we try the original + * first in case the original image is actually called + * that. Likewise, we try adding the -scaled suffix for + * the case that this is a small version of an image + * that was big enough to get scaled down on upload: + * https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/ + */ if ( null !== $src && \str_starts_with( $src, $base ) ) { $img_id = \attachment_url_to_postid( $src ); @@ -455,7 +459,7 @@ protected function get_classic_editor_image_embeds( $max_images ) { * @return array The attachment IDs. */ protected function get_classic_editor_image_attachments( $max_images ) { - // max images can't be negative or zero + // Max images can't be negative or zero. if ( $max_images <= 0 ) { return array(); } @@ -485,12 +489,21 @@ protected function get_classic_editor_image_attachments( $max_images ) { /** * Filter media IDs by object type. * - * @param array $media The media array grouped by type. - * @param string $type The object type. + * @param array $media The media array grouped by type. + * @param string $type The object type. + * @param WP_Post $wp_object The post object. * * @return array The filtered media IDs. */ protected static function filter_media_by_object_type( $media, $type, $wp_object ) { + /** + * Filter the object type for media attachments. + * + * @param string $type The object type. + * @param WP_Post $wp_object The post object. + * + * @return string The filtered object type. + */ $type = \apply_filters( 'filter_media_by_object_type', \strtolower( $type ), $wp_object ); if ( ! empty( $media[ $type ] ) ) { @@ -516,7 +529,7 @@ public static function wp_attachment_to_activity_attachment( $media ) { $attachment = array(); $mime_type = \get_post_mime_type( $id ); $mime_type_parts = \explode( '/', $mime_type ); - // switching on image/audio/video + // Switching on image/audio/video. switch ( $mime_type_parts[0] ) { case 'image': $image_size = 'large'; @@ -564,7 +577,7 @@ public static function wp_attachment_to_activity_attachment( $media ) { 'name' => \esc_attr( \get_the_title( $id ) ), ); $meta = wp_get_attachment_metadata( $id ); - // height and width for videos + // Height and width for videos. if ( isset( $meta['width'] ) && isset( $meta['height'] ) ) { $attachment['width'] = \esc_attr( $meta['width'] ); $attachment['height'] = \esc_attr( $meta['height'] ); @@ -573,6 +586,14 @@ public static function wp_attachment_to_activity_attachment( $media ) { break; } + /** + * Filter the attachment for a post. + * + * @param array $attachment The attachment. + * @param int $id The attachment ID. + * + * @return array The filtered attachment. + */ return \apply_filters( 'activitypub_attachment', $attachment, $id ); } @@ -679,7 +700,11 @@ protected function get_cc() { return $cc; } - + /** + * Returns the Audience for the Post. + * + * @return string|null The audience. + */ public function get_audience() { if ( is_single_user() ) { return null; @@ -818,7 +843,7 @@ protected function get_content() { $content = \apply_filters( 'activitypub_the_content', $content, $post ); - // Don't need these any more, should never appear in a post. + // Don't need these anymore, should never appear in a post. Shortcodes::unregister(); return $content; @@ -863,6 +888,15 @@ protected function get_post_content_template() { * @return array The list of @-Mentions. */ protected function get_mentions() { + /** + * Filter the mentions in the post content. + * + * @param array $mentions The mentions. + * @param string $content The post content. + * @param WP_Post $post The post object. + * + * @return array The filtered mentions. + */ return apply_filters( 'activitypub_extract_mentions', array(), @@ -1002,8 +1036,8 @@ public function get_summary_map() { * @see https://www.w3.org/TR/activitypub/#security-sanitizing-content * @see https://www.w3.org/wiki/ActivityPub/Primer/HTML * - * @param string $block_content The block content (html) - * @param object $block The block object + * @param string $block_content The block content (html). + * @param object $block The block object. * * @return string A block level link */ diff --git a/integration/class-buddypress.php b/integration/class-buddypress.php index 14afbe4db..1e86ee5fc 100644 --- a/integration/class-buddypress.php +++ b/integration/class-buddypress.php @@ -1,23 +1,37 @@ url = bp_core_get_user_domain( $author_id ); // add BP member profile URL as user URL + $author->url = bp_core_get_user_domain( $author_id ); // Add BP member profile URL as user URL. - // add BuddyPress' cover_image instead of WordPress' header_image + // Add BuddyPress' cover_image instead of WordPress' header_image. $cover_image_url = bp_attachments_get_attachment( 'url', array( 'item_id' => $author_id ) ); if ( $cover_image_url ) { @@ -27,7 +41,7 @@ public static function add_user_metadata( $author, $author_id ) { ); } - // change profile URL to BuddyPress' profile URL + // Change profile URL to BuddyPress' profile URL. $author->attachment['profile_url'] = array( 'type' => 'PropertyValue', 'name' => \__( 'Profile', 'activitypub' ), @@ -43,9 +57,9 @@ public static function add_user_metadata( $author, $author_id ) { ), ); - // replace blog URL on multisite + // Replace blog URL on multisite. if ( is_multisite() ) { - $user_blogs = get_blogs_of_user( $author_id ); // get sites of user to send as AP metadata + $user_blogs = get_blogs_of_user( $author_id ); // Get sites of user to send as AP metadata. if ( ! empty( $user_blogs ) ) { unset( $author->attachment['blog_url'] ); diff --git a/integration/class-enable-mastodon-apps.php b/integration/class-enable-mastodon-apps.php index c04b68fa7..04c24ff7c 100644 --- a/integration/class-enable-mastodon-apps.php +++ b/integration/class-enable-mastodon-apps.php @@ -1,4 +1,10 @@ update_icon( $data['avatar'] ) ) { - // unset the avatar so it doesn't get saved again by other plugins. + // Unset the avatar so it doesn't get saved again by other plugins. // Ditto for all other fields below. unset( $data['avatar'] ); } @@ -106,7 +110,7 @@ public static function api_update_credentials( $data, $user_id ) { } /** - * Get extra fields for Mastodon API + * Get extra fields for Mastodon API. * * @param int $user_id The user id to act on. * @return array The extra fields. @@ -126,11 +130,10 @@ private static function get_extra_fields( $user_id ) { } /** - * Set extra fields for Mastodon API + * Set extra fields for Mastodon API. * * @param int $user_id The user id to act on. * @param array $fields The fields to set. It is assumed to be the entire set of desired fields. - * @return void */ private static function set_extra_fields( $user_id, $fields ) { // The Mastodon API submits a simple hash for every field. @@ -168,12 +171,12 @@ private static function set_extra_fields( $user_id, $fields ) { } } } + /** - * Add followers to Mastodon API + * Add followers to Mastodon API. * - * @param array $followers An array of followers - * @param string $user_id The user id - * @param WP_REST_Request $request The request object + * @param array $followers An array of followers. + * @param string $user_id The user id. * * @return array The filtered followers */ @@ -219,18 +222,16 @@ function ( $item ) { $activitypub_followers ); - $followers = array_merge( $mastodon_followers, $followers ); - - return $followers; + return array_merge( $mastodon_followers, $followers ); } /** * Resolve external accounts for Mastodon API * - * @param Enable_Mastodon_Apps\Entity\Account $user_data The user data - * @param string $user_id The user id + * @param Account $user_data The user data. + * @param string $user_id The user id. * - * @return Enable_Mastodon_Apps\Entity\Account The filtered Account + * @return Account The filtered Account. */ public static function api_account_external( $user_data, $user_id ) { if ( $user_data || ( is_numeric( $user_id ) && $user_id ) ) { @@ -258,6 +259,14 @@ public static function api_account_external( $user_data, $user_id ) { return $user_data; } + /** + * Resolve internal accounts for Mastodon API + * + * @param Account $user_data The user data. + * @param string $user_id The user id. + * + * @return Account The filtered Account. + */ public static function api_account_internal( $user_data, $user_id ) { $user_id_to_use = self::maybe_map_user_to_blog( $user_id ); $user = Users::get_by_id( $user_id_to_use ); @@ -266,9 +275,9 @@ public static function api_account_internal( $user_data, $user_id ) { return $user_data; } - // convert user to account. + // Convert user to account. $account = new Account(); - // even if we have a blog user, maintain the provided user_id so as not to confuse clients + // Even if we have a blog user, maintain the provided user_id so as not to confuse clients. $account->id = (int) $user_id; $account->username = $user->get_preferred_username(); $account->acct = $account->username; @@ -301,7 +310,7 @@ public static function api_account_internal( $user_data, $user_id ) { $account->last_status_at = ! empty( $posts ) ? new DateTime( $posts[0]->post_date_gmt ) : $account->created_at; $account->fields = self::get_extra_fields( $user_id_to_use ); - // Now do it in source['fields'] with stripped tags + // Now do it in source['fields'] with stripped tags. $account->source['fields'] = \array_map( function ( $field ) { $field['value'] = \wp_strip_all_tags( $field['value'], true ); @@ -315,6 +324,13 @@ function ( $field ) { return $account; } + /** + * Get account for actor. + * + * @param string $uri The URI. + * + * @return Account|null The account. + */ private static function get_account_for_actor( $uri ) { if ( ! is_string( $uri ) ) { return null; @@ -362,6 +378,14 @@ private static function get_account_for_actor( $uri ) { return $account; } + /** + * Search by URL for Mastodon API. + * + * @param array $search_data The search data. + * @param object $request The request object. + * + * @return array The filtered search data. + */ public static function api_search_by_url( $search_data, $request ) { $p = \wp_parse_url( $request->get_param( 'q' ) ); if ( ! $p || ! isset( $p['host'] ) ) { @@ -386,6 +410,14 @@ public static function api_search_by_url( $search_data, $request ) { return $search_data; } + /** + * Search for Mastodon API. + * + * @param array $search_data The search data. + * @param object $request The request object. + * + * @return array The filtered search data. + */ public static function api_search( $search_data, $request ) { $user_id = \get_current_user_id(); if ( ! $user_id ) { @@ -433,6 +465,13 @@ public static function api_search( $search_data, $request ) { return $search_data; } + /** + * Get posts query args for Mastodon API. + * + * @param array $args The query arguments. + * + * @return array The filtered args. + */ public static function api_get_posts_query_args( $args ) { if ( isset( $args['author'] ) && is_string( $args['author'] ) ) { $uri = Webfinger_Util::resolve( $args['author'] ); @@ -445,6 +484,14 @@ public static function api_get_posts_query_args( $args ) { return $args; } + /** + * Convert an activity to a status. + * + * @param array $item The activity. + * @param Account $account The account. + * + * @return Status|null The status. + */ private static function activity_to_status( $item, $account ) { if ( isset( $item['object'] ) ) { $object = $item['object']; @@ -517,6 +564,14 @@ function ( $attachment ) { return $status; } + /** + * Get posts for Mastodon API. + * + * @param array $statuses The statuses. + * @param array $args The arguments. + * + * @return array The filtered statuses. + */ public static function api_statuses_external( $statuses, $args ) { if ( ! isset( $args['activitypub'] ) ) { return $statuses; @@ -579,6 +634,15 @@ function ( $item ) use ( $account, $args ) { return array_slice( $activitypub_statuses, 0, $limit ); } + /** + * Get replies for Mastodon API. + * + * @param array $context The context. + * @param int $post_id The post id. + * @param string $url The URL. + * + * @return array The filtered context. + */ public static function api_get_replies( $context, $post_id, $url ) { $meta = Http::get_remote_object( $url, true ); if ( is_wp_error( $meta ) || ! isset( $meta['replies']['first']['next'] ) ) { diff --git a/integration/class-jetpack.php b/integration/class-jetpack.php index d3662364a..002de2cb4 100644 --- a/integration/class-jetpack.php +++ b/integration/class-jetpack.php @@ -1,21 +1,40 @@ = '2.0' ) { @@ -47,11 +53,11 @@ public static function add_nodeinfo_data( $nodeinfo, $version ) { } /** - * Extend NodeInfo2 data + * Extend NodeInfo2 data. * - * @param array $nodeinfo NodeInfo2 data + * @param array $nodeinfo NodeInfo2 data. * - * @return array The extended array + * @return array The extended array. */ public static function add_nodeinfo2_data( $nodeinfo ) { $nodeinfo['protocols'][] = 'activitypub'; @@ -66,11 +72,11 @@ public static function add_nodeinfo2_data( $nodeinfo ) { } /** - * Extend the well-known nodeinfo data + * Extend the well-known nodeinfo data. * - * @param array $data The well-known nodeinfo data + * @param array $data The well-known nodeinfo data. * - * @return array The extended array + * @return array The extended array. */ public static function add_wellknown_nodeinfo_data( $data ) { $data['links'][] = array( diff --git a/integration/class-opengraph.php b/integration/class-opengraph.php index 2d0f8c96f..4eb1b1e00 100644 --- a/integration/class-opengraph.php +++ b/integration/class-opengraph.php @@ -1,4 +1,10 @@ get_webfinger(); return $metadata; } if ( \is_author() ) { - // Use the Author of the Archive-Page + // Use the Author of the Archive-Page. $user_id = \get_queried_object_id(); } elseif ( \is_singular() ) { - // Use the Author of the Post + // Use the Author of the Post. $user_id = \get_post_field( 'post_author', \get_queried_object_id() ); } elseif ( ! is_user_type_disabled( 'blog' ) ) { - // Use the Blog-User for any other page, if the Blog-User is not disabled + // Use the Blog-User for any other page, if the Blog-User is not disabled. $user_id = Users::BLOG_USER_ID; } else { - // Do not add any metadata otherwise + // Do not add any metadata otherwise. return $metadata; } @@ -78,7 +84,7 @@ public static function add_metadata( $metadata ) { return $metadata; } - // add WebFinger resource + // Add WebFinger resource. $metadata['fediverse:creator'] = $user->get_webfinger(); return $metadata; diff --git a/integration/class-seriously-simple-podcasting.php b/integration/class-seriously-simple-podcasting.php index 1a5ccbe8c..c678f6d38 100644 --- a/integration/class-seriously-simple-podcasting.php +++ b/integration/class-seriously-simple-podcasting.php @@ -1,4 +1,10 @@ log( sprintf( - // translators: %s is a URL + // translators: %s is a URL. __( 'New Follower: %s', 'activitypub' ), $notification->actor ), diff --git a/integration/class-webfinger.php b/integration/class-webfinger.php index f566e523a..7ae9607d7 100644 --- a/integration/class-webfinger.php +++ b/integration/class-webfinger.php @@ -1,7 +1,12 @@ ID ); @@ -56,12 +61,12 @@ public static function add_user_discovery( $jrd, $uri, $user ) { } /** - * Add WebFinger discovery links + * Add WebFinger discovery links. * - * @param array $jrd the jrd array - * @param string $uri the WebFinger resource + * @param array $jrd The jrd array. + * @param string $uri The WebFinger resource. * - * @return array the jrd array + * @return array|\WP_Error The jrd array or WP_Error. */ public static function add_pseudo_user_discovery( $jrd, $uri ) { $user = User_Collection::get_by_resource( $uri ); diff --git a/integration/load.php b/integration/load.php index ed254bc30..0bdef2880 100644 --- a/integration/load.php +++ b/integration/load.php @@ -1,10 +1,15 @@ - - - + diff --git a/templates/admin-header.php b/templates/admin-header.php index 2c3105729..301f7348a 100644 --- a/templates/admin-header.php +++ b/templates/admin-header.php @@ -1,5 +1,10 @@
From a53486677ef8ea0df41b00e9956bb52bf67d964f Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 8 Oct 2024 17:36:32 +0200 Subject: [PATCH 20/47] update .editorconfig --- .editorconfig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.editorconfig b/.editorconfig index ceb407da4..a541e47e7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,11 +12,10 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true indent_style = tab -indent_size = 4 -[{.jshintrc,*.json,*.yml}] +[*.yml] indent_style = space indent_size = 2 -[{*.txt,wp-config-sample.php}] -end_of_line = lf +[*.md] +trim_trailing_whitespace = false From f8cd515b05b07ce115d1485016a3615ea5acfa2c Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 8 Oct 2024 17:50:46 +0200 Subject: [PATCH 21/47] update links --- FEDERATION.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FEDERATION.md b/FEDERATION.md index b1dabe415..eb6a047c9 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -5,8 +5,8 @@ The WordPress plugin largely follows ActivityPub's server-to-server specificatio ## Supported federation protocols and standards - [ActivityPub](https://www.w3.org/TR/activitypub/) (Server-to-Server) -- [WebFinger](https://webfinger.net/) -- [HTTP Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures) +- [WebFinger](https://swicg.github.io/activitypub-http-signature/) +- [HTTP Signatures](https://www.w3.org/community/reports/socialcg/CG-FINAL-apwf-20240608/) - [NodeInfo](https://nodeinfo.diaspora.software/) ## Supported FEPs From ef666772ec489f6828e451109499e13d47d2432c Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Wed, 9 Oct 2024 01:57:13 -0500 Subject: [PATCH 22/47] PHPCS: Include all files by default (#930) * PHPCS: Include all files by default * simplify menu highlighting * Update includes/transformer/class-factory.php Co-authored-by: Matthias Pfefferle --------- Co-authored-by: Matthias Pfefferle --- includes/class-notification.php | 12 ++--- includes/class-signature.php | 6 +-- includes/collection/class-followers.php | 6 +-- includes/functions.php | 3 +- includes/handler/class-announce.php | 58 ++++++++++++++++--------- includes/handler/class-like.php | 23 ++++++---- includes/model/class-follower.php | 2 +- includes/rest/class-outbox.php | 2 +- includes/table/class-followers.php | 10 ++--- includes/transformer/class-factory.php | 23 +++++----- includes/transformer/class-post.php | 4 +- integration/load.php | 7 ++- phpcs.xml | 6 +-- templates/admin-header.php | 18 ++++++-- templates/blog-followers-list.php | 14 +++--- templates/blog-json.php | 14 ++++-- templates/blog-settings.php | 28 +++++++----- templates/comment-json.php | 16 ++++--- templates/post-json.php | 17 +++++--- templates/settings.php | 48 ++++++++++---------- templates/toolbox.php | 23 +++++++--- templates/user-followers-list.php | 5 +++ templates/user-json.php | 14 ++++-- templates/user-settings.php | 30 ++++++++----- templates/welcome.php | 25 ++++++----- 25 files changed, 250 insertions(+), 164 deletions(-) diff --git a/includes/class-notification.php b/includes/class-notification.php index bab72774c..1dd657325 100644 --- a/includes/class-notification.php +++ b/includes/class-notification.php @@ -42,15 +42,15 @@ class Notification { /** * Notification constructor. * - * @param string $type The type of the notification. - * @param string $actor The actor URL. - * @param array $object The Activity object. - * @param int $target The WordPress User-Id. + * @param string $type The type of the notification. + * @param string $actor The actor URL. + * @param array $activity The Activity object. + * @param int $target The WordPress User-Id. */ - public function __construct( $type, $actor, $object, $target ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound + public function __construct( $type, $actor, $activity, $target ) { $this->type = $type; $this->actor = $actor; - $this->object = $object; + $this->object = $activity; $this->target = $target; } diff --git a/includes/class-signature.php b/includes/class-signature.php index aa2a109bd..b8f8af887 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -221,7 +221,7 @@ public static function generate_signature( $user_id, $http_method, $url, $date, $signature = null; \openssl_sign( $signed_string, $signature, $key, \OPENSSL_ALGO_SHA256 ); - $signature = \base64_encode( $signature ); // phpcs:ignore + $signature = \base64_encode( $signature ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode $key_id = $user->get_url() . '#main-key'; @@ -345,7 +345,7 @@ public static function get_remote_key( $key_id ) { ); } if ( isset( $actor['publicKey']['publicKeyPem'] ) ) { - return \rtrim( $actor['publicKey']['publicKeyPem'] ); // phpcs:ignore + return \rtrim( $actor['publicKey']['publicKeyPem'] ); } return new WP_Error( 'activitypub_no_remote_key_found', @@ -400,7 +400,7 @@ public static function parse_signature_header( $signature ) { $parsed_header['headers'] = \explode( ' ', trim( $matches[1] ) ); } if ( \preg_match( '/signature="(.*?)"/ism', $signature, $matches ) ) { - $parsed_header['signature'] = \base64_decode( preg_replace( '/\s+/', '', trim( $matches[1] ) ) ); // phpcs:ignore + $parsed_header['signature'] = \base64_decode( preg_replace( '/\s+/', '', trim( $matches[1] ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode } if ( ( $parsed_header['signature'] ) && ( $parsed_header['algorithm'] ) && ( ! $parsed_header['headers'] ) ) { diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index dfade8c15..76611ff99 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -94,7 +94,7 @@ public static function remove_follower( $user_id, $actor ) { public static function get_follower( $user_id, $actor ) { global $wpdb; - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:ignore WordPress.DB.DirectDatabaseQuery $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = %s AND pm.meta_key = 'activitypub_user_id' AND pm.meta_value = %d AND p.guid = %s", @@ -119,12 +119,12 @@ public static function get_follower( $user_id, $actor ) { * * @param string $actor The Actor URL. * - * @return Follower|null The Follower object or null. + * @return \Activitypub\Activity\Base_Object|WP_Error|null */ public static function get_follower_by_actor( $actor ) { global $wpdb; - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:ignore WordPress.DB.DirectDatabaseQuery $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE guid=%s", diff --git a/includes/functions.php b/includes/functions.php index 27f50e96a..8aad9766f 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -1012,8 +1012,7 @@ function ( $enclosure ) { function get_comment_ancestors( $comment ) { $comment = \get_comment( $comment ); - // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual - if ( ! $comment || empty( $comment->comment_parent ) || $comment->comment_parent == $comment->comment_ID ) { + if ( ! $comment || empty( $comment->comment_parent ) || (int) $comment->comment_parent === (int) $comment->comment_ID ) { return array(); } diff --git a/includes/handler/class-announce.php b/includes/handler/class-announce.php index b0aa90fd5..377a3c6e3 100644 --- a/includes/handler/class-announce.php +++ b/includes/handler/class-announce.php @@ -33,27 +33,25 @@ public static function init() { /** * Handles "Announce" requests. * - * @param array $array The activity-object. - * @param int $user_id The id of the local blog-user. - * @param \Activitypub\Activity\Activity $activity The activity object. - * - * @return void + * @param array $announcement The activity-object. + * @param int $user_id The id of the local blog-user. + * @param \Activitypub\Activity\Activity $activity The activity object. */ - public static function handle_announce( $array, $user_id, $activity = null ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound + public static function handle_announce( $announcement, $user_id, $activity = null ) { // Check if Activity is public or not. - if ( ! is_activity_public( $array ) ) { + if ( ! is_activity_public( $announcement ) ) { // @todo maybe send email return; } if ( ! ACTIVITYPUB_DISABLE_REACTIONS ) { - self::maybe_save_announce( $array, $user_id, $activity ); + self::maybe_save_announce( $announcement, $user_id ); } - if ( is_string( $array['object'] ) ) { - $object = Http::get_remote_object( $array['object'] ); + if ( is_string( $announcement['object'] ) ) { + $object = Http::get_remote_object( $announcement['object'] ); } else { - $object = $array['object']; + $object = $announcement['object']; } if ( ! $object || is_wp_error( $object ) ) { @@ -66,21 +64,33 @@ public static function handle_announce( $array, $user_id, $activity = null ) { / $type = \strtolower( $object['type'] ); + /** + * Fires after an Announce has been received. + * + * @param array $object The object. + * @param int $user_id The id of the local blog-user. + * @param array $activity The activity object. + */ \do_action( 'activitypub_inbox', $object, $user_id, $type, $activity ); + + /** + * Fires after an Announce of a specific type has been received. + * + * @param array $object The object. + * @param int $user_id The id of the local blog-user. + * @param array $activity The activity object. + */ \do_action( "activitypub_inbox_{$type}", $object, $user_id, $activity ); } /** * Try to save the Announce. * - * @param array $array The activity-object. - * @param int $user_id The id of the local blog-user. - * @param \Activitypub\Activity\Activity $activity The activity object. - * - * @return void + * @param array $activity The activity-object. + * @param int $user_id The id of the local blog-user. */ - public static function maybe_save_announce( $array, $user_id, $activity ) { // phpcs:ignore - $url = object_to_uri( $array['object'] ); + public static function maybe_save_announce( $activity, $user_id ) { + $url = object_to_uri( $activity['object'] ); if ( empty( $url ) ) { return; @@ -91,13 +101,21 @@ public static function maybe_save_announce( $array, $user_id, $activity ) { // p return; } - $state = Interactions::add_reaction( $array ); + $state = Interactions::add_reaction( $activity ); $reaction = null; if ( $state && ! is_wp_error( $state ) ) { $reaction = get_comment( $state ); } - do_action( 'activitypub_handled_announce', $array, $user_id, $state, $reaction ); + /** + * Fires after an Announce has been saved. + * + * @param array $activity The activity-object. + * @param int $user_id The id of the local blog-user. + * @param mixed $state The state of the reaction. + * @param mixed $reaction The reaction. + */ + do_action( 'activitypub_handled_announce', $activity, $user_id, $state, $reaction ); } } diff --git a/includes/handler/class-like.php b/includes/handler/class-like.php index 94facde46..1e9515763 100644 --- a/includes/handler/class-like.php +++ b/includes/handler/class-like.php @@ -31,18 +31,15 @@ public static function init() { /** * Handles "Like" requests. * - * @param array $array The Activity array. - * @param int $user_id The ID of the local blog user. - * @param \Activitypub\Activity\Activity $activity The Activity object. - * - * @return void + * @param array $like The Activity array. + * @param int $user_id The ID of the local blog user. */ - public static function handle_like( $array, $user_id, $activity = null ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound,VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable,Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed + public static function handle_like( $like, $user_id ) { if ( ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS ) { return; } - $url = object_to_uri( $array['object'] ); + $url = object_to_uri( $like['object'] ); if ( empty( $url ) ) { return; @@ -53,13 +50,21 @@ public static function handle_like( $array, $user_id, $activity = null ) { // ph return; } - $state = Interactions::add_reaction( $array ); + $state = Interactions::add_reaction( $like ); $reaction = null; if ( $state && ! is_wp_error( $state ) ) { $reaction = get_comment( $state ); } - do_action( 'activitypub_handled_like', $array, $user_id, $state, $reaction ); + /** + * Fires after a Like has been handled. + * + * @param array $like The Activity array. + * @param int $user_id The ID of the local blog user. + * @param mixed $state The state of the reaction. + * @param mixed $reaction The reaction object. + */ + do_action( 'activitypub_handled_like', $like, $user_id, $state, $reaction ); } } diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index af30303da..45ef6157b 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -149,7 +149,7 @@ public function save() { if ( ! $this->get__id() ) { global $wpdb; - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:ignore WordPress.DB.DirectDatabaseQuery $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE guid=%s", diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index 9218874b7..461d42861 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -102,7 +102,7 @@ public static function user_outbox_get( $request ) { $json->next = \add_query_arg( 'page', $page + 1, $json->partOf ); } - if ( $page && ( $page > 1 ) ) { // phpcs:ignore + if ( $page && ( $page > 1 ) ) { $json->prev = \add_query_arg( 'page', $page - 1, $json->partOf ); } // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase diff --git a/includes/table/class-followers.php b/includes/table/class-followers.php index b1b0cec8a..991f97c1d 100644 --- a/includes/table/class-followers.php +++ b/includes/table/class-followers.php @@ -92,26 +92,22 @@ public function prepare_items() { $args = array(); - // phpcs:ignore WordPress.Security.NonceVerification.Recommended + // phpcs:disable WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['orderby'] ) ) { - // phpcs:ignore WordPress.Security.NonceVerification.Recommended $args['orderby'] = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); } - // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['order'] ) ) { - // phpcs:ignore WordPress.Security.NonceVerification.Recommended $args['order'] = sanitize_text_field( wp_unslash( $_GET['order'] ) ); } - // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['s'] ) && isset( $_REQUEST['_wpnonce'] ) ) { $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ); if ( wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) { - // phpcs:ignore WordPress.Security.NonceVerification.Recommended $args['s'] = sanitize_text_field( wp_unslash( $_GET['s'] ) ); } } + // phpcs:enable WordPress.Security.NonceVerification.Recommended $followers_with_count = FollowerCollection::get_followers_with_count( $this->user_id, $per_page, $page_num, $args ); $followers = $followers_with_count['followers']; @@ -219,7 +215,7 @@ public function process_action() { return; } - $followers = $_REQUEST['followers']; // phpcs:ignore + $followers = $_REQUEST['followers']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput if ( $this->current_action() === 'delete' ) { if ( ! is_array( $followers ) ) { diff --git a/includes/transformer/class-factory.php b/includes/transformer/class-factory.php index e8e597966..a619423af 100644 --- a/includes/transformer/class-factory.php +++ b/includes/transformer/class-factory.php @@ -10,21 +10,22 @@ use WP_Error; /** - * Transformer Factory + * Transformer Factory. */ class Factory { /** * Get the transformer for a given object. * - * @param mixed $object The object to transform. + * @param mixed $data The object to transform. + * * @return Base|WP_Error The transformer to use, or an error. */ - public static function get_transformer( $object ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound - if ( ! \is_object( $object ) ) { + public static function get_transformer( $data ) { + if ( ! \is_object( $data ) ) { return new WP_Error( 'invalid_object', __( 'Invalid object', 'activitypub' ) ); } - $class = \get_class( $object ); + $class = \get_class( $data ); /** * Filter the transformer for a given object. @@ -50,12 +51,12 @@ public static function get_transformer( $object ) { // phpcs:ignore Universal.Na * }, 10, 3 ); * * @param Base $transformer The transformer to use. - * @param mixed $object The object to transform. + * @param mixed $data The object to transform. * @param string $object_class The class of the object to transform. * * @return mixed The transformer to use. */ - $transformer = \apply_filters( 'activitypub_transformer', null, $object, $class ); + $transformer = \apply_filters( 'activitypub_transformer', null, $data, $class ); if ( $transformer ) { if ( @@ -71,12 +72,12 @@ public static function get_transformer( $object ) { // phpcs:ignore Universal.Na // Use default transformer. switch ( $class ) { case 'WP_Post': - if ( 'attachment' === $object->post_type ) { - return new Attachment( $object ); + if ( 'attachment' === $data->post_type ) { + return new Attachment( $data ); } - return new Post( $object ); + return new Post( $data ); case 'WP_Comment': - return new Comment( $object ); + return new Comment( $data ); default: return null; } diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 85a8b809e..f18693fb3 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -868,8 +868,8 @@ protected function get_post_content_template() { $template = "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]"; break; default: - // phpcs:ignore Universal.Operators.DisallowShortTernary.Found - $template = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ) ?: ACTIVITYPUB_CUSTOM_POST_CONTENT; + $content = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ); + $template = empty( $content ) ? ACTIVITYPUB_CUSTOM_POST_CONTENT : $content; break; } diff --git a/integration/load.php b/integration/load.php index 0bdef2880..860f2dc7f 100644 --- a/integration/load.php +++ b/integration/load.php @@ -79,14 +79,13 @@ function plugin_init() { if ( \defined( 'SSP_VERSION' ) ) { add_filter( 'activitypub_transformer', - // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound - function ( $transformer, $object, $object_class ) { + function ( $transformer, $data, $object_class ) { if ( 'WP_Post' === $object_class && - \get_post_meta( $object->ID, 'audio_file', true ) + \get_post_meta( $data->ID, 'audio_file', true ) ) { require_once __DIR__ . '/class-seriously-simple-podcasting.php'; - return new Seriously_Simple_Podcasting( $object ); + return new Seriously_Simple_Podcasting( $data ); } return $transformer; }, diff --git a/phpcs.xml b/phpcs.xml index 8a5449b9b..f75f4d73f 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,14 +1,12 @@ WordPress ActivityPub Standards - ./activitypub.php - ./includes/ - ./integration/ - ./build/ + . *\.(inc|css|js|svg) */vendor/* */node_modules/* + */tests/* *.asset.php diff --git a/templates/admin-header.php b/templates/admin-header.php index 301f7348a..c453328d4 100644 --- a/templates/admin-header.php +++ b/templates/admin-header.php @@ -5,6 +5,16 @@ * @package Activitypub */ +/* @var array $args Template arguments. */ +$args = wp_parse_args( + $args, + array( + 'welcome' => '', + 'settings' => '', + 'blog-profile' => '', + 'followers' => '', + ) +); ?>
@@ -12,21 +22,21 @@
@@ -68,6 +68,10 @@ in_reply_to + + post_type + +

diff --git a/templates/welcome.php b/templates/welcome.php index 7a27f1416..71169da9a 100644 --- a/templates/welcome.php +++ b/templates/welcome.php @@ -27,13 +27,13 @@

%s', - esc_url( $bookmarklet_url ), // Need to escape quotes for the bookmarklet. + esc_attr( $bookmarklet_js ), // Need to escape quotes for the bookmarklet. sprintf( $reply_from_template, \wp_parse_url( \home_url(), PHP_URL_HOST ) ) ); /* translators: %s is where the button HTML will be rendered. */ From 700180e0b82594f7849c0a9206cb7cc2d01a696a Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 15 Oct 2024 11:02:09 +0200 Subject: [PATCH 32/47] Only validate POST params and do not fall back (#934) * Only validate POST params and do not fall back Do not fall back to GET or other params if they are not available in the post! thanks @obenland * move to sever class, because it affects every endpoint --- includes/rest/class-server.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/includes/rest/class-server.php b/includes/rest/class-server.php index aac34982b..e7cf57b21 100644 --- a/includes/rest/class-server.php +++ b/includes/rest/class-server.php @@ -8,6 +8,7 @@ namespace Activitypub\Rest; use WP_Error; +use WP_REST_Server; use WP_REST_Response; use Activitypub\Signature; use Activitypub\Model\Application; @@ -28,6 +29,7 @@ public static function init() { \add_filter( 'rest_request_before_callbacks', array( self::class, 'validate_activitypub_requests' ), 9, 3 ); \add_filter( 'rest_request_before_callbacks', array( self::class, 'authorize_activitypub_requests' ), 10, 3 ); + \add_filter( 'rest_request_parameter_order', array( self::class, 'request_parameter_order' ), 10, 2 ); } /** @@ -181,4 +183,32 @@ public static function validate_activitypub_requests( $response, $handler, $requ return $response; } + + /** + * Modify the parameter priority order for a REST API request. + * + * @param string[] $order Array of types to check, in order of priority. + * @param WP_REST_Request $request The request object. + * + * @return string[] The modified order of types to check. + */ + public static function request_parameter_order( $order, $request ) { + $route = $request->get_route(); + + // Check if it is an activitypub request and exclude webfinger and nodeinfo endpoints. + if ( ! \str_starts_with( $route, '/' . ACTIVITYPUB_REST_NAMESPACE ) ) { + return $order; + } + + $type = $request->get_method(); + + if ( WP_REST_Server::CREATABLE !== $type ) { + return $order; + } + + return array( + 'POST', + 'defaults', + ); + } } From f0d72c9c57099868c65047f023fd9cad45e2a5fc Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 15 Oct 2024 11:14:51 +0200 Subject: [PATCH 33/47] install svn --- .github/workflows/phpunit.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 9d0561647..dc1f257c7 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -23,6 +23,10 @@ jobs: - wp-version: '6.5' php-versions: '7.1' steps: + - name: Install svn + run: | + sudo apt-get update + sudo apt-get install subversion - name: Checkout uses: actions/checkout@v2 From 12ab7768d431d5a38d8f9d4c223824580074451d Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 15 Oct 2024 13:01:41 +0200 Subject: [PATCH 34/47] fix possible PHP Warning --- includes/collection/class-extra-fields.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/includes/collection/class-extra-fields.php b/includes/collection/class-extra-fields.php index 320ed478a..a2bd6dde3 100644 --- a/includes/collection/class-extra-fields.php +++ b/includes/collection/class-extra-fields.php @@ -123,9 +123,14 @@ function ( $rel ) { 'type' => 'Link', 'name' => \get_the_title( $post ), 'href' => \esc_url( $tags->get_attribute( 'href' ) ), - 'rel' => explode( ' ', $tags->get_attribute( 'rel' ) ), ); + $rel = $tags->get_attribute( 'rel' ); + + if ( $rel && \is_string( $rel ) ) { + $attachment['rel'] = \explode( ' ', $rel ); + } + $link_added = true; } } From 83e2b9fb56511c6cb03ebbe907275963d7c27a53 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 15 Oct 2024 22:05:48 +0200 Subject: [PATCH 35/47] add title to link headers fix #941 --- includes/class-activitypub.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index c85a5fb17..b5bb164d2 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -163,13 +163,13 @@ public static function add_headers() { $self_link = esc_url( $self_link ); if ( ! headers_sent() ) { - header( 'Link: <' . $self_link . '>; rel="alternate"; type="application/activity+json"' ); + header( 'Link: <' . $self_link . '>; title="ActivityPub (JSON)" rel="alternate"; type="application/activity+json"' ); } add_action( 'wp_head', function () use ( $self_link ) { - echo PHP_EOL . '' . PHP_EOL; + echo PHP_EOL . '' . PHP_EOL; } ); } From 4b488cf0a9bf3b68344e3822950ba93cfc3300bc Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Wed, 16 Oct 2024 02:17:43 -0500 Subject: [PATCH 36/47] Replies: fix `DivisionByZeroError` (#942) --- includes/collection/class-replies.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/collection/class-replies.php b/includes/collection/class-replies.php index 34a5aa02b..2f10c004b 100644 --- a/includes/collection/class-replies.php +++ b/includes/collection/class-replies.php @@ -156,7 +156,11 @@ public static function get_collection_page( $wp_object, $page, $part_of = null ) $total_replies = \get_comments( array_merge( $args, array( 'count' => true ) ) ); // Modify query args to retrieve paginated results. - $comments_per_page = \get_option( 'comments_per_page' ); + $comments_per_page = (int) \get_option( 'comments_per_page' ); + // If set to zero, we get errors below. You need at least one comment per page, here. + if ( ! $comments_per_page ) { + $comments_per_page = 1; + } // Fetch internal and external comments for current page. $comments = get_comments( self::add_pagination_args( $args, $page, $comments_per_page ) ); From 7ca9a581c43027b230ae5bc883e38f39617e1b1b Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 16 Oct 2024 20:44:42 +0200 Subject: [PATCH 37/47] Add "visibility" feature (#931) --- activitypub.php | 6 ++ build/editor-plugin/plugin.asset.php | 2 +- build/editor-plugin/plugin.js | 2 +- includes/class-activity-dispatcher.php | 16 ++- includes/class-activitypub.php | 2 +- includes/class-blocks.php | 29 +++++- includes/class-scheduler.php | 2 +- includes/functions.php | 119 +++++++++++++++++----- includes/transformer/class-post.php | 40 +++++--- src/editor-plugin/block.json | 2 +- src/editor-plugin/plugin.js | 37 ++++++- tests/test-class-activitypub-activity.php | 2 +- 12 files changed, 208 insertions(+), 51 deletions(-) diff --git a/activitypub.php b/activitypub.php index 53eb1475f..1645a484e 100644 --- a/activitypub.php +++ b/activitypub.php @@ -46,6 +46,12 @@ \defined( 'ACTIVITYPUB_SEND_VARY_HEADER' ) || \define( 'ACTIVITYPUB_SEND_VARY_HEADER', false ); \defined( 'ACTIVITYPUB_DEFAULT_OBJECT_TYPE' ) || \define( 'ACTIVITYPUB_DEFAULT_OBJECT_TYPE', 'note' ); +// Post visibility constants. +\define( 'ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC', '' ); +\define( 'ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC', 'quiet_public' ); +\define( 'ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL', 'local' ); + +// Plugin related constants. \define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) ); diff --git a/build/editor-plugin/plugin.asset.php b/build/editor-plugin/plugin.asset.php index 67a3f1810..c6e6699a7 100644 --- a/build/editor-plugin/plugin.asset.php +++ b/build/editor-plugin/plugin.asset.php @@ -1 +1 @@ - array('react', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-i18n', 'wp-plugins'), 'version' => '88603987940fec29730d'); + array('react', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-i18n', 'wp-plugins', 'wp-primitives'), 'version' => '92b35e5bbecef58deaf6'); diff --git a/build/editor-plugin/plugin.js b/build/editor-plugin/plugin.js index c16cd66e0..826013dbd 100644 --- a/build/editor-plugin/plugin.js +++ b/build/editor-plugin/plugin.js @@ -1 +1 @@ -(()=>{"use strict";const t=window.React,e=window.wp.editor,n=window.wp.plugins,i=window.wp.components,o=window.wp.data,a=window.wp.coreData,r=window.wp.i18n;(0,n.registerPlugin)("activitypub-editor-plugin",{render:()=>{const n=(0,o.useSelect)((t=>t("core/editor").getCurrentPostType()),[]),[w,c]=(0,a.useEntityProp)("postType",n,"meta");return(0,t.createElement)(e.PluginDocumentSettingPanel,{name:"activitypub",title:(0,r.__)("Fediverse","activitypub")},(0,t.createElement)(i.TextControl,{label:(0,r.__)("Content Warning","activitypub"),value:w?.activitypub_content_warning,onChange:t=>{c({...w,activitypub_content_warning:t})},placeholder:(0,r.__)("Optional content warning","activitypub")}))}})})(); \ No newline at end of file +(()=>{"use strict";var e={20:(e,t,i)=>{var n=i(609),o=Symbol.for("react.element"),r=(Symbol.for("react.fragment"),Object.prototype.hasOwnProperty),a=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};t.jsx=function(e,t,i){var n,c={},p=null,s=null;for(n in void 0!==i&&(p=""+i),void 0!==t.key&&(p=""+t.key),void 0!==t.ref&&(s=t.ref),t)r.call(t,n)&&!l.hasOwnProperty(n)&&(c[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps)void 0===c[n]&&(c[n]=t[n]);return{$$typeof:o,type:e,key:p,ref:s,props:c,_owner:a.current}}},848:(e,t,i)=>{e.exports=i(20)},609:e=>{e.exports=window.React}},t={};function i(n){var o=t[n];if(void 0!==o)return o.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}var n=i(609);const o=window.wp.editor,r=window.wp.plugins,a=window.wp.components,l=window.wp.element,c=(0,l.forwardRef)((function({icon:e,size:t=24,...i},n){return(0,l.cloneElement)(e,{width:t,height:t,...i,ref:n})})),p=window.wp.primitives;var s=i(848);const u=(0,s.jsx)(p.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,s.jsx)(p.Path,{d:"M12 3.3c-4.8 0-8.8 3.9-8.8 8.8 0 4.8 3.9 8.8 8.8 8.8 4.8 0 8.8-3.9 8.8-8.8s-4-8.8-8.8-8.8zm6.5 5.5h-2.6C15.4 7.3 14.8 6 14 5c2 .6 3.6 2 4.5 3.8zm.7 3.2c0 .6-.1 1.2-.2 1.8h-2.9c.1-.6.1-1.2.1-1.8s-.1-1.2-.1-1.8H19c.2.6.2 1.2.2 1.8zM12 18.7c-1-.7-1.8-1.9-2.3-3.5h4.6c-.5 1.6-1.3 2.9-2.3 3.5zm-2.6-4.9c-.1-.6-.1-1.1-.1-1.8 0-.6.1-1.2.1-1.8h5.2c.1.6.1 1.1.1 1.8s-.1 1.2-.1 1.8H9.4zM4.8 12c0-.6.1-1.2.2-1.8h2.9c-.1.6-.1 1.2-.1 1.8 0 .6.1 1.2.1 1.8H5c-.2-.6-.2-1.2-.2-1.8zM12 5.3c1 .7 1.8 1.9 2.3 3.5H9.7c.5-1.6 1.3-2.9 2.3-3.5zM10 5c-.8 1-1.4 2.3-1.8 3.8H5.5C6.4 7 8 5.6 10 5zM5.5 15.3h2.6c.4 1.5 1 2.8 1.8 3.7-1.8-.6-3.5-2-4.4-3.7zM14 19c.8-1 1.4-2.2 1.8-3.7h2.6C17.6 17 16 18.4 14 19z"})}),v=(0,s.jsx)(p.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,s.jsx)(p.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})}),w=(0,s.jsx)(p.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,s.jsx)(p.Path,{fillRule:"evenodd",clipRule:"evenodd",d:"M12 18.5A6.5 6.5 0 0 1 6.93 7.931l9.139 9.138A6.473 6.473 0 0 1 12 18.5Zm5.123-2.498a6.5 6.5 0 0 0-9.124-9.124l9.124 9.124ZM4 12a8 8 0 1 1 16 0 8 8 0 0 1-16 0Z"})}),d=window.wp.data,_=window.wp.coreData,y=window.wp.i18n;(0,r.registerPlugin)("activitypub-editor-plugin",{render:()=>{const e=(0,d.useSelect)((e=>e("core/editor").getCurrentPostType()),[]),[t,i]=(0,_.useEntityProp)("postType",e,"meta"),r={verticalAlign:"middle",gap:"4px",justifyContent:"start",display:"inline-flex",alignItems:"center"},l=(e,t)=>(0,n.createElement)(a.__experimentalText,{style:r},(0,n.createElement)(c,{icon:t}),e);return(0,n.createElement)(o.PluginDocumentSettingPanel,{name:"activitypub",title:(0,y.__)("⁂ Fediverse","activitypub")},(0,n.createElement)(a.TextControl,{label:(0,y.__)("Content Warning","activitypub"),value:t?.activitypub_content_warning,onChange:e=>{i({...t,activitypub_content_warning:e})},placeholder:(0,y.__)("Optional content warning","activitypub")}),(0,n.createElement)(a.RadioControl,{label:(0,y.__)("Visibility","activitypub"),help:(0,y.__)("This adjusts the visibility of a post in the fediverse, but note that it won't affect how the post appears on the blog.","activitypub"),selected:t.activitypub_content_visibility?t.activitypub_content_visibility:"public",options:[{label:l((0,y.__)("Public","activitypub"),u),value:"public"},{label:l((0,y.__)("Quiet public","activitypub"),v),value:"quiet_public"},{label:l((0,y.__)("Do not federate","activitypub"),w),value:"local"}],onChange:e=>{i({...t,activitypub_content_visibility:e})},className:"activitypub-visibility"}))}})})(); \ No newline at end of file diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 17281a334..c320e5528 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -269,8 +269,20 @@ public static function add_inboxes_of_follower( $inboxes, $user_id ) { */ public static function add_inboxes_by_mentioned_actors( $inboxes, $user_id, $activity ) { $cc = $activity->get_cc(); - if ( $cc ) { - $mentioned_inboxes = Mention::get_inboxes( $cc ); + $to = $activity->get_to(); + + $audience = array_merge( $cc, $to ); + + // Remove "public placeholder" and "same domain" from the audience. + $audience = array_filter( + $audience, + function ( $actor ) { + return 'https://www.w3.org/ns/activitystreams#Public' !== $actor && ! is_same_domain( $actor ); + } + ); + + if ( $audience ) { + $mentioned_inboxes = Mention::get_inboxes( $audience ); return array_merge( $inboxes, $mentioned_inboxes ); } diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index b5bb164d2..3a3d64459 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -98,7 +98,7 @@ public static function render_json_template( $template ) { $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/user-json.php'; } elseif ( is_comment() ) { $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/comment-json.php'; - } elseif ( \is_singular() ) { + } elseif ( \is_singular() && ! is_post_disabled( \get_the_ID() ) ) { $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php'; } elseif ( \is_home() && ! is_user_type_disabled( 'blog' ) ) { $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/blog-json.php'; diff --git a/includes/class-blocks.php b/includes/class-blocks.php index c42d2fc6a..4464616f5 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -42,7 +42,34 @@ public static function register_postmeta() { 'show_in_rest' => true, 'single' => true, 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field', + 'sanitize_callback' => function ( $warning ) { + if ( $warning ) { + return \sanitize_text_field( $warning ); + } + + return null; + }, + ) + ); + \register_post_meta( + $post_type, + 'activitypub_content_visibility', + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'sanitize_callback' => function ( $visibility ) { + $options = array( + ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, + ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, + ); + + if ( in_array( $visibility, $options, true ) ) { + return $visibility; + } + + return null; + }, ) ); } diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 0774f296d..1cc93cbc7 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -113,7 +113,7 @@ public static function deregister_schedules() { public static function schedule_post_activity( $new_status, $old_status, $post ) { $post = get_post( $post ); - if ( ! $post ) { + if ( ! $post || is_post_disabled( $post ) ) { return; } diff --git a/includes/functions.php b/includes/functions.php index 799af97f7..80b503af3 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -400,6 +400,31 @@ function is_activitypub_request() { return false; } +/** + * Check if a post is disabled for ActivityPub. + * + * @param mixed $post The post object or ID. + * + * @return boolean True if the post is disabled, false otherwise. + */ +function is_post_disabled( $post ) { + $post = \get_post( $post ); + $disabled = false; + $visibility = \get_post_meta( $post->ID, 'activitypub_content_visibility', true ); + + if ( ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL === $visibility ) { + $disabled = true; + } + + /* + * Allow plugins to disable posts for ActivityPub. + * + * @param boolean $disabled True if the post is disabled, false otherwise. + * @param \WP_Post $post The post object. + */ + return \apply_filters( 'activitypub_is_post_disabled', $disabled, $post ); +} + /** * This function checks if a user is disabled for ActivityPub. * @@ -408,50 +433,50 @@ function is_activitypub_request() { * @return boolean True if the user is disabled, false otherwise. */ function is_user_disabled( $user_id ) { - $return = false; + $disabled = false; switch ( $user_id ) { // if the user is the application user, it's always enabled. case \Activitypub\Collection\Users::APPLICATION_USER_ID: - $return = false; + $disabled = false; break; // if the user is the blog user, it's only enabled in single-user mode. case \Activitypub\Collection\Users::BLOG_USER_ID: if ( is_user_type_disabled( 'blog' ) ) { - $return = true; + $disabled = true; break; } - $return = false; + $disabled = false; break; // if the user is any other user, it's enabled if it can publish posts. default: if ( ! \get_user_by( 'id', $user_id ) ) { - $return = true; + $disabled = true; break; } if ( is_user_type_disabled( 'user' ) ) { - $return = true; + $disabled = true; break; } if ( ! \user_can( $user_id, 'activitypub' ) ) { - $return = true; + $disabled = true; break; } - $return = false; + $disabled = false; break; } /** * Allow plugins to disable users for ActivityPub. * - * @param boolean $return True if the user is disabled, false otherwise. - * @param int $user_id The User-ID. + * @param boolean $disabled True if the user is disabled, false otherwise. + * @param int $user_id The User-ID. */ - return apply_filters( 'activitypub_is_user_disabled', $return, $user_id ); + return apply_filters( 'activitypub_is_user_disabled', $disabled, $user_id ); } /** @@ -469,45 +494,45 @@ function is_user_type_disabled( $type ) { case 'blog': if ( \defined( 'ACTIVITYPUB_SINGLE_USER_MODE' ) ) { if ( ACTIVITYPUB_SINGLE_USER_MODE ) { - $return = false; + $disabled = false; break; } } if ( \defined( 'ACTIVITYPUB_DISABLE_BLOG_USER' ) ) { - $return = ACTIVITYPUB_DISABLE_BLOG_USER; + $disabled = ACTIVITYPUB_DISABLE_BLOG_USER; break; } if ( '1' !== \get_option( 'activitypub_enable_blog_user', '0' ) ) { - $return = true; + $disabled = true; break; } - $return = false; + $disabled = false; break; case 'user': if ( \defined( 'ACTIVITYPUB_SINGLE_USER_MODE' ) ) { if ( ACTIVITYPUB_SINGLE_USER_MODE ) { - $return = true; + $disabled = true; break; } } if ( \defined( 'ACTIVITYPUB_DISABLE_USER' ) ) { - $return = ACTIVITYPUB_DISABLE_USER; + $disabled = ACTIVITYPUB_DISABLE_USER; break; } if ( '1' !== \get_option( 'activitypub_enable_users', '1' ) ) { - $return = true; + $disabled = true; break; } - $return = false; + $disabled = false; break; default: - $return = new WP_Error( + $disabled = new WP_Error( 'activitypub_wrong_user_type', __( 'Wrong user type', 'activitypub' ), array( 'status' => 400 ) @@ -518,10 +543,10 @@ function is_user_type_disabled( $type ) { /** * Allow plugins to disable user types for ActivityPub. * - * @param boolean $return True if the user type is disabled, false otherwise. - * @param string $type The User-Type. + * @param boolean $disabled True if the user type is disabled, false otherwise. + * @param string $type The User-Type. */ - return apply_filters( 'activitypub_is_user_type_disabled', $return, $type ); + return apply_filters( 'activitypub_is_user_type_disabled', $disabled, $type ); } /** @@ -1334,3 +1359,51 @@ function get_content_warning( $post_id ) { return $warning; } + +/** + * Check if a URL is from the same domain as the site. + * + * @param string $url The URL to check. + * + * @return boolean True if the URL is from the same domain, false otherwise. + */ +function is_same_domain( $url ) { + $remote = \wp_parse_url( $url, PHP_URL_HOST ); + + if ( ! $remote ) { + return false; + } + + $remote = normalize_host( $remote ); + $self = \wp_parse_url( \home_url(), PHP_URL_HOST ); + $self = normalize_host( $self ); + + return $remote === $self; +} + +/** + * Get the visibility of a post. + * + * @param int $post_id The post ID. + * + * @return string|false The visibility of the post or false if not found. + */ +function get_content_visibility( $post_id ) { + $post = get_post( $post_id ); + if ( ! $post ) { + return false; + } + + $visibility = get_post_meta( $post->ID, 'activitypub_content_visibility', true ); + $_visibility = ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC; + $options = array( + ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, + ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, + ); + + if ( in_array( $visibility, $options, true ) ) { + $_visibility = $visibility; + } + + return \apply_filters( 'activitypub_content_visibility', $_visibility, $post ); +} diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 2067677df..5de8964d4 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -15,9 +15,10 @@ use function Activitypub\esc_hashtag; use function Activitypub\is_single_user; use function Activitypub\get_enclosures; +use function Activitypub\get_content_warning; use function Activitypub\site_supports_blocks; use function Activitypub\generate_post_summary; -use function Activitypub\get_content_warning; +use function Activitypub\get_content_visibility; /** * WordPress Post Transformer. @@ -77,6 +78,12 @@ public function to_object() { $object->set_summary_map( null ); } + // Change order if visibility is "Quiet public". + if ( ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC === get_content_visibility( $post ) ) { + $object->set_to( $this->get_cc() ); + $object->set_cc( $this->get_to() ); + } + return $object; } @@ -686,6 +693,19 @@ protected function get_type() { return $object_type; } + /** + * Returns the recipient of the post. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-to + * + * @return array The recipient URLs of the post. + */ + public function get_to() { + return array( + 'https://www.w3.org/ns/activitystreams#Public', + ); + } + /** * Returns a list of Mentions, used in the Post. * @@ -694,7 +714,9 @@ protected function get_type() { * @return array The list of Mentions. */ protected function get_cc() { - $cc = array(); + $cc = array( + $this->get_actor_object()->get_followers(), + ); $mentions = $this->get_mentions(); if ( $mentions ) { @@ -952,20 +974,6 @@ public function get_in_reply_to() { return null; } - /** - * Returns the recipient of the post. - * - * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-to - * - * @return array The recipient URLs of the post. - */ - public function get_to() { - return array( - 'https://www.w3.org/ns/activitystreams#Public', - $this->get_actor_object()->get_followers(), - ); - } - /** * Returns the published date of the post. * diff --git a/src/editor-plugin/block.json b/src/editor-plugin/block.json index a8d510c7c..22cf5cc10 100644 --- a/src/editor-plugin/block.json +++ b/src/editor-plugin/block.json @@ -6,4 +6,4 @@ "keywords": [ ], "editorScript": "file:./plugin.js" -} \ No newline at end of file +} diff --git a/src/editor-plugin/plugin.js b/src/editor-plugin/plugin.js index 7e95dcda2..21896b606 100644 --- a/src/editor-plugin/plugin.js +++ b/src/editor-plugin/plugin.js @@ -1,6 +1,7 @@ import { PluginDocumentSettingPanel } from '@wordpress/editor'; import { registerPlugin } from '@wordpress/plugins'; -import { TextControl } from '@wordpress/components'; +import { TextControl, RadioControl, __experimentalText as Text } from '@wordpress/components'; +import { Icon, notAllowed, globe, people } from '@wordpress/icons'; import { useSelect } from '@wordpress/data'; import { useEntityProp } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; @@ -13,10 +14,26 @@ const EditorPlugin = () => { ); const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' ); + const labelStyling = { + verticalAlign: "middle", + gap: "4px", + justifyContent: + "start", display: + "inline-flex", + alignItems: "center" + } + + const labelWithIcon = ( text, icon ) => ( + + + {text} + + ); + return ( { } } placeholder={ __( 'Optional content warning', 'activitypub' ) } /> + { + setMeta( { ...meta, activitypub_content_visibility: value } ); + } } + className="activitypub-visibility" + /> ); } -registerPlugin( 'activitypub-editor-plugin', { render: EditorPlugin } ); \ No newline at end of file +registerPlugin( 'activitypub-editor-plugin', { render: EditorPlugin } ); diff --git a/tests/test-class-activitypub-activity.php b/tests/test-class-activitypub-activity.php index 32a1f8304..5f5501656 100644 --- a/tests/test-class-activitypub-activity.php +++ b/tests/test-class-activitypub-activity.php @@ -25,7 +25,7 @@ function ( $mentions ) { $activitypub_activity->set_type( 'Create' ); $activitypub_activity->set_object( $activitypub_post ); - $this->assertContains( \Activitypub\get_rest_url_by_path( 'actors/1/followers' ), $activitypub_activity->get_to() ); + $this->assertContains( \Activitypub\get_rest_url_by_path( 'actors/1/followers' ), $activitypub_activity->get_cc() ); $this->assertContains( 'https://example.com/alex', $activitypub_activity->get_cc() ); remove_all_filters( 'activitypub_extract_mentions' ); From cfb6345ffdcca0a94f8818d89e2910fcaf553647 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 18 Oct 2024 08:45:25 +0200 Subject: [PATCH 38/47] check for `do_blocks` --- includes/functions.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/includes/functions.php b/includes/functions.php index 80b503af3..174701510 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -575,7 +575,10 @@ function site_supports_blocks() { return false; } - if ( ! \function_exists( 'register_block_type_from_metadata' ) ) { + if ( + ! \function_exists( 'register_block_type_from_metadata' ) || + ! \function_exists( 'do_blocks' ) + ) { return false; } From 84fc70374b10de6a977ae48059b19537208075f5 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 18 Oct 2024 12:26:03 +0200 Subject: [PATCH 39/47] prepare docs --- .distignore | 1 + _config.yml | 1 - docs/.keep | 0 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 _config.yml create mode 100644 docs/.keep diff --git a/.distignore b/.distignore index 8715c0240..4ba7015b6 100644 --- a/.distignore +++ b/.distignore @@ -41,3 +41,4 @@ tests node_modules vendor src +docs diff --git a/_config.yml b/_config.yml deleted file mode 100644 index cc35c1df2..000000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-modernist \ No newline at end of file diff --git a/docs/.keep b/docs/.keep new file mode 100644 index 000000000..e69de29bb From c1a8aebe25263bf82fad8e695fd54bf02ddb5c11 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 18 Oct 2024 13:14:53 +0200 Subject: [PATCH 40/47] fix link header --- includes/class-activitypub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 3a3d64459..7bb1f1b85 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -163,7 +163,7 @@ public static function add_headers() { $self_link = esc_url( $self_link ); if ( ! headers_sent() ) { - header( 'Link: <' . $self_link . '>; title="ActivityPub (JSON)" rel="alternate"; type="application/activity+json"' ); + header( 'Link: <' . $self_link . '>; title="ActivityPub (JSON)"; rel="alternate"; type="application/activity+json"' ); } add_action( From 0cc0ca7e1ad9a7b2575afb24e71503d7ac4083f7 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 21 Oct 2024 16:20:40 +0200 Subject: [PATCH 41/47] fix syntax --- src/editor-plugin/plugin.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/editor-plugin/plugin.js b/src/editor-plugin/plugin.js index 21896b606..aaa2e34b1 100644 --- a/src/editor-plugin/plugin.js +++ b/src/editor-plugin/plugin.js @@ -17,9 +17,8 @@ const EditorPlugin = () => { const labelStyling = { verticalAlign: "middle", gap: "4px", - justifyContent: - "start", display: - "inline-flex", + justifyContent: "start", + display: "inline-flex", alignItems: "center" } From 280e640d55b61bd88b3d0b35810885760b2b65da Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 21 Oct 2024 21:47:11 +0200 Subject: [PATCH 42/47] add Attribution-Domains support (#946) --- includes/activity/class-actor.php | 14 ++++++++++++++ includes/class-admin.php | 17 ++++++++++++++++ includes/functions.php | 32 +++++++++++++++++++++++++++++-- includes/model/class-blog.php | 10 ++++++++++ includes/model/class-user.php | 10 ++++++++++ templates/settings.php | 12 +++++++++++- 6 files changed, 92 insertions(+), 3 deletions(-) diff --git a/includes/activity/class-actor.php b/includes/activity/class-actor.php index ddbef8eaa..654ff7f4e 100644 --- a/includes/activity/class-actor.php +++ b/includes/activity/class-actor.php @@ -43,6 +43,10 @@ class Actor extends Base_Object { '@id' => 'lemmy:moderators', '@type' => '@id', ), + 'attributionDomains' => array( + '@id' => 'toot:attributionDomains', + '@type' => '@id', + ), 'postingRestrictedToMods' => 'lemmy:postingRestrictedToMods', 'discoverable' => 'toot:discoverable', 'indexable' => 'toot:indexable', @@ -184,4 +188,14 @@ class Actor extends Base_Object { * @var boolean */ protected $sensitive = null; + + /** + * Domains allowed to use `fediverse:creator` for this actor in + * published articles. + * + * @see https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/ + * + * @var array + */ + protected $attribution_domains = null; } diff --git a/includes/class-admin.php b/includes/class-admin.php index 2d1d86cd9..5745b3cab 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -285,6 +285,23 @@ public static function register_settings() { 'default' => '0', ) ); + \register_setting( + 'activitypub', + 'activitypub_attribution_domains', + array( + 'type' => 'string', + 'description' => \__( 'Websites allowed to credit you.', 'activitypub' ), + 'default' => home_host(), + 'sanitize_callback' => function ( $value ) { + $value = explode( PHP_EOL, $value ); + $value = array_filter( array_map( 'trim', $value ) ); + $value = array_filter( array_map( 'esc_attr', $value ) ); + $value = implode( PHP_EOL, $value ); + + return $value; + }, + ) + ); // Blog-User Settings. \register_setting( diff --git a/includes/functions.php b/includes/functions.php index 174701510..7ff5caabb 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -1378,8 +1378,7 @@ function is_same_domain( $url ) { } $remote = normalize_host( $remote ); - $self = \wp_parse_url( \home_url(), PHP_URL_HOST ); - $self = normalize_host( $self ); + $self = normalize_host( home_host() ); return $remote === $self; } @@ -1410,3 +1409,32 @@ function get_content_visibility( $post_id ) { return \apply_filters( 'activitypub_content_visibility', $_visibility, $post ); } + +/** + * Retrieves the Host for the current site where the front end is accessible. + * + * @return string The host for the current site. + */ +function home_host() { + return \wp_parse_url( \home_url(), PHP_URL_HOST ); +} + +/** + * Returns the website hosts allowed to credit this blog. + * + * @return array|null The attribution domains or null if not found. + */ +function get_attribution_domains() { + if ( '1' !== \get_option( 'activitypub_use_opengraph', '1' ) ) { + return null; + } + + $domains = \get_option( 'activitypub_attribution_domains', home_host() ); + $domains = explode( PHP_EOL, $domains ); + + if ( ! $domains ) { + $domains = null; + } + + return $domains; +} diff --git a/includes/model/class-blog.php b/includes/model/class-blog.php index 3cbd6f91e..40019b9ee 100644 --- a/includes/model/class-blog.php +++ b/includes/model/class-blog.php @@ -18,6 +18,7 @@ use function Activitypub\is_single_user; use function Activitypub\is_blog_public; use function Activitypub\get_rest_url_by_path; +use function Activitypub\get_attribution_domains; /** * Blog class. @@ -530,4 +531,13 @@ public function get_attachment() { $extra_fields = Extra_Fields::get_actor_fields( $this->_id ); return Extra_Fields::fields_to_attachments( $extra_fields ); } + + /** + * Returns the website hosts allowed to credit this blog. + * + * @return array|null The attribution domains or null if not found. + */ + public function get_attribution_domains() { + return get_attribution_domains(); + } } diff --git a/includes/model/class-user.php b/includes/model/class-user.php index 997c52104..96cbb6338 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -15,6 +15,7 @@ use function Activitypub\is_blog_public; use function Activitypub\is_user_disabled; use function Activitypub\get_rest_url_by_path; +use function Activitypub\get_attribution_domains; /** * User class. @@ -403,4 +404,13 @@ public function update_header( $value ) { } return \update_user_option( $this->_id, 'activitypub_header_image', $value ); } + + /** + * Returns the website hosts allowed to credit this blog. + * + * @return array|null The attribution domains or null if not found. + */ + public function get_attribution_domains() { + return get_attribution_domains(); + } } diff --git a/templates/settings.php b/templates/settings.php index f73b785c7..8716b33c8 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -231,7 +231,17 @@

- + +

+

+ + +

From 418b8dcd518502fbaf037d0d9420b1fcffb84839 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 21 Oct 2024 22:05:29 +0200 Subject: [PATCH 43/47] Simplify Actor-Settings (#918) * Simplify Actor-Settings The new version uses a radio button instead of checkboxes. This way it is better to explain the different modes. * revert unnecessary changes * remove old settings to not replace them by accident * Update templates/settings.php Co-authored-by: Konstantin Obenland * Update templates/settings.php Co-authored-by: Alex Kirk * Update templates/settings.php Co-authored-by: Alex Kirk * Update templates/settings.php Co-authored-by: Alex Kirk * Update includes/class-admin.php Co-authored-by: Konstantin Obenland * use constants * Update templates/settings.php Co-authored-by: Alex Kirk * Update templates/settings.php Co-authored-by: Alex Kirk * add comment * add output escaping * fix escaping * Update templates/settings.php Co-authored-by: Matt Wiebe * Update templates/settings.php Co-authored-by: Matt Wiebe * Update templates/settings.php Co-authored-by: Matt Wiebe * call migration! * add unit tests * use descriptive consts --------- Co-authored-by: Konstantin Obenland Co-authored-by: Alex Kirk Co-authored-by: Matt Wiebe --- activitypub.php | 7 ++- includes/class-admin.php | 16 ++----- includes/class-migration.php | 59 ++++++++++++++++++++---- includes/functions.php | 4 +- templates/settings.php | 19 ++++++-- tests/test-class-activitypub-migrate.php | 51 ++++++++++++++++++++ 6 files changed, 128 insertions(+), 28 deletions(-) create mode 100644 tests/test-class-activitypub-migrate.php diff --git a/activitypub.php b/activitypub.php index 1645a484e..0e620ca41 100644 --- a/activitypub.php +++ b/activitypub.php @@ -22,7 +22,7 @@ require_once __DIR__ . '/includes/compat.php'; require_once __DIR__ . '/includes/functions.php'; -\define( 'ACTIVITYPUB_PLUGIN_VERSION', '3.3.3' ); +\define( 'ACTIVITYPUB_PLUGIN_VERSION', '4.0.0' ); /** * Initialize the plugin constants. @@ -46,6 +46,11 @@ \defined( 'ACTIVITYPUB_SEND_VARY_HEADER' ) || \define( 'ACTIVITYPUB_SEND_VARY_HEADER', false ); \defined( 'ACTIVITYPUB_DEFAULT_OBJECT_TYPE' ) || \define( 'ACTIVITYPUB_DEFAULT_OBJECT_TYPE', 'note' ); +// Define Actor-Modes for the plugin. +\define( 'ACTIVITYPUB_ACTOR_MODE', 'actor' ); +\define( 'ACTIVITYPUB_BLOG_MODE', 'blog' ); +\define( 'ACTIVITYPUB_ACTOR_AND_BLOG_MODE', 'actor_blog' ); + // Post visibility constants. \define( 'ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC', '' ); \define( 'ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC', 'quiet_public' ); diff --git a/includes/class-admin.php b/includes/class-admin.php index 5745b3cab..6fec55eb0 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -269,22 +269,14 @@ public static function register_settings() { ); \register_setting( 'activitypub', - 'activitypub_enable_users', + 'activitypub_actor_mode', array( - 'type' => 'boolean', - 'description' => \__( 'Every Author on this Blog (with the publish_posts capability) gets his own ActivityPub enabled Profile.', 'activitypub' ), + 'type' => 'integer', + 'description' => \__( 'Choose your preferred Actor-Mode.', 'activitypub' ), 'default' => '1', ) ); - \register_setting( - 'activitypub', - 'activitypub_enable_blog_user', - array( - 'type' => 'boolean', - 'description' => \__( 'Your Blog becomes an ActivityPub compatible Profile.', 'activitypub' ), - 'default' => '0', - ) - ); + \register_setting( 'activitypub', 'activitypub_attribution_domains', diff --git a/includes/class-migration.php b/includes/class-migration.php index ca4353fdf..8e1e78c58 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -87,7 +87,7 @@ public static function is_locked() { * @return bool True if the database structure is up to date, false otherwise. */ public static function is_latest_version() { - return (bool) version_compare( + return (bool) \version_compare( self::get_version(), self::get_target_version(), '==' @@ -120,23 +120,34 @@ public static function maybe_migrate() { if ( ! \wp_next_scheduled( 'activitypub_migrate', $version_from_db ) ) { \wp_schedule_single_event( \time(), 'activitypub_migrate', array( $version_from_db ) ); } - if ( version_compare( $version_from_db, '0.17.0', '<' ) ) { + if ( \version_compare( $version_from_db, '0.17.0', '<' ) ) { self::migrate_from_0_16(); } - if ( version_compare( $version_from_db, '1.3.0', '<' ) ) { + if ( \version_compare( $version_from_db, '1.3.0', '<' ) ) { self::migrate_from_1_2_0(); } - if ( version_compare( $version_from_db, '2.1.0', '<' ) ) { + if ( \version_compare( $version_from_db, '2.1.0', '<' ) ) { self::migrate_from_2_0_0(); } - if ( version_compare( $version_from_db, '2.3.0', '<' ) ) { + if ( \version_compare( $version_from_db, '2.3.0', '<' ) ) { self::migrate_from_2_2_0(); } - if ( version_compare( $version_from_db, '3.0.0', '<' ) ) { + if ( \version_compare( $version_from_db, '3.0.0', '<' ) ) { self::migrate_from_2_6_0(); } + if ( \version_compare( $version_from_db, '4.0.0', '<' ) ) { + self::migrate_to_4_0_0(); + } + + /** + * Fires when the system has to be migrated. + * + * @param string $version_from_db The version from which to migrate. + * @param string $target_version The target version to migrate to. + */ + \do_action( 'activitypub_migrate', $version_from_db, self::get_target_version() ); - update_option( 'activitypub_db_version', self::get_target_version() ); + \update_option( 'activitypub_db_version', self::get_target_version() ); self::unlock(); } @@ -147,7 +158,7 @@ public static function maybe_migrate() { * @param string $version_from_db The version from which to migrate. */ public static function async_migration( $version_from_db ) { - if ( version_compare( $version_from_db, '1.0.0', '<' ) ) { + if ( \version_compare( $version_from_db, '1.0.0', '<' ) ) { self::migrate_from_0_17(); } } @@ -262,6 +273,13 @@ private static function migrate_from_2_6_0() { self::update_options_key( 'activitypub_blog_user_identifier', 'activitypub_blog_identifier' ); } + /** + * Update actor-mode settings. + */ + private static function migrate_to_4_0_0() { + self::migrate_actor_mode(); + } + /** * Set the defaults needed for the plugin to work. * @@ -323,4 +341,29 @@ private static function update_options_key( $old_key, $new_key ) { array( '%s' ) ); } + + /** + * Migrate the actor mode settings. + */ + public static function migrate_actor_mode() { + $blog_profile = \get_option( 'activitypub_enable_blog_user', '0' ); + $author_profiles = \get_option( 'activitypub_enable_users', '0' ); + + if ( + '1' === $blog_profile && + '1' === $author_profiles + ) { + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + } elseif ( + '1' === $blog_profile && + '1' !== $author_profiles + ) { + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + } elseif ( + '1' !== $blog_profile && + '1' === $author_profiles + ) { + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + } + } } diff --git a/includes/functions.php b/includes/functions.php index 7ff5caabb..095abb041 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -504,7 +504,7 @@ function is_user_type_disabled( $type ) { break; } - if ( '1' !== \get_option( 'activitypub_enable_blog_user', '0' ) ) { + if ( ACTIVITYPUB_ACTOR_MODE === \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) { $disabled = true; break; } @@ -524,7 +524,7 @@ function is_user_type_disabled( $type ) { break; } - if ( '1' !== \get_option( 'activitypub_enable_users', '1' ) ) { + if ( ACTIVITYPUB_BLOG_MODE === \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) { $disabled = true; break; } diff --git a/templates/settings.php b/templates/settings.php index 8716b33c8..2df41f288 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -29,8 +29,8 @@

@@ -41,12 +41,21 @@

- + +

+

+ +

+

+

diff --git a/tests/test-class-activitypub-migrate.php b/tests/test-class-activitypub-migrate.php new file mode 100644 index 000000000..01ead3270 --- /dev/null +++ b/tests/test-class-activitypub-migrate.php @@ -0,0 +1,51 @@ +assertEquals( ACTIVITYPUB_ACTOR_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + + \update_option( 'activitypub_enable_blog_user', '0' ); + \update_option( 'activitypub_enable_users', '1' ); + \delete_option( 'activitypub_actor_mode' ); + + \Activitypub\Migration::migrate_actor_mode(); + + $this->assertEquals( ACTIVITYPUB_ACTOR_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + + \update_option( 'activitypub_enable_blog_user', '1' ); + \update_option( 'activitypub_enable_users', '1' ); + \delete_option( 'activitypub_actor_mode' ); + + \Activitypub\Migration::migrate_actor_mode(); + + $this->assertEquals( ACTIVITYPUB_ACTOR_AND_BLOG_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + + \update_option( 'activitypub_enable_blog_user', '1' ); + \update_option( 'activitypub_enable_users', '0' ); + \delete_option( 'activitypub_actor_mode' ); + + \Activitypub\Migration::migrate_actor_mode(); + + $this->assertEquals( ACTIVITYPUB_BLOG_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + + \delete_option( 'activitypub_enable_blog_user' ); + \update_option( 'activitypub_enable_users', '0' ); + \delete_option( 'activitypub_actor_mode' ); + + \Activitypub\Migration::migrate_actor_mode(); + + $this->assertEquals( ACTIVITYPUB_ACTOR_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + + \update_option( 'activitypub_enable_blog_user', '0' ); + \delete_option( 'activitypub_enable_users' ); + \delete_option( 'activitypub_actor_mode' ); + + \Activitypub\Migration::migrate_actor_mode(); + + $this->assertEquals( ACTIVITYPUB_ACTOR_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + } +} From 4a12d746d38c0becc03cabe6ca210caa3f2a55f4 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 22 Oct 2024 07:26:07 +0200 Subject: [PATCH 44/47] Prepare `?p=` and `?author=` for ActivityPub (#894) * Prepare `?p=` and `?author=` for ActivityPub This way we could replace the permalink with the ID of a post or an author, to work agains the Username and Permalink issue. * version bump * rename var * only use new ID for newer posts posts that where posted after the upgrade * some renamings * replaced but I think we can get of `pretty` @menrath * fix issue and check if post was already federated * prepare `?author=` URLs for new installations * oops * fix unit tests * if a blog has no followers yet, we can switch to the new IDs * webfinger should reference the ID instead of the URL * add phpdoc * fix comment * in a discussion with @akirk I thought it might be better to have the descition on a per user base * use const instead of hardcoding it * use id more consistently * fix typo * removed unneeded `use` * Fix PHPCS * update version * fix issue with post status * use the new IDs in the HTTP/HTML header * update code --------- Co-authored-by: Matt Wiebe --- includes/class-activity-dispatcher.php | 4 +- includes/class-activitypub.php | 69 ++++++++++++++----- includes/class-migration.php | 42 ++++++++++- includes/class-signature.php | 2 +- includes/functions.php | 36 ++++++++++ includes/model/class-application.php | 2 +- includes/model/class-blog.php | 8 ++- includes/model/class-user.php | 8 ++- includes/rest/class-collection.php | 2 +- includes/rest/class-followers.php | 2 +- includes/rest/class-following.php | 2 +- includes/transformer/class-comment.php | 4 +- includes/transformer/class-post.php | 12 +++- integration/class-enable-mastodon-apps.php | 2 +- integration/class-webfinger.php | 9 ++- ...-class-activitypub-activity-dispatcher.php | 6 +- tests/test-class-activitypub-followers.php | 14 ++-- 17 files changed, 177 insertions(+), 47 deletions(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index c320e5528..0ed8c4367 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -135,8 +135,8 @@ public static function send_profile_update( $user_id ) { // Build the update. $activity = new Activity(); $activity->set_type( 'Update' ); - $activity->set_actor( $user->get_url() ); - $activity->set_object( $user->get_url() ); + $activity->set_actor( $user->get_id() ); + $activity->set_object( $user->get_id() ); $activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' ); // Send the update. diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 7bb1f1b85..945610c7f 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -23,6 +23,7 @@ class Activitypub { public static function init() { \add_filter( 'template_include', array( self::class, 'render_json_template' ), 99 ); \add_action( 'template_redirect', array( self::class, 'template_redirect' ) ); + \add_filter( 'redirect_canonical', array( self::class, 'redirect_canonical' ), 10, 2 ); \add_filter( 'query_vars', array( self::class, 'add_query_vars' ) ); \add_filter( 'pre_get_avatar_data', array( self::class, 'pre_get_avatar_data' ), 11, 2 ); @@ -138,42 +139,72 @@ public static function add_headers() { return; } + $id = false; + // Only add self link to author pages... if ( is_author() ) { - if ( is_user_disabled( get_queried_object_id() ) ) { - return; + if ( ! is_user_disabled( get_queried_object_id() ) ) { + $id = get_user_id( get_queried_object_id() ); } } elseif ( is_singular() ) { // or posts/pages/custom-post-types... - if ( ! \post_type_supports( \get_post_type(), 'activitypub' ) ) { - return; + if ( \post_type_supports( \get_post_type(), 'activitypub' ) ) { + $id = get_post_id( get_queried_object_id() ); } - } else { // otherwise return. - return; } - // Add self link to html and http header. - $host = wp_parse_url( home_url() ); - - /** - * Filters the self link. - * - * @param string $self_link The self link. - */ - $self_link = apply_filters( 'self_link', set_url_scheme( 'http://' . $host['host'] . wp_unslash( $request_uri ) ) ); - $self_link = esc_url( $self_link ); + if ( ! $id ) { + return; + } if ( ! headers_sent() ) { - header( 'Link: <' . $self_link . '>; title="ActivityPub (JSON)"; rel="alternate"; type="application/activity+json"' ); + header( 'Link: <' . esc_url( $id ) . '>; title="ActivityPub (JSON)"; rel="alternate"; type="application/activity+json"' ); } add_action( 'wp_head', - function () use ( $self_link ) { - echo PHP_EOL . '' . PHP_EOL; + function () use ( $id ) { + echo PHP_EOL . '' . PHP_EOL; } ); } + /** + * Add support for `p` and `author` query vars. + * + * @param string $redirect_url The URL to redirect to. + * @param string $requested_url The requested URL. + * + * @return string $redirect_url + */ + public static function redirect_canonical( $redirect_url, $requested_url ) { + if ( ! is_activitypub_request() ) { + return $redirect_url; + } + + $query = \wp_parse_url( $requested_url, PHP_URL_QUERY ); + + if ( ! $query ) { + return $redirect_url; + } + + $query_params = \wp_parse_args( $query ); + unset( $query_params['activitypub'] ); + + if ( 1 !== count( $query_params ) ) { + return $redirect_url; + } + + if ( isset( $query_params['p'] ) ) { + return null; + } + + if ( isset( $query_params['author'] ) ) { + return null; + } + + return $requested_url; + } + /** * Custom redirects for ActivityPub requests. * diff --git a/includes/class-migration.php b/includes/class-migration.php index 8e1e78c58..baec05438 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -7,6 +7,7 @@ namespace Activitypub; +use Activitypub\Collection\Users; use Activitypub\Collection\Followers; /** @@ -274,9 +275,48 @@ private static function migrate_from_2_6_0() { } /** - * Update actor-mode settings. + * * Update actor-mode settings. + * * Get the ID of the latest blog post and save it to the options table. */ private static function migrate_to_4_0_0() { + $latest_post_id = 0; + + // Get the ID of the latest blog post and save it to the options table. + $latest_post = get_posts( + array( + 'numberposts' => 1, + 'orderby' => 'date', + 'order' => 'DESC', + 'post_status' => 'publish', + ) + ); + + if ( $latest_post ) { + $latest_post_id = $latest_post[0]->ID; + } + + update_option( 'activitypub_last_post_with_permalink_as_id', $latest_post_id ); + + $users = \get_users( + array( + 'capability__in' => array( 'activitypub' ), + ) + ); + + foreach ( $users as $user ) { + $followers = Followers::get_followers( $user->ID ); + + if ( $followers ) { + \update_user_option( $user->ID, 'activitypub_use_permalink_as_id', '1' ); + } + } + + $followers = Followers::get_followers( Users::BLOG_USER_ID ); + + if ( $followers ) { + \update_option( 'activitypub_use_permalink_as_id_for_blog', '1' ); + } + self::migrate_actor_mode(); } diff --git a/includes/class-signature.php b/includes/class-signature.php index b8f8af887..5d7ecd626 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -223,7 +223,7 @@ public static function generate_signature( $user_id, $http_method, $url, $date, \openssl_sign( $signed_string, $signature, $key, \OPENSSL_ALGO_SHA256 ); $signature = \base64_encode( $signature ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode - $key_id = $user->get_url() . '#main-key'; + $key_id = $user->get_id() . '#main-key'; if ( ! empty( $digest ) ) { return \sprintf( 'keyId="%s",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="%s"', $key_id, $signature ); diff --git a/includes/functions.php b/includes/functions.php index 095abb041..b283942ac 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -11,6 +11,7 @@ use Activitypub\Activity\Activity; use Activitypub\Collection\Followers; use Activitypub\Collection\Users; +use Activitypub\Transformer\Post; /** * Returns the ActivityPub default JSON-context. @@ -1363,6 +1364,41 @@ function get_content_warning( $post_id ) { return $warning; } +/** + * Get the ActivityPub ID of a User by the WordPress User ID. + * + * @param int $id The WordPress User ID. + * + * @return string The ActivityPub ID (a URL) of the User. + */ +function get_user_id( $id ) { + $user = Users::get_by_id( $id ); + + if ( ! $user ) { + return false; + } + + return $user->get_id(); +} + +/** + * Get the ActivityPub ID of a Post by the WordPress Post ID. + * + * @param int $id The WordPress Post ID. + * + * @return string The ActivityPub ID (a URL) of the Post. + */ +function get_post_id( $id ) { + $post = get_post( $id ); + + if ( ! $post ) { + return false; + } + + $transformer = new Post( $post ); + return $transformer->get_id(); +} + /** * Check if a URL is from the same domain as the site. * diff --git a/includes/model/class-application.php b/includes/model/class-application.php index b7d38af24..80e7de23e 100644 --- a/includes/model/class-application.php +++ b/includes/model/class-application.php @@ -94,7 +94,7 @@ public function get_url() { * @return string The User-URL with @-Prefix for the username. */ public function get_alternate_url() { - return $this->get_url(); + return $this->get_id(); } /** diff --git a/includes/model/class-blog.php b/includes/model/class-blog.php index 40019b9ee..21d7b0a6b 100644 --- a/includes/model/class-blog.php +++ b/includes/model/class-blog.php @@ -114,7 +114,13 @@ public function get_discoverable() { * @return string The User ID. */ public function get_id() { - return $this->get_url(); + $permalink = \get_option( 'activitypub_use_permalink_as_id_for_blog', false ); + + if ( $permalink ) { + return $this->get_url(); + } + + return \add_query_arg( 'author', $this->_id, \trailingslashit( \home_url() ) ); } /** diff --git a/includes/model/class-user.php b/includes/model/class-user.php index 96cbb6338..a5c938b47 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -106,7 +106,13 @@ public static function from_wp_user( $user_id ) { * @return string The user ID. */ public function get_id() { - return $this->get_url(); + $permalink = \get_user_option( 'activitypub_use_permalink_as_id', $this->_id ); + + if ( '1' === $permalink ) { + return $this->get_url(); + } + + return \add_query_arg( 'author', $this->_id, \trailingslashit( \home_url() ) ); } /** diff --git a/includes/rest/class-collection.php b/includes/rest/class-collection.php index 97f17676e..06f2203b8 100644 --- a/includes/rest/class-collection.php +++ b/includes/rest/class-collection.php @@ -275,7 +275,7 @@ public static function moderators_get() { $users = User_Collection::get_collection(); foreach ( $users as $user ) { - $response['orderedItems'][] = $user->get_url(); + $response['orderedItems'][] = $user->get_id(); } $rest_response = new WP_REST_Response( $response, 200 ); diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index fe97548ee..1b38187f3 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -102,7 +102,7 @@ function ( $item ) use ( $context ) { if ( 'full' === $context ) { return $item->to_array( false ); } - return $item->get_url(); + return $item->get_id(); }, $data['followers'] ); diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index bf546e96d..cda058962 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -136,7 +136,7 @@ public static function default_following( $follow_list, $user ) { $users = User_Collection::get_collection(); foreach ( $users as $user ) { - $follow_list[] = $user->get_url(); + $follow_list[] = $user->get_id(); } return $follow_list; diff --git a/includes/transformer/class-comment.php b/includes/transformer/class-comment.php index 7f0d3f685..71fbb2dd4 100644 --- a/includes/transformer/class-comment.php +++ b/includes/transformer/class-comment.php @@ -94,10 +94,10 @@ public function to_object() { protected function get_attributed_to() { if ( is_single_user() ) { $user = new Blog(); - return $user->get_url(); + return $user->get_id(); } - return Users::get_by_id( $this->wp_object->user_id )->get_url(); + return Users::get_by_id( $this->wp_object->user_id )->get_id(); } /** diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 5de8964d4..a1cff1ea0 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -121,7 +121,15 @@ protected function get_actor_object() { * * @return string The Posts ID. */ - protected function get_id() { + public function get_id() { + $last_legacy_id = (int) \get_option( 'activitypub_last_post_with_permalink_as_id', 0 ); + $post_id = $this->wp_object->ID; + + if ( $post_id > $last_legacy_id ) { + // Generate URI based on post ID. + return \add_query_arg( 'p', $post_id, \trailingslashit( \home_url() ) ); + } + return $this->get_url(); } @@ -161,7 +169,7 @@ public function get_url() { * @return string The User-URL. */ protected function get_attributed_to() { - return $this->get_actor_object()->get_url(); + return $this->get_actor_object()->get_id(); } /** diff --git a/integration/class-enable-mastodon-apps.php b/integration/class-enable-mastodon-apps.php index 04c24ff7c..e21ab04c0 100644 --- a/integration/class-enable-mastodon-apps.php +++ b/integration/class-enable-mastodon-apps.php @@ -190,7 +190,7 @@ function ( $item ) { if ( $acct && ! is_wp_error( $acct ) ) { $acct = \str_replace( 'acct:', '', $acct ); } else { - $acct = $item->get_url(); + $acct = $item->get_id(); } $account = new Account(); diff --git a/integration/class-webfinger.php b/integration/class-webfinger.php index 7ae9607d7..bc351501b 100644 --- a/integration/class-webfinger.php +++ b/integration/class-webfinger.php @@ -43,13 +43,15 @@ public static function add_user_discovery( $jrd, $uri, $user ) { $jrd['subject'] = sprintf( 'acct:%s', $user->get_webfinger() ); + $jrd['aliases'][] = $user->get_id(); $jrd['aliases'][] = $user->get_url(); $jrd['aliases'][] = $user->get_alternate_url(); + $jrd['aliases'] = array_unique( $jrd['aliases'] ); $jrd['links'][] = array( 'rel' => 'self', 'type' => 'application/activity+json', - 'href' => $user->get_url(), + 'href' => $user->get_id(), ); $jrd['links'][] = array( @@ -76,6 +78,7 @@ public static function add_pseudo_user_discovery( $jrd, $uri ) { } $aliases = array( + $user->get_id(), $user->get_url(), $user->get_alternate_url(), ); @@ -89,12 +92,12 @@ public static function add_pseudo_user_discovery( $jrd, $uri ) { array( 'rel' => 'self', 'type' => 'application/activity+json', - 'href' => $user->get_url(), + 'href' => $user->get_id(), ), array( 'rel' => 'http://webfinger.net/rel/profile-page', 'type' => 'text/html', - 'href' => $user->get_url(), + 'href' => $user->get_id(), ), array( 'rel' => 'http://ostatus.org/schema/1.0/subscribe', diff --git a/tests/test-class-activitypub-activity-dispatcher.php b/tests/test-class-activitypub-activity-dispatcher.php index c1cb3453f..834ba6436 100644 --- a/tests/test-class-activitypub-activity-dispatcher.php +++ b/tests/test-class-activitypub-activity-dispatcher.php @@ -124,7 +124,7 @@ public function test_dispatch_announce() { $json = json_decode( $first_call_args[1]['body'] ); $this->assertEquals( 'Announce', $json->type ); - $this->assertEquals( $user->get_url(), $json->actor ); + $this->assertEquals( $user->get_id(), $json->actor ); remove_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10 ); } @@ -172,8 +172,8 @@ function ( $value, $type ) { $json = json_decode( $first_call_args[1]['body'] ); $this->assertEquals( 'Create', $json->type ); - $this->assertEquals( $user->get_url(), $json->actor ); - $this->assertEquals( $user->get_url(), $json->object->attributedTo ); + $this->assertEquals( $user->get_id(), $json->actor ); + $this->assertEquals( $user->get_id(), $json->object->attributedTo ); remove_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10 ); } diff --git a/tests/test-class-activitypub-followers.php b/tests/test-class-activitypub-followers.php index a2b65f215..e930e931e 100644 --- a/tests/test-class-activitypub-followers.php +++ b/tests/test-class-activitypub-followers.php @@ -74,7 +74,7 @@ public function test_get_followers() { $db_followers = array_map( function ( $item ) { - return $item->get_url(); + return $item->get_id(); }, $db_followers ); @@ -127,7 +127,7 @@ public function test_get_follower() { } $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' ); - $this->assertEquals( 'https://example.com/author/jon', $follower->get_url() ); + $this->assertEquals( 'https://example.com/author/jon', $follower->get_id() ); $follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' ); $this->assertNull( $follower ); @@ -136,10 +136,10 @@ public function test_get_follower() { $this->assertNull( $follower ); $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' ); - $this->assertEquals( 'https://example.com/author/jon', $follower->get_url() ); + $this->assertEquals( 'https://example.com/author/jon', $follower->get_id() ); $follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://user2.example.com' ); - $this->assertEquals( 'https://user2.example.com', $follower2->get_url() ); + $this->assertEquals( 'https://user2.example.com', $follower2->get_id() ); $this->assertEquals( 'úser2', $follower2->get_name() ); } @@ -162,13 +162,13 @@ public function test_delete_follower() { } $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' ); - $this->assertEquals( 'https://example.com/author/jon', $follower->get_url() ); + $this->assertEquals( 'https://example.com/author/jon', $follower->get_id() ); $followers = \Activitypub\Collection\Followers::get_followers( 1 ); $this->assertEquals( 2, count( $followers ) ); $follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://example.com/author/jon' ); - $this->assertEquals( 'https://example.com/author/jon', $follower2->get_url() ); + $this->assertEquals( 'https://example.com/author/jon', $follower2->get_id() ); \Activitypub\Collection\Followers::remove_follower( 1, 'https://example.com/author/jon' ); @@ -176,7 +176,7 @@ public function test_delete_follower() { $this->assertNull( $follower ); $follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://example.com/author/jon' ); - $this->assertEquals( 'https://example.com/author/jon', $follower2->get_url() ); + $this->assertEquals( 'https://example.com/author/jon', $follower2->get_id() ); $followers = \Activitypub\Collection\Followers::get_followers( 1 ); $this->assertEquals( 1, count( $followers ) ); From 47f3750d714d6060f09b4cc0d397ca92c3aaad23 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 22 Oct 2024 08:24:57 +0200 Subject: [PATCH 45/47] better handling of default values --- includes/class-migration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-migration.php b/includes/class-migration.php index baec05438..5f3529565 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -387,7 +387,7 @@ private static function update_options_key( $old_key, $new_key ) { */ public static function migrate_actor_mode() { $blog_profile = \get_option( 'activitypub_enable_blog_user', '0' ); - $author_profiles = \get_option( 'activitypub_enable_users', '0' ); + $author_profiles = \get_option( 'activitypub_enable_users', '1' ); if ( '1' === $blog_profile && From 7a8379ef613553e2fcc5174fab262c0bd34654c0 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 22 Oct 2024 10:17:16 +0200 Subject: [PATCH 46/47] post_type independent --- includes/class-migration.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/class-migration.php b/includes/class-migration.php index 5f3529565..1682e87de 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -287,6 +287,7 @@ private static function migrate_to_4_0_0() { 'numberposts' => 1, 'orderby' => 'date', 'order' => 'DESC', + 'post_type' => '', 'post_status' => 'publish', ) ); From bf2912c40cd01b5804ad6ee9ba4020bdff44319d Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 22 Oct 2024 10:24:18 +0200 Subject: [PATCH 47/47] include any public post_type --- includes/class-migration.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-migration.php b/includes/class-migration.php index 1682e87de..4006f141c 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -287,7 +287,7 @@ private static function migrate_to_4_0_0() { 'numberposts' => 1, 'orderby' => 'date', 'order' => 'DESC', - 'post_type' => '', + 'post_type' => 'any', 'post_status' => 'publish', ) ); @@ -296,7 +296,7 @@ private static function migrate_to_4_0_0() { $latest_post_id = $latest_post[0]->ID; } - update_option( 'activitypub_last_post_with_permalink_as_id', $latest_post_id ); + \update_option( 'activitypub_last_post_with_permalink_as_id', $latest_post_id ); $users = \get_users( array(