Revert severed relationships feature

This commit is contained in:
Claire 2024-03-21 11:35:31 +01:00
parent f3acc08ac6
commit dec6ef7363
49 changed files with 5 additions and 810 deletions

View File

@ -1,61 +0,0 @@
# frozen_string_literal: true
class SeveredRelationshipsController < ApplicationController
layout 'admin'
before_action :authenticate_user!
before_action :set_body_classes
before_action :set_cache_headers
before_action :set_event, only: [:following, :followers]
def index
@events = AccountRelationshipSeveranceEvent.where(account: current_account)
end
def following
respond_to do |format|
format.csv { send_data following_data, filename: "following-#{@event.target_name}-#{@event.created_at.to_date.iso8601}.csv" }
end
end
def followers
respond_to do |format|
format.csv { send_data followers_data, filename: "followers-#{@event.target_name}-#{@event.created_at.to_date.iso8601}.csv" }
end
end
private
def set_event
@event = AccountRelationshipSeveranceEvent.find(params[:id])
end
def following_data
CSV.generate(headers: ['Account address', 'Show boosts', 'Notify on new posts', 'Languages'], write_headers: true) do |csv|
@event.severed_relationships.active.about_local_account(current_account).includes(:remote_account).reorder(id: :desc).each do |follow|
csv << [acct(follow.target_account), follow.show_reblogs, follow.notify, follow.languages&.join(', ')]
end
end
end
def followers_data
CSV.generate(headers: ['Account address'], write_headers: true) do |csv|
@event.severed_relationships.passive.about_local_account(current_account).includes(:remote_account).reorder(id: :desc).each do |follow|
csv << [acct(follow.account)]
end
end
end
def acct(account)
account.local? ? account.local_username_and_domain : account.acct
end
def set_body_classes
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View File

