Merge commit '7d9b209fe84b00eff348ea9d54905cbfffa79788' into glitch-soc/merge-upstream

Conflicts:
- `app/models/form/admin_settings.rb`:
  Upstream changed code style change, including on a line modified by glitch-soc.
  Kept glitch-soc's line but with the code style change applied.
This commit is contained in:
Claire 2023-12-21 19:29:59 +01:00
commit 30ee7339d3
33 changed files with 591 additions and 485 deletions

View file

@ -99,6 +99,16 @@
matchUpdateTypes: ['patch', 'minor'], matchUpdateTypes: ['patch', 'minor'],
groupName: 'eslint (non-major)', groupName: 'eslint (non-major)',
}, },
{
// Group actions/*-artifact in the same PR
matchManagers: ['github-actions'],
matchPackageNames: [
'actions/download-artifact',
'actions/upload-artifact',
],
matchUpdateTypes: ['major'],
groupName: 'artifact actions (major)',
},
{ {
// Update @types/* packages every week, with one grouped PR // Update @types/* packages every week, with one grouped PR
matchPackagePrefixes: '@types/', matchPackagePrefixes: '@types/',

View file

@ -1,33 +1,21 @@
# This configuration was generated by # This configuration was generated by
# `haml-lint --auto-gen-config` # `haml-lint --auto-gen-config`
# on 2023-10-26 09:32:34 -0400 using Haml-Lint version 0.51.0. # on 2023-12-15 11:02:19 -0500 using Haml-Lint version 0.52.0.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the lints are removed from the code base. # one by one as the lints are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
# versions of Haml-Lint, may require this file to be generated again. # versions of Haml-Lint, may require this file to be generated again.
linters: linters:
# Offense count: 16 # Offense count: 11
LineLength: LineLength:
exclude: exclude:
- 'app/views/admin/account_actions/new.html.haml'
- 'app/views/admin/accounts/index.html.haml'
- 'app/views/admin/ip_blocks/new.html.haml'
- 'app/views/admin/roles/_form.html.haml' - 'app/views/admin/roles/_form.html.haml'
- 'app/views/admin/settings/discovery/show.html.haml'
- 'app/views/auth/registrations/edit.html.haml' - 'app/views/auth/registrations/edit.html.haml'
- 'app/views/auth/registrations/new.html.haml' - 'app/views/auth/registrations/new.html.haml'
- 'app/views/filters/_filter_fields.html.haml'
- 'app/views/media/player.html.haml' - 'app/views/media/player.html.haml'
- 'app/views/settings/applications/_fields.html.haml' - 'app/views/settings/applications/_fields.html.haml'
- 'app/views/settings/imports/index.html.haml' - 'app/views/settings/imports/index.html.haml'
- 'app/views/settings/preferences/appearance/show.html.haml' - 'app/views/settings/preferences/appearance/show.html.haml'
- 'app/views/settings/preferences/notifications/show.html.haml' - 'app/views/settings/preferences/notifications/show.html.haml'
- 'app/views/settings/preferences/other/show.html.haml' - 'app/views/settings/preferences/other/show.html.haml'
# Offense count: 9
RuboCop:
exclude:
- 'app/views/admin/accounts/_buttons.html.haml'
- 'app/views/admin/accounts/_local_account.html.haml'
- 'app/views/admin/roles/_form.html.haml'

View file

@ -109,6 +109,17 @@ Rails/SkipsModelValidations:
Exclude: Exclude:
- 'db/*migrate/**/*' - 'db/*migrate/**/*'
# Reason: We want to preserve the ability to migrate from arbitrary old versions,
# and cannot guarantee that every installation has run every migration as they upgrade.
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsunusedignoredcolumns
Rails/UnusedIgnoredColumns:
Enabled: false
# Reason: Prevailing style choice
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsnegateinclude
Rails/NegateInclude:
Enabled: false
# Reason: Some single letter camel case files shouldn't be split # Reason: Some single letter camel case files shouldn't be split
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath # https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath
RSpec/FilePath: RSpec/FilePath:

View file

@ -119,23 +119,6 @@ Rails/LexicallyScopedActionFilter:
- 'app/controllers/auth/passwords_controller.rb' - 'app/controllers/auth/passwords_controller.rb'
- 'app/controllers/auth/registrations_controller.rb' - 'app/controllers/auth/registrations_controller.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/NegateInclude:
Exclude:
- 'app/controllers/concerns/signature_verification.rb'
- 'app/helpers/jsonld_helper.rb'
- 'app/lib/activitypub/activity/create.rb'
- 'app/lib/activitypub/activity/move.rb'
- 'app/lib/feed_manager.rb'
- 'app/lib/link_details_extractor.rb'
- 'app/models/concerns/attachmentable.rb'
- 'app/models/concerns/remotable.rb'
- 'app/models/custom_filter.rb'
- 'app/services/activitypub/process_status_update_service.rb'
- 'app/services/fetch_link_card_service.rb'
- 'app/workers/web/push_notification_worker.rb'
- 'lib/paperclip/color_extractor.rb'
Rails/OutputSafety: Rails/OutputSafety:
Exclude: Exclude:
- 'config/initializers/simple_form.rb' - 'config/initializers/simple_form.rb'
@ -196,19 +179,6 @@ Rails/UniqueValidationWithoutIndex:
- 'app/models/identity.rb' - 'app/models/identity.rb'
- 'app/models/webauthn_credential.rb' - 'app/models/webauthn_credential.rb'
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/UnusedIgnoredColumns:
Exclude:
- 'app/models/account.rb'
- 'app/models/account_stat.rb'
- 'app/models/admin/action_log.rb'
- 'app/models/custom_filter.rb'
- 'app/models/email_domain_block.rb'
- 'app/models/report.rb'
- 'app/models/status_edit.rb'
- 'app/models/user.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle. # Configuration parameters: EnforcedStyle.
# SupportedStyles: exists, where # SupportedStyles: exists, where
@ -426,15 +396,6 @@ Style/RedundantFetchBlock:
- 'config/initializers/paperclip.rb' - 'config/initializers/paperclip.rb'
- 'config/puma.rb' - 'config/puma.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowMultipleReturnValues.
Style/RedundantReturn:
Exclude:
- 'app/controllers/api/v1/directories_controller.rb'
- 'app/controllers/auth/confirmations_controller.rb'
- 'app/lib/ostatus/tag_manager.rb'
- 'app/models/form/import.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
# AllowedMethods: present?, blank?, presence, try, try! # AllowedMethods: present?, blank?, presence, try, try!
@ -455,11 +416,6 @@ Style/SingleArgumentDig:
Exclude: Exclude:
- 'lib/webpacker/manifest_extensions.rb' - 'lib/webpacker/manifest_extensions.rb'
# This cop supports safe autocorrection (--autocorrect).
Style/StderrPuts:
Exclude:
- 'config/boot.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Mode. # Configuration parameters: Mode.
Style/StringConcatenation: Style/StringConcatenation:
@ -478,13 +434,6 @@ Style/StringLiterals:
- 'config/initializers/webauthn.rb' - 'config/initializers/webauthn.rb'
- 'config/routes.rb' - 'config/routes.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
# AllowedMethods: define_method, mail, respond_to
Style/SymbolProc:
Exclude:
- 'config/initializers/3_omniauth.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, AllowSafeAssignment. # Configuration parameters: EnforcedStyle, AllowSafeAssignment.
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex # SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex

View file

@ -515,7 +515,7 @@ GEM
openssl (> 2.0) openssl (> 2.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
ox (2.14.17) ox (2.14.17)
parallel (1.23.0) parallel (1.24.0)
parser (3.2.2.4) parser (3.2.2.4)
ast (~> 2.4.1) ast (~> 2.4.1)
racc racc
@ -679,10 +679,10 @@ GEM
rubocop (~> 1.41) rubocop (~> 1.41)
rubocop-factory_bot (2.24.0) rubocop-factory_bot (2.24.0)
rubocop (~> 1.33) rubocop (~> 1.33)
rubocop-performance (1.19.1) rubocop-performance (1.20.0)
rubocop (>= 1.7.0, < 2.0) rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 0.4.0) rubocop-ast (>= 1.30.0, < 2.0)
rubocop-rails (2.22.2) rubocop-rails (2.23.0)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0) rubocop (>= 1.33.0, < 2.0)

View file

@ -25,6 +25,6 @@ class Api::V1::Accounts::NotesController < Api::BaseController
end end
def relationships_presenter def relationships_presenter
AccountRelationshipsPresenter.new([@account.id], current_user.account_id) AccountRelationshipsPresenter.new([@account], current_user.account_id)
end end
end end

View file

@ -25,6 +25,6 @@ class Api::V1::Accounts::PinsController < Api::BaseController
end end
def relationships_presenter def relationships_presenter
AccountRelationshipsPresenter.new([@account.id], current_user.account_id) AccountRelationshipsPresenter.new([@account], current_user.account_id)
end end
end end

View file

@ -5,7 +5,7 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
before_action :require_user! before_action :require_user!
def index def index
@accounts = Account.where(id: account_ids).select('id') @accounts = Account.where(id: account_ids).select(:id, :domain)
@accounts.merge!(Account.without_suspended) unless truthy_param?(:with_suspended) @accounts.merge!(Account.without_suspended) unless truthy_param?(:with_suspended)
render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships
end end

View file

@ -88,7 +88,7 @@ class Api::V1::AccountsController < Api::BaseController
end end
def relationships(**options) def relationships(**options)
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, **options) AccountRelationshipsPresenter.new([@account], current_user.account_id, **options)
end end
def account_params def account_params

