Unix timestamp (GMT) of start time (Required, if no allday_start) * 'end' => Unix timestamp (GMT) of end time (Optional) * 'allday_start' => Start date of all-day event in YYYYMMDD format (Required, if no start) * 'allday_end' => End date of all-day event in YYYYMMDD format (Optional) * 'summary' => Title of event (Text) * 'description' => Description of event (Text) * 'location' => Location of event (Text) * 'uid' => ID of the event for use by calendaring program. Recommend the url of the node * 'url' => URL of event information * * @param $calname * Name of the calendar. Will use site name if none is specified. * * @return * Text of a iCalendar file */ function ical_export($events, $calname = NULL) { $output .= "BEGIN:VCALENDAR\nVERSION:2.0\n"; $output .= "METHOD:PUBLISH\n"; $output .= 'X-WR-CALNAME:'. variable_get('site_name', '') .' | '. ical_escape_text($calname) ."\n"; $output .= "PRODID:-//strange bird labs//Drupal iCal API//EN\n"; foreach ($events as $uid => $event) { $output .= "BEGIN:VEVENT\n"; $output .= "DTSTAMP;VALUE=DATE:".gmdate("Ymd\THis\Z", time())."\n"; if ($event['allday_start'] && $event['allday_end']) { $output .= "DTSTART;VALUE=DATE:" . $event['allday_start'] . "\n"; $output .= "DTEND;VALUE=DATE:" . $event['allday_end'] . "\n"; } else if ($event['allday_start'] && empty($event['allday_end'])) { $output .= "DTSTART;VALUE=DATE:" . $event['allday_start'] . "\n"; $output .= "DTEND;VALUE=DATE:" . date('Ymd', strtotime($event['allday_start']) + 86400) . "\n"; //If no allday end date, set to day after allday start } else if ($event['start'] && $event['end']) { $output .= "DTSTART;VALUE=DATE-TIME:" . gmdate("Ymd\THis\Z", $event['start']) . "\n"; $output .= "DTEND;VALUE=DATE-TIME:" . gmdate("Ymd\THis\Z", $event['end']) . "\n"; } else if ($event['start']) { $output .= "DTSTART;VALUE=DATE-TIME:" . gmdate("Ymd\THis\Z", $event['start']) . "\n"; } $output .= "UID:" . ($event['uid'] ? $event['uid'] : $uid) . "\n"; if ($event['url']) $output .= "URL;VALUE=URI:" . $event['url'] . "\n"; if ($event['location']) $output .= "LOCATION:" . ical_escape_text($event['location']) . "\n"; $output .= "SUMMARY:" . ical_escape_text($event['summary']) . "\n"; if ($event['description']) $output .= "DESCRIPTION:" . ical_escape_text($event['description']) . "\n"; $output .= "END:VEVENT\n"; } $output .= "END:VCALENDAR\n"; return $output; } /** * Escape #text elements for safe iCal use * * @param $text * Text to escape * * @return * Escaped text * */ function ical_escape_text($text) { //$text = strip_tags($text); $text = str_replace('"', '\"', $text); $text = str_replace("\\", "\\\\", $text); $text = str_replace(",", "\,", $text); $text = str_replace(":", "\:", $text); $text = str_replace(";", "\;", $text); $text = str_replace("\n", "\n ", $text); return $text; } /** * Given the location of a valide iCalendar file, will return an array of event information * * @param $filename * Location (local or remote) of a valid iCalendar file * * @return * An array of associative arrays where * 'start' => Unix timestamp (GMT) of start time * 'end' => Unix timestamp (GMT) of end time * 'allday_start' => Start date of all-day event in YYYYMMDD format * 'allday_end' => End date of all-day event in YYYYMMDD format * 'summary' => Title of event * 'description' => Description of event * 'location' => Location of event * 'uid' => ID of the event in calendaring program * 'url' => URL of event information * */ function ical_import($filename) { $items = array(); $ifile = @fopen($filename, "r"); if ($ifile == FALSE) exit('Invalid input file'); $nextline = fgets($ifile, 1024); if (trim($nextline) != 'BEGIN:VCALENDAR') exit('Invalid calendar file:' . $filename); while (!feof($ifile)) { $line = $nextline; $nextline = fgets($ifile, 1024); $nextline = ereg_replace("[\r\n]", "", $nextline); while (substr($nextline, 0, 1) == " ") { $line = $line . substr($nextline, 1); $nextline = fgets($ifile, 1024); $nextline = ereg_replace("[\r\n]", "", $nextline); } $line = trim($line); switch ($line) { case 'BEGIN:VEVENT': unset($start_unixtime, $start_date, $start_time, $end_unixtime, $end_date, $end_time, $allday_start, $allday_end, $the_duration, $uid, $summary, $description, $url, $location ); break; case 'END:VEVENT': if (empty($uid)) { $uid = $uid_counter; $uid_counter++; } if (empty($end_unixtime) && isset($the_duration)) { $end_unixtime = $start_unixtime + $the_duration; $end_time = date ('Hi', $end_unixtime); } $items[$uid] = array('start' => $start_unixtime, 'end' => $end_unixtime, 'allday_start' => $allday_start, 'allday_end' => $allday_end, 'summary' => $summary, 'description' => $description, 'location' => $location, 'url' => $url, 'uid' => $uid); break; default: unset ($field, $data, $prop_pos, $property); ereg ("([^:]+):(.*)", $line, $line); $field = $line[1]; $data = $line[2]; $property = $field; $prop_pos = strpos($property,';'); if ($prop_pos !== false) $property = substr($property,0,$prop_pos); $property = strtoupper($property); switch ($property) { case 'DTSTART': $zulu_time = false; if (substr($data,-1) == 'Z') $zulu_time = true; $data = str_replace('T', '', $data); $data = str_replace('Z', '', $data); $field = str_replace(';VALUE=DATE-TIME', '', $field); if ((preg_match("/^DTSTART;VALUE=DATE/i", $field)) || (ereg ('^([0-9]{4})([0-9]{2})([0-9]{2})$', $data))) { ereg ('([0-9]{4})([0-9]{2})([0-9]{2})', $data, $dtstart_check); $allday_start = $data; $start_date = $allday_start; $start_unixtime = strtotime($data); } else { if (preg_match("/^DTSTART;TZID=/i", $field)) { $tz_tmp = explode('=', $field); $tz_dtstart = $tz_tmp[1]; unset($tz_tmp); } elseif ($zulu_time) { $tz_dtstart = 'GMT'; } preg_match ('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})/', $data, $regs); $start_date = $regs[1] . $regs[2] . $regs[3]; $start_time = $regs[4] . $regs[5]; $start_unixtime = mktime($regs[4], $regs[5], 0, $regs[2], $regs[3], $regs[1]); $dlst = date('I', $start_unixtime); $server_offset_tmp = _ical_chooseOffset($start_unixtime, 'Same as Server'); if (isset($tz_dtstart)) { if ($tz = _ical_tz($tz_dtstart)) { $offset_tmp = date('I', $start_unixtime) ? $tz->offset_dst : $tz->offset; } else { $offset_tmp = '+0000'; } } else if (isset($calendar_tz)) { if ($tz = _ical_tz($calendar_tz)) { $offset_tmp = date('I', $start_unixtime) ? $tz->offset_dst : $tz->offset; } else { $offset_tmp = '+0000'; } } else { $offset_tmp = $server_offset_tmp; } $start_unixtime = _ical_calcTime($offset_tmp, $server_offset_tmp, $start_unixtime); $start_date = date('Ymd', $start_unixtime); $start_time = date('Hi', $start_unixtime); unset($server_offset_tmp, $offset_tmp, $tz_dtstart); } break; case 'DTEND': $zulu_time = false; if (substr($data,-1) == 'Z') $zulu_time = true; $data = str_replace('T', '', $data); $data = str_replace('Z', '', $data); $field = str_replace(';VALUE=DATE-TIME', '', $field); if ((preg_match("/^DTEND;VALUE=DATE/i", $field)) || (ereg ('^([0-9]{4})([0-9]{2})([0-9]{2})$', $data))) { $allday_end = $data; $end_date = $allday_end; $end_unixtime = strtotime($data); } else { if (preg_match("/^DTEND;TZID=/i", $field)) { $tz_tmp = explode('=', $field); $tz_dtend = $tz_tmp[1]; unset($tz_tmp); } elseif ($zulu_time) { $tz_dtend = 'GMT'; } preg_match ('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})/', $data, $regs); $end_date = $regs[1] . $regs[2] . $regs[3]; $end_time = $regs[4] . $regs[5]; $end_unixtime = mktime($regs[4], $regs[5], 0, $regs[2], $regs[3], $regs[1]); $server_offset_tmp = _ical_chooseOffset($end_unixtime, 'Same as Server'); if (isset($tz_dtend)) { if ($tz = _ical_tz($tz_dtend)) { $offset_tmp = date('I', $end_unixtime) ? $tz->offset_dst : $tz->offset; } else { $offset_tmp = '+0000'; } } else if (isset($calendar_tz)) { if ($tz = _ical_tz($calendar_tz)) { $offset_tmp = date('I', $end_unixtime) ? $tz->offset_dst : $tz->offset; } else { $offset_tmp = '+0000'; } } else { $offset_tmp = $server_offset_tmp; } $end_unixtime = _ical_calcTime($offset_tmp, $server_offset_tmp, $end_unixtime); $end_date = date('Ymd', $end_unixtime); $end_time = date('Hi', $end_unixtime); unset($server_offset_tmp, $offset_tmp, $tz_dtend); } break; case 'DURATION': if (!stristr($field, '=DURATION')) { ereg ('^P([0-9]{1,2}[W])?([0-9]{1,2}[D])?([T]{0,1})?([0-9]{1,2}[H])?([0-9]{1,2}[M])?([0-9]{1,2}[S])?', $data, $duration); $weeks = str_replace('W', '', $duration[1]); $days = str_replace('D', '', $duration[2]); $hours = str_replace('H', '', $duration[4]); $minutes = str_replace('M', '', $duration[5]); $seconds = str_replace('S', '', $duration[6]); $the_duration = ($weeks * 60 * 60 * 24 * 7) + ($days * 60 * 60 * 24) + ($hours * 60 * 60) + ($minutes * 60) + ($seconds); } break; case 'SUMMARY': $summary = $data; break; case 'DESCRIPTION': $description = $data; break; case 'UID': $uid = $data; break; case 'X-WR-CALNAME': $actual_calname = $data; break; case 'X-WR-TIMEZONE': $calendar_tz = $data; break; case 'LOCATION': $location = $data; break; case 'URL': $url = $data; break; } } } return $items; } function ical_help($section) { switch ($section) { case 'admin/modules#description': return t('iCalendar API for Events Modules'); break; } } function _ical_tz($tz) { include_once(drupal_get_path('module', 'event') .'/event_timezones.inc'); foreach (event_get_timezones() as $delta => $zone) { if ($tz == $zone['timezone']); return (object) $zone; } } function _ical_chooseOffset($time, $timezone) { if (!isset($timezone)) $timezone = ''; switch ($timezone) { case '': $offset = 'none'; break; case 'Same as Server': $offset = date('O', $time); break; default: if ($tz = _ical_tz($timezone)) { $offset = date('I', $time) ? $tz->offset_dst : $tz->offset; } else { $offset = '+0000'; } } return $offset; } function _ical_calcTime($have, $want, $time) { if ($have == 'none' || $want == 'none') return $time; $have_secs = _ical_calcOffset($have); $want_secs = _ical_calcOffset($want); $diff = $want_secs - $have_secs; $time += $diff; return $time; } function _ical_calcOffset($offset_str) { $sign = substr($offset_str, 0, 1); $hours = substr($offset_str, 1, 2); $mins = substr($offset_str, 3, 2); $secs = ((int)$hours * 3600) + ((int)$mins * 60); if ($sign == '-') $secs = 0 - $secs; return $secs; } /** * escape ical separators in quoted-printable encoded code */ function ical_quoted_printable_escaped($string) { $replace = array(";" => "\;", ":" => "\:"); return strtr(ical_quoted_printable_encode($string), $replace); } /** * encode text using quoted-printable standard */ function ical_quoted_printable_encode($text, $line_max = 76) { $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); $lines = preg_split("/(?:\r\n|\r|\n)/", $text); $eol = "\r\n"; $linebreak = "=0D=0A"; $escape = "="; $output = ""; for ($x = 0; $x < count($lines); $x++) { $line = $lines[$x]; $line_len = strlen($line); $newline = ""; for ($i = 0; $i < $line_len; $i++) { $c = substr($line, $i, 1); $dec = ord($c); // convert space at end of line if ( ($dec == 32) && ($i == ($line_len - 1)) ) { $c = $escape ."20"; } // convert tab and special chars elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { $h2 = floor($dec/16); $h1 = floor($dec%16); $c = $escape . $hex["$h2"] . $hex["$h1"]; } // see if new output line is needed if ( (strlen($newline) + strlen($c)) >= $line_max ) { $output .= $newline . $escape . $eol; $newline = ""; } $newline .= $c; } $output .= $newline; // skip last line feed if ($x < count($lines) - 1) $output .= $linebreak; } return trim($output); }