commit
b27a9a5903
20 changed files with 676 additions and 51 deletions
@ -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> |
||||
); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,22 @@ |
||||
- content_for :page_title do |
||||
= t('.title', domain: Addressable::IDNA.to_unicode(@domain_block.domain)) |
||||
|
||||
= simple_form_for @domain_block, url: admin_domain_blocks_path(@domain_block) do |f| |
||||
|
||||
%p.hint= t('.preamble_html', domain: Addressable::IDNA.to_unicode(@domain_block.domain)) |
||||
%ul.hint |
||||
%li= t('.stop_communication') |
||||
%li= t('.remove_all_data') |
||||
%li= t('.undo_relationships') |
||||
%li.negative-hint= t('.permanent_action') |
||||
|
||||
- %i(domain severity reject_media reject_reports obfuscate private_comment public_comment).each do |key| |
||||
= f.hidden_field key |
||||
|
||||
%hr.spacer |
||||
|
||||
= react_admin_component :impact_report, domain: @domain_block.domain |
||||
|
||||
.actions |
||||
= link_to t('.cancel'), admin_instances_path, class: 'button button-tertiary' |
||||
= f.button :submit, t('.confirm'), class: 'button negative', name: :confirm |
@ -0,0 +1,78 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
describe 'blocking domains through the moderation interface' do |
||||
before do |
||||
sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user |
||||
end |
||||
|
||||
context 'when silencing a new domain' do |
||||
it 'adds a new domain block' do |
||||
visit new_admin_domain_block_path |
||||
|
||||
fill_in 'domain_block_domain', with: 'example.com' |
||||
select I18n.t('admin.domain_blocks.new.severity.silence'), from: 'domain_block_severity' |
||||
click_on I18n.t('admin.domain_blocks.new.create') |
||||
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'silence')).to be true |
||||
end |
||||
end |
||||
|
||||
context 'when suspending a new domain' do |
||||
it 'presents a confirmation screen before suspending the domain' do |
||||
visit new_admin_domain_block_path |
||||
|
||||
fill_in 'domain_block_domain', with: 'example.com' |
||||
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' |
||||
click_on I18n.t('admin.domain_blocks.new.create') |
||||
|
||||
# It presents a confirmation screen |
||||
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) |
||||
|
||||
# Confirming creates a block |
||||
click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm') |
||||
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be true |
||||
end |
||||
end |
||||
|
||||
context 'when suspending a domain that is already silenced' do |
||||
it 'presents a confirmation screen before suspending the domain' do |
||||
domain_block = Fabricate(:domain_block, domain: 'example.com', severity: 'silence') |
||||
|
||||
visit new_admin_domain_block_path |
||||
|
||||
fill_in 'domain_block_domain', with: 'example.com' |
||||
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' |
||||
click_on I18n.t('admin.domain_blocks.new.create') |
||||
|
||||
# It presents a confirmation screen |
||||
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) |
||||
|
||||
# Confirming updates the block |
||||
click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm') |
||||
|
||||
expect(domain_block.reload.severity).to eq 'silence' |
||||
end |
||||
end |
||||
|
||||
context 'when editing a domain block' do |
||||
it 'presents a confirmation screen before suspending the domain' do |
||||
domain_block = Fabricate(:domain_block, domain: 'example.com', severity: 'silence') |
||||
|
||||
visit edit_admin_domain_block_path(domain_block) |
||||
|
||||
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' |
||||
click_on I18n.t('generic.save_changes') |
||||
|
||||
# It presents a confirmation screen |
||||
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) |
||||
|
||||
# Confirming updates the block |
||||
click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm') |
||||
|
||||
expect(domain_block.reload.severity).to eq 'silence' |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,40 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
describe Admin::Metrics::Measure::InstanceAccountsMeasure do |
||||
subject(:measure) { described_class.new(start_at, end_at, params) } |
||||
|
||||
let(:domain) { 'example.com' } |
||||
|
||||
let(:start_at) { 2.days.ago } |
||||
let(:end_at) { Time.now.utc } |
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) } |
||||
|
||||
before do |
||||
Fabricate(:account, domain: domain, created_at: 1.year.ago) |
||||
Fabricate(:account, domain: domain, created_at: 1.month.ago) |
||||
Fabricate(:account, domain: domain) |
||||
|
||||
Fabricate(:account, domain: "foo.#{domain}", created_at: 1.year.ago) |
||||
Fabricate(:account, domain: "foo.#{domain}") |
||||
Fabricate(:account, domain: "bar.#{domain}") |
||||
end |
||||
|
||||
describe 'total' do |
||||
context 'without include_subdomains' do |
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 3 |
||||
end |
||||
end |
||||
|
||||
context 'with include_subdomains' do |
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') } |
||||
|
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 6 |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,42 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
describe Admin::Metrics::Measure::InstanceFollowersMeasure do |
||||
subject(:measure) { described_class.new(start_at, end_at, params) } |
||||
|
||||
let(:domain) { 'example.com' } |
||||
|
||||
let(:start_at) { 2.days.ago } |
||||
let(:end_at) { Time.now.utc } |
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) } |
||||
|
||||
before do |
||||
local_account = Fabricate(:account) |
||||
|
||||
Fabricate(:account, domain: domain).follow!(local_account) |
||||
Fabricate(:account, domain: domain).follow!(local_account) |
||||
Fabricate(:account, domain: domain) |
||||
|
||||
Fabricate(:account, domain: "foo.#{domain}").follow!(local_account) |
||||
Fabricate(:account, domain: "foo.#{domain}").follow!(local_account) |
||||
Fabricate(:account, domain: "bar.#{domain}") |
||||
end |
||||
|
||||
describe 'total' do |
||||
context 'without include_subdomains' do |
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 2 |
||||
end |
||||
end |
||||
|
||||
context 'with include_subdomains' do |
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') } |
||||
|
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 4 |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,42 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
describe Admin::Metrics::Measure::InstanceFollowsMeasure do |
||||
subject(:measure) { described_class.new(start_at, end_at, params) } |
||||
|
||||
let(:domain) { 'example.com' } |
||||
|
||||
let(:start_at) { 2.days.ago } |
||||
let(:end_at) { Time.now.utc } |
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) } |
||||
|
||||
before do |
||||
local_account = Fabricate(:account) |
||||
|
||||
local_account.follow!(Fabricate(:account, domain: domain)) |
||||
local_account.follow!(Fabricate(:account, domain: domain)) |
||||
Fabricate(:account, domain: domain) |
||||
|
||||
local_account.follow!(Fabricate(:account, domain: "foo.#{domain}")) |
||||
local_account.follow!(Fabricate(:account, domain: "foo.#{domain}")) |
||||
Fabricate(:account, domain: "bar.#{domain}") |
||||
end |
||||
|
||||
describe 'total' do |
||||
context 'without include_subdomains' do |
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 2 |
||||
end |
||||
end |
||||
|
||||
context 'with include_subdomains' do |
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') } |
||||
|
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 4 |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,43 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
describe Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure do |
||||
subject(:measure) { described_class.new(start_at, end_at, params) } |
||||
|
||||
let(:domain) { 'example.com' } |
||||
|
||||
let(:start_at) { 2.days.ago } |
||||
let(:end_at) { Time.now.utc } |
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) } |
||||
|
||||
let(:remote_account) { Fabricate(:account, domain: domain) } |
||||
let(:remote_account_on_subdomain) { Fabricate(:account, domain: "foo.#{domain}") } |
||||
|
||||
before do |
||||
remote_account.media_attachments.create!(file: attachment_fixture('attachment.jpg')) |
||||
remote_account_on_subdomain.media_attachments.create!(file: attachment_fixture('attachment.jpg')) |
||||
end |
||||
|
||||
describe 'total' do |
||||
context 'without include_subdomains' do |
||||
it 'returns the expected number of accounts' do |
||||
expected_total = remote_account.media_attachments.sum(:file_file_size) + remote_account.media_attachments.sum(:thumbnail_file_size) |
||||
expect(measure.total).to eq expected_total |
||||
end |
||||
end |
||||
|
||||
context 'with include_subdomains' do |
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') } |
||||
|
||||
it 'returns the expected number of accounts' do |
||||
expected_total = [remote_account, remote_account_on_subdomain].sum do |account| |
||||
account.media_attachments.sum(:file_file_size) + account.media_attachments.sum(:thumbnail_file_size) |
||||
end |
||||
|
||||
expect(measure.total).to eq expected_total |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,39 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
describe Admin::Metrics::Measure::InstanceReportsMeasure do |
||||
subject(:measure) { described_class.new(start_at, end_at, params) } |
||||
|
||||
let(:domain) { 'example.com' } |
||||
|
||||
let(:start_at) { 2.days.ago } |
||||
let(:end_at) { Time.now.utc } |
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) } |
||||
|
||||
before do |
||||
Fabricate(:report, target_account: Fabricate(:account, domain: domain)) |
||||
Fabricate(:report, target_account: Fabricate(:account, domain: domain)) |
||||
|
||||
Fabricate(:report, target_account: Fabricate(:account, domain: "foo.#{domain}")) |
||||
Fabricate(:report, target_account: Fabricate(:account, domain: "foo.#{domain}")) |
||||
Fabricate(:report, target_account: Fabricate(:account, domain: "bar.#{domain}")) |
||||
end |
||||
|
||||
describe 'total' do |
||||
context 'without include_subdomains' do |
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 2 |
||||
end |
||||
end |
||||
|
||||
context 'with include_subdomains' do |
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') } |
||||
|
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 5 |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,39 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
describe Admin::Metrics::Measure::InstanceStatusesMeasure do |
||||
subject(:measure) { described_class.new(start_at, end_at, params) } |
||||
|
||||
let(:domain) { 'example.com' } |
||||
|
||||
let(:start_at) { 2.days.ago } |
||||
let(:end_at) { Time.now.utc } |
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) } |
||||
|
||||
before do |
||||
Fabricate(:status, account: Fabricate(:account, domain: domain)) |
||||
Fabricate(:status, account: Fabricate(:account, domain: domain)) |
||||
|
||||
Fabricate(:status, account: Fabricate(:account, domain: "foo.#{domain}")) |
||||
Fabricate(:status, account: Fabricate(:account, domain: "foo.#{domain}")) |
||||
Fabricate(:status, account: Fabricate(:account, domain: "bar.#{domain}")) |
||||
end |
||||
|
||||
describe 'total' do |
||||
context 'without include_subdomains' do |
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 2 |
||||
end |
||||
end |
||||
|
||||
context 'with include_subdomains' do |
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') } |
||||
|
||||
it 'returns the expected number of accounts' do |
||||
expect(measure.total).to eq 5 |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue