From b2347278327afa2eb427aa17c6bd55d5dd3fc04c Mon Sep 17 00:00:00 2001 From: evazion Date: Tue, 22 Nov 2022 21:54:22 -0600 Subject: [PATCH] tags: ensure aliased tag categories stay in sync. * When a tag's category is changed, also change the category of any aliases pointing to it. For example, if "ff7" is aliased to "final_fantasy_vii", and "final_fantasy_vii" is changed to a copyright tag, then change the empty "ff7" tag to be a copyright tag too. * Don't allow changing the category of an aliased tag. For example, if "ff7" is aliased to "final_fantasy_vii", then don't allow changing the "ff7" tag to be a non-copyright tag. This ensures that the categories of aliased tags stay in sync with that of their parent tags. This way aliased tags are colored correctly in wikis and other places. --- app/models/tag.rb | 17 ++++++++++++++++ .../fixes/131_fix_aliased_tag_categories.rb | 15 ++++++++++++++ test/unit/post_test.rb | 4 ++-- test/unit/tag_test.rb | 20 +++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100755 script/fixes/131_fix_aliased_tag_categories.rb diff --git a/app/models/tag.rb b/app/models/tag.rb index 207d77d73..d985a41cc 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -23,8 +23,10 @@ class Tag < ApplicationRecord validates :name, tag_name: true, uniqueness: true, on: :create validates :name, tag_name: true, on: :name validates :category, inclusion: { in: TagCategory.category_ids } + validate :validate_category, if: :category_changed? before_create :create_character_tag_for_cosplay_tag, if: :is_cosplay_tag? + after_save :update_tag_alias_categories, if: :saved_change_to_category? after_save :update_category_cache, if: :saved_change_to_category? after_save :update_category_post_counts, if: :saved_change_to_category? @@ -173,6 +175,21 @@ class Tag < ApplicationRecord def update_category_cache Cache.put("tc:#{Cache.hash(name)}", category, 3.hours) end + + # When a tag's category is changed, also change the categories of any aliases pointing to it. + def update_tag_alias_categories + consequent_aliases.each do |tag_alias| + if tag_alias.antecedent_tag.category != category + tag_alias.antecedent_tag.update!(category: category, updater: updater) + end + end + end + + def validate_category + if is_aliased? && category != aliased_tag.category + errors.add(:base, "Can't change the category of an aliased tag") + end + end end concerning :NameMethods do diff --git a/script/fixes/131_fix_aliased_tag_categories.rb b/script/fixes/131_fix_aliased_tag_categories.rb new file mode 100755 index 000000000..5485757a8 --- /dev/null +++ b/script/fixes/131_fix_aliased_tag_categories.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require_relative "base" + +with_confirmation do + fix = ENV.fetch("FIX", "false").truthy? + + aliases = TagAlias.active.joins(:antecedent_tag, :consequent_tag).where("tags.category != consequent_tags_tag_aliases.category") + aliases.find_each do |tag_alias| + tag_alias.antecedent_tag.assign_attributes(category: tag_alias.consequent_tag.category, updater: User.system) + + puts ({ id: tag_alias.id, from: tag_alias.antecedent_tag.name, to: tag_alias.consequent_tag.name, changes: tag_alias.antecedent_tag.changes }).to_json + tag_alias.antecedent_tag.save!(touch: false) if fix + end +end diff --git a/test/unit/post_test.rb b/test/unit/post_test.rb index 4a64a098c..cb681a514 100644 --- a/test/unit/post_test.rb +++ b/test/unit/post_test.rb @@ -587,13 +587,13 @@ class PostTest < ActiveSupport::TestCase assert_equal(Tag.categories.character, tag.last_version.category) end - should "change the category for an aliased tag" do + should "not change the category for an aliased tag" do create(:tag_alias, antecedent_name: "hoge", consequent_name: "moge") post = create(:post, tag_string: "char:hoge") assert_equal(["moge"], post.tag_array) assert_equal(Tag.categories.general, Tag.find_by_name("moge").category) - assert_equal(Tag.categories.character, Tag.find_by_name("hoge").category) + assert_equal(Tag.categories.general, Tag.find_by_name("hoge").category) end should "not raise an exception for an invalid tag name" do diff --git a/test/unit/tag_test.rb b/test/unit/tag_test.rb index 27b421c2c..1b8b5034f 100644 --- a/test/unit/tag_test.rb +++ b/test/unit/tag_test.rb @@ -175,6 +175,17 @@ class TagTest < ActiveSupport::TestCase assert_equal(0, tag.reload.category) end + should "not change category if the tag is aliased" do + t1 = create(:tag, name: "ff7", category: Tag.categories.copyright) + t2 = create(:tag, name: "final_fantasy_vii", category: Tag.categories.copyright) + ta = create(:tag_alias, antecedent_name: "ff7", consequent_name: "final_fantasy_vii") + + t1.reload.category = Tag.categories.character + + assert_equal(false, t1.valid?) + assert_equal(["Can't change the category of an aliased tag"], t1.errors[:base]) + end + should "update post tag counts when the category is changed" do post = FactoryBot.create(:post, tag_string: "test") assert_equal(1, post.tag_count_general) @@ -186,6 +197,15 @@ class TagTest < ActiveSupport::TestCase assert_equal(1, post.tag_count_character) end + should "update aliased tags when the tag's category is changed" do + t1 = create(:tag, name: "ff7", category: Tag.categories.general) + t2 = create(:tag, name: "final_fantasy_vii", category: Tag.categories.general) + ta = create(:tag_alias, antecedent_name: "ff7", consequent_name: "final_fantasy_vii") + t2.update!(category: Tag.categories.copyright, updater: User.system) + + assert_equal("Copyright", t1.reload.category_name) + end + should "be created when one doesn't exist" do assert_difference("Tag.count", 1) do tag = Tag.find_or_create_by_name("hoge")