diff options
| author | Jonas Kümmerlin <jonas@kuemmerlin.eu> | 2024-01-27 13:05:41 +0100 |
|---|---|---|
| committer | Jonas Kümmerlin <jonas@kuemmerlin.eu> | 2024-01-27 13:05:41 +0100 |
| commit | 907f39db4ab00de43016646660d9d1ca9c73d1f9 (patch) | |
| tree | 49642a7e9ab374c3eab2ae91b12a24fdf4a21f62 /ical-feed.php | |
Diffstat (limited to 'ical-feed.php')
| -rw-r--r-- | ical-feed.php | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/ical-feed.php b/ical-feed.php new file mode 100644 index 0000000..ee1eeba --- /dev/null +++ b/ical-feed.php @@ -0,0 +1,138 @@ +<?php +/* DISPLAY - iCal export + * + */ + +function communievents_escape_ical_text($text) { + return strtr($text, array( + "\\" => "\\\\", + ',' => '\,', + ';' => '\;', + "\r\n" => "\\n", // spec says line breaks need to be \n only + "\n" => "\\n" + )); +} + +// FIXME! this assumes valid utf-8 +function communievents_fold_ical_line_helper($text, $index, $maxlength) { + $length = 0; + $i = $index; + + while ($i < strlen($text) && $length < $maxlength) { + $c = ord($text[$i]); + if ($c < 128) { + $clen = 1; + } else if ($c < 224) { + $clen = 2; + } else if ($c < 240) { + $clen = 3; + } else { + $clen = 4; + } + + if ($length + $clen > $maxlength) + break; + + $i += $clen; + $length += $clen; + } + + return substr($text, $index, $length); +} + +function communievents_fold_ical_text($text) { + //XXX: the iCal spec wants the lines to be at most 75 octets, + // but we must not split UTF-8 because some readers (notably + // thunderbird) choke on it, even though the RFC says you + // should handle it gracefully :-( + + if (strlen($text) <= 75) { + return $text; + } else { + $text_arr = array(); + $text_arr[0] = communievents_fold_ical_line_helper($text, 0, 75); + $i = strlen($text_arr[0]); + while ($i < strlen($text)) { + $s = communievents_fold_ical_line_helper($text, $i, 74); + $i += strlen($s); + $text_arr[] = $s; + } + + return join("\r\n ", $text_arr); + } +} + +function communievents_write_ical_line($field, $text) { + echo communievents_fold_ical_text("$field:" . communievents_escape_ical_text($text)) . "\r\n"; +} + +function communievents_print_ical_feed() { + global $wpdb; + + $tz = new DateTimeZone(CommuniApi::TIMEZONE); + $tz_utc = new DateTimeZone('UTC'); + $now_utc = new DateTime('now', $tz_utc); + $dtstamp = $now_utc->format('Ymd\THis\Z'); + + header('Content-Type: text/calendar'); + header('Content-Disposition: attachment; filename="communievents.ics"'); + + communievents_write_ical_line('BEGIN', 'VCALENDAR'); + communievents_write_ical_line('VERSION', '2.0'); + communievents_write_ical_line('PRODID', '-//' . get_bloginfo('name') . '//NONSGML Events//EN'); + communievents_write_ical_line('CALSCALE', 'GREGORIAN'); + communievents_write_ical_line('X-WR-TIMEZONE', CommuniApi::TIMEZONE); + + $table_name = $wpdb->prefix . 'communievents_events'; + + $list = $wpdb->get_results("SELECT * FROM $table_name WHERE official=1 ORDER BY start"); + + if ($list) { + foreach ($list as $event) { + $start_dt = new DateTime($event->start, $tz); + $end_dt = new DateTime($event->end, $tz); + + communievents_write_ical_line('BEGIN', 'VEVENT'); + + communievents_write_ical_line('UID', 'communi-event-' . $event->id . '-' . $start_dt->format('U')); + communievents_write_ical_line('STATUS', 'CONFIRMED'); + communievents_write_ical_line('DTSTAMP', $dtstamp); + if ($event->allday) { + communievents_write_ical_line('DTSTART;VALUE=DATE', $start_dt->format('Ymd')); + if ($end_dt->format('Ymd') != $start_dt->format('Ymd')) { + // end is non-inclusive! move it into the next day + $end_dt->modify('+1 minute'); + communievents_write_ical_line('DTEND;VALUE=DATE', $end_dt->format('Ymd')); + } + } else { + communievents_write_ical_line('DTSTART', $start_dt->format('Ymd\THis')); + if ($event->end != $event->start) { + communievents_write_ical_line('DTEND', $end_dt->format('Ymd\THis')); + } + } + + communievents_write_ical_line('SUMMARY', $event->title); + + if (!empty($event->description)) { + communievents_write_ical_line('DESCRIPTION', html_entity_decode(strip_tags($event->description), ENT_QUOTES|ENT_SUBSTITUTE|ENT_HTML5, 'UTF-8')); + communievents_write_ical_line('X-ALT-DESC;FMTTYPE=text/html', $event->description); + } + + if (!empty($event->location)) { + communievents_write_ical_line('LOCATION', $event->location); + } + + communievents_write_ical_line('URL;VALUE=URI', $event->url); + + communievents_write_ical_line('END', 'VEVENT'); + } + } + + communievents_write_ical_line('END', 'VCALENDAR'); +} + +function communievents_setup_ical_feed() { + add_feed('communievents-ics', 'communievents_print_ical_feed'); + // FIXME: emklahr.de compat HACK + add_feed('eo-events', 'communievents_print_ical_feed'); +} |
