diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-07-23 19:58:31 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-07-23 19:58:31 +0200 |
| commit | f0745de7f661a27b4d20d2ba317d1b31bf9a6c4b (patch) | |
| tree | 8b652038153b4231e6016996016a187bdab1a864 | |
| parent | 7ebb5ce8ef276e6d23d818c216be6ef2974369cc (diff) | |
Force tags to be lowercase. A script unifies existing tags in both cases
| -rw-r--r-- | app/views/nodes/edit.html.erb | 2 | ||||
| -rw-r--r-- | config/initializers/acts_as_taggable_on.rb | 1 | ||||
| -rw-r--r-- | lib/tasks/tags.rake | 37 | ||||
| -rw-r--r-- | public/stylesheets/admin.css | 4 |
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 @@ | |||
| 1 | namespace :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 | ||
| 37 | 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 { | |||
| 966 | padding: 5px; | 966 | padding: 5px; |
| 967 | } | 967 | } |
| 968 | 968 | ||
| 969 | input#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, |