View file

@ -12,7 +12,7 @@ class Api::V1::DirectoriesController < Api::BaseController
private private
def require_enabled! def require_enabled!
return not_found unless Setting.profile_directory not_found unless Setting.profile_directory
end end
def set_accounts def set_accounts

View file

@ -25,11 +25,11 @@ class Api::V1::FollowRequestsController < Api::BaseController
private private
def account def account
Account.find(params[:id]) @account ||= Account.find(params[:id])
end end
def relationships(**options) def relationships(**options)
AccountRelationshipsPresenter.new([params[:id]], current_user.account_id, **options) AccountRelationshipsPresenter.new([account], current_user.account_id, **options)
end end
def load_accounts def load_accounts

View file

@ -63,7 +63,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
end end
def captcha_user_bypass? def captcha_user_bypass?
return true if @confirmation_user.nil? || @confirmation_user.confirmed? @confirmation_user.nil? || @confirmation_user.confirmed?
end end
def set_pack def set_pack

View file

@ -34,7 +34,7 @@ class RelationshipsController < ApplicationController
end end
def set_relationships def set_relationships
@relationships = AccountRelationshipsPresenter.new(@accounts.pluck(:id), current_user.account_id) @relationships = AccountRelationshipsPresenter.new(@accounts, current_user.account_id)
end end
def form_account_batch_params def form_account_batch_params

View file

@ -110,7 +110,7 @@ module ApplicationHelper
def can?(action, record) def can?(action, record)
return false if record.nil? return false if record.nil?
policy(record).public_send("#{action}?") policy(record).public_send(:"#{action}?")
end end
def fa_icon(icon, attributes = {}) def fa_icon(icon, attributes = {})

View file

@ -52,7 +52,7 @@ class OStatus::TagManager
ActivityPub::TagManager.instance.uri_to_local_id(tag) ActivityPub::TagManager.instance.uri_to_local_id(tag)
else else
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag)
return matches[1] unless matches.nil? matches[1] unless matches.nil?
end end
end end

View file

@ -455,8 +455,8 @@ class Account < ApplicationRecord
end end
def inverse_alias(key, original_key) def inverse_alias(key, original_key)
define_method("#{key}=") do |value| define_method(:"#{key}=") do |value|
public_send("#{original_key}=", !ActiveModel::Type::Boolean.new.cast(value)) public_send(:"#{original_key}=", !ActiveModel::Type::Boolean.new.cast(value))
end end
define_method(key) do define_method(key) do

View file

@ -18,16 +18,12 @@ class AccountDomainBlock < ApplicationRecord
belongs_to :account belongs_to :account
validates :domain, presence: true, uniqueness: { scope: :account_id }, domain: true validates :domain, presence: true, uniqueness: { scope: :account_id }, domain: true
after_commit :remove_blocking_cache after_commit :invalidate_domain_blocking_cache
after_commit :remove_relationship_cache
private private
def remove_blocking_cache def invalidate_domain_blocking_cache
Rails.cache.delete("exclude_domains_for:#{account_id}") Rails.cache.delete("exclude_domains_for:#{account_id}")
end Rails.cache.delete(['exclude_domains', account_id, domain])
def remove_relationship_cache
Rails.cache.delete_matched("relationship:#{account_id}:*")
end end
end end

View file

@ -60,12 +60,6 @@ module Account::Interactions
end end
end end
def domain_blocking_map(target_account_ids, account_id)
accounts_map = Account.where(id: target_account_ids).select('id, domain').each_with_object({}) { |a, h| h[a.id] = a.domain }
blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id)
accounts_map.reduce({}) { |h, (id, domain)| h.merge(id => blocked_domains[domain]) }
end
def domain_blocking_map_by_domain(target_domains, account_id) def domain_blocking_map_by_domain(target_domains, account_id)
follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain) follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain)
end end

View file

@ -10,7 +10,7 @@ module RelationshipCacheable
private private
def remove_relationship_cache def remove_relationship_cache
Rails.cache.delete("relationship:#{account_id}:#{target_account_id}") Rails.cache.delete(['relationship', account_id, target_account_id])
Rails.cache.delete("relationship:#{target_account_id}:#{account_id}") Rails.cache.delete(['relationship', target_account_id, account_id])
end end
end end

View file

@ -7,7 +7,7 @@ module Remotable
def remotable_attachment(attachment_name, limit, suppress_errors: true, download_on_assign: true, attribute_name: nil) def remotable_attachment(attachment_name, limit, suppress_errors: true, download_on_assign: true, attribute_name: nil)
attribute_name ||= :"#{attachment_name}_remote_url" attribute_name ||= :"#{attachment_name}_remote_url"
define_method("download_#{attachment_name}!") do |url = nil| define_method(:"download_#{attachment_name}!") do |url = nil|
url ||= self[attribute_name] url ||= self[attribute_name]
return if url.blank? return if url.blank?
@ -24,29 +24,29 @@ module Remotable
Request.new(:get, url).perform do |response| Request.new(:get, url).perform do |response|
raise Mastodon::UnexpectedResponseError, response unless (200...300).cover?(response.code) raise Mastodon::UnexpectedResponseError, response unless (200...300).cover?(response.code)
public_send("#{attachment_name}=", ResponseWithLimit.new(response, limit)) public_send(:"#{attachment_name}=", ResponseWithLimit.new(response, limit))
end end
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e
Rails.logger.debug { "Error fetching remote #{attachment_name}: #{e}" } Rails.logger.debug { "Error fetching remote #{attachment_name}: #{e}" }
public_send("#{attachment_name}=", nil) if public_send("#{attachment_name}_file_name").present? public_send(:"#{attachment_name}=", nil) if public_send(:"#{attachment_name}_file_name").present?
raise e unless suppress_errors raise e unless suppress_errors
rescue Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Paperclip::Error, Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e rescue Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Paperclip::Error, Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e
Rails.logger.debug { "Error fetching remote #{attachment_name}: #{e}" } Rails.logger.debug { "Error fetching remote #{attachment_name}: #{e}" }
public_send("#{attachment_name}=", nil) if public_send("#{attachment_name}_file_name").present? public_send(:"#{attachment_name}=", nil) if public_send(:"#{attachment_name}_file_name").present?
end end
nil nil
end end
define_method("#{attribute_name}=") do |url| define_method(:"#{attribute_name}=") do |url|
return if self[attribute_name] == url && public_send("#{attachment_name}_file_name").present? return if self[attribute_name] == url && public_send(:"#{attachment_name}_file_name").present?
self[attribute_name] = url if has_attribute?(attribute_name) self[attribute_name] = url if has_attribute?(attribute_name)
public_send("download_#{attachment_name}!", url) if download_on_assign public_send(:"download_#{attachment_name}!", url) if download_on_assign
end end
alias_method("reset_#{attachment_name}!", "download_#{attachment_name}!") alias_method(:"reset_#{attachment_name}!", "download_#{attachment_name}!")
end end
end end
end end