@ -14,7 +14,6 @@ import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react'; import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react'; import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react'; import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
import LinkOffIcon from '@/material-icons/400-24px/link_off.svg?react';
import PersonIcon from '@/material-icons/400-24px/person-fill.svg?react'; import PersonIcon from '@/material-icons/400-24px/person-fill.svg?react';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
@ -27,7 +26,6 @@ import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import FollowRequestContainer from '../containers/follow_request_container'; import FollowRequestContainer from '../containers/follow_request_container';
import RelationshipsSeveranceEvent from './relationships_severance_event';
import Report from './report'; import Report from './report';
const messages = defineMessages({ const messages = defineMessages({
@ -360,30 +358,6 @@ class Notification extends ImmutablePureComponent {
); );
} }
renderRelationshipsSevered (notification) {
const { intl, unread } = this.props;
if (!notification.get('event')) {
return null;
}
return (
<HotKeys handlers={this.getHandlers()}>
<div className={classNames('notification notification-severed-relationships focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminReport, { name: notification.getIn(['event', 'target_name']) }), notification.get('created_at'))}>
<div className='notification__message'>
<Icon id='unlink' icon={LinkOffIcon} />
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.severed_relationships' defaultMessage='Relationships with {name} severed' values={{ name: notification.getIn(['event', 'target_name']) }} />
</span>
</div>
<RelationshipsSeveranceEvent event={notification.get('event')} />
</div>
</HotKeys>
);
}
renderAdminSignUp (notification, account, link) { renderAdminSignUp (notification, account, link) {
const { intl, unread } = this.props; const { intl, unread } = this.props;
@ -455,8 +429,6 @@ class Notification extends ImmutablePureComponent {
return this.renderUpdate(notification, link); return this.renderUpdate(notification, link);
case 'poll': case 'poll':
return this.renderPoll(notification, account); return this.renderPoll(notification, account);
case 'severed_relationships':
return this.renderRelationshipsSevered(notification);
case 'admin.sign_up': case 'admin.sign_up':
return this.renderAdminSignUp(notification, account, link); return this.renderAdminSignUp(notification, account, link);
case 'admin.report': case 'admin.report':

View File

@ -1,61 +0,0 @@
import PropTypes from 'prop-types';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
// This needs to be kept in sync with app/models/relationship_severance_event.rb
const messages = defineMessages({
account_suspension: { id: 'relationship_severance_notification.types.account_suspension', defaultMessage: 'Account has been suspended' },
domain_block: { id: 'relationship_severance_notification.types.domain_block', defaultMessage: 'Domain has been suspended' },
user_domain_block: { id: 'relationship_severance_notification.types.user_domain_block', defaultMessage: 'You blocked this domain' },
});
const RelationshipsSeveranceEvent = ({ event, hidden }) => {
const intl = useIntl();
if (hidden || !event) {
return null;
}
return (
<div className='notification__report'>
<div className='notification__report__details'>
<div>
<RelativeTimestamp timestamp={event.get('created_at')} short={false} />
{' · '}
{ event.get('purged') ? (
<FormattedMessage
id='relationship_severance_notification.purged_data'
defaultMessage='purged by administrators'
/>
) : (
<FormattedMessage
id='relationship_severance_notification.relationships'
defaultMessage='{count, plural, one {# relationship} other {# relationships}}'
values={{ count: event.get('relationships_count', 0) }}
/>
)}
<br />
<strong>{intl.formatMessage(messages[event.get('type')])}</strong>
</div>
<div className='notification__report__actions'>
<a href='/severed_relationships' className='button' target='_blank' rel='noopener noreferrer'>
<FormattedMessage id='relationship_severance_notification.view' defaultMessage='View' />
</a>
</div>
</div>
</div>
);
};
RelationshipsSeveranceEvent.propTypes = {
event: ImmutablePropTypes.map.isRequired,
hidden: PropTypes.bool,
};
export default RelationshipsSeveranceEvent;

View File

@ -471,7 +471,6 @@
"notification.own_poll": "Your poll has ended", "notification.own_poll": "Your poll has ended",
"notification.poll": "A poll you have voted in has ended", "notification.poll": "A poll you have voted in has ended",
"notification.reblog": "{name} boosted your post", "notification.reblog": "{name} boosted your post",
"notification.severed_relationships": "Relationships with {name} severed",
"notification.status": "{name} just posted", "notification.status": "{name} just posted",
"notification.update": "{name} edited a post", "notification.update": "{name} edited a post",
"notification_requests.accept": "Accept", "notification_requests.accept": "Accept",
@ -588,12 +587,6 @@
"refresh": "Refresh", "refresh": "Refresh",
"regeneration_indicator.label": "Loading…", "regeneration_indicator.label": "Loading…",
"regeneration_indicator.sublabel": "Your home feed is being prepared!", "regeneration_indicator.sublabel": "Your home feed is being prepared!",
"relationship_severance_notification.purged_data": "purged by administrators",
"relationship_severance_notification.relationships": "{count, plural, one {# relationship} other {# relationships}}",
"relationship_severance_notification.types.account_suspension": "Account has been suspended",
"relationship_severance_notification.types.domain_block": "Domain has been suspended",
"relationship_severance_notification.types.user_domain_block": "You blocked this domain",
"relationship_severance_notification.view": "View",
"relative_time.days": "{number}d", "relative_time.days": "{number}d",
"relative_time.full.days": "{number, plural, one {# day} other {# days}} ago", "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago",
"relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago", "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago",

View File

@ -1,34 +0,0 @@
# frozen_string_literal: true
#
# == Schema Information
#
# Table name: account_relationship_severance_events
#
# id :bigint(8) not null, primary key
# account_id :bigint(8) not null
# relationship_severance_event_id :bigint(8) not null
# relationships_count :integer default(0), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class AccountRelationshipSeveranceEvent < ApplicationRecord
belongs_to :account
belongs_to :relationship_severance_event
delegate :severed_relationships,
:type,
:target_name,
:purged,
:purged?,
to: :relationship_severance_event,
prefix: false
before_create :set_relationships_count!
private
def set_relationships_count!
self.relationships_count = severed_relationships.about_local_account(account).count
end
end

View File

@ -83,11 +83,6 @@ module Account::Interactions
has_many :following, -> { order('follows.id desc') }, through: :active_relationships, source: :target_account has_many :following, -> { order('follows.id desc') }, through: :active_relationships, source: :target_account
has_many :followers, -> { order('follows.id desc') }, through: :passive_relationships, source: :account has_many :followers, -> { order('follows.id desc') }, through: :passive_relationships, source: :account
with_options class_name: 'SeveredRelationship', dependent: :destroy do
has_many :severed_relationships, foreign_key: 'local_account_id', inverse_of: :local_account
has_many :remote_severed_relationships, foreign_key: 'remote_account_id', inverse_of: :remote_account
end
# Account notes # Account notes
has_many :account_notes, dependent: :destroy has_many :account_notes, dependent: :destroy

View File

@ -48,18 +48,6 @@ module Account::Merging
record.update_attribute(:account_warning_id, id) record.update_attribute(:account_warning_id, id)
end end
SeveredRelationship.about_local_account(other_account).reorder(nil).find_each do |record|
record.update_attribute(:local_account_id, id)
rescue ActiveRecord::RecordNotUnique
next
end
SeveredRelationship.about_remote_account(other_account).reorder(nil).find_each do |record|
record.update_attribute(:remote_account_id, id)
rescue ActiveRecord::RecordNotUnique
next
end
# Some follow relationships have moved, so the cache is stale # Some follow relationships have moved, so the cache is stale
Rails.cache.delete_matched("followers_hash:#{id}:*") Rails.cache.delete_matched("followers_hash:#{id}:*")
Rails.cache.delete_matched("relationships:#{id}:*") Rails.cache.delete_matched("relationships:#{id}:*")

View File

@ -54,9 +54,6 @@ class Notification < ApplicationRecord
update: { update: {
filterable: false, filterable: false,
}.freeze, }.freeze,
severed_relationships: {
filterable: false,
}.freeze,
'admin.sign_up': { 'admin.sign_up': {
filterable: false, filterable: false,
}.freeze, }.freeze,
@ -89,7 +86,6 @@ class Notification < ApplicationRecord
belongs_to :favourite, inverse_of: :notification belongs_to :favourite, inverse_of: :notification
belongs_to :poll, inverse_of: false belongs_to :poll, inverse_of: false
belongs_to :report, inverse_of: false belongs_to :report, inverse_of: false
belongs_to :account_relationship_severance_event, inverse_of: false
end end
validates :type, inclusion: { in: TYPES } validates :type, inclusion: { in: TYPES }
@ -186,11 +182,6 @@ class Notification < ApplicationRecord
self.from_account_id = activity&.status&.account_id self.from_account_id = activity&.status&.account_id
when 'Account' when 'Account'
self.from_account_id = activity&.id self.from_account_id = activity&.id
when 'AccountRelationshipSeveranceEvent'
# These do not really have an originating account, but this is mandatory
# in the data model, and the recipient's account will by definition
# always exist
self.from_account_id = account_id
end end
end end

View File

@ -1,56 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: relationship_severance_events
#
# id :bigint(8) not null, primary key
# type :integer not null
# target_name :string not null
# purged :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class RelationshipSeveranceEvent < ApplicationRecord
self.inheritance_column = nil
has_many :severed_relationships, inverse_of: :relationship_severance_event, dependent: :delete_all
enum :type, {
domain_block: 0,
user_domain_block: 1,
account_suspension: 2,
}
scope :about_local_account, ->(account) { where(id: SeveredRelationship.about_local_account(account).select(:relationship_severance_event_id)) }
def import_from_active_follows!(follows)
import_from_follows!(follows, true)
end
def import_from_passive_follows!(follows)
import_from_follows!(follows, false)
end
def affected_local_accounts
Account.where(id: severed_relationships.select(:local_account_id))
end
private
def import_from_follows!(follows, active)
SeveredRelationship.insert_all(
follows.pluck(:account_id, :target_account_id, :show_reblogs, :notify, :languages).map do |account_id, target_account_id, show_reblogs, notify, languages|
{
local_account_id: active ? account_id : target_account_id,
remote_account_id: active ? target_account_id : account_id,
show_reblogs: show_reblogs,
notify: notify,
languages: languages,
relationship_severance_event_id: id,
direction: active ? :active : :passive,
}
end
)
end
end

View File

@ -1,41 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: severed_relationships
#
# id :bigint(8) not null, primary key
# relationship_severance_event_id :bigint(8) not null
# local_account_id :bigint(8) not null
# remote_account_id :bigint(8) not null
# direction :integer not null
# show_reblogs :boolean
# notify :boolean
# languages :string is an Array
# created_at :datetime not null
# updated_at :datetime not null
#
class SeveredRelationship < ApplicationRecord
belongs_to :relationship_severance_event
belongs_to :local_account, class_name: 'Account'
belongs_to :remote_account, class_name: 'Account'
enum :direction, {
passive: 0, # analogous to `local_account.passive_relationships`
active: 1, # analogous to `local_account.active_relationships`
}
scope :about_local_account, ->(account) { where(local_account: account) }
scope :about_remote_account, ->(account) { where(remote_account: account) }
scope :active, -> { where(direction: :active) }
scope :passive, -> { where(direction: :passive) }
def account
active? ? local_account : remote_account
end
def target_account
active? ? remote_account : local_account
end
end

View File

@ -1,9 +0,0 @@
# frozen_string_literal: true
class REST::AccountRelationshipSeveranceEventSerializer < ActiveModel::Serializer
attributes :id, :type, :purged, :target_name, :created_at
def id
object.id.to_s
end
end

View File

@ -6,7 +6,6 @@ class REST::NotificationSerializer < ActiveModel::Serializer
belongs_to :from_account, key: :account, serializer: REST::AccountSerializer belongs_to :from_account, key: :account, serializer: REST::AccountSerializer
belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer
belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer
belongs_to :account_relationship_severance_event, key: :event, if: :relationship_severance_event?, serializer: REST::AccountRelationshipSeveranceEventSerializer
def id def id
object.id.to_s object.id.to_s
@ -19,8 +18,4 @@ class REST::NotificationSerializer < ActiveModel::Serializer
def report_type? def report_type?
object.type == :'admin.report' object.type == :'admin.report'
end end
def relationship_severance_event?
object.type == :severed_relationships
end
end end

View File

@ -9,20 +9,17 @@ class AfterBlockDomainFromAccountService < BaseService
def call(account, domain) def call(account, domain)
@account = account @account = account
@domain = domain @domain = domain
@domain_block_event = nil
clear_notifications! clear_notifications!
remove_follows! remove_follows!
reject_existing_followers! reject_existing_followers!
reject_pending_follow_requests! reject_pending_follow_requests!
notify_of_severed_relationships!
end end
private private
def remove_follows! def remove_follows!
@account.active_relationships.where(target_account: Account.where(domain: @domain)).includes(:target_account).reorder(nil).in_batches do |follows| @account.active_relationships.where(target_account: Account.where(domain: @domain)).includes(:target_account).reorder(nil).in_batches do |follows|
domain_block_event.import_from_active_follows!(follows)
follows.each { |follow| UnfollowService.new.call(@account, follow.target_account) } follows.each { |follow| UnfollowService.new.call(@account, follow.target_account) }
end end
end end
@ -33,7 +30,6 @@ class AfterBlockDomainFromAccountService < BaseService
def reject_existing_followers! def reject_existing_followers!
@account.passive_relationships.where(account: Account.where(domain: @domain)).includes(:account).reorder(nil).in_batches do |follows| @account.passive_relationships.where(account: Account.where(domain: @domain)).includes(:account).reorder(nil).in_batches do |follows|
domain_block_event.import_from_passive_follows!(follows)
follows.each { |follow| reject_follow!(follow) } follows.each { |follow| reject_follow!(follow) }
end end
end end
@ -51,15 +47,4 @@ class AfterBlockDomainFromAccountService < BaseService
ActivityPub::DeliveryWorker.perform_async(Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), @account.id, follow.account.inbox_url) ActivityPub::DeliveryWorker.perform_async(Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), @account.id, follow.account.inbox_url)
end end
def notify_of_severed_relationships!
return if @domain_block_event.nil?
event = AccountRelationshipSeveranceEvent.create!(account: @account, relationship_severance_event: @domain_block_event)
LocalNotificationWorker.perform_async(@account.id, event.id, 'AccountRelationshipSeveranceEvent', 'severed_relationships')
end
def domain_block_event
@domain_block_event ||= RelationshipSeveranceEvent.create!(type: :user_domain_block, target_name: @domain)
end
end end

View File

@ -9,7 +9,6 @@ class BlockDomainService < BaseService
process_domain_block! process_domain_block!
process_retroactive_updates! if update process_retroactive_updates! if update
notify_of_severed_relationships!
end end
private private
@ -40,17 +39,7 @@ class BlockDomainService < BaseService
blocked_domain_accounts.without_suspended.in_batches.update_all(suspended_at: @domain_block.created_at, suspension_origin: :local) blocked_domain_accounts.without_suspended.in_batches.update_all(suspended_at: @domain_block.created_at, suspension_origin: :local)
blocked_domain_accounts.where(suspended_at: @domain_block.created_at).reorder(nil).find_each do |account| blocked_domain_accounts.where(suspended_at: @domain_block.created_at).reorder(nil).find_each do |account|
DeleteAccountService.new.call(account, reserve_username: true, suspended_at: @domain_block.created_at, relationship_severance_event: domain_block_event) DeleteAccountService.new.call(account, reserve_username: true, suspended_at: @domain_block.created_at)
end
end
def notify_of_severed_relationships!
return if @domain_block_event.nil?
# TODO: check how efficient that query is, also check `push_bulk`/`perform_bulk`
@domain_block_event.affected_local_accounts.reorder(nil).find_each do |account|
event = AccountRelationshipSeveranceEvent.create!(account: account, relationship_severance_event: @domain_block_event)
LocalNotificationWorker.perform_async(account.id, event.id, 'AccountRelationshipSeveranceEvent', 'severed_relationships')
end end
end end
@ -58,10 +47,6 @@ class BlockDomainService < BaseService
domain_block.domain domain_block.domain
end end
def domain_block_event
@domain_block_event ||= RelationshipSeveranceEvent.create!(type: :domain_block, target_name: blocked_domain)
end
def blocked_domain_accounts def blocked_domain_accounts
Account.by_domain_and_subdomains(blocked_domain) Account.by_domain_and_subdomains(blocked_domain)
end end

View File

@ -58,8 +58,6 @@ class DeleteAccountService < BaseService
reports reports
targeted_moderation_notes targeted_moderation_notes
targeted_reports targeted_reports
severed_relationships
remote_severed_relationships
).freeze ).freeze
# Suspend or remove an account and remove as much of its data # Suspend or remove an account and remove as much of its data
@ -74,7 +72,6 @@ class DeleteAccountService < BaseService
# @option [Boolean] :skip_side_effects Side effects are ActivityPub and streaming API payloads # @option [Boolean] :skip_side_effects Side effects are ActivityPub and streaming API payloads
# @option [Boolean] :skip_activitypub Skip sending ActivityPub payloads. Implied by :skip_side_effects # @option [Boolean] :skip_activitypub Skip sending ActivityPub payloads. Implied by :skip_side_effects
# @option [Time] :suspended_at Only applicable when :reserve_username is true # @option [Time] :suspended_at Only applicable when :reserve_username is true
# @option [RelationshipSeveranceEvent] :relationship_severance_event Event used to record severed relationships not initiated by the user
def call(account, **options) def call(account, **options)
@account = account @account = account
@options = { reserve_username: true, reserve_email: true }.merge(options) @options = { reserve_username: true, reserve_email: true }.merge(options)
@ -87,7 +84,6 @@ class DeleteAccountService < BaseService
@options[:skip_activitypub] = true if @options[:skip_side_effects] @options[:skip_activitypub] = true if @options[:skip_side_effects]
record_severed_relationships!
distribute_activities! distribute_activities!
purge_content! purge_content!
fulfill_deletion_request! fulfill_deletion_request!
@ -270,20 +266,6 @@ class DeleteAccountService < BaseService
end end
end end
def record_severed_relationships!
return if relationship_severance_event.nil?
@account.active_relationships.in_batches do |follows|
# NOTE: these follows are passive with regards to the local accounts
relationship_severance_event.import_from_passive_follows!(follows)
end
@account.passive_relationships.in_batches do |follows|
# NOTE: these follows are active with regards to the local accounts
relationship_severance_event.import_from_active_follows!(follows)
end
end
def delete_actor_json def delete_actor_json
@delete_actor_json ||= Oj.dump(serialize_payload(@account, ActivityPub::DeleteActorSerializer, signer: @account, always_sign: true)) @delete_actor_json ||= Oj.dump(serialize_payload(@account, ActivityPub::DeleteActorSerializer, signer: @account, always_sign: true))
end end
@ -323,8 +305,4 @@ class DeleteAccountService < BaseService
def skip_activitypub? def skip_activitypub?
@options[:skip_activitypub] @options[:skip_activitypub]
end end
def relationship_severance_event
@options[:relationship_severance_event]
end
end end

View File

@ -9,8 +9,6 @@ class NotifyService < BaseService
update update
poll poll
status status
# TODO: this probably warrants an email notification
severed_relationships
).freeze ).freeze
class DismissCondition class DismissCondition
@ -22,7 +20,7 @@ class NotifyService < BaseService
def dismiss? def dismiss?
blocked = @recipient.unavailable? blocked = @recipient.unavailable?
blocked ||= from_self? && @notification.type != :poll && @notification.type != :severed_relationships blocked ||= from_self? && @notification.type != :poll
return blocked if message? && from_staff? return blocked if message? && from_staff?

