Files
danbooru/app/logical/post_sets/post.rb
evazion ad02e0f62c posts/index: fix rating:s being included in page title in safe mode.
Fixes bug described in d3e4ac7c17 (commitcomment-39049351)

When dealing with searches, there are several variables we have to keep
in mind:

* Whether tag aliases should be applied.
* Whether search terms should be sorted.
* Whether the rating:s and -status:deleted metatags should be added by
  safe mode and the hide deleted posts setting.

Which of these things we need to do depends on the context:

* We want to apply aliases when actually doing the search, calculating
  the count, looking up the wiki excerpt, recording missed/popular
  searches in Reportbooru, and calculating related tags for the sidebar,
  but not when displaying the raw search as typed by the user (for
  example, in the page title or in the tag search box).
* We want to sort the search when calculating cache keys for fast_count
  or related tags, and when recording missed/popular searches, but not
  in the page title or when displaying the raw search.
* We want to add rating:s and -status:deleted when performing the
  search, calculating the count, or recording missed/popular searches,
  but not when calculating related tags for the sidebar, or when
  displaying the page title or raw search.

Here we introduce normalized_query and try to use it in contexts where
query normalization is necessary. When to use the normalized query
versus the raw unnormalized query is still subtle and prone to error.
2020-05-12 21:47:00 -05:00

205 lines
5.4 KiB
Ruby

module PostSets
class Post
MAX_PER_PAGE = 200
MAX_SIDEBAR_TAGS = 25
attr_reader :page, :random, :post_count, :format, :tag_string, :query
delegate :normalized_query, to: :query
def initialize(tags, page = 1, per_page = nil, random: false, format: "html")
@query = PostQueryBuilder.new(tags, CurrentUser.user, safe_mode: CurrentUser.safe_mode?, hide_deleted_posts: CurrentUser.hide_deleted_posts?)
@tag_string = tags
@page = page
@per_page = per_page
@random = random.to_s.truthy?
@format = format.to_s
end
def humanized_tag_string
query.split_query.map { |tag| tag.tr("_", " ").titleize }.to_sentence
end
def has_blank_wiki?
tag.present? && !wiki_page.present?
end
def wiki_page
return nil unless tag.present? && tag.wiki_page.present?
return nil unless !tag.wiki_page.is_deleted?
tag.wiki_page
end
def tag
return nil unless normalized_query.has_single_tag?
@tag ||= Tag.find_by(name: normalized_query.tags.first.name)
end
def artist
return nil unless tag.present? && tag.artist?
return nil unless tag.artist.present? && !tag.artist.is_deleted?
tag.artist
end
def pool
pool_names = normalized_query.select_metatags(:pool, :ordpool).map(&:value)
name = pool_names.first
return nil unless pool_names.size == 1
@pool ||= Pool.find_by_name(name)
end
def favgroup
favgroup_names = normalized_query.select_metatags(:favgroup, :ordfavgroup).map(&:value)
name = favgroup_names.first
return nil unless favgroup_names.size == 1
@favgroup ||= FavoriteGroup.visible(CurrentUser.user).find_by_name_or_id(name, CurrentUser.user)
end
def has_explicit?
posts.any? {|x| x.rating == "e"}
end
def hidden_posts
posts.reject(&:visible?)
end
def banned_posts
posts.select(&:banblocked?)
end
def censored_posts
posts.select { |p| p.levelblocked? && !p.banblocked? }
end
def safe_posts
posts.select { |p| p.safeblocked? && !p.levelblocked? && !p.banblocked? }
end
def per_page
(@per_page || query.find_metatag(:limit) || CurrentUser.user.per_page).to_i.clamp(0, MAX_PER_PAGE)
end
def is_random?
random || query.find_metatag(:order) == "random"
end
def get_post_count
if %w(json atom xml).include?(format.downcase)
# no need to get counts for formats that don't use a paginator
nil
else
normalized_query.fast_count
end
end
def get_random_posts
per_page.times.inject([]) do |all, x|
all << ::Post.user_tag_match(tag_string).random
end.compact.uniq
end
def posts
@posts ||= begin
@post_count = get_post_count
if is_random?
temp = get_random_posts
else
temp = normalized_query.build.paginate(page, count: post_count, search_count: !post_count.nil?, limit: per_page)
end
end
end
def hide_from_crawler?
return true if current_page > 1
return false if query.is_empty_search? || query.is_simple_tag? || query.is_metatag?(:order, :rank)
true
end
def current_page
[page.to_i, 1].max
end
def best_post
# be smarter about this in the future
posts.reject(&:is_deleted).select(&:visible?).max_by(&:fav_count)
end
def pending_bulk_update_requests
return BulkUpdateRequest.none unless tag.present?
@pending_bulk_update_requests ||= BulkUpdateRequest.pending.where_array_includes_any(:tags, tag.name)
end
def post_previews_html(template, show_cropped: true, **options)
html = ""
if none_shown(options)
return template.render("post_sets/blank")
end
posts.each do |post|
html << PostPresenter.preview(post, options.merge(:tags => tag_string))
html << "\n"
end
html.html_safe
end
def not_shown(post, options)
!options[:show_deleted] && post.is_deleted? && tag_string !~ /status:(?:all|any|deleted|banned)/
end
def none_shown(options)
posts.reject {|post| not_shown(post, options) }.empty?
end
concerning :TagListMethods do
def related_tags
if query.is_wildcard_search?
wildcard_tags
elsif query.is_metatag?(:search)
saved_search_tags
elsif query.is_empty_search? || query.is_metatag?(:order, :rank)
popular_tags
elsif query.is_single_term?
similar_tags
else
frequent_tags
end
end
def popular_tags
if PopularSearchService.enabled?
PopularSearchService.new(Date.today).tags
else
frequent_tags
end
end
def similar_tags
RelatedTagCalculator.cached_similar_tags_for_search(normalized_query(implicit: false), MAX_SIDEBAR_TAGS)
end
def frequent_tags
RelatedTagCalculator.frequent_tags_for_post_array(posts).take(MAX_SIDEBAR_TAGS)
end
def wildcard_tags
Tag.wildcard_matches(tag_string)
end
def saved_search_tags
["search:all"] + SavedSearch.labels_for(CurrentUser.user.id).map {|x| "search:#{x}"}
end
def tag_set_presenter
@tag_set_presenter ||= TagSetPresenter.new(related_tags.take(MAX_SIDEBAR_TAGS))
end
def tag_list_html(**options)
tag_set_presenter.tag_list_html(name_only: query.is_metatag?(:search), **options)
end
end
end
end