diff --git a/app/logical/d_text.rb b/app/logical/d_text.rb
index 26f451a47..e0e5ff10e 100644
--- a/app/logical/d_text.rb
+++ b/app/logical/d_text.rb
@@ -157,6 +157,52 @@ class DText
Set.new(parse_external_links(a)) != Set.new(parse_external_links(b))
end
+ # Rewrite wiki links to [[old_name]] with [[new_name]]. We attempt to match
+ # the capitalization of the old tag when rewriting it to the new tag, but if
+ # we can't determine how the new tag should be capitalized based on some
+ # simple heuristics, then we skip rewriting the tag.
+ def self.rewrite_wiki_links(dtext, old_name, new_name)
+ old_name = old_name.downcase.squeeze("_").tr("_", " ").strip
+ new_name = new_name.downcase.squeeze("_").tr("_", " ").strip
+
+ # Match `[[name]]` or `[[name|title]]`
+ dtext.gsub(/\[\[(.*?)(?:\|(.*?))?\]\]/) do |match|
+ name = $1
+ title = $2
+
+ # Skip this link if it isn't the tag we're trying to replace.
+ normalized_name = name.downcase.tr("_", " ").squeeze(" ").strip
+ next match if normalized_name != old_name
+
+ # Strip qualifiers, e.g. `atago (midsummer march) (azur lane)` => `atago`
+ unqualified_name = name.tr("_", " ").squeeze(" ").strip.gsub(/( \(.*\))+\z/, "")
+ has_qualifier = name.match?(/( \(.*\))+\z/)
+
+ # If old tag was lowercase, e.g. [[ink tank (Splatoon)]], then keep new tag in lowercase.
+ if unqualified_name == unqualified_name.downcase
+ final_name = new_name
+ # If old tag was capitalized, e.g. [[Colored pencil (medium)]], then capitialize new tag.
+ elsif unqualified_name == unqualified_name.downcase.capitalize
+ final_name = new_name.capitalize
+ # If old tag was in titlecase, e.g. [[Hatsune Miku (cosplay)]], then titlecase new tag.
+ elsif unqualified_name == unqualified_name.split.map(&:capitalize).join(" ")
+ final_name = new_name.split.map(&:capitalize).join(" ")
+ # If we can't determine how to capitalize the new tag, then keep the old tag.
+ # e.g. [[Suzumiya Haruhi no Yuuutsu]] -> [[The Melancholy of Haruhi Suzumiya]]
+ else
+ next match
+ end
+
+ if title.present?
+ "[[#{final_name}|#{title}]]"
+ elsif has_qualifier
+ "[[#{final_name}|]]"
+ else
+ "[[#{final_name}]]"
+ end
+ end
+ end
+
def self.strip_blocks(string, tag)
n = 0
stripped = ""
diff --git a/app/logical/tag_mover.rb b/app/logical/tag_mover.rb
index 768ad2e1d..6bbd2bd86 100644
--- a/app/logical/tag_mover.rb
+++ b/app/logical/tag_mover.rb
@@ -14,6 +14,7 @@ class TagMover
move_wiki!
move_saved_searches!
move_blacklists!
+ rewrite_wiki_links!
move_posts!
end
end
@@ -63,6 +64,10 @@ class TagMover
User.rewrite_blacklists!(old_tag.name, new_tag.name)
end
+ def rewrite_wiki_links!
+ WikiPage.rewrite_wiki_links!(old_tag.name, new_tag.name)
+ end
+
def merge_artists!
old_artist.lock!
new_artist.lock!
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index f3c9b5611..e10c1312f 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -222,6 +222,16 @@ class WikiPage < ApplicationRecord
TagAlias.to_aliased(titles & tags)
end
+ def self.rewrite_wiki_links!(old_name, new_name)
+ broken_wikis = WikiPage.linked_to(old_name)
+
+ broken_wikis.each do |wiki|
+ wiki.lock!
+ wiki.body = DText.rewrite_wiki_links(wiki.body, old_name, new_name)
+ wiki.save!
+ end
+ end
+
def to_param
if title =~ /\A\d+\z/
"~#{title}"
diff --git a/test/unit/d_text_test.rb b/test/unit/d_text_test.rb
index 0fc68062d..98bb9b415 100644
--- a/test/unit/d_text_test.rb
+++ b/test/unit/d_text_test.rb
@@ -5,6 +5,10 @@ class DTextTest < ActiveSupport::TestCase
assert_equal(expected, DText.strip_dtext(dtext))
end
+ def assert_rewrite_wiki_links(expected, dtext, old, new)
+ assert_equal(expected, DText.rewrite_wiki_links(dtext, old, new))
+ end
+
context "DText" do
context "#strip_dtext" do
should "strip dtext markup from the input" do
@@ -125,6 +129,31 @@ class DTextTest < ActiveSupport::TestCase
end
end
+ context "#rewrite_wiki_links" do
+ should "work" do
+ assert_rewrite_wiki_links("[[rabbit]]", "[[bunny]]", "bunny", "rabbit")
+ assert_rewrite_wiki_links("[[rabbit|bun]]", "[[bunny|bun]]", "bunny", "rabbit")
+
+ assert_rewrite_wiki_links("[[cat]] [[rabbit]]", "[[cat]] [[rabbit]]", "bunny", "rabbit")
+ assert_rewrite_wiki_links("I like [[cat]]s and [[bunny]]s", "I like [[cat]]s and [[rabbit]]s", "rabbit", "bunny")
+
+ assert_rewrite_wiki_links("[[miku hatsune (cosplay)|]]", "[[hatsune miku (cosplay)]]", "hatsune_miku_(cosplay)", "miku_hatsune_(cosplay)")
+ assert_rewrite_wiki_links("[[Miku hatsune (cosplay)|]]", "[[Hatsune miku (cosplay)]]", "hatsune_miku_(cosplay)", "miku_hatsune_(cosplay)")
+ assert_rewrite_wiki_links("[[Miku Hatsune (cosplay)|]]", "[[Hatsune Miku (cosplay)]]", "hatsune_miku_(cosplay)", "miku_hatsune_(cosplay)")
+ assert_rewrite_wiki_links("[[miku hatsune (cosplay)|miku]]", "[[hatsune miku (cosplay)|miku]]", "hatsune_miku_(cosplay)", "miku_hatsune_(cosplay)")
+
+ assert_rewrite_wiki_links("[[the legend of zelda]]", "[[zelda no densetsu]]", "zelda_no_densetsu", "the_legend_of_zelda")
+ assert_rewrite_wiki_links("[[The legend of zelda]]", "[[Zelda no densetsu]]", "zelda_no_densetsu", "the_legend_of_zelda")
+ assert_rewrite_wiki_links("[[The Legend Of Zelda]]", "[[Zelda No Densetsu]]", "zelda_no_densetsu", "the_legend_of_zelda")
+ assert_rewrite_wiki_links("[[the legend of zelda]]", "[[zelda_no_densetsu]]", "zelda_no_densetsu", "the_legend_of_zelda")
+ assert_rewrite_wiki_links("[[The legend of zelda]]", "[[Zelda_no_densetsu]]", "zelda_no_densetsu", "the_legend_of_zelda")
+ assert_rewrite_wiki_links("[[The Legend Of Zelda]]", "[[Zelda_No_Densetsu]]", "zelda_no_densetsu", "the_legend_of_zelda")
+
+ assert_rewrite_wiki_links("[[Zelda no Densetsu]]", "[[Zelda no Densetsu]]", "zelda_no_densetsu", "the_legend_of_zelda")
+ assert_rewrite_wiki_links("[[Zelda_no_Densetsu]]", "[[Zelda_no_Densetsu]]", "zelda_no_densetsu", "the_legend_of_zelda")
+ end
+ end
+
context "#from_html" do
should "convert basic html to dtext" do
assert_equal("[b]abc[/b] [i]def[/i] [u]ghi[/u]", DText.from_html("abc def ghi"))
diff --git a/test/unit/tag_alias_test.rb b/test/unit/tag_alias_test.rb
index bf6ae00bc..bd5af29d1 100644
--- a/test/unit/tag_alias_test.rb
+++ b/test/unit/tag_alias_test.rb
@@ -197,6 +197,17 @@ class TagAliasTest < ActiveSupport::TestCase
assert_equal(%w[111 333], @wiki2.other_names)
assert_equal("second", @wiki2.body)
end
+
+ should "rewrite links in other wikis to use the new tag" do
+ @wiki = create(:wiki_page, body: "foo [[aaa]] bar")
+ @ta = create(:tag_alias, antecedent_name: "aaa", consequent_name: "bbb")
+
+ @ta.approve!(approver: @admin)
+ perform_enqueued_jobs
+ assert_equal("active", @ta.reload.status)
+
+ assert_equal("foo [[bbb]] bar", @wiki.reload.body)
+ end
end
context "when the tags have artist entries" do