View File

@ -4,17 +4,12 @@ class PurgeDomainService < BaseService
def call(domain) def call(domain)
@domain = domain @domain = domain
purge_relationship_severance_events!
purge_accounts! purge_accounts!
purge_emojis! purge_emojis!
Instance.refresh Instance.refresh
end end
def purge_relationship_severance_events!
RelationshipSeveranceEvent.where(type: [:domain_block, :user_domain_block], target_name: @domain).in_batches.update_all(purged: true)
end
def purge_accounts! def purge_accounts!
Account.remote.where(domain: @domain).reorder(nil).find_each do |account| Account.remote.where(domain: @domain).reorder(nil).find_each do |account|
DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true) DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true)

View File

@ -8,7 +8,6 @@ class SuspendAccountService < BaseService
def call(account) def call(account)
return unless account.suspended? return unless account.suspended?
@relationship_severance_event = nil
@account = account @account = account
reject_remote_follows! reject_remote_follows!
@ -16,7 +15,6 @@ class SuspendAccountService < BaseService
unmerge_from_home_timelines! unmerge_from_home_timelines!
unmerge_from_list_timelines! unmerge_from_list_timelines!
privatize_media_attachments! privatize_media_attachments!
notify_of_severed_relationships!
end end
private private
@ -38,8 +36,6 @@ class SuspendAccountService < BaseService
[Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), follow.target_account_id, @account.inbox_url] [Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), follow.target_account_id, @account.inbox_url]
end end
relationship_severance_event.import_from_passive_follows!(follows)
follows.each(&:destroy) follows.each(&:destroy)
end end
end end
@ -105,21 +101,7 @@ class SuspendAccountService < BaseService
end end
end end
def notify_of_severed_relationships!
return if @relationship_severance_event.nil?
# TODO: check how efficient that query is, also check `push_bulk`/`perform_bulk`
@relationship_severance_event.affected_local_accounts.reorder(nil).find_each do |account|
event = AccountRelationshipSeveranceEvent.create!(account: account, relationship_severance_event: @relationship_severance_event)
LocalNotificationWorker.perform_async(account.id, event.id, 'AccountRelationshipSeveranceEvent', 'severed_relationships')
end
end
def signed_activity_json def signed_activity_json
@signed_activity_json ||= Oj.dump(serialize_payload(@account, ActivityPub::UpdateSerializer, signer: @account)) @signed_activity_json ||= Oj.dump(serialize_payload(@account, ActivityPub::UpdateSerializer, signer: @account))
end end
def relationship_severance_event
@relationship_severance_event ||= RelationshipSeveranceEvent.create!(type: :account_suspension, target_name: @account.acct)
end
end end

