Fix #4461: Improve posts/index page titles.
This commit is contained in:
@@ -123,13 +123,13 @@ module ApplicationHelper
|
||||
end
|
||||
end
|
||||
|
||||
def humanized_number(number)
|
||||
def humanized_number(number, million: "M", thousand: "k")
|
||||
if number >= 1_000_000
|
||||
format("%.1fM", number / 1_000_000.0)
|
||||
format("%.1f#{million}", number / 1_000_000.0)
|
||||
elsif number >= 10_000
|
||||
"#{number / 1_000}k"
|
||||
"#{number / 1_000}#{thousand}"
|
||||
elsif number >= 1_000
|
||||
format("%.1fk", number / 1_000.0)
|
||||
format("%.1f#{thousand}", number / 1_000.0)
|
||||
else
|
||||
number.to_s
|
||||
end
|
||||
|
||||
@@ -18,7 +18,7 @@ class PostQuery
|
||||
attr_reader :current_user
|
||||
private attr_reader :tag_limit, :safe_mode, :hide_deleted_posts, :builder
|
||||
|
||||
delegate :tag?, :metatag?, :wildcard?, :metatags, :wildcards, :tag_names, :to_infix, to: :ast
|
||||
delegate :tag?, :metatag?, :wildcard?, :metatags, :wildcards, :tag_names, :to_infix, :to_pretty_string, to: :ast
|
||||
alias_method :safe_mode?, :safe_mode
|
||||
alias_method :hide_deleted_posts?, :hide_deleted_posts
|
||||
alias_method :to_s, :to_infix
|
||||
|
||||
@@ -286,6 +286,30 @@ class PostQuery
|
||||
end
|
||||
end
|
||||
|
||||
# Display the search in "pretty" form, with capitalized tags.
|
||||
def to_pretty_string
|
||||
case self
|
||||
in [:all]
|
||||
""
|
||||
in [:none]
|
||||
"none"
|
||||
in [:wildcard, name]
|
||||
name
|
||||
in [:tag, name]
|
||||
name.tr("_", " ").gsub(/\b([a-z])/, &:capitalize)
|
||||
in [:metatag, name, value, quoted]
|
||||
"#{name}:#{quoted_value}"
|
||||
in :not, child
|
||||
child.term? ? "-#{child.to_pretty_string}" : "-(#{child.to_pretty_string})"
|
||||
in :opt, child
|
||||
child.term? ? "~#{child.to_pretty_string}" : "~(#{child.to_pretty_string})"
|
||||
in :and, *children
|
||||
children.map { _1.children.many? ? "(#{_1.to_pretty_string})" : _1.to_pretty_string }.to_sentence
|
||||
in :or, *children
|
||||
children.map { _1.children.many? ? "(#{_1.to_pretty_string})" : _1.to_pretty_string }.to_sentence(two_words_connector: " or ", last_word_connector: ", or ")
|
||||
end
|
||||
end
|
||||
|
||||
# Convert the AST to a series of nested arrays.
|
||||
def to_tree
|
||||
if term?
|
||||
@@ -445,6 +469,6 @@ class PostQuery
|
||||
end
|
||||
end
|
||||
|
||||
memoize :to_cnf, :simplify, :simplify_once, :rewrite_opts, :trim, :trim_once, :sort, :inquirer, :deconstruct, :inspect, :to_sexp, :to_infix, :to_tree, :nodes, :tags, :metatags, :tag_names, :parents
|
||||
memoize :to_cnf, :simplify, :simplify_once, :rewrite_opts, :trim, :trim_once, :sort, :inquirer, :deconstruct, :inspect, :to_sexp, :to_infix, :to_pretty_string, :to_tree, :nodes, :tags, :metatags, :tag_names, :parents
|
||||
end
|
||||
end
|
||||
|
||||
@@ -493,87 +493,6 @@ class PostQueryBuilder
|
||||
end
|
||||
|
||||
concerning :ParseMethods do
|
||||
# Parse the search into a list of search terms. A search term is a tag or a metatag.
|
||||
# @return [Array<OpenStruct>] a list of terms
|
||||
def scan_query
|
||||
terms = []
|
||||
query = query_string.to_s.gsub(/[[:space:]]/, " ")
|
||||
scanner = StringScanner.new(query)
|
||||
|
||||
until scanner.eos?
|
||||
scanner.skip(/ +/)
|
||||
|
||||
if scanner.scan(/(-)?(#{METATAGS.join("|")}):/io)
|
||||
operator = scanner.captures.first
|
||||
metatag = scanner.captures.second.downcase
|
||||
value, quoted = scan_string(scanner)
|
||||
|
||||
if metatag.in?(COUNT_METATAG_SYNONYMS)
|
||||
metatag = metatag.singularize + "_count"
|
||||
elsif metatag == "order"
|
||||
attribute, direction, _tail = value.to_s.downcase.partition(/_(asc|desc)\z/i)
|
||||
if attribute.in?(COUNT_METATAG_SYNONYMS)
|
||||
value = attribute.singularize + "_count" + direction
|
||||
end
|
||||
end
|
||||
|
||||
terms << OpenStruct.new(type: :metatag, name: metatag, value: value, negated: (operator == "-"), quoted: quoted)
|
||||
elsif scanner.scan(/([-~])?([^ ]+)/)
|
||||
operator = scanner.captures.first
|
||||
tag = scanner.captures.second
|
||||
terms << OpenStruct.new(type: :tag, name: tag.downcase, negated: (operator == "-"), optional: (operator == "~"), wildcard: tag.include?("*"))
|
||||
elsif scanner.scan(/[^ ]+/)
|
||||
terms << OpenStruct.new(type: :tag, name: scanner.matched.downcase)
|
||||
end
|
||||
end
|
||||
|
||||
terms
|
||||
end
|
||||
|
||||
# Parse a single-quoted, double-quoted, or unquoted string. Used for parsing metatag values.
|
||||
# @param scanner [StringScanner] the current parser state
|
||||
# @return [Array<(String, Boolean)>] the string and whether it was quoted
|
||||
def scan_string(scanner)
|
||||
if scanner.scan(/"((?:\\"|[^"])*)"/)
|
||||
value = scanner.captures.first.gsub(/\\(.)/) { $1 }
|
||||
quoted = true
|
||||
elsif scanner.scan(/'((?:\\'|[^'])*)'/)
|
||||
value = scanner.captures.first.gsub(/\\(.)/) { $1 }
|
||||
quoted = true
|
||||
else
|
||||
value = scanner.scan(/(\\ |[^ ])*/)
|
||||
value = value.gsub(/\\ /) { " " }
|
||||
quoted = false
|
||||
end
|
||||
|
||||
[value, quoted]
|
||||
end
|
||||
|
||||
# Split the search query into a list of strings, one per search term.
|
||||
# Roughly the same as splitting on spaces, but accounts for quoted strings.
|
||||
# @return [Array<String>] the list of terms
|
||||
def split_query
|
||||
terms.map do |term|
|
||||
type, name, value = term.type, term.name, term.value
|
||||
|
||||
str = ""
|
||||
str += "-" if term.negated
|
||||
str += "~" if term.optional
|
||||
|
||||
if type == :tag
|
||||
str += name
|
||||
elsif type == :metatag && (term.quoted || value.include?(" "))
|
||||
value = value.gsub(/\\/) { '\\\\' }
|
||||
value = value.gsub(/"/) { '\\"' }
|
||||
str += "#{name}:\"#{value}\""
|
||||
elsif type == :metatag
|
||||
str += "#{name}:#{value}"
|
||||
end
|
||||
|
||||
str
|
||||
end
|
||||
end
|
||||
|
||||
class_methods do
|
||||
# Parse a simple string value into a Ruby type.
|
||||
# @param string [String] the value to parse
|
||||
@@ -700,17 +619,4 @@ class PostQueryBuilder
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
concerning :UtilityMethods do
|
||||
def to_s
|
||||
split_query.join(" ")
|
||||
end
|
||||
|
||||
# The list of search terms. This includes regular tags and metatags.
|
||||
def terms
|
||||
@terms ||= scan_query
|
||||
end
|
||||
end
|
||||
|
||||
memoize :split_query
|
||||
end
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
# @see PostsController#index
|
||||
module PostSets
|
||||
class Post
|
||||
extend Memoist
|
||||
|
||||
MAX_PER_PAGE = 200
|
||||
MAX_SIDEBAR_TAGS = 25
|
||||
MAX_WILDCARD_TAGS = PostQueryBuilder::MAX_WILDCARD_TAGS
|
||||
@@ -25,8 +27,21 @@ module PostSets
|
||||
@show_votes = show_votes
|
||||
end
|
||||
|
||||
def humanized_tag_string
|
||||
query.split_query.map { |tag| tag.tr("_", " ").titleize }.to_sentence
|
||||
# The title of the page for the <title> tag.
|
||||
def page_title
|
||||
post_query.to_pretty_string
|
||||
end
|
||||
|
||||
# The description of the page for the <meta name="description"> tag.
|
||||
def meta_description
|
||||
if post_query.is_simple_tag?
|
||||
humanized_count = ApplicationController.helpers.humanized_number(post_count, million: " million", thousand: " thousand")
|
||||
humanized_count = "over #{humanized_count}" if post_count >= 1_000
|
||||
|
||||
"See #{humanized_count} #{page_title} images on #{Danbooru.config.app_name}. #{DText.excerpt(wiki_page&.body)}"
|
||||
else
|
||||
ApplicationController.helpers.site_description
|
||||
end
|
||||
end
|
||||
|
||||
def has_blank_wiki?
|
||||
@@ -170,5 +185,7 @@ module PostSets
|
||||
searches.take(MAX_SIDEBAR_TAGS)
|
||||
end
|
||||
end
|
||||
|
||||
memoize :page_title
|
||||
end
|
||||
end
|
||||
|
||||
@@ -228,10 +228,9 @@
|
||||
|
||||
<% atom_feed_tag "Posts", posts_url(format: :atom) %>
|
||||
<% else %>
|
||||
<% page_title("#{@post_set.humanized_tag_string} Art") %>
|
||||
<% meta_description("See over #{number_with_delimiter(@post_set.post_count)} #{@post_set.humanized_tag_string} images on #{Danbooru.config.app_name}. #{DText.excerpt(@post_set.wiki_page&.body)}") %>
|
||||
|
||||
<% atom_feed_tag "Posts: #{@post_set.tag_string}", posts_url(tags: @post_set.tag_string, format: :atom) %>
|
||||
<% page_title(@post_set.page_title) %>
|
||||
<% meta_description @post_set.meta_description %>
|
||||
<% atom_feed_tag "Posts: #{@post_set.page_title}", posts_url(tags: @post_set.tag_string, format: :atom) %>
|
||||
<% end %>
|
||||
|
||||
<% if params[:tags].blank? && @post_set.current_page == 1 %>
|
||||
|
||||
@@ -467,8 +467,8 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
context "in safe mode" do
|
||||
should "not include the rating:s tag in the page title" do
|
||||
get posts_path(tags: "1girl", safe_mode: true)
|
||||
assert_select "title", text: "1girl Art | Safebooru"
|
||||
get posts_path(tags: "fate/grand_order", safe_mode: true)
|
||||
assert_select "title", text: "Fate/Grand Order | Safebooru"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -13,13 +13,6 @@ class PostQueryBuilderTest < ActiveSupport::TestCase
|
||||
assert_equal(count, PostQuery.normalize(query, **query_options).with_implicit_metatags.fast_count(**fast_count_options))
|
||||
end
|
||||
|
||||
def assert_parse_equals(expected, query)
|
||||
assert_equal(expected, PostQueryBuilder.new(query).split_query)
|
||||
|
||||
# parsing, serializing, then parsing again should produce the same result.
|
||||
assert_equal(PostQuery.new(query).to_s, PostQuery.new(PostQuery.new(query).to_s).to_s)
|
||||
end
|
||||
|
||||
setup do
|
||||
CurrentUser.user = create(:user)
|
||||
CurrentUser.ip_addr = "127.0.0.1"
|
||||
@@ -1387,17 +1380,6 @@ class PostQueryBuilderTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
context "Parsing:" do
|
||||
should "split a query" do
|
||||
assert_equal(%w(aaa bbb), PostQueryBuilder.new("aaa bbb").split_query)
|
||||
end
|
||||
|
||||
should "not strip out valid characters when scanning" do
|
||||
assert_equal(%w(aaa bbb), PostQueryBuilder.new("aaa bbb").split_query)
|
||||
assert_equal(%w(favgroup:yondemasu_yo,_azazel-san. pool:ichigo_100%), PostQueryBuilder.new("favgroup:yondemasu_yo,_azazel-san. pool:ichigo_100%").split_query)
|
||||
end
|
||||
end
|
||||
|
||||
context "#fast_count" do
|
||||
setup do
|
||||
create(:tag, name: "grey_skirt", post_count: 100)
|
||||
|
||||
Reference in New Issue
Block a user