Add moderation warnings (#9519)

* Add moderation warnings

Replace individual routes for disabling, silencing, and suspending
a user, as well as the report update route, with a unified account
action controller that allows you to select an action (none,
disable, silence, suspend) as well as whether it should generate an
e-mail notification with optional custom text. That notification,
with the optional custom text, is saved as a warning.

Additionally, there are warning presets you can configure to save
time when performing the above.

* Use Account#local_username_and_domain
master
Eugen Rochko 5 years ago committed by GitHub
parent 00862dcaff
commit 3c033c4352
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      app/controllers/admin/account_actions_controller.rb
  2. 1
      app/controllers/admin/account_moderation_notes_controller.rb
  3. 23
      app/controllers/admin/accounts_controller.rb
  4. 79
      app/controllers/admin/reports_controller.rb
  5. 27
      app/controllers/admin/silences_controller.rb
  6. 60
      app/controllers/admin/suspensions_controller.rb
  7. 58
      app/controllers/admin/warning_presets_controller.rb
  8. 7
      app/helpers/admin/action_logs_helper.rb
  9. 4
      app/javascript/images/icon_flag.svg
  10. BIN
      app/javascript/images/mailer/icon_warning.png
  11. 4
      app/javascript/styles/mailer.scss
  12. 4
      app/javascript/styles/mastodon/admin.scss
  13. 12
      app/mailers/user_mailer.rb
  14. 8
      app/models/account.rb
  15. 23
      app/models/account_warning.rb
  16. 15
      app/models/account_warning_preset.rb
  17. 134
      app/models/admin/account_action.rb
  18. 2
      app/models/concerns/account_associations.rb
  19. 7
      app/models/form/admin_suspension_confirmation.rb
  20. 4
      app/policies/account_policy.rb
  21. 19
      app/policies/account_warning_preset_policy.rb
  22. 26
      app/views/admin/account_actions/new.html.haml
  23. 6
      app/views/admin/account_warnings/_account_warning.html.haml
  24. 14
      app/views/admin/accounts/show.html.haml
  25. 17
      app/views/admin/reports/show.html.haml
  26. 25
      app/views/admin/suspensions/new.html.haml
  27. 11
      app/views/admin/warning_presets/edit.html.haml
  28. 30
      app/views/admin/warning_presets/index.html.haml
  29. 63
      app/views/user_mailer/warning.html.haml
  30. 9
      app/views/user_mailer/warning.text.erb
  31. 2
      app/views/user_mailer/welcome.text.erb
  32. 5
      config/locales/ar.yml
  33. 2
      config/locales/ast.yml
  34. 6
      config/locales/ca.yml
  35. 6
      config/locales/co.yml
  36. 6
      config/locales/cs.yml
  37. 6
      config/locales/cy.yml
  38. 6
      config/locales/da.yml
  39. 6
      config/locales/de.yml
  40. 6
      config/locales/el.yml
  41. 32
      config/locales/en.yml
  42. 5
      config/locales/eo.yml
  43. 6
      config/locales/es.yml
  44. 6
      config/locales/eu.yml
  45. 6
      config/locales/fa.yml
  46. 6
      config/locales/fr.yml
  47. 6
      config/locales/gl.yml
  48. 6
      config/locales/it.yml
  49. 6
      config/locales/ja.yml
  50. 6
      config/locales/ko.yml
  51. 6
      config/locales/nl.yml
  52. 6
      config/locales/oc.yml
  53. 6
      config/locales/pl.yml
  54. 6
      config/locales/pt-BR.yml
  55. 6
      config/locales/ru.yml
  56. 19
      config/locales/simple_form.en.yml
  57. 6
      config/locales/sk.yml
  58. 6
      config/locales/sr.yml
  59. 16
      config/routes.rb
  60. 12
      db/migrate/20181213184704_create_account_warnings.rb
  61. 9
      db/migrate/20181213185533_create_account_warning_presets.rb
  62. 21
      db/schema.rb
  63. 52
      spec/controllers/admin/accounts_controller_spec.rb
  64. 84
      spec/controllers/admin/reports_controller_spec.rb
  65. 33
      spec/controllers/admin/silences_controller_spec.rb
  66. 39
      spec/controllers/admin/suspensions_controller_spec.rb
  67. 5
      spec/fabricators/account_warning_fabricator.rb
  68. 3
      spec/fabricators/account_warning_preset_fabricator.rb
  69. 5
      spec/mailers/previews/user_mailer_preview.rb
  70. 5
      spec/models/account_warning_preset_spec.rb
  71. 5
      spec/models/account_warning_spec.rb
  72. 4
      spec/models/admin/account_action_spec.rb

@ -0,0 +1,36 @@
# frozen_string_literal: true
module Admin
class AccountActionsController < BaseController
before_action :set_account
def new
@account_action = Admin::AccountAction.new(type: params[:type], report_id: params[:report_id], send_email_notification: true)
@warning_presets = AccountWarningPreset.all
end
def create
account_action = Admin::AccountAction.new(resource_params)
account_action.target_account = @account
account_action.current_account = current_account
account_action.save!
if account_action.with_report?
redirect_to admin_report_path(account_action.report)
else
redirect_to admin_account_path(@account.id)
end
end
private
def set_account
@account = Account.find(params[:account_id])
end
def resource_params
params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification)
end
end
end

@ -14,6 +14,7 @@ module Admin
else else
@account = @account_moderation_note.target_account @account = @account_moderation_note.target_account
@moderation_notes = @account.targeted_moderation_notes.latest @moderation_notes = @account.targeted_moderation_notes.latest
@warnings = @account.targeted_account_warnings.latest.custom
render template: 'admin/accounts/show' render template: 'admin/accounts/show'
end end

@ -2,9 +2,9 @@
module Admin module Admin
class AccountsController < BaseController class AccountsController < BaseController
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :disable, :memorialize] before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :memorialize]
before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload] before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload]
before_action :require_local_account!, only: [:enable, :disable, :memorialize] before_action :require_local_account!, only: [:enable, :memorialize]
def index def index
authorize :account, :index? authorize :account, :index?
@ -13,8 +13,10 @@ module Admin
def show def show
authorize @account, :show? authorize @account, :show?
@account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account)
@moderation_notes = @account.targeted_moderation_notes.latest @moderation_notes = @account.targeted_moderation_notes.latest
@warnings = @account.targeted_account_warnings.latest.custom
end end
def subscribe def subscribe
@ -43,10 +45,17 @@ module Admin
redirect_to admin_account_path(@account.id) redirect_to admin_account_path(@account.id)
end end
def disable def unsilence
authorize @account.user, :disable? authorize @account, :unsilence?
@account.user.disable! @account.unsilence!
log_action :disable, @account.user log_action :unsilence, @account
redirect_to admin_account_path(@account.id)
end
def unsuspend
authorize @account, :unsuspend?
@account.unsuspend!
log_action :unsuspend, @account
redirect_to admin_account_path(@account.id) redirect_to admin_account_path(@account.id)
end end

@ -13,75 +13,42 @@ module Admin
authorize @report, :show? authorize @report, :show?
@report_note = @report.notes.new @report_note = @report.notes.new
@report_notes = (@report.notes.latest + @report.history).sort_by(&:created_at) @report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at)
@form = Form::StatusBatch.new @form = Form::StatusBatch.new
end end
def update def assign_to_self
authorize @report, :update? authorize @report, :update?
process_report @report.update!(assigned_account_id: current_account.id)
log_action :assigned_to_self, @report
if @report.action_taken? redirect_to admin_report_path(@report)
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
else
redirect_to admin_report_path(@report)
end
end end
private def unassign
authorize @report, :update?
def process_report @report.update!(assigned_account_id: nil)
case params[:outcome].to_s log_action :unassigned, @report
when 'assign_to_self' redirect_to admin_report_path(@report)
@report.update!(assigned_account_id: current_account.id)
log_action :assigned_to_self, @report
when 'unassign'
@report.update!(assigned_account_id: nil)
log_action :unassigned, @report
when 'reopen'
@report.unresolve!
log_action :reopen, @report
when 'resolve'
@report.resolve!(current_account)
log_action :resolve, @report
when 'disable'
@report.resolve!(current_account)
@report.target_account.user.disable!
log_action :resolve, @report
log_action :disable, @report.target_account.user
resolve_all_target_account_reports
when 'silence'
@report.resolve!(current_account)
@report.target_account.update!(silenced: true)
log_action :resolve, @report
log_action :silence, @report.target_account
resolve_all_target_account_reports
else
raise ActiveRecord::RecordNotFound
end
@report.reload
end end
def resolve_all_target_account_reports def reopen
unresolved_reports_for_target_account.update_all(action_taken: true, action_taken_by_account_id: current_account.id) authorize @report, :update?
@report.unresolve!
log_action :reopen, @report
redirect_to admin_report_path(@report)
end end
def unresolved_reports_for_target_account def resolve
Report.where( authorize @report, :update?
target_account: @report.target_account @report.resolve!(current_account)
).unresolved log_action :resolve, @report
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
end end
private
def filtered_reports def filtered_reports
ReportFilter.new(filter_params).results.order(id: :desc).includes( ReportFilter.new(filter_params).results.order(id: :desc).includes(:account, :target_account)
:account,
:target_account
)
end end
def filter_params def filter_params

@ -1,27 +0,0 @@
# frozen_string_literal: true
module Admin
class SilencesController < BaseController
before_action :set_account
def create
authorize @account, :silence?
@account.update!(silenced: true)
log_action :silence, @account
redirect_to admin_accounts_path
end
def destroy
authorize @account, :unsilence?
@account.update!(silenced: false)
log_action :unsilence, @account
redirect_to admin_accounts_path
end
private
def set_account
@account = Account.find(params[:account_id])
end
end
end

@ -1,60 +0,0 @@
# frozen_string_literal: true
module Admin
class SuspensionsController < BaseController
before_action :set_account
def new
@suspension = Form::AdminSuspensionConfirmation.new(report_id: params[:report_id])
end
def create
authorize @account, :suspend?
@suspension = Form::AdminSuspensionConfirmation.new(suspension_params)
if suspension_params[:acct] == @account.acct
resolve_report! if suspension_params[:report_id].present?
perform_suspend!
mark_reports_resolved!
redirect_to admin_accounts_path
else
flash.now[:alert] = I18n.t('admin.suspensions.bad_acct_msg')
render :new
end
end
def destroy
authorize @account, :unsuspend?
@account.unsuspend!
log_action :unsuspend, @account
redirect_to admin_accounts_path
end
private
def set_account
@account = Account.find(params[:account_id])
end
def suspension_params
params.require(:form_admin_suspension_confirmation).permit(:acct, :report_id)
end
def resolve_report!
report = Report.find(suspension_params[:report_id])
report.resolve!(current_account)
log_action :resolve, report
end
def perform_suspend!
@account.suspend!
Admin::SuspensionWorker.perform_async(@account.id)
log_action :suspend, @account
end
def mark_reports_resolved!
Report.where(target_account: @account).unresolved.update_all(action_taken: true, action_taken_by_account_id: current_account.id)
end
end
end

@ -0,0 +1,58 @@
# frozen_string_literal: true
module Admin
class WarningPresetsController < BaseController
before_action :set_warning_preset, except: [:index, :create]
def index
authorize :account_warning_preset, :index?
@warning_presets = AccountWarningPreset.all
@warning_preset = AccountWarningPreset.new
end
def create
authorize :account_warning_preset, :create?
@warning_preset = AccountWarningPreset.new(warning_preset_params)
if @warning_preset.save
redirect_to admin_warning_presets_path
else
@warning_presets = AccountWarningPreset.all
render :index
end
end
def edit
authorize @warning_preset, :update?
end
def update
authorize @warning_preset, :update?
if @warning_preset.update(warning_preset_params)
redirect_to admin_warning_presets_path
else
render :edit
end
end
def destroy
authorize @warning_preset, :destroy?
@warning_preset.destroy!
redirect_to admin_warning_presets_path
end
private
def set_warning_preset
@warning_preset = AccountWarningPreset.find(params[:id])
end
def warning_preset_params
params.require(:account_warning_preset).permit(:text)
end
end
end

@ -23,6 +23,8 @@ module Admin::ActionLogsHelper
link_to record.domain, "https://#{record.domain}" link_to record.domain, "https://#{record.domain}"
when 'Status' when 'Status'
link_to record.account.acct, TagManager.instance.url_for(record) link_to record.account.acct, TagManager.instance.url_for(record)
when 'AccountWarning'
link_to record.target_account.acct, admin_account_path(record.target_account_id)
end end
end end
@ -34,6 +36,7 @@ module Admin::ActionLogsHelper
link_to attributes['domain'], "https://#{attributes['domain']}" link_to attributes['domain'], "https://#{attributes['domain']}"
when 'Status' when 'Status'
tmp_status = Status.new(attributes.except('reblogs_count', 'favourites_count')) tmp_status = Status.new(attributes.except('reblogs_count', 'favourites_count'))
if tmp_status.account if tmp_status.account
link_to tmp_status.account&.acct || "##{tmp_status.account_id}", admin_account_path(tmp_status.account_id) link_to tmp_status.account&.acct || "##{tmp_status.account_id}", admin_account_path(tmp_status.account_id)
else else
@ -81,6 +84,8 @@ module Admin::ActionLogsHelper
'envelope' 'envelope'
when 'Status' when 'Status'
'pencil' 'pencil'
when 'AccountWarning'
'warning'
end end
end end
@ -104,6 +109,6 @@ module Admin::ActionLogsHelper
private private
def opposite_verbs?(log) def opposite_verbs?(log)
%w(DomainBlock EmailDomainBlock).include?(log.target_type) %w(DomainBlock EmailDomainBlock AccountWarning).include?(log.target_type)
end end
end end

@ -0,0 +1,4 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/>
</svg>

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

@ -426,6 +426,10 @@ h5 {
background: $success-green; background: $success-green;
} }
&.alert-icon td {
background: $error-red;
}
img { img {
max-width: 32px; max-width: 32px;
width: 32px; width: 32px;

@ -542,6 +542,10 @@ a.name-tag,
border-left-color: lighten($error-red, 12%); border-left-color: lighten($error-red, 12%);
} }
&.warning {
border-left-color: $gold-star;
}
&__bubble { &__bubble {
padding: 16px; padding: 16px;
padding-left: 14px; padding-left: 14px;

@ -78,4 +78,16 @@ class UserMailer < Devise::Mailer
mail to: @resource.email, subject: I18n.t('user_mailer.backup_ready.subject') mail to: @resource.email, subject: I18n.t('user_mailer.backup_ready.subject')
end end
end end
def warning(user, warning)
@resource = user
@warning = warning
@instance = Rails.configuration.x.local_domain
I18n.with_locale(@resource.locale || I18n.default_locale) do
mail to: @resource.email,
subject: I18n.t("user_mailer.warning.subject.#{@warning.action}", acct: "@#{user.account.local_username_and_domain}"),
reply_to: Setting.site_contact_email
end
end
end end

@ -155,6 +155,14 @@ class Account < ApplicationRecord
ResolveAccountService.new.call(acct) ResolveAccountService.new.call(acct)
end end
def silence!
update!(silenced: true)
end
def unsilence!
update!(silenced: false)
end
def suspend! def suspend!
transaction do transaction do
user&.disable! if local? user&.disable! if local?

@ -0,0 +1,23 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: account_warnings
#
# id :bigint(8) not null, primary key
# account_id :bigint(8)
# target_account_id :bigint(8)
# action :integer default("none"), not null
# text :text default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class AccountWarning < ApplicationRecord
enum action: %i(none disable silence suspend), _suffix: :action
belongs_to :account, inverse_of: :account_warnings
belongs_to :target_account, class_name: 'Account', inverse_of: :targeted_account_warnings
scope :latest, -> { order(created_at: :desc) }
scope :custom, -> { where.not(text: '') }
end

@ -0,0 +1,15 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: account_warning_presets
#
# id :bigint(8) not null, primary key
# text :text default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class AccountWarningPreset < ApplicationRecord
validates :text, presence: true
end

@ -0,0 +1,134 @@
# frozen_string_literal: true
class Admin::AccountAction
include ActiveModel::Model
include AccountableConcern
include Authorization
TYPES = %w(
none
disable
silence
suspend
).freeze
attr_accessor :target_account,
:current_account,
:type,
:text,
:report_id,
:warning_preset_id,
:send_email_notification
attr_reader :warning
def save!
ApplicationRecord.transaction do
process_action!
process_warning!
end
queue_email!
process_reports!
end
def report
@report ||= Report.find(report_id) if report_id.present?
end
def with_report?
!report.nil?
end
class << self
def types_for_account(account)
if account.local?
TYPES
else
TYPES - %w(none disable)
end
end
end
private
def process_action!
case type
when 'disable'
handle_disable!
when 'silence'
handle_silence!
when 'suspend'
handle_suspend!
end
end
def process_warning!
return unless warnable?
authorize(target_account, :warn?)
@warning = AccountWarning.create!(target_account: target_account,
account: current_account,
action: type,
text: text_for_warning)
# A log entry is only interesting if the warning contains
# custom text from someone. Otherwise it's just noise.
log_action(:create, warning) if warning.text.present?
end
def process_reports!
return if report_id.blank?
authorize(report, :update?)
if type == 'none'
log_action(:resolve, report)
report.resolve!(current_account)
else
Report.where(target_account: target_account).unresolved.update_all(action_taken: true, action_taken_by_account_id: current_account.id)
end
end
def handle_disable!
authorize(target_account.user, :disable?)
log_action(:disable, target_account.user)
target_account.user&.disable!
end
def handle_silence!
authorize(target_account, :silence?)
log_action(:silence, target_account)
target_account.silence!
end
def handle_suspend!
authorize(target_account, :suspend?)
log_action(:suspend, target_account)
target_account.suspend!
queue_suspension_worker!
end
def text_for_warning
[warning_preset&.text, text].compact.join("\n\n")
end
def queue_suspension_worker!
Admin::SuspensionWorker.perform_async(target_account.id)
end
def queue_email!
return unless warnable?
UserMailer.warning(target_account.user, warning).deliver_later!
end
def warnable?
send_email_notification && target_account.local?
end
def warning_preset
@warning_preset ||= AccountWarningPreset.find(warning_preset_id) if warning_preset_id.present?
end
end

@ -39,6 +39,8 @@ module AccountAssociations
# Moderation notes # Moderation notes
has_many :account_moderation_notes, dependent: :destroy, inverse_of: :account has_many :account_moderation_notes, dependent: :destroy, inverse_of: :account
has_many :targeted_moderation_notes, class_name: 'AccountModerationNote', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account has_many :targeted_moderation_notes, class_name: 'AccountModerationNote', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account
has_many :account_warnings, dependent: :destroy, inverse_of: :account
has_many :targeted_account_warnings, class_name: 'AccountWarning', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account
# Lists (that the account is on, not owned by the account) # Lists (that the account is on, not owned by the account)
has_many :list_accounts, inverse_of: :account, dependent: :destroy has_many :list_accounts, inverse_of: :account, dependent: :destroy

@ -1,7 +0,0 @@
# frozen_string_literal: true
class Form::AdminSuspensionConfirmation
include ActiveModel::Model
attr_accessor :acct, :report_id
end

@ -9,6 +9,10 @@ class AccountPolicy < ApplicationPolicy
staff? staff?
end end
def warn?
staff? && !record.user&.staff?
end
def suspend? def suspend?
staff? && !record.user&.staff? staff? && !record.user&.staff?
end end

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AccountWarningPresetPolicy < ApplicationPolicy
def index?
staff?
end
def create?
staff?
end
def update?
staff?
end
def destroy?
staff?
end
end

@ -0,0 +1,26 @@
- content_for :page_title do
= t('admin.account_actions.title', acct: @account.acct)
= simple_form_for @account_action, url: admin_account_action_path(@account.id) do |f|
= f.input :report_id, as: :hidden
.fields-group
= f.input :type, collection: Admin::AccountAction.types_for_account(@account), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { I18n.t("simple_form.labels.admin_account_action.types.#{type}")}, hint: t('simple_form.hints.admin_account_action.type_html', acct: @account.acct)
- if @account.local?
%hr.spacer/
.fields-group
= f.input :send_email_notification, as: :boolean, wrapper: :with_label
%hr.spacer/
- unless @warning_presets.empty?
.fields-group
= f.input :warning_preset_id, collection: @warning_presets, label_method: :text, wrapper: :with_block_label
.fields-group
= f.input :text, as: :text, wrapper: :with_block_label, hint: t('simple_form.hints.admin_account_action.text_html', path: admin_warning_presets_path)
.actions
= f.button :button, t('admin.account_actions.action'), type: :submit

@ -0,0 +1,6 @@
.speech-bubble.warning
.speech-bubble__bubble
= Formatter.instance.linkify(account_warning.text)
.speech-bubble__owner
= admin_account_link_to account_warning.account
%time.formatted{ datetime: account_warning.created_at.iso8601 }= l account_warning.created_at

@ -64,7 +64,7 @@
= table_link_to 'unlock', t('admin.accounts.enable'), enable_admin_account_path(@account.id), method: :post if can?(:enable, @account.user) = table_link_to 'unlock', t('admin.accounts.enable'), enable_admin_account_path(@account.id), method: :post if can?(:enable, @account.user)
- else - else
= t('admin.accounts.enabled') = t('admin.accounts.enabled')
= table_link_to 'lock', t('admin.accounts.disable'), disable_admin_account_path(@account.id), method: :post if can?(:disable, @account.user) = table_link_to 'lock', t('admin.accounts.disable'), new_admin_account_action_path(@account.id, type: 'disable') if can?(:disable, @account.user)
%tr %tr
%th= t('admin.accounts.most_recent_ip') %th= t('admin.accounts.most_recent_ip')
%td= @account.user_current_sign_in_ip %td= @account.user_current_sign_in_ip
@ -119,18 +119,18 @@
%div{ style: 'float: left' } %div{ style: 'float: left' }
- if @account.silenced? - if @account.silenced?
= link_to t('admin.accounts.undo_silenced'), admin_account_silence_path(@account.id), method: :delete, class: 'button' if can?(:unsilence, @account) = link_to t('admin.accounts.undo_silenced'), unsilence_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsilence, @account)
- else - else
= link_to t('admin.accounts.silence'), admin_account_silence_path(@account.id), method: :post, class: 'button button--destructive' if can?(:silence, @account) = link_to t('admin.accounts.silence'), new_admin_account_action_path(@account.id, type: 'silence'), class: 'button button--destructive' if can?(:silence, @account)
- if @account.local? - if @account.local?
- unless @account.user_confirmed? - unless @account.user_confirmed?
= link_to t('admin.accounts.confirm'), admin_account_confirmation_path(@account.id), method: :post, class: 'button' if can?(:confirm, @account.user) = link_to t('admin.accounts.confirm'), admin_account_confirmation_path(@account.id), method: :post, class: 'button' if can?(:confirm, @account.user)
- if @account.suspended? - if @account.suspended?
= link_to t('admin.accounts.undo_suspension'), admin_account_suspension_path(@account.id), method: :delete, class: 'button' if can?(:unsuspend, @account) = link_to t('admin.accounts.undo_suspension'), unsuspend_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsuspend, @account)
- else - else
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_suspension_path(@account.id), class: 'button button--destructive' if can?(:suspend, @account) = link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@account.id, type: 'suspend'), class: 'button button--destructive' if can?(:suspend, @account)
- if !@account.local? && @account.hub_url.present? - if !@account.local? && @account.hub_url.present?
%hr.spacer/ %hr.spacer/
@ -184,6 +184,10 @@
%hr.spacer/ %hr.spacer/
= render @warnings
%hr.spacer/
= render @moderation_notes = render @moderation_notes
= simple_form_for @account_moderation_note, url: admin_account_moderation_notes_path do |f| = simple_form_for @account_moderation_note, url: admin_account_moderation_notes_path do |f|