View file

@ -100,7 +100,7 @@ class Form::AdminSettings
KEYS.each do |key| KEYS.each do |key|
define_method(key) do define_method(key) do
return instance_variable_get("@#{key}") if instance_variable_defined?("@#{key}") return instance_variable_get(:"@#{key}") if instance_variable_defined?(:"@#{key}")
stored_value = if UPLOAD_KEYS.include?(key) stored_value = if UPLOAD_KEYS.include?(key)
SiteUpload.where(var: key).first_or_initialize(var: key) SiteUpload.where(var: key).first_or_initialize(var: key)
@ -110,12 +110,12 @@ class Form::AdminSettings
Setting.public_send(key) Setting.public_send(key)
end end
instance_variable_set("@#{key}", stored_value) instance_variable_set(:"@#{key}", stored_value)
end end
end end
UPLOAD_KEYS.each do |key| UPLOAD_KEYS.each do |key|
define_method("#{key}=") do |file| define_method(:"#{key}=") do |file|
value = public_send(key) value = public_send(key)
value.file = file value.file = file
rescue Mastodon::DimensionsValidationError => e rescue Mastodon::DimensionsValidationError => e
@ -130,13 +130,13 @@ class Form::AdminSettings
return false unless errors.empty? && valid? return false unless errors.empty? && valid?
KEYS.each do |key| KEYS.each do |key|
next if PSEUDO_KEYS.include?(key) || !instance_variable_defined?("@#{key}") next if PSEUDO_KEYS.include?(key) || !instance_variable_defined?(:"@#{key}")
if UPLOAD_KEYS.include?(key) if UPLOAD_KEYS.include?(key)
public_send(key).save public_send(key).save
else else
setting = Setting.where(var: key).first_or_initialize(var: key) setting = Setting.where(var: key).first_or_initialize(var: key)
setting.update(value: typecast_value(key, instance_variable_get("@#{key}"))) setting.update(value: typecast_value(key, instance_variable_get(:"@#{key}")))
end end
end end
end end
@ -163,9 +163,9 @@ class Form::AdminSettings
def validate_site_uploads def validate_site_uploads
UPLOAD_KEYS.each do |key| UPLOAD_KEYS.each do |key|
next unless instance_variable_defined?("@#{key}") next unless instance_variable_defined?(:"@#{key}")
upload = instance_variable_get("@#{key}") upload = instance_variable_get(:"@#{key}")
next if upload.valid? next if upload.valid?
upload.errors.each do |error| upload.errors.each do |error|

View file

@ -43,14 +43,19 @@ class Form::Import
validate :validate_data validate :validate_data
def guessed_type def guessed_type
return :muting if csv_headers_match?('Hide notifications') if csv_headers_match?('Hide notifications') || file_name_matches?('mutes') || file_name_matches?('muted_accounts')
return :following if csv_headers_match?('Show boosts') || csv_headers_match?('Notify on new posts') || csv_headers_match?('Languages') :muting
return :following if file_name_matches?('follows') || file_name_matches?('following_accounts') elsif csv_headers_match?('Show boosts') || csv_headers_match?('Notify on new posts') || csv_headers_match?('Languages') || file_name_matches?('follows') || file_name_matches?('following_accounts')
return :blocking if file_name_matches?('blocks') || file_name_matches?('blocked_accounts') :following
return :muting if file_name_matches?('mutes') || file_name_matches?('muted_accounts') elsif file_name_matches?('blocks') || file_name_matches?('blocked_accounts')
return :domain_blocking if file_name_matches?('domain_blocks') || file_name_matches?('blocked_domains') :blocking
return :bookmarks if file_name_matches?('bookmarks') elsif file_name_matches?('domain_blocks') || file_name_matches?('blocked_domains')
return :lists if file_name_matches?('lists') :domain_blocking
elsif file_name_matches?('bookmarks')
:bookmarks
elsif file_name_matches?('lists')
:lists
end
end end
# Whether the uploaded CSV file seems to correspond to a different import type than the one selected # Whether the uploaded CSV file seems to correspond to a different import type than the one selected

View file