View File

@ -1,34 +0,0 @@
- content_for :page_title do
= t('settings.severed_relationships')
%p.muted-hint= t('severed_relationships.preamble')
- unless @events.empty?
.table-wrapper
%table.table
%thead
%tr
%th= t('exports.archive_takeout.date')
%th= t('severed_relationships.type')
%th= t('severed_relationships.lost_follows')
%th= t('severed_relationships.lost_followers')
%tbody
- @events.each do |event|
%tr
%td= l event.created_at
%td= t("severed_relationships.event_type.#{event.type}", target_name: event.target_name)
- if event.purged?
%td{ rowspan: 2 }= t('severed_relationships.purged')
- else
%td
- count = event.severed_relationships.active.about_local_account(current_account).count
- if count.zero?
= t('generic.none')
- else
= table_link_to 'download', t('severed_relationships.download', count: count), following_severed_relationship_path(event, format: :csv)
%td
- count = event.severed_relationships.passive.about_local_account(current_account).count
- if count.zero?
= t('generic.none')
- else
= table_link_to 'download', t('severed_relationships.download', count: count), followers_severed_relationship_path(event, format: :csv)

View File

@ -1664,17 +1664,6 @@ bg:
strikes: Модериране на предупреждения strikes: Модериране на предупреждения
two_factor_authentication: Двустепенно удостоверяване two_factor_authentication: Двустепенно удостоверяване
webauthn_authentication: Ключове за сигурност webauthn_authentication: Ключове за сигурност
severed_relationships:
download: Изтегляне (%{count})
event_type:
account_suspension: Спиране на акаунта (%{target_name})
domain_block: Спиране на сървъра (%{target_name})
user_domain_block: Блокирахте %{target_name}
lost_followers: Загубени последователи
lost_follows: Загубени последвания
preamble: Може да загубите последванията и последователите, блокирайки домейн или когато модераторите ви решават да спрат отдалечен сървър. Случавайки се това, вие ще може да изтеглите списъците с прекъснати връзки, които да се проверят и възможно да се внесат на друг сървър.
purged: Сведенията за този сървър са били прочистени от администраторите на сървъра ви.
type: Събитие
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1664,17 +1664,6 @@ ca:
strikes: Accions de mediació strikes: Accions de mediació
two_factor_authentication: Autenticació de dos factors two_factor_authentication: Autenticació de dos factors
webauthn_authentication: Claus de seguretat webauthn_authentication: Claus de seguretat
severed_relationships:
download: Baixa (%{count})
event_type:
account_suspension: Suspensió del compte (%{target_name})
domain_block: Suspensió del servidor (%{target_name})
user_domain_block: Heu blocat %{target_name}
lost_followers: Seguidors perduts
lost_follows: Seguiments perduts
preamble: Quan bloqueu un domini o els vostres moderadors decideixen suspendre un servidor remot, es poden perdre seguidors i seguiments. Si això passa, podeu baixar-vos la llista de relacions trencades per a revisar-la i, si voleu, importar-les a un altre servidor.
purged: Els administradors del vostre servidor han purgat la informació relativa a aquest servidor.
type: Esdeveniment
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1664,16 +1664,6 @@ da:
strikes: Moderationsadvarsler strikes: Moderationsadvarsler
two_factor_authentication: Tofaktorgodkendelse two_factor_authentication: Tofaktorgodkendelse
webauthn_authentication: Sikkerhedsnøgler webauthn_authentication: Sikkerhedsnøgler
severed_relationships:
download: Download (%{count})
event_type:
account_suspension: Kontosuspendering (%{target_name})
domain_block: Serversuspendering (%{target_name})
user_domain_block: "%{target_name} blev blokeret"
lost_followers: Tabte følgere
preamble: Der kan mistes fulgte objekter og følgere, når et domæne blokeres eller moderatorerne beslutter at suspendere en ekstern server. Når det sker, kan der downloades lister over afbrudte relationer til inspektion og mulig import på anden server.
purged: Oplysninger om denne server er blevet renset af serveradministratoreren.
type: Begivenhed
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1665,17 +1665,6 @@ en:
strikes: Moderation strikes strikes: Moderation strikes
two_factor_authentication: Two-factor Auth two_factor_authentication: Two-factor Auth
webauthn_authentication: Security keys webauthn_authentication: Security keys
severed_relationships:
download: Download (%{count})
event_type:
account_suspension: Account suspension (%{target_name})
domain_block: Server suspension (%{target_name})
user_domain_block: You blocked %{target_name}
lost_followers: Lost followers
lost_follows: Lost follows
preamble: You may lose follows and followers when you block a domain or when your moderators decide to suspend a remote server. When that happens, you will be able to download lists of severed relationships, to be inspected and possibly imported on another server.
purged: Information about this server has been purged by your server's administrators.
type: Event
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1668,17 +1668,6 @@ eu:
strikes: Moderazio neurriak strikes: Moderazio neurriak
two_factor_authentication: Bi faktoreetako autentifikazioa two_factor_authentication: Bi faktoreetako autentifikazioa
webauthn_authentication: Segurtasun gakoak webauthn_authentication: Segurtasun gakoak
severed_relationships:
download: Deskargatu (%{count})
event_type:
account_suspension: Kontua bertan behera uztea (%{target_name})
domain_block: Domeinua bertan behera uztea (%{target_name})
user_domain_block: "%{target_name} blokeatu duzu"
lost_followers: Galdutako jarraitzaileak
lost_follows: Jarraitzeari utzi diozun jendea
preamble: Jarraitzaileak gal ditzakezu, baita jendea jarraitzeari utzi domeinu bat blokeatzen duzunean edo moderatzaileek urruneko zerbitzari bat bertan behera uztea erabakitzen badute. Hau gertatzean, galdutako erlazioen zerrendak deskargatu ahalko dituzu, aztertzeko eta agian, beste zerbitzari batean inportatzeko.
purged: Zerbitzari honen informazioa kendu du zerbitzariko administratzaileak.
type: Gertaera
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1664,17 +1664,6 @@ fi:
strikes: Valvontavaroitukset strikes: Valvontavaroitukset
two_factor_authentication: Kaksivaiheinen todennus two_factor_authentication: Kaksivaiheinen todennus
webauthn_authentication: Suojausavaimet webauthn_authentication: Suojausavaimet
severed_relationships:
download: Lataa (%{count})
event_type:
account_suspension: Tilin jäädytys (%{target_name})
domain_block: Palvelimen jäädytys (%{target_name})
user_domain_block: Estit käyttäjän %{target_name}
lost_followers: Menetetyt seuraajat
lost_follows: Menetetyt seuratut
preamble: Voit menettää seurattusi ja seuraajasi, kun estät verkkotunnuksen tai kun valvojasi päättävät jäädyttää etäpalvelimen. Kun näin tapahtuu, voit ladata luetteloita katkenneista suhteista, jotta voit tarkastella niitä ja mahdollisesti viedä ne toiselle palvelimelle.
purged: Palvelimesi ylläpitäjät ovat tyhjentäneet tämän palvelimen tiedot.
type: Tapahtuma
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1664,17 +1664,6 @@ fo:
strikes: Umsjónaratsóknir strikes: Umsjónaratsóknir
two_factor_authentication: Váttan í tveimum stigum two_factor_authentication: Váttan í tveimum stigum
webauthn_authentication: Trygdarlyklar webauthn_authentication: Trygdarlyklar
severed_relationships:
download: Niðurtøkur (%{count})
event_type:
account_suspension: Kontuógilding (%{target_name})
domain_block: Ambætara-ógilding (%{target_name})
user_domain_block: Tú hevur forðað %{target_name}
lost_followers: Mistir fylgjarar
lost_follows: Mist fylgi
preamble: Tá ið tú forðar einum økisnavni, ella tínir umsitarar velja at avgilda ein fjarambætara, kanst tú missa fylgi og fylgjarar. Um tað hendir, kanst tú taka ein lista av avbrotnum tilknýtum niður, sum tú kanst eftirhyggja og møguliga innflyta á ein nýggjan ambætara.
purged: Umsitararnir fyri tín ambætara hava tømt upplýsingar um henda ambætara úr skipanini.
type: Tiltak
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1716,16 +1716,6 @@ gd:
strikes: Rabhaidhean na maorsainneachd strikes: Rabhaidhean na maorsainneachd
two_factor_authentication: Dearbhadh dà-cheumnach two_factor_authentication: Dearbhadh dà-cheumnach
webauthn_authentication: Iuchraichean tèarainteachd webauthn_authentication: Iuchraichean tèarainteachd
severed_relationships:
download: Luchdaich a-nuas (%{count})
event_type:
account_suspension: Cunntas à rèim (%{target_name})
domain_block: Frithealaiche à rèim (%{target_name})
user_domain_block: Bhac thu %{target_name}
lost_followers: An luchd-leantainn a chaill thu
lost_follows: Daoine nach lean thu tuilleadh
purged: Chaidh am fiosrachadh mun fhrithealaiche seo a phurgaideachadh le rianairean an fhrithealaiche agad.
type: Tachartas
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1664,17 +1664,6 @@ hu:
strikes: Moderációs felrótt vétségek strikes: Moderációs felrótt vétségek
two_factor_authentication: Kétlépcsős hitelesítés two_factor_authentication: Kétlépcsős hitelesítés
webauthn_authentication: Biztonsági kulcsok webauthn_authentication: Biztonsági kulcsok
severed_relationships:
download: Letöltés (%{count})
event_type:
account_suspension: Fiók felfüggesztése (%{target_name})
domain_block: Kiszolgáló felfüggesztése (%{target_name})
user_domain_block: 'Blokkoltad ezt: %{target_name}'
lost_followers: Elvesztett követők
lost_follows: Elvesztett követések
preamble: Ha blokkolsz egy domaint, vagy ha a moderátorok úgy döntenek, hogy blokkolnak egy távoli kiszolgálót, akkor követőket és követéseket veszíthetsz. Amikor ez megtörténik, akkor letöltheted a megszakadt kapcsolatokat, hogy átvizsgáld őket, és esetleg egy másik kiszolgálón importáld őket.
purged: A kiszolgáló információit a kiszolgálód rendszergazdái véglegesen eltávolították.
type: Esemény
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1640,17 +1640,6 @@ ko:
strikes: 중재 기록 strikes: 중재 기록
two_factor_authentication: 2단계 인증 two_factor_authentication: 2단계 인증
webauthn_authentication: 보안 키 webauthn_authentication: 보안 키
severed_relationships:
download: 다운로드 (%{count})
event_type:
account_suspension: 계정 정지 (%{target_name})
domain_block: 서버 정지 (%{target_name})
user_domain_block: 내가 %{target_name}을 차단했습니다
lost_followers: 잃은 팔로워
lost_follows: 잃은 팔로우
preamble: 내가 도메인을 차단하거나 중재진이 다른 서버를 정지하기로 결정했다면 내 팔로우와 팔로워를 잃게 됩니다. 그런 일이 일어났다면 그로 인해 단절된 관계들의 목록을 다운로드 받아 확인하고 다른 서버에서 불러올 수 있습니다.
purged: 이 서버에 대한 정보는 관리자에 의해 삭제되었습니다.
type: 이벤트
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1663,9 +1663,6 @@ lad:
strikes: Amonestamientos de moderasyon strikes: Amonestamientos de moderasyon
two_factor_authentication: Autentifikasyon en dos pasos two_factor_authentication: Autentifikasyon en dos pasos
webauthn_authentication: Yaves de sigurita webauthn_authentication: Yaves de sigurita
severed_relationships:
download: Abasha (%{count})
type: Evenimiento
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1664,17 +1664,6 @@ nl:
strikes: Vastgestelde overtredingen strikes: Vastgestelde overtredingen
two_factor_authentication: Tweestapsverificatie two_factor_authentication: Tweestapsverificatie
webauthn_authentication: Beveiligingssleutels webauthn_authentication: Beveiligingssleutels
severed_relationships:
download: Downloaden (%{count})
event_type:
account_suspension: Accountschorsing (%{target_name})
domain_block: Serverschorsing (%{target_name})
user_domain_block: Je hebt %{target_name} geblokkeerd
lost_followers: Verloren volgers
lost_follows: Verloren gevolgde accounts
preamble: Je kan gevolgde accounts en volgers verliezen wanneer je een domein blokkeert of wanneer de moderators van jouw server beslissen om een externe server op te schorten. Wanneer dat gebeurt, kun je lijsten van verbroken volgrelaties downloaden, deze inspecteren en mogelijk importeren op een andere server.
purged: Informatie over deze server is verwijderd door de beheerders van jouw server.
type: Gebeurtenis
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1664,17 +1664,6 @@ nn:
strikes: Modereringsadvarsler strikes: Modereringsadvarsler
two_factor_authentication: Tostegsautorisering two_factor_authentication: Tostegsautorisering
webauthn_authentication: Sikkerhetsnøkler webauthn_authentication: Sikkerhetsnøkler
severed_relationships:
download: Last ned (%{count})
event_type:
account_suspension: Utestenging av konto (%{target_name})
domain_block: Tenarutestenging (%{target_name})
user_domain_block: Du blokkerte %{target_name}
lost_followers: Mista fylgjarar
lost_follows: Mista fylgjer
preamble: Du kan mista fylgjarar og folk du fylgjer når du blokkerer eit domene eller når moderatorar avgjer å utestenga ein annan tenar. Når det skjer, vil du kunne lasta ned ei liste over brotne forhold, slik at du kan sjå gjennom ho og kanskje importera ho på ein annan tenar.
purged: Informasjonen om denne tenaren er sletta av administratorane på tenaren din.
type: Hending
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1716,17 +1716,6 @@ pl:
strikes: Ostrzeżenia moderacyjne strikes: Ostrzeżenia moderacyjne
two_factor_authentication: Uwierzytelnianie dwuetapowe two_factor_authentication: Uwierzytelnianie dwuetapowe
webauthn_authentication: Klucze bezpieczeństwa webauthn_authentication: Klucze bezpieczeństwa
severed_relationships:
download: Pobierz (%{count})
event_type:
account_suspension: Zawieszenie konta (%{target_name})
domain_block: Zawieszenie serwera (%{target_name})
user_domain_block: Zablokowano %{target_name}
lost_followers: Utraceni obserwujący
lost_follows: Utracone obserwowania
preamble: Możesz stracić obserwowania i obserwujących kiedy zablokujesz domenę albo kiedy twoi moderatorzy postanowią zawiesić obcy serwer. W tym wypadku będziesz móc pobrać listę zerwanych związków do przejrzenia i potencjalnego importu na innym serwerze.
purged: Informacje o tym serwerze zostały wyczyszczone przez administratora twojego serwera.
type: Zdarzenie
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1199,9 +1199,6 @@ sk:
profile: Profil profile: Profil
relationships: Sledovania a následovatelia relationships: Sledovania a následovatelia
two_factor_authentication: Dvojfázové overenie two_factor_authentication: Dvojfázové overenie
severed_relationships:
lost_followers: Stratení nasledovatelia
lost_follows: Stratené sledovania
statuses: statuses:
attached: attached:
description: 'Priložené: %{attached}' description: 'Priložené: %{attached}'

View File

@ -1716,17 +1716,6 @@ sl:
strikes: Ukrepi morediranja strikes: Ukrepi morediranja
two_factor_authentication: Dvofaktorsko overjanje two_factor_authentication: Dvofaktorsko overjanje
webauthn_authentication: Varnostni ključi webauthn_authentication: Varnostni ključi
severed_relationships:
download: Prejmi (%{count})
event_type:
account_suspension: Suspendiranje računa (%{target_name})
domain_block: Suspendiranje strežnika (%{target_name})
user_domain_block: Blokirali ste %{target_name}
lost_followers: Izgubljeni sledilci
lost_follows: Izgubljena sledeni
preamble: Če blokirate domeno ali če se vaši moderatorji odločijo suspendirati oddaljen strežnik, lahko izgubite sledene in sledilce. Ko se to zgodi, boste lahko prejeli sezname prekinjenih vez za pregled in morebitno uvažanje na drugem strežniku.
purged: Podatke o tem strežniku so očistili skrbniki vašega strežnika.
type: Dogodek
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1659,13 +1659,6 @@ sq:
strikes: Paralajmërime nga moderimi strikes: Paralajmërime nga moderimi
two_factor_authentication: Mirëfilltësim Dyfaktorësh two_factor_authentication: Mirëfilltësim Dyfaktorësh
webauthn_authentication: Kyçe sigurie webauthn_authentication: Kyçe sigurie
severed_relationships:
event_type:
account_suspension: Pezullim llogarie (%{target_name})
domain_block: Pezullim shërbyesi (%{target_name})
user_domain_block: Bllokuat %{target_name}
purged: Hollësitë rreth këtij shërbyesi janë spastuar nga përgjegjësit e shërbyesit tuaj.
type: Akt
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1716,17 +1716,6 @@ uk:
strikes: Попередження модераторів strikes: Попередження модераторів
two_factor_authentication: Двофакторна автентифікація two_factor_authentication: Двофакторна автентифікація
webauthn_authentication: Ключі безпеки webauthn_authentication: Ключі безпеки
severed_relationships:
download: Завантажити (%{count})
event_type:
account_suspension: Призупинення облікового запису (%{target_name})
domain_block: Призупинення сервера (%{target_name})
user_domain_block: Ви заблокували %{target_name}
lost_followers: Втрачені підписники
lost_follows: Втрачені підписки
preamble: Ви можете втратити підписки та підписників, коли ви блокуєте домен або коли модератори вирішують призупинити віддалений сервер. Коли це станеться, ви зможете завантажити списки розірваних зв'язків, які мають бути перевірені та, можливо, імпортовані на іншому сервері.
purged: Інформацію про цей сервер очищені адміністраторами вашого сервера.
type: Подія
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1638,17 +1638,6 @@ zh-CN:
strikes: 操作记录 strikes: 操作记录
two_factor_authentication: 双因素认证 two_factor_authentication: 双因素认证
webauthn_authentication: 安全密钥 webauthn_authentication: 安全密钥
severed_relationships:
download: 下载 (%{count})
event_type:
account_suspension: 账户被封禁 (%{target_name})
domain_block: 服务器被封禁 (%{target_name})
user_domain_block: 你屏蔽了 %{target_name}
lost_followers: 失去的关注者
lost_follows: 失去的关注
preamble: 当你屏蔽一个域名或你的管理员决定封禁一个外站服务器时,你可能会失去关注和粉丝。在这种情况下,你可以下载被切断的关系的列表,进行检查以便导入另一个服务器。
purged: 关于此服务器的信息已被你所在服务器的管理员清除。
type: 事件
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -1640,17 +1640,6 @@ zh-TW:
strikes: 管理警告 strikes: 管理警告
two_factor_authentication: 兩階段驗證 two_factor_authentication: 兩階段驗證
webauthn_authentication: 安全金鑰 webauthn_authentication: 安全金鑰
severed_relationships:
download: 下載 (%{count} 份)
event_type:
account_suspension: 停權帳號 (%{target_name})
domain_block: 停權伺服器 (%{target_name})
user_domain_block: 您已封鎖 %{target_name}
lost_followers: 失去之跟隨者
lost_follows: 失去之跟隨中
preamble: 當您封鎖網域或您的管理員決定停權遠端伺服器時,您可能失去跟隨中或跟隨者。當此發生時,您能下載斷絕服務關係之列表並於其他伺服器中檢視或匯入資料。
purged: 關於此伺服器已被您的伺服器管理員清除之資訊
type: 事件
statuses: statuses:
attached: attached:
audio: audio:

