"\\\\", ',' => '\,', ';' => '\;', "\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'); }