Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions app/policies/event_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ def index?
end

def show?
admin? || record.publicly_visible? || (authenticated? && record.published?)
return true if admin?

if record.ended?
authenticated? && record.published? && record.actively_registered?(user.person)
else
record.publicly_visible? || (authenticated? && record.published?)
end
end

def register?
Expand Down Expand Up @@ -39,15 +45,29 @@ def owner?

relation_scope do |relation|
next relation if admin?
if authenticated? # logged in users can see events they are registered for even if registration is closed
relation.left_outer_joins(:registrants)
.published
.where("registration_close_date IS NULL OR registration_close_date >= ? OR people.id = ?", Time.current, user.person_id)
.distinct

if authenticated?
relation
.joins(
"LEFT OUTER JOIN event_registrations
ON event_registrations.event_id = events.id
AND event_registrations.status IN ('registered', 'attended', 'incomplete_attendance')
LEFT OUTER JOIN people
ON people.id = event_registrations.registrant_id"
)
.published
.where(
"(events.end_date >= :now AND (events.registration_close_date IS NULL OR events.registration_close_date >= :now))
OR people.id = :person_id",
now: Time.current,
person_id: user.person_id
)
.distinct
else
relation.publicly_visible
.published
.where("registration_close_date IS NULL OR registration_close_date >= ?", Time.current)
.where("events.end_date >= ?", Time.current)
.where("events.registration_close_date IS NULL OR events.registration_close_date >= ?", Time.current)
end
end
end
11 changes: 9 additions & 2 deletions app/views/events/_card.html.erb
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<% event = event.decorate %>
<% ended = event.ended? %>

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

<% if registered %>
<% if ended %>
<span class="text-sm text-gray-500 italic">Event ended</span>
<% end %>
<% registration = event.active_registration_for(current_user.person) %>
<% if registration&.slug.present? %>
<%= link_to "View registration", registration_ticket_path(registration.slug),
Expand Down
16 changes: 7 additions & 9 deletions app/views/events/_registration_section.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@
<% slug_registered = slug_registration&.active? %>
<% slug_cancelled = slug_registration.present? && slug_registration.status == "cancelled" %>
<%= tag.div id: dom_id(event.object, "registration_section_#{instance}"), class: "registration-section flex flex-col items-center gap-4 mb-6" do %>
<!-- BUTTONS -->
<div class="flex flex-wrap items-center justify-center gap-3">
<div class="flex flex-col items-center justify-center gap-3">
<% if event.ended? %>
<span class="text-2xl font-bold text-blue-400 italic">Event ended</span>
<span class="text-2xl font-bold text-gray-400 italic">Event ended</span>
<% if allowed_to?(:manage?, event) %>
<%= button_to "Register",
event_registrant_registration_path(event_id: event),
class: "admin-only bg-blue-100 btn btn-primary-outline" %>
<% end %>
<% elsif registered %>
<% elsif slug_cancelled && event.registerable? %>
<%= button_to "Register again", registration_reactivate_path(slug_registration.slug),
class: "text-sm text-gray-500 hover:text-blue-600 underline bg-transparent border-0 cursor-pointer p-0" %>
<% elsif !registered && !slug_registered %>
<% if !event.published? %>
<% if slug_cancelled && event.registerable? %>
<%= button_to "Register again", registration_reactivate_path(slug_registration.slug),
class: "text-sm text-gray-500 hover:text-blue-600 underline bg-transparent border-0 cursor-pointer p-0" %>
<% elsif !event.published? %>
<span class="admin-only bg-blue-100 p-1 text-sm text-gray-500 italic">Not published</span>
<% elsif event.registerable? %>
<% if user_signed_in? %>
Expand Down Expand Up @@ -50,7 +48,7 @@
<% end %>
</div>

<% if registered || slug_registered %>
<% if (registered || slug_registered) && !event.ended? %>
<!-- CALENDAR LINKS -->
<div class="flex flex-col items-center mt-4">
<p class="text-xs font-bold text-gray-400 uppercase tracking-wide mb-2">
Expand Down
6 changes: 6 additions & 0 deletions spec/factories/events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
publicly_featured { true }
end

trait :ended do
start_date { 14.days.ago }
end_date { 12.days.ago }
registration_close_date { 13.days.ago }
end

trait :registration_closed do
registration_close_date { 13.days.ago }
end
Expand Down
52 changes: 44 additions & 8 deletions spec/policies/event_policy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
let(:published_event) { build_stubbed :event, :published }
let(:public_event) { build_stubbed :event, publicly_visible: true }
let(:unpublished_event) { build_stubbed :event, :unpublished }
let(:ended_event) { build_stubbed :event, :published, :ended }
let(:open_registration_event) { build_stubbed :event, registration_close_date: 1.day.from_now }
let(:closed_registration_event) { build_stubbed :event, registration_close_date: 1.day.ago }

Expand Down Expand Up @@ -54,6 +55,36 @@ def policy_for(record: nil, user:)
end
end

context "when event has ended" do
context "with admin user" do
subject { policy_for(record: ended_event, user: admin_user) }

it { is_expected.to be_allowed_to(:show?) }
end

context "with registered user" do
subject { policy_for(record: ended_event, user: regular_user) }

before { allow(ended_event).to receive(:actively_registered?).with(regular_user.person).and_return(true) }

it { is_expected.to be_allowed_to(:show?) }
end

context "with unregistered user" do
subject { policy_for(record: ended_event, user: regular_user) }

before { allow(ended_event).to receive(:actively_registered?).with(regular_user.person).and_return(false) }

it { is_expected.not_to be_allowed_to(:show?) }
end

context "with no user" do
subject { policy_for(record: ended_event, user: nil) }

it { is_expected.not_to be_allowed_to(:show?) }
end
end

context "when event is not visible" do
context "with admin user" do
subject { policy_for(record: unpublished_event, user: admin_user) }
Expand Down Expand Up @@ -175,22 +206,27 @@ def policy_for(record: nil, user:)
context "with regular user" do
let(:policy) { policy_for(record: Event, user: regular_user) }

it "returns only visible events with open registration" do
it "returns only visible events with open registration or active registrations" do
scope = policy.apply_scope(Event.all, type: :active_record_relation)
expect(scope.to_sql).to include('`events`.`published` = TRUE')
expect(scope.to_sql).to include('registration_close_date IS NULL OR registration_close_date >=')
expect(scope.to_sql).to include('LEFT OUTER JOIN `event_registrations`')
sql = scope.to_sql
expect(sql).to include('`events`.`published` = TRUE')
expect(sql).to include("events.end_date >=")
expect(sql).to include("events.registration_close_date IS NULL OR events.registration_close_date >=")
expect(sql).to include("LEFT OUTER JOIN event_registrations")
expect(sql).to include("event_registrations.status IN")
end
end

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

it "returns only visible events with open registration" do
it "excludes ended events and events with closed registration" do
scope = policy.apply_scope(Event.all, type: :active_record_relation)
expect(scope.to_sql).to include('`events`.`published` = TRUE')
expect(scope.to_sql).to include('registration_close_date IS NULL OR registration_close_date >=')
expect(scope.to_sql).not_to include('LEFT OUTER JOIN `registrants`')
sql = scope.to_sql
expect(sql).to include('`events`.`published` = TRUE')
expect(sql).to include("events.end_date >=")
expect(sql).to include("events.registration_close_date IS NULL OR events.registration_close_date >=")
expect(sql).not_to include("LEFT OUTER JOIN")
end
end
end
Expand Down
37 changes: 34 additions & 3 deletions spec/requests/events_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
end

context "when user time_zone is set" do
# 19:00 UTC = 12:00 noon PT = 15:00 (3 pm) ET (June 15, 2025 with DST)
let(:utc_start) { Time.utc(2025, 6, 15, 19, 0, 0) }
let(:utc_end) { Time.utc(2025, 6, 15, 20, 0, 0) }
# 19:00 UTC = 12:00 noon PT = 15:00 (3 pm) ET (June 15, 2031 with DST)
let(:utc_start) { Time.utc(2031, 6, 15, 19, 0, 0) }
let(:utc_end) { Time.utc(2031, 6, 15, 20, 0, 0) }
let!(:event_with_fixed_times) do
create(:event, :published,
start_date: utc_start,
Expand Down Expand Up @@ -63,6 +63,37 @@
end
end

describe "GET /show" do
context "when event has ended" do
let(:ended_event) { create(:event, :published, :ended) }

it "allows admin to view" do
sign_in admin
get event_path(ended_event)
expect(response).to have_http_status(:ok)
end

it "allows registered user to view" do
user_with_person = create(:user, :with_person)
sign_in user_with_person
create(:event_registration, event: ended_event, registrant: user_with_person.person, status: "registered")
get event_path(ended_event)
expect(response).to have_http_status(:ok)
end

it "redirects unregistered user" do
sign_in user
get event_path(ended_event)
expect(response).to redirect_to(root_path)
end

it "redirects unauthenticated user" do
get event_path(ended_event)
expect(response).to redirect_to(root_path)
end
end
end

describe "GET /new" do
context "as admin" do
it "renders successfully" do
Expand Down
32 changes: 32 additions & 0 deletions spec/system/events_show_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@
context "event ended" do
before do
event.update!(end_date: 1.day.ago)
create(:event_registration, event: event, registrant: user.person, status: "registered")
end

it "shows 'Event ended' and hides registration buttons" do
Expand All @@ -181,6 +182,37 @@
expect(page).not_to have_button("Register")
expect(page).not_to have_button("De-register")
end

it "shows 'View Registration' for registered user" do
sign_in(user)
visit event_path(event)

expect(page).to have_text("Event ended")
expect(page).to have_text("View Registration")
end

it "hides calendar links" do
sign_in(user)
visit event_path(event)

expect(page).not_to have_text("Add to Your Calendar")
end

it "blocks unregistered users" do
other_user = create(:user, :with_person)
sign_in(other_user)
visit event_path(event)

expect(page).to have_current_path(root_path)
end

it "allows admin to view" do
sign_in(admin)
visit event_path(event)

expect(page).to have_text("Event ended")
expect(page).to have_text("My Event")
end
end

context "guest with reg slug param" do
Expand Down