diff --git a/app/logical/storage_manager.rb b/app/logical/storage_manager.rb index 2937d6b3c..121887a0e 100644 --- a/app/logical/storage_manager.rb +++ b/app/logical/storage_manager.rb @@ -115,7 +115,7 @@ class StorageManager def seo_tags(post) return "" if !tagged_filenames - tags = post.humanized_essential_tag_string.gsub(/[^a-z0-9]+/, "_").gsub(/(?:^_+)|(?:_+$)/, "").gsub(/_{2,}/, "_") + tags = post.presenter.humanized_essential_tag_string.gsub(/[^a-z0-9]+/, "_").gsub(/(?:^_+)|(?:_+$)/, "").gsub(/_{2,}/, "_") "__#{tags}__" end end diff --git a/app/models/post.rb b/app/models/post.rb index 2877031e9..59976bbd7 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -36,7 +36,6 @@ class Post < ApplicationRecord after_save :create_version after_save :update_parent_on_save after_save :apply_post_metatags - after_save :expire_essential_tag_string_cache after_commit :delete_files, :on => :destroy after_commit :remove_iqdb_async, :on => :destroy after_commit :update_iqdb_async, :on => :create @@ -936,36 +935,6 @@ class Post < ApplicationRecord end end - def expire_essential_tag_string_cache - Cache.delete("hets-#{id}") - end - - def humanized_essential_tag_string - @humanized_essential_tag_string ||= Cache.get("hets-#{id}", 1.hour.to_i) do - string = [] - - TagCategory.humanized_list.each do |category| - typetags = typed_tags(category) - TagCategory.humanized_mapping[category]["exclusion"] - if TagCategory.humanized_mapping[category]["slice"] > 0 - typetags = typetags.slice(0,TagCategory.humanized_mapping[category]["slice"]) + (typetags.length > TagCategory.humanized_mapping[category]["slice"] ? ["others"] : []) - end - if TagCategory.humanized_mapping[category]["regexmap"] != // - typetags = typetags.map do |tag| - tag.match(TagCategory.humanized_mapping[category]["regexmap"])[1] - end - end - if typetags.any? - if category != "copyright" || typed_tags("character").any? - string << TagCategory.humanized_mapping[category]["formatstr"] % typetags.to_sentence - else - string << typetags.to_sentence - end - end - end - string.empty? ? "##{id}" : string.join(" ").tr("_", " ") - end - end - TagCategory.categories.each do |category| define_method("tag_string_#{category}") do typed_tags(category).join(" ") diff --git a/app/models/tag.rb b/app/models/tag.rb index 2d3ad47c3..135bd3c46 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -56,10 +56,6 @@ class Tag < ApplicationRecord extend ActiveSupport::Concern module ClassMethods - def counts_for(tag_names) - select_all_sql("SELECT name, post_count FROM tags WHERE name IN (?)", tag_names) - end - def highest_post_count Cache.get("highest-post-count", 4.hours) do select("post_count").order("post_count DESC").first.post_count diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb index f90d44789..f58fa782d 100644 --- a/app/presenters/post_presenter.rb +++ b/app/presenters/post_presenter.rb @@ -1,6 +1,6 @@ class PostPresenter < Presenter attr_reader :pool, :next_post_in_pool - delegate :tag_list_html, :split_tag_list_html, :inline_tag_list_html, to: :tag_set_presenter + delegate :tag_list_html, :split_tag_list_html, :split_tag_list_text, :inline_tag_list_html, to: :tag_set_presenter def self.preview(post, options = {}) if post.nil? @@ -144,29 +144,13 @@ class PostPresenter < Presenter end def humanized_essential_tag_string - @post.humanized_essential_tag_string + @humanized_essential_tag_string ||= tag_set_presenter.humanized_essential_tag_string(default: "##{@post.id}") end def filename_for_download "#{humanized_essential_tag_string} - #{@post.md5}.#{@post.file_ext}" end - def categorized_tag_groups - string = [] - - TagCategory.categorized_list.each do |category| - if @post.typed_tags(category).any? - string << @post.typed_tags(category).join(" ") - end - end - - string - end - - def categorized_tag_string - categorized_tag_groups.join(" \n") - end - def safe_mode_message(template) html = ["This image is unavailable on safe mode (#{Danbooru.config.app_name}). Go to "] html << template.link_to("Danbooru", "https://danbooru.donmai.us") # XXX don't hardcode. diff --git a/app/presenters/post_set_presenters/favorite.rb b/app/presenters/post_set_presenters/favorite.rb index 0d726b276..f08e454a1 100644 --- a/app/presenters/post_set_presenters/favorite.rb +++ b/app/presenters/post_set_presenters/favorite.rb @@ -1,7 +1,8 @@ module PostSetPresenters class Favorite < Base attr_accessor :post_set, :tag_set_presenter - delegate :favorites, :to => :post_set + delegate :favorites, :posts, :to => :post_set + delegate :tag_list_html, to: :tag_set_presenter def initialize(post_set) @post_set = post_set @@ -11,13 +12,5 @@ module PostSetPresenters ).map {|x| x[0]} ) end - - def tag_list_html(template) - tag_set_presenter.tag_list_html(template) - end - - def posts - @posts ||= post_set.posts - end end end diff --git a/app/presenters/post_set_presenters/pool.rb b/app/presenters/post_set_presenters/pool.rb index 4bba321ed..798ec4d6b 100644 --- a/app/presenters/post_set_presenters/pool.rb +++ b/app/presenters/post_set_presenters/pool.rb @@ -12,10 +12,6 @@ module PostSetPresenters ) end - def tag_list_html(template) - tag_set_presenter.tag_list_html(template) - end - def post_previews_html(template) html = "" diff --git a/app/presenters/post_set_presenters/post.rb b/app/presenters/post_set_presenters/post.rb index fbb7b473d..c0d62b13b 100644 --- a/app/presenters/post_set_presenters/post.rb +++ b/app/presenters/post_set_presenters/post.rb @@ -67,12 +67,8 @@ module PostSetPresenters SavedSearch.labels_for(CurrentUser.user.id).map {|x| "search:#{x}"} end - def tag_list_html(template, options = {}) - if post_set.is_saved_search? - options[:name_only] = true - end - - tag_set_presenter.tag_list_html(template, options) + def tag_list_html(**options) + tag_set_presenter.tag_list_html(name_only: post_set.is_saved_search?, **options) end end end diff --git a/app/presenters/tag_set_presenter.rb b/app/presenters/tag_set_presenter.rb index 307964484..42432d778 100644 --- a/app/presenters/tag_set_presenter.rb +++ b/app/presenters/tag_set_presenter.rb @@ -5,16 +5,23 @@ =end class TagSetPresenter < Presenter - def initialize(tags) - @tags = tags + extend Memoist + attr_reader :tag_names + + # @param [Array] a list of tags to present. Tags will be presented in + # the order given. The list should not contain duplicates. The list may + # contain tags that do not exist in the tags table, such as metatags. + def initialize(tag_names) + @tag_names = tag_names end - def tag_list_html(template, options = {}) + def tag_list_html(current_query: "", show_extra_links: false, name_only: false) html = "" - if @tags.present? + + if ordered_tags.present? html << '" end @@ -22,16 +29,17 @@ class TagSetPresenter < Presenter html.html_safe end - def split_tag_list_html(template, category_list: TagCategory.split_header_list, headers: true, **options) + def split_tag_list_html(headers: true, category_list: TagCategory.split_header_list, current_query: "", show_extra_links: false, name_only: false, humanize_tags: true) html = "" category_list.each do |category| - typetags = typed_tags(category) + typetags = tags_for_category(category) + if typetags.any? html << TagCategory.header_mapping[category] if headers html << %{" end @@ -41,69 +49,105 @@ class TagSetPresenter < Presenter end # compact (horizontal) list, as seen in the /comments index. - def inline_tag_list_html(template, classes: "inline-tag-list", **options) - html = split_tag_list_html(template, category_list: TagCategory.categorized_list, headers: false, name_only: true, humanize_tags: true, **options) - template.tag.span(html, class: classes) + def inline_tag_list_html(humanize_tags: true) + html = split_tag_list_html(category_list: TagCategory.categorized_list, headers: false, show_extra_links: false, name_only: true, humanize_tags: humanize_tags) + %{#{html}}.html_safe + end + + # the list of tags inside the tag box in the post edit form. + def split_tag_list_text(category_list: TagCategory.categorized_list) + category_list.map do |category| + tags_for_category(category).map(&:name).join(" ") + end.join(" \n") + end + + def humanized_essential_tag_string(category_list: TagCategory.humanized_list, default: "") + strings = category_list.map do |category| + mapping = TagCategory.humanized_mapping[category] + max_tags = mapping["slice"] + regexmap = mapping["regexmap"] + formatstr = mapping["formatstr"] + excluded_tags = mapping["exclusion"] + + type_tags = tags_for_category(category).map(&:name) - excluded_tags + next if type_tags.empty? + + if max_tags > 0 && type_tags.length > max_tags + type_tags = type_tags.take(max_tags) + ["others"] + end + + if regexmap != // + type_tags = type_tags.map { |tag| tag.match(regexmap)[1] } + end + + if category == "copyright" && tags_for_category("character").blank? + type_tags.to_sentence + else + formatstr % type_tags.to_sentence + end + end + + strings = strings.compact.join(" ").tr("_", " ") + strings.blank? ? default : strings end private - def typed_tags(name) - @typed_tags ||= {} - @typed_tags[name] ||= begin - @tags.select do |tag| - categories[tag] == TagCategory.mapping[name] - end + def tags + Tag.where(name: tag_names).select(:name, :post_count, :category) + end + + def tags_by_category + ordered_tags.group_by(&:category) + end + + def tags_for_category(category_name) + category = TagCategory.mapping[category_name.downcase] + tags_by_category[category] || [] + end + + def ordered_tags + names_to_tags = tags.map { |tag| [tag.name, tag] }.to_h + + tag_names.map do |name| + names_to_tags[name] || Tag.new(name: name).freeze end end - def categories - @categories ||= Tag.categories_for(@tags) - end + def build_list_item(tag, name_only: false, humanize_tags: true, show_extra_links: false, current_query: "") + name = tag.name + count = tag.post_count + category = tag.category - def counts - @counts ||= Tag.counts_for(@tags).inject({}) do |hash, x| - hash[x["name"]] = x["post_count"] - hash - end - end - - def build_list_item(tag, template, name_only: false, humanize_tags: true, show_extra_links: CurrentUser.is_gold?) - html = "" - html << %{
  • } - current_query = template.params[:tags] || "" + html = %{
  • } unless name_only - if categories[tag] == Tag.categories.artist - html << %{? } + if category == Tag.categories.artist + html << %{? } else - html << %{? } + html << %{? } end if show_extra_links && current_query.present? - html << %{+ } - html << %{ } + html << %{+ } + html << %{ } end end - humanized_tag = humanize_tags ? tag.tr("_", " ") : tag - if categories[tag] == Tag.categories.artist - itemprop = 'itemprop="author"' - else - itemprop = nil - end - html << %{#{h(humanized_tag)} } + humanized_tag = humanize_tags ? name.tr("_", " ") : name + itemprop = 'itemprop="author"' if category == Tag.categories.artist + html << %{#{h(humanized_tag)} } - unless name_only - if counts[tag].to_i >= 10_000 - post_count = "#{counts[tag].to_i / 1_000}k" - elsif counts[tag].to_i >= 1_000 - post_count = "%.1fk" % (counts[tag].to_f / 1_000) + unless name_only || tag.new_record? + if count >= 10_000 + post_count = "#{count / 1_000}k" + elsif count >= 1_000 + post_count = "%.1fk" % (count / 1_000.0) else - post_count = counts[tag].to_s + post_count = count end - is_underused_tag = counts[tag].to_i <= 1 && categories[tag] == Tag.categories.general + is_underused_tag = count <= 1 && category == Tag.categories.general klass = "post-count#{is_underused_tag ? " low-post-count" : ""}" title = "New general tag detected. Check the spelling or populate it now." @@ -113,4 +157,6 @@ class TagSetPresenter < Presenter html << "
  • " html end + + memoize :tags, :tags_by_category, :ordered_tags, :humanized_essential_tag_string end diff --git a/app/presenters/upload_presenter.rb b/app/presenters/upload_presenter.rb index 44a1cce2e..d132530eb 100644 --- a/app/presenters/upload_presenter.rb +++ b/app/presenters/upload_presenter.rb @@ -1,5 +1,12 @@ class UploadPresenter < Presenter + attr_reader :upload + delegate :inline_tag_list_html, to: :tag_set_presenter + def initialize(upload) @upload = upload end + + def tag_set_presenter + @tag_set_presenter ||= TagSetPresenter.new(upload.tag_string.split) + end end diff --git a/app/views/comments/index.atom.builder b/app/views/comments/index.atom.builder index 19e1c8b75..14508448b 100644 --- a/app/views/comments/index.atom.builder +++ b/app/views/comments/index.atom.builder @@ -9,7 +9,7 @@ atom_feed(root_url: comments_url(host: Danbooru.config.hostname)) do |feed| @comments.each do |comment| feed.entry(comment, published: comment.created_at, updated: comment.updated_at) do |entry| - entry.title("@#{comment.creator_name} on post ##{comment.post_id} (#{comment.post.humanized_essential_tag_string})") + entry.title("@#{comment.creator_name} on post ##{comment.post_id} (#{comment.post.presenter.humanized_essential_tag_string})") entry.content(<<-EOS.strip_heredoc, type: "html") diff --git a/app/views/comments/partials/index/_header.html.erb b/app/views/comments/partials/index/_header.html.erb index 83c48d12a..735bc541c 100644 --- a/app/views/comments/partials/index/_header.html.erb +++ b/app/views/comments/partials/index/_header.html.erb @@ -26,7 +26,7 @@
    Tags - <%= post.presenter.inline_tag_list_html(self) %> + <%= post.presenter.inline_tag_list_html %>
    diff --git a/app/views/favorites/index.html.erb b/app/views/favorites/index.html.erb index 630e0fcbe..ea14cc280 100644 --- a/app/views/favorites/index.html.erb +++ b/app/views/favorites/index.html.erb @@ -9,7 +9,7 @@

    Tags

    - <%= @favorite_set.presenter.tag_list_html(self) %> + <%= @favorite_set.presenter.tag_list_html %>
    diff --git a/app/views/posts/index.html.erb b/app/views/posts/index.html.erb index bae084e0c..a7a2ca72a 100644 --- a/app/views/posts/index.html.erb +++ b/app/views/posts/index.html.erb @@ -9,7 +9,7 @@

    Tags

    - <%= @post_set.presenter.tag_list_html(self) %> + <%= @post_set.presenter.tag_list_html(current_query: params[:tags], show_extra_links: CurrentUser.user.is_gold?) %>
    <%= render "posts/partials/index/options" %> diff --git a/app/views/posts/partials/show/_edit.html.erb b/app/views/posts/partials/show/_edit.html.erb index 2b5ddc82a..c838e2ecd 100644 --- a/app/views/posts/partials/show/_edit.html.erb +++ b/app/views/posts/partials/show/_edit.html.erb @@ -78,7 +78,7 @@
    <%= f.label :tag_string, "Tags" %> - <%= f.text_area :tag_string, :size => "50x5", :value => post.presenter.categorized_tag_string + " ", :data => { :autocomplete => "tag-edit" } %> + <%= f.text_area :tag_string, :size => "50x5", :value => post.presenter.split_tag_list_text + " ", :data => { :autocomplete => "tag-edit" } %>
    diff --git a/app/views/posts/show.html+tooltip.erb b/app/views/posts/show.html+tooltip.erb index d9d896c33..a4bf9f04c 100644 --- a/app/views/posts/show.html+tooltip.erb +++ b/app/views/posts/show.html+tooltip.erb @@ -46,5 +46,5 @@ <% end %>
    - <%= @post.presenter.inline_tag_list_html(self, humanize_tags: false) %> + <%= @post.presenter.inline_tag_list_html(humanize_tags: false) %> diff --git a/app/views/posts/show.html.erb b/app/views/posts/show.html.erb index dddda0107..4e9e6a440 100644 --- a/app/views/posts/show.html.erb +++ b/app/views/posts/show.html.erb @@ -6,7 +6,7 @@ <%= render "posts/partials/index/blacklist" %>
    - <%= @post.presenter.split_tag_list_html(self) %> + <%= @post.presenter.split_tag_list_html(current_query: params[:tags], show_extra_links: CurrentUser.user.is_gold?) %>
    diff --git a/app/views/uploads/index.html.erb b/app/views/uploads/index.html.erb index dc526624b..70dbe995d 100644 --- a/app/views/uploads/index.html.erb +++ b/app/views/uploads/index.html.erb @@ -57,7 +57,7 @@ Tags - <%= TagSetPresenter.new(upload.tag_string.split).inline_tag_list_html(self) %> + <%= upload.presenter.inline_tag_list_html %>