diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index c47c2afc523..50078c27dd3 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -56,11 +56,10 @@ class ActivityPub::Activity::Create < ActivityPub::Activity process_audience ApplicationRecord.transaction do - @status = Status.create!(@params) + @status = Status.create!(@params.merge(quote: @quote)) attach_tags(@status) attach_mentions(@status) attach_counts(@status) - attach_quote(@status) end resolve_thread(@status) @@ -202,13 +201,6 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end end - def attach_quote(status) - return if @quote.nil? - - @quote.status = status - @quote.save - end - def process_tags return if @object['tag'].nil? diff --git a/app/models/status.rb b/app/models/status.rb index f72a92078b3..0bff4f2825d 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -102,7 +102,7 @@ class Status < ApplicationRecord has_one :quote, inverse_of: :status, dependent: :destroy validates :uri, uniqueness: true, presence: true, unless: :local? - validates :text, presence: true, unless: -> { with_media? || reblog? } + validates :text, presence: true, unless: -> { with_media? || reblog? || with_quote? } validates_with StatusLengthValidator validates_with DisallowedHashtagsValidator validates :reblog, uniqueness: { scope: :account }, if: :reblog? @@ -255,6 +255,10 @@ class Status < ApplicationRecord ordered_media_attachments.any? end + def with_quote? + quote.present? + end + def with_preview_card? preview_cards_status.present? end diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index cdd5cb3194d..80a5c6907c4 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -887,6 +887,33 @@ RSpec.describe ActivityPub::Activity::Create do end end + context 'with an unverifiable quote of a known post, with summary (CW) but no text' do + let(:quoted_status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com')) } + + let(:object_json) do + build_object( + type: 'Note', + summary: 'beware of what she said', + content: nil, + quote: ActivityPub::TagManager.instance.uri_for(quoted_status) + ) + end + + it 'creates a status with an unverified quote' do + expect { subject.perform }.to change(sender.statuses, :count).by(1) + + status = sender.statuses.first + expect(status).to_not be_nil + expect(status.spoiler_text).to eq 'beware of what she said' + expect(status.content).to eq '' + expect(status.quote).to_not be_nil + expect(status.quote).to have_attributes( + state: 'pending', + approval_uri: nil + ) + end + end + context 'with an unverifiable quote of a known post' do let(:quoted_status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com')) } diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index 9875101778f..7af2c67387f 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -600,6 +600,42 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do end end + context 'when an approved quote of a local post gets updated through an explicit update, removing text' do + let(:quoted_account) { Fabricate(:account) } + let(:quoted_status) { Fabricate(:status, account: quoted_account, quote_approval_policy: Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16) } + let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, state: :accepted) } + let(:approval_uri) { ActivityPub::TagManager.instance.approval_uri_for(quote) } + + let(:payload) do + { + '@context': [ + 'https://www.w3.org/ns/activitystreams', + { + '@id': 'https://w3id.org/fep/044f#quote', + '@type': '@id', + }, + { + '@id': 'https://w3id.org/fep/044f#quoteAuthorization', + '@type': '@id', + }, + ], + id: 'foo', + type: 'Note', + summary: 'Show more', + updated: '2021-09-08T22:39:25Z', + quote: ActivityPub::TagManager.instance.uri_for(quoted_status), + quoteAuthorization: approval_uri, + } + end + + it 'updates the quote post without changing the quote status' do + expect { subject.call(status, json, json) } + .to not_change(quote, :approval_uri) + .and not_change(quote, :state).from('accepted') + .and change(status, :text).from('Hello world').to('') + end + end + context 'when an approved quote of a local post gets updated through an explicit update' do let(:quoted_account) { Fabricate(:account) } let(:quoted_status) { Fabricate(:status, account: quoted_account, quote_approval_policy: Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16) } diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index 5494fa0cadf..c434d0cb6ef 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -305,6 +305,14 @@ RSpec.describe PostStatusService do .to enqueue_sidekiq_job(ActivityPub::QuoteRequestWorker) end + it 'allows quotes with spoilers and no text' do + account = Fabricate(:account) + quoted_status = Fabricate(:status, account: Fabricate(:account, domain: 'example.com')) + + expect { subject.call(account, spoiler_text: 'test', quoted_status: quoted_status) } + .to enqueue_sidekiq_job(ActivityPub::QuoteRequestWorker) + end + it 'correctly downgrades visibility for private self-quotes' do account = Fabricate(:account) quoted_status = Fabricate(:status, account: account, visibility: :private)