From 51629c5c42270a346885057a441095c964101cc1 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Tue, 30 Jun 2026 03:55:42 +0200 Subject: Fix events CRUD for standalone events and add events to admin menu - event_params now permits title, description, is_primary - event_information helper lists all node.events, not just the first - Occurrence.generate handles nil node (standalone events) - Page.aggregate order_by title uses correlated subquery to avoid GROUP BY conflict with tag-filter path; order_direction whitelisted to ASC/DESC to prevent SQL injection - Events link added to admin menu bar - events/index shows title, is_primary; drops latitude/longitude columns --- app/views/events/index.html.erb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'app/views/events') diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index 19b21ce..064fa86 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -2,27 +2,27 @@ + + - - <% @events.each do |event| %> + + - - -- cgit v1.3 From b6416e86c9c58b8e886c14de55b01aeb863b8676 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 2 Jul 2026 00:12:02 +0200 Subject: Clean up events views: return_to, subnav, remove custom_rrule - events_controller: wire return_to through show and new actions; create respects return_to with fallback to node/event path; new pre-populates node_id and tag_list from params - events/edit: remove custom_rrule checkbox; node link removed from subnav (use show for node navigation); destroy button added - events/new: remove custom_rrule checkbox; add return_to hidden field and back link; tag_list field added - events/show: fix back link via safe_return_to; add node link to subnav; add destroy button; remove custom_rrule display; show humanized rrule below raw string - events/index: destructive class on destroy button; node_id column replaced with node link; show/edit links carry return_to - nodes/show: add show/edit links to event entries with return_to --- app/controllers/events_controller.rb | 10 +++++++--- app/views/events/edit.html.erb | 9 +++------ app/views/events/index.html.erb | 10 ++++------ app/views/events/new.html.erb | 7 ++++--- app/views/events/show.html.erb | 16 +++++++++++----- app/views/nodes/show.html.erb | 6 +++++- 6 files changed, 34 insertions(+), 24 deletions(-) (limited to 'app/views/events') diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 3a60cf9..9bed5dd 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -21,6 +21,7 @@ class EventsController < ApplicationController # GET /events/1.xml def show @event = Event.find(params[:id]) + @return_to = params[:return_to] || events_path respond_to do |format| format.html # show.html.erb @@ -31,7 +32,10 @@ class EventsController < ApplicationController # GET /events/new # GET /events/new.xml def new - @event = Event.new(:node_id => params[:node_id]) + @event = Event.new( + node_id: params[:node_id], + tag_list: params[:tag_list] + ) respond_to do |format| format.html # new.html.erb @@ -53,7 +57,7 @@ class EventsController < ApplicationController respond_to do |format| if @event.save flash[:notice] = 'Event was successfully created.' - format.html { redirect_to(@event.node ? edit_node_path(@event.node) : edit_event_path(@event)) } + format.html { redirect_to(safe_return_to(params[:return_to] || (@event.node ? edit_node_path(@event.node) : edit_event_path(@event)))) } format.xml { render :xml => @event, :status => :created, :location => @event } else format.html { render :action => "new" } @@ -94,6 +98,6 @@ class EventsController < ApplicationController private def event_params - params.require(:event).permit(:title, :description, :is_primary, :start_time, :end_time, :rrule, :custom_rrule, :allday, :url, :latitude, :longitude, :node_id, :location) + params.require(:event).permit(:title, :description, :start_time, :end_time, :rrule, :allday, :url, :latitude, :longitude, :node_id, :location, :tag_list) end end diff --git a/app/views/events/edit.html.erb b/app/views/events/edit.html.erb index 17457df..5aee501 100644 --- a/app/views/events/edit.html.erb +++ b/app/views/events/edit.html.erb @@ -1,9 +1,6 @@ <% content_for :subnavigation do %> <%= link_to 'back', safe_return_to(params[:return_to] || events_path) %> - <% if @event.node %> - <%= link_to 'node', edit_node_path(@event.node) %> - <% end %> - <%= link_to 'show', @event %> + <%= button_to 'destroy', event_path(@event), method: :delete, form: { data: { confirm: 'Delete this event?' }, class: 'button_to destructive' } %> <% end %>

Editing event

@@ -25,8 +22,8 @@ <%= f.text_field :rrule %>

- <%= f.label :custom_rrule %>
- <%= f.check_box :custom_rrule %> + <%= f.label :tag_list, "Tags" %>
+ <%= f.text_field :tag_list %>

<%= f.label :allday %>
diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index 064fa86..d0458d7 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -3,7 +3,6 @@

TitleIs primary Start time End time Rrule Custom rrule Allday UrlLatitudeLongitude Node
<%=h event.display_title %><%=h event.is_primary %> <%=h event.start_time %> <%=h event.end_time %> <%=h event.rrule %> <%=h event.custom_rrule %> <%=h event.allday %> <%=h event.url %><%=h event.latitude %><%=h event.longitude %> <%=h event.node_id %> <%= link_to 'Show', event %> <%= link_to 'Edit', edit_event_path(event) %>
- @@ -16,17 +15,16 @@ <% @events.each do |event| %> - - - - - + + + + <% end %>
TitleIs primary Start time End time Rrule
<%=h event.display_title %><%=h event.is_primary %> <%=h event.start_time %> <%=h event.end_time %> <%=h event.rrule %> <%=h event.custom_rrule %> <%=h event.allday %> <%=h event.url %><%=h event.node_id %><%= link_to 'Show', event %><%= link_to 'Edit', edit_event_path(event) %><%= button_to 'Destroy', event, method: :delete, form: { data: { confirm: 'Are you sure?' } } %><%= event.node ? link_to(event.node_id, node_path(event.node)) : '' %><%= link_to 'show', event %><%= link_to 'edit', edit_event_path(event) %><%= button_to 'destroy', event, method: :delete, form: { data: { confirm: 'Are you sure?' }, class: 'button_to destructive' } %>
diff --git a/app/views/events/new.html.erb b/app/views/events/new.html.erb index cd892c5..4e1ef53 100644 --- a/app/views/events/new.html.erb +++ b/app/views/events/new.html.erb @@ -16,8 +16,8 @@ <%= f.text_field :rrule %>

- <%= f.label :custom_rrule %>
- <%= f.check_box :custom_rrule %> + <%= f.label :tag_list, "Tags" %>
+ <%= f.text_field :tag_list %>

<%= f.label :allday %>
@@ -37,10 +37,11 @@

<%= f.hidden_field :node_id %> + <%= hidden_field_tag :return_to, params[:return_to] %>

<%= f.submit 'Create' %>

<% end %> -<%= link_to 'Back', events_path %> +<%= link_to 'Back', safe_return_to(params[:return_to] || events_path) %> diff --git a/app/views/events/show.html.erb b/app/views/events/show.html.erb index ba14a7d..e206bc4 100644 --- a/app/views/events/show.html.erb +++ b/app/views/events/show.html.erb @@ -1,6 +1,9 @@ <% content_for :subnavigation do %> - <%= link_to 'back', edit_node_path(@event.node) %> - <%= link_to 'edit', edit_event_path(@event) %> + <% if @event.node %> + <%= link_to 'node', node_path(@event.node) %> + <% end %> + <%= link_to 'edit', edit_event_path(@event, return_to: request.path) %> + <%= button_to 'destroy', event_path(@event), method: :delete, form: { data: { confirm: 'Delete this event?' }, class: 'button_to destructive' } %> <% end %>

Event for node <%= @event.node.unique_name %>

@@ -18,11 +21,14 @@

Rrule: <%=h @event.rrule %> + <% if (human = @event.humanize_rrule(I18n.locale)) %> +
( <%= human %> ) + <% end %>

- Custom rrule: - <%=h @event.custom_rrule %> + Tags: + <%=h @event.tag_list %>

@@ -43,4 +49,4 @@

Longitude: <%=h @event.longitude %> -

\ No newline at end of file +

diff --git a/app/views/nodes/show.html.erb b/app/views/nodes/show.html.erb index 7223219..2ce3853 100644 --- a/app/views/nodes/show.html.erb +++ b/app/views/nodes/show.html.erb @@ -48,7 +48,11 @@ -- cgit v1.3 From 970f10854bccee0528de8435e5a65cdcc18ba93e Mon Sep 17 00:00:00 2001 From: erdgeist Date: Mon, 6 Jul 2026 04:20:55 +0200 Subject: Remove dead custom_rrule references after column drop events#index still read event.custom_rrule - a live bug the column-drop migration introduced, not just stale test data. Caught by four test failures (three fixtures/direct attribute hashes still setting the removed column, one UnknownAttributeError from a stale fixture loaded before any test method runs), none of which were actually testing this view - "should get index" passed throughout with zero Event records present, meaning it could never have caught a per-row rendering bug. Strengthened to create one real event first, so a future stray reference to a dropped or renamed column fails loudly instead of silently passing on an empty table. --- app/views/events/index.html.erb | 2 -- test/controllers/events_controller_test.rb | 3 +++ test/models/event_test.rb | 4 ---- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'app/views/events') diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index d0458d7..ae4f477 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -6,7 +6,6 @@ Start time End time Rrule - Custom rrule Allday Url Node @@ -18,7 +17,6 @@ <%=h event.start_time %> <%=h event.end_time %> <%=h event.rrule %> - <%=h event.custom_rrule %> <%=h event.allday %> <%=h event.url %> <%= event.node ? link_to(event.node_id, node_path(event.node)) : '' %> diff --git a/test/controllers/events_controller_test.rb b/test/controllers/events_controller_test.rb index 9371ca7..46f3f4f 100644 --- a/test/controllers/events_controller_test.rb +++ b/test/controllers/events_controller_test.rb @@ -4,6 +4,9 @@ class EventsControllerTest < ActionController::TestCase test "should get index" do login_as :quentin + node = create_node_with_published_page + Event.create!(node_id: node.id, start_time: Time.now, end_time: Time.now + 1.hour) + get :index assert_response :success assert_not_nil assigns(:events) diff --git a/test/models/event_test.rb b/test/models/event_test.rb index d85aadd..e98605e 100644 --- a/test/models/event_test.rb +++ b/test/models/event_test.rb @@ -29,7 +29,6 @@ class EventTest < ActiveSupport::TestCase :longitude => 13.378944, :rrule => "FOOBAR", :allday => false, - :custom_rrule => false, :node_id => @cal_node.id ) end @@ -44,7 +43,6 @@ class EventTest < ActiveSupport::TestCase :longitude => 13.378944, :rrule => nil, :allday => false, - :custom_rrule => false, :node_id => @cal_node.id ) @@ -62,7 +60,6 @@ class EventTest < ActiveSupport::TestCase :longitude => 13.378944, :rrule => "FREQ=WEEKLY;INTERVAL=1", :allday => false, - :custom_rrule => false, :node_id => @cal_node.id ) @@ -97,7 +94,6 @@ class EventTest < ActiveSupport::TestCase :longitude => 13.378944, :rrule => "FREQ=MONTHLY;INTERVAL=1;BYDAY=-1WE", :allday => false, - :custom_rrule => true, :node_id => @cal_node.id ) -- cgit v1.3 From cb610cebffae879344d5a8c6bebcc71ca5c04b2a Mon Sep 17 00:00:00 2001 From: erdgeist Date: Tue, 7 Jul 2026 21:45:35 +0200 Subject: Add or fix class names for the link underline rules to match --- app/views/assets/index.html.erb | 2 +- app/views/events/index.html.erb | 2 +- app/views/users/index.html.erb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/views/events') diff --git a/app/views/assets/index.html.erb b/app/views/assets/index.html.erb index e26830b..31dae59 100644 --- a/app/views/assets/index.html.erb +++ b/app/views/assets/index.html.erb @@ -5,7 +5,7 @@ <%= will_paginate @assets %> - +
<% @assets.each do |asset| %> diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index ae4f477..3065efd 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -1,6 +1,6 @@

Listing events

-
+
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 59806db..cf00ef6 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,4 +1,4 @@ -
Title Start time
+
-- cgit v1.3 From 92c3acb860adff26e1b960c275fd9c83fc2cf4ca Mon Sep 17 00:00:00 2001 From: erdgeist Date: Tue, 7 Jul 2026 21:46:25 +0200 Subject: Fix column count between table header and body --- app/views/events/index.html.erb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'app/views/events') diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index 3065efd..3143a7b 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -9,6 +9,7 @@ + <% @events.each do |event| %> @@ -20,9 +21,7 @@ - - - + <% end %>

Admins

Allday Url NodeAction
<%=h event.allday %> <%=h event.url %> <%= event.node ? link_to(event.node_id, node_path(event.node)) : '' %><%= link_to 'show', event %><%= link_to 'edit', edit_event_path(event) %><%= button_to 'destroy', event, method: :delete, form: { data: { confirm: 'Are you sure?' }, class: 'button_to destructive' } %><%= link_to 'show', event %> | <%= link_to 'edit', edit_event_path(event) %> | <%= button_to 'destroy', event, method: :delete, form: { data: { confirm: 'Are you sure?' }, class: 'button_to destructive' } %>
-- cgit v1.3 From 3d7ddaf18419c983dd179b50eabe253c3eee8194 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Wed, 8 Jul 2026 11:55:25 +0200 Subject: WIP: dynamic rrule builder --- app/views/events/edit.html.erb | 30 ++++++++++++++++++++++++++++-- public/javascripts/admin_interface.js | 6 +++++- 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'app/views/events') diff --git a/app/views/events/edit.html.erb b/app/views/events/edit.html.erb index 5aee501..d37c299 100644 --- a/app/views/events/edit.html.erb +++ b/app/views/events/edit.html.erb @@ -18,8 +18,34 @@ <%= f.datetime_select :end_time %>

