Merge commit '4aea3f88a6d30f102a79c2da7fcfac96465ba1a8' into merging-upstream
This commit is contained in:
@ -106,6 +106,7 @@ class Account < ApplicationRecord
|
||||
scope :by_domain_accounts, -> { group(:domain).select(:domain, 'COUNT(*) AS accounts_count').order('accounts_count desc') }
|
||||
scope :matches_username, ->(value) { where(arel_table[:username].matches("#{value}%")) }
|
||||
scope :matches_display_name, ->(value) { where(arel_table[:display_name].matches("#{value}%")) }
|
||||
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
|
||||
|
||||
delegate :email,
|
||||
:current_sign_in_ip,
|
||||
@ -174,6 +175,10 @@ class Account < ApplicationRecord
|
||||
end
|
||||
|
||||
class << self
|
||||
def readonly_attributes
|
||||
super - %w(statuses_count following_count followers_count)
|
||||
end
|
||||
|
||||
def domains
|
||||
reorder(nil).pluck('distinct accounts.domain')
|
||||
end
|
||||
|
@ -27,9 +27,11 @@ module Remotable
|
||||
|
||||
matches = response.headers['content-disposition']&.match(/filename="([^"]*)"/)
|
||||
filename = matches.nil? ? parsed_url.path.split('/').last : matches[1]
|
||||
basename = SecureRandom.hex(8)
|
||||
extname = File.extname(filename)
|
||||
|
||||
send("#{attachment_name}=", StringIO.new(response.to_s))
|
||||
send("#{attachment_name}_file_name=", filename)
|
||||
send("#{attachment_name}_file_name=", basename + extname)
|
||||
|
||||
self[attribute_name] = url if has_attribute?(attribute_name)
|
||||
rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError => e
|
||||
|
38
app/models/custom_emoji.rb
Normal file
38
app/models/custom_emoji.rb
Normal file
@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: custom_emojis
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# shortcode :string default(""), not null
|
||||
# domain :string
|
||||
# image_file_name :string
|
||||
# image_content_type :string
|
||||
# image_file_size :integer
|
||||
# image_updated_at :datetime
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class CustomEmoji < ApplicationRecord
|
||||
SHORTCODE_RE_FRAGMENT = '[a-zA-Z0-9_]{2,}'
|
||||
|
||||
SCAN_RE = /(?<=[^[:alnum:]:]|\n|^)
|
||||
:(#{SHORTCODE_RE_FRAGMENT}):
|
||||
(?=[^[:alnum:]:]|$)/x
|
||||
|
||||
has_attached_file :image
|
||||
|
||||
validates_attachment :image, content_type: { content_type: 'image/png' }, presence: true, size: { in: 0..50.kilobytes }
|
||||
validates :shortcode, uniqueness: { scope: :domain }, format: { with: /\A#{SHORTCODE_RE_FRAGMENT}\z/ }, length: { minimum: 2 }
|
||||
|
||||
include Remotable
|
||||
|
||||
class << self
|
||||
def from_text(text, domain)
|
||||
return [] if text.blank?
|
||||
shortcodes = text.scan(SCAN_RE).map(&:first)
|
||||
where(shortcode: shortcodes, domain: domain)
|
||||
end
|
||||
end
|
||||
end
|
28
app/models/instance_filter.rb
Normal file
28
app/models/instance_filter.rb
Normal file
@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class InstanceFilter
|
||||
attr_reader :params
|
||||
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def results
|
||||
scope = Account.remote.by_domain_accounts
|
||||
params.each do |key, value|
|
||||
scope.merge!(scope_for(key, value)) if value.present?
|
||||
end
|
||||
scope
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def scope_for(key, value)
|
||||
case key.to_s
|
||||
when 'domain_name'
|
||||
Account.matches_domain(value)
|
||||
else
|
||||
raise "Unknown filter: #{key}"
|
||||
end
|
||||
end
|
||||
end
|
@ -56,15 +56,21 @@ class MediaAttachment < ApplicationRecord
|
||||
|
||||
validates :account, presence: true
|
||||
|
||||
scope :attached, -> { where.not(status_id: nil) }
|
||||
scope :attached, -> { where.not(status_id: nil) }
|
||||
scope :unattached, -> { where(status_id: nil) }
|
||||
scope :local, -> { where(remote_url: '') }
|
||||
scope :local, -> { where(remote_url: '') }
|
||||
scope :remote, -> { where.not(remote_url: '') }
|
||||
|
||||
default_scope { order(id: :asc) }
|
||||
|
||||
def local?
|
||||
remote_url.blank?
|
||||
end
|
||||
|
||||
def needs_redownload?
|
||||
file.blank? && remote_url.present?
|
||||
end
|
||||
|
||||
def to_param
|
||||
shortcode
|
||||
end
|
||||
|
44
app/models/site_upload.rb
Normal file
44
app/models/site_upload.rb
Normal file
@ -0,0 +1,44 @@
|
||||
# frozen_string_literal: true
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: site_uploads
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# var :string default(""), not null
|
||||
# file_file_name :string
|
||||
# file_content_type :string
|
||||
# file_file_size :integer
|
||||
# file_updated_at :datetime
|
||||
# meta :json
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class SiteUpload < ApplicationRecord
|
||||
has_attached_file :file
|
||||
|
||||
validates_attachment_content_type :file, content_type: /\Aimage\/.*\z/
|
||||
validates :var, presence: true, uniqueness: true
|
||||
|
||||
before_save :set_meta
|
||||
after_commit :clear_cache
|
||||
|
||||
def cache_key
|
||||
"site_uploads/#{var}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_meta
|
||||
tempfile = file.queued_for_write[:original]
|
||||
|
||||
return if tempfile.nil?
|
||||
|
||||
geometry = Paperclip::Geometry.from_file(tempfile)
|
||||
self.meta = { width: geometry.width.to_i, height: geometry.height.to_i }
|
||||
end
|
||||
|
||||
def clear_cache
|
||||
Rails.cache.delete(cache_key)
|
||||
end
|
||||
end
|
@ -55,7 +55,7 @@ class Status < ApplicationRecord
|
||||
has_one :notification, as: :activity, dependent: :destroy
|
||||
has_one :stream_entry, as: :activity, inverse_of: :status
|
||||
|
||||
validates :uri, uniqueness: true, unless: :local?
|
||||
validates :uri, uniqueness: true, presence: true, unless: :local?
|
||||
validates :text, presence: true, unless: :reblog?
|
||||
validates_with StatusLengthValidator
|
||||
validates :reblog, uniqueness: { scope: :account }, if: :reblog?
|
||||
@ -70,7 +70,6 @@ class Status < ApplicationRecord
|
||||
scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') }
|
||||
scope :with_public_visibility, -> { where(visibility: :public) }
|
||||
scope :tagged_with, ->(tag) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag }) }
|
||||
scope :local_only, -> { left_outer_joins(:account).where(accounts: { domain: nil }) }
|
||||
scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: false }) }
|
||||
scope :including_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: true }) }
|
||||
scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) }
|
||||
@ -132,6 +131,10 @@ class Status < ApplicationRecord
|
||||
!sensitive? && media_attachments.any?
|
||||
end
|
||||
|
||||
def emojis
|
||||
CustomEmoji.from_text(text, account.domain)
|
||||
end
|
||||
|
||||
after_create :store_uri, if: :local?
|
||||
|
||||
before_validation :prepare_contents, if: :local?
|
||||
@ -221,7 +224,7 @@ class Status < ApplicationRecord
|
||||
private
|
||||
|
||||
def timeline_scope(local_only = false)
|
||||
starting_scope = local_only ? Status.local_only : Status
|
||||
starting_scope = local_only ? Status.local : Status
|
||||
starting_scope
|
||||
.with_public_visibility
|
||||
.without_reblogs
|
||||
|
Reference in New Issue
Block a user