From e4bb0fc43a2ce90a32e882e08c1326ff9ff70643 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 15 Sep 2025 17:03:44 +0200 Subject: [PATCH] Add server-side support for handling posts with a quote policy allowing followers to quote (#36127) --- app/lib/activitypub/activity/create.rb | 1 + app/lib/activitypub/parser/status_parser.rb | 4 ++-- .../concerns/status/interaction_policy_concern.rb | 13 ++++++++++++- app/serializers/activitypub/note_serializer.rb | 2 +- .../activitypub/process_status_update_service.rb | 2 +- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 50078c27d..607e9be8c 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -88,6 +88,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity @status_parser = ActivityPub::Parser::StatusParser.new( @json, followers_collection: @account.followers_url, + following_collection: @account.following_url, actor_uri: ActivityPub::TagManager.instance.uri_for(@account), object: @object ) diff --git a/app/lib/activitypub/parser/status_parser.rb b/app/lib/activitypub/parser/status_parser.rb index e1f11c20f..ad4ecebbb 100644 --- a/app/lib/activitypub/parser/status_parser.rb +++ b/app/lib/activitypub/parser/status_parser.rb @@ -8,6 +8,7 @@ class ActivityPub::Parser::StatusParser # @param [Hash] json # @param [Hash] options # @option options [String] :followers_collection + # @option options [String] :following_collection # @option options [String] :actor_uri # @option options [Hash] :object def initialize(json, **options) @@ -146,8 +147,7 @@ class ActivityPub::Parser::StatusParser flags |= Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] if allowed_actors.delete('as:Public') || allowed_actors.delete('Public') || allowed_actors.delete('https://www.w3.org/ns/activitystreams#Public') flags |= Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] if allowed_actors.delete(@options[:followers_collection]) - # TODO: we don't actually store that collection URI - # flags |= Status::QUOTE_APPROVAL_POLICY_FLAGS[:followed] + flags |= Status::QUOTE_APPROVAL_POLICY_FLAGS[:following] if allowed_actors.delete(@options[:following_collection]) # Remove the special-meaning actor URI allowed_actors.delete(@options[:actor_uri]) diff --git a/app/models/concerns/status/interaction_policy_concern.rb b/app/models/concerns/status/interaction_policy_concern.rb index 28a568e90..dbac017b3 100644 --- a/app/models/concerns/status/interaction_policy_concern.rb +++ b/app/models/concerns/status/interaction_policy_concern.rb @@ -7,7 +7,7 @@ module Status::InteractionPolicyConcern unsupported_policy: (1 << 0), public: (1 << 1), followers: (1 << 2), - followed: (1 << 3), + following: (1 << 3), }.freeze included do @@ -30,6 +30,7 @@ module Status::InteractionPolicyConcern return :denied if other_account.nil? || direct_visibility? following_author = nil + followed_by_author = nil # Post author is always allowed to quote themselves return :automatic if account_id == other_account.id @@ -44,6 +45,11 @@ module Status::InteractionPolicyConcern return :automatic if following_author end + if automatic_policy.anybits?(QUOTE_APPROVAL_POLICY_FLAGS[:following]) + followed_by_author = account.following?(other_account) if followed_by_author.nil? + return :automatic if followed_by_author + end + # We don't know we are allowed by the automatic policy, considering the manual one return :manual if manual_policy.anybits?(QUOTE_APPROVAL_POLICY_FLAGS[:public]) @@ -52,6 +58,11 @@ module Status::InteractionPolicyConcern return :manual if following_author end + if manual_policy.anybits?(QUOTE_APPROVAL_POLICY_FLAGS[:following]) + followed_by_author = account.following?(other_account) if followed_by_author.nil? + return :manual if followed_by_author + end + return :unknown if (automatic_policy | manual_policy).anybits?(QUOTE_APPROVAL_POLICY_FLAGS[:unsupported_policy]) :denied diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 8592b137e..bfe26068e 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -234,7 +234,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer policy = object.quote_approval_policy >> 16 approved_uris << ActivityPub::TagManager::COLLECTIONS[:public] if policy.anybits?(Status::QUOTE_APPROVAL_POLICY_FLAGS[:public]) approved_uris << ActivityPub::TagManager.instance.followers_uri_for(object.account) if policy.anybits?(Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers]) - approved_uris << ActivityPub::TagManager.instance.following_uri_for(object.account) if policy.anybits?(Status::QUOTE_APPROVAL_POLICY_FLAGS[:followed]) + approved_uris << ActivityPub::TagManager.instance.following_uri_for(object.account) if policy.anybits?(Status::QUOTE_APPROVAL_POLICY_FLAGS[:following]) approved_uris << ActivityPub::TagManager.instance.uri_for(object.account) if approved_uris.empty? { diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 2e0e6e886..d6bbb4519 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -10,7 +10,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService @activity_json = activity_json @json = object_json - @status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: status.account.followers_url, actor_uri: ActivityPub::TagManager.instance.uri_for(status.account)) + @status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: status.account.followers_url, following_collection: status.account.following_url, actor_uri: ActivityPub::TagManager.instance.uri_for(status.account)) @uri = @status_parser.uri @status = status @account = status.account