forked from cybrespace/mastodon
		
	Add domain block notes (#11515)
* Add database columns for adding notes to domain blocks/restrctions * Add admin UI to set private and public comments when blocking a domain * Add text for private and public comments on domain blocks * Show domain block comments in admin UI * Add comments to the domain block undo page * Make UnblockDomainService more robust regarding upgraded domain blocks * Allow editing domain blocks * Rename button from “undo domain block” to “view domain block” in account admin UI * Change test to unsilence silenced users from upgraded blocks
This commit is contained in:
		
							parent
							
								
									94c54997cf
								
							
						
					
					
						commit
						bced70469a
					
				
					 17 changed files with 138 additions and 34 deletions
				
			
		| 
						 | 
				
			
			@ -2,13 +2,17 @@
 | 
			
		|||
 | 
			
		||||
module Admin
 | 
			
		||||
  class DomainBlocksController < BaseController
 | 
			
		||||
    before_action :set_domain_block, only: [:show, :destroy]
 | 
			
		||||
    before_action :set_domain_block, only: [:show, :destroy, :edit, :update]
 | 
			
		||||
 | 
			
		||||
    def new
 | 
			
		||||
      authorize :domain_block, :create?
 | 
			
		||||
      @domain_block = DomainBlock.new(domain: params[:_domain])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def edit
 | 
			
		||||
      authorize :domain_block, :create?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def create
 | 
			
		||||
      authorize :domain_block, :create?
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +39,22 @@ module Admin
 | 
			
		|||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def update
 | 
			
		||||
      authorize :domain_block, :create?
 | 
			
		||||
 | 
			
		||||
      @domain_block.update(update_params)
 | 
			
		||||
 | 
			
		||||
      severity_changed = @domain_block.severity_changed?
 | 
			
		||||
 | 
			
		||||
      if @domain_block.save
 | 
			
		||||
        DomainBlockWorker.perform_async(@domain_block.id, severity_changed)
 | 
			
		||||
        log_action :create, @domain_block
 | 
			
		||||
        redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
 | 
			
		||||
      else
 | 
			
		||||
        render :edit
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def show
 | 
			
		||||
      authorize @domain_block, :show?
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -52,8 +72,12 @@ module Admin
 | 
			
		|||
      @domain_block = DomainBlock.find(params[:id])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def update_params
 | 
			
		||||
      params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def resource_params
 | 
			
		||||
      params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports)
 | 
			
		||||
      params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,8 @@ module Admin
 | 
			
		|||
      @blocks_count    = Block.where(target_account: Account.where(domain: params[:id])).count
 | 
			
		||||
      @available       = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
 | 
			
		||||
      @media_storage   = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
 | 
			
		||||
      @private_comment = @domain_block&.private_comment
 | 
			
		||||
      @public_comment  = @domain_block&.public_comment
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,13 +3,15 @@
 | 
			
		|||
#
 | 
			
		||||
# Table name: domain_blocks
 | 
			
		||||
#
 | 
			
		||||
#  id             :bigint(8)        not null, primary key
 | 
			
		||||
#  domain         :string           default(""), not null
 | 
			
		||||
#  created_at     :datetime         not null
 | 
			
		||||
#  updated_at     :datetime         not null
 | 
			
		||||
#  severity       :integer          default("silence")
 | 
			
		||||
#  reject_media   :boolean          default(FALSE), not null
 | 
			
		||||
#  reject_reports :boolean          default(FALSE), not null
 | 
			
		||||
#  id              :bigint(8)        not null, primary key
 | 
			
		||||
#  domain          :string           default(""), not null
 | 
			
		||||
#  created_at      :datetime         not null
 | 
			
		||||
#  updated_at      :datetime         not null
 | 
			
		||||
#  severity        :integer          default("silence")
 | 
			
		||||
#  reject_media    :boolean          default(FALSE), not null
 | 
			
		||||
#  reject_reports  :boolean          default(FALSE), not null
 | 
			
		||||
#  private_comment :text
 | 
			
		||||
#  public_comment  :text
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class DomainBlock < ApplicationRecord
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,13 +3,22 @@
 | 
			
		|||
class BlockDomainService < BaseService
 | 
			
		||||
  attr_reader :domain_block
 | 
			
		||||
 | 
			
		||||
  def call(domain_block)
 | 
			
		||||
  def call(domain_block, update = false)
 | 
			
		||||
    @domain_block = domain_block
 | 
			
		||||
    process_domain_block!
 | 
			
		||||
    process_retroactive_updates! if update
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def process_retroactive_updates!
 | 
			
		||||
    # If the domain block severity has been changed, undo the appropriate limitations
 | 
			
		||||
    scope = Account.by_domain_and_subdomains(domain_block.domain)
 | 
			
		||||
 | 
			
		||||
    scope.where(silenced_at: domain_block.created_at).in_batches.update_all(silenced_at: nil) unless domain_block.silence?
 | 
			
		||||
    scope.where(suspended_at: domain_block.created_at).in_batches.update_all(suspended_at: nil) unless domain_block.suspend?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def process_domain_block!
 | 
			
		||||
    clear_media! if domain_block.reject_media?
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,24 +10,9 @@ class UnblockDomainService < BaseService
 | 
			
		|||
  end
 | 
			
		||||
 | 
			
		||||
  def process_retroactive_updates
 | 
			
		||||
    blocked_accounts.in_batches.update_all(update_options) unless domain_block.noop?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def blocked_accounts
 | 
			
		||||
    scope = Account.by_domain_and_subdomains(domain_block.domain)
 | 
			
		||||
 | 
			
		||||
    if domain_block.silence?
 | 
			
		||||
      scope.where(silenced_at: @domain_block.created_at)
 | 
			
		||||
    else
 | 
			
		||||
      scope.where(suspended_at: @domain_block.created_at)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update_options
 | 
			
		||||
    { domain_block_impact => nil }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def domain_block_impact
 | 
			
		||||
    domain_block.silence? ? :silenced_at : :suspended_at
 | 
			
		||||
    scope.where(silenced_at: domain_block.created_at).in_batches.update_all(silenced_at: nil) unless domain_block.noop?
 | 
			
		||||
    scope.where(suspended_at: domain_block.created_at).in_batches.update_all(suspended_at: nil) if domain_block.suspend?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -174,7 +174,7 @@
 | 
			
		|||
 | 
			
		||||
      - unless @account.local?
 | 
			
		||||
        - if DomainBlock.where(domain: @account.domain).exists?
 | 
			
		||||
          = link_to t('admin.domain_blocks.undo'), admin_instance_path(@account.domain), class: 'button'
 | 
			
		||||
          = link_to t('admin.domain_blocks.view'), admin_instance_path(@account.domain), class: 'button'
 | 
			
		||||
        - else
 | 
			
		||||
          = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain), class: 'button button--destructive'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								app/views/admin/domain_blocks/edit.html.haml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/views/admin/domain_blocks/edit.html.haml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
- content_for :header_tags do
 | 
			
		||||
  = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
 | 
			
		||||
 | 
			
		||||
- content_for :page_title do
 | 
			
		||||
  = t('admin.domain_blocks.edit')
 | 
			
		||||
 | 
			
		||||
= simple_form_for @domain_block, url: admin_domain_block_path(@domain_block), method: :put do |f|
 | 
			
		||||
  = render 'shared/error_messages', object: @domain_block
 | 
			
		||||
 | 
			
		||||
  .fields-row
 | 
			
		||||
    .fields-row__column.fields-row__column-6.fields-group
 | 
			
		||||
      = f.input :domain, wrapper: :with_label, label: t('admin.domain_blocks.domain'), hint: t('admin.domain_blocks.new.hint'), required: true, readonly: true, disabled: true
 | 
			
		||||
 | 
			
		||||
    .fields-row__column.fields-row__column-6.fields-group
 | 
			
		||||
      = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| t("admin.domain_blocks.new.severity.#{type}") }, hint: t('admin.domain_blocks.new.severity.desc_html')
 | 
			
		||||
 | 
			
		||||
  .fields-group
 | 
			
		||||
    = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint')
 | 
			
		||||
 | 
			
		||||
  .fields-group
 | 
			
		||||
    = f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint')
 | 
			
		||||
 | 
			
		||||
  .field-group
 | 
			
		||||
    = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6
 | 
			
		||||
 | 
			
		||||
  .field-group
 | 
			
		||||
    = f.input :public_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.public_comment'), hint: t('admin.domain_blocks.public_comment_hint'), rows: 6
 | 
			
		||||
 | 
			
		||||
  .actions
 | 
			
		||||
    = f.button :button, t('generic.save_changes'), type: :submit
 | 
			
		||||
| 
						 | 
				
			
			@ -20,5 +20,11 @@
 | 
			
		|||
  .fields-group
 | 
			
		||||
    = f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint')
 | 
			
		||||
 | 
			
		||||
  .field-group
 | 
			
		||||
    = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6
 | 
			
		||||
 | 
			
		||||
  .field-group
 | 
			
		||||
    = f.input :public_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.public_comment'), hint: t('admin.domain_blocks.public_comment_hint'), rows: 6
 | 
			
		||||
 | 
			
		||||
  .actions
 | 
			
		||||
    = f.button :button, t('.create'), type: :submit
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,18 @@
 | 
			
		|||
- content_for :page_title do
 | 
			
		||||
  = t('admin.domain_blocks.show.title', domain: @domain_block.domain)
 | 
			
		||||
 | 
			
		||||
- if @domain_block.private_comment.present?
 | 
			
		||||
  .speech-bubble
 | 
			
		||||
    .speech-bubble__bubble
 | 
			
		||||
      = simple_format(h(@domain_block.private_comment))
 | 
			
		||||
    .speech-bubble__owner= t 'admin.instances.private_comment'
 | 
			
		||||
 | 
			
		||||
- if @domain_block.public_comment.present?
 | 
			
		||||
  .speech-bubble
 | 
			
		||||
    .speech-bubble__bubble
 | 
			
		||||
      = simple_format(h(@domain_block.public_comment))
 | 
			
		||||
    .speech-bubble__owner= t 'admin.instances.public_comment'
 | 
			
		||||
 | 
			
		||||
= simple_form_for @domain_block, url: admin_domain_block_path(@domain_block), method: :delete do |f|
 | 
			
		||||
 | 
			
		||||
  - unless (@domain_block.noop?)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,18 @@
 | 
			
		|||
          = fa_icon 'times'
 | 
			
		||||
      .dashboard__counters__label= t 'admin.instances.delivery_available'
 | 
			
		||||
 | 
			
		||||
- if @private_comment.present?
 | 
			
		||||
  .speech-bubble
 | 
			
		||||
    .speech-bubble__bubble
 | 
			
		||||
      = simple_format(h(@private_comment))
 | 
			
		||||
    .speech-bubble__owner= t 'admin.instances.private_comment'
 | 
			
		||||
 | 
			
		||||
- if @public_comment.present?
 | 
			
		||||
  .speech-bubble
 | 
			
		||||
    .speech-bubble__bubble
 | 
			
		||||
      = simple_format(h(@public_comment))
 | 
			
		||||
    .speech-bubble__owner= t 'admin.instances.public_comment'
 | 
			
		||||
 | 
			
		||||
%hr.spacer/
 | 
			
		||||
 | 
			
		||||
%div{ style: 'overflow: hidden' }
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +53,7 @@
 | 
			
		|||
    - if @domain_allow
 | 
			
		||||
      = link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
 | 
			
		||||
    - elsif @domain_block
 | 
			
		||||
      = link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@domain_block), class: 'button'
 | 
			
		||||
      = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@domain_block), class: 'button'
 | 
			
		||||
    - else
 | 
			
		||||
      = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,8 +3,8 @@
 | 
			
		|||
class DomainBlockWorker
 | 
			
		||||
  include Sidekiq::Worker
 | 
			
		||||
 | 
			
		||||
  def perform(domain_block_id)
 | 
			
		||||
    BlockDomainService.new.call(DomainBlock.find(domain_block_id))
 | 
			
		||||
  def perform(domain_block_id, update = false)
 | 
			
		||||
    BlockDomainService.new.call(DomainBlock.find(domain_block_id), update)
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    true
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -284,6 +284,7 @@ en:
 | 
			
		|||
      created_msg: Domain block is now being processed
 | 
			
		||||
      destroyed_msg: Domain block has been undone
 | 
			
		||||
      domain: Domain
 | 
			
		||||
      edit: Edit domain block
 | 
			
		||||
      existing_domain_block_html: You have already imposed stricter limits on %{name}, you need to <a href="%{unblock_url}">unblock it</a> first.
 | 
			
		||||
      new:
 | 
			
		||||
        create: Create block
 | 
			
		||||
| 
						 | 
				
			
			@ -294,6 +295,10 @@ en:
 | 
			
		|||
          silence: Silence
 | 
			
		||||
          suspend: Suspend
 | 
			
		||||
        title: New domain block
 | 
			
		||||
      private_comment: Private comment
 | 
			
		||||
      private_comment_hint: Comment about this domain limitation for internal use by the moderators.
 | 
			
		||||
      public_comment: Public comment
 | 
			
		||||
      public_comment_hint: Comment about this domain limitation for the general public, if advertising the list of domain limitations is enabled.
 | 
			
		||||
      reject_media: Reject media files
 | 
			
		||||
      reject_media_hint: Removes locally stored media files and refuses to download any in the future. Irrelevant for suspensions
 | 
			
		||||
      reject_reports: Reject reports
 | 
			
		||||
| 
						 | 
				
			
			@ -313,6 +318,7 @@ en:
 | 
			
		|||
        title: Undo domain block for %{domain}
 | 
			
		||||
        undo: Undo
 | 
			
		||||
      undo: Undo domain block
 | 
			
		||||
      view: View domain block
 | 
			
		||||
    email_domain_blocks:
 | 
			
		||||
      add_new: Add new
 | 
			
		||||
      created_msg: Successfully added e-mail domain to blacklist
 | 
			
		||||
| 
						 | 
				
			
			@ -336,6 +342,8 @@ en:
 | 
			
		|||
        all: All
 | 
			
		||||
        limited: Limited
 | 
			
		||||
        title: Moderation
 | 
			
		||||
      private_comment: Private comment
 | 
			
		||||
      public_comment: Public comment
 | 
			
		||||
      title: Federation
 | 
			
		||||
      total_blocked_by_us: Blocked by us
 | 
			
		||||
      total_followed_by_them: Followed by them
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -155,7 +155,11 @@ Rails.application.routes.draw do
 | 
			
		|||
    get '/dashboard', to: 'dashboard#index'
 | 
			
		||||
 | 
			
		||||
    resources :domain_allows, only: [:new, :create, :show, :destroy]
 | 
			
		||||
    resources :domain_blocks, only: [:new, :create, :show, :destroy]
 | 
			
		||||
    resources :domain_blocks, only: [:new, :create, :show, :destroy, :update] do
 | 
			
		||||
      member do
 | 
			
		||||
        get :edit
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    resources :email_domain_blocks, only: [:index, :new, :create, :destroy]
 | 
			
		||||
    resources :action_logs, only: [:index]
 | 
			
		||||
    resources :warning_presets, except: [:new]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
class AddCommentsToDomainBlocks < ActiveRecord::Migration[5.2]
 | 
			
		||||
  def change
 | 
			
		||||
    add_column :domain_blocks, :private_comment, :text
 | 
			
		||||
    add_column :domain_blocks, :public_comment, :text
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +10,7 @@
 | 
			
		|||
#
 | 
			
		||||
# It's strongly recommended that you check this file into your version control system.
 | 
			
		||||
 | 
			
		||||
ActiveRecord::Schema.define(version: 2019_08_05_123746) do
 | 
			
		||||
ActiveRecord::Schema.define(version: 2019_08_07_135426) do
 | 
			
		||||
 | 
			
		||||
  # These are extensions that must be enabled in order to support this database
 | 
			
		||||
  enable_extension "plpgsql"
 | 
			
		||||
| 
						 | 
				
			
			@ -259,6 +259,8 @@ ActiveRecord::Schema.define(version: 2019_08_05_123746) do
 | 
			
		|||
    t.integer "severity", default: 0
 | 
			
		||||
    t.boolean "reject_media", default: false, null: false
 | 
			
		||||
    t.boolean "reject_reports", default: false, null: false
 | 
			
		||||
    t.text "private_comment"
 | 
			
		||||
    t.text "public_comment"
 | 
			
		||||
    t.index ["domain"], name: "index_domain_blocks_on_domain", unique: true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ describe UnblockDomainService, type: :service do
 | 
			
		|||
      subject.call(@domain_block)
 | 
			
		||||
      expect_deleted_domain_block
 | 
			
		||||
      expect(@suspended.reload.suspended?).to be false
 | 
			
		||||
      expect(@silenced.reload.silenced?).to be true
 | 
			
		||||
      expect(@silenced.reload.silenced?).to be false
 | 
			
		||||
      expect(@independently_suspended.reload.suspended?).to be true
 | 
			
		||||
      expect(@independently_silenced.reload.silenced?).to be true
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ describe DomainBlockWorker do
 | 
			
		|||
      result = subject.perform(domain_block.id)
 | 
			
		||||
 | 
			
		||||
      expect(result).to be_nil
 | 
			
		||||
      expect(service).to have_received(:call).with(domain_block)
 | 
			
		||||
      expect(service).to have_received(:call).with(domain_block, false)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'calls domain block service for relevant domain block' do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue