Projet

Général

Profil

Paste
Télécharger (36,9 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / libraries / iCalcreator-2.22.1 / lib / iCal.XML.inc.php @ 9525582e

1
<?php
2
/*********************************************************************************/
3
/**
4
 *
5
 * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
 *
7
 * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
 * @link      http://kigkonsult.se/iCalcreator/index.php
9
 * @license   http://kigkonsult.se/downloads/dl.php?f=LGPL
10
 * @package   iCalcreator
11
 * @version   2.22
12
 */
13
/**
14
 * This library is free software; you can redistribute it and/or
15
 * modify it under the terms of the GNU Lesser General Public
16
 * License as published by the Free Software Foundation; either
17
 * version 2.1 of the License, or (at your option) any later version.
18
 *
19
 * This library is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22
 * Lesser General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Lesser General Public
25
 * License along with this library; if not, write to the Free Software
26
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
 */
28
/*********************************************************************************/
29
/*          iCalcreator XML (rfc6321) helper functions                           */
30
/*********************************************************************************/
31
/**
32
 * format iCal XML output, rfc6321, using PHP SimpleXMLElement
33
 *
34
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
35
 * @since 2.18.1 - 2013-08-18
36
 * @param object $calendar   iCalcreator vcalendar instance reference
37
 * @uses ICALCREATOR_VERSION
38
 * @uses vcalendar::getProperty()
39
 * @uses _addXMLchild()
40
 * @uses vcalendar::getConfig()
41
 * @uses vcalendar::getComponent()
42
 * @uses calendarComponent::$objName
43
 * @uses calendarComponent::getProperty()
44
 * @return string
45
 */
46
function iCal2XML( $calendar ) {
47
            /** fix an SimpleXMLElement instance and create root element */
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
            /** fix calendar properties */
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
            /** prepare to fix components with properties */
65
  $components   = $vcalendar->addChild( 'components' );
66
            /** fix component properties */
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':          // may occur multiple times, below
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':         // single occurence below, if set
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
      } // end switch( $prop )
224
    } // end foreach( $props as $prop )
225
            /** fix subComponent properties, if any */
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':          // may occur multiple times, below
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':      // single occurence below, if set
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'] ); // always local time
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
        } // switch( $prop )
332
      } // end foreach( $subCompProps as $prop )
333
    } // end while( FALSE !== ( $subcomp = $component->getComponent()))
334
  } // end while( FALSE !== ( $component = $calendar->getComponent()))
335
  return $xml->asXML();
336
}
337
/**
338
 * Add children to a SimpleXMLelement
339
 *
340
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
341
 * @since 2.18.10 - 2013-09-04
342
 * @param object $parent   reference to a SimpleXMLelement node
343
 * @param string $name     new element node name
344
 * @param string $type     content type, subelement(-s) name
345
 * @param string $content  new subelement content
346
 * @param array  $params   new element 'attributes'
347
 * @uses iCalUtilityFunctions::_duration2str()
348
 * @uses iCalUtilityFunctions::_geo2str2()
349
 * @uses iCalUtilityFunctions::$geoLatFmt
350
 * @uses iCalUtilityFunctions::$geoLongFmt
351
 * @return void
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
            /** create new child node */
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
  } // end if( !empty( $params ))
390
  if(( empty( $content ) && ( '0' != $content )) || ( !is_array( $content) && ( '-' != substr( $content, 0, 1 ) && ( 0 > $content ))))
391
    return;
392
            /** store content */
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
        } // end switch( $rulelabel )
509
      } // end foreach( $content as $rulelabel => $rulevalue )
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
 * parse xml file into iCalcreator instance
550
 *
551
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
552
 * @since 2.16.22 - 2013-06-18
553
 * @param  string $xmlfile
554
 * @param  array  $iCalcfg iCalcreator config array (opt)
555
 * @return mixediCalcreator instance or FALSE on error
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
 * parse xml string into iCalcreator instance, alias of XML2iCal
564
 *
565
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
566
 * @since 2.16.22 - 2013-06-18
567
 * @param  string $xmlstr
568
 * @param  array  $iCalcfg iCalcreator config array (opt)
569
 * @return mixed  iCalcreator instance or FALSE on error
570
 */
571
function XMLstr2iCal( $xmlstr, $iCalcfg=array()) {
572
  return XML2iCal( $xmlstr, $iCalcfg);
573
}
574
/**
575
 * parse xml string into iCalcreator instance
576
 *
577
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
578
 * @since 2.16.22 - 2013-06-20
579
 * @param  string $xmlstr
580
 * @param  array  $iCalcfg iCalcreator config array (opt)
581
 * @uses vcalendar::vcalendar()
582
 * @uses XMLgetComps()
583
 * @return mixed  iCalcreator instance or FALSE on error
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
 * parse XML string into iCalcreator components
595
 *
596
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
597
 * @since 2.21.11 - 2015-03-21
598
 * @param object $iCal   iCalcreator vcalendar or component object instance
599
 * @param string $xml
600
 * @uses iCalUtilityFunctions::$allComps
601
 * @uses XMLgetTagContent1()
602
 * @uses XMLgetProps()
603
 * @uses XMLgetTagContent2()
604
 * @uses vcalendar::newComponent()
605
 * @uses iCalUtilityFunctions::$allComps
606
 * @uses XMLgetComps()
607
 * @return object
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
 * parse XML into iCalcreator properties
635
 *
636
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
637
 * @since 2.21.11 - 2015-03-21
638
 * @param  array  $iCal iCalcreator calendar/component instance
639
 * @param  string $xml
640
 * @uses XMLgetTagContent2()
641
 * @uses vcalendar::setProperty()
642
 * @uses calendarComponent::setproperty()
643
 * @uses XMLgetTagContent1()
644
 * @uses vcalendar::setProperty()
645
 * @return void
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; // skip parameter valueType
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
    } // if( '<parameters>' == substr( $xml2, 0, 12 ))
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':   // multiple single-date(-times) may exist
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 ); // period
716
          $t         = array();
717
          while( ! empty( $xml3 )) {
718
            $t[]     = XMLgetTagContent2( $xml3, $pType, $endIx5 ); // start - end/duration
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
    } // end switch( $propName )
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
  } // end while( ! empty( $xml ))
806
}
807
/**
808
 * fetch a specific XML tag content
809
 *
810
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
811
 * @since 2.16.22 - 2013-06-20
812
 * @param string $xml
813
 * @param string $tagName
814
 * @param int    $endIx
815
 * @return mixed
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 ))))) { // empty tag
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 ))))) { // empty tag
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>" ))) { // missing end tag??
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
 * fetch next (unknown) XML tagname AND content
849
 *
850
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
851
 * @since 2.16.22 - 2013-06-20
852
 * @param string $xml
853
 * @param string $tagName
854
 * @param int $endIx
855
 * @return mixed
856
 */
857
function XMLgetTagContent2( $xml, & $tagName, & $endIx ) {
858
  $endIx       = strlen( $xml ) + 1; // just in case.. .
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 ))) // skip comment
863
        $sx1  += 1;
864
      else
865
        break; // tagname start here
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 ))) { // empty tag
873
      $tagName = trim( substr( $xml, ( $sx1 + 1 ), ( $sx2 - $sx1 - 1 )));
874
      $endIx   = $sx2 + 2;
875
      return '';
876
    }
877
    if( '>' == substr( $xml, $sx2, 1 )) // tagname ends here
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
}