Skip to content

Commit 3f77f25

Browse files
maebealeclaude
andcommitted
Restrict ended events to registered users and grey out cards
Ended events were visible to all users regardless of registration status. Now only admins and actively registered users can see them in the index and via direct URL. Ended event cards render with reduced opacity and muted styling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9d031a0 commit 3f77f25

4 files changed

Lines changed: 84 additions & 17 deletions

File tree

app/policies/event_policy.rb

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ def index?
88
end
99

1010
def show?
11-
admin? || record.publicly_visible? || (authenticated? && record.published?)
11+
return true if admin?
12+
13+
if record.ended?
14+
authenticated? && record.published? && record.actively_registered?(user.person)
15+
else
16+
record.publicly_visible? || (authenticated? && record.published?)
17+
end
1218
end
1319

1420
def register?
@@ -39,15 +45,30 @@ def owner?
3945

4046
relation_scope do |relation|
4147
next relation if admin?
42-
if authenticated? # logged in users can see events they are registered for even if registration is closed
43-
relation.left_outer_joins(:registrants)
44-
.published
45-
.where("registration_close_date IS NULL OR registration_close_date >= ? OR people.id = ?", Time.current, user.person_id)
46-
.distinct
48+
49+
if authenticated?
50+
active_statuses = EventRegistration::ACTIVE_STATUSES.map { |s| relation.connection.quote(s) }.join(", ")
51+
relation
52+
.joins(
53+
"LEFT OUTER JOIN event_registrations
54+
ON event_registrations.event_id = events.id
55+
AND event_registrations.status IN (#{active_statuses})
56+
LEFT OUTER JOIN people
57+
ON people.id = event_registrations.registrant_id"
58+
)
59+
.published
60+
.where(
61+
"(events.end_date >= :now AND (events.registration_close_date IS NULL OR events.registration_close_date >= :now))
62+
OR people.id = :person_id",
63+
now: Time.current,
64+
person_id: user.person_id
65+
)
66+
.distinct
4767
else
4868
relation.publicly_visible
4969
.published
50-
.where("registration_close_date IS NULL OR registration_close_date >= ?", Time.current)
70+
.where("events.end_date >= ?", Time.current)
71+
.where("events.registration_close_date IS NULL OR events.registration_close_date >= ?", Time.current)
5172
end
5273
end
5374
end

app/views/events/_card.html.erb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<% event = event.decorate %>
2+
<% ended = event.ended? %>
23

34
<%= tag.div id: dom_id(event, :card),
4-
class: "relative bg-white border border-gray-200 rounded-2xl shadow-sm hover:shadow-md
5-
transition p-4 flex flex-col gap-4 h-full" do %>
5+
class: class_names(
6+
"relative rounded-2xl shadow-sm transition p-4 flex flex-col gap-4 h-full",
7+
"bg-gray-100 border border-gray-300 opacity-60": ended,
8+
"bg-white border border-gray-200 hover:shadow-md": !ended
9+
) do %>
610
<!-- Bookmark -->
711
<div class="absolute top-3 right-3 z-20">
812
<%= render "bookmarks/editable_bookmark_icon", resource: event.object %>

spec/factories/events.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
publicly_featured { true }
3030
end
3131

32+
trait :ended do
33+
start_date { 14.days.ago }
34+
end_date { 12.days.ago }
35+
registration_close_date { 13.days.ago }
36+
end
37+
3238
trait :registration_closed do
3339
registration_close_date { 13.days.ago }
3440
end

spec/policies/event_policy_spec.rb

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
let(:published_event) { build_stubbed :event, :published }
77
let(:public_event) { build_stubbed :event, publicly_visible: true }
88
let(:unpublished_event) { build_stubbed :event, :unpublished }
9+
let(:ended_event) { build_stubbed :event, :published, :ended }
910
let(:open_registration_event) { build_stubbed :event, registration_close_date: 1.day.from_now }
1011
let(:closed_registration_event) { build_stubbed :event, registration_close_date: 1.day.ago }
1112

@@ -54,6 +55,36 @@ def policy_for(record: nil, user:)
5455
end
5556
end
5657

58+
context "when event has ended" do
59+
context "with admin user" do
60+
subject { policy_for(record: ended_event, user: admin_user) }
61+
62+
it { is_expected.to be_allowed_to(:show?) }
63+
end
64+
65+
context "with registered user" do
66+
subject { policy_for(record: ended_event, user: regular_user) }
67+
68+
before { allow(ended_event).to receive(:actively_registered?).with(regular_user.person).and_return(true) }
69+
70+
it { is_expected.to be_allowed_to(:show?) }
71+
end
72+
73+
context "with unregistered user" do
74+
subject { policy_for(record: ended_event, user: regular_user) }
75+
76+
before { allow(ended_event).to receive(:actively_registered?).with(regular_user.person).and_return(false) }
77+
78+
it { is_expected.not_to be_allowed_to(:show?) }
79+
end
80+
81+
context "with no user" do
82+
subject { policy_for(record: ended_event, user: nil) }
83+
84+
it { is_expected.not_to be_allowed_to(:show?) }
85+
end
86+
end
87+
5788
context "when event is not visible" do
5889
context "with admin user" do
5990
subject { policy_for(record: unpublished_event, user: admin_user) }
@@ -175,22 +206,27 @@ def policy_for(record: nil, user:)
175206
context "with regular user" do
176207
let(:policy) { policy_for(record: Event, user: regular_user) }
177208

178-
it "returns only visible events with open registration" do
209+
it "returns only visible events with open registration or active registrations" do
179210
scope = policy.apply_scope(Event.all, type: :active_record_relation)
180-
expect(scope.to_sql).to include('`events`.`published` = TRUE')
181-
expect(scope.to_sql).to include('registration_close_date IS NULL OR registration_close_date >=')
182-
expect(scope.to_sql).to include('LEFT OUTER JOIN `event_registrations`')
211+
sql = scope.to_sql
212+
expect(sql).to include('`events`.`published` = TRUE')
213+
expect(sql).to include("events.end_date >=")
214+
expect(sql).to include("events.registration_close_date IS NULL OR events.registration_close_date >=")
215+
expect(sql).to include("LEFT OUTER JOIN event_registrations")
216+
expect(sql).to include("event_registrations.status IN")
183217
end
184218
end
185219

186220
context "with no user" do
187221
let(:policy) { policy_for(record: Event, user: nil) }
188222

189-
it "returns only visible events with open registration" do
223+
it "excludes ended events and events with closed registration" do
190224
scope = policy.apply_scope(Event.all, type: :active_record_relation)
191-
expect(scope.to_sql).to include('`events`.`published` = TRUE')
192-
expect(scope.to_sql).to include('registration_close_date IS NULL OR registration_close_date >=')
193-
expect(scope.to_sql).not_to include('LEFT OUTER JOIN `registrants`')
225+
sql = scope.to_sql
226+
expect(sql).to include('`events`.`published` = TRUE')
227+
expect(sql).to include("events.end_date >=")
228+
expect(sql).to include("events.registration_close_date IS NULL OR events.registration_close_date >=")
229+
expect(sql).not_to include("LEFT OUTER JOIN")
194230
end
195231
end
196232
end

0 commit comments

Comments
 (0)