Add moderation consequences for collections (#37974)
This commit is contained in:
parent
ea34d35b32
commit
f9326efef6
@ -19,7 +19,7 @@ module Admin::ActionLogsHelper
|
|||||||
link_to "##{log.human_identifier.presence || log.target_id}", admin_report_path(log.target_id)
|
link_to "##{log.human_identifier.presence || log.target_id}", admin_report_path(log.target_id)
|
||||||
when 'Instance', 'DomainBlock', 'DomainAllow', 'UnavailableDomain'
|
when 'Instance', 'DomainBlock', 'DomainAllow', 'UnavailableDomain'
|
||||||
log.human_identifier.present? ? link_to(log.human_identifier, admin_instance_path(log.human_identifier)) : I18n.t('admin.action_logs.unavailable_instance')
|
log.human_identifier.present? ? link_to(log.human_identifier, admin_instance_path(log.human_identifier)) : I18n.t('admin.action_logs.unavailable_instance')
|
||||||
when 'Status'
|
when 'Status', 'Collection'
|
||||||
link_to log.human_identifier, log.permalink
|
link_to log.human_identifier, log.permalink
|
||||||
when 'AccountWarning'
|
when 'AccountWarning'
|
||||||
link_to log.human_identifier, disputes_strike_path(log.target_id)
|
link_to log.human_identifier, disputes_strike_path(log.target_id)
|
||||||
|
|||||||
@ -18,6 +18,10 @@ class Admin::ModerationAction < Admin::BaseAction
|
|||||||
@statuses ||= Status.with_discarded.where(id: status_ids).reorder(nil)
|
@statuses ||= Status.with_discarded.where(id: status_ids).reorder(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def collections
|
||||||
|
report.collections
|
||||||
|
end
|
||||||
|
|
||||||
def process_action!
|
def process_action!
|
||||||
case type
|
case type
|
||||||
when 'delete'
|
when 'delete'
|
||||||
@ -29,19 +33,16 @@ class Admin::ModerationAction < Admin::BaseAction
|
|||||||
|
|
||||||
def handle_delete!
|
def handle_delete!
|
||||||
statuses.each { |status| authorize([:admin, status], :destroy?) }
|
statuses.each { |status| authorize([:admin, status], :destroy?) }
|
||||||
|
collections.each { |collection| authorize([:admin, collection], :destroy?) }
|
||||||
|
|
||||||
ApplicationRecord.transaction do
|
ApplicationRecord.transaction do
|
||||||
statuses.each do |status|
|
delete_statuses!
|
||||||
status.discard_with_reblogs
|
delete_collections!
|
||||||
log_action(:destroy, status)
|
|
||||||
end
|
|
||||||
|
|
||||||
report.resolve!(current_account)
|
|
||||||
log_action(:resolve, report)
|
|
||||||
|
|
||||||
|
resolve_report!
|
||||||
process_strike!(:delete_statuses)
|
process_strike!(:delete_statuses)
|
||||||
|
|
||||||
statuses.each { |status| Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) } unless target_account.local?
|
create_tombstones! unless target_account.local?
|
||||||
end
|
end
|
||||||
|
|
||||||
process_notification!
|
process_notification!
|
||||||
@ -50,10 +51,39 @@ class Admin::ModerationAction < Admin::BaseAction
|
|||||||
end
|
end
|
||||||
|
|
||||||
def handle_mark_as_sensitive!
|
def handle_mark_as_sensitive!
|
||||||
representative_account = Account.representative
|
collections.each { |collection| authorize([:admin, collection], :update?) }
|
||||||
|
|
||||||
# Can't use a transaction here because UpdateStatusService queues
|
# Can't use a transaction here because UpdateStatusService queues
|
||||||
# Sidekiq jobs
|
# Sidekiq jobs
|
||||||
|
mark_statuses_as_sensitive!
|
||||||
|
mark_collections_as_sensitive!
|
||||||
|
|
||||||
|
resolve_report!
|
||||||
|
process_strike!(:mark_statuses_as_sensitive)
|
||||||
|
process_notification!
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_statuses!
|
||||||
|
statuses.each do |status|
|
||||||
|
status.discard_with_reblogs
|
||||||
|
log_action(:destroy, status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_collections!
|
||||||
|
collections.each do |collection|
|
||||||
|
collection.destroy!
|
||||||
|
log_action(:destroy, collection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_tombstones!
|
||||||
|
(statuses + collections).each { |record| Tombstone.find_or_create_by(uri: record.uri, account: target_account, by_moderator: true) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def mark_statuses_as_sensitive!
|
||||||
|
representative_account = Account.representative
|
||||||
|
|
||||||
statuses.includes(:media_attachments, preview_cards_status: :preview_card).find_each do |status|
|
statuses.includes(:media_attachments, preview_cards_status: :preview_card).find_each do |status|
|
||||||
next if status.discarded? || !(status.with_media? || status.with_preview_card?)
|
next if status.discarded? || !(status.with_media? || status.with_preview_card?)
|
||||||
|
|
||||||
@ -66,14 +96,20 @@ class Admin::ModerationAction < Admin::BaseAction
|
|||||||
end
|
end
|
||||||
|
|
||||||
log_action(:update, status)
|
log_action(:update, status)
|
||||||
|
end
|
||||||
report.resolve!(current_account)
|
|
||||||
log_action(:resolve, report)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
process_strike!(:mark_statuses_as_sensitive)
|
def mark_collections_as_sensitive!
|
||||||
|
collections.each do |collection|
|
||||||
|
UpdateCollectionService.new.call(collection, sensitive: true)
|
||||||
|
|
||||||
process_notification!
|
log_action(:update, collection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_report!
|
||||||
|
report.resolve!(current_account)
|
||||||
|
log_action(:resolve, report)
|
||||||
end
|
end
|
||||||
|
|
||||||
def target_account
|
def target_account
|
||||||
|
|||||||
@ -69,6 +69,14 @@ class Collection < ApplicationRecord
|
|||||||
:featured_collection
|
:featured_collection
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_log_human_identifier
|
||||||
|
account.acct
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_log_permalink
|
||||||
|
ActivityPub::TagManager.instance.uri_for(self)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def tag_is_usable
|
def tag_is_usable
|
||||||
|
|||||||
19
app/policies/admin/collection_policy.rb
Normal file
19
app/policies/admin/collection_policy.rb
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Admin::CollectionPolicy < ApplicationPolicy
|
||||||
|
def index?
|
||||||
|
role.can?(:manage_reports, :manage_users)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show?
|
||||||
|
role.can?(:manage_reports, :manage_users)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy?
|
||||||
|
role.can?(:manage_reports)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update?
|
||||||
|
role.can?(:manage_reports)
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -5,7 +5,7 @@
|
|||||||
= link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(report), method: :post, class: 'button'
|
= link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(report), method: :post, class: 'button'
|
||||||
.report-actions__item__description
|
.report-actions__item__description
|
||||||
= t('admin.reports.actions.resolve_description_html')
|
= t('admin.reports.actions.resolve_description_html')
|
||||||
- if statuses.any? { |status| (status.with_media? || status.with_preview_card?) && !status.discarded? }
|
- if report.collections.any? || statuses.any? { |status| (status.with_media? || status.with_preview_card?) && !status.discarded? }
|
||||||
.report-actions__item
|
.report-actions__item
|
||||||
.report-actions__item__button
|
.report-actions__item__button
|
||||||
= form.button t('admin.reports.mark_as_sensitive'),
|
= form.button t('admin.reports.mark_as_sensitive'),
|
||||||
@ -18,8 +18,8 @@
|
|||||||
= form.button t('admin.reports.delete_and_resolve'),
|
= form.button t('admin.reports.delete_and_resolve'),
|
||||||
name: :delete,
|
name: :delete,
|
||||||
class: 'button button--destructive',
|
class: 'button button--destructive',
|
||||||
disabled: statuses.empty?,
|
disabled: (report.collections + statuses).empty?,
|
||||||
title: statuses.empty? ? t('admin.reports.actions_no_posts') : ''
|
title: (report.collections + statuses).empty? ? t('admin.reports.actions_no_posts') : ''
|
||||||
.report-actions__item__description
|
.report-actions__item__description
|
||||||
= t('admin.reports.actions.delete_description_html')
|
= t('admin.reports.actions.delete_description_html')
|
||||||
.report-actions__item
|
.report-actions__item
|
||||||
|
|||||||
@ -267,6 +267,7 @@ en:
|
|||||||
demote_user_html: "%{name} demoted user %{target}"
|
demote_user_html: "%{name} demoted user %{target}"
|
||||||
destroy_announcement_html: "%{name} deleted announcement %{target}"
|
destroy_announcement_html: "%{name} deleted announcement %{target}"
|
||||||
destroy_canonical_email_block_html: "%{name} unblocked email with the hash %{target}"
|
destroy_canonical_email_block_html: "%{name} unblocked email with the hash %{target}"
|
||||||
|
destroy_collection_html: "%{name} removed collection by %{target}"
|
||||||
destroy_custom_emoji_html: "%{name} deleted emoji %{target}"
|
destroy_custom_emoji_html: "%{name} deleted emoji %{target}"
|
||||||
destroy_domain_allow_html: "%{name} disallowed federation with domain %{target}"
|
destroy_domain_allow_html: "%{name} disallowed federation with domain %{target}"
|
||||||
destroy_domain_block_html: "%{name} unblocked domain %{target}"
|
destroy_domain_block_html: "%{name} unblocked domain %{target}"
|
||||||
@ -306,6 +307,7 @@ en:
|
|||||||
unsilence_account_html: "%{name} undid limit of %{target}'s account"
|
unsilence_account_html: "%{name} undid limit of %{target}'s account"
|
||||||
unsuspend_account_html: "%{name} unsuspended %{target}'s account"
|
unsuspend_account_html: "%{name} unsuspended %{target}'s account"
|
||||||
update_announcement_html: "%{name} updated announcement %{target}"
|
update_announcement_html: "%{name} updated announcement %{target}"
|
||||||
|
update_collection_html: "%{name} updated collection by %{target}"
|
||||||
update_custom_emoji_html: "%{name} updated emoji %{target}"
|
update_custom_emoji_html: "%{name} updated emoji %{target}"
|
||||||
update_domain_block_html: "%{name} updated domain block for %{target}"
|
update_domain_block_html: "%{name} updated domain block for %{target}"
|
||||||
update_ip_block_html: "%{name} changed rule for IP %{target}"
|
update_ip_block_html: "%{name} changed rule for IP %{target}"
|
||||||
|
|||||||
@ -32,6 +32,36 @@ RSpec.describe Admin::ModerationAction do
|
|||||||
end
|
end
|
||||||
expect(report.reload).to be_action_taken
|
expect(report.reload).to be_action_taken
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with attached collections', feature: :collections do
|
||||||
|
let(:status_ids) { [] }
|
||||||
|
let(:collections) { Fabricate.times(2, :collection, account: target_account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
report.collections = collections
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deletes the collections and creates an action log' do
|
||||||
|
expect { subject.save! }.to change(Collection, :count).by(-2)
|
||||||
|
.and change(Admin::ActionLog, :count).by(3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a remote collection', feature: :collections do
|
||||||
|
let(:status_ids) { [] }
|
||||||
|
let(:collection) { Fabricate(:remote_collection) }
|
||||||
|
let(:target_account) { collection.account }
|
||||||
|
|
||||||
|
before do
|
||||||
|
report.collections << collection
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a tombstone' do
|
||||||
|
expect { subject.save! }.to change(Tombstone, :count).by(1)
|
||||||
|
|
||||||
|
expect(Tombstone.last.uri).to eq collection.uri
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when `type` is `mark_as_sensitive`' do
|
context 'when `type` is `mark_as_sensitive`' do
|
||||||
@ -52,6 +82,24 @@ RSpec.describe Admin::ModerationAction do
|
|||||||
end
|
end
|
||||||
expect(report.reload).to be_action_taken
|
expect(report.reload).to be_action_taken
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with attached collections', feature: :collections do
|
||||||
|
let(:status_ids) { [] }
|
||||||
|
let(:collections) { Fabricate.times(2, :collection, account: target_account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
report.collections = collections
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'marks the collections as sensitive' do
|
||||||
|
subject.save!
|
||||||
|
|
||||||
|
collections.each do |collection|
|
||||||
|
expect(collection.reload).to be_sensitive
|
||||||
|
end
|
||||||
|
expect(report.reload).to be_action_taken
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -138,4 +138,18 @@ RSpec.describe Collection do
|
|||||||
expect(subject.object_type).to eq :featured_collection
|
expect(subject.object_type).to eq :featured_collection
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#to_log_human_identifier' do
|
||||||
|
subject { Fabricate(:collection) }
|
||||||
|
|
||||||
|
it 'returns the account name' do
|
||||||
|
expect(subject.to_log_human_identifier).to eq subject.account.acct
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_log_permalink' do
|
||||||
|
it 'includes the URI of the collection' do
|
||||||
|
expect(subject.to_log_permalink).to eq ActivityPub::TagManager.instance.uri_for(subject)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
24
spec/policies/admin/collection_policy_spec.rb
Normal file
24
spec/policies/admin/collection_policy_spec.rb
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Admin::CollectionPolicy do
|
||||||
|
let(:policy) { described_class }
|
||||||
|
let(:admin) { Fabricate(:admin_user).account }
|
||||||
|
let(:john) { Fabricate(:account) }
|
||||||
|
let(:collection) { Fabricate(:collection) }
|
||||||
|
|
||||||
|
permissions :index?, :show?, :update?, :destroy? do
|
||||||
|
context 'with an admin' do
|
||||||
|
it 'permits' do
|
||||||
|
expect(policy).to permit(admin, Collection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a non-admin' do
|
||||||
|
it 'denies' do
|
||||||
|
expect(policy).to_not permit(john, Collection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
x
Reference in New Issue
Block a user