diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-08-03 17:12:34 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-08-03 17:12:34 +0200 |
| commit | 84657fa9d633a7d3e20c10645589061ecebeee3f (patch) | |
| tree | f4bda793d90193db5002d0f2848dc441e9c65c11 | |
| parent | a74e40be88cd1331155c967656e393618e88cd27 (diff) | |
Render dates, booleans and counts through the locale files
| -rw-r--r-- | app/helpers/datetime_helper.rb | 9 | ||||
| -rw-r--r-- | app/helpers/events_helper.rb | 5 | ||||
| -rw-r--r-- | app/models/event.rb | 3 | ||||
| -rw-r--r-- | app/views/events/index.html.erb | 14 | ||||
| -rw-r--r-- | app/views/events/show.html.erb | 4 | ||||
| -rw-r--r-- | app/views/events/without_node.html.erb | 8 | ||||
| -rw-r--r-- | app/views/nodes/_node_list.html.erb | 2 | ||||
| -rw-r--r-- | app/views/nodes/show.html.erb | 4 | ||||
| -rw-r--r-- | app/views/nodes/sitemap.html.erb | 2 | ||||
| -rw-r--r-- | app/views/revisions/index.html.erb | 2 | ||||
| -rw-r--r-- | app/views/revisions/show.html.erb | 4 | ||||
| -rw-r--r-- | app/views/users/show.html.erb | 2 | ||||
| -rw-r--r-- | config/locales/de.yml | 8 | ||||
| -rw-r--r-- | config/locales/en.yml | 8 | ||||
| -rw-r--r-- | test/controllers/nodes_controller_test.rb | 2 |
15 files changed, 53 insertions, 24 deletions
diff --git a/app/helpers/datetime_helper.rb b/app/helpers/datetime_helper.rb index 8497b1c0..ea6fd5c2 100644 --- a/app/helpers/datetime_helper.rb +++ b/app/helpers/datetime_helper.rb | |||
| @@ -50,4 +50,13 @@ module DatetimeHelper | |||
| 50 | locale == :de ? "vor #{count} #{word}" : "#{count} #{word} ago" | 50 | locale == :de ? "vor #{count} #{word}" : "#{count} #{word} ago" |
| 51 | end | 51 | end |
| 52 | 52 | ||
| 53 | def admin_datetime(time) | ||
| 54 | return "" if time.blank? | ||
| 55 | I18n.l(time, :format => :ccc) | ||
| 56 | end | ||
| 57 | |||
| 58 | def admin_date(time) | ||
| 59 | return "" if time.blank? | ||
| 60 | I18n.l(time, :format => :ccc_date) | ||
| 61 | end | ||
| 53 | end | 62 | end |
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 5e84f534..1e2e91dc 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb | |||
| @@ -10,4 +10,9 @@ module EventsHelper | |||
| 10 | return "" if rrule.blank? | 10 | return "" if rrule.blank? |
| 11 | raw(rrule.split(';', -1).map { |part| CGI.escapeHTML(part) }.join(';<wbr>')) | 11 | raw(rrule.split(';', -1).map { |part| CGI.escapeHTML(part) }.join(';<wbr>')) |
| 12 | end | 12 | end |
| 13 | |||
| 14 | def external_url_link(url) | ||
| 15 | return nil if url.blank? | ||
| 16 | url.match?(%r{\Ahttps?://}i) ? link_to(url, url) : h(url) | ||
| 17 | end | ||
| 13 | end | 18 | end |
diff --git a/app/models/event.rb b/app/models/event.rb index 7726f9bf..792ab448 100644 --- a/app/models/event.rb +++ b/app/models/event.rb | |||
| @@ -6,6 +6,9 @@ class Event < ApplicationRecord | |||
| 6 | acts_as_taggable_on :tags | 6 | acts_as_taggable_on :tags |
| 7 | 7 | ||
| 8 | validates :title, presence: true, unless: -> { node_id.present? } | 8 | validates :title, presence: true, unless: -> { node_id.present? } |
| 9 | validates :url, :format => { :with => %r{\Ahttps?://}i, | ||
| 10 | :allow_blank => true, | ||
| 11 | :message => :must_be_http } | ||
| 9 | 12 | ||
| 10 | after_save :generate_occurrences | 13 | after_save :generate_occurrences |
| 11 | 14 | ||
diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index 138a02ec..7dfb854c 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb | |||
| @@ -24,16 +24,12 @@ | |||
| 24 | <% @events.each do |event| %> | 24 | <% @events.each do |event| %> |
| 25 | <tr> | 25 | <tr> |
| 26 | <td><%= link_to event.display_title, event %></td> | 26 | <td><%= link_to event.display_title, event %></td> |
| 27 | <td><%=h event.start_time %></td> | 27 | <td><%= admin_datetime(event.start_time) %></td> |
| 28 | <td><%=h event.end_time %></td> | 28 | <td><%= admin_datetime(event.end_time) %></td> |
| 29 | <td class="rrule_cell"><span class="rrule_text"><%= rrule_with_break_opportunities(event.rrule) %></span></td> | 29 | <td class="rrule_cell"><span class="rrule_text"><%= rrule_with_break_opportunities(event.rrule) %></span></td> |
| 30 | <td><%=h event.allday %> → <%= event.allday ? t("admin.common.yes") : t("admin.common.no") %></td> | 30 | <td><%= t("admin.common.#{event.allday ? "yes" : "no"}") %></td> |
| 31 | <td class="url_cell"> | 31 | <td class="url_cell"><span class="truncate"><%= external_url_link(event.url) %></span></td> |
| 32 | <% if event.url.present? %> | 32 | <td><%= event.node ? link_to(event.node.unique_name, node_path(event.node)) : "" %></td> |
| 33 | <span class="truncate"><%= link_to event.url, event.url %></span> | ||
| 34 | <% end %> | ||
| 35 | </td> | ||
| 36 | <td><%= event.node ? link_to(event.node_id, node_path(event.node)) : '' %></td> | ||
| 37 | <td><%= link_to t(".edit_link"), edit_event_path(event) %></td> | 33 | <td><%= link_to t(".edit_link"), edit_event_path(event) %></td> |
| 38 | </tr> | 34 | </tr> |
| 39 | <% end %> | 35 | <% end %> |
diff --git a/app/views/events/show.html.erb b/app/views/events/show.html.erb index 98e972c6..d77f770e 100644 --- a/app/views/events/show.html.erb +++ b/app/views/events/show.html.erb | |||
| @@ -24,10 +24,10 @@ | |||
| 24 | </div> | 24 | </div> |
| 25 | 25 | ||
| 26 | <div class="layout_row_label"><%= Event.human_attribute_name(:start_time) %></div> | 26 | <div class="layout_row_label"><%= Event.human_attribute_name(:start_time) %></div> |
| 27 | <div class="layout_row_content"><%=h @event.start_time %></div> | 27 | <div class="layout_row_content"><%= admin_datetime(@event.start_time) %></div> |
| 28 | 28 | ||
| 29 | <div class="layout_row_label"><%= Event.human_attribute_name(:end_time) %></div> | 29 | <div class="layout_row_label"><%= Event.human_attribute_name(:end_time) %></div> |
| 30 | <div class="layout_row_content"><%=h @event.end_time %></div> | 30 | <div class="layout_row_content"><%= admin_datetime(@event.end_time) %></div> |
| 31 | 31 | ||
| 32 | <div class="layout_row_label"><%= Event.human_attribute_name(:rrule) %></div> | 32 | <div class="layout_row_label"><%= Event.human_attribute_name(:rrule) %></div> |
| 33 | <div class="layout_row_content"><%=h @event.rrule %></div> | 33 | <div class="layout_row_content"><%=h @event.rrule %></div> |
diff --git a/app/views/events/without_node.html.erb b/app/views/events/without_node.html.erb index c610b560..e59eedc7 100644 --- a/app/views/events/without_node.html.erb +++ b/app/views/events/without_node.html.erb | |||
| @@ -20,11 +20,11 @@ | |||
| 20 | <% @events.each do |event| %> | 20 | <% @events.each do |event| %> |
| 21 | <tr> | 21 | <tr> |
| 22 | <td><%= link_to event.display_title, event %></td> | 22 | <td><%= link_to event.display_title, event %></td> |
| 23 | <td><%=h event.start_time %></td> | 23 | <td><%= admin_datetime(event.start_time) %></td> |
| 24 | <td><%=h event.end_time %></td> | 24 | <td><%= admin_datetime(event.end_time) %></td> |
| 25 | <td><%=h event.allday %> → <%= event.allday ? t("admin.common.yes") : t("admin.common.no") %></td> | 25 | <td><%= t("admin.common.#{event.allday ? "yes" : "no"}") %></td> |
| 26 | <td><%=h event.url %></td> | 26 | <td><%=h event.url %></td> |
| 27 | <td><%= link_to t("events.index.edit_link"), edit_event_path(event) %></td> | 27 | <td class="url_cell"><span class="truncate"><%= external_url_link(event.url) %></span></td> |
| 28 | </tr> | 28 | </tr> |
| 29 | <% end %> | 29 | <% end %> |
| 30 | </table> | 30 | </table> |
diff --git a/app/views/nodes/_node_list.html.erb b/app/views/nodes/_node_list.html.erb index 1e5d0ed9..0dc3c5a0 100644 --- a/app/views/nodes/_node_list.html.erb +++ b/app/views/nodes/_node_list.html.erb | |||
| @@ -34,7 +34,7 @@ | |||
| 34 | <%= link_to 'revisions', node_revisions_path(node) %> | 34 | <%= link_to 'revisions', node_revisions_path(node) %> |
| 35 | </td> | 35 | </td> |
| 36 | <td><%= node.lock_owner.login if node.lock_owner %></td> | 36 | <td><%= node.lock_owner.login if node.lock_owner %></td> |
| 37 | <td><%= node.draft ? node.draft.revision : (node.head ? node.head.revision : "EMPTY") %></td> | 37 | <td><%= node.draft ? node.draft.revision : (node.head ? node.head.revision : t(".no_revision")) %></td> |
| 38 | </tr> | 38 | </tr> |
| 39 | <% end %> | 39 | <% end %> |
| 40 | </table> | 40 | </table> |
diff --git a/app/views/nodes/show.html.erb b/app/views/nodes/show.html.erb index a6167c9e..a94e001c 100644 --- a/app/views/nodes/show.html.erb +++ b/app/views/nodes/show.html.erb | |||
| @@ -195,12 +195,12 @@ | |||
| 195 | <div class="info_group_items"> | 195 | <div class="info_group_items"> |
| 196 | <div class="info_item"> | 196 | <div class="info_item"> |
| 197 | <span class="info_label"><%= t(".last_updated") %></span> | 197 | <span class="info_label"><%= t(".last_updated") %></span> |
| 198 | <%= @page.updated_at %> | 198 | <%= admin_datetime(@page.updated_at) %> |
| 199 | </div> | 199 | </div> |
| 200 | <% if @page.published_at.present? %> | 200 | <% if @page.published_at.present? %> |
| 201 | <div class="info_item"> | 201 | <div class="info_item"> |
| 202 | <span class="info_label"><%= @page.public? ? t(".published_at") : t(".will_publish_at") %></span> | 202 | <span class="info_label"><%= @page.public? ? t(".published_at") : t(".will_publish_at") %></span> |
| 203 | <%= @page.published_at %> | 203 | <%= admin_datetime(@page.published_at) %> |
| 204 | </div> | 204 | </div> |
| 205 | <% end %> | 205 | <% end %> |
| 206 | </div> | 206 | </div> |
diff --git a/app/views/nodes/sitemap.html.erb b/app/views/nodes/sitemap.html.erb index f749a803..ea64b356 100644 --- a/app/views/nodes/sitemap.html.erb +++ b/app/views/nodes/sitemap.html.erb | |||
| @@ -23,7 +23,7 @@ | |||
| 23 | <% if next_level && next_level > level %> | 23 | <% if next_level && next_level > level %> |
| 24 | <details<%= ' open' if sitemap_node_open?(node) %>> | 24 | <details<%= ' open' if sitemap_node_open?(node) %>> |
| 25 | <summary> | 25 | <summary> |
| 26 | <%= pluralize(@sitemap_descendant_counts[node.id], 'descendant', 'descendants') %> | 26 | <%= t(".descendants", :count => @sitemap_descendant_counts[node.id]) %> |
| 27 | </summary> | 27 | </summary> |
| 28 | <% open_details.push(level) %> | 28 | <% open_details.push(level) %> |
| 29 | <% end %> | 29 | <% end %> |
diff --git a/app/views/revisions/index.html.erb b/app/views/revisions/index.html.erb index d118f96a..3fb33b90 100644 --- a/app/views/revisions/index.html.erb +++ b/app/views/revisions/index.html.erb | |||
| @@ -52,7 +52,7 @@ | |||
| 52 | <td class="revision"><%= page.revision %></td> | 52 | <td class="revision"><%= page.revision %></td> |
| 53 | <td class="title"><%= page.translations.find_by(:locale => @translation_locale)&.title || "—" %></td> | 53 | <td class="title"><%= page.translations.find_by(:locale => @translation_locale)&.title || "—" %></td> |
| 54 | <td class="user"><%= page.editor.try(:login) %></td> | 54 | <td class="user"><%= page.editor.try(:login) %></td> |
| 55 | <td class="date"><%= page.updated_at %></td> | 55 | <td class="date"><%= admin_datetime(page.updated_at) %></td> |
| 56 | <td><%= link_to t(".show_link"), node_revision_path(@node, page, :translation_locale => @translation_locale) %></td> | 56 | <td><%= link_to t(".show_link"), node_revision_path(@node, page, :translation_locale => @translation_locale) %></td> |
| 57 | <td> | 57 | <td> |
| 58 | <%= button_to t(".restore_link"), restore_node_revision_path(@node, page), | 58 | <%= button_to t(".restore_link"), restore_node_revision_path(@node, page), |
diff --git a/app/views/revisions/show.html.erb b/app/views/revisions/show.html.erb index addcfa1a..1456a051 100644 --- a/app/views/revisions/show.html.erb +++ b/app/views/revisions/show.html.erb | |||
| @@ -22,10 +22,10 @@ | |||
| 22 | <div class="layout_row_content"><%= @page.editor.try(:login) %></div> | 22 | <div class="layout_row_content"><%= @page.editor.try(:login) %></div> |
| 23 | 23 | ||
| 24 | <div class="layout_row_label"><%= t("nodes.show.last_updated") %></div> | 24 | <div class="layout_row_label"><%= t("nodes.show.last_updated") %></div> |
| 25 | <div class="layout_row_content"><%= @page.updated_at %></div> | 25 | <div class="layout_row_content"><%= admin_datetime(@page.updated_at) %></div> |
| 26 | 26 | ||
| 27 | <div class="layout_row_label"><%= t("nodes.show.published_at") %></div> | 27 | <div class="layout_row_label"><%= t("nodes.show.published_at") %></div> |
| 28 | <div class="layout_row_content"><%= @page.published_at %></div> | 28 | <div class="layout_row_content"><%= admin_datetime(@page.published_at) %></div> |
| 29 | 29 | ||
| 30 | <div class="layout_row_label"><%= Page.human_attribute_name(:title) %></div> | 30 | <div class="layout_row_label"><%= Page.human_attribute_name(:title) %></div> |
| 31 | <div class="layout_row_content"><%= translation&.title %></div> | 31 | <div class="layout_row_content"><%= translation&.title %></div> |
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index fc6df4e3..2685a15b 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb | |||
| @@ -28,7 +28,7 @@ | |||
| 28 | <div class="layout_row_label"><%= t("users.labels.last_login") %></div> | 28 | <div class="layout_row_label"><%= t("users.labels.last_login") %></div> |
| 29 | <div class="layout_row_content"> | 29 | <div class="layout_row_content"> |
| 30 | <% if @user.last_login_at %> | 30 | <% if @user.last_login_at %> |
| 31 | <%= I18n.l(@user.last_login_at, :format => :ccc_date) %> | 31 | <%= admin_date(@user.last_login_at) %> |
| 32 | <% else %> | 32 | <% else %> |
| 33 | <%= t("users.never_signed_in") %> | 33 | <%= t("users.never_signed_in") %> |
| 34 | <% end %> | 34 | <% end %> |
diff --git a/config/locales/de.yml b/config/locales/de.yml index b3319e61..9428c4be 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml | |||
| @@ -161,6 +161,10 @@ de: | |||
| 161 | not_permitted: "Dieses Asset ist an geschützte Seiten angehängt; nur die Redaktion darf es löschen" | 161 | not_permitted: "Dieses Asset ist an geschützte Seiten angehängt; nur die Redaktion darf es löschen" |
| 162 | upload: | 162 | upload: |
| 163 | unreadable_image: "konnte nicht als Bild gelesen werden. Möglicherweise ist sie beschädigt oder hat ein anderes Format, als der Name vermuten lässt." | 163 | unreadable_image: "konnte nicht als Bild gelesen werden. Möglicherweise ist sie beschädigt oder hat ein anderes Format, als der Name vermuten lässt." |
| 164 | event: | ||
| 165 | attributes: | ||
| 166 | url: | ||
| 167 | must_be_http: "must start with http:// or https://" | ||
| 164 | 168 | ||
| 165 | tags: | 169 | tags: |
| 166 | index: | 170 | index: |
| @@ -540,11 +544,15 @@ de: | |||
| 540 | title: "Nodes mit Tag: %{tag}" | 544 | title: "Nodes mit Tag: %{tag}" |
| 541 | sitemap: | 545 | sitemap: |
| 542 | title: "Sitemap" | 546 | title: "Sitemap" |
| 547 | descendants: | ||
| 548 | one: "1 Unterseite" | ||
| 549 | other: "%{count} Unterseiten" | ||
| 543 | chapters: | 550 | chapters: |
| 544 | title: "Dezentrale" | 551 | title: "Dezentrale" |
| 545 | filter: "Filter" | 552 | filter: "Filter" |
| 546 | node_list: | 553 | node_list: |
| 547 | search_placeholder: "Titel, Abstract, Text durchsuchen…" | 554 | search_placeholder: "Titel, Abstract, Text durchsuchen…" |
| 555 | no_revision: "keine" | ||
| 548 | trashed: | 556 | trashed: |
| 549 | title: "Papierkorb" | 557 | title: "Papierkorb" |
| 550 | empty: "Der Papierkorb ist leer." | 558 | empty: "Der Papierkorb ist leer." |
diff --git a/config/locales/en.yml b/config/locales/en.yml index ccca0ad0..dcf4275d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml | |||
| @@ -107,6 +107,10 @@ en: | |||
| 107 | not_permitted: "Only Redaktion members may change assets related to nodes in this section" | 107 | not_permitted: "Only Redaktion members may change assets related to nodes in this section" |
| 108 | upload: | 108 | upload: |
| 109 | unreadable_image: "could not be read as an image. It may be damaged, or not the format its name suggests." | 109 | unreadable_image: "could not be read as an image. It may be damaged, or not the format its name suggests." |
| 110 | event: | ||
| 111 | attributes: | ||
| 112 | url: | ||
| 113 | must_be_http: "must start with http:// or https://" | ||
| 110 | 114 | ||
| 111 | tags: | 115 | tags: |
| 112 | index: | 116 | index: |
| @@ -487,11 +491,15 @@ en: | |||
| 487 | title: "Nodes tagged: %{tag}" | 491 | title: "Nodes tagged: %{tag}" |
| 488 | sitemap: | 492 | sitemap: |
| 489 | title: "Sitemap" | 493 | title: "Sitemap" |
| 494 | descendants: | ||
| 495 | one: "1 descendant" | ||
| 496 | other: "%{count} descendants" | ||
| 490 | chapters: | 497 | chapters: |
| 491 | title: "Chapters" | 498 | title: "Chapters" |
| 492 | filter: "Filter" | 499 | filter: "Filter" |
| 493 | node_list: | 500 | node_list: |
| 494 | search_placeholder: "Search title, abstract, body…" | 501 | search_placeholder: "Search title, abstract, body…" |
| 502 | no_revision: "none" | ||
| 495 | trashed: | 503 | trashed: |
| 496 | title: "Trash" | 504 | title: "Trash" |
| 497 | empty: "The Trash is empty." | 505 | empty: "The Trash is empty." |
diff --git a/test/controllers/nodes_controller_test.rb b/test/controllers/nodes_controller_test.rb index f15be067..27c28cfe 100644 --- a/test/controllers/nodes_controller_test.rb +++ b/test/controllers/nodes_controller_test.rb | |||
| @@ -671,7 +671,7 @@ class NodesControllerTest < ActionController::TestCase | |||
| 671 | erfas_details = erfas_node_div.next_element | 671 | erfas_details = erfas_node_div.next_element |
| 672 | 672 | ||
| 673 | assert_equal 'details', erfas_details.name | 673 | assert_equal 'details', erfas_details.name |
| 674 | assert_match "2 descendants", erfas_details.at_css('summary').text | 674 | assert_match I18n.t("nodes.sitemap.descendants", :count => 2), @response.body |
| 675 | end | 675 | end |
| 676 | 676 | ||
| 677 | test "sitemap shows Show and Create Child, not Revisions" do | 677 | test "sitemap shows Show and Create Child, not Revisions" do |
