first commit
[step2_drupal.git] / event / ical.inc
blobd9212730b118cddc3235a760ae1b8cd34d0c0773
1 <?php
2 /* $Id: ical.inc,v 1.17 2008/12/31 00:07:23 killes Exp $ */
4 /**
5  * @file
6  * API for event import/export in iCalendar format as outlined in Internet Calendaring and Scheduling Core Object Specification
7  * http://www.ietf.org/rfc/rfc2445.txt
8  *
9  * This module is IN DEVELOPMENT and not a finished product
10  */
12 /**
13  * Turn an array of events into a valid iCalendar file
14  *
15  * @param $events
16  *   An array of associative arrays where
17  *      'start'         => Start time (Required, if no allday_start)
18  *      'end'           => End time (Optional)
19  *      'allday_start'  => Start date of all-day event in YYYYMMDD format (Required, if no start)
20  *      'allday_end'    => End date of all-day event in YYYYMMDD format (Optional)
21  *      'summary'       => Title of event (Text)
22  *      'description'   => Description of event (Text)
23  *      'location'      => Location of event (Text)
24  *      'uid'           => ID of the event for use by calendaring program.  Recommend the url of the node
25  *      'url'           => URL of event information
26  *
27  * @param $calname
28  *   Name of the calendar.  Will use site name if none is specified.
29  *
30  * @return
31  *   Text of a iCalendar file
32  */
33 function ical_export($events, $calname = NULL) {
34   $output = "BEGIN:VCALENDAR\nVERSION:2.0\n";
35   $output .= "METHOD:PUBLISH\n";
36   $output .= 'X-WR-CALNAME:'. variable_get('site_name', '') .' | '. ical_escape_text($calname) ."\n";
37   $output .= "PRODID:-//strange bird labs//Drupal iCal API//EN\n";
38   foreach ($events as $uid => $event) {
39     $output .= "BEGIN:VEVENT\n";
40     $output .= "DTSTAMP;VALUE=DATE-TIME:". gmdate("Ymd\THis\Z", time()) ."\n";
41     if (!$event['has_time']) { // all day event
42       $output .= "DTSTART;VALUE=DATE-TIME:" . event_format_date($event['start_utc'], 'custom', "Ymd\THis\Z") ."\n";
43       //If allday event, set to day after allday start
44       $end_date = event_date_later($event['start'], 1);
45       $output .= "DTEND;VALUE=DATE-TIME:" . event_format_date($end_date, 'custom', 'Ymd') ."\n";
46     }
47     else if (!empty($event['start_utc']) && !empty($event['end_utc'])) {
48       $output .= "DTSTART;VALUE=DATE-TIME:". event_format_date($event['start_utc'], 'custom', "Ymd\THis\Z") ."\n";
49       $output .= "DTEND;VALUE=DATE-TIME:". event_format_date($event['end_utc'], 'custom', "Ymd\THis\Z") ."\n";
50     }
51     else if (!empty($event['start_utc'])) {
52       $output .= "DTSTART;VALUE=DATE-TIME:". event_format_date($event['start_utc'], 'custom', "Ymd\THis\Z") ."\n";
53     }
54     $output .= "UID:". ($event['uid'] ? $event['uid'] : $uid) ."\n";
55     if (!empty($event['url'])) {
56       $output .= "URL;VALUE=URI:" . $event['url'] ."\n";
57     }
58     if (!empty($event['location'])) {
59       $output .= "LOCATION:" . ical_escape_text($event['location']) ."\n";
60     }
61     $output .= "SUMMARY:" . ical_escape_text($event['summary']) ."\n";
62     if (!empty($event['description'])) {
63       $output .= "DESCRIPTION:" . ical_escape_text($event['description']) ."\n";
64     }
65     $output .= "END:VEVENT\n";
66   }
67   $output .= "END:VCALENDAR\n";
68   return $output;
71 /**
72  * Escape #text elements for safe iCal use
73  *
74  * @param $text
75  *   Text to escape
76  *
77  * @return
78  *   Escaped text
79  *
80  */
81 function ical_escape_text($text) {
82   //$text = strip_tags($text);
83   $text = str_replace('"', '\"', $text);
84   $text = str_replace("\\", "\\\\", $text);
85   $text = str_replace(",", "\,", $text);
86   $text = str_replace(":", "\:", $text);
87   $text = str_replace(";", "\;", $text);
88   $text = str_replace("\n", "\n ", $text);
89   return $text;
92 /**
93  * Given the location of a valide iCalendar file, will return an array of event information
94  *
95  * @param $filename
96  *   Location (local or remote) of a valid iCalendar file
97  *
98  * @return
99  *   An array of associative arrays where
100  *      'start'         => start time as date array
101  *      'end'           => end time as date array
102  *      'summary'       => Title of event
103  *      'description'   => Description of event
104  *      'location'      => Location of event
105  *      'uid'           => ID of the event in calendaring program
106  *      'url'           => URL of event information                                    */
107 function ical_import($ical) {
108   $ical = explode("\n", $ical);
109   $items = array();
110 #  $ifile = @fopen($filename, "r");
111 #  if ($ifile == FALSE) exit('Invalid input file');
112   $nextline = $ical[0];
113   if (trim($nextline) != 'BEGIN:VCALENDAR') exit('Invalid calendar file:'. $nextline);
114   foreach ($ical as $line) {
115     $line = $nextline;
116     $nextline = next($ical);
117     $nextline = ereg_replace("[\r\n]", "", $nextline);
118     while (substr($nextline, 0, 1) == " ") {
119       $line .= substr($nextline, 1);
120       $nextline = next($ical);
121       $nextline = ereg_replace("[\r\n]", "", $nextline);
122     }
123     $line = trim($line);
124     switch ($line) {
125       case 'BEGIN:VEVENT':
126         unset($start_date, $start_time,
127           $end_date, $end_time,
128           $tz_dtstart, $tz_dtend,
129           $allday_start, $allday_end,
130           $uid,
131           $summary,
132           $description,
133           $url,
134           $location
135               );
136         break;
137       case 'END:VEVENT':
138         if (empty($uid)) {
139           $uid = $uid_counter;
140           $uid_counter++;
141         }
143         $items[$uid] = array('start' => $start_date .' '. $start_time,
144           'end' => $end_date .' '. $end_time,
145           'allday_start' => $allday_start,
146           'tz_start' => $tz_dtstart,
147           'tz_end' => $tz_dtend,
148           'allday_end' => $allday_end,
149           'summary' => $summary,
150           'description' => $description,
151           'location' => $location,
152           'url' => $url,
153           'uid' => $uid);
154         break;
155       default:
156         unset($field, $data, $prop_pos, $property);
157         ereg("([^:]+):(.*)", $line, $line);
158         $field = $line[1];
159         $data = $line[2];
161         $property = $field;
162         $prop_pos = strpos($property, ';');
163         if ($prop_pos !== FALSE) {
164           $property = substr($property, 0, $prop_pos);
165         }
166         $property = strtoupper($property);
168         switch ($property) {
169           case 'DTSTART':
170             $zulu_time = FALSE;
171             if (substr($data, -1) == 'Z') {
172               $zulu_time = TRUE;
173             }
174             $data = str_replace('T', '', $data);
175             $data = str_replace('Z', '', $data);
176             $field = str_replace(';VALUE=DATE-TIME', '', $field);
177             if ((preg_match("/^DTSTART;VALUE=DATE/i", $field)) || (ereg ('^([0-9]{4})([0-9]{2})([0-9]{2})$', $data))) {
178               ereg('([0-9]{4})([0-9]{2})([0-9]{2})', $data, $dtstart_check);
179               $allday_start = $data;
180               $start_date = $allday_start;
181             }
182             else {
183               if (preg_match("/^DTSTART;TZID=/i", $field)) {
184                 $tz_tmp = explode('=', $field);
185                 $tz_tmp = explode(':', $tz_tmp[1]);
186                 $check = event_zone_by_name($tz_tmp[0]);
187                 // Found TZ
188                 if ($check['name'] === $tz_tmp[0]) {
189                   $tz_dtstart = $tz_tmp[0];
190                 }
191                 else {
192                   $messages['error'][] = t('Timezone %tz not found, using default timezone.', array('%tz' => $tz_tmp[0]));
193                 }
194                 unset($check, $tz_tmp);
195               }
196               elseif ($zulu_time) {
197                 $tz_dtstart = 'Etc/GMT';
198               }
199               if (!isset($tz_dtstart)) {
200                 $tz_dtstart = event_zonelist_by_id(variable_get('date_default_timezone_id', 487));
201                 $tz_dtstart = $tz_dtstart['name'];
202               }
203               preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})/', $data, $regs);
204               $start_date = $regs[1] .'-'. $regs[2] .'-'. $regs[3];
205               $start_time = $regs[4] .':'. $regs[5] .':00';
206               unset($regs);
207             }
208             break;
209           case 'DTEND':
210             $zulu_time = FALSE;
211             if (substr($data, -1) == 'Z') {
212               $zulu_time = TRUE;
213             }
214             $data = str_replace('T', '', $data);
215             $data = str_replace('Z', '', $data);
216             $field = str_replace(';VALUE=DATE-TIME', '', $field);
217             if ((preg_match("/^DTEND;VALUE=DATE/i", $field)) || (ereg ('^([0-9]{4})([0-9]{2})([0-9]{2})$', $data))) {
218               $allday_end = $data;
219               $end_date = $allday_end;
220             }
221             else {
222               if (preg_match("/^DTEND;TZID=/i", $field)) {
223                 $tz_tmp = explode('=', $field);
224                 $tz_tmp = explode(':', $tz_tmp[1]);
225                 $check = event_zone_by_name($tz_tmp[0]);
226                 if ($check['name'] === $tz_tmp[0]) {
227                   $tz_dtend = $tz_tmp[0];
228                 }
229                 else {
230                   $messages['error'][] = t('Timezone %tz not found, using default timezone.', array('%tz' => $tz_tmp[0]));
231                 }
232                 unset($check, $tz_tmp);
233               }
234               elseif ($zulu_time) {
235                 $tz_dtend = 'Etc/GMT';
236               }
237               if (!isset($tz_dtend)) {
238                 $tz_dtend = event_zonelist_by_id(variable_get('date_default_timezone_id', 487));
239                 $tz_dtend = $tz_dtend['name'];
240               }
241               preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})/', $data, $regs);
242               $end_date = $regs[1] .'-'. $regs[2] .'-'. $regs[3];
243               $end_time = $regs[4] .':'. $regs[5] .':00';
244               unset($regs);
245             }
246             break;
247           case 'SUMMARY':
248             $summary = ical_parse_text($field, $data);
249             break;
250           case 'DESCRIPTION':
251             $description = ical_parse_text($field, $data);
252             break;
253           case 'UID':
254             $uid = $data;
255             break;
256           case 'X-WR-CALNAME':
257             $actual_calname = ical_parse_text($field, $data);
258             break;
259           case 'X-WR-TIMEZONE':
260             $calendar_tz = ical_parse_text($field, $data);
261             break;
262           case 'LOCATION':
263             $location = ical_parse_text($field, $data);
264             break;
265           case 'URL':
266             $url = $data;
267             break;
268         }
269     }
270   }
271   return $items;
274 function ical_help($path, $arg) {
275   switch ($path) {
276   case 'admin/modules#description':
277     return t('iCalendar API for Events Modules');
278     break;
279   }
283  *  escape ical separators in quoted-printable encoded code
284  */
285 function ical_quoted_printable_escaped($string) {
286   $replace = array(";" => "\;", ":" => "\:");
287   return strtr(ical_quoted_printable_encode($string), $replace);
291  *  encode text using quoted-printable standard
292  */
293 function ical_quoted_printable_encode($text, $line_max = 76) {
294   $hex       = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
295   $lines     = preg_split("/(?:\r\n|\r|\n)/", $text);
296   $eol       = "\r\n";
297   $linebreak = "=0D=0A";
298   $escape    = "=";
299   $output    = "";
301   for ($x = 0; $x < count($lines); $x++) {
302     $line     = $lines[$x];
303     $line_len = strlen($line);
304     $newline  = "";
305     for ($i = 0; $i < $line_len; $i++) {
306     $c   = substr($line, $i, 1);
307     $dec = ord($c);
308     // convert space at end of line
309     if ( ($dec == 32) && ($i == ($line_len - 1)) ) {
310       $c = $escape ."20";
311     }
312     // convert tab and special chars
313     elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) {
314       $h2 = floor($dec/16);
315       $h1 = floor($dec%16);
316       $c  = $escape . $hex["$h2"] . $hex["$h1"];
317     }
318     // see if new output line is needed
319     if ( (strlen($newline) + strlen($c)) >= $line_max ) {
320       $output .= $newline . $escape . $eol;
321       $newline = "";
322     }
323     $newline .= $c;
324     }
325     $output .= $newline;
327     // skip last line feed
328     if ($x < count($lines) - 1) $output .= $linebreak;
329   }
330   return trim($output);
334  * From date_ical_parse_text
336  * @param $field the field
337  * @param $text the text
339  * @return formatted text.
340  */
341 function ical_parse_text($field, $text) {
342   if (strstr($field, 'QUOTED-PRINTABLE')) {
343     $text = quoted_printable_decode($text);
344   }
345   // Strip line breaks within element
346   $text = str_replace(array("\r\n ", "\n ", "\r "), '', $text);
347   // Put in line breaks where encoded
348   $text = str_replace(array("\\n", "\\N"), "\n", $text);
349   // Remove other escaping
350   $text = stripslashes($text);
351   return $text;