@ -8,13 +8,14 @@
- if @report.unresolved? - if @report.unresolved?
%div{ style: 'float: right' } %div{ style: 'float: right' }
- if @report.target_account.local? - if @report.target_account.local?
= link_to t('admin.accounts.disable'), admin_report_path(@report, outcome: 'disable'), method: :put, class: 'button button--destructive' = link_to t('admin.accounts.warn'), new_admin_account_action_path(@report.target_account_id, type: 'none', report_id: @report.id), class: 'button'
= link_to t('admin.accounts.silence'), admin_report_path(@report, outcome: 'silence'), method: :put, class: 'button button--destructive' = link_to t('admin.accounts.disable'), new_admin_account_action_path(@report.target_account_id, type: 'disable', report_id: @report.id), class: 'button button--destructive'
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_suspension_path(@report.target_account_id, report_id: @report.id), class: 'button button--destructive' = link_to t('admin.accounts.silence'), new_admin_account_action_path(@report.target_account_id, type: 'silence', report_id: @report.id), class: 'button button--destructive'
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@report.target_account_id, type: 'suspend', report_id: @report.id), class: 'button button--destructive'
%div{ style: 'float: left' } %div{ style: 'float: left' }
= link_to t('admin.reports.mark_as_resolved'), admin_report_path(@report, outcome: 'resolve'), method: :put, class: 'button' = link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(@report), method: :post, class: 'button'
- else - else
= link_to t('admin.reports.mark_as_unresolved'), admin_report_path(@report, outcome: 'reopen'), method: :put, class: 'button' = link_to t('admin.reports.mark_as_unresolved'), reopen_admin_report_path(@report), method: :post, class: 'button'
%hr.spacer %hr.spacer
@ -67,10 +68,10 @@
= admin_account_link_to @report.assigned_account = admin_account_link_to @report.assigned_account
%td %td
- if @report.assigned_account != current_user.account - if @report.assigned_account != current_user.account
= table_link_to 'user', t('admin.reports.assign_to_self'), admin_report_path(@report, outcome: 'assign_to_self'), method: :put = table_link_to 'user', t('admin.reports.assign_to_self'), assign_to_self_admin_report_path(@report), method: :post
%td %td
- if !@report.assigned_account.nil? - if !@report.assigned_account.nil?
= table_link_to 'trash', t('admin.reports.unassign'), admin_report_path(@report, outcome: 'unassign'), method: :put = table_link_to 'trash', t('admin.reports.unassign'), unassign_admin_report_path(@report), method: :post
%hr.spacer %hr.spacer
@ -104,7 +105,7 @@
- @report_notes.each do |item| - @report_notes.each do |item|
- if item.is_a?(Admin::ActionLog) - if item.is_a?(Admin::ActionLog)
= render partial: 'action_log', locals: { action_log: item } = render partial: 'action_log', locals: { action_log: item }
- elsif item.is_a?(ReportNote) - else
= render item = render item
= simple_form_for @report_note, url: admin_report_notes_path do |f| = simple_form_for @report_note, url: admin_report_notes_path do |f|

@ -1,25 +0,0 @@
- content_for :page_title do
= t('admin.suspensions.title', acct: @account.acct)
= simple_form_for @suspension, url: admin_account_suspension_path(@account.id), method: :post do |f|
%p.hint= t('admin.suspensions.warning_html')
.fields-group
%ul
%li.negative-hint
= number_to_human @account.statuses_count, strip_insignificant_zeros: true
= t('accounts.posts', count: @account.statuses_count)
%li.negative-hint
= number_to_human @account.following_count, strip_insignificant_zeros: true
= t('accounts.following', count: @account.following_count)
%li.negative-hint
= number_to_human @account.followers_count, strip_insignificant_zeros: true
= t('accounts.followers', count: @account.followers_count)
%p.hint= t('admin.suspensions.hint_html', value: content_tag(:code, @account.acct))
= f.input :acct
= f.input_field :report_id, as: :hidden
.actions
= f.button :button, t('admin.suspensions.proceed'), type: :submit, class: 'negative'

@ -0,0 +1,11 @@
- content_for :page_title do
= t('admin.warning_presets.edit_preset')
= simple_form_for @warning_preset, url: admin_warning_preset_path(@warning_preset) do |f|
= render 'shared/error_messages', object: @warning_preset
.fields-group
= f.input :text, wrapper: :with_block_label
.actions
= f.button :button, t('generic.save_changes'), type: :submit

@ -0,0 +1,30 @@
- content_for :page_title do
= t('admin.warning_presets.title')
- if can? :create, :account_warning_preset
= simple_form_for @warning_preset, url: admin_warning_presets_path do |f|
= render 'shared/error_messages', object: @warning_preset
.fields-group
= f.input :text, wrapper: :with_block_label
.actions
= f.button :button, t('admin.warning_presets.add_new'), type: :submit
%hr.spacer/
- unless @warning_presets.empty?
.table-wrapper
%table.table
%thead
%tr
%th= t('simple_form.labels.account_warning_preset.text')
%th
%tbody
- @warning_presets.each do |preset|
%tr
%td
= Formatter.instance.linkify(preset.text)
%td
= table_link_to 'pencil', t('admin.warning_presets.edit'), edit_admin_warning_preset_path(preset)
= table_link_to 'trash', t('admin.warning_presets.delete'), admin_warning_preset_path(preset), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }

@ -0,0 +1,63 @@
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.hero
.email-row
.col-6
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.text-center.padded
%table.hero-icon.alert-icon{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td
= image_tag full_pack_url('icon_warning.png'), alt: ''
%h1= t "user_mailer.warning.title.#{@warning.action}"
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.content-start
.email-row
.col-6
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.text-center
- unless @warning.none_action?
%p= t "user_mailer.warning.explanation.#{@warning.action}"
- unless @warning.text.blank?
= Formatter.instance.linkify(@warning.text)
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.button-cell
%table.button{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.button-primary
= link_to about_more_url do
%span= t 'user_mailer.warning.review_server_policies'

@ -0,0 +1,9 @@
<%= t "user_mailer.warning.title.#{@warning.action}" %>
===
<% unless @warning.none_action? %>
<%= t "user_mailer.warning.explanation.#{@warning.action}" %>
<% end %>
<%= @warning.text %>

@ -2,7 +2,7 @@
=== ===
<%= t 'user_mailer.welcome.full_handle' %> (<%= "@#{@resource.account.username}@#{@instance}" %>) <%= t 'user_mailer.welcome.full_handle' %> (<%= "@#{@resource.account.local_username_and_domain}" %>)
<%= t 'user_mailer.welcome.full_handle_hint', instance: @instance %> <%= t 'user_mailer.welcome.full_handle_hint', instance: @instance %>
--- ---

@ -455,11 +455,6 @@ ar:
last_delivery: آخر إيداع last_delivery: آخر إيداع
title: WebSub title: WebSub
topic: الموضوع topic: الموضوع
suspensions:
bad_acct_msg: قيمة التأكيد غير متطابقة. متأكد مِن أنك بصدد تعليق الحساب الصحيح؟
hint_html: 'لتأكيد إجراء تعليق الحساب، يُرجى إدخال %{value} في الحقل التالي:'
proceed: مواصلة
title: تعليق الحساب %{acct}
tags: tags:
accounts: الحسابات accounts: الحسابات
hidden: المخفية hidden: المخفية

@ -121,8 +121,6 @@ ast:
failed_to_execute: Fallu al executar failed_to_execute: Fallu al executar
subscriptions: subscriptions:
title: WebSub title: WebSub
suspensions:
warning_html: 'El suspender esta cuenta va desaniciar <strong>de mou irreversible</strong> los sos datos qu''inclúin:'
title: Alministración title: Alministración
admin_mailer: admin_mailer:
new_report: new_report:

@ -439,12 +439,6 @@ ca:
last_delivery: Últim lliurament last_delivery: Últim lliurament
title: WebSub title: WebSub
topic: Tema topic: Tema
suspensions:
bad_acct_msg: El valor de confirmació no s'ha trobat. Estàs suspenen el compte correcte?
hint_html: 'Per confirmar la suspensió del compte, introdueix %{value} al camp següent:'
proceed: Procedeix
title: Suspèn %{acct}
warning_html: 'Suspenen aquest compte esborrarà <strong>irreversiblement</strong> les dades del compte, incloent:'
tags: tags:
accounts: Comptes accounts: Comptes
hidden: Amagat hidden: Amagat

@ -439,12 +439,6 @@ co:
last_delivery: Ultima arricata last_delivery: Ultima arricata
title: WebSub title: WebSub
topic: Sughjettu topic: Sughjettu
suspensions:
bad_acct_msg: U valore di cunfirmazione ùn era micca curretta. Site sicuru·a di suspende u bonu contu?
hint_html: 'Per cunfirmà a suspensione di u contu, entrate %{value} quì sottu:'
proceed: Cuntinuà
title: Suspende %{acct}
warning_html: 'A suspensione di u contu sguasserà di manera <strong>irreversibile</strong> i so dati, cum''è:'
tags: tags:
accounts: Conti accounts: Conti
hidden: Piattatu hidden: Piattatu

@ -444,12 +444,6 @@ cs:
last_delivery: Poslední doručení last_delivery: Poslední doručení
title: WebSub title: WebSub
topic: Téma topic: Téma
suspensions:
bad_acct_msg: Hodnota pro potvrzení neodpovídá. Suspendujete správný účet?
hint_html: 'Pro potvrzení suspenzace účtu prosím zadejte do pole níže %{value}:'
proceed: Pokračovat
title: Suspendovat účet %{acct}
warning_html: 'Suspenzace tohoto účtu <strong>nenávratně</strong> smaže z tohoto účtu data, včetně:'
tags: tags:
accounts: Účty accounts: Účty
hidden: Skryté hidden: Skryté

@ -423,12 +423,6 @@ cy:
last_delivery: Danfoniad diwethaf last_delivery: Danfoniad diwethaf
title: WebSub title: WebSub
topic: Pwnc topic: Pwnc
suspensions:
bad_acct_msg: Nid yw'r gwerthoedd cadarnhau yn cyfateb. Ydych chi'n atal y cyfrif cywir?
hint_html: 'I gadarnhau atal y cyfrif, mewnbynwch %{value} yn y maes isod:'
proceed: Parhau
title: Atal %{acct}
warning_html: 'Mi fydd atal y cyfrif hwn yn dileu data <strong>am byth</strong> o''r cyfrif hwn, gan gynnwys:'
title: Gweinyddiaeth title: Gweinyddiaeth
admin_mailer: admin_mailer:
new_report: new_report:

@ -427,12 +427,6 @@ da:
last_delivery: Sidste levering last_delivery: Sidste levering
title: Websub title: Websub
topic: Emne topic: Emne
suspensions:
bad_acct_msg: Bekræftelsværdien stemte ikke overens. Er du ved at udelukke den rigtige konto?
hint_html: 'For at bekræfte udelukkelsen af kontoen, indtast venligst %{value} i nedenstående felt:'
proceed: Fortsæt
title: Udeluk %{acct}
warning_html: 'Udelukkelse af denne konto vil <strong>uigenkaldeligt</strong> slette al data fra denne konto, hvilket indebærer:'
title: Administration title: Administration
admin_mailer: admin_mailer:
new_report: new_report:

@ -439,12 +439,6 @@ de:
last_delivery: Letzte Zustellung last_delivery: Letzte Zustellung
title: WebSub title: WebSub
topic: Thema topic: Thema
suspensions:
bad_acct_msg: Der Bestätigungswert stimmt nicht überein. Sperrst du das richtige Benutzerkonto?
hint_html: 'Um die Sperrung des Benutzerkontos zu genehmigen tippe %{value} in das Feld unten ein:'
proceed: Fortfahren
title: "%{acct} sperren"
warning_html: 'Die Sperrung des Benutzerkontos wird <strong>unwiederrufliche</strong> Schäden hervorrufen und alle Daten löschen, die folgendes beinhalten:'
tags: tags:
accounts: Konten accounts: Konten
hidden: Versteckt hidden: Versteckt

@ -439,12 +439,6 @@ el:
last_delivery: Τελευταία παράδοση last_delivery: Τελευταία παράδοση
title: WebSub title: WebSub
topic: Θέμα topic: Θέμα
suspensions:
bad_acct_msg: Η τιμή επιβεβαίωσης δεν ταιριάζει. Σίγουρα αναστέλλεις το σωστό λογαριασμό;
hint_html: 'Για να επιβεβαιώσεις την αναστολή του λογαριασμού, γράψε %{value} στο ακόλουθο πεδίο:'
proceed: Συνέχεια
title: Αναστολή %{acct}
warning_html: 'Αναστέλλοντας αυτό το λογαριασμό θα διαγραφούν <strong>αμετάκλητα</strong> δεδομένα του, μεταξύ των οποίων:'
tags: tags:
accounts: Λογαριασμοί accounts: Λογαριασμοί
hidden: Κρυμμένες hidden: Κρυμμένες

@ -70,6 +70,9 @@ en:
moderator: Mod moderator: Mod
unfollow: Unfollow unfollow: Unfollow
admin: admin:
account_actions:
action: Perform action
title: Perform moderation action on %{acct}
account_moderation_notes: account_moderation_notes:
create: Leave note create: Leave note
created_msg: Moderation note successfully created! created_msg: Moderation note successfully created!
@ -173,6 +176,7 @@ en:
assigned_to_self_report: "%{name} assigned report %{target} to themselves" assigned_to_self_report: "%{name} assigned report %{target} to themselves"
change_email_user: "%{name} changed the e-mail address of user %{target}" change_email_user: "%{name} changed the e-mail address of user %{target}"
confirm_user: "%{name} confirmed e-mail address of user %{target}" confirm_user: "%{name} confirmed e-mail address of user %{target}"
create_account_warning: "%{name} sent a warning to %{target}"
create_custom_emoji: "%{name} uploaded new emoji %{target}" create_custom_emoji: "%{name} uploaded new emoji %{target}"
create_domain_block: "%{name} blocked domain %{target}" create_domain_block: "%{name} blocked domain %{target}"
create_email_domain_block: "%{name} blacklisted e-mail domain %{target}" create_email_domain_block: "%{name} blacklisted e-mail domain %{target}"
@ -441,12 +445,6 @@ en:
last_delivery: Last delivery last_delivery: Last delivery
title: WebSub title: WebSub
topic: Topic topic: Topic
suspensions:
bad_acct_msg: The confirmation value didn't match up. Are you suspending the right account?
hint_html: 'To confirm the suspension of the account, please enter %{value} into the field below:'
proceed: Proceed
title: Suspend %{acct}
warning_html: 'Suspending this account will <strong>irreversibly</strong> delete data from this account, which includes:'
tags: tags:
accounts: Accounts accounts: Accounts
hidden: Hidden hidden: Hidden
@ -456,6 +454,12 @@ en:
unhide: Show in directory unhide: Show in directory
visible: Visible visible: Visible
title: Administration title: Administration
warning_presets:
add_new: Add new
delete: Delete
edit: Edit
edit_preset: Edit warning preset
title: Manage warning presets
admin_mailer: admin_mailer:
new_report: new_report:
body: "%{reporter} has reported %{target}" body: "%{reporter} has reported %{target}"
@ -922,6 +926,22 @@ en:
explanation: You requested a full backup of your Mastodon account. It's now ready for download! explanation: You requested a full backup of your Mastodon account. It's now ready for download!
subject: Your archive is ready for download subject: Your archive is ready for download
title: Archive takeout title: Archive takeout
warning:
explanation:
disable: While your account is frozen, your account data remains intact, but you cannot perform any actions until it is unlocked.
silence: While your account is limited, only people who are already following you will see your toots on this server, and you may be excluded from various public listings. However, others may still manually follow you.
suspend: Your account has been suspended, and all of your toots and your uploaded media files have been irreversibly removed from this server, and servers where you had followers.
review_server_policies: Review server policies
subject:
disable: Your account %{acct} has been frozen
none: Warning for %{acct}
silence: Your account %{acct} has been limited
suspend: Your account %{acct} has been suspended
title:
disable: Account frozen
none: Warning
silence: Account limited
suspend: Account suspended
welcome: welcome:
edit_profile_action: Setup profile edit_profile_action: Setup profile
edit_profile_step: You can customize your profile by uploading an avatar, header, changing your display name and more. If you’d like to review new followers before they’re allowed to follow you, you can lock your account. edit_profile_step: You can customize your profile by uploading an avatar, header, changing your display name and more. If you’d like to review new followers before they’re allowed to follow you, you can lock your account.

@ -427,11 +427,6 @@ eo:
last_delivery: Lasta livero last_delivery: Lasta livero
title: WebSub title: WebSub
topic: Temo topic: Temo
suspensions:
hint_html: 'Por konformi la haltigo de la konto, bonvolu enigi %{value} en la kampo sube:'
proceed: Daŭrigita
title: Haltigi %{acct}
warning_html: 'Haltigi ĉi tiu konton forigos <strong>senrevene</strong> datumojn de ĉi tiu konto, inklusive de:'
title: Administrado title: Administrado
admin_mailer: admin_mailer:
new_report: new_report:

@ -433,12 +433,6 @@ es:
last_delivery: Última entrega last_delivery: Última entrega
title: WebSub title: WebSub
topic: Tópico topic: Tópico
suspensions:
bad_acct_msg: El valor de confirmación no cuadra. ¿Estás suspendiendo la cuenta correcta?
hint_html: 'Para confirmar las suspensión de la cuenta, por favor introduce %{value} en el campo de abajo:'
proceed: Proceder
title: Suspender %{acct}
warning_html: 'Suspender esta cuenta borrará <strong>irreversiblemente</strong> los datos de stra cuenta que incluyen:'
title: Administración title: Administración
admin_mailer: admin_mailer:
new_report: new_report:

@ -435,12 +435,6 @@ eu:
last_delivery: Azken bidalketa last_delivery: Azken bidalketa
title: WebSub title: WebSub
topic: Mintzagaia topic: Mintzagaia
suspensions:
bad_acct_msg: Berrespen balioa ez dator bat. Dagokion kontua kanporatzen ari zara?
hint_html: 'Kontuaren kanporatzea berresteko, sartu %{value} beheko eremuan:'
proceed: Jarraitu
title: Kanporatu %{acct}
warning_html: 'Kontu hau kanporatzeak <strong>behin betiko</strong> ezabatuko ditu kontu honetako datuak, hauek barne:'
tags: tags:
accounts: Kontuak accounts: Kontuak
hidden: Ezkutatuta hidden: Ezkutatuta

@ -433,12 +433,6 @@ fa:
last_delivery: آخرین ارسال last_delivery: آخرین ارسال
title: WebSub title: WebSub
topic: موضوع topic: موضوع
suspensions:
bad_acct_msg: محتوایی که برای تأیید وارد کردید منطبق نبود. آیا دارید حساب درستی را معلق میکنید؟
hint_html: 'برای تأیید معلقکردن حساب، لطفاً در کادر زیر %{value} را وارد کنید:'
proceed: ادامه
title: معلقکردن %{acct}
warning_html: 'معلقکردن این حساب <strong>برای همیشه</strong> دادههایش را پاک میکند. دادههایی شامل:'
title: مدیریت سرور title: مدیریت سرور
admin_mailer: admin_mailer:
new_report: new_report:

@ -439,12 +439,6 @@ fr:
last_delivery: Dernière livraison last_delivery: Dernière livraison
title: WebSub title: WebSub
topic: Sujet topic: Sujet
suspensions:
bad_acct_msg: La valeur de confirmation n'a pas correspondu. Êtes-vous certain de suspendre le bon compte ?
hint_html: 'Pour confirmer la suspension du compte, veuillez entrer %{value} dans le champ ci-dessous :'
proceed: Confirmer
title: Suspension de %{acct}
warning_html: 'Suspendre ce compte effacera <strong>irréversiblement</strong> les données de ce compte, ce qui inclut :'
tags: tags:
accounts: Comptes accounts: Comptes
hidden: Masqué hidden: Masqué

@ -439,12 +439,6 @@ gl:
last_delivery: Última entrega last_delivery: Última entrega
title: WebSub title: WebSub
topic: Asunto topic: Asunto
suspensions:
bad_acct_msg: O valor de confirmación non é coincidente. Está a suspender a conta correcta?
hint_html: 'Para confirmar a suspensión da conta introduza %{value} no campo inferior:'
proceed: Proceder
title: Suspender %{acct}
warning_html: 'Ao suspender esta conta eliminará <strong>de xeito irreversible</strong> os datos de esta conta, que inclúe:'
tags: tags:
accounts: Contas accounts: Contas
hidden: Ocultas hidden: Ocultas

@ -429,12 +429,6 @@ it:
confirmed: Confermato confirmed: Confermato
expires_in: Scade in expires_in: Scade in
topic: Argomento topic: Argomento
suspensions:
bad_acct_msg: Il valore di conferma non corrisponde. Stai sospendendo l'account giusto?
hint_html: 'Per confermare la sospensione dell''account, inserisci %{value} nel campo qui sotto:'
proceed: Continua
title: Sospendi %{acct}
warning_html: 'La sospensione dell''account comporta la cancellazione <strong>irreversibile</strong> dei suoi dati, che comprendono:'
title: Amministrazione title: Amministrazione
application_mailer: application_mailer:
notification_preferences: Cambia preferenze email notification_preferences: Cambia preferenze email

@ -441,12 +441,6 @@ ja:
last_delivery: 最終配送 last_delivery: 最終配送
title: WebSub title: WebSub
topic: トピック topic: トピック
suspensions:
bad_acct_msg: 値が一致しませんでした。停止しようとしているアカウントに間違いはありませんか?
hint_html: 'アカウントの停止を確認するには、以下のフィールドに %{value} と入力してください:'
proceed: 完全に活動停止させる
title: "%{acct} を停止"
warning_html: 'このアカウントを停止すると、このアカウントから次のようなデータが<strong>不可逆的に</strong>削除されます:'
tags: tags:
accounts: アカウント accounts: アカウント
hidden: 非表示 hidden: 非表示

@ -441,12 +441,6 @@ ko:
last_delivery: 최종 발송 last_delivery: 최종 발송
title: WebSub title: WebSub
topic: 토픽 topic: 토픽
suspensions:
bad_acct_msg: 확인값이 일치하지 않습니다. 정지하려는 계정이 맞습니까?
hint_html: '이 계정을 정지하려면 %{value}를 아래 입력칸에 입력하세요:'
proceed: 완전히 정지시키기
title: "%{acct} 정지하기"
warning_html: '이 계정을 정지하면 계정의 데이터를 모두 삭제하며 <strong>되돌릴 수 없습니다</strong>. 이것은 다음을 포함합니다:'
tags: tags:
accounts: 계정들 accounts: 계정들
hidden: 숨겨짐 hidden: 숨겨짐

@ -439,12 +439,6 @@ nl:
last_delivery: Laatste bezorging last_delivery: Laatste bezorging
title: WebSub title: WebSub
topic: Account topic: Account
suspensions:
bad_acct_msg: De bevestigingswaarde kwam niet overeen. Schort je wel het juiste account op?
hint_html: Vul in het veld hieronder %{value} in, om het opschorten van dit account te bevestigen.
proceed: Ga verder
title: "%{acct} opschorten"
warning_html: 'Door het opschorten van dit account worden gegevens van dit account <strong>permanent</strong> verwijderd, waaronder:'
tags: tags:
accounts: Accounts accounts: Accounts
hidden: Verborgen hidden: Verborgen

@ -439,12 +439,6 @@ oc:
last_delivery: Darrièra distribucion last_delivery: Darrièra distribucion
title: WebSub title: WebSub
topic: Subjècte topic: Subjècte
suspensions:
bad_acct_msg: La valor de confirmacion a pas coïncidit. Sètz a suspendre lo bon compte?
hint_html: 'Per confirmar la suspension del compte, picatz %{value} al camp çai-jos :'
proceed: Tractat
title: Suspension de %{acct}
warning_html: 'Suspendre aqueste compte suprimirà <strong>irreversiblament</strong> las donadas del compte, aquò compren:'
tags: tags:
accounts: Comptes accounts: Comptes
hidden: Amagat hidden: Amagat

@ -445,12 +445,6 @@ pl:
last_delivery: Ostatnio doręczono last_delivery: Ostatnio doręczono
title: WebSub title: WebSub
topic: Temat topic: Temat
suspensions:
bad_acct_msg: Zawartość potwierdzenia nie zgadza się. Czy próbujesz zawiesić właściwe konto?
hint_html: 'Aby potwierdzić zawieszenie konta, wprowadź %{value} w poniższe pole:'
proceed: Przejdź
title: Zawieś %{acct}
warning_html: 'Zawieszenie konta będzie skutkowało <strong>nieodwracalnym</strong> usunięciem danych z tego konta, wliczając:'
tags: tags:
accounts: Konta accounts: Konta
hidden: Ukryte hidden: Ukryte

@ -439,12 +439,6 @@ pt-BR:
last_delivery: Última entrega last_delivery: Última entrega
title: WebSub title: WebSub
topic: Tópico topic: Tópico
suspensions:
bad_acct_msg: Os valores de confirmação não correspondem. Você está suspendendo a conta certa?
hint_html: 'Para confirmar a suspensão da conta, por favor digite %{value} no campo abaixo:'
proceed: Prosseguir
title: Suspender %{acct}
warning_html: 'Suspender essa conta vai remover <strong>irreversivelmente</strong> dados dessa conta, o que inclui:'
tags: tags:
accounts: Contas accounts: Contas
hidden: Escondido hidden: Escondido

@ -427,12 +427,6 @@ ru:
last_delivery: Последняя доставка last_delivery: Последняя доставка
title: WebSub title: WebSub
topic: Тема topic: Тема
suspensions:
bad_acct_msg: Не удалось найти такое число подтверждения. Вы уверены, что замораживаете нужный аккаунт?
hint_html: 'Чтобы подтвердить заморозку аккаунта, пожалуйста, введите %{value} в поле ниже:'
proceed: Продолжить
title: Заморозить %{acct}
warning_html: 'Заморозка этого аккаунта приведёт к <strong>необратимому</strong> удалению данных с этого аккаунта, включая:'
title: Администрирование title: Администрирование
admin_mailer: admin_mailer:
new_report: new_report:

@ -2,6 +2,13 @@
en: en:
simple_form: simple_form:
hints: hints:
account_warning_preset:
text: You can use toot syntax, such as URLs, hashtags and mentions
admin_account_action:
send_email_notification: The user will receive an explanation of what happened with their account
text_html: Optional. You can use toot syntax. You can <a href="%{path}">add warning presets</a> to save time
type_html: Choose what to do with <strong>%{acct}</strong>
warning_preset_id: Optional. You can still add custom text to end of the preset
defaults: defaults:
autofollow: People who sign up through the invite will automatically follow you autofollow: People who sign up through the invite will automatically follow you
avatar: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px avatar: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px
@ -40,6 +47,18 @@ en:
fields: fields:
name: Label name: Label
value: Content value: Content
account_warning_preset:
text: Preset text
admin_account_action:
send_email_notification: Notify the user per e-mail
text: Custom warning
type: Action
types:
disable: Disable
none: Do nothing
silence: Silence
suspend: Suspend and irreversibly delete account data
warning_preset_id: Use a warning preset
defaults: defaults:
autofollow: Invite to follow your account autofollow: Invite to follow your account
avatar: Avatar avatar: Avatar

@ -444,12 +444,6 @@ sk:
last_delivery: Posledné doručenie last_delivery: Posledné doručenie
title: WebSub title: WebSub
topic: Téma topic: Téma
suspensions:
bad_acct_msg: Hodnota pre potvrdenie sa nezhoduje. Si si istý/á že zamrazuješ ten správny účet?
hint_html: 'Pre potvrdenie zamrazenia účtu, prosím napíš %{value} do následujúceho políčka:'
proceed: Pokračuj
title: Zamraziť %{acct}
warning_html: 'Zamrazením tohto účtu budú dáta na tomto účte <strong>nenávratne</strong> zmazané, zahŕňajúc:'
tags: tags:
accounts: Účty accounts: Účty
hidden: Skryté hidden: Skryté

@ -443,12 +443,6 @@ sr:
last_delivery: Последња достава last_delivery: Последња достава
title: WebSub title: WebSub
topic: Topic topic: Topic
suspensions:
bad_acct_msg: Вредност потврде се не поклапа. Да ли суспендујете прави рачун?
hint_html: 'Да бисте потврдили суспензију налога, унесите %{value} у поље испод:'
proceed: Настави
title: Суспендуј %{acct}
warning_html: 'Суспендовање овог налога ће <strong>неповратно</strong>избрисати све податке са овог налога, који укључују:'
title: Администрација title: Администрација
admin_mailer: admin_mailer:
new_report: new_report:

@ -139,6 +139,7 @@ Rails.application.routes.draw do
resources :domain_blocks, only: [:index, :new, :create, :show, :destroy] resources :domain_blocks, only: [:index, :new, :create, :show, :destroy]
resources :email_domain_blocks, only: [:index, :new, :create, :destroy] resources :email_domain_blocks, only: [:index, :new, :create, :destroy]
resources :action_logs, only: [:index] resources :action_logs, only: [:index]
resources :warning_presets, except: [:new]
resource :settings, only: [:edit, :update] resource :settings, only: [:edit, :update]
resources :invites, only: [:index, :create, :destroy] do resources :invites, only: [:index, :create, :destroy] do
@ -160,7 +161,14 @@ Rails.application.routes.draw do
end end
end end
resources :reports, only: [:index, :show, :update] do resources :reports, only: [:index, :show] do
member do
post :assign_to_self
post :unassign
post :reopen
post :resolve
end
resources :reported_statuses, only: [:create] resources :reported_statuses, only: [:create]
end end
@ -171,7 +179,8 @@ Rails.application.routes.draw do
post :subscribe post :subscribe
post :unsubscribe post :unsubscribe
post :enable post :enable
post :disable post :unsilence
post :unsuspend
post :redownload post :redownload
post :remove_avatar post :remove_avatar
post :remove_header post :remove_header
@ -180,8 +189,7 @@ Rails.application.routes.draw do
resource :change_email, only: [:show, :update] resource :change_email, only: [:show, :update]
resource :reset, only: [:create] resource :reset, only: [:create]
resource :silence, only: [:create, :destroy] resource :action, only: [:new, :create], controller: 'account_actions'
resource :suspension, only: [:new, :create, :destroy]
resources :statuses, only: [:index, :create, :update, :destroy] resources :statuses, only: [:index, :create, :update, :destroy]
resource :confirmation, only: [:create] do resource :confirmation, only: [:create] do

@ -0,0 +1,12 @@
class CreateAccountWarnings < ActiveRecord::Migration[5.2]
def change
create_table :account_warnings do |t|
t.belongs_to :account, foreign_key: { on_delete: :nullify }
t.belongs_to :target_account, foreign_key: { to_table: 'accounts', on_delete: :cascade }
t.integer :action, null: false, default: 0
t.text :text, null: false, default: ''
t.timestamps
end
end
end

@ -0,0 +1,9 @@
class CreateAccountWarningPresets < ActiveRecord::Migration[5.2]
def change
create_table :account_warning_presets do |t|
t.text :text, null: false, default: ''
t.timestamps
end
end
end

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2018_12_07_011115) do ActiveRecord::Schema.define(version: 2018_12_13_185533) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -76,6 +76,23 @@ ActiveRecord::Schema.define(version: 2018_12_07_011115) do
t.index ["tag_id"], name: "index_account_tag_stats_on_tag_id", unique: true t.index ["tag_id"], name: "index_account_tag_stats_on_tag_id", unique: true
end end
create_table "account_warning_presets", force: :cascade do |t|
t.text "text", default: "", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "account_warnings", force: :cascade do |t|
t.bigint "account_id"
t.bigint "target_account_id"
t.integer "action", default: 0, null: false
t.text "text", default: "", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_account_warnings_on_account_id"
t.index ["target_account_id"], name: "index_account_warnings_on_target_account_id"
end
create_table "accounts", force: :cascade do |t| create_table "accounts", force: :cascade do |t|
t.string "username", default: "", null: false t.string "username", default: "", null: false
t.string "domain" t.string "domain"
@ -656,6 +673,8 @@ ActiveRecord::Schema.define(version: 2018_12_07_011115) do
add_foreign_key "account_pins", "accounts", on_delete: :cascade add_foreign_key "account_pins", "accounts", on_delete: :cascade
add_foreign_key "account_stats", "accounts", on_delete: :cascade add_foreign_key "account_stats", "accounts", on_delete: :cascade
add_foreign_key "account_tag_stats", "tags", on_delete: :cascade add_foreign_key "account_tag_stats", "tags", on_delete: :cascade
add_foreign_key "account_warnings", "accounts", column: "target_account_id", on_delete: :cascade
add_foreign_key "account_warnings", "accounts", on_delete: :nullify
add_foreign_key "accounts", "accounts", column: "moved_to_account_id", on_delete: :nullify add_foreign_key "accounts", "accounts", column: "moved_to_account_id", on_delete: :nullify
add_foreign_key "admin_action_logs", "accounts", on_delete: :cascade add_foreign_key "admin_action_logs", "accounts", on_delete: :cascade
add_foreign_key "backups", "users", on_delete: :nullify add_foreign_key "backups", "users", on_delete: :nullify

@ -191,58 +191,6 @@ RSpec.describe Admin::AccountsController, type: :controller do
end end
end end
describe 'POST #disable' do
subject { post :disable, params: { id: account.id } }
let(:current_user) { Fabricate(:user, admin: current_user_admin) }
let(:account) { Fabricate(:account, user: user) }
let(:user) { Fabricate(:user, disabled: false, admin: target_user_admin) }
context 'when user is admin' do
let(:current_user_admin) { true }
context 'when target user is admin' do
let(:target_user_admin) { true }
it 'fails to disable account' do
is_expected.to have_http_status :forbidden
expect(user.reload).not_to be_disabled
end
end
context 'when target user is not admin' do
let(:target_user_admin) { false }
it 'succeeds in disabling account' do
is_expected.to redirect_to admin_account_path(account.id)
expect(user.reload).to be_disabled
end
end
end
context 'when user is not admin' do
let(:current_user_admin) { false }
context 'when target user is admin' do
let(:target_user_admin) { true }
it 'fails to disable account' do
is_expected.to have_http_status :forbidden
expect(user.reload).not_to be_disabled
end
end
context 'when target user is not admin' do
let(:target_user_admin) { false }
it 'fails to disable account' do
is_expected.to have_http_status :forbidden
expect(user.reload).not_to be_disabled
end
end
end
end
describe 'POST #redownload' do describe 'POST #redownload' do
subject { post :redownload, params: { id: account.id } } subject { post :redownload, params: { id: account.id } }

@ -46,73 +46,37 @@ describe Admin::ReportsController do
end end
end end
describe 'PUT #update' do describe 'POST #reopen' do
describe 'with an unknown outcome' do it 'reopens the report' do
it 'rejects the change' do report = Fabricate(:report)
report = Fabricate(:report)
put :update, params: { id: report, outcome: 'unknown' }
expect(response).to have_http_status(404)
end
end
describe 'with an outcome of `resolve`' do
it 'resolves the report' do
report = Fabricate(:report)
put :update, params: { id: report, outcome: 'resolve' }
expect(response).to redirect_to(admin_reports_path)
report.reload
expect(report.action_taken_by_account).to eq user.account
expect(report.action_taken).to eq true
end
end
describe 'with an outsome of `silence`' do
it 'silences the reported account' do
report = Fabricate(:report)
put :update, params: { id: report, outcome: 'silence' }
expect(response).to redirect_to(admin_reports_path)
report.reload
expect(report.action_taken_by_account).to eq user.account
expect(report.action_taken).to eq true
expect(report.target_account).to be_silenced
end
end
describe 'with an outsome of `reopen`' do
it 'reopens the report' do
report = Fabricate(:report)
put :update, params: { id: report, outcome: 'reopen' } put :reopen, params: { id: report }
expect(response).to redirect_to(admin_report_path(report)) expect(response).to redirect_to(admin_report_path(report))
report.reload report.reload
expect(report.action_taken_by_account).to eq nil expect(report.action_taken_by_account).to eq nil
expect(report.action_taken).to eq false expect(report.action_taken).to eq false
end
end end
end
describe 'with an outsome of `assign_to_self`' do describe 'POST #assign_to_self' do
it 'reopens the report' do it 'reopens the report' do
report = Fabricate(:report) report = Fabricate(:report)
put :update, params: { id: report, outcome: 'assign_to_self' } put :assign_to_self, params: { id: report }
expect(response).to redirect_to(admin_report_path(report)) expect(response).to redirect_to(admin_report_path(report))
report.reload report.reload
expect(report.assigned_account).to eq user.account expect(report.assigned_account).to eq user.account
end
end end
end
describe 'with an outsome of `unassign`' do describe 'POST #unassign' do
it 'reopens the report' do it 'reopens the report' do
report = Fabricate(:report) report = Fabricate(:report)
put :update, params: { id: report, outcome: 'unassign' } put :unassign, params: { id: report }
expect(response).to redirect_to(admin_report_path(report)) expect(response).to redirect_to(admin_report_path(report))
report.reload report.reload
expect(report.assigned_account).to eq nil expect(report.assigned_account).to eq nil
end
end end
end end
end end

@ -1,33 +0,0 @@
require 'rails_helper'
describe Admin::SilencesController do
render_views
before do
sign_in Fabricate(:user, admin: true), scope: :user
end
describe 'POST #create' do
it 'redirects to admin accounts page' do
account = Fabricate(:account, silenced: false)
post :create, params: { account_id: account.id }
account.reload
expect(account.silenced?).to eq true
expect(response).to redirect_to(admin_accounts_path)
end
end
describe 'DELETE #destroy' do
it 'redirects to admin accounts page' do
account = Fabricate(:account, silenced: true)
delete :destroy, params: { account_id: account.id }
account.reload
expect(account.silenced?).to eq false
expect(response).to redirect_to(admin_accounts_path)
end
end
end

@ -1,39 +0,0 @@
require 'rails_helper'
describe Admin::SuspensionsController do
render_views
before do
sign_in Fabricate(:user, admin: true), scope: :user
end
describe 'GET #new' do
it 'returns 200' do
get :new, params: { account_id: Fabricate(:account).id, report_id: Fabricate(:report).id }
expect(response).to have_http_status(200)
end
end
describe 'POST #create' do
it 'redirects to admin accounts page' do
account = Fabricate(:account, suspended: false)
expect(Admin::SuspensionWorker).to receive(:perform_async).with(account.id)
post :create, params: { account_id: account.id, form_admin_suspension_confirmation: { acct: account.acct } }
expect(response).to redirect_to(admin_accounts_path)
end
end
describe 'DELETE #destroy' do
it 'redirects to admin accounts page' do
account = Fabricate(:account, suspended: true)
delete :destroy, params: { account_id: account.id }
account.reload
expect(account.suspended?).to eq false
expect(response).to redirect_to(admin_accounts_path)
end
end
end

@ -0,0 +1,5 @@
Fabricator(:account_warning) do
account nil
target_account nil
text "MyText"
end

@ -0,0 +1,3 @@
Fabricator(:account_warning_preset) do
text "MyText"
end

@ -39,4 +39,9 @@ class UserMailerPreview < ActionMailer::Preview
def backup_ready def backup_ready
UserMailer.backup_ready(User.first, Backup.first) UserMailer.backup_ready(User.first, Backup.first)
end end
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/warning
def warning
UserMailer.warning(User.first, AccountWarning.new(text: '', action: :silence))
end
end end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe AccountWarningPreset, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe AccountWarning, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,4 @@
require 'rails_helper'
RSpec.describe Admin::AccountAction, type: :model do
end
Loading…
Cancel
Save