Replace deprecated split() calls.
[awl.git] / inc / vEvent.php
blob9ac90487a636d1fc7ff059c345149eef6f54cff3
1 <?php
2 /**
3 * A Class for handling vEvent data
5 * @package awl
6 * @subpackage iCalendar
7 * @author Andrew McMillan <andrew@mcmillan.net.nz>
8 * @copyright Catalyst IT Ltd
9 * @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
12 if ( class_defined('vEvent') ) return true;
14 /**
15 * A Class for handling Events on a calendar
17 * @package awl
19 class vEvent {
20 /**#@+
21 * @access private
24 /**
25 * List of participants in this event
26 * @var participants array
28 var $participants = array();
30 /**
31 * An array of arbitrary properties
32 * @var properties array
34 var $properties;
36 /**
37 * The typical location name for the standard timezone such as "Pacific/Auckland"
38 * @var tz_locn string
40 var $tz_locn;
42 /**
43 * The type of iCalendar data VEVENT/VTODO
44 * @var type string
46 var $type;
48 /**#@-*/
50 /**
51 * The constructor takes an array of args. If there is an element called 'vevent'
52 * then that will be parsed into the vEvent object. Otherwise the array elements
53 * are converted into properties of the vEvent object directly.
55 function vEvent( $args ) {
56 global $c;
58 // Probably a good idea to always have values for these things...
59 if ( isset($c->local_tzid ) ) $this->properties['tz_id'] = $c->local_tzid;
60 // $this->properties['dtstamp'] = date('Ymd\THis');
61 // $this->properties['last-modified'] = $this->properties['dtstamp'];
62 // $this->properties['sequence'] = 1;
63 // $this->properties['uid'] = sprintf( "%s@%s", time() * 1000 + rand(0,1000), $c->domain_name);
65 if ( !isset($args) || !is_array($args) ) return;
67 if ( isset($args['vevent']) ) {
68 $this->BuildFromText($args['vevent']);
69 $this->DealWithTimeZones();
70 return;
73 foreach( $args AS $k => $v ) {
74 $this->properties[strtoupper($k)] = $v;
78 /**
79 * Build the vEvent object from a text string which is a single VEVENT
81 * @var vevent string
83 function BuildFromText( $vevent ) {
84 // $vevent = preg_replace('/[\r\n]+ /', ' ', $vevent );
85 // $lines = preg_split('/[\r\n]+/', $vevent );
86 // According to RFC2445 we should always end with CRLF, but the CalDAV spec says
87 // that normalising XML parses often muck with it and may remove the CR.
88 $vevent = preg_replace('/\r?\n /', '', $vevent );
89 $lines = preg_split('/\r?\n/', $vevent );
90 $properties = array();
92 $vtimezone = "";
93 $state = 0;
94 foreach( $lines AS $k => $v ) {
95 dbg_error_log( "vEvent", "LINE %03d: >>>%s<<<", $k, $v );
97 switch( $state ) {
98 case 0:
99 if ( $v == 'BEGIN:VEVENT' ) {
100 $state = $v;
101 $this->type = 'VEVENT';
103 else if ( $v == 'BEGIN:VTODO' ) {
104 $state = $v;
105 $this->type = 'VTODO';
107 else if ( $v == 'BEGIN:VTIMEZONE' ) $state = $v;
108 break;
110 case 'BEGIN:VEVENT':
111 if ( $v == 'END:VEVENT' ) $state = 0;
112 break;
114 case 'BEGIN:VTODO':
115 if ( $v == 'END:VTODO' ) $state = 0;
116 break;
118 case 'BEGIN:VTIMEZONE':
119 if ( $v == 'END:VTIMEZONE' ) {
120 $state = 0;
121 $vtimezone .= $v;
123 break;
126 if ( ($state == 'BEGIN:VEVENT' || $state == 'BEGIN:VTODO') && $state != $v ) {
127 list( $property, $value ) = explode(':', $v, 2 );
128 if ( strpos( $property, ';' ) > 0 ) {
129 $parameterlist = explode(';', $v );
130 $property = array_shift($parameterlist);
131 foreach( $parameterlist AS $pk => $pv ) {
132 if ( preg_match('/^TZID=(.*)$/', $pv, $matches) ) {
133 $properties['TZID'] = $tz_id;
135 elseif ( $pv == "VALUE=DATE" ) {
136 $value .= 'T000000';
140 $properties[strtoupper($property)] = $value;
142 if ( $state == 'BEGIN:VTIMEZONE' ) {
143 $vtimezone .= $v . "\n";
144 @list( $parameter, $value ) = explode(':', $v );
145 if ( !isset($this->tz_locn) && $parameter == 'X-LIC-LOCATION' ) {
146 $this->tz_locn = $value;
151 if ( $vtimezone != "" ) {
152 $properties['VTIMEZONE'] = $vtimezone;
155 $this->properties = &$properties;
160 * Do what must be done with time zones from on file. Attempt to turn
161 * them into something that PostgreSQL can understand...
163 function DealWithTimeZones() {
164 if ( isset($c->save_time_zone_defs) ) {
165 $qry = new PgQuery( "SELECT tz_locn FROM time_zone WHERE tz_id = ?;", $this->properties['TZID'] );
166 if ( $qry->Exec('vEvent') && $qry->rows() == 1 ) {
167 $row = $qry->Fetch();
168 $this->tz_locn = $row->tz_locn;
172 if ( !isset($this->tz_locn) && isset($this->properties['TZID']) ) {
173 // In case there was no X-LIC-LOCATION defined, let's hope there is something in the TZID
174 $this->tz_locn = preg_replace('/^.*([a-z]+\/[a-z]+)$/i','$1',$this->properties['TZID'] );
177 if ( isset($c->save_time_zone_defs) && $qry->rows() != 1 ) {
178 $qry2 = new PgQuery( "INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES( ?, ?, ? );",
179 $this->properties['TZID'], $this->tz_locn, $this->properties['VTIMEZONE'] );
180 $qry2->Exec("vEvent");
186 * Get the value of a property
188 function Get( $key ) {
189 if ( isset($this->properties[strtoupper($key)]) ) return $this->properties[strtoupper($key)];
194 * Put the value of a property
196 function Put( $key, $value ) {
197 return $this->properties[strtoupper($key)] = $value;
202 * Returns a PostgreSQL Date Format string suitable for returning iCal dates
204 function SqlDateFormat() {
205 return "'YYYYMMDD\"T\"HH24MISS'";
210 * Returns a PostgreSQL Date Format string suitable for returning dates which
211 * have been cast to UTC
213 function SqlUTCFormat() {
214 return "'YYYYMMDD\"T\"HH24MISS\"Z\"'";
219 * Returns a PostgreSQL Date Format string suitable for returning iCal durations
220 * - this doesn't work for negative intervals, but events should not have such!
222 function SqlDurationFormat() {
223 return "'\"PT\"HH24\"H\"MI\"M\"'";
227 * Returns a suitably escaped RFC2445 content string.
229 * @param string The incoming name[;param] prefixing the string.
230 * @param string The incoming string to be escaped.
232 function RFC2445ContentEscape( $name, $value ) {
233 $value = str_replace( '\\', '\\\\', $value);
234 $value = str_replace( "\n", '\\n', $value);
235 $value = str_replace( "\r", '\\r', $value);
236 // $value = preg_replace( "\n", '\\n', $value);
237 $value = preg_replace( "/([,;:\"\'])/", '\\\\$1', $value);
238 $result = wordwrap("$name:$value", 75, " \r\n ", true ) . "\r\n";
239 return $result;
243 BEGIN:VCALENDAR
244 CALSCALE:GREGORIAN
245 PRODID:-//Ximian//NONSGML Evolution Calendar//EN
246 VERSION:2.0
247 BEGIN:VEVENT
248 UID:20060918T005755Z-21151-1000-1-7@ubu
249 DTSTAMP:20060918T005755Z
250 DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
251 20060918T153000
252 DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
253 20060918T160000
254 SUMMARY:Lunch
255 X-EVOLUTION-CALDAV-HREF:http:
256 //andrew@mycaldav/caldav.php/andrew/20060918T005757Z.ics
257 BEGIN:VALARM
258 X-EVOLUTION-ALARM-UID:20060918T005755Z-21149-1000-1-12@ubu
259 ACTION:DISPLAY
260 TRIGGER;VALUE=DURATION;RELATED=START:-PT15M
261 DESCRIPTION:Lunch
262 END:VALARM
263 END:VEVENT
264 BEGIN:VTIMEZONE
265 TZID:/softwarestudio.org/Olson_20011030_5/Pacific/Auckland
266 X-LIC-LOCATION:Pacific/Auckland
267 BEGIN:STANDARD
268 TZOFFSETFROM:+1300
269 TZOFFSETTO:+1200
270 TZNAME:NZST
271 DTSTART:19700315T030000
272 RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3
273 END:STANDARD
274 BEGIN:DAYLIGHT
275 TZOFFSETFROM:+1200
276 TZOFFSETTO:+1300
277 TZNAME:NZDT
278 DTSTART:19701004T020000
279 RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=10
280 END:DAYLIGHT
281 END:VTIMEZONE
282 END:VCALENDAR
285 * Render the vEvent object as a text string which is a single VEVENT
287 function Render( ) {
288 $interesting = array( "uid", "dtstamp", "dtstart", "duration", "summary", "uri", "last-modified",
289 "location", "description", "class", "transp", "sequence", "timezone" );
291 $wrap_at = 75;
292 $result = <<<EOTXT
293 BEGIN:VCALENDAR\r
294 PRODID:-//Catalyst.Net.NZ//NONSGML AWL Calendar//EN\r
295 VERSION:2.0\r
296 BEGIN:VEVENT\r
298 EOTXT;
300 foreach( $interesting AS $k => $v ) {
301 $v = strtoupper($v);
302 if ( isset($this->properties[$v]) && $this->properties[$v] != "" ) {
303 dbg_error_log( "vEvent", "Rendering '%s' which is '%s'", $v, $this->properties[$v] );
304 $result .= $this->RFC2445ContentEscape($v,$this->properties[$v]);
308 // DTEND and DURATION may not exist together
309 if ( ( isset($this->properties['DTEND']) && $this->properties['DTEND'] != "" )
310 && !( isset($this->properties['DURATION']) && $this->properties['DURATION'] != "" ) ) {
311 dbg_error_log( "vEvent", "Rendering '%s' which is '%s'", 'DTEND',$this->properties['DTEND'] );
312 $result .= $this->RFC2445ContentEscape('DTEND',$this->properties['DTEND']);
315 $result .= <<<EOTXT
316 END:VEVENT\r
317 END:VCALENDAR\r
319 EOTXT;
321 return $result;