5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-22 23:56:22 +00:00

feat: modificar el estado de moderación en masa #15328 #15327

This commit is contained in:
f 2024-03-12 13:57:31 -03:00
parent 522cef8c9a
commit bf75d50cc3
No known key found for this signature in database
10 changed files with 88 additions and 118 deletions

View file

@ -27,7 +27,7 @@ class ActivityPubsController < ApplicationController
authorize activity_pubs
action = params[:activity_pub_action].to_sym
method = :"#{action}!"
method = :"#{action}_all!"
may = :"may_#{action}?"
redirect_to_moderation_queue!
@ -35,36 +35,25 @@ class ActivityPubsController < ApplicationController
return unless ActivityPub.events.include? action
# Crear una sola remote flag por autore
if action == :report
message = remote_flag_params(activity_pubs.first).dig(:remote_flag_attributes, :message)
activity_pubs.distinct.pluck(:actor_id).each do |actor_id|
remote_flag = ActivityPub::RemoteFlag.find_or_initialize_by(actor_id: actor_id, site_id: site.id)
remote_flag.message = message
# Lo estamos actualizando, con lo que lo vamos a volver a enviar
remote_flag.requeue if remote_flag.persisted?
remote_flag.save
# XXX: Idealmente todas las ActivityPub que enviamos pueden
# cambiar de estado, pero chequeamos de todas formas.
remote_flag.activity_pubs << (activity_pubs.where(actor_id: actor_id).to_a.select { |a| a.public_send(may) })
end
end
ActivityPub.transaction do
activity_pubs.find_each do |activity_pub|
next unless activity_pub.public_send(may)
if action == :report
message = remote_flag_params(activity_pubs.first).dig(:remote_flag_attributes, :message)
activity_pub.public_send(method)
flash[:success] = I18n.t('activity_pubs.action_on_several.success')
rescue Exception => e
ExceptionNotifier.notify_exception(e,
data: { site: site.name, params: params.permit!.to_h,
activity_pub: activity_pub.id })
flash.delete(:success)
flash[:error] = I18n.t('activity_pubs.action_on_several.error')
activity_pubs.distinct.pluck(:actor_id).each do |actor_id|
remote_flag = ActivityPub::RemoteFlag.find_or_initialize_by(actor_id: actor_id, site_id: site.id)
remote_flag.message = message
# Lo estamos actualizando, con lo que lo vamos a volver a enviar
remote_flag.requeue if remote_flag.persisted?
remote_flag.save
# XXX: Idealmente todas las ActivityPub que enviamos pueden
# cambiar de estado, pero chequeamos de todas formas.
remote_flag.activity_pubs << (activity_pubs.where(actor_id: actor_id).to_a.select { |a| a.public_send(may) })
end
end
message = activity_pubs.public_send(method) ? :success : :error
flash[message] = I18n.t("activity_pubs.action_on_several.#{message}")
end
end

View file

@ -37,7 +37,7 @@ class ActorModerationsController < ApplicationController
authorize actor_moderations
action = params[:actor_moderation_action].to_sym
method = :"#{action}!"
method = :"#{action}_all!"
may = :"may_#{action}?"
redirect_to_moderation_queue!
@ -45,20 +45,17 @@ class ActorModerationsController < ApplicationController
return unless ActorModeration.events.include? action
ActorModeration.transaction do
actor_moderations.find_each do |actor_moderation|
next unless actor_moderation.public_send(may)
if action == :report
actor_moderations.find_each do |actor_moderation|
next unless actor_moderation.public_send(may)
actor_moderation.update(actor_moderation_params(actor_moderation)) if action == :report
actor_moderation.public_send(method)
flash[:success] = I18n.t('actor_moderations.action_on_several.success')
rescue Exception => e
ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h })
flash.delete(:success)
flash[:error] = I18n.t('actor_moderations.action_on_several.error')
actor_moderation.update(actor_moderation_params(actor_moderation))
end
end
message = actor_moderation.public_send(method) ? :success : :error
flash[message] = I18n.t("actor_moderations.action_on_several.#{message}")
end
end

View file

