From 648cdbc04a21580a89d337edb0f45308aff1b93f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 30 Jul 2019 13:10:40 +0200 Subject: Add hashtag score for better sorting of autosuggestions (#11427) * Add hashtag score for better sorting of autosuggestions * Do not use `~<~` operator with no text_pattern_ops index --- db/migrate/20190729185330_add_score_to_tags.rb | 5 +++++ db/schema.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20190729185330_add_score_to_tags.rb (limited to 'db') diff --git a/db/migrate/20190729185330_add_score_to_tags.rb b/db/migrate/20190729185330_add_score_to_tags.rb new file mode 100644 index 000000000..75fee4b57 --- /dev/null +++ b/db/migrate/20190729185330_add_score_to_tags.rb @@ -0,0 +1,5 @@ +class AddScoreToTags < ActiveRecord::Migration[5.2] + def change + add_column :tags, :score, :int + end +end diff --git a/db/schema.rb b/db/schema.rb index 2d83d8b76..e3af9c31a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_07_28_084117) do +ActiveRecord::Schema.define(version: 2019_07_29_185330) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -659,6 +659,7 @@ ActiveRecord::Schema.define(version: 2019_07_28_084117) do t.string "name", default: "", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "score" t.index "lower((name)::text)", name: "index_tags_on_name_lower", unique: true end -- cgit From 115dab78f1cc5357281dcb593f04ac8b2629cec6 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 5 Aug 2019 19:54:29 +0200 Subject: Change admin UI for hashtags and add back whitelisted trends (#11490) Fix #271 Add back the `GET /api/v1/trends` API with the caveat that it does not return tags that have not been allowed to trend by the staff. When a hashtag begins to trend (internally) and that hashtag has not been previously reviewed by the staff, the staff is notified. The new admin UI for hashtags allows filtering hashtags by where they are used (e.g. in the profile directory), whether they have been reviewed or are pending reviewal, they show by how many people the hashtag is used in the directory, how many people used it today, how many statuses with it have been created today, and it allows fixing the name of the hashtag to make it more readable. The disallowed hashtags feature has been reworked. It is now controlled from the admin UI for hashtags instead of from the file `config/settings.yml` --- app/controllers/admin/dashboard_controller.rb | 2 +- app/controllers/admin/tags_controller.rb | 36 ++++++++----- app/controllers/api/v1/trends_controller.rb | 17 ++++++ app/controllers/settings/preferences_controller.rb | 2 +- app/helpers/admin/filter_helper.rb | 5 +- app/mailers/admin_mailer.rb | 10 ++++ app/models/application_record.rb | 11 ++++ app/models/tag.rb | 60 +++++++++++++++++++--- app/models/trending_tags.rb | 48 ++++++++--------- app/models/user.rb | 4 ++ app/policies/tag_policy.rb | 4 +- app/validators/disallowed_hashtags_validator.rb | 21 +------- app/views/admin/dashboard/index.html.haml | 2 +- app/views/admin/tags/_tag.html.haml | 24 +++++---- app/views/admin/tags/index.html.haml | 26 +++++----- app/views/admin/tags/show.html.haml | 16 ++++++ app/views/admin_mailer/new_trending_tag.text.erb | 5 ++ .../preferences/notifications/show.html.haml | 1 + config/locales/en.yml | 18 ++++--- config/locales/simple_form.en.yml | 7 +++ config/navigation.rb | 2 +- config/routes.rb | 9 +--- config/settings.yml | 1 + .../20190805123746_add_capabilities_to_tags.rb | 9 ++++ db/schema.rb | 7 ++- spec/controllers/admin/tags_controller_spec.rb | 56 ++------------------ spec/policies/tag_policy_spec.rb | 2 +- .../disallowed_hashtags_validator_spec.rb | 26 +++++----- 28 files changed, 258 insertions(+), 173 deletions(-) create mode 100644 app/controllers/api/v1/trends_controller.rb create mode 100644 app/views/admin/tags/show.html.haml create mode 100644 app/views/admin_mailer/new_trending_tag.text.erb create mode 100644 db/migrate/20190805123746_add_capabilities_to_tags.rb (limited to 'db') diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index e74e4755f..70afdedd7 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -27,7 +27,7 @@ module Admin @saml_enabled = ENV['SAML_ENABLED'] == 'true' @pam_enabled = ENV['PAM_ENABLED'] == 'true' @hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true' - @trending_hashtags = TrendingTags.get(7) + @trending_hashtags = TrendingTags.get(10, filtered: false) @profile_directory = Setting.profile_directory @timeline_preview = Setting.timeline_preview @spam_check_enabled = Setting.spam_check_enabled diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb index e9f4f2cfa..0e9dda302 100644 --- a/app/controllers/admin/tags_controller.rb +++ b/app/controllers/admin/tags_controller.rb @@ -4,41 +4,49 @@ module Admin class TagsController < BaseController before_action :set_tags, only: :index before_action :set_tag, except: :index - before_action :set_filter_params def index authorize :tag, :index? end - def hide - authorize @tag, :hide? - @tag.account_tag_stat.update!(hidden: true) - redirect_to admin_tags_path(@filter_params) + def show + authorize @tag, :show? end - def unhide - authorize @tag, :unhide? - @tag.account_tag_stat.update!(hidden: false) - redirect_to admin_tags_path(@filter_params) + def update + authorize @tag, :update? + + if @tag.update(tag_params.merge(reviewed_at: Time.now.utc)) + redirect_to admin_tag_path(@tag.id) + else + render :show + end end private def set_tags - @tags = Tag.discoverable - @tags.merge!(Tag.hidden) if filter_params[:hidden] + @tags = filtered_tags.page(params[:page]) end def set_tag @tag = Tag.find(params[:id]) end - def set_filter_params - @filter_params = filter_params.to_hash.symbolize_keys + def filtered_tags + scope = Tag + scope = scope.discoverable if filter_params[:context] == 'directory' + scope = scope.reviewed if filter_params[:review] == 'reviewed' + scope = scope.pending_review if filter_params[:review] == 'pending_review' + scope.reorder(score: :desc) end def filter_params - params.permit(:hidden) + params.slice(:context, :review).permit(:context, :review) + end + + def tag_params + params.require(:tag).permit(:name, :trendable, :usable, :listable) end end end diff --git a/app/controllers/api/v1/trends_controller.rb b/app/controllers/api/v1/trends_controller.rb new file mode 100644 index 000000000..bcea9857e --- /dev/null +++ b/app/controllers/api/v1/trends_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class Api::V1::TrendsController < Api::BaseController + before_action :set_tags + + respond_to :json + + def index + render json: @tags, each_serializer: REST::TagSerializer + end + + private + + def set_tags + @tags = TrendingTags.get(limit_param(10)) + end +end diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 742c97cdb..d548072a8 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -56,7 +56,7 @@ class Settings::PreferencesController < Settings::BaseController :setting_advanced_layout, :setting_use_blurhash, :setting_use_pending_items, - notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account), + notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag), interactions: %i(must_be_follower must_be_following must_be_following_dm) ) end diff --git a/app/helpers/admin/filter_helper.rb b/app/helpers/admin/filter_helper.rb index 0bda25974..506429e10 100644 --- a/app/helpers/admin/filter_helper.rb +++ b/app/helpers/admin/filter_helper.rb @@ -5,15 +5,16 @@ module Admin::FilterHelper REPORT_FILTERS = %i(resolved account_id target_account_id).freeze INVITE_FILTER = %i(available expired).freeze CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze - TAGS_FILTERS = %i(hidden).freeze + TAGS_FILTERS = %i(context review).freeze INSTANCES_FILTERS = %i(limited by_domain).freeze FOLLOWERS_FILTERS = %i(relationship status by_domain activity order).freeze FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS + TAGS_FILTERS + INSTANCES_FILTERS + FOLLOWERS_FILTERS def filter_link_to(text, link_to_params, link_class_params = link_to_params) - new_url = filtered_url_for(link_to_params) + new_url = filtered_url_for(link_to_params) new_class = filtered_url_for(link_class_params) + link_to text, new_url, class: filter_link_class(new_class) end diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index 9ab3e2bbd..8abce5f05 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -24,4 +24,14 @@ class AdminMailer < ApplicationMailer mail to: @me.user_email, subject: I18n.t('admin_mailer.new_pending_account.subject', instance: @instance, username: @account.username) end end + + def new_trending_tag(recipient, tag) + @tag = tag + @me = recipient + @instance = Rails.configuration.x.local_domain + + locale_for_account(@me) do + mail to: @me.user_email, subject: I18n.t('admin_mailer.new_trending_tag.subject', instance: @instance, name: @tag.name) + end + end end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 83134d41a..c1b873da6 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -2,5 +2,16 @@ class ApplicationRecord < ActiveRecord::Base self.abstract_class = true + include Remotable + + def boolean_with_default(key, default_value) + value = attributes[key] + + if value.nil? + default_value + else + value + end + end end diff --git a/app/models/tag.rb b/app/models/tag.rb index c7f0af86d..6a02581fa 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -3,11 +3,16 @@ # # Table name: tags # -# id :bigint(8) not null, primary key -# name :string default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# score :integer +# id :bigint(8) not null, primary key +# name :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# score :integer +# usable :boolean +# trendable :boolean +# listable :boolean +# reviewed_at :datetime +# requested_review_at :datetime # class Tag < ApplicationRecord @@ -22,16 +27,17 @@ class Tag < ApplicationRecord HASHTAG_RE = /(?:^|[^\/\)\w])#(#{HASHTAG_NAME_RE})/i validates :name, presence: true, format: { with: /\A(#{HASHTAG_NAME_RE})\z/i } + validate :validate_name_change, if: -> { !new_record? && name_changed? } - scope :discoverable, -> { joins(:account_tag_stat).where(AccountTagStat.arel_table[:accounts_count].gt(0)).where(account_tag_stats: { hidden: false }).order(Arel.sql('account_tag_stats.accounts_count desc')) } - scope :hidden, -> { where(account_tag_stats: { hidden: true }) } + scope :reviewed, -> { where.not(reviewed_at: nil) } + scope :pending_review, -> { where(reviewed_at: nil).where.not(requested_review_at: nil) } + scope :discoverable, -> { where.not(listable: false).joins(:account_tag_stat).where(AccountTagStat.arel_table[:accounts_count].gt(0)).order(Arel.sql('account_tag_stats.accounts_count desc')) } scope :most_used, ->(account) { joins(:statuses).where(statuses: { account: account }).group(:id).order(Arel.sql('count(*) desc')) } delegate :accounts_count, :accounts_count=, :increment_count!, :decrement_count!, - :hidden?, to: :account_tag_stat after_save :save_account_tag_stat @@ -48,6 +54,40 @@ class Tag < ApplicationRecord name end + def usable + boolean_with_default('usable', true) + end + + alias usable? usable + + def listable + boolean_with_default('listable', true) + end + + alias listable? listable + + def trendable + boolean_with_default('trendable', false) + end + + alias trendable? trendable + + def requires_review? + reviewed_at.nil? + end + + def reviewed? + reviewed_at.present? + end + + def requested_review? + requested_review_at.present? + end + + def trending? + TrendingTags.trending?(self) + end + def history days = [] @@ -117,4 +157,8 @@ class Tag < ApplicationRecord return unless account_tag_stat&.changed? account_tag_stat.save end + + def validate_name_change + errors.add(:name, I18n.t('tags.does_not_match_previous_name')) unless name_was.mb_chars.casecmp(name.mb_chars).zero? + end end diff --git a/app/models/trending_tags.rb b/app/models/trending_tags.rb index 211c8f1dc..e9b9b25e3 100644 --- a/app/models/trending_tags.rb +++ b/app/models/trending_tags.rb @@ -10,20 +10,28 @@ class TrendingTags include Redisable def record_use!(tag, account, at_time = Time.now.utc) - return if disallowed_hashtags.include?(tag.name) || account.silenced? || account.bot? + return if account.silenced? || account.bot? || !tag.usable? || !(tag.trendable? || tag.requires_review?) increment_historical_use!(tag.id, at_time) increment_unique_use!(tag.id, account.id, at_time) - increment_vote!(tag.id, at_time) + increment_vote!(tag, at_time) end - def get(limit) - key = "#{KEY}:#{Time.now.utc.beginning_of_day.to_i}" - tag_ids = redis.zrevrange(key, 0, limit - 1).map(&:to_i) - tags = Tag.where(id: tag_ids).to_a.each_with_object({}) { |tag, h| h[tag.id] = tag } + def get(limit, filtered: true) + tag_ids = redis.zrevrange("#{KEY}:#{Time.now.utc.beginning_of_day.to_i}", 0, limit - 1).map(&:to_i) + + tags = Tag.where(id: tag_ids) + tags = tags.where(trendable: true) if filtered + tags = tags.each_with_object({}) { |tag, h| h[tag.id] = tag } + tag_ids.map { |tag_id| tags[tag_id] }.compact end + def trending?(tag) + rank = redis.zrevrank("#{KEY}:#{Time.now.utc.beginning_of_day.to_i}", tag.id) + rank.present? && rank <= 10 + end + private def increment_historical_use!(tag_id, at_time) @@ -38,33 +46,27 @@ class TrendingTags redis.expire(key, EXPIRE_HISTORY_AFTER) end - def increment_vote!(tag_id, at_time) + def increment_vote!(tag, at_time) key = "#{KEY}:#{at_time.beginning_of_day.to_i}" - expected = redis.pfcount("activity:tags:#{tag_id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts").to_f + expected = redis.pfcount("activity:tags:#{tag.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts").to_f expected = 1.0 if expected.zero? - observed = redis.pfcount("activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}:accounts").to_f + observed = redis.pfcount("activity:tags:#{tag.id}:#{at_time.beginning_of_day.to_i}:accounts").to_f if expected > observed || observed < THRESHOLD - redis.zrem(key, tag_id.to_s) + redis.zrem(key, tag.id) else - score = ((observed - expected)**2) / expected - added = redis.zadd(key, score, tag_id.to_s) - bump_tag_score!(tag_id) if added + score = ((observed - expected)**2) / expected + old_rank = redis.zrevrank(key, tag.id) + + redis.zadd(key, score, tag.id) + request_review!(tag) if (old_rank.nil? || old_rank > 10) && redis.zrevrank(key, tag.id) <= 10 && !tag.trendable? && tag.requires_review? && !tag.requested_review? end redis.expire(key, EXPIRE_TRENDS_AFTER) end - def bump_tag_score!(tag_id) - Tag.where(id: tag_id).update_all('score = COALESCE(score, 0) + 1') - end - - def disallowed_hashtags - return @disallowed_hashtags if defined?(@disallowed_hashtags) - - @disallowed_hashtags = Setting.disallowed_hashtags.nil? ? [] : Setting.disallowed_hashtags - @disallowed_hashtags = @disallowed_hashtags.split(' ') if @disallowed_hashtags.is_a? String - @disallowed_hashtags = @disallowed_hashtags.map(&:downcase) + def request_review!(tag) + User.staff.includes(:account).find_each { |u| AdminMailer.new_trending_tag(u.account, tag).deliver_later! if u.allows_trending_tag_emails? } end end end diff --git a/app/models/user.rb b/app/models/user.rb index 6806c0362..b83e26af3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -207,6 +207,10 @@ class User < ApplicationRecord settings.notification_emails['pending_account'] end + def allows_trending_tag_emails? + settings.notification_emails['trending_tag'] + end + def hides_network? @hides_network ||= settings.hide_network end diff --git a/app/policies/tag_policy.rb b/app/policies/tag_policy.rb index c63de01db..aaf70fcab 100644 --- a/app/policies/tag_policy.rb +++ b/app/policies/tag_policy.rb @@ -5,11 +5,11 @@ class TagPolicy < ApplicationPolicy staff? end - def hide? + def show? staff? end - def unhide? + def update? staff? end end diff --git a/app/validators/disallowed_hashtags_validator.rb b/app/validators/disallowed_hashtags_validator.rb index ee06b20f6..d745b767f 100644 --- a/app/validators/disallowed_hashtags_validator.rb +++ b/app/validators/disallowed_hashtags_validator.rb @@ -4,24 +4,7 @@ class DisallowedHashtagsValidator < ActiveModel::Validator def validate(status) return unless status.local? && !status.reblog? - @status = status - tags = select_tags - - status.errors.add(:text, I18n.t('statuses.disallowed_hashtags', tags: tags.join(', '), count: tags.size)) unless tags.empty? - end - - private - - def select_tags - tags = Extractor.extract_hashtags(@status.text) - tags.keep_if { |tag| disallowed_hashtags.include? tag.downcase } - end - - def disallowed_hashtags - return @disallowed_hashtags if @disallowed_hashtags - - @disallowed_hashtags = Setting.disallowed_hashtags.nil? ? [] : Setting.disallowed_hashtags - @disallowed_hashtags = @disallowed_hashtags.split(' ') if @disallowed_hashtags.is_a? String - @disallowed_hashtags = @disallowed_hashtags.map(&:downcase) + disallowed_hashtags = Tag.matching_name(Extractor.extract_hashtags(status.text)).reject(&:usable?) + status.errors.add(:text, I18n.t('statuses.disallowed_hashtags', tags: disallowed_hashtags.map(&:name).join(', '), count: disallowed_hashtags.size)) unless disallowed_hashtags.empty? end end diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 77cc1a2a0..910896075 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -107,5 +107,5 @@ %ul - @trending_hashtags.each do |tag| %li - = link_to "##{tag.name}", web_url("timelines/tag/#{tag.name}") + = link_to content_tag(:span, "##{tag.name}", class: !tag.trendable? && !tag.reviewed? ? 'warning-hint' : (!tag.trendable? ? 'negative-hint' : nil)), admin_tag_path(tag.id) %span.pull-right= number_with_delimiter(tag.history[0][:accounts].to_i) diff --git a/app/views/admin/tags/_tag.html.haml b/app/views/admin/tags/_tag.html.haml index 961b83f93..91af8e492 100644 --- a/app/views/admin/tags/_tag.html.haml +++ b/app/views/admin/tags/_tag.html.haml @@ -1,12 +1,16 @@ -%tr - %td - = link_to explore_hashtag_path(tag) do +.directory__tag + = link_to admin_tag_path(tag.id) do + %h4 = fa_icon 'hashtag' = tag.name - %td - = t('directories.people', count: tag.accounts_count) - %td - - if tag.hidden? - = table_link_to 'eye', t('admin.tags.unhide'), unhide_admin_tag_path(tag.id, **@filter_params), method: :post - - else - = table_link_to 'eye-slash', t('admin.tags.hide'), hide_admin_tag_path(tag.id, **@filter_params), method: :post + + %small + = t('admin.tags.in_directory', count: tag.accounts_count) + • + = t('admin.tags.unique_uses_today', count: tag.history.first[:accounts]) + + - if tag.trending? + = fa_icon 'fire fw' + = t('admin.tags.trending_right_now') + + .trends__item__current= number_to_human tag.history.first[:uses], strip_insignificant_zeros: true diff --git a/app/views/admin/tags/index.html.haml b/app/views/admin/tags/index.html.haml index 4ba395860..5e4ee21f5 100644 --- a/app/views/admin/tags/index.html.haml +++ b/app/views/admin/tags/index.html.haml @@ -3,17 +3,19 @@ .filters .filter-subset - %strong= t('admin.reports.status') + %strong= t('admin.tags.context') %ul - %li= filter_link_to t('admin.tags.visible'), hidden: nil - %li= filter_link_to t('admin.tags.hidden'), hidden: '1' + %li= filter_link_to t('generic.all'), context: nil + %li= filter_link_to t('admin.tags.directory'), context: 'directory' -.table-wrapper - %table.table - %thead - %tr - %th= t('admin.tags.name') - %th= t('admin.tags.accounts') - %th - %tbody - = render @tags + .filter-subset + %strong= t('admin.tags.review') + %ul + %li= filter_link_to t('generic.all'), review: nil + %li= filter_link_to t('admin.tags.reviewed'), review: 'reviewed' + %li= filter_link_to safe_join([t('admin.accounts.moderation.pending'), "(#{Tag.pending_review.count})"], ' '), review: 'pending_review' + +%hr.spacer/ + += render @tags += paginate @tags diff --git a/app/views/admin/tags/show.html.haml b/app/views/admin/tags/show.html.haml new file mode 100644 index 000000000..27c8dc92b --- /dev/null +++ b/app/views/admin/tags/show.html.haml @@ -0,0 +1,16 @@ +- content_for :page_title do + = "##{@tag.name}" + += simple_form_for @tag, url: admin_tag_path(@tag.id) do |f| + = render 'shared/error_messages', object: @tag + + .fields-group + = f.input :name, wrapper: :with_block_label + + .fields-group + = f.input :usable, as: :boolean, wrapper: :with_label + = f.input :trendable, as: :boolean, wrapper: :with_label + = f.input :listable, as: :boolean, wrapper: :with_label + + .actions + = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/admin_mailer/new_trending_tag.text.erb b/app/views/admin_mailer/new_trending_tag.text.erb new file mode 100644 index 000000000..f3087df37 --- /dev/null +++ b/app/views/admin_mailer/new_trending_tag.text.erb @@ -0,0 +1,5 @@ +<%= raw t('application_mailer.salutation', name: display_name(@me)) %> + +<%= raw t('admin_mailer.new_trending_tag.body', name: @tag.name) %> + +<%= raw t('application_mailer.view')%> <%= admin_tags_url(review: 'pending_review') %> diff --git a/app/views/settings/preferences/notifications/show.html.haml b/app/views/settings/preferences/notifications/show.html.haml index acc646fc3..f666ae4ff 100644 --- a/app/views/settings/preferences/notifications/show.html.haml +++ b/app/views/settings/preferences/notifications/show.html.haml @@ -15,6 +15,7 @@ - if current_user.staff? = ff.input :report, as: :boolean, wrapper: :with_label = ff.input :pending_account, as: :boolean, wrapper: :with_label + = ff.input :trending_tag, as: :boolean, wrapper: :with_label .fields-group = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| diff --git a/config/locales/en.yml b/config/locales/en.yml index 6c1a34300..9b62aac3a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -483,13 +483,14 @@ en: title: Account statuses with_media: With media tags: - accounts: Accounts - hidden: Hidden - hide: Hide from directory - name: Hashtag + context: Context + directory: In directory + in_directory: "%{count} in directory" + review: Review status + reviewed: Reviewed title: Hashtags - unhide: Show in directory - visible: Visible + trending_right_now: Trending right now + unique_uses_today: "%{count} posting today" title: Administration warning_presets: add_new: Add new @@ -505,6 +506,9 @@ en: body: "%{reporter} has reported %{target}" body_remote: Someone from %{domain} has reported %{target} subject: New report for %{instance} (#%{id}) + new_trending_tag: + body: 'The hashtag #%{name} is trending today, but has not been previously reviewed. It will not be displayed publicly unless you allow it to, or just save the form as it is to never hear about it again.' + subject: New hashtag up for review on %{instance} (#%{name}) appearance: advanced_web_interface: Advanced web interface advanced_web_interface_hint: 'If you want to make use of your entire screen width, the advanced web interface allows you to configure many different columns to see as much information at the same time as you want: Home, notifications, federated timeline, any number of lists and hashtags.' @@ -939,6 +943,8 @@ en: pinned: Pinned toot reblogged: boosted sensitive_content: Sensitive content + tags: + does_not_match_previous_name: does not match the previous name terms: body_html: |

Privacy Policy

diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 10b30e627..6fdfc9d7b 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -48,6 +48,8 @@ en: text: This will help us review your application sessions: otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:' + tag: + name: You can only change the casing of the letters, for example, to make it more readable user: chosen_languages: When checked, only toots in selected languages will be displayed in public timelines labels: @@ -137,6 +139,11 @@ en: pending_account: Send e-mail when a new account needs review reblog: Send e-mail when someone boosts your status report: Send e-mail when a new report is submitted + trending_tag: Send e-mail when an unreviewed hashtag is trending + tag: + listable: Allow this hashtag to appear on the profile directory + trendable: Allow this hashtag to appear under trends + usable: Allow toots to use this hashtag 'no': 'No' recommended: Recommended required: diff --git a/config/navigation.rb b/config/navigation.rb index 9b46da603..38668bbf7 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -38,7 +38,7 @@ SimpleNavigation::Configuration.run do |navigation| s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports} s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts} s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path - s.item :tags, safe_join([fa_icon('tag fw'), t('admin.tags.title')]), admin_tags_path + s.item :tags, safe_join([fa_icon('hashtag fw'), t('admin.tags.title')]), admin_tags_path, highlights_on: %r{/admin/tags} s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url(limited: whitelist_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.admin? } s.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? } end diff --git a/config/routes.rb b/config/routes.rb index 04424bbbd..60f7d2e05 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -243,13 +243,7 @@ Rails.application.routes.draw do end resources :account_moderation_notes, only: [:create, :destroy] - - resources :tags, only: [:index] do - member do - post :hide - post :unhide - end - end + resources :tags, only: [:index, :show, :update] end get '/admin', to: redirect('/admin/dashboard', status: 302) @@ -311,6 +305,7 @@ Rails.application.routes.draw do resources :mutes, only: [:index] resources :favourites, only: [:index] resources :reports, only: [:create] + resources :trends, only: [:index] resources :filters, only: [:index, :create, :show, :update, :destroy] resources :endorsements, only: [:index] diff --git a/config/settings.yml b/config/settings.yml index ad2970bb7..10180201f 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -43,6 +43,7 @@ defaults: &defaults digest: true report: true pending_account: true + trending_tag: true interactions: must_be_follower: false must_be_following: false diff --git a/db/migrate/20190805123746_add_capabilities_to_tags.rb b/db/migrate/20190805123746_add_capabilities_to_tags.rb new file mode 100644 index 000000000..43c7763b1 --- /dev/null +++ b/db/migrate/20190805123746_add_capabilities_to_tags.rb @@ -0,0 +1,9 @@ +class AddCapabilitiesToTags < ActiveRecord::Migration[5.2] + def change + add_column :tags, :usable, :boolean + add_column :tags, :trendable, :boolean + add_column :tags, :listable, :boolean + add_column :tags, :reviewed_at, :datetime + add_column :tags, :requested_review_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index e3af9c31a..d1b6825b4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_07_29_185330) do +ActiveRecord::Schema.define(version: 2019_08_05_123746) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -660,6 +660,11 @@ ActiveRecord::Schema.define(version: 2019_07_29_185330) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "score" + t.boolean "usable" + t.boolean "trendable" + t.boolean "listable" + t.datetime "reviewed_at" + t.datetime "requested_review_at" t.index "lower((name)::text)", name: "index_tags_on_name_lower", unique: true end diff --git a/spec/controllers/admin/tags_controller_spec.rb b/spec/controllers/admin/tags_controller_spec.rb index 3af994071..5c1944fc7 100644 --- a/spec/controllers/admin/tags_controller_spec.rb +++ b/spec/controllers/admin/tags_controller_spec.rb @@ -10,62 +10,14 @@ RSpec.describe Admin::TagsController, type: :controller do end describe 'GET #index' do - before do - account_tag_stat = Fabricate(:tag).account_tag_stat - account_tag_stat.update(hidden: hidden, accounts_count: 1) - get :index, params: { hidden: hidden } - end - - context 'with hidden tags' do - let(:hidden) { true } - - it 'returns status 200' do - expect(response).to have_http_status(200) - end - end - - context 'without hidden tags' do - let(:hidden) { false } - - it 'returns status 200' do - expect(response).to have_http_status(200) - end - end - end - - describe 'POST #hide' do - let(:tag) { Fabricate(:tag) } + let!(:tag) { Fabricate(:tag) } before do - tag.account_tag_stat.update(hidden: false) - post :hide, params: { id: tag.id } - end - - it 'hides tag' do - tag.reload - expect(tag).to be_hidden - end - - it 'redirects to admin_tags_path' do - expect(response).to redirect_to(admin_tags_path(controller.instance_variable_get(:@filter_params))) - end - end - - describe 'POST #unhide' do - let(:tag) { Fabricate(:tag) } - - before do - tag.account_tag_stat.update(hidden: true) - post :unhide, params: { id: tag.id } - end - - it 'unhides tag' do - tag.reload - expect(tag).not_to be_hidden + get :index end - it 'redirects to admin_tags_path' do - expect(response).to redirect_to(admin_tags_path(controller.instance_variable_get(:@filter_params))) + it 'returns status 200' do + expect(response).to have_http_status(200) end end end diff --git a/spec/policies/tag_policy_spec.rb b/spec/policies/tag_policy_spec.rb index c7afaa7c9..c63875dc0 100644 --- a/spec/policies/tag_policy_spec.rb +++ b/spec/policies/tag_policy_spec.rb @@ -8,7 +8,7 @@ RSpec.describe TagPolicy do let(:admin) { Fabricate(:user, admin: true).account } let(:john) { Fabricate(:user).account } - permissions :index?, :hide?, :unhide? do + permissions :index?, :show?, :update? do context 'staff?' do it 'permits' do expect(subject).to permit(admin, Tag) diff --git a/spec/validators/disallowed_hashtags_validator_spec.rb b/spec/validators/disallowed_hashtags_validator_spec.rb index 8ec1302ab..9deec0bb9 100644 --- a/spec/validators/disallowed_hashtags_validator_spec.rb +++ b/spec/validators/disallowed_hashtags_validator_spec.rb @@ -3,42 +3,44 @@ require 'rails_helper' RSpec.describe DisallowedHashtagsValidator, type: :validator do + let(:disallowed_tags) { [] } + describe '#validate' do before do - allow_any_instance_of(described_class).to receive(:select_tags) { tags } + disallowed_tags.each { |name| Fabricate(:tag, name: name, usable: false) } described_class.new.validate(status) end - let(:status) { double(errors: errors, local?: local, reblog?: reblog, text: '') } + let(:status) { double(errors: errors, local?: local, reblog?: reblog, text: disallowed_tags.map { |x| '#' + x }.join(' ')) } let(:errors) { double(add: nil) } - context 'unless status.local? && !status.reblog?' do + context 'for a remote reblog' do let(:local) { false } let(:reblog) { true } - it 'not calls errors.add' do + it 'does not add errors' do expect(errors).not_to have_received(:add).with(:text, any_args) end end - context 'status.local? && !status.reblog?' do + context 'for a local original status' do let(:local) { true } let(:reblog) { false } - context 'tags.empty?' do - let(:tags) { [] } + context 'when does not contain any disallowed hashtags' do + let(:disallowed_tags) { [] } - it 'not calls errors.add' do + it 'does not add errors' do expect(errors).not_to have_received(:add).with(:text, any_args) end end - context '!tags.empty?' do - let(:tags) { %w(a b c) } + context 'when contains disallowed hashtags' do + let(:disallowed_tags) { %w(a b c) } - it 'calls errors.add' do + it 'adds an error' do expect(errors).to have_received(:add) - .with(:text, I18n.t('statuses.disallowed_hashtags', tags: tags.join(', '), count: tags.size)) + .with(:text, I18n.t('statuses.disallowed_hashtags', tags: disallowed_tags.join(', '), count: disallowed_tags.size)) end end end -- cgit From bced70469a6c4aecdb3c71055f329a0f579eb14c Mon Sep 17 00:00:00 2001 From: ThibG Date: Wed, 7 Aug 2019 20:20:23 +0200 Subject: Add domain block notes (#11515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- app/controllers/admin/domain_blocks_controller.rb | 28 ++++++++++++++++++-- app/controllers/admin/instances_controller.rb | 2 ++ app/models/domain_block.rb | 16 +++++++----- app/services/block_domain_service.rb | 11 +++++++- app/services/unblock_domain_service.rb | 19 ++------------ app/views/admin/accounts/show.html.haml | 2 +- app/views/admin/domain_blocks/edit.html.haml | 30 ++++++++++++++++++++++ app/views/admin/domain_blocks/new.html.haml | 6 +++++ app/views/admin/domain_blocks/show.html.haml | 12 +++++++++ app/views/admin/instances/show.html.haml | 13 ++++++++++ app/workers/domain_block_worker.rb | 4 +-- config/locales/en.yml | 8 ++++++ config/routes.rb | 6 ++++- ...20190807135426_add_comments_to_domain_blocks.rb | 7 +++++ db/schema.rb | 4 ++- spec/services/unblock_domain_service_spec.rb | 2 +- spec/workers/domain_block_worker_spec.rb | 2 +- 17 files changed, 138 insertions(+), 34 deletions(-) create mode 100644 app/views/admin/domain_blocks/edit.html.haml create mode 100644 db/migrate/20190807135426_add_comments_to_domain_blocks.rb (limited to 'db') diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index 7129656da..74a36b79c 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -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 diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb index d4f201807..b47b18f8e 100644 --- a/app/controllers/admin/instances_controller.rb +++ b/app/controllers/admin/instances_controller.rb @@ -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 diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index 25d3b87ef..3f5b9f23e 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -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 diff --git a/app/services/block_domain_service.rb b/app/services/block_domain_service.rb index c5e5e5761..0ec6be503 100644 --- a/app/services/block_domain_service.rb +++ b/app/services/block_domain_service.rb @@ -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? diff --git a/app/services/unblock_domain_service.rb b/app/services/unblock_domain_service.rb index fc262a50a..d502d9e49 100644 --- a/app/services/unblock_domain_service.rb +++ b/app/services/unblock_domain_service.rb @@ -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 diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index 7494c9fa2..59babd3b0 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -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' diff --git a/app/views/admin/domain_blocks/edit.html.haml b/app/views/admin/domain_blocks/edit.html.haml new file mode 100644 index 000000000..29e47ef3b --- /dev/null +++ b/app/views/admin/domain_blocks/edit.html.haml @@ -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 diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index 055d2fbd7..ed1581936 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -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 diff --git a/app/views/admin/domain_blocks/show.html.haml b/app/views/admin/domain_blocks/show.html.haml index dca4dbac7..e64aaa629 100644 --- a/app/views/admin/domain_blocks/show.html.haml +++ b/app/views/admin/domain_blocks/show.html.haml @@ -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?) diff --git a/app/views/admin/instances/show.html.haml b/app/views/admin/instances/show.html.haml index fbb49ba02..294c9495d 100644 --- a/app/views/admin/instances/show.html.haml +++ b/app/views/admin/instances/show.html.haml @@ -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' diff --git a/app/workers/domain_block_worker.rb b/app/workers/domain_block_worker.rb index 884477829..35518d6b5 100644 --- a/app/workers/domain_block_worker.rb +++ b/app/workers/domain_block_worker.rb @@ -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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 17ff24726..b677a6651 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 unblock it 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 diff --git a/config/routes.rb b/config/routes.rb index 60f7d2e05..9c33b8190 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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] diff --git a/db/migrate/20190807135426_add_comments_to_domain_blocks.rb b/db/migrate/20190807135426_add_comments_to_domain_blocks.rb new file mode 100644 index 000000000..b660a71ad --- /dev/null +++ b/db/migrate/20190807135426_add_comments_to_domain_blocks.rb @@ -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 + diff --git a/db/schema.rb b/db/schema.rb index d1b6825b4..f8fc6a821 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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 diff --git a/spec/services/unblock_domain_service_spec.rb b/spec/services/unblock_domain_service_spec.rb index 619aefb5c..27dbc92ad 100644 --- a/spec/services/unblock_domain_service_spec.rb +++ b/spec/services/unblock_domain_service_spec.rb @@ -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 diff --git a/spec/workers/domain_block_worker_spec.rb b/spec/workers/domain_block_worker_spec.rb index c4138501f..48b3e38c4 100644 --- a/spec/workers/domain_block_worker_spec.rb +++ b/spec/workers/domain_block_worker_spec.rb @@ -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 -- cgit