From f0745de7f661a27b4d20d2ba317d1b31bf9a6c4b Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 23 Jul 2026 19:58:31 +0200 Subject: Force tags to be lowercase. A script unifies existing tags in both cases --- app/views/nodes/edit.html.erb | 2 +- config/initializers/acts_as_taggable_on.rb | 1 + lib/tasks/tags.rake | 37 ++++++++++++++++++++++++++++++ public/stylesheets/admin.css | 4 ++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 config/initializers/acts_as_taggable_on.rb create mode 100644 lib/tasks/tags.rake diff --git a/app/views/nodes/edit.html.erb b/app/views/nodes/edit.html.erb index fc1d18fa..55397d42 100644 --- a/app/views/nodes/edit.html.erb +++ b/app/views/nodes/edit.html.erb @@ -117,7 +117,7 @@
Tags
<%= text_field_tag :tag_list, @page.tag_list.join(', ') %> - Comma separated. + Comma separated, lowercase.
Publish at
diff --git a/config/initializers/acts_as_taggable_on.rb b/config/initializers/acts_as_taggable_on.rb new file mode 100644 index 00000000..08d8aa67 --- /dev/null +++ b/config/initializers/acts_as_taggable_on.rb @@ -0,0 +1 @@ +ActsAsTaggableOn.force_lowercase = true diff --git a/lib/tasks/tags.rake b/lib/tasks/tags.rake new file mode 100644 index 00000000..7521c2f9 --- /dev/null +++ b/lib/tasks/tags.rake @@ -0,0 +1,37 @@ +namespace :tags do + desc "Merge tags differing only in case into one lowercase tag and " \ + "lowercase every remaining name. Idempotent; safe to re-run." + task :merge_case_duplicates => :environment do + merged = renamed = 0 + + ActiveRecord::Base.transaction do + ActsAsTaggableOn::Tag.pluck(Arel.sql("DISTINCT LOWER(name)")).each do |lower| + group = ActsAsTaggableOn::Tag.where("LOWER(name) = ?", lower).order(:id).to_a + keeper = group.find { |t| t.name == lower } || group.first + + (group - [keeper]).each do |dup| + dup.taggings.find_each do |tagging| + duplicate = ActsAsTaggableOn::Tagging.where( + :tag_id => keeper.id, + :taggable_type => tagging.taggable_type, + :taggable_id => tagging.taggable_id, + :context => tagging.context, + :tagger_type => tagging.tagger_type, + :tagger_id => tagging.tagger_id).exists? + duplicate ? tagging.destroy : tagging.update!(:tag_id => keeper.id) + end + dup.reload.destroy! + merged += 1 + end + + if keeper.name != lower + keeper.update!(:name => lower) + renamed += 1 + end + ActsAsTaggableOn::Tag.reset_counters(keeper.id, :taggings) + end + end + + puts "Merged #{merged} duplicate tags, lowercased #{renamed} names" + end +end diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index 69f0fcf2..fe702b09 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css @@ -966,6 +966,10 @@ div#page_editor { padding: 5px; } +input#tag_list { + text-transform: lowercase; +} + @media(min-width:1016px) { input#menu_search_term, input#menu_item_node_id, -- cgit v1.3