View File

@ -39,7 +39,6 @@ module Mastodon::CLI
class Webhook < ApplicationRecord; end class Webhook < ApplicationRecord; end
class BulkImport < ApplicationRecord; end class BulkImport < ApplicationRecord; end
class SoftwareUpdate < ApplicationRecord; end class SoftwareUpdate < ApplicationRecord; end
class SeveredRelationship < ApplicationRecord; end
class DomainBlock < ApplicationRecord class DomainBlock < ApplicationRecord
enum :severity, { silence: 0, suspend: 1, noop: 2 } enum :severity, { silence: 0, suspend: 1, noop: 2 }
@ -130,20 +129,6 @@ module Mastodon::CLI
record.update_attribute(:account_warning_id, id) record.update_attribute(:account_warning_id, id)
end end
end end
if db_table_exists?(:severed_relationships)
SeveredRelationship.where(local_account_id: other_account.id).reorder(nil).find_each do |record|
record.update_attribute(:local_account_id, id)
rescue ActiveRecord::RecordNotUnique
next
end
SeveredRelationship.where(remote_account_id: other_account.id).reorder(nil).find_each do |record|
record.update_attribute(:remote_account_id, id)
rescue ActiveRecord::RecordNotUnique
next
end
end
end end
end end

View File

@ -1,6 +0,0 @@
# frozen_string_literal: true
Fabricator(:account_relationship_severance_event) do
account
relationship_severance_event
end