@ -10,6 +10,12 @@ class InstanceModerationsController < ApplicationController
instance_moderation.public_send(:"#{event}!") if instance_moderation.public_send(:"may_#{event}?")
flash[:success] = I18n.t("instance_moderations.#{event}.success")
rescue Exception => e
ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h })
flash[:error] = I18n.t("instance_moderations.#{event}.error")
ensure
redirect_to_moderation_queue!
end
end
@ -20,17 +26,16 @@ class InstanceModerationsController < ApplicationController
authorize instance_moderations
action = params[:instance_moderation_action].to_sym
method = :"#{action}!"
may = :"may_#{action}?"
method = :"#{action}_all!"
redirect_to_moderation_queue!
return unless InstanceModeration.events.include? action
InstanceModeration.transaction do
instance_moderations.find_each do |instance_moderation|
instance_moderation.public_send(method) if instance_moderation.public_send(may)
end
message = instance_moderations.public_send(method) ? :success : :error
flash[:message] = I18n.t("instance_moderations.action_on_several.#{message}")
end
end

View file

@ -14,7 +14,6 @@ class ActivityPub
end
instances = ActivityPub::Instance.where(hostname: hostnames)
success = true
Site.transaction do
# Crea todas las moderaciones de instancia con un estado por
@ -22,17 +21,11 @@ class ActivityPub
instances.find_each do |instance|
# Esto bloquea cada una individualmente en la Social Inbox,
# idealmente son pocas instancias las que aparecen.
site.instance_moderations.find_or_create_by(instance: instance).tap do |instance_moderation|
instance_moderation.block! if instance_moderation.may_block?
# Notificar todos los errores sin detener la ejecución
rescue Exception => e
ExceptionNotifier.notify_exception(e, data: { site: site.name, instance_moderation: instance_moderation.id })
success = false
end
site.instance_moderations.find_or_create_by(instance: instance)
end
end
success
site.instance_moderations.where(instance_id: instances.ids).block_all!
end
end
end
end

View file

@ -38,28 +38,6 @@ class ActivityPub < ApplicationRecord
end
end
# Permite todos los comentarios. No podemos hacer acciones en masa
# sobre comentarios en la Social Inbox, con lo que tenemos que
# comunicarnos individualmente.
def self.allow_all!
self.find_each do |activity_pub|
activity_pub.allow! if activity_pub.may_allow?
rescue Exception => e
notify_exception!(e, activity_pub)
end
end
# Rechaza todos los comentarios. No podemos hacer acciones en masa
# sobre comentarios en la Social Inbox, con lo que tenemos que
# comunicarnos individualmente.
def self.reject_all!
self.find_each do |activity_pub|
activity_pub.reject! if activity_pub.may_reject?
rescue Exception => e
notify_exception!(e, activity_pub)
end
end
aasm do
# Todavía no hay una decisión sobre el objeto
state :paused, initial: true

View file

@ -14,19 +14,6 @@ class ActorModeration < ApplicationRecord
accepts_nested_attributes_for :remote_flag
# Bloquea todes les Actores bloqueables
def self.block_all!
self.update_all(aasm_state: 'blocked', updated_at: Time.now)
end
def self.pause_all!
self.update_all(aasm_state: 'paused', updated_at: Time.now)
end
def self.remove_all!
self.update_all(aasm_state: 'removed', updated_at: Time.now)
end
aasm do
state :paused, initial: true
state :allowed

View file

@ -28,6 +28,26 @@ module AasmEventsConcern
aasm.states.map(&:name) - self::IGNORED_STATES
end
# Define un método que cambia el estado para todos los objetos del
# scope actual.
#
# @return [Bool] Si hubo al menos un error, devuelve false.
self.events.each do |event|
define_singleton_method(:"#{event}_all!") do
success = true
self.find_each do |object|
object.public_send(:"#{event}!") if object.public_send(:"may_#{event}?")
rescue Exception => e
success = false
notify_exception! e, object
end
success
end
end
# Envía notificación de errores
#
# @param exception [Exception]

View file

