diff options
Diffstat (limited to 'app/models/concerns')
-rw-r--r-- | app/models/concerns/account_interactions.rb | 13 | ||||
-rw-r--r-- | app/models/concerns/attachmentable.rb | 32 | ||||
-rw-r--r-- | app/models/concerns/cacheable.rb | 15 | ||||
-rw-r--r-- | app/models/concerns/remotable.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/status_threading_concern.rb | 38 |
5 files changed, 76 insertions, 24 deletions
diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb index 3830ba9b0..20fc74ba6 100644 --- a/app/models/concerns/account_interactions.rb +++ b/app/models/concerns/account_interactions.rb @@ -20,6 +20,10 @@ module AccountInteractions follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id) end + def blocked_by_map(target_account_ids, account_id) + follow_mapping(Block.where(account_id: target_account_ids, target_account_id: account_id), :account_id) + end + def muting_map(target_account_ids, account_id) Mute.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |mute, mapping| mapping[mute.target_account_id] = { @@ -38,8 +42,12 @@ module AccountInteractions def domain_blocking_map(target_account_ids, account_id) accounts_map = Account.where(id: target_account_ids).select('id, domain').map { |a| [a.id, a.domain] }.to_h - blocked_domains = AccountDomainBlock.where(account_id: account_id, domain: accounts_map.values).pluck(:domain) - accounts_map.map { |id, domain| [id, blocked_domains.include?(domain)] }.to_h + blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id) + accounts_map.map { |id, domain| [id, blocked_domains[domain]] }.to_h + end + + def domain_blocking_map_by_domain(target_domains, account_id) + follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain) end private @@ -93,6 +101,7 @@ module AccountInteractions if mute.hide_notifications? != notifications mute.update!(hide_notifications: notifications) end + mute end def mute_conversation!(conversation) diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb index 90ce88463..6f8489b89 100644 --- a/app/models/concerns/attachmentable.rb +++ b/app/models/concerns/attachmentable.rb @@ -1,10 +1,15 @@ # frozen_string_literal: true +require 'mime/types' + module Attachmentable extend ActiveSupport::Concern + MAX_MATRIX_LIMIT = 16_777_216 # 4096x4096px or approx. 16MB + included do before_post_process :set_file_extensions + before_post_process :check_image_dimensions end private @@ -12,10 +17,31 @@ module Attachmentable def set_file_extensions self.class.attachment_definitions.each_key do |attachment_name| attachment = send(attachment_name) + next if attachment.blank? - extension = Paperclip::Interpolations.content_type_extension(attachment, :original) - basename = Paperclip::Interpolations.basename(attachment, :original) - attachment.instance_write :file_name, [basename, extension].delete_if(&:blank?).join('.') + + attachment.instance_write :file_name, [Paperclip::Interpolations.basename(attachment, :original), appropriate_extension(attachment)].delete_if(&:blank?).join('.') + end + end + + def check_image_dimensions + self.class.attachment_definitions.each_key do |attachment_name| + attachment = send(attachment_name) + + next if attachment.blank? || !attachment.content_type.match?(/image.*/) || attachment.queued_for_write[:original].blank? + + width, height = FastImage.size(attachment.queued_for_write[:original].path) + + raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported" if width.present? && height.present? && (width * height >= MAX_MATRIX_LIMIT) end end + + def appropriate_extension(attachment) + mime_type = MIME::Types[attachment.content_type] + + extensions_for_mime_type = mime_type.empty? ? [] : mime_type.first.extensions + original_extension = Paperclip::Interpolations.extension(attachment, :original) + + extensions_for_mime_type.include?(original_extension) ? original_extension : extensions_for_mime_type.first + end end diff --git a/app/models/concerns/cacheable.rb b/app/models/concerns/cacheable.rb index 51451d260..d7524cdfd 100644 --- a/app/models/concerns/cacheable.rb +++ b/app/models/concerns/cacheable.rb @@ -3,14 +3,19 @@ module Cacheable extend ActiveSupport::Concern - class_methods do + module ClassMethods + @cache_associated = [] + def cache_associated(*associations) @cache_associated = associations end - end - included do - scope :with_includes, -> { includes(@cache_associated) } - scope :cache_ids, -> { select(:id, :updated_at) } + def with_includes + includes(@cache_associated) + end + + def cache_ids + select(:id, :updated_at) + end end end diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb index 3b8c507c3..7f1ef5191 100644 --- a/app/models/concerns/remotable.rb +++ b/app/models/concerns/remotable.rb @@ -38,7 +38,7 @@ module Remotable self[attribute_name] = url if has_attribute?(attribute_name) end - rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError => e + rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}" nil end diff --git a/app/models/concerns/status_threading_concern.rb b/app/models/concerns/status_threading_concern.rb index fffc095ee..8e817be00 100644 --- a/app/models/concerns/status_threading_concern.rb +++ b/app/models/concerns/status_threading_concern.rb @@ -7,8 +7,8 @@ module StatusThreadingConcern find_statuses_from_tree_path(ancestor_ids(limit), account) end - def descendants(account = nil) - find_statuses_from_tree_path(descendant_ids, account) + def descendants(limit, account = nil, max_child_id = nil, since_child_id = nil, depth = nil) + find_statuses_from_tree_path(descendant_ids(limit, max_child_id, since_child_id, depth), account) end private @@ -46,34 +46,46 @@ module StatusThreadingConcern SQL end - def descendant_ids - descendant_statuses.pluck(:id) + def descendant_ids(limit, max_child_id, since_child_id, depth) + descendant_statuses(limit, max_child_id, since_child_id, depth).pluck(:id) end - def descendant_statuses - Status.find_by_sql([<<-SQL.squish, id: id]) + def descendant_statuses(limit, max_child_id, since_child_id, depth) + Status.find_by_sql([<<-SQL.squish, id: id, limit: limit, max_child_id: max_child_id, since_child_id: since_child_id, depth: depth]) WITH RECURSIVE search_tree(id, path) AS ( SELECT id, ARRAY[id] FROM statuses - WHERE in_reply_to_id = :id + WHERE in_reply_to_id = :id AND COALESCE(id < :max_child_id, TRUE) AND COALESCE(id > :since_child_id, TRUE) UNION ALL SELECT statuses.id, path || statuses.id FROM search_tree JOIN statuses ON statuses.in_reply_to_id = search_tree.id - WHERE NOT statuses.id = ANY(path) + WHERE COALESCE(array_length(path, 1) < :depth, TRUE) AND NOT statuses.id = ANY(path) ) SELECT id FROM search_tree ORDER BY path + LIMIT :limit SQL end def find_statuses_from_tree_path(ids, account) - statuses = statuses_with_accounts(ids).to_a + statuses = statuses_with_accounts(ids).to_a + account_ids = statuses.map(&:account_id).uniq + domains = statuses.map(&:account_domain).compact.uniq + + relations = if account.present? + { + blocking: Account.blocking_map(account_ids, account.id), + blocked_by: Account.blocked_by_map(account_ids, account.id), + muting: Account.muting_map(account_ids, account.id), + following: Account.following_map(account_ids, account.id), + domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id), + } + end - # FIXME: n+1 bonanza - statuses.reject! { |status| filter_from_context?(status, account) } + statuses.reject! { |status| filter_from_context?(status, account, relations) } # Order ancestors/descendants by tree path statuses.sort_by! { |status| ids.index(status.id) } @@ -83,7 +95,7 @@ module StatusThreadingConcern Status.where(id: ids).includes(:account) end - def filter_from_context?(status, account) - StatusFilter.new(status, account).filtered? + def filter_from_context?(status, account, relations) + StatusFilter.new(status, account, relations).filtered? end end |