@ -5,8 +5,9 @@ class AccountRelationshipsPresenter
:muting, :requested, :requested_by, :domain_blocking, :muting, :requested, :requested_by, :domain_blocking,
:endorsed, :account_note :endorsed, :account_note
def initialize(account_ids, current_account_id, **options) def initialize(accounts, current_account_id, **options)
@account_ids = account_ids.map { |a| a.is_a?(Account) ? a.id : a.to_i } @accounts = accounts.to_a
@account_ids = @accounts.pluck(:id)
@current_account_id = current_account_id @current_account_id = current_account_id
@following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id)) @following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
@ -16,10 +17,11 @@ class AccountRelationshipsPresenter
@muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id)) @muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id))
@requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id)) @requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id))
@requested_by = cached[:requested_by].merge(Account.requested_by_map(@uncached_account_ids, @current_account_id)) @requested_by = cached[:requested_by].merge(Account.requested_by_map(@uncached_account_ids, @current_account_id))
@domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id))
@endorsed = cached[:endorsed].merge(Account.endorsed_map(@uncached_account_ids, @current_account_id)) @endorsed = cached[:endorsed].merge(Account.endorsed_map(@uncached_account_ids, @current_account_id))
@account_note = cached[:account_note].merge(Account.account_note_map(@uncached_account_ids, @current_account_id)) @account_note = cached[:account_note].merge(Account.account_note_map(@uncached_account_ids, @current_account_id))
@domain_blocking = domain_blocking_map
cache_uncached! cache_uncached!
@following.merge!(options[:following_map] || {}) @following.merge!(options[:following_map] || {})
@ -36,6 +38,31 @@ class AccountRelationshipsPresenter
private private
def domain_blocking_map
target_domains = @accounts.pluck(:domain).compact.uniq
blocks_by_domain = {}
# Fetch from cache
cache_keys = target_domains.map { |domain| domain_cache_key(domain) }
Rails.cache.read_multi(*cache_keys).each do |key, blocking|
blocks_by_domain[key.last] = blocking
end
uncached_domains = target_domains - blocks_by_domain.keys
# Read uncached values from database
AccountDomainBlock.where(account_id: @current_account_id, domain: uncached_domains).pluck(:domain).each do |domain|
blocks_by_domain[domain] = true
end
# Write database reads to cache
to_cache = uncached_domains.to_h { |domain| [domain_cache_key(domain), blocks_by_domain[domain]] }
Rails.cache.write_multi(to_cache, expires_in: 1.day)
# Return formatted value
@accounts.each_with_object({}) { |account, h| h[account.id] = blocks_by_domain[account.domain] }
end
def cached def cached
return @cached if defined?(@cached) return @cached if defined?(@cached)
@ -47,28 +74,23 @@ class AccountRelationshipsPresenter
muting: {}, muting: {},
requested: {}, requested: {},
requested_by: {}, requested_by: {},
domain_blocking: {},
endorsed: {}, endorsed: {},
account_note: {}, account_note: {},
} }
@uncached_account_ids = [] @uncached_account_ids = @account_ids.uniq
@account_ids.each do |account_id| cache_ids = @account_ids.map { |account_id| relationship_cache_key(account_id) }
maps_for_account = Rails.cache.read("relationship:#{@current_account_id}:#{account_id}") Rails.cache.read_multi(*cache_ids).each do |key, maps_for_account|
if maps_for_account.is_a?(Hash)
@cached.deep_merge!(maps_for_account) @cached.deep_merge!(maps_for_account)
else @uncached_account_ids.delete(key.last)
@uncached_account_ids << account_id
end
end end
@cached @cached
end end
def cache_uncached! def cache_uncached!
@uncached_account_ids.each do |account_id| to_cache = @uncached_account_ids.to_h do |account_id|
maps_for_account = { maps_for_account = {
following: { account_id => following[account_id] }, following: { account_id => following[account_id] },
followed_by: { account_id => followed_by[account_id] }, followed_by: { account_id => followed_by[account_id] },
@ -77,12 +99,21 @@ class AccountRelationshipsPresenter
muting: { account_id => muting[account_id] }, muting: { account_id => muting[account_id] },
requested: { account_id => requested[account_id] }, requested: { account_id => requested[account_id] },
requested_by: { account_id => requested_by[account_id] }, requested_by: { account_id => requested_by[account_id] },
domain_blocking: { account_id => domain_blocking[account_id] },
endorsed: { account_id => endorsed[account_id] }, endorsed: { account_id => endorsed[account_id] },
account_note: { account_id => account_note[account_id] }, account_note: { account_id => account_note[account_id] },
} }
Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day) [relationship_cache_key(account_id), maps_for_account]
end end
Rails.cache.write_multi(to_cache, expires_in: 1.day)
end
def domain_cache_key(domain)
['exclude_domains', @current_account_id, domain]
end
def relationship_cache_key(account_id)
['relationship', @current_account_id, account_id]
end end
end end

View file

