Skip to content

Commit

Permalink
Add a confirmation screen when suspending a domain (mastodon#25144)
Browse files Browse the repository at this point in the history
  • Loading branch information
ClearlyClaire committed Jun 1, 2023
1 parent b922ad7 commit e9385e9
Show file tree
Hide file tree
Showing 20 changed files with 679 additions and 51 deletions.
42 changes: 28 additions & 14 deletions app/controllers/admin/domain_blocks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,41 @@ def create
@domain_block = DomainBlock.new(resource_params)
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil

# Disallow accidentally downgrading a domain block
if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block)
@domain_block.save
flash.now[:alert] = I18n.t('admin.domain_blocks.existing_domain_block_html', name: existing_domain_block.domain, unblock_url: admin_domain_block_path(existing_domain_block)).html_safe
@domain_block.errors.delete(:domain)
render :new
return render :new
end

# Allow transparently upgrading a domain block
if existing_domain_block.present?
@domain_block = existing_domain_block
@domain_block.assign_attributes(resource_params)
end

# Require explicit confirmation when suspending
return render :confirm_suspension if requires_confirmation?

if @domain_block.save
DomainBlockWorker.perform_async(@domain_block.id)
log_action :create, @domain_block
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
else
if existing_domain_block.present?
@domain_block = existing_domain_block
@domain_block.update(resource_params)
end

if @domain_block.save
DomainBlockWorker.perform_async(@domain_block.id)
log_action :create, @domain_block
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
else
render :new
end
render :new
end
end

def update
authorize :domain_block, :update?

if @domain_block.update(update_params)
@domain_block.assign_attributes(update_params)

# Require explicit confirmation when suspending
return render :confirm_suspension if requires_confirmation?

if @domain_block.save
DomainBlockWorker.perform_async(@domain_block.id, @domain_block.severity_previously_changed?)
log_action :update, @domain_block
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
Expand Down Expand Up @@ -92,5 +102,9 @@ def form_domain_block_batch_params
def action_from_button
'save' if params[:save]
end

def requires_confirmation?
@domain_block.valid? && (@domain_block.new_record? || @domain_block.severity_changed?) && @domain_block.severity.to_s == 'suspend' && !params[:confirm]
end
end
end
91 changes: 91 additions & 0 deletions app/javascript/mastodon/components/admin/ImpactReport.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';

import { FormattedNumber, FormattedMessage } from 'react-intl';

import classNames from 'classnames';

import api from 'mastodon/api';
import { Skeleton } from 'mastodon/components/skeleton';

