Add quote_approval_policy parameter when posting and editing statuses (#35699)

This commit is contained in:
Claire 2025-08-06 16:23:12 +02:00 committed by GitHub
parent 6e48322055
commit 9ec99ffef1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 70 additions and 2 deletions

View File

@ -82,6 +82,7 @@ class Api::V1::StatusesController < Api::BaseController
text: status_params[:status], text: status_params[:status],
thread: @thread, thread: @thread,
quoted_status: @quoted_status, quoted_status: @quoted_status,
quote_approval_policy: quote_approval_policy,
media_ids: status_params[:media_ids], media_ids: status_params[:media_ids],
sensitive: status_params[:sensitive], sensitive: status_params[:sensitive],
spoiler_text: status_params[:spoiler_text], spoiler_text: status_params[:spoiler_text],
@ -113,7 +114,8 @@ class Api::V1::StatusesController < Api::BaseController
sensitive: status_params[:sensitive], sensitive: status_params[:sensitive],
language: status_params[:language], language: status_params[:language],
spoiler_text: status_params[:spoiler_text], spoiler_text: status_params[:spoiler_text],
poll: status_params[:poll] poll: status_params[:poll],
quote_approval_policy: quote_approval_policy
) )
render json: @status, serializer: REST::StatusSerializer render json: @status, serializer: REST::StatusSerializer
@ -180,6 +182,7 @@ class Api::V1::StatusesController < Api::BaseController
:status, :status,
:in_reply_to_id, :in_reply_to_id,
:quoted_status_id, :quoted_status_id,
:quote_approval_policy,
:sensitive, :sensitive,
:spoiler_text, :spoiler_text,
:visibility, :visibility,
@ -202,6 +205,23 @@ class Api::V1::StatusesController < Api::BaseController
) )
end end
def quote_approval_policy
# TODO: handle `nil` separately
return nil unless Mastodon::Feature.outgoing_quotes_enabled? && status_params[:quote_approval_policy].present?
case status_params[:quote_approval_policy]
when 'public'
Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16
when 'followers'
Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16
when 'nobody'
0
else
# TODO: raise more useful message
raise ActiveRecord::RecordInvalid
end
end
def serializer_for_status def serializer_for_status
@status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
end end

View File

@ -19,6 +19,7 @@ class PostStatusService < BaseService
# @option [String] :text Message # @option [String] :text Message
# @option [Status] :thread Optional status to reply to # @option [Status] :thread Optional status to reply to
# @option [Status] :quoted_status Optional status to quote # @option [Status] :quoted_status Optional status to quote
# @option [String] :quote_approval_policy Approval policy for quotes, one of `public`, `followers` or `nobody`
# @option [Boolean] :sensitive # @option [Boolean] :sensitive
# @option [String] :visibility # @option [String] :visibility
# @option [String] :spoiler_text # @option [String] :spoiler_text
@ -215,6 +216,7 @@ class PostStatusService < BaseService
language: valid_locale_cascade(@options[:language], @account.user&.preferred_posting_language, I18n.default_locale), language: valid_locale_cascade(@options[:language], @account.user&.preferred_posting_language, I18n.default_locale),
application: @options[:application], application: @options[:application],
rate_limit: @options[:with_rate_limit], rate_limit: @options[:with_rate_limit],
quote_approval_policy: @options[:quote_approval_policy],
}.compact }.compact
end end

View File

@ -115,6 +115,7 @@ class UpdateStatusService < BaseService
@status.spoiler_text = @options[:spoiler_text] || '' if @options.key?(:spoiler_text) @status.spoiler_text = @options[:spoiler_text] || '' if @options.key?(:spoiler_text)
@status.sensitive = @options[:sensitive] || @options[:spoiler_text].present? if @options.key?(:sensitive) || @options.key?(:spoiler_text) @status.sensitive = @options[:sensitive] || @options[:spoiler_text].present? if @options.key?(:sensitive) || @options.key?(:spoiler_text)
@status.language = valid_locale_cascade(@options[:language], @status.language, @status.account.user&.preferred_posting_language, I18n.default_locale) @status.language = valid_locale_cascade(@options[:language], @status.language, @status.account.user&.preferred_posting_language, I18n.default_locale)
@status.quote_approval_policy = @options[:quote_approval_policy] if @options[:quote_approval_policy].present?
# We raise here to rollback the entire transaction # We raise here to rollback the entire transaction
raise NoChangesSubmittedError unless significant_changes? raise NoChangesSubmittedError unless significant_changes?

View File

@ -158,6 +158,31 @@ RSpec.describe '/api/v1/statuses' do
end end
end end
context 'with a quote policy', feature: :outgoing_quotes do
let(:quoted_status) { Fabricate(:status, account: user.account) }
let(:params) do
{
status: 'Hello world, this is a self-quote',
quote_approval_policy: 'followers',
}
end
it 'returns post with appropriate quote policy, as well as rate limit headers', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
expect(response.parsed_body[:quote_approval]).to include({
automatic: ['followers'],
manual: [],
current_user: 'automatic',
})
expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s
expect(response.headers['X-RateLimit-Remaining']).to eq (RateLimiter::FAMILIES[:statuses][:limit] - 1).to_s
end
end
context 'with a self-quote post', feature: :outgoing_quotes do context 'with a self-quote post', feature: :outgoing_quotes do
let(:quoted_status) { Fabricate(:status, account: user.account) } let(:quoted_status) { Fabricate(:status, account: user.account) }
let(:params) do let(:params) do
@ -307,9 +332,10 @@ RSpec.describe '/api/v1/statuses' do
describe 'PUT /api/v1/statuses/:id' do describe 'PUT /api/v1/statuses/:id' do
subject do subject do
put "/api/v1/statuses/#{status.id}", headers: headers, params: { status: 'I am updated' } put "/api/v1/statuses/#{status.id}", headers: headers, params: params
end end
let(:params) { { status: 'I am updated' } }
let(:scopes) { 'write:statuses' } let(:scopes) { 'write:statuses' }
let(:status) { Fabricate(:status, account: user.account) } let(:status) { Fabricate(:status, account: user.account) }
@ -323,6 +349,19 @@ RSpec.describe '/api/v1/statuses' do
.to start_with('application/json') .to start_with('application/json')
expect(status.reload.text).to eq 'I am updated' expect(status.reload.text).to eq 'I am updated'
end end
context 'when updating only the quote policy' do
let(:params) { { status: status.text, quote_approval_policy: 'public' } }
it 'updates the status', :aggregate_failures, feature: :outgoing_quotes do
expect { subject }
.to change { status.reload.quote_approval_policy }.to(Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16)
expect(response).to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
end
end
end end
end end

View File

@ -160,6 +160,12 @@ RSpec.describe PostStatusService do
expect(status.language).to eq 'en' expect(status.language).to eq 'en'
end end
it 'creates a status with the quote approval policy set' do
status = create_status_with_options(quote_approval_policy: Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16)
expect(status.quote_approval_policy).to eq(Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16)
end
it 'processes mentions' do it 'processes mentions' do
mention_service = instance_double(ProcessMentionsService) mention_service = instance_double(ProcessMentionsService)
allow(mention_service).to receive(:call) allow(mention_service).to receive(:call)