mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-12 22:15:29 -05:00
Add interface for moderation subscriptions and suggestions
This commit is contained in:
96
app/controllers/admin/moderation_subscriptions_controller.rb
Normal file
96
app/controllers/admin/moderation_subscriptions_controller.rb
Normal file
@@ -0,0 +1,96 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::ModerationSubscriptionsController < Admin::BaseController
|
||||
before_action :set_moderation_subscriptions, only: :index
|
||||
before_action :set_moderation_subscription, only: [:show, :edit, :update, :destroy]
|
||||
before_action :set_subscribed_advisories, only: :show
|
||||
|
||||
def index
|
||||
authorize :moderation_subscription, :index?
|
||||
end
|
||||
|
||||
def show
|
||||
authorize @moderation_subscription, :show?
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :moderation_subscription, :create?
|
||||
@moderation_subscription = ModerationSubscription.new
|
||||
end
|
||||
|
||||
def edit
|
||||
authorize @moderation_subscription, :update?
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :moderation_subscription, :create?
|
||||
|
||||
# TODO: require confirmation for dangerous subscriptions
|
||||
|
||||
@moderation_subscription = ModerationSubscription.new(resource_params.merge('type' => 'csv_list'))
|
||||
|
||||
if @moderation_subscription.save
|
||||
log_action :create, @moderation_subscription
|
||||
redirect_to admin_moderation_subscriptions_path
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @moderation_subscription, :update?
|
||||
|
||||
# TODO: require confirmation for dangerous subscriptions
|
||||
|
||||
if @moderation_subscription.update(resource_params)
|
||||
log_action :update, @moderation_subscription
|
||||
redirect_to admin_moderation_subscription_path(@moderation_subscription)
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @moderation_subscription, :destroy?
|
||||
|
||||
ModerationSubscription.transaction do
|
||||
# Re-run moderation list service with empty list to ensure
|
||||
# Retractions are handled properly
|
||||
@moderation_subscription.advisories.delete_all
|
||||
ProcessModerationListsService.new.call
|
||||
|
||||
@moderation_subscription.destroy!
|
||||
log_action :destroy, @moderation_subscription
|
||||
end
|
||||
|
||||
redirect_to admin_moderation_subscriptions_path, notice: I18n.t('admin.moderation_subscriptions.destroyed_msg')
|
||||
end
|
||||
|
||||
def logs
|
||||
authorize :moderation_subscription, :index?
|
||||
|
||||
@action_logs = Admin::ActionLog.latest.where.not(moderation_subscription_id: nil).page(params[:page])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.expect(
|
||||
moderation_subscription: [
|
||||
:name, :url, :priority, :list_action, :apply_conditions, :retract_automatically
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def set_moderation_subscriptions
|
||||
@moderation_subscriptions = ModerationSubscription.order(priority: :asc)
|
||||
end
|
||||
|
||||
def set_moderation_subscription
|
||||
@moderation_subscription = ModerationSubscription.find(params[:id])
|
||||
end
|
||||
|
||||
def set_subscribed_advisories
|
||||
@subscribed_advisories = @moderation_subscription.advisories.order(id: :asc).page(params[:page])
|
||||
end
|
||||
end
|
||||
159
app/controllers/admin/moderation_suggestions_controller.rb
Normal file
159
app/controllers/admin/moderation_suggestions_controller.rb
Normal file
@@ -0,0 +1,159 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::ModerationSuggestionsController < Admin::BaseController
|
||||
before_action :set_moderation_suggestion_targets, only: :index
|
||||
before_action :set_moderation_suggestions_by_target, only: :index
|
||||
before_action :set_moderation_advisories_by_target, only: :index
|
||||
before_action :set_current_state_by_target, only: :index
|
||||
before_action :set_moderation_suggestion, only: [:destroy, :apply]
|
||||
|
||||
def index
|
||||
authorize :moderation_suggestion, :index?
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @moderation_suggestion, :dismiss?
|
||||
|
||||
# TODO: log?
|
||||
|
||||
# Actually dismiss all of the suggestions for the same target
|
||||
ModerationSuggestion.where(target_type: @moderation_suggestion.target_type, target_key: @moderation_suggestion.target_key).update_all(state: :dismissed)
|
||||
|
||||
redirect_to admin_moderation_suggestions_path, notice: I18n.t('admin.moderation_suggestions.destroyed_msg', target_key: @moderation_suggestion.target_key)
|
||||
end
|
||||
|
||||
def apply
|
||||
authorize @moderation_suggestion, :apply?
|
||||
|
||||
case [@moderation_suggestion.target_type, @moderation_suggestion.action]
|
||||
when ['domain', 'accept']
|
||||
domain_allow = @moderation_suggestion.to_domain_allow
|
||||
|
||||
ApplicationRecord.transaction do
|
||||
# TODO: log
|
||||
domain_allow.save!
|
||||
@moderation_suggestion.mark_as_applied!
|
||||
end
|
||||
|
||||
redirect_to admin_moderation_suggestions_path, notice: I18n.t('admin.moderation_suggestions.applied_msg', target_key: @moderation_suggestion.target_key)
|
||||
when ['domain', 'reject'], ['domain', 'limit']
|
||||
domain_block = @moderation_suggestion.to_domain_block
|
||||
|
||||
# TODO: factor with `DomainBlocksController#create`?
|
||||
existing_domain_block = DomainBlock.rule_for(domain_block.domain)
|
||||
|
||||
# We can't create a laxer block for a subdomain than we have for a domain
|
||||
if existing_domain_block.present? && existing_domain_block.domain != TagManager.instance.normalize_domain(domain_block.domain) && !domain_block.stricter_than?(existing_domain_block)
|
||||
return redirect_to admin_moderation_suggestions_path,
|
||||
alert: I18n.t('admin.moderation_suggestions.unable_to_apply_msg',
|
||||
target_key: @moderation_suggestion.target_key)
|
||||
end
|
||||
|
||||
update = false
|
||||
|
||||
# Allow transparently upgrading a domain block
|
||||
if existing_domain_block.present? && existing_domain_block.domain == TagManager.instance.normalize_domain(domain_block.domain)
|
||||
# Downgrading requires confirmation
|
||||
return render :confirm_downgrade unless domain_block.stricter_than?(existing_domain_block) || params[:confirm_downgrade]
|
||||
|
||||
# Transparent upgrading is allowed
|
||||
existing_domain_block.assign_attributes(domain_block.attributes.without('id', 'created_at', 'updated_at'))
|
||||
domain_block = existing_domain_block
|
||||
|
||||
update = domain_block.severity_changed?
|
||||
end
|
||||
|
||||
# Require explicit confirmation on block
|
||||
return render :confirm_reject if requires_confirmation?(domain_block)
|
||||
|
||||
domain_block.save!
|
||||
# TODO: is this the way we want to log it?
|
||||
log_action (update ? :update : :create), domain_block, moderation_subscription_id: domain_block.moderation_subscription_id
|
||||
DomainBlockWorker.perform_async(domain_block.id, update)
|
||||
@moderation_suggestion.mark_as_applied!
|
||||
|
||||
redirect_to admin_moderation_suggestions_path, notice: I18n.t('admin.moderation_suggestions.applied_msg', target_key: @moderation_suggestion.target_key)
|
||||
when ['domain', 'retract']
|
||||
ApplicationRecord.transaction do
|
||||
if Rails.configuration.x.mastodon.limited_federation_mode
|
||||
domain_allow = DomainAllow.find_by(domain: @moderation_suggestion.target_key)
|
||||
UnallowDomainService.new.call(domain_allow)
|
||||
# TODO: is this the way we want to log it?
|
||||
log_action :destroy, domain_allow, moderation_subscription_id: domain_block.moderation_subscription_id
|
||||
else
|
||||
domain_block = DomainBlock.find_by(domain: @moderation_suggestion.target_key)
|
||||
UnblockDomainService.new.call(domain_block)
|
||||
# TODO: is this the way we want to log it?
|
||||
log_action :destroy, domain_block, moderation_subscription_id: domain_block.moderation_subscription_id
|
||||
end
|
||||
|
||||
@moderation_suggestion.mark_as_applied!
|
||||
end
|
||||
|
||||
redirect_to admin_moderation_suggestions_path, notice: I18n.t('admin.moderation_suggestions.retracted_msg', target_key: @moderation_suggestion.target_key)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def requires_confirmation?(domain_block)
|
||||
domain_block.valid? && (domain_block.new_record? || domain_block.severity_changed?) && domain_block.suspend? && !params[:confirm]
|
||||
end
|
||||
|
||||
def set_moderation_suggestion_targets
|
||||
@moderation_suggestion_targets = ModerationSuggestion.where(state: ['new', 'mailed']).reorder([target_type: :asc, target_key: :asc]).distinct.pluck(:target_type, :target_key)
|
||||
end
|
||||
|
||||
def set_moderation_suggestions_by_target
|
||||
@moderation_suggestions_by_target = begin
|
||||
@moderation_suggestion_targets.group_by(&:first).reduce(ModerationSuggestion.none) do |scope, (target_type, target_pairs)|
|
||||
scope.or(ModerationSuggestion.where(target_type: target_type, target_key: target_pairs.map(&:second)))
|
||||
end
|
||||
end.group_by { |suggestion| [suggestion.target_type, suggestion.target_key] }
|
||||
end
|
||||
|
||||
def set_moderation_advisories_by_target
|
||||
@moderation_advisories_by_target = begin
|
||||
@moderation_suggestion_targets.group_by(&:first).reduce(SubscribedAdvisory.none) do |scope, (target_type, target_pairs)|
|
||||
scope.or(SubscribedAdvisory.joins(:moderation_subscription).where(target_type: target_type, target_key: target_pairs.map(&:second)))
|
||||
end
|
||||
end.group_by { |advisory| [advisory.target_type, advisory.target_key] }
|
||||
|
||||
@moderation_advisories_by_target.each_value do |advisories|
|
||||
advisories.sort_by! { |advisory| advisory.moderation_subscription.priority }
|
||||
end
|
||||
|
||||
@moderation_advisories_by_target
|
||||
end
|
||||
|
||||
def set_current_state_by_target
|
||||
# TODO: handle other target types
|
||||
|
||||
if Rails.configuration.x.mastodon.limited_federation_mode
|
||||
@current_state_by_target = DomainAllow.where(domain: @moderation_suggestion_targets.filter_map { |type, key| key if type == 'domain' }).pluck(:domain).to_h { |domain| [['domain', domain], 'accept'] }
|
||||
@current_state_by_target.default = 'reject'
|
||||
else
|
||||
@current_state_by_target = DomainBlock.where(domain: @moderation_suggestion_targets.filter_map { |type, key| key if type == 'domain' }).to_h do |domain_block|
|
||||
action = begin
|
||||
case domain_block.severity
|
||||
when 'suspend'
|
||||
'reject'
|
||||
when 'silence'
|
||||
'limit'
|
||||
else
|
||||
'accept'
|
||||
end
|
||||
end
|
||||
|
||||
[['domain', domain_block.domain], action]
|
||||
end
|
||||
|
||||
@current_state_by_target.default = 'accept'
|
||||
end
|
||||
@current_state_by_target
|
||||
end
|
||||
|
||||
def set_moderation_suggestion
|
||||
@moderation_suggestion = ModerationSuggestion.find(params[:id])
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user