export default class ImpactReport extends PureComponent {

static propTypes = {
domain: PropTypes.string.isRequired,
};

state = {
loading: true,
data: null,
};

componentDidMount () {
const { domain } = this.props;

const params = {
domain: domain,
include_subdomains: true,
};

api().post('/api/v1/admin/measures', {
keys: ['instance_accounts', 'instance_follows', 'instance_followers'],
start_at: null,
end_at: null,
instance_accounts: params,
instance_follows: params,
instance_followers: params,
}).then(res => {
this.setState({
loading: false,
data: res.data,
});
}).catch(err => {
console.error(err);
});
}

render () {
const { loading, data } = this.state;

return (
<div className='dimension'>
<h4><FormattedMessage id='admin.impact_report.title' defaultMessage='Impact summary' /></h4>

<table>
<tbody>
<tr className='dimension__item'>
<td className='dimension__item__key'>
<FormattedMessage id='admin.impact_report.instance_accounts' defaultMessage='Accounts profiles this would delete' />
</td>

<td className='dimension__item__value'>
{loading ? <Skeleton width={60} /> : <FormattedNumber value={data[0].total} />}
</td>
</tr>

<tr className={classNames('dimension__item', { negative: !loading && data[1].total > 0 })}>
<td className='dimension__item__key'>
<FormattedMessage id='admin.impact_report.instance_follows' defaultMessage='Followers their users would lose' />
</td>

<td className='dimension__item__value'>
{loading ? <Skeleton width={60} /> : <FormattedNumber value={data[1].total} />}
</td>
</tr>

<tr className={classNames('dimension__item', { negative: !loading && data[2].total > 0 })}>
<td className='dimension__item__key'>
<FormattedMessage id='admin.impact_report.instance_followers' defaultMessage='Followers our users would lose' />
</td>

<td className='dimension__item__value'>
{loading ? <Skeleton width={60} /> : <FormattedNumber value={data[2].total} />}
</td>
</tr>
</tbody>
</table>
</div>
);
}

}
4 changes: 4 additions & 0 deletions app/javascript/mastodon/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
"admin.dashboard.retention.average": "Average",
"admin.dashboard.retention.cohort": "Sign-up month",
"admin.dashboard.retention.cohort_size": "New users",
"admin.impact_report.instance_accounts": "Accounts profiles this would delete",
"admin.impact_report.instance_followers": "Followers our users would lose",
"admin.impact_report.instance_follows": "Followers their users would lose",
"admin.impact_report.title": "Impact summary",
"alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
"alert.rate_limited.title": "Rate limited",
"alert.unexpected.message": "An unexpected error occurred.",
Expand Down
9 changes: 9 additions & 0 deletions app/javascript/styles/mastodon/admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,15 @@ a.sparkline {
&:last-child {
border-bottom: 0;
}

&.negative {
color: $error-value-color;
font-weight: 700;

.dimension__item__value {
color: $error-value-color;
}
}
}
}

Expand Down
16 changes: 13 additions & 3 deletions app/lib/admin/metrics/measure/instance_accounts_measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,31 @@ def total_in_time_range?
protected

def perform_total_query
Account.where(domain: params[:domain]).count
domain = params[:domain]
domain = Instance.by_domain_and_subdomains(params[:domain]).select(:domain) if params[:include_subdomains]
Account.where(domain: domain).count
end

def perform_previous_total_query
nil
end

def perform_data_query
account_matching_sql = begin
if params[:include_subdomains]
"accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))"
else
'accounts.domain = $3::text'
end
end

sql = <<-SQL.squish
SELECT axis.*, (
WITH new_accounts AS (
SELECT accounts.id
FROM accounts
WHERE date_trunc('day', accounts.created_at)::date = axis.period
AND accounts.domain = $3::text
AND #{account_matching_sql}
)
SELECT count(*) FROM new_accounts
) AS value
Expand All @@ -53,6 +63,6 @@ def previous_time_period
end

def params
@params.permit(:domain)
@params.permit(:domain, :include_subdomains)
end
end
16 changes: 13 additions & 3 deletions app/lib/admin/metrics/measure/instance_followers_measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,32 @@ def total_in_time_range?
protected

def perform_total_query
Follow.joins(:account).merge(Account.where(domain: params[:domain])).count
domain = params[:domain]
domain = Instance.by_domain_and_subdomains(params[:domain]).select(:domain) if params[:include_subdomains]
Follow.joins(:account).merge(Account.where(domain: domain)).count
end

def perform_previous_total_query
nil
end

def perform_data_query
account_matching_sql = begin
if params[:include_subdomains]
"accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))"
else
'accounts.domain = $3::text'
end
end

sql = <<-SQL.squish
SELECT axis.*, (
WITH new_followers AS (
SELECT follows.id
FROM follows
INNER JOIN accounts ON follows.account_id = accounts.id
WHERE date_trunc('day', follows.created_at)::date = axis.period
AND accounts.domain = $3::text
AND #{account_matching_sql}
)
SELECT count(*) FROM new_followers
) AS value
Expand All @@ -54,6 +64,6 @@ def previous_time_period
end

