1
|
<?php
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
|
13
|
|
14
|
|
15
|
|
16
|
|
17
|
|
18
|
|
19
|
|
20
|
|
21
|
|
22
|
|
23
|
|
24
|
|
25
|
|
26
|
|
27
|
|
28
|
|
29
|
|
30
|
|
31
|
|
32
|
|
33
|
|
34
|
|
35
|
|
36
|
|
37
|
|
38
|
|
39
|
|
40
|
|
41
|
|
42
|
|
43
|
|
44
|
|
45
|
|
46
|
function iCal2XML( $calendar ) {
|
47
|
|
48
|
$xmlstr = '<?xml version="1.0" encoding="utf-8"?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0">';
|
49
|
$xmlstr .= '<!-- created '.gmdate( 'Ymd\THis\Z' );
|
50
|
$xmlstr .= ' using kigkonsult.se '.ICALCREATOR_VERSION.' iCal2XMl (rfc6321) -->';
|
51
|
$xmlstr .= '</icalendar>';
|
52
|
$xml = new SimpleXMLElement( $xmlstr );
|
53
|
$vcalendar = $xml->addChild( 'vcalendar' );
|
54
|
|
55
|
$properties = $vcalendar->addChild( 'properties' );
|
56
|
$calProps = array( 'version', 'prodid', 'calscale', 'method' );
|
57
|
foreach( $calProps as $calProp ) {
|
58
|
if( FALSE !== ( $content = $calendar->getProperty( $calProp )))
|
59
|
_addXMLchild( $properties, $calProp, 'text', $content );
|
60
|
}
|
61
|
while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE )))
|
62
|
_addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
|
63
|
$langCal = $calendar->getConfig( 'language' );
|
64
|
|
65
|
$components = $vcalendar->addChild( 'components' );
|
66
|
|
67
|
while( FALSE !== ( $component = $calendar->getComponent())) {
|
68
|
$compName = $component->objName;
|
69
|
$child = $components->addChild( $compName );
|
70
|
$properties = $child->addChild( 'properties' );
|
71
|
$langComp = $component->getConfig( 'language' );
|
72
|
$props = $component->getConfig( 'setPropertyNames' );
|
73
|
foreach( $props as $prop ) {
|
74
|
switch( strtolower( $prop )) {
|
75
|
case 'attach':
|
76
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
77
|
$type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri';
|
78
|
unset( $content['params']['VALUE'] );
|
79
|
_addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
|
80
|
}
|
81
|
break;
|
82
|
case 'attendee':
|
83
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
84
|
if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
|
85
|
if( $langComp )
|
86
|
$content['params']['LANGUAGE'] = $langComp;
|
87
|
elseif( $langCal )
|
88
|
$content['params']['LANGUAGE'] = $langCal;
|
89
|
}
|
90
|
_addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
|
91
|
}
|
92
|
break;
|
93
|
case 'exdate':
|
94
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
95
|
$type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time';
|
96
|
unset( $content['params']['VALUE'] );
|
97
|
_addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
|
98
|
}
|
99
|
break;
|
100
|
case 'freebusy':
|
101
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
102
|
if( is_array( $content ) && isset( $content['value']['fbtype'] )) {
|
103
|
$content['params']['FBTYPE'] = $content['value']['fbtype'];
|
104
|
unset( $content['value']['fbtype'] );
|
105
|
}
|
106
|
_addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] );
|
107
|
}
|
108
|
break;
|
109
|
case 'request-status':
|
110
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
111
|
if( !isset( $content['params']['LANGUAGE'] )) {
|
112
|
if( $langComp )
|
113
|
$content['params']['LANGUAGE'] = $langComp;
|
114
|
elseif( $langCal )
|
115
|
$content['params']['LANGUAGE'] = $langCal;
|
116
|
}
|
117
|
_addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] );
|
118
|
}
|
119
|
break;
|
120
|
case 'rdate':
|
121
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
122
|
$type = 'date-time';
|
123
|
if( isset( $content['params']['VALUE'] )) {
|
124
|
if( 'DATE' == $content['params']['VALUE'] )
|
125
|
$type = 'date';
|
126
|
elseif( 'PERIOD' == $content['params']['VALUE'] )
|
127
|
$type = 'period';
|
128
|
}
|
129
|
unset( $content['params']['VALUE'] );
|
130
|
_addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
|
131
|
}
|
132
|
break;
|
133
|
case 'categories':
|
134
|
case 'comment':
|
135
|
case 'contact':
|
136
|
case 'description':
|
137
|
case 'related-to':
|
138
|
case 'resources':
|
139
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
140
|
if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
|
141
|
if( $langComp )
|
142
|
$content['params']['LANGUAGE'] = $langComp;
|
143
|
elseif( $langCal )
|
144
|
$content['params']['LANGUAGE'] = $langCal;
|
145
|
}
|
146
|
_addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
|
147
|
}
|
148
|
break;
|
149
|
case 'x-prop':
|
150
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
|
151
|
_addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
|
152
|
break;
|
153
|
case 'created':
|
154
|
case 'completed':
|
155
|
case 'dtstamp':
|
156
|
case 'last-modified':
|
157
|
$utcDate = TRUE;
|
158
|
case 'dtstart':
|
159
|
case 'dtend':
|
160
|
case 'due':
|
161
|
case 'recurrence-id':
|
162
|
if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
163
|
$type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time';
|
164
|
unset( $content['params']['VALUE'] );
|
165
|
if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] ))
|
166
|
unset( $content['params']['TZID'] );
|
167
|
_addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
|
168
|
}
|
169
|
unset( $utcDate );
|
170
|
break;
|
171
|
case 'duration':
|
172
|
if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
|
173
|
_addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
|
174
|
break;
|
175
|
case 'exrule':
|
176
|
case 'rrule':
|
177
|
while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
|
178
|
_addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
|
179
|
break;
|
180
|
case 'class':
|
181
|
case 'location':
|
182
|
case 'status':
|
183
|
case 'summary':
|
184
|
case 'transp':
|
185
|
case 'tzid':
|
186
|
case 'uid':
|
187
|
if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
188
|
if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) {
|
189
|
if( $langComp )
|
190
|
$content['params']['LANGUAGE'] = $langComp;
|
191
|
elseif( $langCal )
|
192
|
$content['params']['LANGUAGE'] = $langCal;
|
193
|
}
|
194
|
_addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
|
195
|
}
|
196
|
break;
|
197
|
case 'geo':
|
198
|
if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
|
199
|
_addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] );
|
200
|
break;
|
201
|
case 'organizer':
|
202
|
if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
|
203
|
if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
|
204
|
if( $langComp )
|
205
|
$content['params']['LANGUAGE'] = $langComp;
|
206
|
elseif( $langCal )
|
207
|
$content['params']['LANGUAGE'] = $langCal;
|
208
|
}
|
209
|
_addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
|
210
|
}
|
211
|
break;
|
212
|
case 'percent-complete':
|
213
|
case 'priority':
|
214
|
case 'sequence':
|
215
|
if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
|
216
|
_addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
|
217
|
break;
|
218
|
case 'tzurl':
|
219
|
case 'url':
|
220
|
if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
|
221
|
_addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] );
|
222
|
break;
|
223
|
}
|
224
|
}
|
225
|
|
226
|
while( FALSE !== ( $subcomp = $component->getComponent())) {
|
227
|
$subCompName = $subcomp->objName;
|
228
|
$child2 = $child->addChild( $subCompName );
|
229
|
$properties = $child2->addChild( 'properties' );
|
230
|
$langComp = $subcomp->getConfig( 'language' );
|
231
|
$subCompProps = $subcomp->getConfig( 'setPropertyNames' );
|
232
|
foreach( $subCompProps as $prop ) {
|
233
|
switch( strtolower( $prop )) {
|
234
|
case 'attach':
|
235
|
while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
|
236
|
$type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri';
|
237
|
unset( $content['params']['VALUE'] );
|
238
|
_addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
|
239
|
}
|
240
|
break;
|
241
|
case 'attendee':
|
242
|
while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
|
243
|
if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
|
244
|
if( $langComp )
|
245
|
$content['params']['LANGUAGE'] = $langComp;
|
246
|
elseif( $langCal )
|
247
|
$content['params']['LANGUAGE'] = $langCal;
|
248
|
}
|
249
|
_addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
|
250
|
}
|
251
|
break;
|
252
|
case 'comment':
|
253
|
case 'tzname':
|
254
|
while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
|
255
|
if( !isset( $content['params']['LANGUAGE'] )) {
|
256
|
if( $langComp )
|
257
|
$content['params']['LANGUAGE'] = $langComp;
|
258
|
elseif( $langCal )
|
259
|
$content['params']['LANGUAGE'] = $langCal;
|
260
|
}
|
261
|
_addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
|
262
|
}
|
263
|
break;
|
264
|
case 'rdate':
|
265
|
while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
|
266
|
$type = 'date-time';
|
267
|
if( isset( $content['params']['VALUE'] )) {
|
268
|
if( 'DATE' == $content['params']['VALUE'] )
|
269
|
$type = 'date';
|
270
|
elseif( 'PERIOD' == $content['params']['VALUE'] )
|
271
|
$type = 'period';
|
272
|
}
|
273
|
unset( $content['params']['VALUE'] );
|
274
|
_addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
|
275
|
}
|
276
|
break;
|
277
|
case 'x-prop':
|
278
|
while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
|
279
|
_addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
|
280
|
break;
|
281
|
case 'action':
|
282
|
case 'description':
|
283
|
case 'summary':
|
284
|
if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
|
285
|
if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
|
286
|
if( $langComp )
|
287
|
$content['params']['LANGUAGE'] = $langComp;
|
288
|
elseif( $langCal )
|
289
|
$content['params']['LANGUAGE'] = $langCal;
|
290
|
}
|
291
|
_addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
|
292
|
}
|
293
|
break;
|
294
|
case 'dtstart':
|
295
|
if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
|
296
|
unset( $content['value']['tz'], $content['params']['VALUE'] );
|
297
|
_addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] );
|
298
|
}
|
299
|
break;
|
300
|
case 'duration':
|
301
|
if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
|
302
|
_addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
|
303
|
break;
|
304
|
case 'repeat':
|
305
|
if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
|
306
|
_addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
|
307
|
break;
|
308
|
case 'trigger':
|
309
|
if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
|
310
|
if( isset( $content['value']['year'] ) &&
|
311
|
isset( $content['value']['month'] ) &&
|
312
|
isset( $content['value']['day'] ))
|
313
|
$type = 'date-time';
|
314
|
else {
|
315
|
$type = 'duration';
|
316
|
if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] ))
|
317
|
$content['params']['RELATED'] = 'END';
|
318
|
}
|
319
|
_addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
|
320
|
}
|
321
|
break;
|
322
|
case 'tzoffsetto':
|
323
|
case 'tzoffsetfrom':
|
324
|
if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
|
325
|
_addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] );
|
326
|
break;
|
327
|
case 'rrule':
|
328
|
while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
|
329
|
_addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
|
330
|
break;
|
331
|
}
|
332
|
}
|
333
|
}
|
334
|
}
|
335
|
return $xml->asXML();
|
336
|
}
|
337
|
|
338
|
|
339
|
|
340
|
|
341
|
|
342
|
|
343
|
|
344
|
|
345
|
|
346
|
|
347
|
|
348
|
|
349
|
|
350
|
|
351
|
|
352
|
|
353
|
function _addXMLchild( & $parent, $name, $type, $content, $params=array()) {
|
354
|
static $fmtYmd = '%04d-%02d-%02d';
|
355
|
static $fmtYmdHis = '%04d-%02d-%02dT%02d:%02d:%02d';
|
356
|
|
357
|
$name = strtolower( $name );
|
358
|
$child = $parent->addChild( $name );
|
359
|
if( !empty( $params )) {
|
360
|
$parameters = $child->addChild( 'parameters' );
|
361
|
foreach( $params as $param => $parVal ) {
|
362
|
if( 'VALUE' == $param )
|
363
|
continue;
|
364
|
$param = strtolower( $param );
|
365
|
if( 'x-' == substr( $param, 0, 2 )) {
|
366
|
$p1 = $parameters->addChild( $param );
|
367
|
$p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal ));
|
368
|
}
|
369
|
else {
|
370
|
$p1 = $parameters->addChild( $param );
|
371
|
switch( $param ) {
|
372
|
case 'altrep':
|
373
|
case 'dir': $ptype = 'uri'; break;
|
374
|
case 'delegated-from':
|
375
|
case 'delegated-to':
|
376
|
case 'member':
|
377
|
case 'sent-by': $ptype = 'cal-address'; break;
|
378
|
case 'rsvp': $ptype = 'boolean'; break ;
|
379
|
default: $ptype = 'text'; break;
|
380
|
}
|
381
|
if( is_array( $parVal )) {
|
382
|
foreach( $parVal as $pV )
|
383
|
$p2 = $p1->addChild( $ptype, htmlspecialchars( $pV ));
|
384
|
}
|
385
|
else
|
386
|
$p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal ));
|
387
|
}
|
388
|
}
|
389
|
}
|
390
|
if(( empty( $content ) && ( '0' != $content )) || ( !is_array( $content) && ( '-' != substr( $content, 0, 1 ) && ( 0 > $content ))))
|
391
|
return;
|
392
|
|
393
|
switch( $type ) {
|
394
|
case 'binary':
|
395
|
$v = $child->addChild( $type, $content );
|
396
|
break;
|
397
|
case 'boolean':
|
398
|
break;
|
399
|
case 'cal-address':
|
400
|
$v = $child->addChild( $type, $content );
|
401
|
break;
|
402
|
case 'date':
|
403
|
if( array_key_exists( 'year', $content ))
|
404
|
$content = array( $content );
|
405
|
foreach( $content as $date ) {
|
406
|
$str = sprintf( $fmtYmd, (int) $date['year'], (int) $date['month'], (int) $date['day'] );
|
407
|
$v = $child->addChild( $type, $str );
|
408
|
}
|
409
|
break;
|
410
|
case 'date-time':
|
411
|
if( array_key_exists( 'year', $content ))
|
412
|
$content = array( $content );
|
413
|
foreach( $content as $dt ) {
|
414
|
if( !isset( $dt['hour'] )) $dt['hour'] = 0;
|
415
|
if( !isset( $dt['min'] )) $dt['min'] = 0;
|
416
|
if( !isset( $dt['sec'] )) $dt['sec'] = 0;
|
417
|
$str = sprintf( $fmtYmdHis, (int) $dt['year'], (int) $dt['month'], (int) $dt['day'], (int) $dt['hour'], (int) $dt['min'], (int) $dt['sec'] );
|
418
|
if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] ))
|
419
|
$str .= 'Z';
|
420
|
$v = $child->addChild( $type, $str );
|
421
|
}
|
422
|
break;
|
423
|
case 'duration':
|
424
|
$output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : '';
|
425
|
$v = $child->addChild( $type, $output.iCalUtilityFunctions::_duration2str( $content ) );
|
426
|
break;
|
427
|
case 'geo':
|
428
|
if( !empty( $content )) {
|
429
|
$v1 = $child->addChild( 'latitude', iCalUtilityFunctions::_geo2str2( $content['latitude'], iCalUtilityFunctions::$geoLatFmt ));
|
430
|
$v1 = $child->addChild( 'longitude', iCalUtilityFunctions::_geo2str2( $content['longitude'], iCalUtilityFunctions::$geoLongFmt ));
|
431
|
}
|
432
|
break;
|
433
|
case 'integer':
|
434
|
$v = $child->addChild( $type, (string) $content );
|
435
|
break;
|
436
|
case 'period':
|
437
|
if( !is_array( $content ))
|
438
|
break;
|
439
|
foreach( $content as $period ) {
|
440
|
$v1 = $child->addChild( $type );
|
441
|
$str = sprintf( $fmtYmdHis, (int) $period[0]['year'], (int) $period[0]['month'], (int) $period[0]['day'], (int) $period[0]['hour'], (int) $period[0]['min'], (int) $period[0]['sec'] );
|
442
|
if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] ))
|
443
|
$str .= 'Z';
|
444
|
$v2 = $v1->addChild( 'start', $str );
|
445
|
if( array_key_exists( 'year', $period[1] )) {
|
446
|
$str = sprintf( $fmtYmdHis, (int) $period[1]['year'], (int) $period[1]['month'], (int) $period[1]['day'], (int) $period[1]['hour'], (int) $period[1]['min'], (int) $period[1]['sec'] );
|
447
|
if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] ))
|
448
|
$str .= 'Z';
|
449
|
$v2 = $v1->addChild( 'end', $str );
|
450
|
}
|
451
|
else
|
452
|
$v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_duration2str( $period[1] ));
|
453
|
}
|
454
|
break;
|
455
|
case 'recur':
|
456
|
$content = array_change_key_case( $content );
|
457
|
foreach( $content as $rulelabel => $rulevalue ) {
|
458
|
switch( $rulelabel ) {
|
459
|
case 'until':
|
460
|
if( isset( $rulevalue['hour'] ))
|
461
|
$str = sprintf( $fmtYmdHis, (int) $rulevalue['year'], (int) $rulevalue['month'], (int) $rulevalue['day'], (int) $rulevalue['hour'], (int) $rulevalue['min'], (int) $rulevalue['sec'] ).'Z';
|
462
|
else
|
463
|
$str = sprintf( $fmtYmd, (int) $rulevalue['year'], (int) $rulevalue['month'], (int) $rulevalue['day'] );
|
464
|
$v = $child->addChild( $rulelabel, $str );
|
465
|
break;
|
466
|
case 'bysecond':
|
467
|
case 'byminute':
|
468
|
case 'byhour':
|
469
|
case 'bymonthday':
|
470
|
case 'byyearday':
|
471
|
case 'byweekno':
|
472
|
case 'bymonth':
|
473
|
case 'bysetpos': {
|
474
|
if( is_array( $rulevalue )) {
|
475
|
foreach( $rulevalue as $vix => $valuePart )
|
476
|
$v = $child->addChild( $rulelabel, $valuePart );
|
477
|
}
|
478
|
else
|
479
|
$v = $child->addChild( $rulelabel, $rulevalue );
|
480
|
break;
|
481
|
}
|
482
|
case 'byday': {
|
483
|
if( isset( $rulevalue['DAY'] )) {
|
484
|
$str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : '';
|
485
|
$str .= $rulevalue['DAY'];
|
486
|
$p = $child->addChild( $rulelabel, $str );
|
487
|
}
|
488
|
else {
|
489
|
foreach( $rulevalue as $valuePart ) {
|
490
|
if( isset( $valuePart['DAY'] )) {
|
491
|
$str = ( isset( $valuePart[0] )) ? $valuePart[0] : '';
|
492
|
$str .= $valuePart['DAY'];
|
493
|
$p = $child->addChild( $rulelabel, $str );
|
494
|
}
|
495
|
else
|
496
|
$p = $child->addChild( $rulelabel, $valuePart );
|
497
|
}
|
498
|
}
|
499
|
break;
|
500
|
}
|
501
|
case 'freq':
|
502
|
case 'count':
|
503
|
case 'interval':
|
504
|
case 'wkst':
|
505
|
default:
|
506
|
$p = $child->addChild( $rulelabel, $rulevalue );
|
507
|
break;
|
508
|
}
|
509
|
}
|
510
|
break;
|
511
|
case 'rstatus':
|
512
|
$v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', ''));
|
513
|
$v = $child->addChild( 'description', htmlspecialchars( $content['text'] ));
|
514
|
if( isset( $content['extdata'] ))
|
515
|
$v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] ));
|
516
|
break;
|
517
|
case 'text':
|
518
|
if( !is_array( $content ))
|
519
|
$content = array( $content );
|
520
|
foreach( $content as $part )
|
521
|
$v = $child->addChild( $type, htmlspecialchars( $part ));
|
522
|
break;
|
523
|
case 'time':
|
524
|
break;
|
525
|
case 'uri':
|
526
|
$v = $child->addChild( $type, $content );
|
527
|
break;
|
528
|
case 'utc-offset':
|
529
|
if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) {
|
530
|
$str = substr( $content, 0, 1 );
|
531
|
$content = substr( $content, 1 );
|
532
|
}
|
533
|
else
|
534
|
$str = '+';
|
535
|
$str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 );
|
536
|
if( 4 < strlen( $content ))
|
537
|
$str .= ':'.substr( $content, 4 );
|
538
|
$v = $child->addChild( $type, $str );
|
539
|
break;
|
540
|
case 'unknown':
|
541
|
default:
|
542
|
if( is_array( $content ))
|
543
|
$content = implode( '', $content );
|
544
|
$v = $child->addChild( 'unknown', htmlspecialchars( $content ));
|
545
|
break;
|
546
|
}
|
547
|
}
|
548
|
|
549
|
|
550
|
|
551
|
|
552
|
|
553
|
|
554
|
|
555
|
|
556
|
|
557
|
function XMLfile2iCal( $xmlfile, $iCalcfg=array()) {
|
558
|
if( FALSE === ( $xmlstr = file_get_contents( $xmlfile )))
|
559
|
return FALSE;
|
560
|
return xml2iCal( $xmlstr, $iCalcfg );
|
561
|
}
|
562
|
|
563
|
|
564
|
|
565
|
|
566
|
|
567
|
|
568
|
|
569
|
|
570
|
|
571
|
function XMLstr2iCal( $xmlstr, $iCalcfg=array()) {
|
572
|
return XML2iCal( $xmlstr, $iCalcfg);
|
573
|
}
|
574
|
|
575
|
|
576
|
|
577
|
|
578
|
|
579
|
|
580
|
|
581
|
|
582
|
|
583
|
|
584
|
|
585
|
function XML2iCal( $xmlstr, $iCalcfg=array()) {
|
586
|
$xmlstr = str_replace( array( "\r\n", "\n\r", "\n", "\r" ), '', $xmlstr );
|
587
|
$xml = XMLgetTagContent1( $xmlstr, 'vcalendar', $endIx );
|
588
|
$iCal = new vcalendar( $iCalcfg );
|
589
|
XMLgetComps( $iCal, $xmlstr );
|
590
|
unset( $xmlstr );
|
591
|
return $iCal;
|
592
|
}
|
593
|
|
594
|
|
595
|
|
596
|
|
597
|
|
598
|
|
599
|
|
600
|
|
601
|
|
602
|
|
603
|
|
604
|
|
605
|
|
606
|
|
607
|
|
608
|
|
609
|
function XMLgetComps( $iCal, $xml ) {
|
610
|
$sx = 0;
|
611
|
while(( FALSE !== substr( $xml, ( $sx + 11 ), 1 )) &&
|
612
|
( '<properties>' != substr( $xml, $sx, 12 )) && ( '<components>' != substr( $xml, $sx, 12 )))
|
613
|
$sx += 1;
|
614
|
if( FALSE === substr( $xml, ( $sx + 11 ), 1 ))
|
615
|
return FALSE;
|
616
|
if( '<properties>' == substr( $xml, $sx, 12 )) {
|
617
|
$xml2 = XMLgetTagContent1( $xml, 'properties', $endIx );
|
618
|
XMLgetProps( $iCal, $xml2 );
|
619
|
$xml = substr( $xml, $endIx );
|
620
|
}
|
621
|
if( '<components>' == substr( $xml, 0, 12 ))
|
622
|
$xml = XMLgetTagContent1( $xml, 'components', $endIx );
|
623
|
while( ! empty( $xml )) {
|
624
|
$xml2 = XMLgetTagContent2( $xml, $tagName, $endIx );
|
625
|
if( in_array( strtolower( $tagName ), iCalUtilityFunctions::$allComps ) &&
|
626
|
( FALSE !== ( $subComp = $iCal->newComponent( $tagName ))))
|
627
|
XMLgetComps( $subComp, $xml2 );
|
628
|
$xml = substr( $xml, $endIx);
|
629
|
}
|
630
|
unset( $xml );
|
631
|
return $iCal;
|
632
|
}
|
633
|
|
634
|
|
635
|
|
636
|
|
637
|
|
638
|
|
639
|
|
640
|
|
641
|
|
642
|
|
643
|
|
644
|
|
645
|
|
646
|
|
647
|
function XMLgetProps( $iCal, $xml) {
|
648
|
while( ! empty( $xml )) {
|
649
|
$xml2 = XMLgetTagContent2( $xml, $propName, $endIx );
|
650
|
$propName = strtoupper( $propName );
|
651
|
if( empty( $xml2 ) && ( '0' != $xml2 )) {
|
652
|
$iCal->setProperty( $propName );
|
653
|
$xml = substr( $xml, $endIx);
|
654
|
continue;
|
655
|
}
|
656
|
$params = array();
|
657
|
if( '<parameters/>' == substr( $xml2, 0, 13 ))
|
658
|
$xml2 = substr( $xml2, 13 );
|
659
|
elseif( '<parameters>' == substr( $xml2, 0, 12 )) {
|
660
|
$xml3 = XMLgetTagContent1( $xml2, 'parameters', $endIx2 );
|
661
|
while( ! empty( $xml3 )) {
|
662
|
$xml4 = XMLgetTagContent2( $xml3, $paramKey, $endIx3 );
|
663
|
$pType = FALSE;
|
664
|
$paramKey = strtoupper( $paramKey );
|
665
|
static $mParams = array( 'DELEGATED-FROM', 'DELEGATED-TO', 'MEMBER' );
|
666
|
if( in_array( $paramKey, $mParams )) {
|
667
|
while( ! empty( $xml4 )) {
|
668
|
if( ! isset( $params[$paramKey] ))
|
669
|
$params[$paramKey] = array( XMLgetTagContent1( $xml4, 'cal-address', $endIx4 ));
|
670
|
else
|
671
|
$params[$paramKey][] = XMLgetTagContent1( $xml4, 'cal-address', $endIx4 );
|
672
|
$xml4 = substr( $xml4, $endIx4 );
|
673
|
}
|
674
|
}
|
675
|
else {
|
676
|
if( ! isset( $params[$paramKey] ))
|
677
|
$params[$paramKey] = html_entity_decode( XMLgetTagContent2( $xml4, $pType, $endIx4 ));
|
678
|
else
|
679
|
$params[$paramKey] .= ','.html_entity_decode( XMLgetTagContent2( $xml4, $pType, $endIx4 ));
|
680
|
}
|
681
|
$xml3 = substr( $xml3, $endIx3 );
|
682
|
}
|
683
|
$xml2 = substr( $xml2, $endIx2 );
|
684
|
}
|
685
|
$valueType = FALSE;
|
686
|
$value = ( ! empty( $xml2 ) || ( '0' == $xml2 )) ? XMLgetTagContent2( $xml2, $valueType, $endIx3 ) : '';
|
687
|
switch( $propName ) {
|
688
|
case 'CATEGORIES':
|
689
|
case 'RESOURCES':
|
690
|
$tValue = array();
|
691
|
while( ! empty( $xml2 )) {
|
692
|
$tValue[] = html_entity_decode( XMLgetTagContent2( $xml2, $valueType, $endIx4 ));
|
693
|
$xml2 = substr( $xml2, $endIx4 );
|
694
|
}
|
695
|
$value = $tValue;
|
696
|
break;
|
697
|
case 'EXDATE':
|
698
|
case 'RDATE':
|
699
|
if( 'period' != $valueType ) {
|
700
|
if( 'date' == $valueType )
|
701
|
$params['VALUE'] = 'DATE';
|
702
|
$t = array();
|
703
|
while( ! empty( $xml2 ) && ( '<date' == substr( $xml2, 0, 5 ))) {
|
704
|
$t[] = XMLgetTagContent2( $xml2, $pType, $endIx4 );
|
705
|
$xml2 = substr( $xml2, $endIx4 );
|
706
|
}
|
707
|
$value = $t;
|
708
|
break;
|
709
|
}
|
710
|
case 'FREEBUSY':
|
711
|
if( 'RDATE' == $propName )
|
712
|
$params['VALUE'] = 'PERIOD';
|
713
|
$value = array();
|
714
|
while( ! empty( $xml2 ) && ( '<period>' == substr( $xml2, 0, 8 ))) {
|
715
|
$xml3 = XMLgetTagContent1( $xml2, 'period', $endIx4 );
|
716
|
$t = array();
|
717
|
while( ! empty( $xml3 )) {
|
718
|
$t[] = XMLgetTagContent2( $xml3, $pType, $endIx5 );
|
719
|
$xml3 = substr( $xml3, $endIx5 );
|
720
|
}
|
721
|
$value[] = $t;
|
722
|
$xml2 = substr( $xml2, $endIx4 );
|
723
|
}
|
724
|
break;
|
725
|
case 'TZOFFSETTO':
|
726
|
case 'TZOFFSETFROM':
|
727
|
$value = str_replace( ':', '', $value );
|
728
|
break;
|
729
|
case 'GEO':
|
730
|
$tValue = array( 'latitude' => $value );
|
731
|
$tValue['longitude'] = XMLgetTagContent1( substr( $xml2, $endIx3 ), 'longitude', $endIx3 );
|
732
|
$value = $tValue;
|
733
|
break;
|
734
|
case 'EXRULE':
|
735
|
case 'RRULE':
|
736
|
$tValue = array( $valueType => $value );
|
737
|
$xml2 = substr( $xml2, $endIx3 );
|
738
|
$valueType = FALSE;
|
739
|
while( ! empty( $xml2 )) {
|
740
|
$t = XMLgetTagContent2( $xml2, $valueType, $endIx4 );
|
741
|
switch( $valueType ) {
|
742
|
case 'freq':
|
743
|
case 'count':
|
744
|
case 'until':
|
745
|
case 'interval':
|
746
|
case 'wkst':
|
747
|
$tValue[$valueType] = $t;
|
748
|
break;
|
749
|
case 'byday':
|
750
|
if( 2 == strlen( $t ))
|
751
|
$tValue[$valueType][] = array( 'DAY' => $t );
|
752
|
else {
|
753
|
$day = substr( $t, -2 );
|
754
|
$key = substr( $t, 0, ( strlen( $t ) - 2 ));
|
755
|
$tValue[$valueType][] = array( $key, 'DAY' => $day );
|
756
|
}
|
757
|
break;
|
758
|
default:
|
759
|
$tValue[$valueType][] = $t;
|
760
|
}
|
761
|
$xml2 = substr( $xml2, $endIx4 );
|
762
|
}
|
763
|
$value = $tValue;
|
764
|
break;
|
765
|
case 'REQUEST-STATUS':
|
766
|
$tValue = array();
|
767
|
while( ! empty( $xml2 )) {
|
768
|
$t = html_entity_decode( XMLgetTagContent2( $xml2, $valueType, $endIx4 ));
|
769
|
$tValue[$valueType] = $t;
|
770
|
$xml2 = substr( $xml2, $endIx4 );
|
771
|
}
|
772
|
if( ! empty( $tValue ))
|
773
|
$value = $tValue;
|
774
|
else
|
775
|
$value = array( 'code' => null, 'description' => null );
|
776
|
break;
|
777
|
default:
|
778
|
switch( $valueType ) {
|
779
|
case 'binary': $params['VALUE'] = 'BINARY'; break;
|
780
|
case 'date': $params['VALUE'] = 'DATE'; break;
|
781
|
case 'date-time': $params['VALUE'] = 'DATE-TIME'; break;
|
782
|
case 'text':
|
783
|
case 'unknown': $value = html_entity_decode( $value ); break;
|
784
|
}
|
785
|
break;
|
786
|
}
|
787
|
if( 'FREEBUSY' == $propName ) {
|
788
|
$fbtype = $params['FBTYPE'];
|
789
|
unset( $params['FBTYPE'] );
|
790
|
$iCal->setProperty( $propName, $fbtype, $value, $params );
|
791
|
}
|
792
|
elseif( 'GEO' == $propName )
|
793
|
$iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params );
|
794
|
elseif( 'REQUEST-STATUS' == $propName ) {
|
795
|
if( !isset( $value['data'] ))
|
796
|
$value['data'] = FALSE;
|
797
|
$iCal->setProperty( $propName, $value['code'], $value['description'], $value['data'], $params );
|
798
|
}
|
799
|
else {
|
800
|
if( empty( $value ) && ( is_array( $value ) || ( '0' > $value )))
|
801
|
$value = '';
|
802
|
$iCal->setProperty( $propName, $value, $params );
|
803
|
}
|
804
|
$xml = substr( $xml, $endIx);
|
805
|
}
|
806
|
}
|
807
|
|
808
|
|
809
|
|
810
|
|
811
|
|
812
|
|
813
|
|
814
|
|
815
|
|
816
|
|
817
|
function XMLgetTagContent1( $xml, $tagName, & $endIx=0 ) {
|
818
|
$strlen = strlen( $tagName );
|
819
|
$sx1 = 0;
|
820
|
while( FALSE !== substr( $xml, $sx1, 1 )) {
|
821
|
if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 1 ), 1 )) &&
|
822
|
( strtolower( "<$tagName>" ) == strtolower( substr( $xml, $sx1, ( $strlen + 2 )))))
|
823
|
break;
|
824
|
if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 3 ), 1 )) &&
|
825
|
( strtolower( "<$tagName />" ) == strtolower( substr( $xml, $sx1, ( $strlen + 4 ))))) {
|
826
|
$endIx = $strlen + 5;
|
827
|
return '';
|
828
|
}
|
829
|
if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 2 ), 1 )) &&
|
830
|
( strtolower( "<$tagName/>" ) == strtolower( substr( $xml, $sx1, ( $strlen + 3 ))))) {
|
831
|
$endIx = $strlen + 4;
|
832
|
return '';
|
833
|
}
|
834
|
$sx1 += 1;
|
835
|
}
|
836
|
if( FALSE === substr( $xml, $sx1, 1 )) {
|
837
|
$endIx = ( empty( $sx )) ? 0 : $sx - 1;
|
838
|
return '';
|
839
|
}
|
840
|
if( FALSE === ( $pos = stripos( $xml, "</$tagName>" ))) {
|
841
|
$endIx = strlen( $xml ) + 1;
|
842
|
return '';
|
843
|
}
|
844
|
$endIx = $pos + $strlen + 3;
|
845
|
return substr( $xml, ( $sx1 + $strlen + 2 ), ( $pos - $sx1 - 2 - $strlen ));
|
846
|
}
|
847
|
|
848
|
|
849
|
|
850
|
|
851
|
|
852
|
|
853
|
|
854
|
|
855
|
|
856
|
|
857
|
function XMLgetTagContent2( $xml, & $tagName, & $endIx ) {
|
858
|
$endIx = strlen( $xml ) + 1;
|
859
|
$sx1 = 0;
|
860
|
while( FALSE !== substr( $xml, $sx1, 1 )) {
|
861
|
if( '<' == substr( $xml, $sx1, 1 )) {
|
862
|
if(( FALSE !== substr( $xml, ( $sx1 + 3 ), 1 )) && ( '<!--' == substr( $xml, $sx1, 4 )))
|
863
|
$sx1 += 1;
|
864
|
else
|
865
|
break;
|
866
|
}
|
867
|
else
|
868
|
$sx1 += 1;
|
869
|
}
|
870
|
$sx2 = $sx1;
|
871
|
while( FALSE !== substr( $xml, $sx2 )) {
|
872
|
if(( FALSE !== substr( $xml, ( $sx2 + 1 ), 1 )) && ( '/>' == substr( $xml, $sx2, 2 ))) {
|
873
|
$tagName = trim( substr( $xml, ( $sx1 + 1 ), ( $sx2 - $sx1 - 1 )));
|
874
|
$endIx = $sx2 + 2;
|
875
|
return '';
|
876
|
}
|
877
|
if( '>' == substr( $xml, $sx2, 1 ))
|
878
|
break;
|
879
|
$sx2 += 1;
|
880
|
}
|
881
|
$tagName = substr( $xml, ( $sx1 + 1 ), ( $sx2 - $sx1 - 1 ));
|
882
|
$endIx = $sx2 + 1;
|
883
|
if( FALSE === substr( $xml, $sx2, 1 )) {
|
884
|
return '';
|
885
|
}
|
886
|
$strlen = strlen( $tagName );
|
887
|
if(( 'duration' == $tagName ) &&
|
888
|
( FALSE !== ( $pos1 = stripos( $xml, "<duration>", $sx1+1 ))) &&
|
889
|
( FALSE !== ( $pos2 = stripos( $xml, "</duration>", $pos1+1 ))) &&
|
890
|
( FALSE !== ( $pos3 = stripos( $xml, "</duration>", $pos2+1 ))) &&
|
891
|
( $pos1 < $pos2 ) && ( $pos2 < $pos3 ))
|
892
|
$pos = $pos3;
|
893
|
elseif( FALSE === ( $pos = stripos( $xml, "</$tagName>", $sx2 )))
|
894
|
return '';
|
895
|
$endIx = $pos + $strlen + 3;
|
896
|
return substr( $xml, ( $sx1 + $strlen + 2 ), ( $pos - $strlen - 2 ));
|
897
|
}
|