Merge remote-tracking branch 'tootsuite/main' into custom/quote
This commit is contained in:
@@ -3,29 +3,30 @@
|
||||
#
|
||||
# Table name: statuses
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# uri :string
|
||||
# text :text default(""), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# in_reply_to_id :bigint(8)
|
||||
# reblog_of_id :bigint(8)
|
||||
# url :string
|
||||
# sensitive :boolean default(FALSE), not null
|
||||
# visibility :integer default("public"), not null
|
||||
# spoiler_text :text default(""), not null
|
||||
# reply :boolean default(FALSE), not null
|
||||
# language :string
|
||||
# conversation_id :bigint(8)
|
||||
# local :boolean
|
||||
# account_id :bigint(8) not null
|
||||
# application_id :bigint(8)
|
||||
# in_reply_to_account_id :bigint(8)
|
||||
# quote_id :bigint(8)
|
||||
# poll_id :bigint(8)
|
||||
# deleted_at :datetime
|
||||
# edited_at :datetime
|
||||
# trendable :boolean
|
||||
# id :bigint(8) not null, primary key
|
||||
# uri :string
|
||||
# text :text default(""), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# in_reply_to_id :bigint(8)
|
||||
# reblog_of_id :bigint(8)
|
||||
# url :string
|
||||
# sensitive :boolean default(FALSE), not null
|
||||
# visibility :integer default("public"), not null
|
||||
# spoiler_text :text default(""), not null
|
||||
# reply :boolean default(FALSE), not null
|
||||
# language :string
|
||||
# conversation_id :bigint(8)
|
||||
# local :boolean
|
||||
# account_id :bigint(8) not null
|
||||
# application_id :bigint(8)
|
||||
# in_reply_to_account_id :bigint(8)
|
||||
# quote_id :bigint(8)
|
||||
# poll_id :bigint(8)
|
||||
# deleted_at :datetime
|
||||
# edited_at :datetime
|
||||
# trendable :boolean
|
||||
# ordered_media_attachment_ids :bigint(8) is an Array
|
||||
#
|
||||
|
||||
class Status < ApplicationRecord
|
||||
@@ -35,6 +36,7 @@ class Status < ApplicationRecord
|
||||
include Paginable
|
||||
include Cacheable
|
||||
include StatusThreadingConcern
|
||||
include StatusSnapshotConcern
|
||||
include RateLimitable
|
||||
|
||||
rate_limit by: :account, family: :statuses
|
||||
@@ -60,8 +62,6 @@ class Status < ApplicationRecord
|
||||
belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true
|
||||
belongs_to :quote, foreign_key: 'quote_id', class_name: 'Status', inverse_of: :quoted, optional: true
|
||||
|
||||
has_many :edits, class_name: 'StatusEdit', inverse_of: :status, dependent: :destroy
|
||||
|
||||
has_many :favourites, inverse_of: :status, dependent: :destroy
|
||||
has_many :bookmarks, inverse_of: :status, dependent: :destroy
|
||||
has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy
|
||||
@@ -149,16 +149,27 @@ class Status < ApplicationRecord
|
||||
ids += favourites.where(account: Account.local).pluck(:account_id)
|
||||
ids += reblogs.where(account: Account.local).pluck(:account_id)
|
||||
ids += bookmarks.where(account: Account.local).pluck(:account_id)
|
||||
ids += poll.votes.where(account: Account.local).pluck(:account_id) if poll.present?
|
||||
else
|
||||
ids += preloaded.mentions[id] || []
|
||||
ids += preloaded.favourites[id] || []
|
||||
ids += preloaded.reblogs[id] || []
|
||||
ids += preloaded.bookmarks[id] || []
|
||||
ids += preloaded.votes[id] || []
|
||||
end
|
||||
|
||||
ids.uniq
|
||||
end
|
||||
|
||||
def searchable_text
|
||||
[
|
||||
spoiler_text,
|
||||
FormattingHelper.extract_status_plain_text(self),
|
||||
preloadable_poll ? preloadable_poll.options.join("\n\n") : nil,
|
||||
ordered_media_attachments.map(&:description).join("\n\n"),
|
||||
].compact.join("\n\n")
|
||||
end
|
||||
|
||||
def reply?
|
||||
!in_reply_to_id.nil? || attributes['reply']
|
||||
end
|
||||
@@ -223,24 +234,14 @@ class Status < ApplicationRecord
|
||||
public_visibility? || unlisted_visibility?
|
||||
end
|
||||
|
||||
def snapshot!(media_attachments_changed: false, account_id: nil, at_time: nil)
|
||||
edits.create!(
|
||||
text: text,
|
||||
spoiler_text: spoiler_text,
|
||||
media_attachments_changed: media_attachments_changed,
|
||||
account_id: account_id || self.account_id,
|
||||
created_at: at_time || edited_at
|
||||
)
|
||||
end
|
||||
|
||||
def edited?
|
||||
edited_at.present?
|
||||
end
|
||||
|
||||
alias sign? distributable?
|
||||
|
||||
def with_media?
|
||||
media_attachments.any?
|
||||
ordered_media_attachments.any?
|
||||
end
|
||||
|
||||
def with_preview_card?
|
||||
preview_cards.any?
|
||||
end
|
||||
|
||||
def non_sensitive_with_media?
|
||||
@@ -265,6 +266,15 @@ class Status < ApplicationRecord
|
||||
CustomEmoji.from_text(quote_fields.join(' '), quote&.account&.domain)
|
||||
end
|
||||
|
||||
def ordered_media_attachments
|
||||
if ordered_media_attachment_ids.nil?
|
||||
media_attachments
|
||||
else
|
||||
map = media_attachments.index_by(&:id)
|
||||
ordered_media_attachment_ids.filter_map { |media_attachment_id| map[media_attachment_id] }
|
||||
end
|
||||
end
|
||||
|
||||
def replies_count
|
||||
status_stat&.replies_count || 0
|
||||
end
|
||||
@@ -293,6 +303,10 @@ class Status < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def requires_review?
|
||||
attributes['trendable'].nil? && account.requires_review?
|
||||
end
|
||||
|
||||
def requires_review_notification?
|
||||
attributes['trendable'].nil? && account.requires_review_notification?
|
||||
end
|
||||
@@ -358,28 +372,6 @@ class Status < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def permitted_for(target_account, account)
|
||||
visibility = [:public, :unlisted]
|
||||
|
||||
if account.nil?
|
||||
where(visibility: visibility)
|
||||
elsif target_account.blocking?(account) || (account.domain.present? && target_account.domain_blocking?(account.domain)) # get rid of blocked peeps
|
||||
none
|
||||
elsif account.id == target_account.id # author can see own stuff
|
||||
all
|
||||
else
|
||||
# followers can see followers-only stuff, but also things they are mentioned in.
|
||||
# non-followers can see everything that isn't private/direct, but can see stuff they are mentioned in.
|
||||
visibility.push(:private) if account.following?(target_account)
|
||||
|
||||
scope = left_outer_joins(:reblog)
|
||||
|
||||
scope.where(visibility: visibility)
|
||||
.or(scope.where(id: account.mentions.select(:status_id)))
|
||||
.merge(scope.where(reblog_of_id: nil).or(scope.where.not(reblogs_statuses: { account_id: account.excluded_from_timeline_account_ids })))
|
||||
end
|
||||
end
|
||||
|
||||
def from_text(text)
|
||||
return [] if text.blank?
|
||||
|
||||
@@ -400,6 +392,63 @@ class Status < ApplicationRecord
|
||||
super || build_status_stat
|
||||
end
|
||||
|
||||
# Hack to use a "INSERT INTO ... SELECT ..." query instead of "INSERT INTO ... VALUES ..." query
|
||||
def self._insert_record(values)
|
||||
if values.is_a?(Hash) && values['reblog_of_id'].present?
|
||||
primary_key = self.primary_key
|
||||
primary_key_value = nil
|
||||
|
||||
if primary_key
|
||||
primary_key_value = values[primary_key]
|
||||
|
||||
if !primary_key_value && prefetch_primary_key?
|
||||
primary_key_value = next_sequence_value
|
||||
values[primary_key] = primary_key_value
|
||||
end
|
||||
end
|
||||
|
||||
# The following line is where we differ from stock ActiveRecord implementation
|
||||
im = _compile_reblog_insert(values)
|
||||
|
||||
# Since we are using SELECT instead of VALUES, a non-error `nil` return is possible.
|
||||
# For our purposes, it's equivalent to a foreign key constraint violation
|
||||
result = connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
|
||||
raise ActiveRecord::InvalidForeignKey, "(reblog_of_id)=(#{values['reblog_of_id']}) is not present in table \"statuses\"" if result.nil?
|
||||
|
||||
result
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def self._compile_reblog_insert(values)
|
||||
# This is somewhat equivalent to the following code of ActiveRecord::Persistence:
|
||||
# `arel_table.compile_insert(_substitute_values(values))`
|
||||
# The main difference is that we use a `SELECT` instead of a `VALUES` clause,
|
||||
# which means we have to build the `SELECT` clause ourselves and do a bit more
|
||||
# manual work.
|
||||
|
||||
# Instead of using Arel::InsertManager#values, we are going to use Arel::InsertManager#select
|
||||
im = Arel::InsertManager.new
|
||||
im.into(arel_table)
|
||||
|
||||
binds = []
|
||||
reblog_bind = nil
|
||||
values.each do |name, value|
|
||||
attr = arel_table[name]
|
||||
bind = predicate_builder.build_bind_attribute(attr.name, value)
|
||||
|
||||
im.columns << attr
|
||||
binds << bind
|
||||
|
||||
reblog_bind = bind if name == 'reblog_of_id'
|
||||
end
|
||||
|
||||
im.select(arel_table.where(arel_table[:id].eq(reblog_bind)).where(arel_table[:deleted_at].eq(nil)).project(*binds))
|
||||
|
||||
im
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_status_stat!(attrs)
|
||||
|
Reference in New Issue
Block a user