def params
@params.permit(:domain)
@params.permit(:domain, :include_subdomains)
end
end
16 changes: 13 additions & 3 deletions app/lib/admin/metrics/measure/instance_follows_measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,32 @@ def total_in_time_range?
protected

def perform_total_query
Follow.joins(:target_account).merge(Account.where(domain: params[:domain])).count
domain = params[:domain]
domain = Instance.by_domain_and_subdomains(params[:domain]).select(:domain) if params[:include_subdomains]
Follow.joins(:target_account).merge(Account.where(domain: domain)).count
end

def perform_previous_total_query
nil
end

def perform_data_query
account_matching_sql = begin
if params[:include_subdomains]
"accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))"
else
'accounts.domain = $3::text'
end
end

sql = <<-SQL.squish
SELECT axis.*, (
WITH new_follows AS (
SELECT follows.id
FROM follows
INNER JOIN accounts ON follows.target_account_id = accounts.id
WHERE date_trunc('day', follows.created_at)::date = axis.period
AND accounts.domain = $3::text
AND #{account_matching_sql}
)
SELECT count(*) FROM new_follows
) AS value
Expand All @@ -54,6 +64,6 @@ def previous_time_period
end

def params
@params.permit(:domain)
@params.permit(:domain, :include_subdomains)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,32 @@ def total_in_time_range?
protected

def perform_total_query
MediaAttachment.joins(:account).merge(Account.where(domain: params[:domain])).sum('COALESCE(file_file_size, 0) + COALESCE(thumbnail_file_size, 0)')
domain = params[:domain]
domain = Instance.by_domain_and_subdomains(params[:domain]).select(:domain) if params[:include_subdomains]
MediaAttachment.joins(:account).merge(Account.where(domain: domain)).sum('COALESCE(file_file_size, 0) + COALESCE(thumbnail_file_size, 0)')
end

def perform_previous_total_query
nil
end

def perform_data_query
account_matching_sql = begin
if params[:include_subdomains]
"accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))"
else
'accounts.domain = $3::text'
end
end

sql = <<-SQL.squish
SELECT axis.*, (
WITH new_media_attachments AS (
SELECT COALESCE(media_attachments.file_file_size, 0) + COALESCE(media_attachments.thumbnail_file_size, 0) AS size
FROM media_attachments
INNER JOIN accounts ON accounts.id = media_attachments.account_id
WHERE date_trunc('day', media_attachments.created_at)::date = axis.period
AND accounts.domain = $3::text
AND #{account_matching_sql}
)
SELECT SUM(size) FROM new_media_attachments
) AS value
Expand All @@ -64,6 +74,6 @@ def previous_time_period
end

def params
@params.permit(:domain)
@params.permit(:domain, :include_subdomains)
end
end
16 changes: 13 additions & 3 deletions app/lib/admin/metrics/measure/instance_reports_measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,32 @@ def total_in_time_range?
protected

def perform_total_query
Report.where(target_account: Account.where(domain: params[:domain])).count
domain = params[:domain]
domain = Instance.by_domain_and_subdomains(params[:domain]).select(:domain) if params[:include_subdomains]
Report.where(target_account: Account.where(domain: domain)).count
end

def perform_previous_total_query
nil
end

def perform_data_query
account_matching_sql = begin
if params[:include_subdomains]
"accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))"
else
'accounts.domain = $3::text'
end
end

sql = <<-SQL.squish
SELECT axis.*, (
WITH new_reports AS (
SELECT reports.id
FROM reports
INNER JOIN accounts ON accounts.id = reports.target_account_id
WHERE date_trunc('day', reports.created_at)::date = axis.period
AND accounts.domain = $3::text
AND #{account_matching_sql}
)
SELECT count(*) FROM new_reports
) AS value
Expand All @@ -54,6 +64,6 @@ def previous_time_period
end

def params
@params.permit(:domain)
@params.permit(:domain, :include_subdomains)
end
end

0 comments on commit e9385e9

Please sign in to comment.