0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 05:42:36 +08:00
discourse/lib/ical_encoder.rb
Bannon Tanner f5203dfcca
FIX: Preserve URI delimiters in ICS URL properties, removing TEXT escaping (#42107)
## Summary

Correctly emit `URL` properties in ICS export without RFC 5545 TEXT
escaping by introducing `IcalEncoder.encode_uri`, which decodes HTML
entities, strips CR/LF characters, and leaves valid URI delimiters such
as commas and semicolons intact. This change is applied to both the
Discourse Events calendar feed and the user bookmarks ICS feed; all
textual properties (`SUMMARY`, `LOCATION`, `DESCRIPTION`) continue to
use the existing TEXT encoder.

## Source

- Patch Triage: https://patch.discourse.org/patch-triage/1547

Co-authored-by: discourse-patch-triage
<272280883+discourse-patch-triage[bot]@users.noreply.github.com>
2026-07-28 14:53:16 -05:00

32 lines
1.1 KiB
Ruby
Vendored

# frozen_string_literal: true
module IcalEncoder
SANITIZER = Rails::Html::FullSanitizer.new
# Encodes a string for use in iCalendar text fields (SUMMARY, DESCRIPTION, LOCATION).
# Strips HTML tags, decodes HTML entities, and escapes special characters per RFC 5545.
def self.encode(text)
return "" if text.blank?
text = SANITIZER.sanitize(text)
text = CGI.unescapeHTML(text)
text
.gsub("\\", "\\\\\\\\")
.gsub(",", "\\,")
.gsub(";", "\\;")
.gsub("\r\n", "\\n")
.gsub("\n", "\\n")
.html_safe
end
# Encodes a URI for iCalendar URL properties (RFC 5545 §3.8.4.6).
# URL values are URIs per RFC 3986, so comma and semicolon (valid sub-delimiters)
# must not be TEXT-escaped. Entities are decoded first, then CR/LF stripped —
# order matters: stripping CR/LF after decoding neutralizes entity-encoded
# newlines (e.g. `&#13;&#10;`) that would otherwise inject additional ICS
# properties on the following line.
def self.encode_uri(uri)
return "" if uri.blank?
CGI.unescapeHTML(uri.to_s).delete("\r\n").html_safe
end
end