@ -39,12 +39,6 @@ class FediblockState < ApplicationRecord
# Luego esta tarea crea las que falten e ignora las que ya se
# bloquearon.
ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames)
# Bloquear a todes les Actores de las instancias bloqueadas para
# indicarle a le usuarie que les tiene que desbloquear
# manualmente.
actor_ids = ActivityPub::Actor.where(instance_id: instance_ids).ids
ActorModeration.where(actor_id: actor_ids).paused.block_all!
end
end
@ -66,12 +60,6 @@ class FediblockState < ApplicationRecord
# bloqueadas por otros fediblocks.
instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids
site.instance_moderations.where(instance_id: instance_ids).pause_all!
# Volver a pausar todes les actores de esta instancia que fueron
# bloqueades, a menos que hayan sido bloqueades por otro
# fediblock.
actor_ids = ActivityPub::Actor.where(instance_id: instance_ids).ids
ActorModeration.where(actor_id: actor_ids).blocked.pause_all!
end
end
end

View file

@ -11,26 +11,13 @@ class InstanceModeration < ApplicationRecord
belongs_to :site
belongs_to :instance, class_name: 'ActivityPub::Instance'
# Traer todas las instancias bloqueables, según la máquina de estados,
# todas las que no estén bloqueadas ya.
scope :may_block, -> { where.not(aasm_state: 'blocked') }
scope :may_pause, -> { where.not(aasm_state: 'paused') }
# Bloquear instancias en masa
def self.block_all!
self.may_block.update_all(aasm_state: 'blocked', updated_at: Time.now)
end
# Pausar instancias en masa
def self.pause_all!
self.may_pause.update_all(aasm_state: 'paused', updated_at: Time.now)
end
aasm do
state :paused, initial: true
state :allowed
state :blocked
# Al volver la instancia a pausa no cambiamos el estado de
# moderación de actores pre-existente.
event :pause do
transitions from: %i[allowed blocked], to: :paused
@ -39,23 +26,36 @@ class InstanceModeration < ApplicationRecord
end
end
# Al permitir, también permitimos todes les actores que no hayan
# tenido acciones de moderación.
event :allow do
transitions from: %i[paused blocked], to: :allowed
before do
allow_remotely!
site.actor_moderations.paused.where(actor_id: actor_ids).allow_all!
end
end
# Al bloquear, también bloqueamos a todes les actores que no hayan
# tenido acciones de moderación.
event :block do
transitions from: %i[paused allowed], to: :blocked
before do
block_remotely!
site.actor_moderations.paused.where(actor_id: actor_ids).block_all!
end
end
end
# @return [Array<String>]
def actor_ids
ActivityPub::Actor.where(instance_id: self.instance_id).ids
end
# Elimina la instancia de todas las listas
#
# @return [Boolean]

View file

@ -145,6 +145,19 @@ es:
report:
success: "Cuenta reportada a su instancia."
error: "No se pudo reportar la cuenta. Hemos recibido el reporte y lo estaremos verificando."
instance_moderations:
action_on_several:
success: "Se ha modificado el estado de moderación de varias instancias. Podés encontrarlas usando los filtros en la sección Instancias."
error: "Hubo un error al modificar el estado de moderación de varias instancias. Hemos recibido el reporte y lo estaremos verificando."
pause:
success: "Instancia pausada. Todos los comentarios y cuentas de esta instancia necesitan ser aprobados manualmente. No se ha modificado el estado de moderación de cuentas y comentarios pre-existentes."
error: "No se pudo pausar la instancia. Hemos recibido el reporte y lo estaremos verificando."
allow:
success: "Instancia permitida. Todos los comentarios y cuentas pendientes de moderación fueron aprobados y los próximos serán aprobados inmediatamente."
error: "No se pudo permitir la instancia. Hemos recibido el reporte y lo estaremos verificando."
block:
success: "Instancia bloqueada. Todos los comentarios y cuentas serán rechazados inmediatamente. Todos los comentarios y cuentas pendientes de moderación fueron rechazados."
error: "No se pudo bloquear la instancia. Hemos recibido el reporte y lo estaremos verificando."
fediblock_states:
action_on_several:
success: "Se habilitaron las listas de bloqueo, podés encontrar las instancias filtrando por Bloqueadas. Todas las cuentas de estas instancias pendientes de moderación han sido bloqueadas. Podés activarlas individualmente en la sección Cuentas."