View File

@ -1,6 +0,0 @@
# frozen_string_literal: true
Fabricator(:relationship_severance_event) do
type { :domain_block }
target_name { 'example.com' }
end

View File

@ -1,8 +0,0 @@
# frozen_string_literal: true
Fabricator(:severed_relationship) do
local_account { Fabricate.build(:account) }
remote_account { Fabricate.build(:account) }
relationship_severance_event { Fabricate.build(:relationship_severance_event) }
direction { :active }
end

View File

@ -1,49 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe RelationshipSeveranceEvent do
let(:local_account) { Fabricate(:account) }
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
let(:event) { Fabricate(:relationship_severance_event) }
describe '#import_from_active_follows!' do
before do
local_account.follow!(remote_account)
end
it 'imports the follow relationships with the expected direction' do
event.import_from_active_follows!(local_account.active_relationships)
relationships = event.severed_relationships.to_a
expect(relationships.size).to eq 1
expect(relationships[0].account).to eq local_account
expect(relationships[0].target_account).to eq remote_account
end
end
describe '#import_from_passive_follows!' do
before do
remote_account.follow!(local_account)
end
it 'imports the follow relationships with the expected direction' do
event.import_from_passive_follows!(local_account.passive_relationships)
relationships = event.severed_relationships.to_a
expect(relationships.size).to eq 1
expect(relationships[0].account).to eq remote_account
expect(relationships[0].target_account).to eq local_account
end
end
describe '#affected_local_accounts' do
before do
event.severed_relationships.create!(local_account: local_account, remote_account: remote_account, direction: :active)
end
it 'correctly lists local accounts' do
expect(event.affected_local_accounts.to_a).to contain_exactly(local_account)
end
end
end

