From 9b6a5ed109e1986149c1f15a41d4f442ae8ae39c Mon Sep 17 00:00:00 2001 From: ThibG Date: Mon, 19 Aug 2019 11:35:48 +0200 Subject: Add public blocks to /about/blocks (#11298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- app/controllers/about_controller.rb | 34 ++++++++++++++- app/javascript/packs/public.js | 9 ++++ app/javascript/styles/mastodon/tables.scss | 67 ++++++++++++++++++++++++++++++ app/models/domain_block.rb | 1 + app/models/form/admin_settings.rb | 4 ++ app/views/about/blocks.html.haml | 48 +++++++++++++++++++++ app/views/admin/settings/edit.html.haml | 6 +++ 7 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 app/views/about/blocks.html.haml (limited to 'app') diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index d276e8fe5..5e942e5c0 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -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 diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index b58622a8d..c5cd7129f 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -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(); diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 11ac6dfeb..fe6beba5d 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -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; + } +} diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index 37b8d98c6..4383cbd05 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -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) diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 051268375..6bc3ca9f5 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -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 diff --git a/app/views/about/blocks.html.haml b/app/views/about/blocks.html.haml new file mode 100644 index 000000000..a81a4d1eb --- /dev/null +++ b/app/views/about/blocks.html.haml @@ -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' diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index 28c0ece15..28880c087 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -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? -- cgit