- <%= f.label :rrule %>
- <%= f.text_field :rrule %> + <%= f.label :rrule, "Recurrence" %> +

+

+ + +

+
+

+

+ <% %w[MO TU WE TH FR SA SU].each do |code| %> + + <% end %> +

+
+ +

+ + <%= select_tag "rrule_excluded_month", options_for_select((1..12).map { |m| [RruleHumanizer::MONTH_NAMES[:de][m-1], m] }), style: "display: none;" %> +

+ Builds the field below automatically. If your pattern is more complex than this covers, just edit it directly below - these controls won't touch it unless you use them. +
+ <%= f.text_field :rrule, id: "event_rrule" %>

<%= f.label :tag_list, "Tags" %>
diff --git a/public/javascripts/admin_interface.js b/public/javascripts/admin_interface.js index 450438f..cb32d5a 100644 --- a/public/javascripts/admin_interface.js +++ b/public/javascripts/admin_interface.js @@ -50,7 +50,11 @@ $(document).ready(function () { if ($("#move_to_search_term").length != 0) { move_to_search.initialize_search(); } - + + if ($("#rrule_builder").length != 0) { + rrule_builder.initialize(); + } + if ($('#recent_changes_toggle').length != 0) { hide_all(); $('#recent_changes_toggle').attr("class", "selected"); -- cgit v1.3 From 3ae22916cd5fd0f63ffacc7a9e1aa97d311b83e6 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 9 Jul 2026 04:53:56 +0200 Subject: Fix button height mismatch between bordered and pill button styles, extend table styling to assets/users/events Fix button height mismatch, extend table styling to assets/users/events a.action_button (bordered) and the state_changing/destructive/computation pills (borderless) rendered at different heights, since each pill variant set its own padding at higher specificity than the shared rule meant to equalize them. Padding and border-radius now live only in one shared rule; also restores the #page_editor prefix on a.action_button, needed to outrank the #page_editor a wavy-underline rule. table.assets_table, table.user_table, and table.events_table now share table.node_table's border-collapse/header/hover treatment. tr min-height is dropped -- browsers don't honor height on -- in favor of cell padding, kept at zero for node_table (spacing already comes from its h4/p content) and set to 8px for the plain-text tables. --- app/views/assets/index.html.erb | 9 +++- app/views/events/index.html.erb | 12 ++--- app/views/events/show.html.erb | 103 ++++++++++++++++++++-------------------- app/views/users/index.html.erb | 14 +++++- public/stylesheets/admin.css | 60 ++++++++++++++++------- 5 files changed, 118 insertions(+), 80 deletions(-) (limited to 'app/views/events') diff --git a/app/views/assets/index.html.erb b/app/views/assets/index.html.erb index 31dae59..4301fee 100644 --- a/app/views/assets/index.html.erb +++ b/app/views/assets/index.html.erb @@ -2,11 +2,16 @@ <%= link_to 'New asset', new_asset_path %> <% end %> - <%= will_paginate @assets %> - + + + + + + + <% @assets.each do |asset| %> diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index 3143a7b..c3e0e5a 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -1,7 +1,7 @@

Listing events

PreviewNameType
- + @@ -9,23 +9,21 @@ - + <% @events.each do |event| %> - + - + <% end %>
Title Start time End timeAllday Url NodeAction
<%=h event.display_title %><%= link_to event.display_title, event %> <%=h event.start_time %> <%=h event.end_time %> <%=h event.rrule %> <%=h event.allday %> <%=h event.url %> <%= event.node ? link_to(event.node_id, node_path(event.node)) : '' %><%= link_to 'show', event %> | <%= link_to 'edit', edit_event_path(event) %> | <%= button_to 'destroy', event, method: :delete, form: { data: { confirm: 'Are you sure?' }, class: 'button_to destructive' } %><%= link_to 'edit', edit_event_path(event) %>
-
- -<%= link_to 'New event', new_event_path %> +<%= link_to 'New event', new_event_path, class: 'action_button' %> diff --git a/app/views/events/show.html.erb b/app/views/events/show.html.erb index e206bc4..5812228 100644 --- a/app/views/events/show.html.erb +++ b/app/views/events/show.html.erb @@ -1,52 +1,51 @@ -<% content_for :subnavigation do %> - <% if @event.node %> - <%= link_to 'node', node_path(@event.node) %> - <% end %> - <%= link_to 'edit', edit_event_path(@event, return_to: request.path) %> - <%= button_to 'destroy', event_path(@event), method: :delete, form: { data: { confirm: 'Delete this event?' }, class: 'button_to destructive' } %> -<% end %> - -

Event for node <%= @event.node.unique_name %>

- -

- Start time: - <%=h @event.start_time %> -

- -

- End time: - <%=h @event.end_time %> -

- -

- Rrule: - <%=h @event.rrule %> - <% if (human = @event.humanize_rrule(I18n.locale)) %> -
( <%= human %> ) - <% end %> -

- -

- Tags: - <%=h @event.tag_list %> -

- -

- Allday: - <%=h @event.allday %> -

- -

- Url: - <%=h @event.url %> -

- -

- Latitude: - <%=h @event.latitude %> -

- -

- Longitude: - <%=h @event.longitude %> -

+
+

<%= @event.node ? "Event for node #{@event.node.unique_name}" : "Event" %>

+ +
+
Actions
+
+
+ <% if @event.node %> +
+ <%= link_to 'Node', node_path(@event.node) %> +
+ <% end %> +
+ <%= link_to 'Edit', edit_event_path(@event, return_to: request.path), class: 'action_button' %> +
+
+ <%= button_to 'Destroy', event_path(@event), method: :delete, form: { data: { confirm: 'Delete this event?' }, class: 'button_to destructive' } %> +
+
+
+ +
Start time
+
<%=h @event.start_time %>
+ +
End time
+
<%=h @event.end_time %>
+ +
Rrule
+
<%=h @event.rrule %>
+ + <% if (human = @event.humanize_rrule(I18n.locale)) %> +
Recurrence
+
<%= human %>
+ <% end %> + +
Tags
+
<%=h @event.tag_list %>
+ +
Allday
+
<%=h @event.allday %>
+ +
Url
+
<%=h @event.url %>
+ +
Latitude
+
<%=h @event.latitude %>
+ +
Longitude
+
<%=h @event.longitude %>
+
+
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index cf00ef6..88868f5 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -2,13 +2,25 @@

Admins

+ + Login + + + + <%= render :partial => "user", :locals => {:users => @users[:admin] ||= []} %>

Users

+ + Login + + + + <%= render :partial => "user", :locals => {:users => @users[:user] ||= []} %> <% content_for :subnavigation do %> <%= link_to "create", new_user_path %> -<% end %> \ No newline at end of file +<% end %> diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index 3607166..66f13b8 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css @@ -274,8 +274,6 @@ form.button_to.state_changing input[type="submit"], form.button_to.state_changing button[type="submit"] { color: #0d47a1; background-color: #e3f2fd; - border-radius: 2px; - padding: 2px 8px; } form.button_to.state_changing input[type="submit"]:hover, @@ -288,8 +286,6 @@ form.button_to.destructive input[type="submit"], form.button_to.destructive button[type="submit"] { color: #cc0000; background-color: #fdecea; - border-radius: 2px; - padding: 2px 8px; } form.button_to.destructive input[type="submit"]:hover, @@ -302,8 +298,6 @@ form.button_to.computation input[type="submit"], form.button_to.computation button[type="submit"] { color: #00838f; background-color: #e0f2f1; - border-radius: 2px; - padding: 2px 8px; } form.button_to.computation input[type="submit"]:hover, @@ -385,15 +379,23 @@ table tr.header { text-align: left; } -table.node_table { +table.node_table , +table.assets_table, +table.user_table, +table.events_table { border-collapse: collapse; } table.node_table tr { - min-height: 2rem; border-bottom: 1px solid #000000; } +table.assets_table tr, +table.user_table tr, +table.events_table tr { + border-bottom: 1px solid #e8e8e8; +} + table.node_table th.node_id, table.node_table th.revision, table.node_table th.title { @@ -408,7 +410,10 @@ table.node_table th.title { min-width: 12rem; } -table.node_table tr.header { +table.node_table tr.header, +table.assets_table tr.header, +table.user_table tr.header, +table.events_table tr.header { height: 2rem; text-align: left; } @@ -421,6 +426,16 @@ table.node_table td { min-width: 2rem; } +table.assets_table td, +table.user_table td, +table.events_table td { + padding-top: 8px; + padding-bottom: 8px; + padding-right: 25px; + padding-left: 0px; + min-width: 2rem; +} + table.node_table .node_id { padding-left: 10px; padding-right: 15px; @@ -430,11 +445,17 @@ table.node_table .actions { text-transform: lowercase; } -table.node_table tr.header:hover { +table.node_table tr.header:hover, +table.assets_table tr.header:hover, +table.user_table tr.header:hover, +table.events_table tr.header:hover { background-color: #ffffff; } -table.node_table tr:hover { +table.node_table tr:hover, +table.assets_table tr:hover, +table.user_table tr:hover, +table.events_table tr:hover { background-color: #f1f1f1; } @@ -598,17 +619,18 @@ table.user_table td.user_login { display: block; } -#page_editor a.action_button, -.node_status form.button_to input[type="submit"], -.node_status form.button_to button[type="submit"] { +a.action_button, +form.button_to input[type="submit"], +form.button_to button[type="submit"] { padding: 4px 12px; line-height: 1.2; box-sizing: border-box; } -.node_status form.button_to input[type="submit"], -.node_status form.button_to button[type="submit"] { +form.button_to input[type="submit"], +form.button_to button[type="submit"] { border: 1px solid transparent; + border-radius: 2px; } .field_hint { @@ -659,7 +681,8 @@ div#page_editor { margin-left: 10px; } -#page_editor a.action_button { +#page_editor a.action_button, +a.action_button { display: inline-block; -webkit-appearance: none; appearance: none; @@ -671,7 +694,8 @@ div#page_editor { color: #000000; } -#page_editor a.action_button:hover { +#page_editor a.action_button:hover, +a.action_button:hover { color: #ffffff; background-color: #000000; } -- cgit v1.3 From 8310751d4db2854597d6379b24e346c65622972d Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 9 Jul 2026 14:19:36 +0200 Subject: Major resource view cleanup Remove dead pages#index and occurrences resource; fix menu_items#new menu_items#new called menu_item_params, which requires a submitted menu_item key that can never be present on a fresh GET -- new only ever needs a blank record to render against. pages#index and the entire occurrences resource were unmodified Rails scaffold generator output, unlinked from any nav and unreachable except by direct URL. pages#index had no controller action at all (@pages was never assigned); occurrences#index queried every row with no scoping or pagination and its layout referenced a scaffold.css asset that no longer exists in the pipeline. Neither is a real editorial surface -- occurrences are auto-generated from an event's RRULE and were never meant to be browsed as a flat list. Removed rather than repaired. pages#preview and pages#sort_images remain, now as explicit routes rather than under a full resources :pages block. --- app/controllers/menu_items_controller.rb | 2 +- app/controllers/users_controller.rb | 2 +- app/views/assets/index.html.erb | 6 ++-- app/views/assets/show.html.erb | 50 ++++++++++++++++------------- app/views/events/index.html.erb | 4 ++- app/views/menu_items/edit.html.erb | 51 +++++++++++++----------------- app/views/menu_items/index.html.erb | 9 ++---- app/views/nodes/index.html.erb | 5 +++ app/views/occurrences/index.html.erb | 2 +- app/views/pages/index.html.erb | 2 +- app/views/users/edit.html.erb | 54 ++++++++++++++------------------ app/views/users/index.html.erb | 17 +++++----- app/views/users/new.html.erb | 52 ++++++++++++++---------------- app/views/users/show.html.erb | 39 ++++++++++++----------- config/routes.rb | 9 ++---- public/stylesheets/admin.css | 32 +++++++++++++++++++ 16 files changed, 177 insertions(+), 159 deletions(-) (limited to 'app/views/events') diff --git a/app/controllers/menu_items_controller.rb b/app/controllers/menu_items_controller.rb index 1b1eb59..314d1ea 100644 --- a/app/controllers/menu_items_controller.rb +++ b/app/controllers/menu_items_controller.rb @@ -14,7 +14,7 @@ class MenuItemsController < ApplicationController end def new - @menu_item = MenuItem.new menu_item_params + @menu_item = MenuItem.new end def create diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f01691f..6572b7a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -15,7 +15,7 @@ class UsersController < ApplicationController end def new - @user = User.new + @user = User.new(admin: params[:admin].present?) end def create diff --git a/app/views/assets/index.html.erb b/app/views/assets/index.html.erb index 4301fee..6591091 100644 --- a/app/views/assets/index.html.erb +++ b/app/views/assets/index.html.erb @@ -1,6 +1,6 @@ -<% content_for :subnavigation do %> - <%= link_to 'New asset', new_asset_path %> -<% end %> +

Assets

+ +<%= link_to 'New asset', new_asset_path, class: 'action_button' %> <%= will_paginate @assets %> diff --git a/app/views/assets/show.html.erb b/app/views/assets/show.html.erb index 694be5a..0286678 100644 --- a/app/views/assets/show.html.erb +++ b/app/views/assets/show.html.erb @@ -1,23 +1,29 @@ -<% content_for :subnavigation do %> - <%= link_to 'Edit', edit_asset_path(@asset) %> - <%= link_to 'Back', assets_path %> -<% end %> +
+

<%= @asset.name %>

- - - - - - - - - - - - - - - - - -
Thumbnail<%= image_tag @asset.upload.url(:medium), style: "max-width: 300px; max-height: 300px;" %>
Public Path<%= @asset.upload.url.sub(/\?\d+$/, "") %>
Content Type<%= @asset.upload.content_type %>
Size<%= "#{@asset.upload.size/1024} KB" %>
+
+
Actions
+
+
+
+ <%= link_to 'Edit', edit_asset_path(@asset), class: 'action_button' %> +
+
+ <%= link_to 'Back', assets_path %> +
+
+
+ +
Thumbnail
+
<%= image_tag @asset.upload.url(:medium), style: "max-width: 300px; max-height: 300px;" %>
+ +
Public Path
+
<%= @asset.upload.url.sub(/\?\d+$/, "") %>
+ +
Content Type
+
<%= @asset.upload.content_type %>
+ +
Size
+
<%= "#{@asset.upload.size/1024} KB" %>
+
+
diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index c3e0e5a..b6a5b1f 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -1,4 +1,6 @@ -

Listing events

+

Events

+ +<%= link_to 'New event', new_event_path, class: 'action_button' %> diff --git a/app/views/menu_items/edit.html.erb b/app/views/menu_items/edit.html.erb index 9891708..dc5e8f9 100644 --- a/app/views/menu_items/edit.html.erb +++ b/app/views/menu_items/edit.html.erb @@ -1,32 +1,25 @@

Edit Menu Item

-<%= form_for @menu_item do |f| %> -
- - - - - - - - - - - - - - - - - - - - - -
Search - <%= text_field_tag :menu_search_term %> -
+
+ <%= form_for @menu_item do |f| %> +
+
Search
+
+ <%= text_field_tag :menu_search_term %> +
+
-
-
Node Id<%= f.text_field :node_id %>
Path<%= f.text_field :path %>
Title<%= f.text_field :title %>
<%= f.submit 'Update' %>
-<% end %> +
Node Id
+
<%= f.text_field :node_id %>
+ +
Path
+
<%= f.text_field :path %>
+ +
Title
+
<%= f.text_field :title %>
+ +
+
<%= f.submit 'Update' %>
+ <% end %> + + diff --git a/app/views/menu_items/index.html.erb b/app/views/menu_items/index.html.erb index c52c150..e84199b 100644 --- a/app/views/menu_items/index.html.erb +++ b/app/views/menu_items/index.html.erb @@ -1,10 +1,7 @@ -<% content_for :subnavigation do %> - <%= link_to "new", new_menu_item_path %> -<% end %> - -

Menu Items

+<%= link_to 'New menu item', new_menu_item_path, class: 'action_button' %> + <% @menu_items.each do |menu_item| %> @@ -16,7 +13,7 @@ <% end %> diff --git a/app/views/nodes/index.html.erb b/app/views/nodes/index.html.erb index 2399105..903e80b 100644 --- a/app/views/nodes/index.html.erb +++ b/app/views/nodes/index.html.erb @@ -3,6 +3,11 @@ <% end %>

Nodes

+
+ <%= link_to 'New node', new_node_path, class: 'action_button' %> + Creates a node with no parent or kind set — for a specific page, use the wizard's create flow or "add child" from an existing node instead. +
+ <%= will_paginate @nodes %> diff --git a/app/views/occurrences/index.html.erb b/app/views/occurrences/index.html.erb index 0e99857..82818d0 100644 --- a/app/views/occurrences/index.html.erb +++ b/app/views/occurrences/index.html.erb @@ -1,4 +1,4 @@ -

Listing occurrences

+

Occurrences

diff --git a/app/views/pages/index.html.erb b/app/views/pages/index.html.erb index 91e5359..05c7311 100644 --- a/app/views/pages/index.html.erb +++ b/app/views/pages/index.html.erb @@ -1,4 +1,4 @@ -

Listing pages

+

Pages

diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index df1005b..77b33c6 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -2,38 +2,32 @@ <% if @user.errors.any? %>
-
    <% @user.errors.full_messages.each do |msg| %>
  • <%= msg.gsub("Slug", "Title") %>
  • <% end %>
+
    <% @user.errors.full_messages.each do |msg| %>
  • <%= msg %>
  • <% end %>
<% end %> +
+ <%= form_for @user do |f| %> +
+
Login
+
<%= f.text_field :login %>
-<%= form_for @user do |f| %> -
- - - - - - - - - - - - - - - - - <% if current_user.admin? %> - - - - +
E-Mail
+
<%= f.text_field :email %>
+ +
Password
+
<%= f.password_field :password %>
+ +
Confirm
+
<%= f.password_field :password_confirmation %>
+ + <% if current_user.admin? %> +
Admin?
+
<%= f.check_box :admin %>
+ <% end %> + +
+
<%= f.submit "Update" %>
<% end %> - - - - -
Login<%= f.text_field :login %>
E-Mail<%= f.text_field :email %>
Password<%= f.password_field :password %>
Confirm<%= f.password_field :password_confirmation %>
Admin?<%= f.check_box :admin %>
<%= f.submit "Update" %>
-<% end %> + + diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 88868f5..46e958b 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,7 +1,6 @@ +

Admins

+<%= link_to 'New admin user', new_user_path(admin: true), class: 'action_button' %> - - - @@ -9,9 +8,11 @@ <%= render :partial => "user", :locals => {:users => @users[:admin] ||= []} %> - - - +

Admins

Login

Users

+ +

Users

+<%= link_to 'New user', new_user_path, class: 'action_button' %> + @@ -20,7 +21,3 @@ <%= render :partial => "user", :locals => {:users => @users[:user] ||= []} %>
Login
- -<% content_for :subnavigation do %> - <%= link_to "create", new_user_path %> -<% end %> diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 6beda4f..ce38fdf 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -2,36 +2,30 @@ <% if @user.errors.any? %>
-
    <% @user.errors.full_messages.each do |msg| %>
  • <%= msg.gsub("Slug", "Title") %>
  • <% end %>
+
    <% @user.errors.full_messages.each do |msg| %>
  • <%= msg %>
  • <% end %>
<% end %> +
+ <%= form_for @user do |f| %> +
+
Login
+
<%= f.text_field :login %>
-<%= form_for @user do |f| %> - - - - - - - - - - - - - - - - - - - - - - - - - -
Login<%= f.text_field :login %>
E-Mail<%= f.text_field :email %>
Password<%= f.password_field :password %>
Confirm<%= f.password_field :password_confirmation %>
Admin?<%= f.check_box :admin %>
<%= f.submit "Create" %>
-<% end %> +
E-Mail
+
<%= f.text_field :email %>
+ +
Password
+
<%= f.password_field :password %>
+ +
Confirm
+
<%= f.password_field :password_confirmation %>
+ +
Admin?
+
<%= f.check_box :admin %>
+ +
+
<%= f.submit "Create" %>
+ <% end %> +
+
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 80483d3..e744efe 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,20 +1,23 @@ -<% content_for :subnavigation do %> - <%= link_to 'Edit', edit_user_path(@user) %> -<% end %> +
+

<%= @user.login %>

-

User: <%= @user.login %>

+
+
Actions
+
+
+
+ <%= link_to 'Edit', edit_user_path(@user), class: 'action_button' %> +
+
+
- - - - - - - - - - - - - -
Login<%= @user.login %>
E-Mail<%= @user.email %>
Admin?<%= @user.admin ? "yes" : "no" %>
\ No newline at end of file +
Login
+
<%= @user.login %>
+ +
E-Mail
+
<%= @user.email %>
+ +
Admin?
+
<%= @user.admin ? "yes" : "no" %>
+
+
diff --git a/config/routes.rb b/config/routes.rb index da46e5c..5d68828 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,15 +20,10 @@ Cccms::Application.routes.draw do scope '(:locale)', locale: /de|en/ do resources :tags - resources :occurrences resources :events - resources :pages do - member do - get :preview - put :sort_images - end - end + get 'pages/:id/preview', to: 'pages#preview', as: :preview_page + put 'pages/:id/sort_images', to: 'pages#sort_images', as: :sort_images_page resources :nodes do collection do diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index 66f13b8..f00a658 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css @@ -640,6 +640,13 @@ form.button_to button[type="submit"] { color: #969696; padding-bottom: 4px; } + +.action_button + .field_hint { + display: inline; + margin-left: 0.5rem; + margin-top: 0; +} + /* Identical declaration block to #search_results p span.result_path / #menu_search_results p span.result_path below -- three independent copies of the same "small gray helper text" style. */ @@ -692,6 +699,7 @@ a.action_button { font-weight: bold; text-decoration: none; color: #000000; + margin-bottom: 1rem; } #page_editor a.action_button:hover, @@ -700,6 +708,11 @@ a.action_button:hover { background-color: #000000; } +#page_editor form input[type=text], +#page_editor form input[type=password] { + padding: 5px; +} + @media(min-width:1016px) { input#tag_list, input#node_staged_slug, @@ -731,6 +744,11 @@ a.action_button:hover { #page_editor #metadata, #page_editor #content, #admin_overview { margin-left: -125px; } + + #page_editor form input[type=text], + #page_editor form input[type=password] { + width: 690px; + } } @media(max-width:1015px) { @@ -773,6 +791,12 @@ a.action_button:hover { font-weight: bold; } + #page_editor form input[type=text], + #page_editor form input[type=password] { + box-sizing: border-box; + width: 100%; + } + input[type=text], textarea { font-size: 1.5rem; } @@ -920,6 +944,14 @@ div#draft_list table td.actions a { cursor: grab; } +#menu_item_list td a { + text-decoration: underline; + text-decoration-style: wavy; + text-decoration-color: #b0b0b0; + text-decoration-thickness: 1px; + text-underline-offset: 2px; +} + .ui-state-highlight td { height: 20px; } -- cgit v1.3 From ae48c9e86411f8f54f29e72a11b1e4ced48fe437 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 9 Jul 2026 15:50:22 +0200 Subject: Paginate Events index --- app/controllers/events_controller.rb | 4 ++-- app/views/events/index.html.erb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'app/views/events') diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index b98a38e..b9b7892 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -9,10 +9,10 @@ class EventsController < ApplicationController # GET /events # GET /events.xml def index - @events = Event.all + @events = Event.order(:id) respond_to do |format| - format.html # index.html.erb + format.html { @events = @events.paginate(page: params[:page], per_page: 25) } format.xml { render :xml => @events } end end diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index b6a5b1f..f579fc0 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -2,6 +2,8 @@ <%= link_to 'New event', new_event_path, class: 'action_button' %> +<%= will_paginate @events %> + @@ -27,5 +29,3 @@ <% end %>
Title
- -<%= link_to 'New event', new_event_path, class: 'action_button' %> -- cgit v1.3 From 7b57d898da07811bb35ec088cb12505783a55214 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 9 Jul 2026 18:54:39 +0200 Subject: Add an events view for events without node --- app/views/events/without_node.html.erb | 28 ++++++++++++++++++++++++++++ config/routes.rb | 7 ++++++- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 app/views/events/without_node.html.erb (limited to 'app/views/events') diff --git a/app/views/events/without_node.html.erb b/app/views/events/without_node.html.erb new file mode 100644 index 0000000..52514ab --- /dev/null +++ b/app/views/events/without_node.html.erb @@ -0,0 +1,28 @@ +

Events without a node

+ +<%= link_to '← All events', events_path %> +<%= link_to 'New event', new_event_path, class: 'action_button' %> + +<%= will_paginate @events %> + + + + + + + + + + + +<% @events.each do |event| %> + + + + + + + + +<% end %> +
TitleStart timeEnd timeAlldayUrl
<%= link_to event.display_title, event %><%=h event.start_time %><%=h event.end_time %><%=h event.allday %><%=h event.url %><%= link_to 'edit', edit_event_path(event) %>
diff --git a/config/routes.rb b/config/routes.rb index 5d68828..c0aef2f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,7 +20,12 @@ Cccms::Application.routes.draw do scope '(:locale)', locale: /de|en/ do resources :tags - resources :events + + resources :events do + collection do + get :without_node + end + end get 'pages/:id/preview', to: 'pages#preview', as: :preview_page put 'pages/:id/sort_images', to: 'pages#sort_images', as: :sort_images_page -- cgit v1.3 From 6dcbf019deda9ec2809f8735c8b10ba3a5701189 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 9 Jul 2026 18:55:04 +0200 Subject: Add an events view for events without node, part 2 --- app/controllers/events_controller.rb | 5 +++++ app/views/events/index.html.erb | 1 + 2 files changed, 6 insertions(+) (limited to 'app/views/events') diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index b9b7892..be3f547 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -17,6 +17,11 @@ class EventsController < ApplicationController end end + # GET /events/without_node + def without_node + @events = Event.where(node_id: nil).order(:start_time).paginate(page: params[:page], per_page: 25) + end + # GET /events/1 # GET /events/1.xml def show diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index f579fc0..8127e3a 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -1,6 +1,7 @@

Events

<%= link_to 'New event', new_event_path, class: 'action_button' %> +<%= link_to 'View events without a page →', without_node_events_path %> <%= will_paginate @events %> -- cgit v1.3 From 83a74c6d01e0be50d5806d8253de9f2ad6d5c143 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 9 Jul 2026 18:55:25 +0200 Subject: Rework events#new with the new layout paradigm --- app/views/events/new.html.erb | 89 +++++++++++++++++++---------------- public/javascripts/admin_interface.js | 4 ++ public/javascripts/admin_search.js | 59 +++++++++++++++++++++++ 3 files changed, 112 insertions(+), 40 deletions(-) (limited to 'app/views/events') diff --git a/app/views/events/new.html.erb b/app/views/events/new.html.erb index 4e1ef53..b20fe48 100644 --- a/app/views/events/new.html.erb +++ b/app/views/events/new.html.erb @@ -1,47 +1,56 @@

New event

+<%= link_to 'Back', safe_return_to(params[:return_to] || events_path) %> + <%= form_for(@event) do |f| %> <%= form_error_messages(f) %> -

- <%= f.label :start_time %>
- <%= f.datetime_select :start_time %> -

-

- <%= f.label :end_time %>
- <%= f.datetime_select :end_time %> -

-

- <%= f.label :rrule %>
- <%= f.text_field :rrule %> -

-

- <%= f.label :tag_list, "Tags" %>
- <%= f.text_field :tag_list %> -

-

- <%= f.label :allday %>
- <%= f.check_box :allday %> -

-

- <%= f.label :url %>
- <%= f.text_field :url %> -

-

- <%= f.label :latitude %>
- <%= f.text_field :latitude %> -

-

- <%= f.label :longitude %>
- <%= f.text_field :longitude %> -

-

- <%= f.hidden_field :node_id %> - <%= hidden_field_tag :return_to, params[:return_to] %> -

-

- <%= f.submit 'Create' %> -

+
+
+
Node
+
+ <%= text_field_tag :event_node_search_term %> +
+ <%= f.hidden_field :node_id %> + Optional — search and pick a node to associate this event with a page. +
+ +
Start time
+
<%= f.datetime_select :start_time %>
+ +
End time
+
<%= f.datetime_select :end_time %>
+ +
Rrule
+
<%= f.text_field :rrule %>
+ +
Title
+
+ <%= f.text_field :title %> + Optional — if left blank, the associated node's title is used. +
+ +
Tags
+
<%= f.text_field :tag_list %>
+ +
Allday
+
<%= f.check_box :allday %>
+ +
Url
+
<%= f.text_field :url %>
+ +
Latitude
+
<%= f.text_field :latitude %>
+ +
Longitude
+
<%= f.text_field :longitude %>
+ +
+
<%= hidden_field_tag :return_to, params[:return_to] %>
+ +
+
<%= f.submit 'Create' %>
+
+
<% end %> -<%= link_to 'Back', safe_return_to(params[:return_to] || events_path) %> diff --git a/public/javascripts/admin_interface.js b/public/javascripts/admin_interface.js index 4107ce8..8ca5b5e 100644 --- a/public/javascripts/admin_interface.js +++ b/public/javascripts/admin_interface.js @@ -51,6 +51,10 @@ $(document).ready(function () { move_to_search.initialize_search(); } + if ($("#event_node_search_term").length != 0) { + event_search.initialize_search(); + } + if ($("#rrule_builder").length != 0) { rrule_builder.initialize(); } diff --git a/public/javascripts/admin_search.js b/public/javascripts/admin_search.js index 6ef9087..5be5d57 100644 --- a/public/javascripts/admin_search.js +++ b/public/javascripts/admin_search.js @@ -283,3 +283,62 @@ move_to_search = { return barf; } } + +event_search = { + initialize_search : function() { + $("#event_node_search_term").bind("input", function() { + if ($(this).val()) { + $.ajax({ + type: "GET", + url: ADMIN_MENU_SEARCH_URL, + data: "search_term=" + $(this).val(), + dataType: "json", + success : function(results) { + event_search.show_results(results); + } + }); + } + else { + $('#search_results').slideUp(); + $('#search_results').empty(); + } + }); + }, + + show_results : function(results) { + $("#search_results").empty(); + var found = false; + for (result in results) { + var link = $(( + "

" + results[result].title + + "" + results[result].unique_name + "" + + "

")); + + $(link).bind("click", event_search.link_closure(results[result])); + + $("#search_results").append(link); + found = true; + } + if (found) + $('#search_results').slideDown(); + else + $('#search_results').slideUp(); + }, + + link_closure : function(node) { + var barf = function(){ + $("#event_node_search_term").val(node.title); + $("#event_node_id").val(node.node_id); + $('#search_results').slideUp(); + $('#search_results').empty(); + + var title_field = $("#event_title"); + if (title_field.val() === "") { + $("#event_title_hint").text("Using \"" + node.title + "\" from the associated node."); + } + + return false; + } + return barf; + } +}; -- cgit v1.3 From ff2edea873de50c5be41cd24bb7a8d1e0d75d9ee Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 9 Jul 2026 19:27:47 +0200 Subject: Modernize events#edit: node re-link guard, editable Title, layout overhaul Converts the

-per-field layout to node_description/node_content, matching every other converted view. The RRULE builder's internal markup and JS-driven show/hide are untouched -- only its containing block moved. Adds the same node picker and optional Title field events#new just got, with one addition specific to edit: an already-linked event shows its node as a plain link, with the picker itself gated behind a

disclosure. Re-linking a live event to a different node is rare and has real side effects (which page it appears under, which tag-prefill applies), so it gets an extra click rather than sitting as easy to trigger as the unlinked case, which stays unguarded. Moves Destroy out of the subnav and into the page body, matching events#show. Required extracting it (and Back) into their own block outside form_for -- button_to generates its own nested
, which the browser silently drops when nested inside another; this is the same failure mode nodes#edit hit earlier in this branch. Reuses the existing .node_action_bar class rather than the naked block that would've resulted, adding a "standalone_action_bar" modifier for the two things specific to living outside #page_editor: the -125px margin every other line in the form gets for free, and link underline visibility, since this is the first .node_action_bar instance with a bare rather than only buttons. --- app/views/events/edit.html.erb | 151 +++++++++++++++++++++++------------------ public/stylesheets/admin.css | 5 ++ 2 files changed, 90 insertions(+), 66 deletions(-) (limited to 'app/views/events') diff --git a/app/views/events/edit.html.erb b/app/views/events/edit.html.erb index d37c299..6cb04bd 100644 --- a/app/views/events/edit.html.erb +++ b/app/views/events/edit.html.erb @@ -1,76 +1,95 @@ -<% content_for :subnavigation do %> - <%= link_to 'back', safe_return_to(params[:return_to] || events_path) %> - <%= button_to 'destroy', event_path(@event), method: :delete, form: { data: { confirm: 'Delete this event?' }, class: 'button_to destructive' } %> -<% end %> -

Editing event

+
+ <%= link_to 'Back', safe_return_to(params[:return_to] || events_path) %> + <%= button_to 'Destroy', event_path(@event), method: :delete, + form: { data: { confirm: 'Delete this event?' }, class: 'button_to destructive' } %> +
+ <%= form_for(@event) do |f| %> <%= hidden_field_tag :return_to, @return_to %> <%= form_error_messages(f) %> -

- <%= f.label :start_time %>
- <%= f.datetime_select :start_time %> -

-

- <%= f.label :end_time %>
- <%= f.datetime_select :end_time %> -

-

- <%= f.label :rrule, "Recurrence" %> -

-

- - -

-
-

-

- <% %w[MO TU WE TH FR SA SU].each do |code| %> - - <% end %> -

+
+
+
Node
+
+ <% if @event.node %> + <%= link_to @event.node.title, node_path(@event.node) %> +
+ Change node + <%= text_field_tag :event_node_search_term %> +
+ This will re-link the event to a different node — rarely needed. +
+ <% else %> + <%= text_field_tag :event_node_search_term %> +
+ Optional — search and pick a node to associate this event with a page. + <% end %> + <%= f.hidden_field :node_id %> +
+ +
Start time
+
<%= f.datetime_select :start_time %>
+ +
End time
+
<%= f.datetime_select :end_time %>
+ +
Recurrence
+
+
+

+ + +

+
+

+

+ <% %w[MO TU WE TH FR SA SU].each do |code| %> + + <% end %> +

+
+ +

+ + <%= select_tag "rrule_excluded_month", options_for_select((1..12).map { |m| [RruleHumanizer::MONTH_NAMES[:de][m-1], m] }), style: "display: none;" %> +

+ Builds the field below automatically. If your pattern is more complex than this covers, just edit it directly below - these controls won't touch it unless you use them. +
+ <%= f.text_field :rrule, id: "event_rrule" %>
- - <%= f.text_field :rrule, id: "event_rrule" %> -

-

- <%= f.label :tag_list, "Tags" %>
- <%= f.text_field :tag_list %> -

-

- <%= f.label :allday %>
- <%= f.check_box :allday %> -

-

- <%= f.label :url %>
- <%= f.text_field :url %> -

-

- <%= f.label :latitude %>
- <%= f.text_field :latitude %> -

-

- <%= f.label :longitude %>
- <%= f.text_field :longitude %> -

-

- <%= f.hidden_field :node_id %> -

-

- <%= f.submit 'Update' %> -

+
<% end %> diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index 3f95b0c..fa36c18 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css @@ -31,6 +31,7 @@ table.events_table a, table.user_table a, table.revisions_table a, .add_child_links a, +.standalone_action_bar a, div#draft_list table td.actions a { text-decoration: underline; -webkit-text-decoration-style: wavy; @@ -83,6 +84,10 @@ input[type=radio] { .node_action_bar.node_action_bar_save { margin-left: 120px; } + + .standalone_action_bar { + margin-left: -125px; + } } @media(max-width:1015px) { #wrapper { -- cgit v1.3 From c9401e45433ea45b46f9a8faf1e7e537e4683244 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Fri, 10 Jul 2026 18:07:15 +0200 Subject: Fix rrule/url column overflow on events#index Long RRULEs previously overflowed their column with no wrap point; now escaped and rendered with a after each semicolon, so a long rule wraps at a clause boundary instead of running off the table. Deliberately not truncated -- a cut-off RRULE's trailing clause (BYDAY, BYMONTH, etc.) is usually the most specific part. The url column is now a real link, truncated with an ellipsis at a fixed width -- deliberately no tooltip, since hovering a real link already shows the full address in the browser's own status bar. rrule_with_break_opportunities splits on the raw string's own semicolons before escaping each piece, not after -- escaping first and searching the result for semicolons also matches the ones inside </> entities, corrupting anything containing a literal < or >. --- app/helpers/events_helper.rb | 11 +++++++++++ app/views/events/index.html.erb | 8 ++++++-- public/stylesheets/admin.css | 15 +++++++++++++++ test/models/helpers/events_helper_test.rb | 21 +++++++++++++++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) (limited to 'app/views/events') diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 8a9a878..5e84f53 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -1,2 +1,13 @@ +require 'cgi' + module EventsHelper + # Insert a zero-width break opportunity after each semicolon, so a long + # RRULE can wrap at a clause boundary instead of overflowing its column. + # Deliberately , not ellipsis -- unlike a URL, an RRULE's trailing + # characters (BYDAY, BYMONTH, etc.) are usually the most specific part, + # and truncating them would hide exactly the wrong end of the string. + def rrule_with_break_opportunities(rrule) + return "" if rrule.blank? + raw(rrule.split(';', -1).map { |part| CGI.escapeHTML(part) }.join(';')) + end end diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb index 8127e3a..3d953d5 100644 --- a/app/views/events/index.html.erb +++ b/app/views/events/index.html.erb @@ -22,9 +22,13 @@ <%= link_to event.display_title, event %> <%=h event.start_time %> <%=h event.end_time %> - <%=h event.rrule %> + <%= rrule_with_break_opportunities(event.rrule) %> <%=h event.allday %> - <%=h event.url %> + + <% if event.url.present? %> + <%= link_to event.url, event.url %> + <% end %> + <%= event.node ? link_to(event.node_id, node_path(event.node)) : '' %> <%= link_to 'edit', edit_event_path(event) %> diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index 1a81728..4723f50 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css @@ -519,6 +519,21 @@ table.revisions_table tr:hover { background-color: #f1f1f1; } +.events_table .rrule_text { + display: inline-block; + max-width: 300px; + overflow-wrap: break-word; +} + +.events_table .truncate { + display: inline-block; + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: bottom; +} + #diffview del { background: #ffd7d5; color: #82071e; diff --git a/test/models/helpers/events_helper_test.rb b/test/models/helpers/events_helper_test.rb index 2e7567e..0486b3e 100644 --- a/test/models/helpers/events_helper_test.rb +++ b/test/models/helpers/events_helper_test.rb @@ -1,4 +1,25 @@ require 'test_helper' class EventsHelperTest < ActionView::TestCase + test "rrule_with_break_opportunities inserts a break opportunity after each semicolon" do + result = rrule_with_break_opportunities("FREQ=MONTHLY;BYMONTH=1,2,3;BYDAY=-1TH") + assert_equal "FREQ=MONTHLY;BYMONTH=1,2,3;BYDAY=-1TH", result + assert result.html_safe? + end + + test "rrule_with_break_opportunities escapes HTML-significant characters" do + result = rrule_with_break_opportunities("FREQ=WEEKLY;BYDAY=