diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index c9bc60d5f..b43e6b7ad 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -33,9 +33,7 @@ module ApplicationHelper end def format_text(text, **options) - raw DTextRagel.parse(text, **options) - rescue DTextRagel::Error => e - raw "" + raw DText.format_text(text, **options) end def strip_dtext(text) diff --git a/app/javascript/src/styles/common/dtext.scss b/app/javascript/src/styles/common/dtext.scss index 15f0d2d24..d51cb8c7b 100644 --- a/app/javascript/src/styles/common/dtext.scss +++ b/app/javascript/src/styles/common/dtext.scss @@ -108,6 +108,10 @@ div.prose { padding: 0 0.2em 0 0.3em; vertical-align: 1px; } + + a.dtext-wiki-does-not-exist, a.dtext-tag-does-not-exist { + text-decoration: dotted underline; + } } // avoid empty gaps beneath dtext blocks in table rows. diff --git a/app/logical/d_text.rb b/app/logical/d_text.rb index ad9d31eae..f93699beb 100644 --- a/app/logical/d_text.rb +++ b/app/logical/d_text.rb @@ -4,6 +4,54 @@ require 'uri' class DText MENTION_REGEXP = /(?<=^| )@\S+/ + def self.format_text(text, data: nil, **options) + data = preprocess([text]) if data.nil? + html = DTextRagel.parse(text, **options) + html = postprocess(html, *data) + html + rescue DTextRagel::Error => e + "" + end + + def self.preprocess(dtext_messages) + names = dtext_messages.map { |message| parse_wiki_titles(message) }.flatten.uniq + wiki_pages = WikiPage.where(title: names) + tags = Tag.where(name: names) + + [wiki_pages, tags] + end + + def self.postprocess(html, wiki_pages, tags) + fragment = Nokogiri::HTML.fragment(html) + + fragment.css("a.dtext-wiki-link").each do |node| + name = node["href"][%r!\A/wiki_pages/show_or_new\?title=(.*)\z!i, 1] + name = CGI.unescape(name) + name = WikiPage.normalize_title(name) + wiki = wiki_pages.find { |wiki| wiki.title == name } + tag = tags.find { |tag| tag.name == name } + + if wiki.blank? + node["class"] += " dtext-wiki-does-not-exist" + node["title"] = "This wiki page does not exist" + end + + if WikiPage.is_meta_wiki?(name) + # skip (meta wikis aren't expected to have a tag) + elsif tag.blank? + node["class"] += " dtext-tag-does-not-exist" + node["title"] = "This wiki page does not have a tag" + elsif tag.post_count <= 0 + node["class"] += " dtext-tag-empty" + node["title"] = "This wiki page does not have a tag" + else + node["class"] += " tag-type-#{tag.category}" + end + end + + fragment.to_s + end + def self.quote(message, creator_name) stripped_body = DText.strip_blocks(message, "quote") "[quote]\n#{creator_name} said:\n\n#{stripped_body}\n[/quote]\n\n" diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 236bb24cf..8e3c50e7e 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -1,6 +1,8 @@ class WikiPage < ApplicationRecord class RevertError < Exception ; end + META_WIKIS = ["list_of_", "tag_group:", "pool_group:", "howto:", "about:", "help:", "template:"] + before_save :normalize_title before_save :normalize_other_names after_save :create_version @@ -155,6 +157,10 @@ class WikiPage < ApplicationRecord title.tr("_", " ") end + def self.is_meta_wiki?(title) + title.starts_with?(*META_WIKIS) + end + def wiki_page_changed? saved_change_to_title? || saved_change_to_body? || saved_change_to_is_locked? || saved_change_to_is_deleted? || saved_change_to_other_names? end diff --git a/app/views/comments/_index_by_comment.html.erb b/app/views/comments/_index_by_comment.html.erb index 03a321ed4..0ec786a7b 100644 --- a/app/views/comments/_index_by_comment.html.erb +++ b/app/views/comments/_index_by_comment.html.erb @@ -8,7 +8,7 @@ <%= link_to(image_tag(comment.post.preview_file_url), post_path(comment.post)) %> <% end %> - <%= render :partial => "comments/partials/show/comment", :collection => [comment] %> + <%= render partial: "comments/partials/show/comment", collection: [comment], locals: { dtext_data: DText.preprocess(@comments.map(&:body)) } %> <% end %> <% end %> <% end %> diff --git a/app/views/comments/index_for_post.js.erb b/app/views/comments/index_for_post.js.erb index 504ac10c1..0c0d584ff 100644 --- a/app/views/comments/index_for_post.js.erb +++ b/app/views/comments/index_for_post.js.erb @@ -1,5 +1,5 @@ $("#threshold-comments-notice-for-<%= @post.id %>").hide(); var current_comment_section = $("div.comments-for-post[data-post-id=<%= @post.id %>] div.list-of-comments"); -current_comment_section.html("<%= j(render(:partial => 'comments/partials/show/comment', :collection => @comments))%>"); +current_comment_section.html("<%= j(render(partial: 'comments/partials/show/comment', collection: @comments, locals: { dtext_data: DText.preprocess(@comments.map(&:body)) })) %>"); $(window).trigger("danbooru:index_for_post", [<%= @post.id %>]); diff --git a/app/views/comments/partials/index/_list.html.erb b/app/views/comments/partials/index/_list.html.erb index a7a712058..d8e816947 100644 --- a/app/views/comments/partials/index/_list.html.erb +++ b/app/views/comments/partials/index/_list.html.erb @@ -13,7 +13,7 @@
<% if comments.present? %> - <%= render partial: "comments/partials/show/comment", collection: comments %> + <%= render partial: "comments/partials/show/comment", collection: comments, locals: { dtext_data: DText.preprocess(comments.map(&:body)) } %> <% elsif post.last_commented_at.present? %>

