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 19b21ce8..064fa860 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 3a60cf93..9bed5ddd 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 17457df4..5aee5019 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 064fa860..d0458d71 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 cd892c5d..4e1ef53e 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 ba14a7d3..e206bc4c 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 72232194..2ce3853e 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 d0458d71..ae4f4773 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 9371ca78..46f3f4f1 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 d85aadd9..e98605e7 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 e26830ba..31dae598 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 ae4f4773..3065efd7 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 59806db2..cf00ef69 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 3065efd7..3143a7b1 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 5aee5019..d37c299f 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 450438f3..cb32d5a0 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 31dae598..4301fee6 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 3143a7b1..c3e0e5ae 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 e206bc4c..58122280 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 cf00ef69..88868f56 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 3607166f..66f13b8e 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 1b1eb598..314d1ead 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 f01691f7..6572b7a2 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 4301fee6..6591091e 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 694be5a5..02866780 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 c3e0e5ae..b6a5b1f0 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 98917082..dc5e8f97 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 c52c1506..e84199b3 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 2399105f..903e80bf 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 0e998572..82818d09 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 91e53598..05c73117 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 df1005bc..77b33c6a 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 88868f56..46e958bf 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 6beda4f1..ce38fdf9 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 80483d3b..e744efeb 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 da46e5c4..5d688285 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 66f13b8e..f00a6581 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 b98a38e8..b9b78926 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 b6a5b1f0..f579fc06 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 00000000..52514ab9 --- /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 5d688285..c0aef2ff 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 b9b78926..be3f5476 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 f579fc06..8127e3a4 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 4e1ef53e..b20fe488 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 4107ce8e..8ca5b5ee 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 6ef9087a..5be5d579 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 d37c299f..6cb04bd7 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 3f95b0c7..fa36c18b 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 8a9a878c..5e84f534 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 8127e3a4..3d953d5f 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 1a817286..4723f507 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 2e7567e0..0486b3e7 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=