View File

@ -1,45 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SeveredRelationship do
let(:local_account) { Fabricate(:account) }
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
let(:event) { Fabricate(:relationship_severance_event) }
describe '#account' do
context 'when the local account is the follower' do
let(:severed_relationship) { Fabricate(:severed_relationship, relationship_severance_event: event, local_account: local_account, remote_account: remote_account, direction: :active) }
it 'returns the local account' do
expect(severed_relationship.account).to eq local_account
end
end
context 'when the local account is being followed' do
let(:severed_relationship) { Fabricate(:severed_relationship, relationship_severance_event: event, local_account: local_account, remote_account: remote_account, direction: :passive) }
it 'returns the remote account' do
expect(severed_relationship.account).to eq remote_account
end
end
end
describe '#target_account' do
context 'when the local account is the follower' do
let(:severed_relationship) { Fabricate(:severed_relationship, relationship_severance_event: event, local_account: local_account, remote_account: remote_account, direction: :active) }
it 'returns the remote account' do
expect(severed_relationship.target_account).to eq remote_account
end
end
context 'when the local account is being followed' do
let(:severed_relationship) { Fabricate(:severed_relationship, relationship_severance_event: event, local_account: local_account, remote_account: remote_account, direction: :passive) }
it 'returns the local account' do
expect(severed_relationship.target_account).to eq local_account
end
end
end
end

View File

@ -1,24 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Severed relationships page' do
include RoutingHelper
describe 'GET severed_relationships#index' do
let(:user) { Fabricate(:user) }
let(:event) { Fabricate(:account_relationship_severance_event, account: user.account) }
before do
sign_in user
Fabricate.times(3, :severed_relationship, local_account: user.account, relationship_severance_event: event.relationship_severance_event)
end
it 'returns http success' do
get severed_relationships_path
expect(response).to have_http_status(200)
end
end
end

View File

@ -20,7 +20,7 @@ RSpec.describe AfterBlockDomainFromAccountService do
end end
end end
it 'purges followers from blocked domain, sends them Reject->Follow, and records severed relationships', :aggregate_failures do it 'purges followers from blocked domain, sends them Reject->Follow', :aggregate_failures do
subject.call(alice, 'evil.org') subject.call(alice, 'evil.org')
expect(wolf.following?(alice)).to be false expect(wolf.following?(alice)).to be false
@ -28,10 +28,5 @@ RSpec.describe AfterBlockDomainFromAccountService do
[a_string_including('"type":"Reject"'), alice.id, wolf.inbox_url], [a_string_including('"type":"Reject"'), alice.id, wolf.inbox_url],
[a_string_including('"type":"Undo"'), alice.id, dog.inbox_url] [a_string_including('"type":"Undo"'), alice.id, dog.inbox_url]
) )
severed_relationships = alice.severed_relationships.to_a
expect(severed_relationships.count).to eq 2
expect(severed_relationships[0].relationship_severance_event).to eq severed_relationships[1].relationship_severance_event
expect(severed_relationships.map { |rel| [rel.account, rel.target_account] }).to contain_exactly([wolf, alice], [alice, dog])
end end
end end

View File

@ -19,7 +19,7 @@ RSpec.describe BlockDomainService do
bystander.follow!(local_account) bystander.follow!(local_account)
end end
it 'creates a domain block, suspends remote accounts with appropriate suspension date, records severed relationships', :aggregate_failures do it 'creates a domain block, suspends remote accounts with appropriate suspension date', :aggregate_failures do
subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend)) subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend))
expect(DomainBlock.blocked?('evil.org')).to be true expect(DomainBlock.blocked?('evil.org')).to be true
@ -36,12 +36,6 @@ RSpec.describe BlockDomainService do
expect { bad_status_plain.reload }.to raise_exception ActiveRecord::RecordNotFound expect { bad_status_plain.reload }.to raise_exception ActiveRecord::RecordNotFound
expect { bad_status_with_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound expect { bad_status_with_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound
expect { bad_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound expect { bad_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound
# Records severed relationships
severed_relationships = local_account.severed_relationships.to_a
expect(severed_relationships.count).to eq 2
expect(severed_relationships[0].relationship_severance_event).to eq severed_relationships[1].relationship_severance_event
expect(severed_relationships.map { |rel| [rel.account, rel.target_account] }).to contain_exactly([bystander, local_account], [local_account, bad_account])
end end
end end

View File

@ -85,14 +85,10 @@ RSpec.describe SuspendAccountService, :sidekiq_inline do
account.follow!(local_followee) account.follow!(local_followee)
end end
it 'sends a Reject Follow activity, and records severed relationships', :aggregate_failures do it 'sends a Reject Follow activity' do
subject subject
expect(a_request(:post, account.inbox_url).with { |req| match_reject_follow_request(req, account, local_followee) }).to have_been_made.once expect(a_request(:post, account.inbox_url).with { |req| match_reject_follow_request(req, account, local_followee) }).to have_been_made.once
severed_relationships = local_followee.severed_relationships.to_a
expect(severed_relationships.count).to eq 1
expect(severed_relationships.map { |rel| [rel.account, rel.target_account] }).to contain_exactly([account, local_followee])
end end
end end
end end