There are no visible comments.

<% else %> diff --git a/app/views/comments/partials/show/_comment.html.erb b/app/views/comments/partials/show/_comment.html.erb index 3db1cc28a..b8571e163 100644 --- a/app/views/comments/partials/show/_comment.html.erb +++ b/app/views/comments/partials/show/_comment.html.erb @@ -22,7 +22,7 @@
- <%= format_text(comment.body) %> + <%= format_text(comment.body, data: dtext_data) %>
<%= render "application/update_notice", record: comment %> diff --git a/app/views/forum_posts/_forum_post.html.erb b/app/views/forum_posts/_forum_post.html.erb index da49b2c17..f7cc58ac0 100644 --- a/app/views/forum_posts/_forum_post.html.erb +++ b/app/views/forum_posts/_forum_post.html.erb @@ -11,7 +11,7 @@
- <%= format_text(parse_embedded_tag_request_text(forum_post.body)) %> + <%= format_text(parse_embedded_tag_request_text(forum_post.body), data: dtext_data) %>
<%= render "application/update_notice", record: forum_post %> diff --git a/app/views/forum_posts/_listing.html.erb b/app/views/forum_posts/_listing.html.erb index 93d9e3768..0612f2522 100644 --- a/app/views/forum_posts/_listing.html.erb +++ b/app/views/forum_posts/_listing.html.erb @@ -1,8 +1,9 @@ <%- # forum_post %> <%- # original_forum_post_id %> +<%- # dtext_data %>
<% forum_posts.each do |forum_post| %> - <%= render "forum_posts/forum_post", :forum_post => forum_post, :original_forum_post_id => original_forum_post_id %> + <%= render "forum_posts/forum_post", forum_post: forum_post, original_forum_post_id: original_forum_post_id, dtext_data: dtext_data %> <% end %>
diff --git a/app/views/forum_topics/show.html.erb b/app/views/forum_topics/show.html.erb index 699175cfc..c8d64d5dc 100644 --- a/app/views/forum_topics/show.html.erb +++ b/app/views/forum_topics/show.html.erb @@ -20,7 +20,7 @@
<% end %> - <%= render "forum_posts/listing", :forum_posts => @forum_posts, :original_forum_post_id => @forum_topic.original_post.id %> + <%= render "forum_posts/listing", forum_posts: @forum_posts, original_forum_post_id: @forum_topic.original_post.id, dtext_data: DText.preprocess(@forum_posts.map(&:body)) %> <% if CurrentUser.is_member? %> <% if CurrentUser.is_moderator? || !@forum_topic.is_locked? %> diff --git a/test/unit/d_text_test.rb b/test/unit/d_text_test.rb index 81c2e61f4..053abc2db 100644 --- a/test/unit/d_text_test.rb +++ b/test/unit/d_text_test.rb @@ -38,5 +38,23 @@ class DTextTest < ActiveSupport::TestCase assert_strip_dtext("one two three\nfour\n\nfive six", "one [b]two[/b] three\nfour\n\nfive six") end end + + context "#format_text" do + should "add tag types to wiki links" do + create(:tag, name: "bkub", category: Tag.categories.artist, post_count: 42) + assert_match(/tag-type-#{Tag.categories.artist}/, DText.format_text("[[bkub]]")) + end + + should "mark links to nonexistent tags or wikis" do + create(:tag, name: "no_wiki", post_count: 42) + create(:tag, name: "empty_tag", post_count: 0) + + assert_match(/dtext-wiki-does-not-exist/, DText.format_text("[[no wiki]]")) + assert_match(/dtext-tag-does-not-exist/, DText.format_text("[[no tag]]")) + assert_match(/dtext-tag-empty/, DText.format_text("[[empty tag]]")) + + refute_match(/dtext-tag-does-not-exist/, DText.format_text("[[help:nothing]]")) + end + end end end