Merge remote-tracking branch 'parent/main' into kb_migration
This commit is contained in:
commit
979292d950
56 changed files with 774 additions and 277 deletions
157
app/lib/account_statuses_filter.rb
Normal file
157
app/lib/account_statuses_filter.rb
Normal file
|
@ -0,0 +1,157 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AccountStatusesFilter
|
||||
KEYS = %i(
|
||||
pinned
|
||||
tagged
|
||||
only_media
|
||||
exclude_replies
|
||||
exclude_reblogs
|
||||
).freeze
|
||||
|
||||
attr_reader :params, :account, :current_account
|
||||
|
||||
def initialize(account, current_account, params = {})
|
||||
@account = account
|
||||
@current_account = current_account
|
||||
@params = params
|
||||
end
|
||||
|
||||
def results
|
||||
scope = initial_scope
|
||||
|
||||
scope.merge!(pinned_scope) if pinned?
|
||||
scope.merge!(only_media_scope) if only_media?
|
||||
scope.merge!(no_replies_scope) if exclude_replies?
|
||||
scope.merge!(no_reblogs_scope) if exclude_reblogs?
|
||||
scope.merge!(hashtag_scope) if tagged?
|
||||
|
||||
available_searchabilities = [:public, :unlisted, :private, :direct, :limited, nil]
|
||||
available_visibilities = [:public, :public_unlisted, :login, :unlisted, :private, :direct, :limited]
|
||||
|
||||
available_searchabilities = [:public] if domain_block&.reject_send_not_public_searchability
|
||||
available_visibilities -= [:public_unlisted] if domain_block&.reject_send_public_unlisted || (domain_block&.detect_invalid_subscription && @account.user&.setting_reject_public_unlisted_subscription)
|
||||
available_visibilities -= [:unlisted] if domain_block&.detect_invalid_subscription && @account.user&.setting_reject_unlisted_subscription
|
||||
available_visibilities -= [:login] if current_account.nil?
|
||||
|
||||
scope.merge!(scope.where(spoiler_text: ['', nil])) if domain_block&.reject_send_sensitive
|
||||
scope.merge!(scope.where(searchability: available_searchabilities))
|
||||
scope.merge!(scope.where(visibility: available_visibilities))
|
||||
|
||||
scope
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initial_scope
|
||||
if (suspended? || (domain_block&.reject_send_dissubscribable && @account.dissubscribable)) || domain_block&.reject_send_media || blocked?
|
||||
Status.none
|
||||
elsif anonymous?
|
||||
account.statuses.where(visibility: %i(public unlisted public_unlisted))
|
||||
elsif author?
|
||||
account.statuses.all # NOTE: #merge! does not work without the #all
|
||||
else
|
||||
filtered_scope
|
||||
end
|
||||
end
|
||||
|
||||
def filtered_scope
|
||||
scope = account.statuses.left_outer_joins(:mentions)
|
||||
|
||||
scope.merge!(scope.where(visibility: follower? ? %i(public unlisted public_unlisted login private) : %i(public unlisted public_unlisted login)).or(scope.where(mentions: { account_id: current_account.id })).group(Status.arel_table[:id]))
|
||||
scope.merge!(filtered_reblogs_scope) if reblogs_may_occur?
|
||||
|
||||
scope
|
||||
end
|
||||
|
||||
def filtered_reblogs_scope
|
||||
scope = Status.left_outer_joins(reblog: :account)
|
||||
scope
|
||||
.where(reblog_of_id: nil)
|
||||
.or(
|
||||
scope
|
||||
.where.not(reblog: { account_id: current_account.excluded_from_timeline_account_ids })
|
||||
.where.not(reblog: { accounts: { domain: current_account.excluded_from_timeline_domains } })
|
||||
)
|
||||
end
|
||||
|
||||
def only_media_scope
|
||||
Status.joins(:media_attachments).merge(account.media_attachments.reorder(nil)).group(Status.arel_table[:id])
|
||||
end
|
||||
|
||||
def no_replies_scope
|
||||
Status.without_replies
|
||||
end
|
||||
|
||||
def no_reblogs_scope
|
||||
Status.without_reblogs
|
||||
end
|
||||
|
||||
def pinned_scope
|
||||
account.pinned_statuses.group(Status.arel_table[:id], StatusPin.arel_table[:created_at])
|
||||
end
|
||||
|
||||
def hashtag_scope
|
||||
tag = Tag.find_normalized(params[:tagged])
|
||||
|
||||
if tag
|
||||
Status.tagged_with(tag.id)
|
||||
else
|
||||
Status.none
|
||||
end
|
||||
end
|
||||
|
||||
def suspended?
|
||||
account.suspended?
|
||||
end
|
||||
|
||||
def anonymous?
|
||||
current_account.nil?
|
||||
end
|
||||
|
||||
def author?
|
||||
current_account.id == account.id
|
||||
end
|
||||
|
||||
def blocked?
|
||||
return false if current_account.nil?
|
||||
|
||||
account.blocking?(current_account) || (current_account.domain.present? && account.domain_blocking?(current_account.domain))
|
||||
end
|
||||
|
||||
def follower?
|
||||
current_account.following?(account)
|
||||
end
|
||||
|
||||
def reblogs_may_occur?
|
||||
!exclude_reblogs? && !only_media? && !tagged?
|
||||
end
|
||||
|
||||
def pinned?
|
||||
truthy_param?(:pinned)
|
||||
end
|
||||
|
||||
def only_media?
|
||||
truthy_param?(:only_media)
|
||||
end
|
||||
|
||||
def exclude_replies?
|
||||
truthy_param?(:exclude_replies)
|
||||
end
|
||||
|
||||
def exclude_reblogs?
|
||||
truthy_param?(:exclude_reblogs)
|
||||
end
|
||||
|
||||
def tagged?
|
||||
params[:tagged].present?
|
||||
end
|
||||
|
||||
def truthy_param?(key)
|
||||
ActiveModel::Type::Boolean.new.cast(params[key])
|
||||
end
|
||||
|
||||
def domain_block
|
||||
@domain_block = DomainBlock.find_by(domain: @account&.domain)
|
||||
end
|
||||
end
|
9
app/lib/admin/account_statuses_filter.rb
Normal file
9
app/lib/admin/account_statuses_filter.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::AccountStatusesFilter < AccountStatusesFilter
|
||||
private
|
||||
|
||||
def blocked?
|
||||
false
|
||||
end
|
||||
end
|
|
@ -8,7 +8,7 @@ class SearchQueryParser < Parslet::Parser
|
|||
rule(:operator) { (str('+') | str('-')).as(:operator) }
|
||||
rule(:prefix) { term >> colon }
|
||||
rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) }
|
||||
rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) }
|
||||
rule(:phrase) { (quote >> (match('[^\s"]').repeat(1).as(:term) >> space.maybe).repeat >> quote).as(:phrase) }
|
||||
rule(:clause) { (operator.maybe >> prefix.maybe.as(:prefix) >> (phrase | term | shortcode)).as(:clause) | prefix.as(:clause) | quote.as(:junk) }
|
||||
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
|
||||
root(:query)
|
||||
|
|
|
@ -10,6 +10,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
after
|
||||
during
|
||||
in
|
||||
domain
|
||||
).freeze
|
||||
|
||||
class Query
|
||||
|
@ -72,7 +73,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
searchability_limited,
|
||||
]
|
||||
definition_should << searchability_public if %i(public).include?(@searchability)
|
||||
definition_should << searchability_private if %i(public private).include?(@searchability)
|
||||
definition_should << searchability_private if %i(public unlisted private).include?(@searchability)
|
||||
|
||||
{
|
||||
bool: {
|
||||
|
@ -95,9 +96,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: {
|
||||
_index: StatusesIndex.index_name,
|
||||
},
|
||||
term: { _index: StatusesIndex.index_name },
|
||||
},
|
||||
{
|
||||
term: {
|
||||
|
@ -117,12 +116,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
term: { _index: StatusesIndex.index_name },
|
||||
},
|
||||
{
|
||||
exists: {
|
||||
field: 'searchability',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: { searchable_by: @account.id },
|
||||
term: { searchable_by: @options[:current_account].id },
|
||||
},
|
||||
],
|
||||
must_not: [
|
||||
|
@ -139,9 +133,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
bool: {
|
||||
must: [
|
||||
{
|
||||
exists: {
|
||||
field: 'searchability',
|
||||
},
|
||||
term: { _index: StatusesIndex.index_name },
|
||||
},
|
||||
{
|
||||
term: { searchability: 'public' },
|
||||
|
@ -156,9 +148,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
bool: {
|
||||
must: [
|
||||
{
|
||||
exists: {
|
||||
field: 'searchability',
|
||||
},
|
||||
term: { _index: StatusesIndex.index_name },
|
||||
},
|
||||
{
|
||||
term: { searchability: 'private' },
|
||||
|
@ -176,20 +166,27 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
bool: {
|
||||
must: [
|
||||
{
|
||||
exists: {
|
||||
field: 'searchability',
|
||||
},
|
||||
term: { _index: StatusesIndex.index_name },
|
||||
},
|
||||
{
|
||||
term: { searchability: 'limited' },
|
||||
},
|
||||
{
|
||||
term: { account_id: @account.id },
|
||||
term: { account_id: @options[:current_account].id },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
def following_account_ids
|
||||
return @following_account_ids if defined?(@following_account_ids)
|
||||
|
||||
account_exists_sql = Account.where('accounts.id = follows.target_account_id').where(searchability: %w(public private)).reorder(nil).select(1).to_sql
|
||||
status_exists_sql = Status.where('statuses.account_id = follows.target_account_id').where(reblog_of_id: nil).where(searchability: %w(public private)).reorder(nil).select(1).to_sql
|
||||
following_accounts = Follow.where(account_id: @options[:current_account].id).merge(Account.where("EXISTS (#{account_exists_sql})").or(Account.where("EXISTS (#{status_exists_sql})")))
|
||||
@following_account_ids = following_accounts.pluck(:target_account_id)
|
||||
end
|
||||
end
|
||||
|
||||
class Operator
|
||||
|
@ -217,7 +214,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
|
||||
def to_query
|
||||
if @term.start_with?('#')
|
||||
{ match: { tags: { query: @term } } }
|
||||
{ match: { tags: { query: @term, operator: 'and' } } }
|
||||
else
|
||||
# { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } }
|
||||
{ match_phrase: { text: { query: @term } } }
|
||||
|
@ -332,17 +329,16 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
rule(clause: subtree(:clause)) do
|
||||
prefix = clause[:prefix][:term].to_s if clause[:prefix]
|
||||
operator = clause[:operator]&.to_s
|
||||
term = clause[:phrase] ? clause[:phrase].map { |term| term[:term].to_s }.join(' ') : clause[:term].to_s
|
||||
|
||||
if clause[:prefix] && SUPPORTED_PREFIXES.include?(prefix)
|
||||
PrefixClause.new(prefix, operator, clause[:term].to_s, current_account: current_account)
|
||||
PrefixClause.new(prefix, operator, term, current_account: current_account)
|
||||
elsif clause[:prefix]
|
||||
TermClause.new(operator, "#{prefix} #{clause[:term]}")
|
||||
TermClause.new(operator, "#{prefix} #{term}")
|
||||
elsif clause[:term]
|
||||
TermClause.new(operator, clause[:term].to_s)
|
||||
elsif clause[:shortcode]
|
||||
TermClause.new(operator, ":#{clause[:term]}:")
|
||||
TermClause.new(operator, term)
|
||||
elsif clause[:phrase]
|
||||
PhraseClause.new(operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s)
|
||||
PhraseClause.new(operator, term)
|
||||
else
|
||||
raise "Unexpected clause type: #{clause}"
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ class TagManager
|
|||
domain = uri.host + (uri.port ? ":#{uri.port}" : '')
|
||||
|
||||
TagManager.instance.web_domain?(domain)
|
||||
rescue Addressable::URI::InvalidURIError
|
||||
rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
|
||||
false
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue