From 4dd49b1eebb0a99d3aee66b7eca539c87a9c6332 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Sun, 28 Jun 2026 04:43:28 +0200 Subject: Phase 2: chapter nodes, aggregate partial, fixes - _chapter.html.erb: new partial for erfa/chaostreff aggregated lists; renders title, location, external_url, sanitized body - content_helper: fix aggregate attr regex to allow hyphens in values (erfa-detail tag was silently dropped); add debug logging (remove) - page.rb: suppress libxml stderr noise in rewrite_links_in_body - db/seeds/chapters.rb: one-shot seed script for erfa and chaostreff chapter nodes under parent nodes 548/549; creates bilingual pages, external_url, primary events with RRULEs where known Note: run Node.rebuild!(false) after execution to fix lft/rgt values --- app/helpers/content_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/helpers/content_helper.rb') diff --git a/app/helpers/content_helper.rb b/app/helpers/content_helper.rb index 21cc579..53bf5b2 100644 --- a/app/helpers/content_helper.rb +++ b/app/helpers/content_helper.rb @@ -80,7 +80,7 @@ module ContentHelper begin if content =~ /\[aggregate([^\]]*)\]/ tag = $~.to_s - matched_data = $1.scan(/\w+\="[a-zA-Z\s\/_\d,.=]*"/) + matched_data = $1.scan(/\w+\="[a-zA-Z\s\/_\d,.=-]*"/) matched_data.each do |data| splitted_data = data.split("=", 2) -- cgit v1.3 From 175b408948e601d3db568768a309eed8f2edb307 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 2 Jul 2026 15:13:52 +0200 Subject: Add 'open today' sidebar widget and open days in chapter lists - open_erfas_today helper: samples 3 random open-day-tagged occurrences of the current day; widget hidden entirely on days without open chapters; heading shows current weekday - content/_open_erfas_today partial rendered in right column above tags and featured articles - _chapter partial: open-day schedules (event_schedule_text) shown inline in aggregated chapter lists with 'Offene Tage' label - ccc.css: open_erfas_today added to existing right-column widget selector groups (headings, lists, links) --- app/helpers/content_helper.rb | 16 ++++++++++++++++ app/views/content/_open_erfas_today.html.erb | 11 +++++++++++ app/views/custom/partials/_chapter.html.erb | 9 +++++++++ app/views/layouts/application.html.erb | 1 + config/locales/de.yml | 2 ++ config/locales/en.yml | 2 ++ public/stylesheets/ccc.css | 10 +++++----- 7 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 app/views/content/_open_erfas_today.html.erb (limited to 'app/helpers/content_helper.rb') diff --git a/app/helpers/content_helper.rb b/app/helpers/content_helper.rb index 53bf5b2..bf7287f 100644 --- a/app/helpers/content_helper.rb +++ b/app/helpers/content_helper.rb @@ -23,6 +23,22 @@ module ContentHelper ) end + def open_erfas_today + occurrences = Occurrence + .find_in_range(Time.now.beginning_of_day, Time.now.end_of_day) + .joins(event: :tags) + .where(tags: { name: 'open-day' }) + .reject { |o| o.node.nil? || o.node.head.nil? } + .sample(3) + + return if occurrences.empty? + + render( + :partial => 'content/open_erfas_today', + :locals => { :occurrences => occurrences } + ) + end + def tags render :partial => 'content/tags' end diff --git a/app/views/content/_open_erfas_today.html.erb b/app/views/content/_open_erfas_today.html.erb new file mode 100644 index 0000000..3448af3 --- /dev/null +++ b/app/views/content/_open_erfas_today.html.erb @@ -0,0 +1,11 @@ +
+

<%= t(:open_today) %>

+
    + <% occurrences.each do |occurrence| %> +
  • + <%= link_to_path occurrence.node.head.title, occurrence.node.unique_name %> + <%= occurrence.start_time.strftime("%H:%M") %> +
  • + <% end %> +
+
diff --git a/app/views/custom/partials/_chapter.html.erb b/app/views/custom/partials/_chapter.html.erb index a5b8662..805559b 100644 --- a/app/views/custom/partials/_chapter.html.erb +++ b/app/views/custom/partials/_chapter.html.erb @@ -6,5 +6,14 @@ <% if page.node.external_url.present? %>
<%= link_to page.node.external_url, page.node.external_url, target: '_blank', rel: 'noopener' %>
<% end %> + <% open_days = page.node.events.tagged_with('open-day').order(:start_time) %> + <% if open_days.any? %> +
+ <%= t(:open_days_label) %>: + <% open_days.each do |event| %> + <%= event_schedule_text(event) %> + <% end %> +
+ <% end %>

<%= sanitize page.body %>

diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index edab5fc..57d12e6 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -72,6 +72,7 @@
+ <%= open_erfas_today %> <%= tags %> <%= featured_articles %>
diff --git a/config/locales/de.yml b/config/locales/de.yml index 9719a1c..e8ae4e1 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -11,7 +11,9 @@ de: event_schedule_unrecognized: "Termin-Regel nicht automatisch lesbar" event_schedule_none: "Kein Termin" open_days: "Offene Tage" + open_days_label: "Offene Tage" upcoming_events: "Weitere Termine" + open_today: "Heute dabei" date: formats: default: "%d.%m.%Y" diff --git a/config/locales/en.yml b/config/locales/en.yml index aa477f0..980a6b8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -12,7 +12,9 @@ en: event_schedule_unrecognized: "Schedule not automatically readable" event_schedule_none: "No schedule" open_days: "Open days" + open_days_label: "Open days" upcoming_events: "Upcoming events" + open_today: "Open today" time: formats: diff --git a/public/stylesheets/ccc.css b/public/stylesheets/ccc.css index 30eb8dd..422b7c0 100644 --- a/public/stylesheets/ccc.css +++ b/public/stylesheets/ccc.css @@ -216,7 +216,7 @@ div#frontpage_calendar { } } -div#frontpage_calendar h2, div#tags h2, div#featured_articles h2, div.main_navigation h2 { +div#frontpage_calendar h2, div#tags h2, div#featured_articles h2, div#open_erfas_today h2, div.main_navigation h2 { border-top: 2px solid; border-bottom: 2px solid; font-size: 1.1em; @@ -248,7 +248,7 @@ div#frontpage_calendar h2 { } @media(min-width:1016px) { - div#frontpage_calendar h2, div#tags h2, div#featured_articles h2 { + div#frontpage_calendar h2, div#tags h2, div#featured_articles h2, div#open_erfas_today h2 { font-size: 1rem; } @@ -260,7 +260,7 @@ div#frontpage_calendar h2 { } -div#frontpage_calendar ul, div#tags ul, div#featured_articles ul { +div#frontpage_calendar ul, div#tags ul, div#featured_articles ul, div#open_erfas_today ul { padding: 0px; font-size: 1rem; line-height: 1.5em; @@ -278,11 +278,11 @@ div#frontpage_calendar li { margin-bottom: 20px; } -div#frontpage_calendar li, div#tags li, div#featured_articles li { +div#frontpage_calendar li, div#tags li, div#featured_articles li, div#open_erfas_today li { list-style-type: none; } -div#frontpage_calendar li a, div#tags li a, div#featured_articles li a { +div#frontpage_calendar li a, div#tags li a, div#featured_articles li a, div#open_erfas_today li a { text-decoration: none; color: color-mix(in srgb, CanvasText, #808080 25%); } -- cgit v1.3 From 287569c9bbfdadae767254792cd2b53d0515d928 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Fri, 3 Jul 2026 03:22:02 +0200 Subject: Add weekday abbreviation for open-today widget RruleHumanizer gains WEEKDAY_NAMES_ABBR (Mo/Di/Mi/...) alongside the existing WEEKDAY_NAMES and WEEKDAY_NAMES_ADVERBIAL hashes, plus a self.wday_abbr(time, locale) utility mapping a Time's wday to its RFC5545 code and looking up the abbreviation - keeping RFC5545 vocabulary in one place rather than teaching ContentHelper about day codes directly. ContentHelper#weekday_abbr is a thin wrapper passing I18n.locale through. The partial now renders "Fr 19:00" instead of just "19:00", joined with a non-breaking space so the pair can't split across a line wrap. Takes an explicit Time rather than reading Date.today, matching this file's existing style of passing state in rather than reading it ambiently - and staying correct if the currently-unused calendar/_front_page_calendar partial (spanning six weeks, not just today) is ever revived and reuses this helper. --- app/helpers/content_helper.rb | 4 ++++ app/models/concerns/rrule_humanizer.rb | 9 +++++++++ app/views/content/_open_erfas_today.html.erb | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'app/helpers/content_helper.rb') diff --git a/app/helpers/content_helper.rb b/app/helpers/content_helper.rb index bf7287f..6043089 100644 --- a/app/helpers/content_helper.rb +++ b/app/helpers/content_helper.rb @@ -39,6 +39,10 @@ module ContentHelper ) end + def weekday_abbr(time) + RruleHumanizer.wday_abbr(time, I18n.locale) + end + def tags render :partial => 'content/tags' end diff --git a/app/models/concerns/rrule_humanizer.rb b/app/models/concerns/rrule_humanizer.rb index 6cee711..8231de8 100644 --- a/app/models/concerns/rrule_humanizer.rb +++ b/app/models/concerns/rrule_humanizer.rb @@ -10,6 +10,10 @@ module RruleHumanizer de: { "MO"=>"montags","TU"=>"dienstags","WE"=>"mittwochs","TH"=>"donnerstags","FR"=>"freitags","SA"=>"samstags","SU"=>"sonntags" } }.freeze + WEEKDAY_NAMES_ABBR = { + de: { "MO"=>"Mo","TU"=>"Di","WE"=>"Mi","TH"=>"Do","FR"=>"Fr","SA"=>"Sa","SU"=>"So" } + }.freeze + ORDINAL_NAMES = { de: { 1=>"ersten", 2=>"zweiten", 3=>"dritten", 4=>"vierten", -1=>"letzten", -2=>"vorletzten" }, en: { 1=>"first", 2=>"second", 3=>"third", 4=>"fourth", -1=>"last", -2=>"second-to-last" } @@ -79,4 +83,9 @@ module RruleHumanizer base end + + def self.wday_abbr(time, locale) + code = %w[SU MO TU WE TH FR SA][time.wday] + (WEEKDAY_NAMES_ABBR[locale.to_sym] || WEEKDAY_NAMES_ABBR[:de])[code] + end end diff --git a/app/views/content/_open_erfas_today.html.erb b/app/views/content/_open_erfas_today.html.erb index 3448af3..468bedc 100644 --- a/app/views/content/_open_erfas_today.html.erb +++ b/app/views/content/_open_erfas_today.html.erb @@ -4,7 +4,7 @@ <% occurrences.each do |occurrence| %>
  • <%= link_to_path occurrence.node.head.title, occurrence.node.unique_name %> - <%= occurrence.start_time.strftime("%H:%M") %> + <%= weekday_abbr(occurrence.start_time) %> <%= occurrence.start_time.strftime("%H:%M") %>
  • <% end %> -- cgit v1.3 From 527376039c527eb8f559c5e6da76429bc3f3ee4f Mon Sep 17 00:00:00 2001 From: erdgeist Date: Mon, 6 Jul 2026 19:40:48 +0200 Subject: Add tree-position scoping to Page.aggregate New :children option ("direct" or "all") on the [aggregate] shortcode, composable with the existing :tags filter - both apply as independent successive .where clauses, ANDing together automatically, no special casing needed. content_helper.rb passes the current @page.node through as :node whenever :children is requested, since Page.aggregate has no way to resolve "which node" from the shortcode's bare option strings on its own. Guarded so nothing changes for any existing [aggregate] shortcode that never uses children= - :node stays nil, the new branch never fires. "all" resolves via node.descendants.pluck(:id) rather than embedding the descendants relation directly as a subquery. Not proven strictly necessary - extensive debugging this session initially pointed at a subquery/table-alias collision, but every actual failure traced instead to accumulated test-node debris left in the dev DB by earlier interrupted runs, confirmed by re-running against a cleaned database. Kept anyway as a one-round-trip-cheaper, more defensive default regardless. --- app/helpers/content_helper.rb | 1 + app/models/page.rb | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'app/helpers/content_helper.rb') diff --git a/app/helpers/content_helper.rb b/app/helpers/content_helper.rb index 6043089..6bcd437 100644 --- a/app/helpers/content_helper.rb +++ b/app/helpers/content_helper.rb @@ -108,6 +108,7 @@ module ContentHelper end options[:partial] = select_partial(options[:partial]) + options[:node] = @page.node if options[:children].present? sanitize(content.sub(tag, render_collection(options)), :attributes => cccms_attributes) else diff --git a/app/models/page.rb b/app/models/page.rb index 4d1e94a..6fff7d6 100644 --- a/app/models/page.rb +++ b/app/models/page.rb @@ -63,6 +63,12 @@ class Page < ApplicationRecord end end + if options[:node] && options[:children] == "direct" + scope = scope.where(nodes: { parent_id: options[:node].id }) + elsif options[:node] && options[:children] == "all" + scope = scope.where(nodes: { id: options[:node].descendants.pluck(:id) }) + end + direction = %w[ASC DESC].include?(options[:order_direction]&.upcase) ? options[:order_direction].upcase : "ASC" if options[:order_by] == "title" -- cgit v1.3 From 644744183602c68508abb02dbb9f1f321a63f88f Mon Sep 17 00:00:00 2001 From: erdgeist Date: Fri, 10 Jul 2026 04:08:55 +0200 Subject: Fix docstring for the aggregate shortcode --- app/helpers/content_helper.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'app/helpers/content_helper.rb') diff --git a/app/helpers/content_helper.rb b/app/helpers/content_helper.rb index 6bcd437..5810966 100644 --- a/app/helpers/content_helper.rb +++ b/app/helpers/content_helper.rb @@ -87,11 +87,14 @@ module ContentHelper # Syntax of the [aggregate ] short code: # # [aggregate - # flags="update, pressemitteilung" + # children="all" | children="direct" # optional, at least one of children + # tags="update, pressemitteilung" # or tags is required # limit="20" # order_by="published_at" # order_direction="DESC" # ] + + def aggregate? content options = {} -- cgit v1.3