@ -6,8 +6,8 @@
%p.muted-hint= deletion_request.present? ? t('admin.accounts.suspension_reversible_hint_html', date: content_tag(:strong, l(deletion_request.due_at.to_date))) : t('admin.accounts.suspension_irreversible') %p.muted-hint= deletion_request.present? ? t('admin.accounts.suspension_reversible_hint_html', date: content_tag(:strong, l(deletion_request.due_at.to_date))) : t('admin.accounts.suspension_irreversible')
= link_to t('admin.accounts.undo_suspension'), unsuspend_admin_account_path(account.id), method: :post, class: 'button' if can?(:unsuspend, account) = link_to t('admin.accounts.undo_suspension'), unsuspend_admin_account_path(account.id), method: :post, class: 'button' if can?(:unsuspend, account)
= link_to t('admin.accounts.redownload'), redownload_admin_account_path(account.id), method: :post, class: 'button' if can?(:redownload, account) && account.suspension_origin_remote? = link_to t('admin.accounts.redownload'), redownload_admin_account_path(account.id), method: :post, class: 'button' if can?(:redownload, account) && account.suspension_origin_remote?
- if deletion_request.present? - if deletion_request.present? && can?(:destroy, account)
= link_to t('admin.accounts.delete'), admin_account_path(account.id), method: :delete, class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure') } if can?(:destroy, account) = link_to t('admin.accounts.delete'), admin_account_path(account.id), method: :delete, class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure') }
- else - else
.action-buttons .action-buttons
%div %div
@ -15,8 +15,8 @@
= link_to t('admin.accounts.warn'), new_admin_account_action_path(account.id, type: 'none'), class: 'button' if can?(:warn, account) = link_to t('admin.accounts.warn'), new_admin_account_action_path(account.id, type: 'none'), class: 'button' if can?(:warn, account)
- if account.user_disabled? - if account.user_disabled?
= link_to t('admin.accounts.enable'), enable_admin_account_path(account.id), method: :post, class: 'button' if can?(:enable, account.user) = link_to t('admin.accounts.enable'), enable_admin_account_path(account.id), method: :post, class: 'button' if can?(:enable, account.user)
- else - elsif can?(:disable, account.user)
= link_to t('admin.accounts.disable'), new_admin_account_action_path(account.id, type: 'disable'), class: 'button' if can?(:disable, account.user) = link_to t('admin.accounts.disable'), new_admin_account_action_path(account.id, type: 'disable'), class: 'button'
- if account.sensitized? - if account.sensitized?
= link_to t('admin.accounts.undo_sensitized'), unsensitive_admin_account_path(account.id), method: :post, class: 'button' if can?(:unsensitive, account) = link_to t('admin.accounts.undo_sensitized'), unsensitive_admin_account_path(account.id), method: :post, class: 'button' if can?(:unsensitive, account)
- elsif !account.local? || account.user_approved? - elsif !account.local? || account.user_approved?
@ -29,13 +29,13 @@
- if account.user_pending? - if account.user_pending?
= link_to t('admin.accounts.approve'), approve_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' if can?(:approve, account.user) = link_to t('admin.accounts.approve'), approve_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' if can?(:approve, account.user)
= link_to t('admin.accounts.reject'), reject_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:reject, account.user) = link_to t('admin.accounts.reject'), reject_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:reject, account.user)
- unless account.user_confirmed? - if !account.user_confirmed? && can?(:confirm, account.user)
= link_to t('admin.accounts.confirm'), admin_account_confirmation_path(account.id), method: :post, class: 'button' if can?(:confirm, account.user) = link_to t('admin.accounts.confirm'), admin_account_confirmation_path(account.id), method: :post, class: 'button'
- if !account.local? || account.user_approved? - if (!account.local? || account.user_approved?) && can?(:suspend, account)
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(account.id, type: 'suspend'), class: 'button' if can?(:suspend, account) = link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(account.id, type: 'suspend'), class: 'button'
%div %div
- if account.local? - if account.local?
- if !account.memorial? && account.user_approved? - if !account.memorial? && account.user_approved? && can?(:memorialize, account)
= link_to t('admin.accounts.memorialize'), memorialize_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:memorialize, account) = link_to t('admin.accounts.memorialize'), memorialize_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive'
- else - elsif can?(:redownload, account)
= link_to t('admin.accounts.redownload'), redownload_admin_account_path(account.id), method: :post, class: 'button' if can?(:redownload, account) = link_to t('admin.accounts.redownload'), redownload_admin_account_path(account.id), method: :post, class: 'button'

View file

@ -47,8 +47,8 @@
- else - else
= t 'admin.accounts.security_measures.only_password' = t 'admin.accounts.security_measures.only_password'
%td %td
- if account.user&.two_factor_enabled? - if account.user&.two_factor_enabled? && can?(:disable_2fa, account.user)
= table_link_to 'unlock', t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(account.user.id), method: :delete if can?(:disable_2fa, account.user) = table_link_to 'unlock', t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(account.user.id), method: :delete
- if can?(:reset_password, account.user) - if can?(:reset_password, account.user)
%tr %tr
%td %td

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
unless ENV.key?('RAILS_ENV') unless ENV.key?('RAILS_ENV')
STDERR.puts 'ERROR: Missing RAILS_ENV environment variable, please set it to "production", "development", or "test".' warn 'ERROR: Missing RAILS_ENV environment variable, please set it to "production", "development", or "test".'
exit 1 exit 1
end end

View file

@ -79,7 +79,7 @@ Devise.setup do |config|
oidc_options[:client_auth_method] = ENV['OIDC_CLIENT_AUTH_METHOD'] if ENV['OIDC_CLIENT_AUTH_METHOD'] # OPTIONAL (default: basic) oidc_options[:client_auth_method] = ENV['OIDC_CLIENT_AUTH_METHOD'] if ENV['OIDC_CLIENT_AUTH_METHOD'] # OPTIONAL (default: basic)
scope_string = ENV['OIDC_SCOPE'] if ENV['OIDC_SCOPE'] # NEED scope_string = ENV['OIDC_SCOPE'] if ENV['OIDC_SCOPE'] # NEED
scopes = scope_string.split(',') scopes = scope_string.split(',')
oidc_options[:scope] = scopes.map { |x| x.to_sym } oidc_options[:scope] = scopes.map(&:to_sym)
oidc_options[:response_type] = ENV['OIDC_RESPONSE_TYPE'] if ENV['OIDC_RESPONSE_TYPE'] # OPTIONAL (default: code) oidc_options[:response_type] = ENV['OIDC_RESPONSE_TYPE'] if ENV['OIDC_RESPONSE_TYPE'] # OPTIONAL (default: code)
oidc_options[:response_mode] = ENV['OIDC_RESPONSE_MODE'] if ENV['OIDC_RESPONSE_MODE'] # OPTIONAL (default: query) oidc_options[:response_mode] = ENV['OIDC_RESPONSE_MODE'] if ENV['OIDC_RESPONSE_MODE'] # OPTIONAL (default: query)
oidc_options[:display] = ENV['OIDC_DISPLAY'] if ENV['OIDC_DISPLAY'] # OPTIONAL (default: page) oidc_options[:display] = ENV['OIDC_DISPLAY'] if ENV['OIDC_DISPLAY'] # OPTIONAL (default: page)

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
Fabricator(:custom_emoji_category) do
name { sequence(:name) { |i| "name_#{i}" } }
end

View file

@ -178,11 +178,11 @@ RSpec.describe Remotable do
allow(foo).to receive(:public_send) allow(foo).to receive(:public_send)
foo.hoge_remote_url = url foo.hoge_remote_url = url
expect(foo).to have_received(:public_send).with("download_#{hoge}!", url) expect(foo).to have_received(:public_send).with(:"download_#{hoge}!", url)
allow(foo).to receive(:public_send) allow(foo).to receive(:public_send)
foo.download_hoge!(url) foo.download_hoge!(url)
expect(foo).to have_received(:public_send).with("#{hoge}=", response_with_limit) expect(foo).to have_received(:public_send).with(:"#{hoge}=", response_with_limit)
end end
end end
end end

View file

@ -0,0 +1,118 @@
# frozen_string_literal: true
require 'rails_helper'
describe Form::CustomEmojiBatch do
describe '#save' do
subject { described_class.new({ current_account: account }.merge(options)) }
let(:options) { {} }
let(:account) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
context 'with empty custom_emoji_ids' do
let(:options) { { custom_emoji_ids: [] } }
it 'does nothing if custom_emoji_ids is empty' do
expect(subject.save).to be_nil
end
end
describe 'the update action' do
let(:custom_emoji) { Fabricate(:custom_emoji, category: Fabricate(:custom_emoji_category)) }
let(:custom_emoji_category) { Fabricate(:custom_emoji_category) }
context 'without anything to change' do
let(:options) { { action: 'update' } }
it 'silently exits without updating any custom emojis' do
expect { subject.save }.to_not change(Admin::ActionLog, :count)
end
end
context 'with a category_id' do
let(:options) { { action: 'update', custom_emoji_ids: [custom_emoji.id], category_id: custom_emoji_category.id } }
it 'updates the category of the emoji' do
subject.save
expect(custom_emoji.reload.category).to eq(custom_emoji_category)
end
end
context 'with a category_name' do
let(:options) { { action: 'update', custom_emoji_ids: [custom_emoji.id], category_name: custom_emoji_category.name } }
it 'updates the category of the emoji' do
subject.save
expect(custom_emoji.reload.category).to eq(custom_emoji_category)
end
end
end
describe 'the list action' do
let(:custom_emoji) { Fabricate(:custom_emoji, visible_in_picker: false) }
let(:options) { { action: 'list', custom_emoji_ids: [custom_emoji.id] } }
it 'updates the picker visibility of the emoji' do
subject.save
expect(custom_emoji.reload.visible_in_picker).to be(true)
end
end
describe 'the unlist action' do
let(:custom_emoji) { Fabricate(:custom_emoji, visible_in_picker: true) }
let(:options) { { action: 'unlist', custom_emoji_ids: [custom_emoji.id] } }
it 'updates the picker visibility of the emoji' do
subject.save
expect(custom_emoji.reload.visible_in_picker).to be(false)
end
end
describe 'the enable action' do
let(:custom_emoji) { Fabricate(:custom_emoji, disabled: true) }
let(:options) { { action: 'enable', custom_emoji_ids: [custom_emoji.id] } }
it 'updates the disabled value of the emoji' do
subject.save
expect(custom_emoji.reload).to_not be_disabled
end
end
describe 'the disable action' do
let(:custom_emoji) { Fabricate(:custom_emoji, visible_in_picker: false) }
let(:options) { { action: 'disable', custom_emoji_ids: [custom_emoji.id] } }
it 'updates the disabled value of the emoji' do
subject.save
expect(custom_emoji.reload).to be_disabled
end
end
describe 'the copy action' do
let(:custom_emoji) { Fabricate(:custom_emoji) }
let(:options) { { action: 'copy', custom_emoji_ids: [custom_emoji.id] } }
it 'makes a copy of the emoji' do
expect { subject.save }
.to change(CustomEmoji, :count).by(1)
end
end
describe 'the delete action' do
let(:custom_emoji) { Fabricate(:custom_emoji) }
let(:options) { { action: 'delete', custom_emoji_ids: [custom_emoji.id] } }
it 'destroys the emoji' do
subject.save
expect { custom_emoji.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end

View file

@ -5,19 +5,18 @@ require 'rails_helper'
RSpec.describe AccountRelationshipsPresenter do RSpec.describe AccountRelationshipsPresenter do
describe '.initialize' do describe '.initialize' do
before do before do
allow(Account).to receive(:following_map).with(account_ids, current_account_id).and_return(default_map) allow(Account).to receive(:following_map).with(accounts.pluck(:id), current_account_id).and_return(default_map)
allow(Account).to receive(:followed_by_map).with(account_ids, current_account_id).and_return(default_map) allow(Account).to receive(:followed_by_map).with(accounts.pluck(:id), current_account_id).and_return(default_map)
allow(Account).to receive(:blocking_map).with(account_ids, current_account_id).and_return(default_map) allow(Account).to receive(:blocking_map).with(accounts.pluck(:id), current_account_id).and_return(default_map)
allow(Account).to receive(:muting_map).with(account_ids, current_account_id).and_return(default_map) allow(Account).to receive(:muting_map).with(accounts.pluck(:id), current_account_id).and_return(default_map)
allow(Account).to receive(:requested_map).with(account_ids, current_account_id).and_return(default_map) allow(Account).to receive(:requested_map).with(accounts.pluck(:id), current_account_id).and_return(default_map)
allow(Account).to receive(:requested_by_map).with(account_ids, current_account_id).and_return(default_map) allow(Account).to receive(:requested_by_map).with(accounts.pluck(:id), current_account_id).and_return(default_map)
allow(Account).to receive(:domain_blocking_map).with(account_ids, current_account_id).and_return(default_map)
end end
let(:presenter) { described_class.new(account_ids, current_account_id, **options) } let(:presenter) { described_class.new(accounts, current_account_id, **options) }
let(:current_account_id) { Fabricate(:account).id } let(:current_account_id) { Fabricate(:account).id }
let(:account_ids) { [Fabricate(:account).id] } let(:accounts) { [Fabricate(:account)] }
let(:default_map) { { 1 => true } } let(:default_map) { { accounts[0].id => true } }
context 'when options are not set' do context 'when options are not set' do
let(:options) { {} } let(:options) { {} }
@ -29,7 +28,33 @@ RSpec.describe AccountRelationshipsPresenter do
blocking: default_map, blocking: default_map,
muting: default_map, muting: default_map,
requested: default_map, requested: default_map,
domain_blocking: default_map domain_blocking: { accounts[0].id => nil }
)
end
end
context 'with a warm cache' do
let(:options) { {} }
before do
described_class.new(accounts, current_account_id, **options)
allow(Account).to receive(:following_map).with([], current_account_id).and_return({})
allow(Account).to receive(:followed_by_map).with([], current_account_id).and_return({})
allow(Account).to receive(:blocking_map).with([], current_account_id).and_return({})
allow(Account).to receive(:muting_map).with([], current_account_id).and_return({})
allow(Account).to receive(:requested_map).with([], current_account_id).and_return({})
allow(Account).to receive(:requested_by_map).with([], current_account_id).and_return({})
end
it 'sets returns expected values' do
expect(presenter).to have_attributes(
following: default_map,
followed_by: default_map,
blocking: default_map,
muting: default_map,
requested: default_map,
domain_blocking: { accounts[0].id => nil }
) )
end end
end end
@ -86,7 +111,7 @@ RSpec.describe AccountRelationshipsPresenter do
let(:options) { { domain_blocking_map: { 7 => true } } } let(:options) { { domain_blocking_map: { 7 => true } } }
it 'sets @domain_blocking merged with default_map and options[:domain_blocking_map]' do it 'sets @domain_blocking merged with default_map and options[:domain_blocking_map]' do
expect(presenter.domain_blocking).to eq default_map.merge(options[:domain_blocking_map]) expect(presenter.domain_blocking).to eq({ accounts[0].id => nil }.merge(options[:domain_blocking_map]))
end end
end end
end end

View file

@ -4,7 +4,7 @@ require 'rails_helper'
describe 'OmniAuth callbacks' do describe 'OmniAuth callbacks' do
shared_examples 'omniauth provider callbacks' do |provider| shared_examples 'omniauth provider callbacks' do |provider|
subject { post send "user_#{provider}_omniauth_callback_path" } subject { post send :"user_#{provider}_omniauth_callback_path" }
context 'with full information in response' do context 'with full information in response' do
before do before do

608
yarn.lock

File diff suppressed because it is too large Load diff