Add notifications for statuses deleted by moderators (#17204)
parent
d5c9feb7b7
commit
14f436c457
@ -1,44 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class ReportedStatusesController < BaseController
|
|
||||||
before_action :set_report
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :status, :update?
|
|
||||||
|
|
||||||
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
|
|
||||||
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
|
|
||||||
|
|
||||||
redirect_to admin_report_path(@report)
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.statuses.no_status_selected')
|
|
||||||
|
|
||||||
redirect_to admin_report_path(@report)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def status_params
|
|
||||||
params.require(:status).permit(:sensitive)
|
|
||||||
end
|
|
||||||
|
|
||||||
def form_status_batch_params
|
|
||||||
params.require(:form_status_batch).permit(status_ids: [])
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
if params[:nsfw_on]
|
|
||||||
'nsfw_on'
|
|
||||||
elsif params[:nsfw_off]
|
|
||||||
'nsfw_off'
|
|
||||||
elsif params[:delete]
|
|
||||||
'delete'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_report
|
|
||||||
@report = Report.find(params[:report_id])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -0,0 +1,159 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import api from 'mastodon/api';
|
||||||
|
import { injectIntl, defineMessages } from 'react-intl';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
other: { id: 'report.categories.other', defaultMessage: 'Other' },
|
||||||
|
spam: { id: 'report.categories.spam', defaultMessage: 'Spam' },
|
||||||
|
violation: { id: 'report.categories.violation', defaultMessage: 'Content violates one or more server rules' },
|
||||||
|
});
|
||||||
|
|
||||||
|
class Category extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
text: PropTypes.string.isRequired,
|
||||||
|
selected: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
onSelect: PropTypes.func,
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
const { id, disabled, onSelect } = this.props;
|
||||||
|
|
||||||
|
if (!disabled) {
|
||||||
|
onSelect(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { id, text, disabled, selected, children } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div tabIndex='0' role='button' className={classNames('report-reason-selector__category', { selected, disabled })} onClick={this.handleClick}>
|
||||||
|
{selected && <input type='hidden' name='report[category]' value={id} />}
|
||||||
|
|
||||||
|
<div className='report-reason-selector__category__label'>
|
||||||
|
<span className={classNames('poll__input', { active: selected, disabled })} />
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(selected && children) && (
|
||||||
|
<div className='report-reason-selector__category__rules'>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Rule extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
text: PropTypes.string.isRequired,
|
||||||
|
selected: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
onToggle: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
const { id, disabled, onToggle } = this.props;
|
||||||
|
|
||||||
|
if (!disabled) {
|
||||||
|
onToggle(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { id, text, disabled, selected } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div tabIndex='0' role='button' className={classNames('report-reason-selector__rule', { selected, disabled })} onClick={this.handleClick}>
|
||||||
|
<span className={classNames('poll__input', { checkbox: true, active: selected, disabled })} />
|
||||||
|
{selected && <input type='hidden' name='report[rule_ids][]' value={id} />}
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class ReportReasonSelector extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
category: PropTypes.string.isRequired,
|
||||||
|
rule_ids: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
category: this.props.category,
|
||||||
|
rule_ids: this.props.rule_ids || [],
|
||||||
|
rules: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
api().get('/api/v1/instance').then(res => {
|
||||||
|
this.setState({
|
||||||
|
rules: res.data.rules,
|
||||||
|
});
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_save = () => {
|
||||||
|
const { id, disabled } = this.props;
|
||||||
|
const { category, rule_ids } = this.state;
|
||||||
|
|
||||||
|
if (disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
api().put(`/api/v1/admin/reports/${id}`, {
|
||||||
|
category,
|
||||||
|
rule_ids,
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSelect = id => {
|
||||||
|
this.setState({ category: id }, () => this._save());
|
||||||
|
};
|
||||||
|
|
||||||
|
handleToggle = id => {
|
||||||
|
const { rule_ids } = this.state;
|
||||||
|
|
||||||
|
if (rule_ids.includes(id)) {
|
||||||
|
this.setState({ rule_ids: rule_ids.filter(x => x !== id ) }, () => this._save());
|
||||||
|
} else {
|
||||||
|
this.setState({ rule_ids: [...rule_ids, id] }, () => this._save());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { disabled, intl } = this.props;
|
||||||
|
const { rules, category, rule_ids } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='report-reason-selector'>
|
||||||
|
<Category id='other' text={intl.formatMessage(messages.other)} selected={category === 'other'} onSelect={this.handleSelect} disabled={disabled} />
|
||||||
|
<Category id='spam' text={intl.formatMessage(messages.spam)} selected={category === 'spam'} onSelect={this.handleSelect} disabled={disabled} />
|
||||||
|
<Category id='violation' text={intl.formatMessage(messages.violation)} selected={category === 'violation'} onSelect={this.handleSelect} disabled={disabled}>
|
||||||
|
{rules.map(rule => <Rule key={rule.id} id={rule.id} text={rule.text} selected={rule_ids.includes(rule.id)} onToggle={this.handleToggle} disabled={disabled} />)}
|
||||||
|
</Category>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Admin::StatusBatchAction
|
||||||
|
include ActiveModel::Model
|
||||||
|
include AccountableConcern
|
||||||
|
include Authorization
|
||||||
|
|
||||||
|
attr_accessor :current_account, :type,
|
||||||
|
:status_ids, :report_id
|
||||||
|
|
||||||
|
def save!
|
||||||
|
process_action!
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def statuses
|
||||||
|
Status.with_discarded.where(id: status_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_action!
|
||||||
|
return if status_ids.empty?
|
||||||
|
|
||||||
|
case type
|
||||||
|
when 'delete'
|
||||||
|
handle_delete!
|
||||||
|
when 'report'
|
||||||
|
handle_report!
|
||||||
|
when 'remove_from_report'
|
||||||
|
handle_remove_from_report!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_delete!
|
||||||
|
statuses.each { |status| authorize(status, :destroy?) }
|
||||||
|
|
||||||
|
ApplicationRecord.transaction do
|
||||||
|
statuses.each do |status|
|
||||||
|
status.discard
|
||||||
|
log_action(:destroy, status)
|
||||||
|
end
|
||||||
|
|
||||||
|
if with_report?
|
||||||
|
report.resolve!(current_account)
|
||||||
|
log_action(:resolve, report)
|
||||||
|
end
|
||||||
|
|
||||||
|
@warning = target_account.strikes.create!(
|
||||||
|
action: :delete_statuses,
|
||||||
|
account: current_account,
|
||||||
|
report: report,
|
||||||
|
status_ids: status_ids
|
||||||
|
)
|
||||||
|
|
||||||
|
statuses.each { |status| Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) } unless target_account.local?
|
||||||
|
end
|
||||||
|
|
||||||
|
UserMailer.warning(target_account.user, @warning).deliver_later! if target_account.local?
|
||||||
|
RemovalWorker.push_bulk(status_ids) { |status_id| [status_id, preserve: target_account.local?, immediate: !target_account.local?] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_report!
|
||||||
|
@report = Report.new(report_params) unless with_report?
|
||||||
|
@report.status_ids = (@report.status_ids + status_ids.map(&:to_i)).uniq
|
||||||
|
@report.save!
|
||||||
|
|
||||||
|
@report_id = @report.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_remove_from_report!
|
||||||
|
return unless with_report?
|
||||||
|
|
||||||
|
report.status_ids -= status_ids.map(&:to_i)
|
||||||
|
report.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
def report
|
||||||
|
@report ||= Report.find(report_id) if report_id.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_report?
|
||||||
|
!report.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def target_account
|
||||||
|
@target_account ||= statuses.first.account
|
||||||
|
end
|
||||||
|
|
||||||
|
def report_params
|
||||||
|
{ account: current_account, target_account: target_account }
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,41 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Admin::StatusFilter
|
||||||
|
KEYS = %i(
|
||||||
|
media
|
||||||
|
id
|
||||||
|
report_id
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
attr_reader :params
|
||||||
|
|
||||||
|
def initialize(account, params)
|
||||||
|
@account = account
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def results
|
||||||
|
scope = @account.statuses.where(visibility: [:public, :unlisted])
|
||||||
|
|
||||||
|
params.each do |key, value|
|
||||||
|
next if %w(page report_id).include?(key.to_s)
|
||||||
|
|
||||||
|
scope.merge!(scope_for(key, value.to_s.strip)) if value.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
scope
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def scope_for(key, value)
|
||||||
|
case key.to_s
|
||||||
|
when 'media'
|
||||||
|
Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)
|
||||||
|
when 'id'
|
||||||
|
Status.where(id: value)
|
||||||
|
else
|
||||||
|
raise "Unknown filter: #{key}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,45 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Form::StatusBatch
|
|
||||||
include ActiveModel::Model
|
|
||||||
include AccountableConcern
|
|
||||||
|
|
||||||
attr_accessor :status_ids, :action, :current_account
|
|
||||||
|
|
||||||
def save
|
|
||||||
case action
|
|
||||||
when 'nsfw_on', 'nsfw_off'
|
|
||||||
change_sensitive(action == 'nsfw_on')
|
|
||||||
when 'delete'
|
|
||||||
delete_statuses
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def change_sensitive(sensitive)
|
|
||||||
media_attached_status_ids = MediaAttachment.where(status_id: status_ids).pluck(:status_id)
|
|
||||||
|
|
||||||
ApplicationRecord.transaction do
|
|
||||||
Status.where(id: media_attached_status_ids).reorder(nil).find_each do |status|
|
|
||||||
status.update!(sensitive: sensitive)
|
|
||||||
log_action :update, status
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
|
||||||
rescue ActiveRecord::RecordInvalid
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_statuses
|
|
||||||
Status.where(id: status_ids).reorder(nil).find_each do |status|
|
|
||||||
status.discard
|
|
||||||
RemovalWorker.perform_async(status.id, immediate: true)
|
|
||||||
Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true)
|
|
||||||
log_action :destroy, status
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,7 +1,18 @@
|
|||||||
.speech-bubble
|
.report-notes__item
|
||||||
.speech-bubble__bubble
|
= image_tag report_note.account.avatar.url, class: 'report-notes__item__avatar'
|
||||||
|
|
||||||
|
.report-notes__item__header
|
||||||
|
%span.username
|
||||||
|
= link_to display_name(report_note.account), admin_account_path(report_note.account_id)
|
||||||
|
%time{ datetime: report_note.created_at.iso8601, title: l(report_note.created_at) }
|
||||||
|
- if report_note.created_at.today?
|
||||||
|
= t('admin.report_notes.today_at', time: l(report_note.created_at, format: :time))
|
||||||
|
- else
|
||||||
|
= l report_note.created_at.to_date
|
||||||
|
|
||||||
|
.report-notes__item__content
|
||||||
= simple_format(h(report_note.content))
|
= simple_format(h(report_note.content))
|
||||||
.speech-bubble__owner
|
|
||||||
= admin_account_link_to report_note.account
|
- if can?(:destroy, report_note)
|
||||||
%time.formatted{ datetime: report_note.created_at.iso8601 }= l report_note.created_at
|
.report-notes__item__actions
|
||||||
= table_link_to 'trash', t('admin.reports.notes.delete'), admin_report_note_path(report_note), method: :delete if can?(:destroy, report_note)
|
= table_link_to 'trash', t('admin.reports.notes.delete'), admin_report_note_path(report_note), method: :delete
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
.speech-bubble.positive
|
|
||||||
.speech-bubble__bubble
|
|
||||||
= t("admin.action_logs.actions.#{action_log.action}_#{action_log.target_type.underscore}_html", name: content_tag(:span, action_log.account.username, class: 'username'), target: content_tag(:span, log_target(action_log), class: 'target'))
|
|
||||||
.speech-bubble__owner
|
|
||||||
= admin_account_link_to(action_log.account)
|
|
||||||
%time.formatted{ datetime: action_log.created_at.iso8601 }= l action_log.created_at
|
|
@ -1,27 +0,0 @@
|
|||||||
- content_for :page_title do
|
|
||||||
= t('admin.statuses.title')
|
|
||||||
\-
|
|
||||||
= "@#{@account.acct}"
|
|
||||||
|
|
||||||
.filters
|
|
||||||
.back-link
|
|
||||||
= link_to admin_account_path(@account.id) do
|
|
||||||
%i.fa.fa-chevron-left.fa-fw
|
|
||||||
= t('admin.statuses.back_to_account')
|
|
||||||
|
|
||||||
%hr.spacer/
|
|
||||||
|
|
||||||
= form_for(@form, url: admin_account_statuses_path(@account.id)) do |f|
|
|
||||||
= hidden_field_tag :page, params[:page]
|
|
||||||
= hidden_field_tag :media, params[:media]
|
|
||||||
|
|
||||||
.batch-table
|
|
||||||
.batch-table__toolbar
|
|
||||||
%label.batch-table__toolbar__select.batch-checkbox-all
|
|
||||||
= check_box_tag :batch_checkbox_all, nil, false
|
|
||||||
.batch-table__toolbar__actions
|
|
||||||
= f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
|
|
||||||
= f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
|
|
||||||
= f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
|
|
||||||
.batch-table__body
|
|
||||||
= render partial: 'admin/reports/status', collection: @statuses, locals: { f: f }
|
|
@ -1,8 +1,8 @@
|
|||||||
<% if status.spoiler_text? %>
|
<% if status.spoiler_text? %>
|
||||||
<%= raw status.spoiler_text %>
|
> <%= raw word_wrap(status.spoiler_text, break_sequence: "\n> ") %>
|
||||||
----
|
> ----
|
||||||
|
>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= raw Formatter.instance.plaintext(status) %>
|
> <%= raw word_wrap(Formatter.instance.plaintext(status), break_sequence: "\n> ") %>
|
||||||
|
|
||||||
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %>
|
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %>
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
require Rails.root.join('lib', 'mastodon', 'migration_helpers')
|
||||||
|
|
||||||
|
class AddCategoryToReports < ActiveRecord::Migration[6.1]
|
||||||
|
include Mastodon::MigrationHelpers
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
safety_assured { add_column_with_default :reports, :category, :int, default: 0, allow_null: false }
|
||||||
|
add_column :reports, :action_taken_at, :datetime
|
||||||
|
add_column :reports, :rule_ids, :bigint, array: true
|
||||||
|
safety_assured { execute 'UPDATE reports SET action_taken_at = updated_at WHERE action_taken = TRUE' }
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
safety_assured { execute 'UPDATE reports SET action_taken = TRUE WHERE action_taken_at IS NOT NULL' }
|
||||||
|
remove_column :reports, :category
|
||||||
|
remove_column :reports, :action_taken_at
|
||||||
|
remove_column :reports, :rule_ids
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,6 @@
|
|||||||
|
class AddReportIdToAccountWarnings < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
safety_assured { add_reference :account_warnings, :report, foreign_key: { on_delete: :cascade }, index: false }
|
||||||
|
add_column :account_warnings, :status_ids, :string, array: true
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,21 @@
|
|||||||
|
class FixAccountWarningActions < ActiveRecord::Migration[6.1]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
safety_assured do
|
||||||
|
execute 'UPDATE account_warnings SET action = 1000 WHERE action = 1'
|
||||||
|
execute 'UPDATE account_warnings SET action = 2000 WHERE action = 2'
|
||||||
|
execute 'UPDATE account_warnings SET action = 3000 WHERE action = 3'
|
||||||
|
execute 'UPDATE account_warnings SET action = 4000 WHERE action = 4'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
safety_assured do
|
||||||
|
execute 'UPDATE account_warnings SET action = 1 WHERE action = 1000'
|
||||||
|
execute 'UPDATE account_warnings SET action = 2 WHERE action = 2000'
|
||||||
|
execute 'UPDATE account_warnings SET action = 3 WHERE action = 3000'
|
||||||
|
execute 'UPDATE account_warnings SET action = 4 WHERE action = 4000'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,7 @@
|
|||||||
|
class AddDeletedAtIndexOnStatuses < ActiveRecord::Migration[6.1]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_index :statuses, :deleted_at, where: 'deleted_at IS NOT NULL', algorithm: :concurrently
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveActionTakenFromReports < ActiveRecord::Migration[5.2]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def change
|
||||||
|
safety_assured { remove_column :reports, :action_taken, :boolean, default: false, null: false }
|
||||||
|
end
|
||||||
|
end
|
@ -1,59 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
describe Admin::ReportedStatusesController do
|
|
||||||
render_views
|
|
||||||
|
|
||||||
let(:user) { Fabricate(:user, admin: true) }
|
|
||||||
let(:report) { Fabricate(:report, status_ids: [status.id]) }
|
|
||||||
let(:status) { Fabricate(:status) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
sign_in user, scope: :user
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'POST #create' do
|
|
||||||
subject do
|
|
||||||
-> { post :create, params: { :report_id => report, action => '', :form_status_batch => { status_ids: status_ids } } }
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:action) { 'nsfw_on' }
|
|
||||||
let(:status_ids) { [status.id] }
|
|
||||||
let(:status) { Fabricate(:status, sensitive: !sensitive) }
|
|
||||||
let(:sensitive) { true }
|
|
||||||
let!(:media_attachment) { Fabricate(:media_attachment, status: status) }
|
|
||||||
|
|
||||||
context 'when action is nsfw_on' do
|
|
||||||
it 'updates sensitive column' do
|
|
||||||
is_expected.to change {
|
|
||||||
status.reload.sensitive
|
|
||||||
}.from(false).to(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when action is nsfw_off' do
|
|
||||||
let(:action) { 'nsfw_off' }
|
|
||||||
let(:sensitive) { false }
|
|
||||||
|
|
||||||
it 'updates sensitive column' do
|
|
||||||
is_expected.to change {
|
|
||||||
status.reload.sensitive
|
|
||||||
}.from(true).to(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when action is delete' do
|
|
||||||
let(:action) { 'delete' }
|
|
||||||
|
|
||||||
it 'removes a status' do
|
|
||||||
allow(RemovalWorker).to receive(:perform_async)
|
|
||||||
subject.call
|
|
||||||
expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first, immediate: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'redirects to report page' do
|
|
||||||
subject.call
|
|
||||||
expect(response).to redirect_to(admin_report_path(report))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,6 +1,6 @@
|
|||||||
Fabricator(:report) do
|
Fabricator(:report) do
|
||||||
account
|
account
|
||||||
target_account { Fabricate(:account) }
|
target_account { Fabricate(:account) }
|
||||||
comment "You nasty"
|
comment "You nasty"
|
||||||
action_taken false
|
action_taken_at nil
|
||||||
end
|
end
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
describe Form::StatusBatch do
|
|
||||||
let(:form) { Form::StatusBatch.new(action: action, status_ids: status_ids) }
|
|
||||||
let(:status) { Fabricate(:status) }
|
|
||||||
|
|
||||||
describe 'with nsfw action' do
|
|
||||||
let(:status_ids) { [status.id, nonsensitive_status.id, sensitive_status.id] }
|
|
||||||
let(:nonsensitive_status) { Fabricate(:status, sensitive: false) }
|
|
||||||
let(:sensitive_status) { Fabricate(:status, sensitive: true) }
|
|
||||||
let!(:shown_media_attachment) { Fabricate(:media_attachment, status: nonsensitive_status) }
|
|
||||||
let!(:hidden_media_attachment) { Fabricate(:media_attachment, status: sensitive_status) }
|
|
||||||
|
|
||||||
context 'nsfw_on' do
|
|
||||||
let(:action) { 'nsfw_on' }
|
|
||||||
|
|
||||||
it { expect(form.save).to be true }
|
|
||||||
it { expect { form.save }.to change { nonsensitive_status.reload.sensitive }.from(false).to(true) }
|
|
||||||
it { expect { form.save }.not_to change { sensitive_status.reload.sensitive } }
|
|
||||||
it { expect { form.save }.not_to change { status.reload.sensitive } }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'nsfw_off' do
|
|
||||||
let(:action) { 'nsfw_off' }
|
|
||||||
|
|
||||||
it { expect(form.save).to be true }
|
|
||||||
it { expect { form.save }.to change { sensitive_status.reload.sensitive }.from(true).to(false) }
|
|
||||||
it { expect { form.save }.not_to change { nonsensitive_status.reload.sensitive } }
|
|
||||||
it { expect { form.save }.not_to change { status.reload.sensitive } }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'with delete action' do
|
|
||||||
let(:status_ids) { [status.id] }
|
|
||||||
let(:action) { 'delete' }
|
|
||||||
let!(:another_status) { Fabricate(:status) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(RemovalWorker).to receive(:perform_async)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'call RemovalWorker' do
|
|
||||||
form.save
|
|
||||||
expect(RemovalWorker).to have_received(:perform_async).with(status.id, immediate: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'do not call RemovalWorker' do
|
|
||||||
form.save
|
|
||||||
expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id, immediate: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in new issue