mirror of https://github.com/mastodon/mastodon
Add experimental basic quote post authoring (#35355)
parent
81da377d8e
commit
5a88b7f683
@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Status::InteractionPolicyConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
QUOTE_APPROVAL_POLICY_FLAGS = {
|
||||
unknown: (1 << 0),
|
||||
public: (1 << 1),
|
||||
followers: (1 << 2),
|
||||
followed: (1 << 3),
|
||||
}.freeze
|
||||
|
||||
def quote_policy_as_keys(kind)
|
||||
case kind
|
||||
when :automatic
|
||||
policy = quote_approval_policy >> 16
|
||||
when :manual
|
||||
policy = quote_approval_policy & 0xFFFF
|
||||
end
|
||||
|
||||
QUOTE_APPROVAL_POLICY_FLAGS.keys.select { |key| policy.anybits?(QUOTE_APPROVAL_POLICY_FLAGS[key]) }.map(&:to_s)
|
||||
end
|
||||
|
||||
# Returns `:automatic`, `:manual`, `:unknown` or `:denied`
|
||||
def quote_policy_for_account(other_account, preloaded_relations: {})
|
||||
return :denied if other_account.nil?
|
||||
|
||||
following_author = nil
|
||||
|
||||
# Post author is always allowed to quote themselves
|
||||
return :automatic if account_id == other_account.id
|
||||
|
||||
automatic_policy = quote_approval_policy >> 16
|
||||
manual_policy = quote_approval_policy & 0xFFFF
|
||||
|
||||
# Checking for public policy first because it's less expensive than looking at mentions
|
||||
return :automatic if automatic_policy.anybits?(QUOTE_APPROVAL_POLICY_FLAGS[:public])
|
||||
|
||||
# Mentioned users are always allowed to quote
|
||||
if active_mentions.loaded?
|
||||
return :automatic if active_mentions.any? { |mention| mention.account_id == other_account.id }
|
||||
elsif active_mentions.exists?(account: other_account)
|
||||
return :automatic
|
||||
end
|
||||
|
||||
if automatic_policy.anybits?(QUOTE_APPROVAL_POLICY_FLAGS[:followers])
|
||||
following_author = preloaded_relations[:following] ? preloaded_relations[:following][account_id] : other_account.following?(account) if following_author.nil?
|
||||
return :automatic if following_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])
|
||||
|
||||
if manual_policy.anybits?(QUOTE_APPROVAL_POLICY_FLAGS[:followers])
|
||||
following_author = preloaded_relations[:following] ? preloaded_relations[:following][account_id] : other_account.following?(account) if following_author.nil?
|
||||
return :manual if following_author
|
||||
end
|
||||
|
||||
return :unknown if (automatic_policy | manual_policy).anybits?(QUOTE_APPROVAL_POLICY_FLAGS[:unknown])
|
||||
|
||||
:denied
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::QuoteRequestWorker < ActivityPub::RawDistributionWorker
|
||||
def perform(quote_id)
|
||||
@quote = Quote.find(quote_id)
|
||||
@account = @quote.account
|
||||
|
||||
distribute!
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def inboxes
|
||||
@inboxes ||= [@quote.quoted_account&.inbox_url].compact
|
||||
end
|
||||
|
||||
def payload
|
||||
@payload ||= Oj.dump(serialize_payload(@quote, ActivityPub::QuoteRequestSerializer, signer: @account))
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Status::InteractionPolicyConcern do
|
||||
let(:status) { Fabricate(:status, quote_approval_policy: (0b0101 << 16) | 0b0010) }
|
||||
|
||||
describe '#quote_policy_as_keys' do
|
||||
it 'returns the expected values' do
|
||||
expect(status.quote_policy_as_keys(:automatic)).to eq ['unknown', 'followers']
|
||||
expect(status.quote_policy_as_keys(:manual)).to eq ['public']
|
||||
end
|
||||
end
|
||||
|
||||
describe '#quote_policy_for_account' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
context 'when the account is not following the user' do
|
||||
it 'returns :manual because of the public entry in the manual policy' do
|
||||
expect(status.quote_policy_for_account(account)).to eq :manual
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the account is following the user' do
|
||||
before do
|
||||
account.follow!(status.account)
|
||||
end
|
||||
|
||||
it 'returns :automatic because of the followers entry in the automatic policy' do
|
||||
expect(status.quote_policy_for_account(account)).to eq :automatic
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the account falls into the unknown bucket' do
|
||||
let(:status) { Fabricate(:status, quote_approval_policy: (0b0001 << 16) | 0b0100) }
|
||||
|
||||
it 'returns :automatic because of the followers entry in the automatic policy' do
|
||||
expect(status.quote_policy_for_account(account)).to eq :unknown
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::QuoteRequestWorker do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:quoted_account) { Fabricate(:account, inbox_url: 'http://example.com', domain: 'example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let(:status) { Fabricate(:status, text: 'foo') }
|
||||
let(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, activity_uri: 'TODO') } # TODO: activity URI
|
||||
|
||||
describe '#perform' do
|
||||
it 'sends the expected QuoteRequest activity' do
|
||||
subject.perform(quote.id)
|
||||
|
||||
expect(ActivityPub::DeliveryWorker)
|
||||
.to have_enqueued_sidekiq_job(match_object_shape, quote.account_id, 'http://example.com', {})
|
||||
end
|
||||
|
||||
def match_object_shape
|
||||
match_json_values(
|
||||
type: 'QuoteRequest',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(quote.account),
|
||||
object: ActivityPub::TagManager.instance.uri_for(quoted_status),
|
||||
instrument: anything # TODO: inline post in request?
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue