diff --git a/.rubocop.yml b/.rubocop.yml index 14728bf0e9..2af0f59bbe 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,7 +2,8 @@ require: - rubocop-rails AllCops: - TargetRubyVersion: 2.4 + TargetRubyVersion: 2.5 + NewCops: disable Exclude: - 'spec/**/*' - 'db/**/*' diff --git a/Dockerfile b/Dockerfile index 95d45bab42..136b2a35ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:20.04 as build-dep # Use bash for the shell -SHELL ["bash", "-c"] +SHELL ["/usr/bin/bash", "-c"] # Install Node v12 (LTS) ENV NODE_VER="12.20.0" diff --git a/Gemfile b/Gemfile index 598b1ffe9c..004b3120c6 100644 --- a/Gemfile +++ b/Gemfile @@ -135,7 +135,7 @@ group :development do gem 'active_record_query_trace', '~> 1.8' gem 'annotate', '~> 3.1' gem 'better_errors', '~> 2.9' - gem 'binding_of_caller', '~> 0.7' + gem 'binding_of_caller', '~> 1.0' gem 'bullet', '~> 6.1' gem 'letter_opener', '~> 1.7' gem 'letter_opener_web', '~> 1.4' diff --git a/Gemfile.lock b/Gemfile.lock index fb06ec207d..8d6ce3e0ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,7 +100,7 @@ GEM erubi (>= 1.0.0) rack (>= 0.9.0) bindata (2.4.8) - binding_of_caller (0.8.0) + binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) blurhash (0.1.4) ffi (~> 1.10.0) @@ -165,7 +165,7 @@ GEM crass (1.0.6) css_parser (1.7.1) addressable - debug_inspector (0.0.3) + debug_inspector (1.0.0) devise (4.7.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -691,7 +691,7 @@ DEPENDENCIES annotate (~> 3.1) aws-sdk-s3 (~> 1.87) better_errors (~> 2.9) - binding_of_caller (~> 0.7) + binding_of_caller (~> 1.0) blurhash (~> 0.1) bootsnap (~> 1.5) brakeman (~> 4.10) diff --git a/Procfile.dev b/Procfile.dev index e589bbf630..ba04fb661b 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,4 @@ -web: env PORT=3000 bundle exec puma -C config/puma.rb -sidekiq: env PORT=3000 bundle exec sidekiq +web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb +sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq stream: env PORT=4000 yarn run start webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0 diff --git a/app/controllers/activitypub/inboxes_controller.rb b/app/controllers/activitypub/inboxes_controller.rb index d3044f180f..92dcb5ac77 100644 --- a/app/controllers/activitypub/inboxes_controller.rb +++ b/app/controllers/activitypub/inboxes_controller.rb @@ -5,7 +5,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController include JsonLdHelper include AccountOwnedConcern - before_action :skip_unknown_actor_delete + before_action :skip_unknown_actor_activity before_action :require_signature! skip_before_action :authenticate_user! @@ -18,13 +18,13 @@ class ActivityPub::InboxesController < ActivityPub::BaseController private - def skip_unknown_actor_delete - head 202 if unknown_deleted_account? + def skip_unknown_actor_activity + head 202 if unknown_affected_account? end - def unknown_deleted_account? + def unknown_affected_account? json = Oj.load(body, mode: :strict) - json.is_a?(Hash) && json['type'] == 'Delete' && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists? + json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists? rescue Oj::ParseError false end diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 3e66ff212e..953874e1ab 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -42,7 +42,7 @@ class Api::V1::AccountsController < Api::BaseController end def mute - MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration] || 0)) + MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration]&.to_i || 0)) render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships end diff --git a/app/controllers/api/v1/crypto/keys/claims_controller.rb b/app/controllers/api/v1/crypto/keys/claims_controller.rb index 34b21a3809..f9d202d67b 100644 --- a/app/controllers/api/v1/crypto/keys/claims_controller.rb +++ b/app/controllers/api/v1/crypto/keys/claims_controller.rb @@ -12,7 +12,7 @@ class Api::V1::Crypto::Keys::ClaimsController < Api::BaseController private def set_claim_results - @claim_results = devices.map { |device_params| ::Keys::ClaimService.new.call(current_account, device_params[:account_id], device_params[:device_id]) }.compact + @claim_results = devices.filter_map { |device_params| ::Keys::ClaimService.new.call(current_account, device_params[:account_id], device_params[:device_id]) } end def resource_params diff --git a/app/controllers/api/v1/crypto/keys/queries_controller.rb b/app/controllers/api/v1/crypto/keys/queries_controller.rb index 0851d797d3..e6ce9f9192 100644 --- a/app/controllers/api/v1/crypto/keys/queries_controller.rb +++ b/app/controllers/api/v1/crypto/keys/queries_controller.rb @@ -17,7 +17,7 @@ class Api::V1::Crypto::Keys::QueriesController < Api::BaseController end def set_query_results - @query_results = @accounts.map { |account| ::Keys::QueryService.new.call(account) }.compact + @query_results = @accounts.filter_map { |account| ::Keys::QueryService.new.call(account) } end def account_ids diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb index abbdb410a5..8d82eda5c1 100644 --- a/app/controllers/concerns/cache_concern.rb +++ b/app/controllers/concerns/cache_concern.rb @@ -45,7 +45,7 @@ module CacheConcern end end - raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact + raw.filter_map { |item| cached_keys_with_value[item.id] || uncached[item.id] } end def cache_collection_paginated_by_id(raw, klass, limit, options) diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js index 578375a7f4..ffa783e3b0 100644 --- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js +++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js @@ -227,7 +227,7 @@ class FocalPointModal extends ImmutablePureComponent { const worker = createWorker({ workerPath: tesseractWorkerPath, corePath: tesseractCorePath, - langPath: assetHost, + langPath: `${assetHost}/ocr/lang-data/`, logger: ({ status, progress }) => { if (status === 'recognizing text') { this.setState({ ocrStatus: 'detecting', progress }); diff --git a/app/lib/activitypub/activity/flag.rb b/app/lib/activitypub/activity/flag.rb index 1659bc61fe..8dfc76f0a6 100644 --- a/app/lib/activitypub/activity/flag.rb +++ b/app/lib/activitypub/activity/flag.rb @@ -4,8 +4,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) + target_accounts = object_uris.filter_map { |uri| account_from_uri(uri) }.select(&:local?) + target_statuses_by_account = object_uris.filter_map { |uri| status_from_uri(uri) }.select(&:local?).group_by(&:account_id) target_accounts.each do |target_account| target_statuses = target_statuses_by_account[target_account.id] diff --git a/app/lib/entity_cache.rb b/app/lib/entity_cache.rb index 89cbf8d2c4..e38a3adcdc 100644 --- a/app/lib/entity_cache.rb +++ b/app/lib/entity_cache.rb @@ -29,7 +29,7 @@ class EntityCache uncached.each_value { |item| Rails.cache.write(to_key(:emoji, item.shortcode, domain), item, expires_in: MAX_EXPIRATION) } end - shortcodes.map { |shortcode| cached[to_key(:emoji, shortcode, domain)] || uncached[shortcode] }.compact + shortcodes.filter_map { |shortcode| cached[to_key(:emoji, shortcode, domain)] || uncached[shortcode] } end def to_key(type, *ids) diff --git a/app/lib/spam_check.rb b/app/lib/spam_check.rb index 652d036150..68e586d00b 100644 --- a/app/lib/spam_check.rb +++ b/app/lib/spam_check.rb @@ -186,9 +186,9 @@ class SpamCheck def matching_status_ids if nilsimsa? - other_digests.select { |record| record.start_with?('nilsimsa') && nilsimsa_compare_value(digest, record.split(':')[1]) >= NILSIMSA_COMPARE_THRESHOLD }.map { |record| record.split(':')[2] }.compact + other_digests.select { |record| record.start_with?('nilsimsa') && nilsimsa_compare_value(digest, record.split(':')[1]) >= NILSIMSA_COMPARE_THRESHOLD }.filter_map { |record| record.split(':')[2] } else - other_digests.select { |record| record.start_with?('md5') && record.split(':')[1] == digest }.map { |record| record.split(':')[2] }.compact + other_digests.select { |record| record.start_with?('md5') && record.split(':')[1] == digest }.filter_map { |record| record.split(':')[2] } end end diff --git a/app/models/account.rb b/app/models/account.rb index f6aba74c6c..15bd8a9179 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -387,15 +387,17 @@ class Account < ApplicationRecord end class Field < ActiveModelSerializers::Model - attributes :name, :value, :verified_at, :account, :errors + attributes :name, :value, :verified_at, :account def initialize(account, attributes) - @account = account - @attributes = attributes - @name = attributes['name'].strip[0, string_limit] - @value = attributes['value'].strip[0, string_limit] - @verified_at = attributes['verified_at']&.to_datetime - @errors = {} + @original_field = attributes + string_limit = account.local? ? 255 : 2047 + super( + account: account, + name: attributes['name'].strip[0, string_limit], + value: attributes['value'].strip[0, string_limit], + verified_at: attributes['verified_at']&.to_datetime, + ) end def verified? @@ -417,22 +419,12 @@ class Account < ApplicationRecord end def mark_verified! - @verified_at = Time.now.utc - @attributes['verified_at'] = @verified_at + self.verified_at = Time.now.utc + @original_field['verified_at'] = verified_at end def to_h - { name: @name, value: @value, verified_at: @verified_at } - end - - private - - def string_limit - if account.local? - 255 - else - 2047 - end + { name: name, value: value, verified_at: verified_at } end end @@ -518,7 +510,7 @@ class Account < ApplicationRecord def from_text(text) return [] if text.blank? - text.scan(MENTION_RE).map { |match| match.first.split('@', 2) }.uniq.map do |(username, domain)| + text.scan(MENTION_RE).map { |match| match.first.split('@', 2) }.uniq.filter_map do |(username, domain)| domain = begin if TagManager.instance.local_domain?(domain) nil @@ -527,7 +519,7 @@ class Account < ApplicationRecord end end EntityCache.instance.mention(username, domain) - end.compact + end end private diff --git a/app/models/concerns/status_threading_concern.rb b/app/models/concerns/status_threading_concern.rb index a0ead1995a..5c04108e4c 100644 --- a/app/models/concerns/status_threading_concern.rb +++ b/app/models/concerns/status_threading_concern.rb @@ -83,7 +83,7 @@ module StatusThreadingConcern def find_statuses_from_tree_path(ids, account, promote: false) statuses = Status.with_accounts(ids).to_a account_ids = statuses.map(&:account_id).uniq - domains = statuses.map(&:account_domain).compact.uniq + domains = statuses.filter_map(&:account_domain).uniq relations = relations_map_for_account(account, account_ids, domains) statuses.reject! { |status| StatusFilter.new(status, account, relations).filtered? } diff --git a/app/models/custom_filter.rb b/app/models/custom_filter.rb index 8df8a4fbf3..9d0f3729b3 100644 --- a/app/models/custom_filter.rb +++ b/app/models/custom_filter.rb @@ -46,7 +46,7 @@ class CustomFilter < ApplicationRecord private def clean_up_contexts - self.context = Array(context).map(&:strip).map(&:presence).compact + self.context = Array(context).map(&:strip).filter_map(&:presence) end def remove_cache diff --git a/app/models/home_feed.rb b/app/models/home_feed.rb index 0fe9dae464..d6ebb5fa6b 100644 --- a/app/models/home_feed.rb +++ b/app/models/home_feed.rb @@ -2,12 +2,11 @@ class HomeFeed < Feed def initialize(account) - @type = :home - @id = account.id @account = account + super(:home, account.id) end def regenerating? - redis.exists?("account:#{@id}:regeneration") + redis.exists?("account:#{@account.id}:regeneration") end end diff --git a/app/models/list_feed.rb b/app/models/list_feed.rb index f371e4ed90..47b9281b8c 100644 --- a/app/models/list_feed.rb +++ b/app/models/list_feed.rb @@ -2,7 +2,6 @@ class ListFeed < Feed def initialize(list) - @type = :list - @id = list.id + super(:list, list.id) end end diff --git a/app/models/notification.rb b/app/models/notification.rb index e83123c973..b6db37d6dc 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -92,7 +92,7 @@ class Notification < ApplicationRecord end def reload_stale_associations!(cached_items) - account_ids = (cached_items.map(&:from_account_id) + cached_items.map { |item| item.target_status&.account_id }.compact).uniq + account_ids = (cached_items.map(&:from_account_id) + cached_items.filter_map { |item| item.target_status&.account_id }).uniq return if account_ids.empty? diff --git a/app/models/poll.rb b/app/models/poll.rb index e1ca552521..d2a17277b2 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -73,10 +73,12 @@ class Poll < ApplicationRecord attributes :id, :title, :votes_count, :poll def initialize(poll, id, title, votes_count) - @poll = poll - @id = id - @title = title - @votes_count = votes_count + super( + poll: poll, + id: id, + title: title, + votes_count: votes_count, + ) end end diff --git a/app/models/public_feed.rb b/app/models/public_feed.rb index 2839da5cb5..2528ef1b6f 100644 --- a/app/models/public_feed.rb +++ b/app/models/public_feed.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class PublicFeed < Feed +class PublicFeed # @param [Account] account # @param [Hash] options # @option [Boolean] :with_replies @@ -35,36 +35,38 @@ class PublicFeed < Feed private + attr_reader :account, :options + def allow_local_only? - local_account? && (local_only? || @options[:allow_local_only]) + local_account? && (local_only? || options[:allow_local_only]) end def with_reblogs? - @options[:with_reblogs] + options[:with_reblogs] end def with_replies? - @options[:with_replies] + options[:with_replies] end def local_only? - @options[:local] + options[:local] end def remote_only? - @options[:remote] + options[:remote] end def account? - @account.present? + account.present? end def local_account? - @account&.local? + account&.local? end def media_only? - @options[:only_media] + options[:only_media] end def public_scope @@ -96,9 +98,9 @@ class PublicFeed < Feed end def account_filters_scope - Status.not_excluded_by_account(@account).tap do |scope| - scope.merge!(Status.not_domain_blocked_by_account(@account)) unless local_only? - scope.merge!(Status.in_chosen_languages(@account)) if @account.chosen_languages.present? + Status.not_excluded_by_account(account).tap do |scope| + scope.merge!(Status.not_domain_blocked_by_account(account)) unless local_only? + scope.merge!(Status.in_chosen_languages(account)) if account.chosen_languages.present? end end end diff --git a/app/models/status.rb b/app/models/status.rb index 0d15304b6f..766c13a407 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -387,7 +387,7 @@ class Status < ApplicationRecord def from_text(text) return [] if text.blank? - text.scan(FetchLinkCardService::URL_PATTERN).map(&:first).uniq.map do |url| + text.scan(FetchLinkCardService::URL_PATTERN).map(&:first).uniq.filter_map do |url| status = begin if TagManager.instance.local_url?(url) ActivityPub::TagManager.instance.uri_to_resource(url, Status) @@ -396,7 +396,7 @@ class Status < ApplicationRecord end end status&.distributable? ? status : nil - end.compact + end end end diff --git a/app/models/tag_feed.rb b/app/models/tag_feed.rb index a7d583a7e1..fbbdbaae27 100644 --- a/app/models/tag_feed.rb +++ b/app/models/tag_feed.rb @@ -13,9 +13,8 @@ class TagFeed < PublicFeed # @option [Boolean] :remote # @option [Boolean] :only_media def initialize(tag, account, options = {}) - @tag = tag - @account = account - @options = options + @tag = tag + super(account, options) end # @param [Integer] limit @@ -41,15 +40,15 @@ class TagFeed < PublicFeed private def tagged_with_any_scope - Status.group(:id).tagged_with(tags_for(Array(@tag.name) | Array(@options[:any]))) + Status.group(:id).tagged_with(tags_for(Array(@tag.name) | Array(options[:any]))) end def tagged_with_all_scope - Status.group(:id).tagged_with_all(tags_for(@options[:all])) + Status.group(:id).tagged_with_all(tags_for(options[:all])) end def tagged_with_none_scope - Status.group(:id).tagged_with_none(tags_for(@options[:none])) + Status.group(:id).tagged_with_none(tags_for(options[:none])) end def tags_for(names) diff --git a/app/presenters/status_relationships_presenter.rb b/app/presenters/status_relationships_presenter.rb index 3cc905a75e..f4849d245b 100644 --- a/app/presenters/status_relationships_presenter.rb +++ b/app/presenters/status_relationships_presenter.rb @@ -14,7 +14,7 @@ class StatusRelationshipsPresenter else statuses = statuses.compact status_ids = statuses.flat_map { |s| [s.id, s.reblog_of_id] }.uniq.compact - conversation_ids = statuses.map(&:conversation_id).compact.uniq + conversation_ids = statuses.filter_map(&:conversation_id).uniq pinnable_status_ids = statuses.map(&:proper).select { |s| s.account_id == current_account_id && %w(public unlisted).include?(s.visibility) }.map(&:id) @reblogs_map = Status.reblogs_map(status_ids, current_account_id).merge(options[:reblogs_map] || {}) diff --git a/app/services/activitypub/fetch_featured_collection_service.rb b/app/services/activitypub/fetch_featured_collection_service.rb index 2c27704661..82c861f5b8 100644 --- a/app/services/activitypub/fetch_featured_collection_service.rb +++ b/app/services/activitypub/fetch_featured_collection_service.rb @@ -24,8 +24,7 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService def process_items(items) status_ids = items.map { |item| value_or_id(item) } .reject { |uri| ActivityPub::TagManager.instance.local_uri?(uri) } - .map { |uri| ActivityPub::FetchRemoteStatusService.new.call(uri) } - .compact + .filter_map { |uri| ActivityPub::FetchRemoteStatusService.new.call(uri) } .select { |status| status.account_id == @account.id } .map(&:id) diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb index f1d175dacc..170e6709cd 100644 --- a/app/services/activitypub/process_collection_service.rb +++ b/app/services/activitypub/process_collection_service.rb @@ -37,7 +37,7 @@ class ActivityPub::ProcessCollectionService < BaseService end def process_items(items) - items.reverse_each.map { |item| process_item(item) }.compact + items.reverse_each.filter_map { |item| process_item(item) } end def supported_context? diff --git a/app/services/activitypub/process_poll_service.rb b/app/services/activitypub/process_poll_service.rb index 903b6a78ad..d83e614d88 100644 --- a/app/services/activitypub/process_poll_service.rb +++ b/app/services/activitypub/process_poll_service.rb @@ -30,7 +30,7 @@ class ActivityPub::ProcessPollService < BaseService voters_count = @json['votersCount'] - latest_options = items.map { |item| item['name'].presence || item['content'] }.compact + latest_options = items.filter_map { |item| item['name'].presence || item['content'] } # If for some reasons the options were changed, it invalidates all previous # votes, so we need to remove them diff --git a/app/services/activitypub/synchronize_followers_service.rb b/app/services/activitypub/synchronize_followers_service.rb index d83fcf55e6..93cd602533 100644 --- a/app/services/activitypub/synchronize_followers_service.rb +++ b/app/services/activitypub/synchronize_followers_service.rb @@ -14,7 +14,7 @@ class ActivityPub::SynchronizeFollowersService < BaseService # should never happen in practice, since in almost all cases we keep an # Account record, and should we not do that, we should have sent a Delete. # In any case there is not much we can do if that occurs. - @expected_followers = items.map { |uri| ActivityPub::TagManager.instance.uri_to_resource(uri, Account) }.compact + @expected_followers = items.filter_map { |uri| ActivityPub::TagManager.instance.uri_to_resource(uri, Account) } remove_unexpected_local_followers! handle_unexpected_outgoing_follows! diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 7efa310547..255490d5cd 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -67,7 +67,7 @@ class FetchLinkCardService < BaseService else html = Nokogiri::HTML(@status.text) links = html.css('a') - urls = links.map { |a| Addressable::URI.parse(a['href']) unless skip_link?(a) }.compact.map(&:normalize).compact + urls = links.filter_map { |a| Addressable::URI.parse(a['href']) unless skip_link?(a) }.filter_map(&:normalize) end urls.reject { |uri| bad_url?(uri) }.first diff --git a/app/services/import_service.rb b/app/services/import_service.rb index 0c6ef2238e..b115322831 100644 --- a/app/services/import_service.rb +++ b/app/services/import_service.rb @@ -107,12 +107,12 @@ class ImportService < BaseService end end - statuses = items.map do |uri| + statuses = items.filter_map do |uri| status = ActivityPub::TagManager.instance.uri_to_resource(uri, Status) next if status.nil? && ActivityPub::TagManager.instance.local_uri?(uri) status || ActivityPub::FetchRemoteStatusService.new.call(uri) - end.compact + end account_ids = statuses.map(&:account_id) preloaded_relations = relations_map_for_account(@account, account_ids) diff --git a/app/services/keys/claim_service.rb b/app/services/keys/claim_service.rb index 6721191308..69568a0d19 100644 --- a/app/services/keys/claim_service.rb +++ b/app/services/keys/claim_service.rb @@ -8,11 +8,13 @@ class Keys::ClaimService < BaseService :key, :signature def initialize(account, device_id, key_attributes = {}) - @account = account - @device_id = device_id - @key_id = key_attributes[:key_id] - @key = key_attributes[:key] - @signature = key_attributes[:signature] + super( + account: account, + device_id: device_id, + key_id: key_attributes[:key_id], + key: key_attributes[:key], + signature: key_attributes[:signature], + ) end end diff --git a/app/services/keys/query_service.rb b/app/services/keys/query_service.rb index 286fbd8347..ac3388bdc0 100644 --- a/app/services/keys/query_service.rb +++ b/app/services/keys/query_service.rb @@ -7,8 +7,10 @@ class Keys::QueryService < BaseService attributes :account, :devices def initialize(account, devices) - @account = account - @devices = devices || [] + super( + account: account, + devices: devices || [], + ) end def find(device_id) @@ -20,11 +22,13 @@ class Keys::QueryService < BaseService attributes :device_id, :name, :identity_key, :fingerprint_key def initialize(attributes = {}) - @device_id = attributes[:device_id] - @name = attributes[:name] - @identity_key = attributes[:identity_key] - @fingerprint_key = attributes[:fingerprint_key] - @claim_url = attributes[:claim_url] + super( + device_id: attributes[:device_id], + name: attributes[:name], + identity_key: attributes[:identity_key], + fingerprint_key: attributes[:fingerprint_key], + ) + @claim_url = attributes[:claim_url] end def valid_claim_url? diff --git a/app/validators/existing_username_validator.rb b/app/validators/existing_username_validator.rb index b31d098273..723302ec9b 100644 --- a/app/validators/existing_username_validator.rb +++ b/app/validators/existing_username_validator.rb @@ -5,7 +5,7 @@ class ExistingUsernameValidator < ActiveModel::EachValidator return if value.blank? if options[:multiple] - missing_usernames = value.split(',').map { |username| username.strip.gsub(/\A@/, '') }.map { |username| username unless Account.find_local(username) }.compact + missing_usernames = value.split(',').map { |username| username.strip.gsub(/\A@/, '') }.filter_map { |username| username unless Account.find_local(username) } record.errors.add(attribute, I18n.t('existing_username_validator.not_found_multiple', usernames: missing_usernames.join(', '))) if missing_usernames.any? else record.errors.add(attribute, I18n.t('existing_username_validator.not_found')) unless Account.find_local(value.strip.gsub(/\A@/, '')) diff --git a/config/application.rb b/config/application.rb index af77352212..696dc17e66 100644 --- a/config/application.rb +++ b/config/application.rb @@ -7,6 +7,7 @@ require 'rails/all' Bundler.require(*Rails.groups) require_relative '../app/lib/exceptions' +require_relative '../lib/enumerable' require_relative '../lib/redis/namespace_extensions' require_relative '../lib/paperclip/url_generator_extensions' require_relative '../lib/paperclip/attachment_extensions' diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index e8d7697a11..9e037f421f 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -60,6 +60,7 @@ Devise.setup do |config| saml_options[:attribute_statements][:verified] = [ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED']] if ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED'] saml_options[:attribute_statements][:verified_email] = [ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL']] if ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL'] saml_options[:uid_attribute] = ENV['SAML_UID_ATTRIBUTE'] if ENV['SAML_UID_ATTRIBUTE'] + saml_options[:allowed_clock_drift] = ENV['SAML_ALLOWED_CLOCK_DRIFT'] if ENV['SAML_ALLOWED_CLOCK_DRIFT'] config.omniauth :saml, saml_options end end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index a3f2c5796a..cc4140d145 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -68,7 +68,7 @@ en: deleted: explanation: The following security key has been deleted from your account subject: 'Mastodon: Security key deleted' - title: One of you security keys has been deleted + title: One of your security keys has been deleted webauthn_disabled: explanation: Authentication with security keys has been disabled for your account. Login is now possible using only the token generated by the paired TOTP app. subject: 'Mastodon: Authentication with security keys disabled' diff --git a/lib/enumerable.rb b/lib/enumerable.rb new file mode 100644 index 0000000000..66918f65e8 --- /dev/null +++ b/lib/enumerable.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Enumerable + # TODO: Remove this once stop to support Ruby 2.6 + if RUBY_VERSION < '2.7.0' + def filter_map + if block_given? + result = [] + each do |element| + res = yield element + result << res if res + end + result + else + Enumerator.new do |yielder| + result = [] + each do |element| + res = yielder.yield element + result << res if res + end + result + end + end + end + end +end diff --git a/lib/mastodon/maintenance_cli.rb b/lib/mastodon/maintenance_cli.rb index 822051ceb8..029d42a05a 100644 --- a/lib/mastodon/maintenance_cli.rb +++ b/lib/mastodon/maintenance_cli.rb @@ -160,6 +160,7 @@ module Mastodon deduplicate_tags! deduplicate_webauthn_credentials! + Scenic.database.refresh_materialized_view('instances', concurrently: true, cascade: false) if ActiveRecord::Migrator.current_version >= 2020_12_06_004238 Rails.cache.clear @prompt.say 'Finished!' @@ -188,6 +189,11 @@ module Mastodon else ActiveRecord::Base.connection.add_index :accounts, "lower (username), COALESCE(lower(domain), '')", name: 'index_accounts_on_username_and_domain_lower', unique: true end + + @prompt.say 'Reindexing textual indexes on accounts…' + ActiveRecord::Base.connection.execute('REINDEX INDEX search_index;') + ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_uri;') + ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_url;') end def deduplicate_users! diff --git a/lib/paperclip/attachment_extensions.rb b/lib/paperclip/attachment_extensions.rb index 94f7769b65..e25a34213b 100644 --- a/lib/paperclip/attachment_extensions.rb +++ b/lib/paperclip/attachment_extensions.rb @@ -27,7 +27,7 @@ module Paperclip return true if original_filename == other_filename return false if original_filename.nil? - formats = styles.values.map(&:format).compact + formats = styles.values.filter_map(&:format) return false if formats.empty? diff --git a/lib/paperclip/color_extractor.rb b/lib/paperclip/color_extractor.rb index f850dc067c..a70a3d21f2 100644 --- a/lib/paperclip/color_extractor.rb +++ b/lib/paperclip/color_extractor.rb @@ -142,7 +142,7 @@ module Paperclip g = 0.0 b = 0.0 - if s == 0.0 + if s.zero? r = l.to_f g = l.to_f b = l.to_f # achromatic diff --git a/package.json b/package.json index caf9a99b6f..98f8b6d83f 100644 --- a/package.json +++ b/package.json @@ -167,7 +167,7 @@ "tiny-queue": "^0.2.1", "uuid": "^8.3.1", "webpack": "^4.44.2", - "webpack-assets-manifest": "^3.1.1", + "webpack-assets-manifest": "^4.0.0", "webpack-bundle-analyzer": "^4.3.0", "webpack-cli": "^3.3.12", "webpack-merge": "^5.7.3", diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb index 1e656503fb..d9ee37ffa2 100644 --- a/spec/controllers/api/v1/accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts_controller_spec.rb @@ -268,6 +268,34 @@ RSpec.describe Api::V1::AccountsController, type: :controller do it_behaves_like 'forbidden for wrong scope', 'read:accounts' end + describe 'POST #mute with nonzero duration set' do + let(:scopes) { 'write:mutes' } + let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + + before do + user.account.follow!(other_account) + post :mute, params: { id: other_account.id, duration: 300 } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'does not remove the following relation between user and target user' do + expect(user.account.following?(other_account)).to be true + end + + it 'creates a muting relation' do + expect(user.account.muting?(other_account)).to be true + end + + it 'mutes notifications' do + expect(user.account.muting_notifications?(other_account)).to be true + end + + it_behaves_like 'forbidden for wrong scope', 'read:accounts' + end + describe 'POST #unmute' do let(:scopes) { 'write:mutes' } let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } diff --git a/yarn.lock b/yarn.lock index 91270c11ca..df415f517a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2798,7 +2798,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0, chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2815,7 +2815,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0, chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== @@ -6831,6 +6831,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lockfile@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" + integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA== + dependencies: + signal-exit "^3.0.2" + lodash.capitalize@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" @@ -6841,6 +6848,11 @@ lodash.defaults@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= +lodash.escaperegexp@^4.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= + lodash.get@^4.0: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -7199,14 +7211,14 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0, mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -9561,7 +9573,7 @@ schema-utils@^2.2.0, schema-utils@^2.6.5: ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0: +schema-utils@^3.0, schema-utils@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== @@ -10377,7 +10389,7 @@ table@^6.0.4: slice-ansi "^4.0.0" string-width "^4.2.0" -tapable@^1.0.0, tapable@^1.1.3: +tapable@^1.0, tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== @@ -11051,18 +11063,21 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webpack-assets-manifest@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/webpack-assets-manifest/-/webpack-assets-manifest-3.1.1.tgz#39bbc3bf2ee57fcd8ba07cda51c9ba4a3c6ae1de" - integrity sha512-JV9V2QKc5wEWQptdIjvXDUL1ucbPLH2f27toAY3SNdGZp+xSaStAgpoMcvMZmqtFrBc9a5pTS1058vxyMPOzRQ== +webpack-assets-manifest@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/webpack-assets-manifest/-/webpack-assets-manifest-4.0.0.tgz#6c906f6a77945aa326822e158f22d172ccc59f0f" + integrity sha512-zbozd1Cr6gS5XMWWHfISusocOO2bO05ktpJXWdoYtv12/FSXsNqyVjNwLE9ehBXDsEOwRtqd3kPDdTZKFjjD/w== dependencies: - chalk "^2.0" + chalk "^4.0" + deepmerge "^4.2.2" + lockfile "^1.0.4" + lodash.escaperegexp "^4.0" lodash.get "^4.0" lodash.has "^4.0" - mkdirp "^0.5" - schema-utils "^1.0.0" - tapable "^1.0.0" - webpack-sources "^1.0.0" + mkdirp "^1.0" + schema-utils "^3.0" + tapable "^1.0" + webpack-sources "^1.0" webpack-bundle-analyzer@^4.3.0: version "4.3.0" @@ -11162,7 +11177,7 @@ webpack-merge@^5.7.3: clone-deep "^4.0.1" wildcard "^2.0.0" -webpack-sources@^1.0.0, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: +webpack-sources@^1.0, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==