Add public blocks to /about/blocks (#11298)
* Add automatic blocklist display in /about/blocks Inspired by https://github.com/Gargron/mastodon.social-misc * Add admin option to set who can see instance blocks * Normalize locales files * Rename “Sandbox” to “Silence” for consistency * Disable /about/blocks when in whitelist mode * Optionally display rationale for domain blocks * Only display domain blocks that have user-facing limitations, and order them * Redesign table of blocked domains to better handle long domain names and rationales * Change domain blocks ordering now that rationales aren't displayed right away * Only show explanation for block severities actually in use * Reword instance block explanations and add disclaimer for public fetch mode
This commit is contained in:
@ -3,10 +3,12 @@
|
||||
class AboutController < ApplicationController
|
||||
layout 'public'
|
||||
|
||||
before_action :require_open_federation!, only: [:show, :more]
|
||||
before_action :require_open_federation!, only: [:show, :more, :blocks]
|
||||
before_action :check_blocklist_enabled, only: [:blocks]
|
||||
before_action :authenticate_user!, only: [:blocks], if: :blocklist_account_required?
|
||||
before_action :set_body_classes, only: :show
|
||||
before_action :set_instance_presenter
|
||||
before_action :set_expires_in
|
||||
before_action :set_expires_in, only: [:show, :more, :terms]
|
||||
|
||||
skip_before_action :require_functional!, only: [:more, :terms]
|
||||
|
||||
@ -18,12 +20,40 @@ class AboutController < ApplicationController
|
||||
|
||||
def terms; end
|
||||
|
||||
def blocks
|
||||
@show_rationale = Setting.show_domain_blocks_rationale == 'all'
|
||||
@show_rationale |= Setting.show_domain_blocks_rationale == 'users' && !current_user.nil? && current_user.functional?
|
||||
@blocks = DomainBlock.with_user_facing_limitations.order('(CASE severity WHEN 0 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 0 END), reject_media, domain').to_a
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_open_federation!
|
||||
not_found if whitelist_mode?
|
||||
end
|
||||
|
||||
def check_blocklist_enabled
|
||||
not_found if Setting.show_domain_blocks == 'disabled'
|
||||
end
|
||||
|
||||
def blocklist_account_required?
|
||||
Setting.show_domain_blocks == 'users'
|
||||
end
|
||||
|
||||
def block_severity_text(block)
|
||||
if block.severity == 'suspend'
|
||||
I18n.t('domain_blocks.suspension')
|
||||
else
|
||||
limitations = []
|
||||
limitations << I18n.t('domain_blocks.media_block') if block.reject_media?
|
||||
limitations << I18n.t('domain_blocks.silence') if block.severity == 'silence'
|
||||
limitations.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
helper_method :block_severity_text
|
||||
helper_method :public_fetch_mode?
|
||||
|
||||
def new_user
|
||||
User.new.tap do |user|
|
||||
user.build_account
|
||||
|
@ -141,6 +141,15 @@ function main() {
|
||||
return false;
|
||||
});
|
||||
|
||||
delegate(document, '.blocks-table button.icon-button', 'click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const classList = this.firstElementChild.classList;
|
||||
classList.toggle('fa-chevron-down');
|
||||
classList.toggle('fa-chevron-up');
|
||||
this.parentElement.parentElement.nextElementSibling.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
delegate(document, '.modal-button', 'click', e => {
|
||||
e.preventDefault();
|
||||
|
||||
|
@ -241,3 +241,70 @@ a.table-action-link {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blocks-table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
border: 1px solid darken($ui-base-color, 8%);
|
||||
|
||||
thead {
|
||||
border: 1px solid darken($ui-base-color, 8%);
|
||||
background: darken($ui-base-color, 4%);
|
||||
font-weight: 500;
|
||||
|
||||
th.severity-column {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
th.button-column {
|
||||
width: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody > tr {
|
||||
border: 1px solid darken($ui-base-color, 8%);
|
||||
border-bottom: 0;
|
||||
background: darken($ui-base-color, 4%);
|
||||
|
||||
&:hover {
|
||||
background: darken($ui-base-color, 2%);
|
||||
}
|
||||
|
||||
&.even {
|
||||
background: $ui-base-color;
|
||||
|
||||
&:hover {
|
||||
background: lighten($ui-base-color, 2%);
|
||||
}
|
||||
}
|
||||
|
||||
&.rationale {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-top: 0;
|
||||
|
||||
&:hover {
|
||||
background: lighten($ui-base-color, 6%);
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 8px;
|
||||
line-height: 18px;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ class DomainBlock < ApplicationRecord
|
||||
delegate :count, to: :accounts, prefix: true
|
||||
|
||||
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
|
||||
scope :with_user_facing_limitations, -> { where(severity: [:silence, :suspend]).or(where(reject_media: true)) }
|
||||
|
||||
class << self
|
||||
def suspend?(domain)
|
||||
|
@ -30,6 +30,8 @@ class Form::AdminSettings
|
||||
mascot
|
||||
spam_check_enabled
|
||||
trends
|
||||
show_domain_blocks
|
||||
show_domain_blocks_rationale
|
||||
).freeze
|
||||
|
||||
BOOLEAN_KEYS = %i(
|
||||
@ -60,6 +62,8 @@ class Form::AdminSettings
|
||||
validates :site_contact_email, :site_contact_username, presence: true
|
||||
validates :site_contact_username, existing_username: true
|
||||
validates :bootstrap_timeline_accounts, existing_username: { multiple: true }
|
||||
validates :show_domain_blocks, inclusion: { in: %w(disabled users all) }
|
||||
validates :show_domain_blocks_rationale, inclusion: { in: %w(disabled users all) }
|
||||
|
||||
def initialize(_attributes = {})
|
||||
super
|
||||
|
48
app/views/about/blocks.html.haml
Normal file
48
app/views/about/blocks.html.haml
Normal file
@ -0,0 +1,48 @@
|
||||
- content_for :page_title do
|
||||
= t('domain_blocks.title', instance: site_hostname)
|
||||
|
||||
.grid
|
||||
.column-0
|
||||
.box-widget.rich-formatting
|
||||
%h2= t('domain_blocks.blocked_domains')
|
||||
%p= t('domain_blocks.description', instance: site_hostname)
|
||||
.table-wrapper
|
||||
%table.blocks-table
|
||||
%thead
|
||||
%tr
|
||||
%th= t('domain_blocks.domain')
|
||||
%th.severity-column= t('domain_blocks.severity')
|
||||
- if @show_rationale
|
||||
%th.button-column
|
||||
%tbody
|
||||
- if @blocks.empty?
|
||||
%tr
|
||||
%td{ colspan: @show_rationale ? 3 : 2 }= t('domain_blocks.no_domain_blocks')
|
||||
- else
|
||||
- @blocks.each_with_index do |block, i|
|
||||
%tr{ class: i % 2 == 0 ? 'even': nil }
|
||||
%td{ title: block.domain }= block.domain
|
||||
%td= block_severity_text(block)
|
||||
- if @show_rationale
|
||||
%td
|
||||
- if block.public_comment.present?
|
||||
%button.icon-button{ title: t('domain_blocks.show_rationale'), 'aria-label' => t('domain_blocks.show_rationale') }
|
||||
= fa_icon 'chevron-down fw', 'aria-hidden' => true
|
||||
- if @show_rationale
|
||||
- if block.public_comment.present?
|
||||
%tr.rationale.hidden
|
||||
%td{ colspan: 3 }= block.public_comment.presence
|
||||
%h2= t('domain_blocks.severity_legend.title')
|
||||
- if @blocks.any? { |block| block.reject_media? }
|
||||
%h3= t('domain_blocks.media_block')
|
||||
%p= t('domain_blocks.severity_legend.media_block')
|
||||
- if @blocks.any? { |block| block.severity == 'silence' }
|
||||
%h3= t('domain_blocks.silence')
|
||||
%p= t('domain_blocks.severity_legend.silence')
|
||||
- if @blocks.any? { |block| block.severity == 'suspend' }
|
||||
%h3= t('domain_blocks.suspension')
|
||||
%p= t('domain_blocks.severity_legend.suspension')
|
||||
- if public_fetch_mode?
|
||||
%p= t('domain_blocks.severity_legend.suspension_disclaimer')
|
||||
.column-1
|
||||
= render 'application/sidebar'
|
@ -79,6 +79,12 @@
|
||||
.fields-group
|
||||
= f.input :min_invite_role, wrapper: :with_label, collection: %i(disabled user moderator admin), label: t('admin.settings.registrations.min_invite_role.title'), label_method: lambda { |role| role == :disabled ? t('admin.settings.registrations.min_invite_role.disabled') : t("admin.accounts.roles.#{role}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
|
||||
|
||||
.fields-row
|
||||
.fields-row__column.fields-row__column-6.fields-group
|
||||
= f.input :show_domain_blocks, wrapper: :with_label, collection: %i(disabled users all), label: t('admin.settings.domain_blocks.title'), label_method: lambda { |value| t("admin.settings.domain_blocks.#{value}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
|
||||
.fields-row__column.fields-row__column-6.fields-group
|
||||
= f.input :show_domain_blocks_rationale, wrapper: :with_label, collection: %i(disabled users all), label: t('admin.settings.domain_blocks_rationale.title'), label_method: lambda { |value| t("admin.settings.domain_blocks.#{value}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
|
||||
|
||||
.fields-group
|
||||
= f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 }
|
||||
= f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } unless whitelist_mode?
|
||||
|
Reference in New Issue
Block a user