1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: require_once('vComponent.php');
22:
23: class vCalendar extends vComponent {
24:
25: 26: 27: 28: 29: 30: 31: 32: 33:
34: private $contained_type;
35: private $primary_component;
36: private $timezones;
37: private $organizer;
38: private $attendees;
39: private $schedule_agent;
40:
41: 42: 43: 44: 45: 46: 47: 48: 49: 50:
51: function __construct($content=null) {
52: $this->contained_type = null;
53: $this->primary_component = null;
54: $this->timezones = array();
55: if ( empty($content) || is_array($content) ) {
56: parent::__construct();
57: $this->SetType('VCALENDAR');
58: $this->AddProperty('PRODID', '-//davical.org//NONSGML AWL Calendar//EN');
59: $this->AddProperty('VERSION', '2.0');
60: $this->AddProperty('CALSCALE', 'GREGORIAN');
61: if ( !empty($content) ) {
62: foreach( $content AS $k => $v ) {
63: $this->AddProperty($k,$v);
64: }
65: }
66: }
67: else {
68: parent::__construct($content);
69: $components = $this->GetComponents();
70: if(isset($components) && count($components) > 0){
71: foreach( $components AS $k => $comp ) {
72: if ( $comp->GetType() == 'VTIMEZONE' ) {
73: $this->AddTimeZone($comp, true);
74: }
75: else if ( empty($this->contained_type) ) {
76: $this->contained_type = $comp->GetType();
77: $this->primary_component = $comp;
78: }
79: }
80: }
81:
82: if ( !isset($this->contained_type) && !empty($this->timezones) ) {
83: $this->contained_type = 'VTIMEZONE';
84: $this->primary_component = reset($this->timezones);
85: }
86: }
87: }
88:
89:
90: 91: 92:
93: function AddTimeZone(vComponent $vtz, $in_components=false) {
94: $tzid = $vtz->GetPValue('TZID');
95: if ( empty($tzid) ) {
96: dbg_error_log('ERROR','Ignoring invalid VTIMEZONE with no TZID parameter!');
97: dbg_log_array('LOG', 'vTimezone', $vtz, true);
98: return;
99: }
100: $this->timezones[$tzid] = $vtz;
101: if ( !$in_components ) $this->AddComponent($vtz);
102: }
103:
104:
105: 106: 107: 108: 109:
110: function GetTimeZone( $tzid ) {
111: if ( empty($this->timezones[$tzid]) ) return null;
112: return $this->timezones[$tzid];
113: }
114:
115:
116: 117: 118: 119:
120: function GetOrganizer() {
121: if ( !isset($this->organizer) ) {
122: $organizers = $this->GetPropertiesByPath('/VCALENDAR/*/ORGANIZER');
123: $organizer = (count($organizers) > 0 ? $organizers[0] : false);
124: $this->organizer = (empty($organizer) ? false : $organizer );
125: if ( $this->organizer ) {
126: $this->schedule_agent = $organizer->GetParameterValue('SCHEDULE-AGENT');
127: if ( empty($schedule_agent) ) $this->schedule_agent = 'SERVER';
128: }
129: }
130: return $this->organizer;
131: }
132:
133:
134: 135: 136: 137:
138: function GetScheduleAgent() {
139: if ( !isset($this->schedule_agent) ) $this->GetOrganizer();
140: return $this->schedule_agent;
141: }
142:
143:
144: 145: 146:
147: function GetAttendees() {
148: if ( !isset($this->attendees) ) {
149: $this->attendees = array();
150: $attendees = $this->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
151: $wr_attendees = $this->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
152: if ( count ( $wr_attendees ) > 0 ) {
153: dbg_error_log( 'PUT', 'Non-compliant iCal request. Using X-WR-ATTENDEE property' );
154: foreach( $wr_attendees AS $k => $v ) {
155: $attendees[] = $v;
156: }
157: }
158: $this->attendees = $attendees;
159: }
160: return $this->attendees;
161: }
162:
163:
164:
165: 166: 167: 168: 169:
170: function UpdateAttendeeStatus( $email, vProperty $statusProperty ) {
171: foreach($this->GetComponents() AS $ck => $v ) {
172: if ($v->GetType() == 'VEVENT' || $v->GetType() == 'VTODO' ) {
173: $new_attendees = array();
174: foreach( $v->GetProperties() AS $p ) {
175: if ( $p->Name() == 'ATTENDEE' ) {
176: if ( $p->Value() == $email || $p->Value() == 'mailto:'.$email ) {
177: $new_attendees[] = $statusProperty;
178: }
179: else {
180: $new_attendees[] = clone($p);
181: }
182: }
183: }
184: $v->SetProperties($new_attendees,'ATTENDEE');
185: $this->attendees = null;
186: $this->rendered = null;
187: }
188: }
189: }
190:
191:
192:
193: 194: 195: 196:
197: function UpdateOrganizerStatus( vProperty $statusProperty ) {
198: $this->rendered = null;
199: foreach($this->GetComponents() AS $ck => $v ) {
200: if ($v->GetType() == 'VEVENT' || $v->GetType() == 'VTODO' ) {
201: $v->SetProperties(array($statusProperty), 'ORGANIZER');
202: $this->organizer = null;
203: $this->rendered = null;
204: }
205: }
206: }
207:
208:
209:
210: 211: 212: 213: 214: 215: 216: 217: 218:
219: function StartFilter( $filters ) {
220: dbg_error_log('vCalendar', ':StartFilter we have %d filters to test', count($filters) );
221:
222: if ( count($filters) != 1 ) return false;
223:
224: $tag = $filters[0]->GetNSTag();
225: $name = $filters[0]->GetAttribute("name");
226: if ( $tag != "urn:ietf:params:xml:ns:caldav:comp-filter" || $name != 'VCALENDAR' ) return false;
227: return $this->TestFilter($filters[0]->GetContent());
228: }
229:
230:
231: 232: 233: 234: 235: 236:
237: function GetOlsonName( vComponent $vtz ) {
238: $tzstring = $vtz->GetPValue('TZID');
239: $tzid = olson_from_tzstring($tzstring);
240: if ( !empty($tzid) ) return $tzid;
241:
242: $tzstring = $vtz->GetPValue('X-LIC-LOCATION');
243: $tzid = olson_from_tzstring($tzstring);
244: if ( !empty($tzid) ) return $tzid;
245:
246: $tzcdo = $vtz->GetPValue('X-MICROSOFT-CDO-TZID');
247: if ( empty($tzcdo) ) return null;
248: switch( $tzcdo ) {
249: 250: 251: 252:
253: case 0: return('UTC');
254: case 1: return('Europe/London');
255: case 2: return('Europe/Lisbon');
256: case 3: return('Europe/Paris');
257: case 4: return('Europe/Berlin');
258: case 5: return('Europe/Bucharest');
259: case 6: return('Europe/Prague');
260: case 7: return('Europe/Athens');
261: case 8: return('America/Brasilia');
262: case 9: return('America/Halifax');
263: case 10: return('America/New_York');
264: case 11: return('America/Chicago');
265: case 12: return('America/Denver');
266: case 13: return('America/Los_Angeles');
267: case 14: return('America/Anchorage');
268: case 15: return('Pacific/Honolulu');
269: case 16: return('Pacific/Apia');
270: case 17: return('Pacific/Auckland');
271: case 18: return('Australia/Brisbane');
272: case 19: return('Australia/Adelaide');
273: case 20: return('Asia/Tokyo');
274: case 21: return('Asia/Singapore');
275: case 22: return('Asia/Bangkok');
276: case 23: return('Asia/Kolkata');
277: case 24: return('Asia/Muscat');
278: case 25: return('Asia/Tehran');
279: case 26: return('Asia/Baghdad');
280: case 27: return('Asia/Jerusalem');
281: case 28: return('America/St_Johns');
282: case 29: return('Atlantic/Azores');
283: case 30: return('America/Noronha');
284: case 31: return('Africa/Casablanca');
285: case 32: return('America/Argentina/Buenos_Aires');
286: case 33: return('America/La_Paz');
287: case 34: return('America/Indiana/Indianapolis');
288: case 35: return('America/Bogota');
289: case 36: return('America/Regina');
290: case 37: return('America/Tegucigalpa');
291: case 38: return('America/Phoenix');
292: case 39: return('Pacific/Kwajalein');
293: case 40: return('Pacific/Fiji');
294: case 41: return('Asia/Magadan');
295: case 42: return('Australia/Hobart');
296: case 43: return('Pacific/Guam');
297: case 44: return('Australia/Darwin');
298: case 45: return('Asia/Shanghai');
299: case 46: return('Asia/Novosibirsk');
300: case 47: return('Asia/Karachi');
301: case 48: return('Asia/Kabul');
302: case 49: return('Africa/Cairo');
303: case 50: return('Africa/Harare');
304: case 51: return('Europe/Moscow');
305: case 53: return('Atlantic/Cape_Verde');
306: case 54: return('Asia/Yerevan');
307: case 55: return('America/Panama');
308: case 56: return('Africa/Nairobi');
309: case 58: return('Asia/Yekaterinburg');
310: case 59: return('Europe/Helsinki');
311: case 60: return('America/Godthab');
312: case 61: return('Asia/Rangoon');
313: case 62: return('Asia/Kathmandu');
314: case 63: return('Asia/Irkutsk');
315: case 64: return('Asia/Krasnoyarsk');
316: case 65: return('America/Santiago');
317: case 66: return('Asia/Colombo');
318: case 67: return('Pacific/Tongatapu');
319: case 68: return('Asia/Vladivostok');
320: case 69: return('Africa/Ndjamena');
321: case 70: return('Asia/Yakutsk');
322: case 71: return('Asia/Dhaka');
323: case 72: return('Asia/Seoul');
324: case 73: return('Australia/Perth');
325: case 74: return('Asia/Riyadh');
326: case 75: return('Asia/Taipei');
327: case 76: return('Australia/Sydney');
328:
329: case 57:
330: case 52:
331: default:
332: }
333: return null;
334: }
335:
336:
337: 338: 339: 340: 341:
342: function Confidential() {
343: static $keep_properties = array( 'DTSTAMP'=>1, 'DTSTART'=>1, 'RRULE'=>1, 'DURATION'=>1, 'DTEND'=>1, 'DUE'=>1, 'UID'=>1, 'CLASS'=>1, 'TRANSP'=>1, 'CREATED'=>1, 'LAST-MODIFIED'=>1 );
344: static $resource_components = array( 'VEVENT'=>1, 'VTODO'=>1, 'VJOURNAL'=>1 );
345: $this->MaskComponents(array( 'VTIMEZONE'=>1, 'VEVENT'=>1, 'VTODO'=>1, 'VJOURNAL'=>1 ), false);
346: $this->MaskProperties($keep_properties, $resource_components );
347: if ( isset($this->rendered) ) unset($this->rendered);
348: foreach( $this->GetComponents() AS $comp ) {
349: if ( isset($resource_components[$comp->GetType()] ) ) {
350: if ( isset($comp->rendered) ) unset($comp->rendered);
351: $comp->AddProperty( 'SUMMARY', translate('Busy') );
352: }
353: }
354:
355: return $this;
356: }
357:
358:
359: 360: 361:
362: function GetItip($method, $attendee_value ) {
363: $iTIP = clone($this);
364: static $keep_properties = array( 'DTSTART'=>1, 'DURATION'=>1, 'DTEND'=>1, 'DUE'=>1, 'UID'=>1,
365: 'SEQUENCE'=>1, 'ORGANIZER'=>1, 'ATTENDEE'=>1 );
366: static $resource_components = array( 'VEVENT'=>1, 'VTODO'=>1, 'VJOURNAL'=>1 );
367: $iTIP->MaskComponents($resource_components, false);
368: $iTIP->MaskProperties($keep_properties, $resource_components );
369: $iTIP->AddProperty('METHOD',$method);
370: if ( isset($iTIP->rendered) ) unset($iTIP->rendered);
371: if ( !empty($attendee_value) ) {
372: $iTIP->attendees = array();
373: foreach( $iTIP->GetComponents() AS $comp ) {
374: if ( isset($resource_components[$comp->GetType()] ) ) {
375: foreach( $comp->GetProperties() AS $k=> $property ) {
376: switch( $property->Name() ) {
377: case 'ATTENDEE':
378: if ( $property->Value() == $attendee_value )
379: $iTIP->attendees[] = $property->ClearParameters(array('CUTYPE'=>true, 'SCHEDULE-STATUS'=>true));
380: else
381: $comp->clearPropertyAt($k);
382: break;
383: case 'SEQUENCE':
384: $property->Value( $property->Value() + 1);
385: break;
386: }
387: }
388: $comp->AddProperty('DTSTAMP', date('Ymd\THis\Z'));
389: }
390: }
391: }
392:
393: return $iTIP;
394: }
395:
396:
397: 398: 399:
400: function GetUID() {
401: if ( empty($this->primary_component) ) return null;
402: return $this->primary_component->GetPValue('UID');
403:
404: }
405:
406:
407: 408: 409: 410:
411: function SetUID( $newUid ) {
412: if ( empty($this->primary_component) ) return;
413: $this->primary_component->SetProperties( array( new vProperty('UID', $newUid) ), 'UID');
414: }
415:
416: }
417: