Skip to content

Commit 04c5a6b

Browse files
Backend/requests: Fix calendar event cancellation (#8635)
1 parent cb70b3a commit 04c5a6b

2 files changed

Lines changed: 17 additions & 6 deletions

File tree

app/backend/src/couchers/email/calendar_events.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ def create_host_request_ics(
2525
host_request: HostRequest, other_name: str, hosting: bool, loc_context: LocalizationContext
2626
) -> str:
2727
event = create_host_request_event(host_request, other_name, hosting, loc_context)
28-
return event_to_ics(event, loc_context)
28+
29+
# METHOD:PUBLISH means this is part of a stream of calendar event information.
30+
# It allows for later cancellation, and doesn't expose accept/decline functionality.
31+
return event_to_ics(event, "PUBLISH", loc_context)
2932

3033

3134
def create_host_request_event(
@@ -36,6 +39,9 @@ def create_host_request_event(
3639
event = Event()
3740
event.uid = get_host_request_event_uid(host_request.host_request_id)
3841

42+
# Explicitly allow later sequencing of a cancellation with SEQUENCE:1
43+
event.extra.append(ContentLine(name="SEQUENCE", value="0"))
44+
3945
if hosting:
4046
event.name = get_emails_i18next().localize(
4147
"calendar_events.host_requests.title_host", loc_context.locale, {"name": other_name}
@@ -76,17 +82,20 @@ def create_host_request_cancellation_ics(
7682
)
7783
event.status = "CANCELLED"
7884

79-
# Cancellation is sequenced after creation (which defaults to sequence number 0)
85+
# Sequence cancellation is after creation (which uses SEQUENCE:0)
8086
event.extra.append(ContentLine(name="SEQUENCE", value="1"))
8187

82-
return event_to_ics(event, loc_context)
88+
# METHOD:PUBLISH means this is part of a stream of calendar event information.
89+
# Gmail™ will immediately remove the event from the user's calendar.
90+
# METHOD:CANCEL might leave the event in cancelled state or not work.
91+
return event_to_ics(event, "PUBLISH", loc_context)
8392

8493

85-
def event_to_ics(event: Event, loc_context: LocalizationContext) -> str:
94+
def event_to_ics(event: Event, method: str | None, loc_context: LocalizationContext) -> str:
8695
# PRODID is mandatory and generally follows "-//[Organization]//[Product Name]//[Language]"
8796
calendar = Calendar(creator=f"-//Couchers.org//Couchers//{loc_context.locale.upper()}")
88-
if event.status == "CANCELLED":
89-
calendar.method = "CANCEL"
97+
if method:
98+
calendar.method = method
9099
calendar.events.add(event)
91100
return cast(str, calendar.serialize())
92101

app/backend/src/tests/test_calendar_events.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def test_ics_content():
4545
VERSION:2.0
4646
PRODID:-//Couchers.org//Couchers//EN
4747
BEGIN:VEVENT
48+
SEQUENCE:0
4849
DTSTART;VALUE=DATE:20000101
4950
DTEND;VALUE=DATE:20000103
5051
DESCRIPTION:<stripped>/42
@@ -53,6 +54,7 @@ def test_ics_content():
5354
UID:host_request.42@<stripped>
5455
URL:<stripped>/42
5556
END:VEVENT
57+
METHOD:PUBLISH
5658
END:VCALENDAR
5759
""".strip()
5860
)

0 commit comments

Comments
 (0)