From ddd30f331c7a2af38176d72d9ce2265068984bed Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 17 Oct 2018 17:13:04 +0200 Subject: Improve support for aspects/circles (#8950) * Add silent column to mentions * Save silent mentions in ActivityPub Create handler and optimize it Move networking calls out of the database transaction * Add "limited" visibility level masked as "private" in the API Unlike DMs, limited statuses are pushed into home feeds. The access control rules between direct and limited statuses is almost the same, except for counter and conversation logic * Ensure silent column is non-null, add spec * Ensure filters don't check silent mentions for blocks/mutes As those are "this person is also allowed to see" rather than "this person is involved", therefore does not warrant filtering * Clean up code * Use Status#active_mentions to limit returned mentions * Fix code style issues * Use Status#active_mentions in Notification And remove stream_entry eager-loading from Notification --- app/models/account_conversation.rb | 2 +- app/models/mention.rb | 8 ++++++++ app/models/notification.rb | 2 +- app/models/status.rb | 15 ++++++++++----- app/models/stream_entry.rb | 2 +- 5 files changed, 21 insertions(+), 8 deletions(-) (limited to 'app/models') diff --git a/app/models/account_conversation.rb b/app/models/account_conversation.rb index a7205ec1a..c12c8d233 100644 --- a/app/models/account_conversation.rb +++ b/app/models/account_conversation.rb @@ -85,7 +85,7 @@ class AccountConversation < ApplicationRecord private def participants_from_status(recipient, status) - ((status.mentions.pluck(:account_id) + [status.account_id]).uniq - [recipient.id]).sort + ((status.active_mentions.pluck(:account_id) + [status.account_id]).uniq - [recipient.id]).sort end end diff --git a/app/models/mention.rb b/app/models/mention.rb index 8ab886b18..d01a88e32 100644 --- a/app/models/mention.rb +++ b/app/models/mention.rb @@ -8,6 +8,7 @@ # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) +# silent :boolean default(FALSE), not null # class Mention < ApplicationRecord @@ -18,10 +19,17 @@ class Mention < ApplicationRecord validates :account, uniqueness: { scope: :status } + scope :active, -> { where(silent: false) } + scope :silent, -> { where(silent: true) } + delegate( :username, :acct, to: :account, prefix: true ) + + def active? + !silent? + end end diff --git a/app/models/notification.rb b/app/models/notification.rb index b9bec0808..78b180301 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -24,7 +24,7 @@ class Notification < ApplicationRecord favourite: 'Favourite', }.freeze - STATUS_INCLUDES = [:account, :application, :stream_entry, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, :application, :media_attachments, :tags, mentions: :account]].freeze + STATUS_INCLUDES = [:account, :application, :media_attachments, :tags, active_mentions: :account, reblog: [:account, :application, :media_attachments, :tags, active_mentions: :account]].freeze belongs_to :account, optional: true belongs_to :from_account, class_name: 'Account', optional: true diff --git a/app/models/status.rb b/app/models/status.rb index f61bd0fee..b18cb56b2 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -37,7 +37,7 @@ class Status < ApplicationRecord update_index('statuses#status', :proper) if Chewy.enabled? - enum visibility: [:public, :unlisted, :private, :direct], _suffix: :visibility + enum visibility: [:public, :unlisted, :private, :direct, :limited], _suffix: :visibility belongs_to :application, class_name: 'Doorkeeper::Application', optional: true @@ -51,7 +51,8 @@ class Status < ApplicationRecord has_many :favourites, inverse_of: :status, dependent: :destroy has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :thread - has_many :mentions, dependent: :destroy + has_many :mentions, dependent: :destroy, inverse_of: :status + has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status has_many :media_attachments, dependent: :nullify has_and_belongs_to_many :tags @@ -89,7 +90,7 @@ class Status < ApplicationRecord :status_stat, :tags, :stream_entry, - mentions: :account, + active_mentions: :account, reblog: [ :account, :application, @@ -98,7 +99,7 @@ class Status < ApplicationRecord :media_attachments, :conversation, :status_stat, - mentions: :account, + active_mentions: :account, ], thread: :account @@ -171,7 +172,11 @@ class Status < ApplicationRecord end def hidden? - private_visibility? || direct_visibility? + private_visibility? || direct_visibility? || limited_visibility? + end + + def distributable? + public_visibility? || unlisted_visibility? end def with_media? diff --git a/app/models/stream_entry.rb b/app/models/stream_entry.rb index a2f273281..1a9afc5c7 100644 --- a/app/models/stream_entry.rb +++ b/app/models/stream_entry.rb @@ -48,7 +48,7 @@ class StreamEntry < ApplicationRecord end def mentions - orphaned? ? [] : status.mentions.map(&:account) + orphaned? ? [] : status.active_mentions.map(&:account) end private -- cgit From 72d7d3003b1e21ef8a6f5112f1e8cb32c72e8992 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 17 Oct 2018 22:04:40 +0200 Subject: Do not show "limited" visibility in default visibility preference (#8999) * Do not show "limited" visibility in default visibility preference Fix regression from #8950 * Fix code style issue --- app/models/status.rb | 4 ++++ app/views/settings/preferences/show.html.haml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'app/models') diff --git a/app/models/status.rb b/app/models/status.rb index b18cb56b2..bcb7dd373 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -238,6 +238,10 @@ class Status < ApplicationRecord left_outer_joins(:status_stat).select('statuses.id, greatest(statuses.updated_at, status_stats.updated_at) AS updated_at') end + def selectable_visibilities + visibilities.keys - %w(direct limited) + end + def in_chosen_languages(account) where(language: nil).or where(language: account.chosen_languages) end diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index d889702fe..ecb789f93 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -22,7 +22,7 @@ %hr#settings_publishing/ .fields-group - = f.input :setting_default_privacy, collection: Status.visibilities.keys - ['direct'], wrapper: :with_floating_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' + = f.input :setting_default_privacy, collection: Status.selectable_visibilities, wrapper: :with_floating_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label -- cgit From a38a452481d0f5207bb27ba7a2707c0028d2ac18 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 19 Oct 2018 01:47:29 +0200 Subject: Add unread indicator to conversations (#9009) --- app/controllers/api/v1/conversations_controller.rb | 20 +++++++++++++++++-- app/controllers/api/v1/reports_controller.rb | 1 - app/javascript/mastodon/actions/conversations.js | 11 +++++++++++ .../direct_timeline/components/conversation.js | 14 ++++++++++--- .../containers/conversation_container.js | 8 +++++++- app/javascript/mastodon/reducers/conversations.js | 10 ++++++++++ app/javascript/styles/mastodon/components.scss | 5 +++++ app/models/account_conversation.rb | 2 ++ app/serializers/rest/conversation_serializer.rb | 3 ++- config/initializers/doorkeeper.rb | 2 +- config/routes.rb | 7 ++++++- ...18205649_add_unread_to_account_conversations.rb | 23 ++++++++++++++++++++++ db/schema.rb | 3 ++- 13 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20181018205649_add_unread_to_account_conversations.rb (limited to 'app/models') diff --git a/app/controllers/api/v1/conversations_controller.rb b/app/controllers/api/v1/conversations_controller.rb index 736cb21ca..b19f27ebf 100644 --- a/app/controllers/api/v1/conversations_controller.rb +++ b/app/controllers/api/v1/conversations_controller.rb @@ -3,9 +3,11 @@ class Api::V1::ConversationsController < Api::BaseController LIMIT = 20 - before_action -> { doorkeeper_authorize! :read, :'read:statuses' } + before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :index + before_action -> { doorkeeper_authorize! :write, :'write:conversations' }, except: :index before_action :require_user! - after_action :insert_pagination_headers + before_action :set_conversation, except: :index + after_action :insert_pagination_headers, only: :index respond_to :json @@ -14,8 +16,22 @@ class Api::V1::ConversationsController < Api::BaseController render json: @conversations, each_serializer: REST::ConversationSerializer end + def read + @conversation.update!(unread: false) + render json: @conversation, serializer: REST::ConversationSerializer + end + + def destroy + @conversation.destroy! + render_empty + end + private + def set_conversation + @conversation = AccountConversation.where(account: current_account).find(params[:id]) + end + def paginated_conversations AccountConversation.where(account: current_account) .paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb index 726817927..e182a9c6c 100644 --- a/app/controllers/api/v1/reports_controller.rb +++ b/app/controllers/api/v1/reports_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Api::V1::ReportsController < Api::BaseController - before_action -> { doorkeeper_authorize! :read, :'read:reports' }, except: [:create] before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create] before_action :require_user! diff --git a/app/javascript/mastodon/actions/conversations.js b/app/javascript/mastodon/actions/conversations.js index cab05c1ba..aefd2fef7 100644 --- a/app/javascript/mastodon/actions/conversations.js +++ b/app/javascript/mastodon/actions/conversations.js @@ -13,6 +13,8 @@ export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS'; export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL'; export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE'; +export const CONVERSATIONS_READ = 'CONVERSATIONS_READ'; + export const mountConversations = () => ({ type: CONVERSATIONS_MOUNT, }); @@ -21,6 +23,15 @@ export const unmountConversations = () => ({ type: CONVERSATIONS_UNMOUNT, }); +export const markConversationRead = conversationId => (dispatch, getState) => { + dispatch({ + type: CONVERSATIONS_READ, + id: conversationId, + }); + + api(getState).post(`/api/v1/conversations/${conversationId}/read`); +}; + export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => { dispatch(expandConversationsRequest()); diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversation.js b/app/javascript/mastodon/features/direct_timeline/components/conversation.js index f9a8d4f72..52e33c3c8 100644 --- a/app/javascript/mastodon/features/direct_timeline/components/conversation.js +++ b/app/javascript/mastodon/features/direct_timeline/components/conversation.js @@ -8,6 +8,7 @@ import DisplayName from '../../../components/display_name'; import Avatar from '../../../components/avatar'; import AttachmentList from '../../../components/attachment_list'; import { HotKeys } from 'react-hotkeys'; +import classNames from 'classnames'; export default class Conversation extends ImmutablePureComponent { @@ -19,8 +20,10 @@ export default class Conversation extends ImmutablePureComponent { conversationId: PropTypes.string.isRequired, accounts: ImmutablePropTypes.list.isRequired, lastStatus: ImmutablePropTypes.map.isRequired, + unread:PropTypes.bool.isRequired, onMoveUp: PropTypes.func, onMoveDown: PropTypes.func, + markRead: PropTypes.func.isRequired, }; handleClick = () => { @@ -28,7 +31,12 @@ export default class Conversation extends ImmutablePureComponent { return; } - const { lastStatus } = this.props; + const { lastStatus, unread, markRead } = this.props; + + if (unread) { + markRead(); + } + this.context.router.history.push(`/statuses/${lastStatus.get('id')}`); } @@ -41,7 +49,7 @@ export default class Conversation extends ImmutablePureComponent { } render () { - const { accounts, lastStatus, lastAccount } = this.props; + const { accounts, lastStatus, lastAccount, unread } = this.props; if (lastStatus === null) { return null; @@ -61,7 +69,7 @@ export default class Conversation extends ImmutablePureComponent { return ( -
+
{accounts.map(account => )}
diff --git a/app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js b/app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js index 4166ee2ac..e2e2e3afb 100644 --- a/app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js +++ b/app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import Conversation from '../components/conversation'; +import { markConversationRead } from '../../../actions/conversations'; const mapStateToProps = (state, { conversationId }) => { const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId); @@ -7,9 +8,14 @@ const mapStateToProps = (state, { conversationId }) => { return { accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)), + unread: conversation.get('unread'), lastStatus, lastAccount: lastStatus === null ? null : state.getIn(['accounts', lastStatus.get('account')], null), }; }; -export default connect(mapStateToProps)(Conversation); +const mapDispatchToProps = (dispatch, { conversationId }) => ({ + markRead: () => dispatch(markConversationRead(conversationId)), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Conversation); diff --git a/app/javascript/mastodon/reducers/conversations.js b/app/javascript/mastodon/reducers/conversations.js index 6b3f22d25..ea39fccee 100644 --- a/app/javascript/mastodon/reducers/conversations.js +++ b/app/javascript/mastodon/reducers/conversations.js @@ -6,6 +6,7 @@ import { CONVERSATIONS_FETCH_SUCCESS, CONVERSATIONS_FETCH_FAIL, CONVERSATIONS_UPDATE, + CONVERSATIONS_READ, } from '../actions/conversations'; import compareId from '../compare_id'; @@ -18,6 +19,7 @@ const initialState = ImmutableMap({ const conversationToMap = item => ImmutableMap({ id: item.id, + unread: item.unread, accounts: ImmutableList(item.accounts.map(a => a.id)), last_status: item.last_status.id, }); @@ -80,6 +82,14 @@ export default function conversations(state = initialState, action) { return state.update('mounted', count => count + 1); case CONVERSATIONS_UNMOUNT: return state.update('mounted', count => count - 1); + case CONVERSATIONS_READ: + return state.update('items', list => list.map(item => { + if (item.get('id') === action.id) { + return item.set('unread', false); + } + + return item; + })); default: return state; } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 129bde856..24b614a37 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -5503,6 +5503,11 @@ noscript { border-bottom: 1px solid lighten($ui-base-color, 8%); cursor: pointer; + &--unread { + background: lighten($ui-base-color, 8%); + border-bottom-color: lighten($ui-base-color, 12%); + } + &__header { display: flex; margin-bottom: 15px; diff --git a/app/models/account_conversation.rb b/app/models/account_conversation.rb index c12c8d233..b7447d805 100644 --- a/app/models/account_conversation.rb +++ b/app/models/account_conversation.rb @@ -10,6 +10,7 @@ # status_ids :bigint(8) default([]), not null, is an Array # last_status_id :bigint(8) # lock_version :integer default(0), not null +# unread :boolean default(FALSE), not null # class AccountConversation < ApplicationRecord @@ -58,6 +59,7 @@ class AccountConversation < ApplicationRecord def add_status(recipient, status) conversation = find_or_initialize_by(account: recipient, conversation_id: status.conversation_id, participant_account_ids: participants_from_status(recipient, status)) conversation.status_ids << status.id + conversation.unread = status.account_id != recipient.id conversation.save conversation rescue ActiveRecord::StaleObjectError diff --git a/app/serializers/rest/conversation_serializer.rb b/app/serializers/rest/conversation_serializer.rb index 884253f89..b09ca6341 100644 --- a/app/serializers/rest/conversation_serializer.rb +++ b/app/serializers/rest/conversation_serializer.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class REST::ConversationSerializer < ActiveModel::Serializer - attribute :id + attributes :id, :unread + has_many :participant_accounts, key: :accounts, serializer: REST::AccountSerializer has_one :last_status, serializer: REST::StatusSerializer diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index fe2490b32..367eead6a 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -58,6 +58,7 @@ Doorkeeper.configure do optional_scopes :write, :'write:accounts', :'write:blocks', + :'write:conversations', :'write:favourites', :'write:filters', :'write:follows', @@ -76,7 +77,6 @@ Doorkeeper.configure do :'read:lists', :'read:mutes', :'read:notifications', - :'read:reports', :'read:search', :'read:statuses', :follow, diff --git a/config/routes.rb b/config/routes.rb index a2468c9bd..b203e1329 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -261,7 +261,12 @@ Rails.application.routes.draw do resources :streaming, only: [:index] resources :custom_emojis, only: [:index] resources :suggestions, only: [:index, :destroy] - resources :conversations, only: [:index] + + resources :conversations, only: [:index, :destroy] do + member do + post :read + end + end get '/search', to: 'search#index', as: :search diff --git a/db/migrate/20181018205649_add_unread_to_account_conversations.rb b/db/migrate/20181018205649_add_unread_to_account_conversations.rb new file mode 100644 index 000000000..3c28b9a64 --- /dev/null +++ b/db/migrate/20181018205649_add_unread_to_account_conversations.rb @@ -0,0 +1,23 @@ +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddUnreadToAccountConversations < ActiveRecord::Migration[5.2] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def up + safety_assured do + add_column_with_default( + :account_conversations, + :unread, + :boolean, + allow_null: false, + default: false + ) + end + end + + def down + remove_column :account_conversations, :unread, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index f79f26f16..046975ac9 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: 2018_10_10_141500) do +ActiveRecord::Schema.define(version: 2018_10_18_205649) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -22,6 +22,7 @@ ActiveRecord::Schema.define(version: 2018_10_10_141500) do t.bigint "status_ids", default: [], null: false, array: true t.bigint "last_status_id" t.integer "lock_version", default: 0, null: false + t.boolean "unread", default: false, null: false t.index ["account_id", "conversation_id", "participant_account_ids"], name: "index_unique_conversations", unique: true t.index ["account_id"], name: "index_account_conversations_on_account_id" t.index ["conversation_id"], name: "index_account_conversations_on_conversation_id" -- cgit From fd5285658f477c5b6a9c7af0935b5c889729b2e0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 20 Oct 2018 08:02:44 +0200 Subject: Add option to block reports from domain (#8830) --- app/controllers/admin/domain_blocks_controller.rb | 2 +- app/javascript/packs/admin.js | 21 ++++++++------------- app/lib/activitypub/activity/flag.rb | 8 ++++++++ app/models/domain_block.rb | 13 +++++++------ .../admin/domain_blocks/_domain_block.html.haml | 5 ++++- app/views/admin/domain_blocks/index.html.haml | 1 + app/views/admin/domain_blocks/new.html.haml | 3 +++ .../_email_domain_block.html.haml | 2 +- app/views/admin/instances/_instance.html.haml | 2 +- config/locales/en.yml | 2 ++ ...017170937_add_reject_reports_to_domain_blocks.rb | 17 +++++++++++++++++ db/schema.rb | 1 + 12 files changed, 54 insertions(+), 23 deletions(-) create mode 100644 db/migrate/20181017170937_add_reject_reports_to_domain_blocks.rb (limited to 'app/models') diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index 64de2cbf0..90c70275a 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -46,7 +46,7 @@ module Admin end def resource_params - params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive) + params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :retroactive) end def retroactive_unblock? diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js index ce5f70ee7..f0c0ee0b7 100644 --- a/app/javascript/packs/admin.js +++ b/app/javascript/packs/admin.js @@ -1,17 +1,5 @@ import { delegate } from 'rails-ujs'; -function handleDeleteStatus(event) { - const [data] = event.detail; - const element = document.querySelector(`[data-id="${data.id}"]`); - if (element) { - element.parentNode.removeChild(element); - } -} - -[].forEach.call(document.querySelectorAll('.trash-button'), (content) => { - content.addEventListener('ajax:success', handleDeleteStatus); -}); - const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]'; delegate(document, '#batch_checkbox_all', 'change', ({ target }) => { @@ -22,6 +10,7 @@ delegate(document, '#batch_checkbox_all', 'change', ({ target }) => { delegate(document, batchCheckboxClassName, 'change', () => { const checkAllElement = document.querySelector('#batch_checkbox_all'); + if (checkAllElement) { checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); @@ -41,8 +30,14 @@ delegate(document, '.media-spoiler-hide-button', 'click', () => { }); delegate(document, '#domain_block_severity', 'change', ({ target }) => { - const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media'); + const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media'); + const rejectReportsDiv = document.querySelector('.input.with_label.domain_block_reject_reports'); + if (rejectMediaDiv) { rejectMediaDiv.style.display = (target.value === 'suspend') ? 'none' : 'block'; } + + if (rejectReportsDiv) { + rejectReportsDiv.style.display = (target.value === 'suspend') ? 'none' : 'block'; + } }); diff --git a/app/lib/activitypub/activity/flag.rb b/app/lib/activitypub/activity/flag.rb index 36d3c5730..92e59bb81 100644 --- a/app/lib/activitypub/activity/flag.rb +++ b/app/lib/activitypub/activity/flag.rb @@ -2,6 +2,8 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity def perform + return if skip_reports? + target_accounts = object_uris.map { |uri| account_from_uri(uri) }.compact.select(&:local?) target_statuses_by_account = object_uris.map { |uri| status_from_uri(uri) }.compact.select(&:local?).group_by(&:account_id) @@ -19,6 +21,12 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity end end + private + + def skip_reports? + DomainBlock.find_by(domain: @account.domain)&.reject_reports? + end + def object_uris @object_uris ||= Array(@object.is_a?(Array) ? @object.map { |item| value_or_id(item) } : value_or_id(@object)) end diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index 93658793b..b828a9d71 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -3,12 +3,13 @@ # # 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 +# 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 # class DomainBlock < ApplicationRecord diff --git a/app/views/admin/domain_blocks/_domain_block.html.haml b/app/views/admin/domain_blocks/_domain_block.html.haml index 17a3de81c..7bfea3574 100644 --- a/app/views/admin/domain_blocks/_domain_block.html.haml +++ b/app/views/admin/domain_blocks/_domain_block.html.haml @@ -1,10 +1,13 @@ %tr - %td.domain + %td %samp= domain_block.domain %td.severity = t("admin.domain_blocks.severities.#{domain_block.severity}") %td.reject_media - if domain_block.reject_media? || domain_block.suspend? %i.fa.fa-check + %td.reject_reports + - if domain_block.reject_reports? || domain_block.suspend? + %i.fa.fa-check %td = table_link_to 'undo', t('admin.domain_blocks.undo'), admin_domain_block_path(domain_block) diff --git a/app/views/admin/domain_blocks/index.html.haml b/app/views/admin/domain_blocks/index.html.haml index 42b8ca53f..4c5221c42 100644 --- a/app/views/admin/domain_blocks/index.html.haml +++ b/app/views/admin/domain_blocks/index.html.haml @@ -8,6 +8,7 @@ %th= t('admin.domain_blocks.domain') %th= t('admin.domain_blocks.severity') %th= t('admin.domain_blocks.reject_media') + %th= t('admin.domain_blocks.reject_reports') %th %tbody = render @domain_blocks diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index f0eb217eb..055d2fbd7 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -17,5 +17,8 @@ .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') + .actions = f.button :button, t('.create'), type: :submit diff --git a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml index 61cff9395..bf66c9001 100644 --- a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml +++ b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml @@ -1,5 +1,5 @@ %tr - %td.domain + %td %samp= email_domain_block.domain %td = table_link_to 'trash', t('admin.email_domain_blocks.delete'), admin_email_domain_block_path(email_domain_block), method: :delete diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml index 6efbbbe60..e36ebae47 100644 --- a/app/views/admin/instances/_instance.html.haml +++ b/app/views/admin/instances/_instance.html.haml @@ -1,5 +1,5 @@ %tr - %td.domain + %td = link_to instance.domain, admin_accounts_path(by_domain: instance.domain) %td.count = instance.accounts_count diff --git a/config/locales/en.yml b/config/locales/en.yml index 0360e719e..a2859aa5d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -263,6 +263,8 @@ en: title: New domain block 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 + reject_reports_hint: Ignore all reports coming from this domain. Irrelevant for suspensions severities: noop: None silence: Silence diff --git a/db/migrate/20181017170937_add_reject_reports_to_domain_blocks.rb b/db/migrate/20181017170937_add_reject_reports_to_domain_blocks.rb new file mode 100644 index 000000000..f05d50fcd --- /dev/null +++ b/db/migrate/20181017170937_add_reject_reports_to_domain_blocks.rb @@ -0,0 +1,17 @@ +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddRejectReportsToDomainBlocks < ActiveRecord::Migration[5.2] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def up + safety_assured do + add_column_with_default :domain_blocks, :reject_reports, :boolean, default: false, allow_null: false + end + end + + def down + remove_column :domain_blocks, :reject_reports + end +end diff --git a/db/schema.rb b/db/schema.rb index 046975ac9..8facfa259 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -186,6 +186,7 @@ ActiveRecord::Schema.define(version: 2018_10_18_205649) do t.datetime "updated_at", null: false t.integer "severity", default: 0 t.boolean "reject_media", default: false, null: false + t.boolean "reject_reports", default: false, null: false t.index ["domain"], name: "index_domain_blocks_on_domain", unique: true end -- cgit