summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-07-23 19:58:31 +0200
committererdgeist <erdgeist@erdgeist.org>2026-07-23 19:58:31 +0200
commitf0745de7f661a27b4d20d2ba317d1b31bf9a6c4b (patch)
tree8b652038153b4231e6016996016a187bdab1a864
parent7ebb5ce8ef276e6d23d818c216be6ef2974369cc (diff)
Force tags to be lowercase. A script unifies existing tags in both cases
-rw-r--r--app/views/nodes/edit.html.erb2
-rw-r--r--config/initializers/acts_as_taggable_on.rb1
-rw-r--r--lib/tasks/tags.rake37
-rw-r--r--public/stylesheets/admin.css4
4 files changed, 43 insertions, 1 deletions
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 @@
117 <div class="node_description">Tags</div> 117 <div class="node_description">Tags</div>
118 <div class="node_content"> 118 <div class="node_content">
119 <%= text_field_tag :tag_list, @page.tag_list.join(', ') %> 119 <%= text_field_tag :tag_list, @page.tag_list.join(', ') %>
120 <span class="field_hint">Comma separated.</span> 120 <span class="field_hint">Comma separated, lowercase.</span>
121 </div> 121 </div>
122 122
123 <div class="node_description">Publish at</div> 123 <div class="node_description">Publish at</div>
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 @@
1namespace :tags do
2 desc "Merge tags differing only in case into one lowercase tag and " \
3 "lowercase every remaining name. Idempotent; safe to re-run."
4 task :merge_case_duplicates => :environment do
5 merged = renamed = 0
6
7 ActiveRecord::Base.transaction do
8 ActsAsTaggableOn::Tag.pluck(Arel.sql("DISTINCT LOWER(name)")).each do |lower|
9 group = ActsAsTaggableOn::Tag.where("LOWER(name) = ?", lower).order(:id).to_a
10 keeper = group.find { |t| t.name == lower } || group.first
11
12 (group - [keeper]).each do |dup|
13 dup.taggings.find_each do |tagging|
14 duplicate = ActsAsTaggableOn::Tagging.where(
15 :tag_id => keeper.id,
16 :taggable_type => tagging.taggable_type,
17 :taggable_id => tagging.taggable_id,
18 :context => tagging.context,
19 :tagger_type => tagging.tagger_type,
20 :tagger_id => tagging.tagger_id).exists?
21 duplicate ? tagging.destroy : tagging.update!(:tag_id => keeper.id)
22 end
23 dup.reload.destroy!
24 merged += 1
25 end
26
27 if keeper.name != lower
28 keeper.update!(:name => lower)
29 renamed += 1
30 end
31 ActsAsTaggableOn::Tag.reset_counters(keeper.id, :taggings)
32 end
33 end
34
35 puts "Merged #{merged} duplicate tags, lowercased #{renamed} names"
36 end
37end
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 {
966 padding: 5px; 966 padding: 5px;
967} 967}
968 968
969input#tag_list {
970 text-transform: lowercase;
971}
972
969@media(min-width:1016px) { 973@media(min-width:1016px) {
970 input#menu_search_term, 974 input#menu_search_term,
971 input#menu_item_node_id, 975 input#menu_item_node_id,