[PLUGINS] +clavettes et dependances
[lhc/web/clavette_www.git] / www / plugins / icalendar / lib / iCalcreator.class.php
1 <?php
2 /*********************************************************************************/
3 /**
4 * iCalcreator v2.16.12
5 * copyright (c) 2007-2013 Kjell-Inge Gustafsson kigkonsult
6 * kigkonsult.se/iCalcreator/index.php
7 * ical@kigkonsult.se
8 *
9 * Description:
10 * This file is a PHP implementation of rfc2445/rfc5545.
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26 /*********************************************************************************/
27 /*********************************************************************************/
28 /* A little setup */
29 /*********************************************************************************/
30 /* your local language code */
31 // define( 'ICAL_LANG', 'sv' );
32 // alt. autosetting
33 /*
34 $langstr = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
35 $pos = strpos( $langstr, ';' );
36 if ($pos !== false) {
37 $langstr = substr( $langstr, 0, $pos );
38 $pos = strpos( $langstr, ',' );
39 if ($pos !== false) {
40 $pos = strpos( $langstr, ',' );
41 $langstr = substr( $langstr, 0, $pos );
42 }
43 define( 'ICAL_LANG', $langstr );
44 }
45 */
46 /*********************************************************************************/
47 /* version, do NOT remove!! */
48 define( 'ICALCREATOR_VERSION', 'iCalcreator 2.16.12' );
49 /*********************************************************************************/
50 /*********************************************************************************/
51 /**
52 * vcalendar class
53 *
54 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
55 * @since 2.9.6 - 2011-05-14
56 */
57 class vcalendar {
58 // calendar property variables
59 var $calscale;
60 var $method;
61 var $prodid;
62 var $version;
63 var $xprop;
64 // container for calendar components
65 var $components;
66 // component config variables
67 var $allowEmpty;
68 var $unique_id;
69 var $language;
70 var $directory;
71 var $filename;
72 var $url;
73 var $delimiter;
74 var $nl;
75 var $format;
76 var $dtzid;
77 // component internal variables
78 var $attributeDelimiter;
79 var $valueInit;
80 // component xCal declaration container
81 var $xcaldecl;
82 /**
83 * constructor for calendar object
84 *
85 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
86 * @since 2.9.6 - 2011-05-14
87 * @param array $config
88 * @return void
89 */
90 function vcalendar ( $config = array()) {
91 $this->_makeVersion();
92 $this->calscale = null;
93 $this->method = null;
94 $this->_makeUnique_id();
95 $this->prodid = null;
96 $this->xprop = array();
97 $this->language = null;
98 $this->directory = null;
99 $this->filename = null;
100 $this->url = null;
101 $this->dtzid = null;
102 /**
103 * language = <Text identifying a language, as defined in [RFC 1766]>
104 */
105 if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
106 $config['language'] = ICAL_LANG;
107 if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
108 if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
109 if( !isset( $config['format'] )) $config['format'] = 'iCal';
110 if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
111 $this->setConfig( $config );
112
113 $this->xcaldecl = array();
114 $this->components = array();
115 }
116 /*********************************************************************************/
117 /**
118 * Property Name: CALSCALE
119 */
120 /**
121 * creates formatted output for calendar property calscale
122 *
123 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
124 * @since 2.10.16 - 2011-10-28
125 * @return string
126 */
127 function createCalscale() {
128 if( empty( $this->calscale )) return FALSE;
129 switch( $this->format ) {
130 case 'xcal':
131 return $this->nl.' calscale="'.$this->calscale.'"';
132 break;
133 default:
134 return 'CALSCALE:'.$this->calscale.$this->nl;
135 break;
136 }
137 }
138 /**
139 * set calendar property calscale
140 *
141 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
142 * @since 2.4.8 - 2008-10-21
143 * @param string $value
144 * @return void
145 */
146 function setCalscale( $value ) {
147 if( empty( $value )) return FALSE;
148 $this->calscale = $value;
149 }
150 /*********************************************************************************/
151 /**
152 * Property Name: METHOD
153 */
154 /**
155 * creates formatted output for calendar property method
156 *
157 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
158 * @since 2.10.16 - 2011-10-28
159 * @return string
160 */
161 function createMethod() {
162 if( empty( $this->method )) return FALSE;
163 switch( $this->format ) {
164 case 'xcal':
165 return $this->nl.' method="'.$this->method.'"';
166 break;
167 default:
168 return 'METHOD:'.$this->method.$this->nl;
169 break;
170 }
171 }
172 /**
173 * set calendar property method
174 *
175 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
176 * @since 2.4.8 - 2008-20-23
177 * @param string $value
178 * @return bool
179 */
180 function setMethod( $value ) {
181 if( empty( $value )) return FALSE;
182 $this->method = $value;
183 return TRUE;
184 }
185 /*********************************************************************************/
186 /**
187 * Property Name: PRODID
188 *
189 * The identifier is RECOMMENDED to be the identical syntax to the
190 * [RFC 822] addr-spec. A good method to assure uniqueness is to put the
191 * domain name or a domain literal IP address of the host on which.. .
192 */
193 /**
194 * creates formatted output for calendar property prodid
195 *
196 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
197 * @since 2.12.11 - 2012-05-13
198 * @return string
199 */
200 function createProdid() {
201 if( !isset( $this->prodid ))
202 $this->_makeProdid();
203 switch( $this->format ) {
204 case 'xcal':
205 return $this->nl.' prodid="'.$this->prodid.'"';
206 break;
207 default:
208 $toolbox = new calendarComponent();
209 $toolbox->setConfig( $this->getConfig());
210 return $toolbox->_createElement( 'PRODID', '', $this->prodid );
211 break;
212 }
213 }
214 /**
215 * make default value for calendar prodid
216 *
217 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
218 * @since 2.6.8 - 2009-12-30
219 * @return void
220 */
221 function _makeProdid() {
222 $this->prodid = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language );
223 }
224 /**
225 * Conformance: The property MUST be specified once in an iCalendar object.
226 * Description: The vendor of the implementation SHOULD assure that this
227 * is a globally unique identifier; using some technique such as an FPI
228 * value, as defined in [ISO 9070].
229 */
230 /**
231 * make default unique_id for calendar prodid
232 *
233 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
234 * @since 0.3.0 - 2006-08-10
235 * @return void
236 */
237 function _makeUnique_id() {
238 $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
239 }
240 /*********************************************************************************/
241 /**
242 * Property Name: VERSION
243 *
244 * Description: A value of "2.0" corresponds to this memo.
245 */
246 /**
247 * creates formatted output for calendar property version
248
249 *
250 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
251 * @since 2.10.16 - 2011-10-28
252 * @return string
253 */
254 function createVersion() {
255 if( empty( $this->version ))
256 $this->_makeVersion();
257 switch( $this->format ) {
258 case 'xcal':
259 return $this->nl.' version="'.$this->version.'"';
260 break;
261 default:
262 return 'VERSION:'.$this->version.$this->nl;
263 break;
264 }
265 }
266 /**
267 * set default calendar version
268 *
269 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
270 * @since 0.3.0 - 2006-08-10
271 * @return void
272 */
273 function _makeVersion() {
274 $this->version = '2.0';
275 }
276 /**
277 * set calendar version
278 *
279 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
280 * @since 2.4.8 - 2008-10-23
281 * @param string $value
282 * @return void
283 */
284 function setVersion( $value ) {
285 if( empty( $value )) return FALSE;
286 $this->version = $value;
287 return TRUE;
288 }
289 /*********************************************************************************/
290 /**
291 * Property Name: x-prop
292 */
293 /**
294 * creates formatted output for calendar property x-prop, iCal format only
295 *
296 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
297 * @since 2.16.2 - 2012-12-18
298 * @return string
299 */
300 function createXprop() {
301 if( empty( $this->xprop ) || !is_array( $this->xprop )) return FALSE;
302 $output = null;
303 $toolbox = new calendarComponent();
304 $toolbox->setConfig( $this->getConfig());
305 foreach( $this->xprop as $label => $xpropPart ) {
306 if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) {
307 $output .= $toolbox->_createElement( $label );
308 continue;
309 }
310 $attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
311 if( is_array( $xpropPart['value'] )) {
312 foreach( $xpropPart['value'] as $pix => $theXpart )
313 $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep( $theXpart, $this->format, $this->nl );
314 $xpropPart['value'] = implode( ',', $xpropPart['value'] );
315 }
316 else
317 $xpropPart['value'] = iCalUtilityFunctions::_strrep( $xpropPart['value'], $this->format, $this->nl );
318 $output .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] );
319 if( is_array( $toolbox->xcaldecl ) && ( 0 < count( $toolbox->xcaldecl ))) {
320 foreach( $toolbox->xcaldecl as $localxcaldecl )
321 $this->xcaldecl[] = $localxcaldecl;
322 }
323 }
324 return $output;
325 }
326 /**
327 * set calendar property x-prop
328 *
329 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
330 * @since 2.11.9 - 2012-01-16
331 * @param string $label
332 * @param string $value
333 * @param array $params optional
334 * @return bool
335 */
336 function setXprop( $label, $value, $params=FALSE ) {
337 if( empty( $label ))
338 return FALSE;
339 if( 'X-' != strtoupper( substr( $label, 0, 2 )))
340 return FALSE;
341 if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
342 $xprop = array( 'value' => $value );
343 $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
344 if( !is_array( $this->xprop )) $this->xprop = array();
345 $this->xprop[strtoupper( $label )] = $xprop;
346 return TRUE;
347 }
348 /*********************************************************************************/
349 /**
350 * delete calendar property value
351 *
352 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
353 * @since 2.8.8 - 2011-03-15
354 * @param mixed $propName, bool FALSE => X-property
355 * @param int $propix, optional, if specific property is wanted in case of multiply occurences
356 * @return bool, if successfull delete
357 */
358 function deleteProperty( $propName=FALSE, $propix=FALSE ) {
359 $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
360 if( !$propix )
361 $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
362 $this->propdelix[$propName] = --$propix;
363 $return = FALSE;
364 switch( $propName ) {
365 case 'CALSCALE':
366 if( isset( $this->calscale )) {
367 $this->calscale = null;
368 $return = TRUE;
369 }
370 break;
371 case 'METHOD':
372 if( isset( $this->method )) {
373 $this->method = null;
374 $return = TRUE;
375 }
376 break;
377 default:
378 $reduced = array();
379 if( $propName != 'X-PROP' ) {
380 if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; }
381 foreach( $this->xprop as $k => $a ) {
382 if(( $k != $propName ) && !empty( $a ))
383 $reduced[$k] = $a;
384 }
385 }
386 else {
387 if( count( $this->xprop ) <= $propix ) return FALSE;
388 $xpropno = 0;
389 foreach( $this->xprop as $xpropkey => $xpropvalue ) {
390 if( $propix != $xpropno )
391 $reduced[$xpropkey] = $xpropvalue;
392 $xpropno++;
393 }
394 }
395 $this->xprop = $reduced;
396 if( empty( $this->xprop )) {
397 unset( $this->propdelix[$propName] );
398 return FALSE;
399 }
400 return TRUE;
401 }
402 return $return;
403 }
404 /**
405 * get calendar property value/params
406 *
407 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
408 * @since 2.13.4 - 2012-08-08
409 * @param string $propName, optional
410 * @param int $propix, optional, if specific property is wanted in case of multiply occurences
411 * @param bool $inclParam=FALSE
412 * @return mixed
413 */
414 function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) {
415 $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
416 if( 'X-PROP' == $propName ) {
417 if( !$propix )
418 $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
419 $this->propix[$propName] = --$propix;
420 }
421 else
422 $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' );
423 switch( $propName ) {
424 case 'ATTENDEE':
425 case 'CATEGORIES':
426 case 'CONTACT':
427 case 'DTSTART':
428 case 'GEOLOCATION':
429 case 'LOCATION':
430 case 'ORGANIZER':
431 case 'PRIORITY':
432 case 'RESOURCES':
433 case 'STATUS':
434 case 'SUMMARY':
435 case 'RECURRENCE-ID-UID':
436 case 'RELATED-TO':
437 case 'R-UID':
438 case 'UID':
439 case 'URL':
440 $output = array();
441 foreach ( $this->components as $cix => $component) {
442 if( !in_array( $component->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' )))
443 continue;
444 if( in_array( strtoupper( $propName ), $mProps )) {
445 $component->_getProperties( $propName, $output );
446 continue;
447 }
448 elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) {
449 if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' )))
450 $content = $component->getProperty( 'UID' );
451 }
452 elseif( 'GEOLOCATION' == $propName ) {
453 $content = $component->getProperty( 'LOCATION' );
454 $content = ( !empty( $content )) ? $content.' ' : '';
455 if(( FALSE === ( $geo = $component->getProperty( 'GEO' ))) || empty( $geo ))
456 continue;
457 if( 0.0 < $geo['latitude'] )
458 $sign = '+';
459 else
460 $sign = ( 0.0 > $geo['latitude'] ) ? '-' : '';
461 $content .= ' '.$sign.sprintf( "%09.6f", abs( $geo['latitude'] ));
462 $content = rtrim( rtrim( $content, '0' ), '.' );
463 if( 0.0 < $geo['longitude'] )
464 $sign = '+';
465 else
466 $sign = ( 0.0 > $geo['longitude'] ) ? '-' : '';
467 $content .= $sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/';
468 }
469 elseif( FALSE === ( $content = $component->getProperty( $propName )))
470 continue;
471 if(( FALSE === $content ) || empty( $content ))
472 continue;
473 elseif( is_array( $content )) {
474 if( isset( $content['year'] )) {
475 $key = sprintf( '%04d%02d%02d', $content['year'], $content['month'], $content['day'] );
476 if( !isset( $output[$key] ))
477 $output[$key] = 1;
478 else
479 $output[$key] += 1;
480 }
481 else {
482 foreach( $content as $partValue => $partCount ) {
483 if( !isset( $output[$partValue] ))
484 $output[$partValue] = $partCount;
485 else
486 $output[$partValue] += $partCount;
487 }
488 }
489 } // end elseif( is_array( $content )) {
490 elseif( !isset( $output[$content] ))
491 $output[$content] = 1;
492 else
493 $output[$content] += 1;
494 } // end foreach ( $this->components as $cix => $component)
495 if( !empty( $output ))
496 ksort( $output );
497 return $output;
498 break;
499 case 'CALSCALE':
500 return ( !empty( $this->calscale )) ? $this->calscale : FALSE;
501 break;
502 case 'METHOD':
503 return ( !empty( $this->method )) ? $this->method : FALSE;
504 break;
505 case 'PRODID':
506 if( empty( $this->prodid ))
507 $this->_makeProdid();
508 return $this->prodid;
509 break;
510 case 'VERSION':
511 return ( !empty( $this->version )) ? $this->version : FALSE;
512 break;
513 default:
514 if( $propName != 'X-PROP' ) {
515 if( !isset( $this->xprop[$propName] )) return FALSE;
516 return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
517 : array( $propName, $this->xprop[$propName]['value'] );
518 }
519 else {
520 if( empty( $this->xprop )) return FALSE;
521 $xpropno = 0;
522 foreach( $this->xprop as $xpropkey => $xpropvalue ) {
523 if( $propix == $xpropno )
524 return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
525 : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
526 else
527 $xpropno++;
528 }
529 unset( $this->propix[$propName] );
530 return FALSE; // not found ??
531 }
532 }
533 return FALSE;
534 }
535 /**
536 * general vcalendar property setting
537 *
538 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
539 * @since 2.2.13 - 2007-11-04
540 * @param mixed $args variable number of function arguments,
541 * first argument is ALWAYS component name,
542 * second ALWAYS component value!
543 * @return bool
544 */
545 function setProperty () {
546 $numargs = func_num_args();
547 if( 1 > $numargs )
548 return FALSE;
549 $arglist = func_get_args();
550 $arglist[0] = strtoupper( $arglist[0] );
551 switch( $arglist[0] ) {
552 case 'CALSCALE':
553 return $this->setCalscale( $arglist[1] );
554 case 'METHOD':
555 return $this->setMethod( $arglist[1] );
556 case 'VERSION':
557 return $this->setVersion( $arglist[1] );
558 default:
559 if( !isset( $arglist[1] )) $arglist[1] = null;
560 if( !isset( $arglist[2] )) $arglist[2] = null;
561 return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
562 }
563 return FALSE;
564 }
565 /*********************************************************************************/
566 /**
567 * get vcalendar config values or * calendar components
568 *
569 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
570 * @since 2.11.7 - 2012-01-12
571 * @param mixed $config
572 * @return value
573 */
574 function getConfig( $config = FALSE ) {
575 if( !$config ) {
576 $return = array();
577 $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
578 $return['DELIMITER'] = $this->getConfig( 'DELIMITER' );
579 $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' );
580 $return['FILENAME'] = $this->getConfig( 'FILENAME' );
581 $return['DIRFILE'] = $this->getConfig( 'DIRFILE' );
582 $return['FILESIZE'] = $this->getConfig( 'FILESIZE' );
583 $return['FORMAT'] = $this->getConfig( 'FORMAT' );
584 if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
585 $return['LANGUAGE'] = $lang;
586 $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
587 $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
588 if( FALSE !== ( $url = $this->getConfig( 'URL' )))
589 $return['URL'] = $url;
590 $return['TZID'] = $this->getConfig( 'TZID' );
591 return $return;
592 }
593 switch( strtoupper( $config )) {
594 case 'ALLOWEMPTY':
595 return $this->allowEmpty;
596 break;
597 case 'COMPSINFO':
598 unset( $this->compix );
599 $info = array();
600 foreach( $this->components as $cix => $component ) {
601 if( empty( $component )) continue;
602 $info[$cix]['ordno'] = $cix + 1;
603 $info[$cix]['type'] = $component->objName;
604 $info[$cix]['uid'] = $component->getProperty( 'uid' );
605 $info[$cix]['props'] = $component->getConfig( 'propinfo' );
606 $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
607 }
608 return $info;
609 break;
610 case 'DELIMITER':
611 return $this->delimiter;
612 break;
613 case 'DIRECTORY':
614 if( empty( $this->directory ) && ( '0' != $this->directory ))
615 $this->directory = '.';
616 return $this->directory;
617 break;
618 case 'DIRFILE':
619 return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' );
620 break;
621 case 'FILEINFO':
622 return array( $this->getConfig( 'directory' )
623 , $this->getConfig( 'filename' )
624 , $this->getConfig( 'filesize' ));
625 break;
626 case 'FILENAME':
627 if( empty( $this->filename ) && ( '0' != $this->filename )) {
628 if( 'xcal' == $this->format )
629 $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. .
630 else
631 $this->filename = date( 'YmdHis' ).'.ics';
632 }
633 return $this->filename;
634 break;
635 case 'FILESIZE':
636 $size = 0;
637 if( empty( $this->url )) {
638 $dirfile = $this->getConfig( 'dirfile' );
639 if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile ))))
640 $size = 0;
641 clearstatcache();
642 }
643 return $size;
644 break;
645 case 'FORMAT':
646 return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal';
647 break;
648 case 'LANGUAGE':
649 /* get language for calendar component as defined in [RFC 1766] */
650 return $this->language;
651 break;
652 case 'NL':
653 case 'NEWLINECHAR':
654 return $this->nl;
655 break;
656 case 'TZID':
657 return $this->dtzid;
658 break;
659 case 'UNIQUE_ID':
660 return $this->unique_id;
661 break;
662 case 'URL':
663 if( !empty( $this->url ))
664 return $this->url;
665 else
666 return FALSE;
667 break;
668 }
669 }
670 /**
671 * general vcalendar config setting
672 *
673 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
674 * @since 2.16.7 - 2013-01-11
675 * @param mixed $config
676 * @param string $value
677 * @return void
678 */
679 function setConfig( $config, $value = FALSE) {
680 if( is_array( $config )) {
681 $ak = array_keys( $config );
682 foreach( $ak as $k ) {
683 if( 'DIRECTORY' == strtoupper( $k )) {
684 if( FALSE === $this->setConfig( 'DIRECTORY', $config[$k] ))
685 return FALSE;
686 unset( $config[$k] );
687 }
688 elseif( 'NEWLINECHAR' == strtoupper( $k )) {
689 if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] ))
690 return FALSE;
691 unset( $config[$k] );
692 }
693 }
694 foreach( $config as $cKey => $cValue ) {
695 if( FALSE === $this->setConfig( $cKey, $cValue ))
696 return FALSE;
697 }
698 return TRUE;
699 }
700 $res = FALSE;
701 switch( strtoupper( $config )) {
702 case 'ALLOWEMPTY':
703 $this->allowEmpty = $value;
704 $subcfg = array( 'ALLOWEMPTY' => $value );
705 $res = TRUE;
706 break;
707 case 'DELIMITER':
708 $this->delimiter = $value;
709 return TRUE;
710 break;
711 case 'DIRECTORY':
712 $value = trim( $value );
713 $del = $this->getConfig('delimiter');
714 if( $del == substr( $value, ( 0 - strlen( $del ))))
715 $value = substr( $value, 0, ( strlen( $value ) - strlen( $del )));
716 if( is_dir( $value )) {
717 /* local directory */
718 clearstatcache();
719 $this->directory = $value;
720 $this->url = null;
721 return TRUE;
722 }
723 else
724 return FALSE;
725 break;
726 case 'FILENAME':
727 $value = trim( $value );
728 if( !empty( $this->url )) {
729 /* remote directory+file -> URL */
730 $this->filename = $value;
731 return TRUE;
732 }
733 $dirfile = $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$value;
734 if( file_exists( $dirfile )) {
735 /* local file exists */
736 if( is_readable( $dirfile ) || is_writable( $dirfile )) {
737 clearstatcache();
738 $this->filename = $value;
739 return TRUE;
740 }
741 else
742 return FALSE;
743 }
744 elseif( is_readable($this->getConfig( 'directory' ) ) || is_writable( $this->getConfig( 'directory' ) )) {
745 /* read- or writable directory */
746 $this->filename = $value;
747 return TRUE;
748 }
749 else
750 return FALSE;
751 break;
752 case 'FORMAT':
753 $value = trim( strtolower( $value ));
754 if( 'xcal' == $value ) {
755 $this->format = 'xcal';
756 $this->attributeDelimiter = $this->nl;
757 $this->valueInit = null;
758 }
759 else {
760 $this->format = null;
761 $this->attributeDelimiter = ';';
762 $this->valueInit = ':';
763 }
764 $subcfg = array( 'FORMAT' => $value );
765 $res = TRUE;
766 break;
767 case 'LANGUAGE': // set language for calendar component as defined in [RFC 1766]
768 $value = trim( $value );
769 $this->language = $value;
770 $this->_makeProdid();
771 $subcfg = array( 'LANGUAGE' => $value );
772 $res = TRUE;
773 break;
774 case 'NL':
775 case 'NEWLINECHAR':
776 $this->nl = $value;
777 if( 'xcal' == $value ) {
778 $this->attributeDelimiter = $this->nl;
779 $this->valueInit = null;
780 }
781 else {
782 $this->attributeDelimiter = ';';
783 $this->valueInit = ':';
784 }
785 $subcfg = array( 'NL' => $value );
786 $res = TRUE;
787 break;
788 case 'TZID':
789 $this->dtzid = $value;
790 $subcfg = array( 'TZID' => $value );
791 $res = TRUE;
792 break;
793 case 'UNIQUE_ID':
794 $value = trim( $value );
795 $this->unique_id = $value;
796 $this->_makeProdid();
797 $subcfg = array( 'UNIQUE_ID' => $value );
798 $res = TRUE;
799 break;
800 case 'URL':
801 /* remote file - URL */
802 $value = str_replace( array( 'HTTP://', 'WEBCAL://', 'webcal://' ), 'http://', trim( $value ));
803 if( 'http://' != substr( $value, 0, 7 ))
804 return FALSE;
805 $s1 = $this->url;
806 $this->url = $value;
807 $s2 = $this->directory;
808 $this->directory = null;
809 $parts = pathinfo( $value );
810 if( FALSE === $this->setConfig( 'filename', $parts['basename'] )) {
811 $this->url = $s1;
812 $this->directory = $s2;
813 return FALSE;
814 }
815 else
816 return TRUE;
817 break;
818 default: // any unvalid config key.. .
819 return TRUE;
820 }
821 if( !$res ) return FALSE;
822 if( isset( $subcfg ) && !empty( $this->components )) {
823 foreach( $subcfg as $cfgkey => $cfgvalue ) {
824 foreach( $this->components as $cix => $component ) {
825 $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE );
826 if( !$res )
827 break 2;
828 $this->components[$cix] = $component->copy(); // PHP4 compliant
829 }
830 }
831 }
832 return $res;
833 }
834 /*********************************************************************************/
835 /**
836 * add calendar component to container
837 *
838 * alias to setComponent
839 *
840 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
841 * @since 1.x.x - 2007-04-24
842 * @param object $component calendar component
843 * @return void
844 */
845 function addComponent( $component ) {
846 $this->setComponent( $component );
847 }
848 /**
849 * delete calendar component from container
850 *
851 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
852 * @since 2.8.8 - 2011-03-15
853 * @param mixed $arg1 ordno / component type / component uid
854 * @param mixed $arg2 optional, ordno if arg1 = component type
855 * @return void
856 */
857 function deleteComponent( $arg1, $arg2=FALSE ) {
858 $argType = $index = null;
859 if ( ctype_digit( (string) $arg1 )) {
860 $argType = 'INDEX';
861 $index = (int) $arg1 - 1;
862 }
863 elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
864 $argType = strtolower( $arg1 );
865 $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
866 }
867 $cix1dC = 0;
868 foreach ( $this->components as $cix => $component) {
869 if( empty( $component )) continue;
870 if(( 'INDEX' == $argType ) && ( $index == $cix )) {
871 unset( $this->components[$cix] );
872 return TRUE;
873 }
874 elseif( $argType == $component->objName ) {
875 if( $index == $cix1dC ) {
876 unset( $this->components[$cix] );
877 return TRUE;
878 }
879 $cix1dC++;
880 }
881 elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
882 unset( $this->components[$cix] );
883 return TRUE;
884 }
885 }
886 return FALSE;
887 }
888 /**
889 * get calendar component from container
890 *
891 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
892 * @since 2.13.5 - 2012-08-08
893 * @param mixed $arg1 optional, ordno/component type/ component uid
894 * @param mixed $arg2 optional, ordno if arg1 = component type
895 * @return object
896 */
897 function getComponent( $arg1=FALSE, $arg2=FALSE ) {
898 $index = $argType = null;
899 if ( !$arg1 ) { // first or next in component chain
900 $argType = 'INDEX';
901 $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
902 }
903 elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain
904 $argType = 'INDEX';
905 $index = (int) $arg1;
906 unset( $this->compix );
907 }
908 elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
909 $arg2 = implode( '-', array_keys( $arg1 ));
910 $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1;
911 $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' );
912 $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' );
913 $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' );
914 }
915 elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name
916 unset( $this->compix['INDEX'] );
917 $argType = strtolower( $arg1 );
918 if( !$arg2 )
919 $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
920 elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
921 $index = (int) $arg2;
922 }
923 elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument
924 if( !$arg2 )
925 $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1;
926 elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
927 $index = (int) $arg2;
928 }
929 if( isset( $index ))
930 $index -= 1;
931 $ckeys = array_keys( $this->components );
932 if( !empty( $index) && ( $index > end( $ckeys )))
933 return FALSE;
934 $cix1gC = 0;
935 foreach ( $this->components as $cix => $component) {
936 if( empty( $component )) continue;
937 if(( 'INDEX' == $argType ) && ( $index == $cix ))
938 return $component->copy();
939 elseif( $argType == $component->objName ) {
940 if( $index == $cix1gC )
941 return $component->copy();
942 $cix1gC++;
943 }
944 elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
945 $hit = array();
946 foreach( $arg1 as $pName => $pValue ) {
947 $pName = strtoupper( $pName );
948 if( !in_array( $pName, $dateProps ) && !in_array( $pName, $otherProps ))
949 continue;
950 if( in_array( $pName, $mProps )) { // multiple occurrence
951 $propValues = array();
952 $component->_getProperties( $pName, $propValues );
953 $propValues = array_keys( $propValues );
954 $hit[] = ( in_array( $pValue, $propValues )) ? TRUE : FALSE;
955 continue;
956 } // end if(.. .// multiple occurrence
957 if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence
958 $hit[] = FALSE; // missing property
959 continue;
960 }
961 if( 'SUMMARY' == $pName ) { // exists within (any case)
962 $hit[] = ( FALSE !== stripos( $value, $pValue )) ? TRUE : FALSE;
963 continue;
964 }
965 if( in_array( strtoupper( $pName ), $dateProps )) {
966 $valuedate = sprintf( '%04d%02d%02d', $value['year'], $value['month'], $value['day'] );
967 if( 8 < strlen( $pValue )) {
968 if( isset( $value['hour'] )) {
969 if( 'T' == substr( $pValue, 8, 1 ))
970 $pValue = str_replace( 'T', '', $pValue );
971 $valuedate .= sprintf( '%02d%02d%02d', $value['hour'], $value['min'], $value['sec'] );
972 }
973 else
974 $pValue = substr( $pValue, 0, 8 );
975 }
976 $hit[] = ( $pValue == $valuedate ) ? TRUE : FALSE;
977 continue;
978 }
979 elseif( !is_array( $value ))
980 $value = array( $value );
981 foreach( $value as $part ) {
982 $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part );
983 foreach( $part as $subPart ) {
984 if( $pValue == $subPart ) {
985 $hit[] = TRUE;
986 continue 3;
987 }
988 }
989 } // end foreach( $value as $part )
990 $hit[] = FALSE; // no hit in property
991 } // end foreach( $arg1 as $pName => $pValue )
992 if( in_array( TRUE, $hit )) {
993 if( $index == $cix1gC )
994 return $component->copy();
995 $cix1gC++;
996 }
997 } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
998 elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID
999 if( $index == $cix1gC )
1000 return $component->copy();
1001 $cix1gC++;
1002 }
1003 } // end foreach ( $this->components.. .
1004 /* not found.. . */
1005 unset( $this->compix );
1006 return FALSE;
1007 }
1008 /**
1009 * create new calendar component, already included within calendar
1010 *
1011 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1012 * @since 2.6.33 - 2011-01-03
1013 * @param string $compType component type
1014 * @return object (reference)
1015 */
1016 function & newComponent( $compType ) {
1017 $config = $this->getConfig();
1018 $keys = array_keys( $this->components );
1019 $ix = end( $keys) + 1;
1020 switch( strtoupper( $compType )) {
1021 case 'EVENT':
1022 case 'VEVENT':
1023 $this->components[$ix] = new vevent( $config );
1024 break;
1025 case 'TODO':
1026 case 'VTODO':
1027 $this->components[$ix] = new vtodo( $config );
1028 break;
1029 case 'JOURNAL':
1030 case 'VJOURNAL':
1031 $this->components[$ix] = new vjournal( $config );
1032 break;
1033 case 'FREEBUSY':
1034 case 'VFREEBUSY':
1035 $this->components[$ix] = new vfreebusy( $config );
1036 break;
1037 case 'TIMEZONE':
1038 case 'VTIMEZONE':
1039 array_unshift( $this->components, new vtimezone( $config ));
1040 $ix = 0;
1041 break;
1042 default:
1043 return FALSE;
1044 }
1045 return $this->components[$ix];
1046 }
1047 /**
1048 * select components from calendar on date or selectOption basis
1049 *
1050 * Ensure DTSTART is set for every component.
1051 * No date controls occurs.
1052 *
1053 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1054 * @since 2.16.12 - 2013-02-10
1055 * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] )
1056 * @param int $startM optional, start Month, default current Month
1057 * @param int $startD optional, start Day, default current Day
1058 * @param int $endY optional, end Year, default $startY
1059 * @param int $endY optional, end Month, default $startM
1060 * @param int $endY optional, end Day, default $startD
1061 * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
1062 * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][]
1063 * TRUE => output : array[] (ignores split)
1064 * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period
1065 * FALSE - only component(-s) that starts within period
1066 * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the
1067 * period (implies flat=FALSE)
1068 * FALSE - one occurance of component only in output array
1069 * @return array or FALSE
1070 */
1071 function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) {
1072 /* check if empty calendar */
1073 if( 0 >= count( $this->components )) return FALSE;
1074 if( is_array( $startY ))
1075 return $this->selectComponents2( $startY );
1076 /* check default dates */
1077 if( !$startY ) $startY = date( 'Y' );
1078 if( !$startM ) $startM = date( 'm' );
1079 if( !$startD ) $startD = date( 'd' );
1080 $startDate = mktime( 0, 0, 0, $startM, $startD, $startY );
1081 if( !$endY ) $endY = $startY;
1082 if( !$endM ) $endM = $startM;
1083 if( !$endD ) $endD = $startD;
1084 $endDate = mktime( 23, 59, 59, $endM, $endD, $endY );
1085 // echo 'selectComp arg='.date( 'Y-m-d H:i:s', $startDate).' -- '.date( 'Y-m-d H:i:s', $endDate)."<br />\n"; $tcnt = 0;// test ###
1086 /* check component types */
1087 $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
1088 if( is_array( $cType )) {
1089 foreach( $cType as $cix => $theType ) {
1090 $cType[$cix] = $theType = strtolower( $theType );
1091 if( !in_array( $theType, $validTypes ))
1092 $cType[$cix] = 'vevent';
1093 }
1094 $cType = array_unique( $cType );
1095 }
1096 elseif( !empty( $cType )) {
1097 $cType = strtolower( $cType );
1098 if( !in_array( $cType, $validTypes ))
1099 $cType = array( 'vevent' );
1100 else
1101 $cType = array( $cType );
1102 }
1103 else
1104 $cType = $validTypes;
1105 if( 0 >= count( $cType ))
1106 $cType = $validTypes;
1107 if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination
1108 $split = FALSE;
1109 if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination
1110 $split = FALSE;
1111 /* iterate components */
1112 $result = array();
1113 $this->sort( 'UID' );
1114 $compUIDcmp = null;
1115 $recurridList = array();
1116 foreach ( $this->components as $cix => $component ) {
1117 if( empty( $component )) continue;
1118 unset( $start );
1119 /* deselect unvalid type components */
1120 if( !in_array( $component->objName, $cType ))
1121 continue;
1122 $start = $component->getProperty( 'dtstart' );
1123 /* select due when dtstart is missing */
1124 if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' ))))
1125 continue;
1126 if( empty( $start ))
1127 continue;
1128 $compUID = $component->getProperty( 'UID' );
1129 if( $compUIDcmp != $compUID ) {
1130 $compUIDcmp = $compUID;
1131 unset( $exdatelist, $recurridList );
1132 }
1133 $dtendExist = $dueExist = $durationExist = $endAllDayEvent = $recurrid = FALSE;
1134 unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $workstart, $workend, $endDateFormat ); // clean up
1135 $startWdate = iCalUtilityFunctions::_date2timestamp( $start );
1136 $startDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
1137 /* get end date from dtend/due/duration properties */
1138 $end = $component->getProperty( 'dtend' );
1139 if( !empty( $end )) {
1140 $dtendExist = TRUE;
1141 $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
1142 }
1143 if( empty( $end ) && ( $component->objName == 'vtodo' )) {
1144 $end = $component->getProperty( 'due' );
1145 if( !empty( $end )) {
1146 $dueExist = TRUE;
1147 $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
1148 }
1149 }
1150 if( !empty( $end ) && !isset( $end['hour'] )) {
1151 /* a DTEND without time part regards an event that ends the day before,
1152 for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
1153 $endAllDayEvent = TRUE;
1154 $endWdate = mktime( 23, 59, 59, $end['month'], ($end['day'] - 1), $end['year'] );
1155 $end['year'] = date( 'Y', $endWdate );
1156 $end['month'] = date( 'm', $endWdate );
1157 $end['day'] = date( 'd', $endWdate );
1158 $end['hour'] = 23;
1159 $end['min'] = $end['sec'] = 59;
1160 }
1161 if( empty( $end )) {
1162 $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format
1163 if( !empty( $end ))
1164 $durationExist = TRUE;
1165 $endDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
1166 // if( !empty($end)) echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
1167 }
1168 if( empty( $end )) { // assume one day duration if missing end date
1169 $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
1170 }
1171 // if( isset($end)) echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
1172 $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
1173 if( $endWdate < $startWdate ) { // MUST be after start date!!
1174 $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
1175 $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
1176 }
1177 $rdurWsecs = $endWdate - $startWdate; // compute event (component) duration in seconds
1178 /* make a list of optional exclude dates for component occurence from exrule and exdate */
1179 $exdatelist = array();
1180 $workstart = iCalUtilityFunctions::_timestamp2date(( $startDate - $rdurWsecs ), 6);
1181 $workend = iCalUtilityFunctions::_timestamp2date(( $endDate + $rdurWsecs ), 6);
1182 while( FALSE !== ( $exrule = $component->getProperty( 'exrule' ))) // check exrule
1183 iCalUtilityFunctions::_recur2date( $exdatelist, $exrule, $start, $workstart, $workend );
1184 while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) { // check exdate
1185 foreach( $exdate as $theExdate ) {
1186 $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate );
1187 $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate )); // on a day-basis !!!
1188 if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate ))
1189 $exdatelist[$exWdate] = TRUE;
1190 } // end - foreach( $exdate as $theExdate )
1191 } // end - check exdate
1192 /* check recurrence-id (note, a missing sequence=0, don't test foer sequence), remove hit with reccurr-id date */
1193 if( FALSE !== ( $recurrid = $component->getProperty( 'recurrence-id' ))) {
1194 // echo "adding ".$recurrid['year'].'-'.$recurrid['month'].'-'.$recurrid['day']." to recurridList<br>\n"; // test ###
1195 $recurrid = iCalUtilityFunctions::_date2timestamp( $recurrid );
1196 $recurrid = mktime( 0, 0, 0, date( 'm', $recurrid ), date( 'd', $recurrid ), date( 'Y', $recurrid )); // on a day-basis !!!
1197 $recurridList[$recurrid] = TRUE; // no recurring to start this day
1198 } // end recurrence-id/sequence test
1199 /* select only components with.. . */
1200 if(( !$any && ( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) || // (dt)start within the period
1201 ( $any && ( $startWdate < $endDate ) && ( $endWdate >= $startDate ))) { // occurs within the period
1202 /* add the selected component (WITHIN valid dates) to output array */
1203 if( $flat ) { // any=true/false, ignores split
1204 if( !$recurrid )
1205 $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id)
1206 }
1207 elseif( $split ) { // split the original component
1208 if( $endWdate > $endDate )
1209 $endWdate = $endDate; // use period end date
1210 $rstart = $startWdate;
1211 if( $rstart < $startDate )
1212 $rstart = $startDate; // use period start date
1213 $startYMD = $rstartYMD = date( 'Ymd', $rstart );
1214 $endYMD = date( 'Ymd', $endWdate );
1215 $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1216 // echo "start org comp = $rstartYMD, endYMD=$endYMD<br />\n"; // test ###
1217 if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
1218 while( $rstartYMD <= $endYMD ) { // iterate
1219 if( isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist
1220 $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1221 $rstartYMD = date( 'Ymd', $rstart );
1222 continue;
1223 }
1224 if( $rstartYMD > $startYMD ) // date after dtstart
1225 $datestring = date( $startDateFormat, $checkDate ); // mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )));
1226 else
1227 $datestring = date( $startDateFormat, $rstart );
1228 if( isset( $start['tz'] ))
1229 $datestring .= ' '.$start['tz'];
1230 // echo "split org comp rstartYMD=$rstartYMD (datestring=$datestring)<br />\n"; // test ###
1231 $component->setProperty( 'X-CURRENT-DTSTART', $datestring );
1232 if( $dtendExist || $dueExist || $durationExist ) {
1233 if( $rstartYMD < $endYMD ) // not the last day
1234 $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ));
1235 else
1236 $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1237 if( $endAllDayEvent && $dtendExist )
1238 $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day
1239 $datestring = date( $endDateFormat, $tend );
1240 if( isset( $end['tz'] ))
1241 $datestring .= ' '.$end['tz'];
1242 $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1243 $component->setProperty( $propName, $datestring );
1244 } // end if( $dtendExist || $dueExist || $durationExist )
1245 $wd = getdate( $rstart );
1246 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output
1247 $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1248 $rstartYMD = date( 'Ymd', $rstart );
1249 $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1250 } // end while( $rstart <= $endWdate )
1251 }
1252 } // end elseif( $split ) - else use component date
1253 elseif( $recurrid && !$flat && !$any && !$split )
1254 $continue = TRUE;
1255 else { // !$flat && !$split, i.e. no flat array and DTSTART within period
1256 $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!!
1257 if( !$any || !isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist
1258 $wd = getdate( $startWdate );
1259 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output
1260 }
1261 }
1262 } // end if(( $startWdate >= $startDate ) && ( $startWdate <= $endDate ))
1263 /* if 'any' components, check components with reccurrence rules, removing all excluding dates */
1264 if( TRUE === $any ) {
1265 /* make a list of optional repeating dates for component occurence, rrule, rdate */
1266 $recurlist = array();
1267 while( FALSE !== ( $rrule = $component->getProperty( 'rrule' ))) // check rrule
1268 iCalUtilityFunctions::_recur2date( $recurlist, $rrule, $start, $workstart, $workend );
1269 foreach( $recurlist as $recurkey => $recurvalue ) // key=match date as timestamp
1270 $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds
1271 while( FALSE !== ( $rdate = $component->getProperty( 'rdate' ))) { // check rdate
1272 foreach( $rdate as $theRdate ) {
1273 if( is_array( $theRdate ) && ( 2 == count( $theRdate )) && // all days within PERIOD
1274 array_key_exists( '0', $theRdate ) && array_key_exists( '1', $theRdate )) {
1275 $rstart = iCalUtilityFunctions::_date2timestamp( $theRdate[0] );
1276 if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate ))
1277 continue;
1278 if( isset( $theRdate[1]['year'] )) // date-date period
1279 $rend = iCalUtilityFunctions::_date2timestamp( $theRdate[1] );
1280 else { // date-duration period
1281 $rend = iCalUtilityFunctions::_duration2date( $theRdate[0], $theRdate[1] );
1282 $rend = iCalUtilityFunctions::_date2timestamp( $rend );
1283 }
1284 while( $rstart < $rend ) {
1285 $recurlist[$rstart] = $rdurWsecs; // set start date for recurrence instance + rdate duration in seconds
1286 $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1287 }
1288 } // PERIOD end
1289 else { // single date
1290 $theRdate = iCalUtilityFunctions::_date2timestamp( $theRdate );
1291 if((( $startDate - $rdurWsecs ) <= $theRdate ) && ( $endDate >= $theRdate ))
1292 $recurlist[$theRdate] = $rdurWsecs; // set start date for recurrence instance + event duration in seconds
1293 }
1294 }
1295 } // end - check rdate
1296 foreach( $recurlist as $recurkey => $durvalue ) { // remove all recurrence START dates found in the exdatelist
1297 $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!!
1298 if( isset( $exdatelist[$checkDate] )) // no recurring to start this day
1299 unset( $recurlist[$recurkey] );
1300 }
1301 if( 0 < count( $recurlist )) {
1302 ksort( $recurlist );
1303 $xRecurrence = 1;
1304 $component2 = $component->copy();
1305 $compUID = $component2->getProperty( 'UID' );
1306 foreach( $recurlist as $recurkey => $durvalue ) {
1307 // echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCalUtilityFunctions::offsetSec2His( $durvalue )."<br />\n"; // test ###;
1308 if((( $startDate - $rdurWsecs ) > $recurkey ) || ( $endDate < $recurkey )) // not within period
1309 continue;
1310 $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!!
1311 if( isset( $recurridList[$checkDate] )) // no recurring to start this day
1312 continue;
1313 if( isset( $exdatelist[$checkDate] )) // check excluded dates
1314 continue;
1315 if( $startWdate >= $recurkey ) // exclude component start date
1316 continue;
1317 $rstart = $recurkey;
1318 $rend = $recurkey + $durvalue;
1319 /* add repeating components within valid dates to output array, only start date set */
1320 if( $flat ) {
1321 if( !isset( $result[$compUID] )) // only one comp
1322 $result[$compUID] = $component2->copy(); // copy to output
1323 }
1324 /* add repeating components within valid dates to output array, one each day */
1325 elseif( $split ) {
1326 $xRecurrence += 1;
1327 if( $rend > $endDate )
1328 $rend = $endDate;
1329 $startYMD = $rstartYMD = date( 'Ymd', $rstart );
1330 $endYMD = date( 'Ymd', $rend );
1331 // echo "splitStart=".date( 'Y-m-d H:i:s', $rstart ).' end='.date( 'Y-m-d H:i:s', $rend )."<br />\n"; // test ###;
1332 while( $rstart <= $rend ) { // iterate.. .
1333 $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1334 if( isset( $recurridList[$checkDate] )) // no recurring to start this day
1335 break;
1336 if( isset( $exdatelist[$checkDate] )) // exclude any recurrence START date, found in exdatelist
1337 break;
1338 // echo "checking date after startdate=".date( 'Y-m-d H:i:s', $rstart ).' mot '.date( 'Y-m-d H:i:s', $startDate )."<br />"; // test ###;
1339 if( $rstart >= $startDate ) { // date after dtstart
1340 if( $rstartYMD > $startYMD ) // date after dtstart
1341 $datestring = date( $startDateFormat, $checkDate );
1342 else
1343 $datestring = date( $startDateFormat, $rstart );
1344 if( isset( $start['tz'] ))
1345 $datestring .= ' '.$start['tz'];
1346 // echo "spliting = $datestring<BR>\n"; // test ###
1347 $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
1348 if( $dtendExist || $dueExist || $durationExist ) {
1349 if( $rstartYMD < $endYMD ) // not the last day
1350 $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ));
1351 else
1352 $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1353 if( $endAllDayEvent && $dtendExist )
1354 $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day
1355 $datestring = date( $endDateFormat, $tend );
1356 if( isset( $end['tz'] ))
1357 $datestring .= ' '.$end['tz'];
1358 $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1359 $component2->setProperty( $propName, $datestring );
1360 } // end if( $dtendExist || $dueExist || $durationExist )
1361 $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
1362 $wd = getdate( $rstart );
1363 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output
1364 } // end if( $checkDate > $startYMD ) { // date after dtstart
1365 $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1366 $rstartYMD = date( 'Ymd', $rstart );
1367 } // end while( $rstart <= $rend )
1368 } // end elseif( $split )
1369 elseif( $rstart >= $startDate ) { // date within period //* flat=FALSE && split=FALSE => one comp every recur startdate *//
1370 $xRecurrence += 1;
1371 $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1372 if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
1373 $datestring = date( $startDateFormat, $rstart );
1374 if( isset( $start['tz'] ))
1375 $datestring .= ' '.$start['tz'];
1376 //echo "X-CURRENT-DTSTART 2 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ###
1377 $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
1378 if( $dtendExist || $dueExist || $durationExist ) {
1379 $tend = $rstart + $rdurWsecs;
1380 if( date( 'Ymd', $tend ) < date( 'Ymd', $endWdate ))
1381 $tend = mktime( 23, 59, 59, date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend ));
1382 else
1383 $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend ) ); // on a day-basis !!!
1384 if( $endAllDayEvent && $dtendExist )
1385 $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day
1386 $datestring = date( $endDateFormat, $tend );
1387 if( isset( $end['tz'] ))
1388 $datestring .= ' '.$end['tz'];
1389 $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1390 $component2->setProperty( $propName, $datestring );
1391 } // end if( $dtendExist || $dueExist || $durationExist )
1392 $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
1393 $wd = getdate( $rstart );
1394 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output
1395 } // end if( !isset( $exdatelist[$checkDate] ))
1396 } // end elseif( $rstart >= $startDate )
1397 } // end foreach( $recurlist as $recurkey => $durvalue )
1398 unset( $component2 );
1399 } // end if( 0 < count( $recurlist ))
1400 /* deselect components with startdate/enddate not within period */
1401 if(( $endWdate < $startDate ) || ( $startWdate > $endDate ))
1402 continue;
1403 } // end if( TRUE === $any )
1404 } // end foreach ( $this->components as $cix => $component )
1405 unset( $dtendExist, $dueExist, $durationExist, $endAllDayEvent, $recurrid, $recurridList,
1406 $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $recurlist, $workstart, $workend, $endDateFormat ); // clean up
1407 if( 0 >= count( $result )) return FALSE;
1408 elseif( !$flat ) {
1409 foreach( $result as $y => $yeararr ) {
1410 foreach( $yeararr as $m => $montharr ) {
1411 foreach( $montharr as $d => $dayarr ) {
1412 if( empty( $result[$y][$m][$d] ))
1413 unset( $result[$y][$m][$d] );
1414 else
1415 $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index, hoping they are in hour order.. .
1416 }
1417 if( empty( $result[$y][$m] ))
1418 unset( $result[$y][$m] );
1419 else
1420 ksort( $result[$y][$m] );
1421 }
1422 if( empty( $result[$y] ))
1423 unset( $result[$y] );
1424 else
1425 ksort( $result[$y] );
1426 }
1427 if( empty( $result ))
1428 unset( $result );
1429 else
1430 ksort( $result );
1431 } // end elseif( !$flat )
1432 if( 0 >= count( $result ))
1433 return FALSE;
1434 return $result;
1435 }
1436 /**
1437 * select components from calendar on based on specific property value(-s)
1438 *
1439 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1440 * @since 2.16.6 - 2012-12-26
1441 * @param array $selectOptions, (string) key => (mixed) value, (key=propertyName)
1442 * @return array
1443 */
1444 function selectComponents2( $selectOptions ) {
1445 $output = array();
1446 $allowedComps = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
1447 $allowedProperties = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' );
1448 foreach( $this->components as $cix => $component3 ) {
1449 if( !in_array( $component3->objName, $allowedComps ))
1450 continue;
1451 $uid = $component3->getProperty( 'UID' );
1452 foreach( $selectOptions as $propName => $pvalue ) {
1453 $propName = strtoupper( $propName );
1454 if( !in_array( $propName, $allowedProperties ))
1455 continue;
1456 if( !is_array( $pvalue ))
1457 $pvalue = array( $pvalue );
1458 if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) {
1459 $output[$uid][] = $component3->copy();
1460 continue;
1461 }
1462 elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'CONTACT' == $propName ) || ( 'RELATED-TO' == $propName ) || ( 'RESOURCES' == $propName )) { // multiple occurrence?
1463 $propValues = array();
1464 $component3->_getProperties( $propName, $propValues );
1465 $propValues = array_keys( $propValues );
1466 foreach( $pvalue as $theValue ) {
1467 if( in_array( $theValue, $propValues )) { // && !isset( $output[$uid] )) {
1468 $output[$uid][] = $component3->copy();
1469 break;
1470 }
1471 }
1472 continue;
1473 } // end elseif( // multiple occurrence?
1474 elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single occurrence
1475 continue;
1476 if( is_array( $d )) {
1477 foreach( $d as $part ) {
1478 if( in_array( $part, $pvalue ) && !isset( $output[$uid] ))
1479 $output[$uid][] = $component3->copy();
1480 }
1481 }
1482 elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) {
1483 foreach( $pvalue as $pval ) {
1484 if( FALSE !== stripos( $d, $pval )) {
1485 $output[$uid][] = $component3->copy();
1486 break;
1487 }
1488 }
1489 }
1490 elseif( in_array( $d, $pvalue ) && !isset( $output[$uid] ))
1491 $output[$uid][] = $component3->copy();
1492 } // end foreach( $selectOptions as $propName => $pvalue ) {
1493 } // end foreach( $this->components as $cix => $component3 ) {
1494 if( !empty( $output )) {
1495 ksort( $output ); // uid order
1496 $output2 = array();
1497 foreach( $output as $uid => $components ) {
1498 foreach( $components as $component )
1499 $output2[] = $component;
1500 }
1501 $output = $output2;
1502 }
1503 return $output;
1504 }
1505 /**
1506 * add calendar component to container
1507 *
1508 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1509 * @since 2.8.8 - 2011-03-15
1510 * @param object $component calendar component
1511 * @param mixed $arg1 optional, ordno/component type/ component uid
1512 * @param mixed $arg2 optional, ordno if arg1 = component type
1513 * @return void
1514 */
1515 function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) {
1516 $component->setConfig( $this->getConfig(), FALSE, TRUE );
1517 if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) {
1518 /* make sure dtstamp and uid is set */
1519 $dummy1 = $component->getProperty( 'dtstamp' );
1520 $dummy2 = $component->getProperty( 'uid' );
1521 }
1522 if( !$arg1 ) { // plain insert, last in chain
1523 $this->components[] = $component->copy();
1524 return TRUE;
1525 }
1526 $argType = $index = null;
1527 if ( ctype_digit( (string) $arg1 )) { // index insert/replace
1528 $argType = 'INDEX';
1529 $index = (int) $arg1 - 1;
1530 }
1531 elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
1532 $argType = strtolower( $arg1 );
1533 $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
1534 }
1535 // else if arg1 is set, arg1 must be an UID
1536 $cix1sC = 0;
1537 foreach ( $this->components as $cix => $component2) {
1538 if( empty( $component2 )) continue;
1539 if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
1540 $this->components[$cix] = $component->copy();
1541 return TRUE;
1542 }
1543 elseif( $argType == $component2->objName ) { // component Type index insert/replace
1544 if( $index == $cix1sC ) {
1545 $this->components[$cix] = $component->copy();
1546 return TRUE;
1547 }
1548 $cix1sC++;
1549 }
1550 elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
1551 $this->components[$cix] = $component->copy();
1552 return TRUE;
1553 }
1554 }
1555 /* arg1=index and not found.. . insert at index .. .*/
1556 if( 'INDEX' == $argType ) {
1557 $this->components[$index] = $component->copy();
1558 ksort( $this->components, SORT_NUMERIC );
1559 }
1560 else /* not found.. . insert last in chain anyway .. .*/
1561 $this->components[] = $component->copy();
1562 return TRUE;
1563 }
1564 /**
1565 * sort iCal compoments
1566 *
1567 * ascending sort on properties (if exist) x-current-dtstart, dtstart,
1568 * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid if called without arguments,
1569 * otherwise sorting on specific (argument) property values
1570 *
1571 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1572 * @since 2.16.4 - 2012-12-17
1573 * @param string $sortArg, optional
1574 * @return void
1575 *
1576 */
1577 function sort( $sortArg=FALSE ) {
1578 if( is_array( $this->components )) {
1579 if( $sortArg ) {
1580 $sortArg = strtoupper( $sortArg );
1581 if( !in_array( $sortArg, array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'DTSTAMP', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' )))
1582 $sortArg = FALSE;
1583 }
1584 /* set sort parameters for each component */
1585 foreach( $this->components as $cix => & $c ) {
1586 $c->srtk = array( '0', '0', '0', '0' );
1587 if( 'vtimezone' == $c->objName ) {
1588 if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' )))
1589 $c->srtk[0] = 0;
1590 continue;
1591 }
1592 elseif( $sortArg ) {
1593 if(( 'ATTENDEE' == $sortArg ) || ( 'CATEGORIES' == $sortArg ) || ( 'CONTACT' == $sortArg ) || ( 'RELATED-TO' == $sortArg ) || ( 'RESOURCES' == $sortArg )) {
1594 $propValues = array();
1595 $c->_getProperties( $sortArg, $propValues );
1596 if( !empty( $propValues )) {
1597 $sk = array_keys( $propValues );
1598 $c->srtk[0] = $sk[0];
1599 if( 'RELATED-TO' == $sortArg )
1600 $c->srtk[0] .= $c->getProperty( 'uid' );
1601 }
1602 elseif( 'RELATED-TO' == $sortArg )
1603 $c->srtk[0] = $c->getProperty( 'uid' );
1604 }
1605 elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) {
1606 $c->srtk[0] = $d;
1607 if( 'UID' == $sortArg ) {
1608 if( FALSE !== ( $d = $c->getProperty( 'recurrence-id' ))) {
1609 $c->srtk[1] = iCalUtilityFunctions::_date2strdate( $d );
1610 if( FALSE === ( $c->srtk[2] = $c->getProperty( 'sequence' )))
1611 $c->srtk[2] = PHP_INT_MAX;
1612 }
1613 else
1614 $c->srtk[1] = $c->srtk[2] = PHP_INT_MAX;
1615 }
1616 }
1617 continue;
1618 }
1619 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) {
1620 $c->srtk[0] = iCalUtilityFunctions::_strdate2date( $d[1] );
1621 unset( $c->srtk[0]['unparsedtext'] );
1622 }
1623 elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' )))
1624 $c->srtk[1] = 0; // sortkey 0 : dtstart
1625 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) {
1626 $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); // sortkey 1 : dtend/due(/dtstart+duration)
1627 unset( $c->srtk[1]['unparsedtext'] );
1628 }
1629 elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) {
1630 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) {
1631 $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] );
1632 unset( $c->srtk[1]['unparsedtext'] );
1633 }
1634 elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' )))
1635 if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE )))
1636 $c->srtk[1] = 0;
1637 }
1638 if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp
1639 if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' )))
1640 $c->srtk[2] = 0;
1641 if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid
1642 $c->srtk[3] = 0;
1643 } // end foreach( $this->components as & $c
1644 /* sort */
1645 usort( $this->components, array( 'iCalUtilityFunctions', '_cmpfcn' ));
1646 }
1647 }
1648 /**
1649 * parse iCal text/file into vcalendar, components, properties and parameters
1650 *
1651 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1652 * @since 2.16.2 - 2012-12-18
1653 * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings
1654 * @return bool FALSE if error occurs during parsing
1655 *
1656 */
1657 function parse( $unparsedtext=FALSE ) {
1658 $nl = $this->getConfig( 'nl' );
1659 if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) {
1660 /* directory+filename is set previously via setConfig directory+filename or url */
1661 if( FALSE === ( $filename = $this->getConfig( 'url' )))
1662 $filename = $this->getConfig( 'dirfile' );
1663 /* READ FILE */
1664 if( FALSE === ( $rows = file_get_contents( $filename )))
1665 return FALSE; /* err 1 */
1666 }
1667 elseif( is_array( $unparsedtext ))
1668 $rows = implode( '\n'.$nl, $unparsedtext );
1669 else
1670 $rows = & $unparsedtext;
1671 /* fix line folding */
1672 $rows = explode( $nl, iCalUtilityFunctions::convEolChar( $rows, $nl ));
1673 /* skip leading (empty/invalid) lines */
1674 foreach( $rows as $lix => $line ) {
1675 if( FALSE !== stripos( $line, 'BEGIN:VCALENDAR' ))
1676 break;
1677 unset( $rows[$lix] );
1678 }
1679 $rcnt = count( $rows );
1680 if( 3 > $rcnt ) /* err 10 */
1681 return FALSE;
1682 /* skip trailing empty lines and ensure an end row */
1683 $lix = array_keys( $rows );
1684 $lix = end( $lix );
1685 while( 3 < $lix ) {
1686 $tst = trim( $rows[$lix] );
1687 if(( '\n' == $tst ) || empty( $tst )) {
1688 unset( $rows[$lix] );
1689 $lix--;
1690 continue;
1691 }
1692 if( FALSE === stripos( $rows[$lix], 'END:VCALENDAR' ))
1693 $rows[] = 'END:VCALENDAR';
1694 break;
1695 }
1696 $comp = & $this;
1697 $calsync = $compsync = 0;
1698 /* identify components and update unparsed data within component */
1699 $config = $this->getConfig();
1700 $endtxt = array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' );
1701 foreach( $rows as $lix => $line ) {
1702 if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) {
1703 $calsync++;
1704 continue;
1705 }
1706 elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) {
1707 if( 0 < $compsync )
1708 $this->components[] = $comp->copy();
1709 $compsync--;
1710 $calsync--;
1711 break;
1712 }
1713 elseif( 1 != $calsync )
1714 return FALSE; /* err 20 */
1715 elseif( in_array( strtoupper( substr( $line, 0, 6 )), $endtxt )) {
1716 $this->components[] = $comp->copy();
1717 $compsync--;
1718 continue;
1719 }
1720 if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) {
1721 $comp = new vevent( $config );
1722 $compsync++;
1723 }
1724 elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) {
1725 $comp = new vfreebusy( $config );
1726 $compsync++;
1727 }
1728 elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) {
1729 $comp = new vjournal( $config );
1730 $compsync++;
1731 }
1732 elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) {
1733 $comp = new vtodo( $config );
1734 $compsync++;
1735 }
1736 elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) {
1737 $comp = new vtimezone( $config );
1738 $compsync++;
1739 }
1740 else { /* update component with unparsed data */
1741 $comp->unparsed[] = $line;
1742 }
1743 } // end foreach( $rows as $line )
1744 unset( $config, $endtxt );
1745 /* parse data for calendar (this) object */
1746 if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) {
1747 /* concatenate property values spread over several lines */
1748 $propnames = array( 'calscale','method','prodid','version','x-' );
1749 $proprows = array();
1750 for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines
1751 $line = rtrim( $this->unparsed[$i], $nl );
1752 while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} ))
1753 $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl );
1754 $proprows[] = $line;
1755 }
1756 $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' );
1757 $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' );
1758 $paramProto4 = array( 'crid:', 'news:', 'pres:' );
1759 foreach( $proprows as $line ) {
1760 if( '\n' == substr( $line, -2 ))
1761 $line = substr( $line, 0, -2 );
1762 /* get property name */
1763 $propname = '';
1764 $cix = 0;
1765 while( FALSE !== ( $char = substr( $line, $cix, 1 ))) {
1766 if( in_array( $char, array( ':', ';' )))
1767 break;
1768 else
1769 $propname .= $char;
1770 $cix++;
1771 }
1772 /* skip non standard property names */
1773 if(( 'x-' != strtolower( substr( $propname, 0, 2 ))) && !in_array( strtolower( $propname ), $propnames ))
1774 continue;
1775 /* ignore version/prodid properties */
1776 if( in_array( strtolower( $propname ), array( 'version', 'prodid' )))
1777 continue;
1778 /* rest of the line is opt.params and value */
1779 $line = substr( $line, $cix);
1780 /* separate attributes from value */
1781 $attr = array();
1782 $attrix = -1;
1783 $strlen = strlen( $line );
1784 $WithinQuotes = FALSE;
1785 $cix = 0;
1786 while( FALSE !== substr( $line, $cix, 1 )) {
1787 if( ( ':' == $line[$cix] ) &&
1788 ( substr( $line,$cix, 3 ) != '://' ) &&
1789 ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) &&
1790 ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) &&
1791 ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) &&
1792 ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) &&
1793 !$WithinQuotes ) {
1794 $attrEnd = TRUE;
1795 if(( $cix < ( $strlen - 4 )) &&
1796 ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
1797 for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
1798 if( '://' == substr( $line, $c2ix - 2, 3 )) {
1799 $attrEnd = FALSE;
1800 break; // an URI with a portnr!!
1801 }
1802 }
1803 }
1804 if( $attrEnd) {
1805 $line = substr( $line, ( $cix + 1 ));
1806 break;
1807 }
1808 }
1809 if( '"' == $line[$cix] )
1810 $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE;
1811 if( ';' == $line[$cix] )
1812 $attr[++$attrix] = null;
1813 else
1814 $attr[$attrix] .= $line[$cix];
1815 $cix++;
1816 }
1817 /* make attributes in array format */
1818 $propattr = array();
1819 foreach( $attr as $attribute ) {
1820 $attrsplit = explode( '=', $attribute, 2 );
1821 if( 1 < count( $attrsplit ))
1822 $propattr[$attrsplit[0]] = $attrsplit[1];
1823 else
1824 $propattr[] = $attribute;
1825 }
1826 /* update Property */
1827 if( FALSE !== strpos( $line, ',' )) {
1828 $content = array( 0 => '' );
1829 $cix = $lix = 0;
1830 while( FALSE !== substr( $line, $lix, 1 )) {
1831 if(( 0 < $lix ) && ( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) {
1832 $cix++;
1833 $content[$cix] = '';
1834 }
1835 else
1836 $content[$cix] .= $line[$lix];
1837 $lix++;
1838 }
1839 if( 1 < count( $content )) {
1840 foreach( $content as $cix => $contentPart )
1841 $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart );
1842 $this->setProperty( $propname, $content, $propattr );
1843 continue;
1844 }
1845 else
1846 $line = reset( $content );
1847 $line = iCalUtilityFunctions::_strunrep( $line );
1848 }
1849 $this->setProperty( $propname, rtrim( $line, "\x00..\x1F" ), $propattr );
1850 } // end - foreach( $this->unparsed.. .
1851 } // end - if( is_array( $this->unparsed.. .
1852 unset( $unparsedtext, $rows, $this->unparsed, $proprows );
1853 /* parse Components */
1854 if( is_array( $this->components ) && ( 0 < count( $this->components ))) {
1855 $ckeys = array_keys( $this->components );
1856 foreach( $ckeys as $ckey ) {
1857 if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
1858 $this->components[$ckey]->parse();
1859 }
1860 }
1861 }
1862 else
1863 return FALSE; /* err 91 or something.. . */
1864 return TRUE;
1865 }
1866 /*********************************************************************************/
1867 /**
1868 * creates formatted output for calendar object instance
1869 *
1870 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1871 * @since 2.10.16 - 2011-10-28
1872 * @return string
1873 */
1874 function createCalendar() {
1875 $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = '';
1876 switch( $this->format ) {
1877 case 'xcal':
1878 $calendarInit = '<?xml version="1.0" encoding="UTF-8"?>'.$this->nl.
1879 '<!DOCTYPE vcalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'.$this->nl.
1880 '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"';
1881 $calendarStart = '>'.$this->nl.'<vcalendar';
1882 break;
1883 default:
1884 $calendarStart = 'BEGIN:VCALENDAR'.$this->nl;
1885 break;
1886 }
1887 $calendarStart .= $this->createVersion();
1888 $calendarStart .= $this->createProdid();
1889 $calendarStart .= $this->createCalscale();
1890 $calendarStart .= $this->createMethod();
1891 if( 'xcal' == $this->format )
1892 $calendarStart .= '>'.$this->nl;
1893 $calendar .= $this->createXprop();
1894
1895 foreach( $this->components as $component ) {
1896 if( empty( $component )) continue;
1897 $component->setConfig( $this->getConfig(), FALSE, TRUE );
1898 $calendar .= $component->createComponent( $this->xcaldecl );
1899 }
1900 if(( 'xcal' == $this->format ) && ( 0 < count( $this->xcaldecl ))) { // xCal only
1901 $calendarInit .= ' [';
1902 $old_xcaldecl = array();
1903 foreach( $this->xcaldecl as $declix => $declPart ) {
1904 if(( 0 < count( $old_xcaldecl)) &&
1905 isset( $declPart['uri'] ) && isset( $declPart['external'] ) &&
1906 isset( $old_xcaldecl['uri'] ) && isset( $old_xcaldecl['external'] ) &&
1907 ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) &&
1908 ( in_array( $declPart['external'], $old_xcaldecl['external'] )))
1909 continue; // no duplicate uri and ext. references
1910 if(( 0 < count( $old_xcaldecl)) &&
1911 !isset( $declPart['uri'] ) && !isset( $declPart['uri'] ) &&
1912 isset( $declPart['ref'] ) && isset( $old_xcaldecl['ref'] ) &&
1913 ( in_array( $declPart['ref'], $old_xcaldecl['ref'] )))
1914 continue; // no duplicate element declarations
1915 $calendarxCaldecl .= $this->nl.'<!';
1916 foreach( $declPart as $declKey => $declValue ) {
1917 switch( $declKey ) { // index
1918 case 'xmldecl': // no 1
1919 $calendarxCaldecl .= $declValue.' ';
1920 break;
1921 case 'uri': // no 2
1922 $calendarxCaldecl .= $declValue.' ';
1923 $old_xcaldecl['uri'][] = $declValue;
1924 break;
1925 case 'ref': // no 3
1926 $calendarxCaldecl .= $declValue.' ';
1927 $old_xcaldecl['ref'][] = $declValue;
1928 break;
1929 case 'external': // no 4
1930 $calendarxCaldecl .= '"'.$declValue.'" ';
1931 $old_xcaldecl['external'][] = $declValue;
1932 break;
1933 case 'type': // no 5
1934 $calendarxCaldecl .= $declValue.' ';
1935 break;
1936 case 'type2': // no 6
1937 $calendarxCaldecl .= $declValue;
1938 break;
1939 }
1940 }
1941 $calendarxCaldecl .= '>';
1942 }
1943 $calendarxCaldecl .= $this->nl.']';
1944 }
1945 switch( $this->format ) {
1946 case 'xcal':
1947 $calendar .= '</vcalendar>'.$this->nl;
1948 break;
1949 default:
1950 $calendar .= 'END:VCALENDAR'.$this->nl;
1951 break;
1952 }
1953 return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar;
1954 }
1955 /**
1956 * a HTTP redirect header is sent with created, updated and/or parsed calendar
1957 *
1958 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1959 * @since 2.10.24 - 2011-12-23
1960 * @param bool $utf8Encode
1961 * @param bool $gzip
1962 * @return redirect
1963 */
1964 function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE ) {
1965 $filename = $this->getConfig( 'filename' );
1966 $output = $this->createCalendar();
1967 if( $utf8Encode )
1968 $output = utf8_encode( $output );
1969 if( $gzip ) {
1970 $output = gzencode( $output, 9 );
1971 header( 'Content-Encoding: gzip' );
1972 header( 'Vary: *' );
1973 header( 'Content-Length: '.strlen( $output ));
1974 }
1975 if( 'xcal' == $this->format )
1976 header( 'Content-Type: application/calendar+xml; charset=utf-8' );
1977 else
1978 header( 'Content-Type: text/calendar; charset=utf-8' );
1979 header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
1980 header( 'Cache-Control: max-age=10' );
1981 die( $output );
1982 }
1983 /**
1984 * save content in a file
1985 *
1986 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1987 * @since 2.2.12 - 2007-12-30
1988 * @param string $directory optional
1989 * @param string $filename optional
1990 * @param string $delimiter optional
1991 * @return bool
1992 */
1993 function saveCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE ) {
1994 if( $directory )
1995 $this->setConfig( 'directory', $directory );
1996 if( $filename )
1997 $this->setConfig( 'filename', $filename );
1998 if( $delimiter && ($delimiter != DIRECTORY_SEPARATOR ))
1999 $this->setConfig( 'delimiter', $delimiter );
2000 if( FALSE === ( $dirfile = $this->getConfig( 'url' )))
2001 $dirfile = $this->getConfig( 'dirfile' );
2002 $iCalFile = @fopen( $dirfile, 'w' );
2003 if( $iCalFile ) {
2004 if( FALSE === fwrite( $iCalFile, $this->createCalendar() ))
2005 return FALSE;
2006 fclose( $iCalFile );
2007 return TRUE;
2008 }
2009 else
2010 return FALSE;
2011 }
2012 /**
2013 * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent
2014 * else FALSE is returned
2015 *
2016 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2017 * @since 2.2.12 - 2007-10-28
2018 * @param string $directory optional alt. int timeout
2019 * @param string $filename optional
2020 * @param string $delimiter optional
2021 * @param int timeout optional, default 3600 sec
2022 * @return redirect/FALSE
2023 */
2024 function useCachedCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE, $timeout=3600) {
2025 if ( $directory && ctype_digit( (string) $directory ) && !$filename ) {
2026 $timeout = (int) $directory;
2027 $directory = FALSE;
2028 }
2029 if( $directory )
2030 $this->setConfig( 'directory', $directory );
2031 if( $filename )
2032 $this->setConfig( 'filename', $filename );
2033 if( $delimiter && ( $delimiter != DIRECTORY_SEPARATOR ))
2034 $this->setConfig( 'delimiter', $delimiter );
2035 $filesize = $this->getConfig( 'filesize' );
2036 if( 0 >= $filesize )
2037 return FALSE;
2038 $dirfile = $this->getConfig( 'dirfile' );
2039 if( time() - filemtime( $dirfile ) < $timeout) {
2040 clearstatcache();
2041 $dirfile = $this->getConfig( 'dirfile' );
2042 $filename = $this->getConfig( 'filename' );
2043 // if( headers_sent( $filename, $linenum ))
2044 // die( "Headers already sent in $filename on line $linenum\n" );
2045 if( 'xcal' == $this->format )
2046 header( 'Content-Type: application/calendar+xml; charset=utf-8' );
2047 else
2048 header( 'Content-Type: text/calendar; charset=utf-8' );
2049 header( 'Content-Length: '.$filesize );
2050 header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
2051 header( 'Cache-Control: max-age=10' );
2052 $fp = @fopen( $dirfile, 'r' );
2053 if( $fp ) {
2054 fpassthru( $fp );
2055 fclose( $fp );
2056 }
2057 die();
2058 }
2059 else
2060 return FALSE;
2061 }
2062 }
2063 /*********************************************************************************/
2064 /*********************************************************************************/
2065 /**
2066 * abstract class for calendar components
2067 *
2068 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2069 * @since 2.9.6 - 2011-05-14
2070 */
2071 class calendarComponent {
2072 // component property variables
2073 var $uid;
2074 var $dtstamp;
2075
2076 // component config variables
2077 var $allowEmpty;
2078 var $language;
2079 var $nl;
2080 var $unique_id;
2081 var $format;
2082 var $objName; // created automatically at instance creation
2083 var $dtzid; // default (local) timezone
2084 // component internal variables
2085 var $componentStart1;
2086 var $componentStart2;
2087 var $componentEnd1;
2088 var $componentEnd2;
2089 var $elementStart1;
2090 var $elementStart2;
2091 var $elementEnd1;
2092 var $elementEnd2;
2093 var $intAttrDelimiter;
2094 var $attributeDelimiter;
2095 var $valueInit;
2096 // component xCal declaration container
2097 var $xcaldecl;
2098 /**
2099 * constructor for calendar component object
2100 *
2101 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2102 * @since 2.9.6 - 2011-05-17
2103 */
2104 function calendarComponent() {
2105 $this->objName = ( isset( $this->timezonetype )) ?
2106 strtolower( $this->timezonetype ) : get_class ( $this );
2107 $this->uid = array();
2108 $this->dtstamp = array();
2109
2110 $this->language = null;
2111 $this->nl = null;
2112 $this->unique_id = null;
2113 $this->format = null;
2114 $this->dtzid = null;
2115 $this->allowEmpty = TRUE;
2116 $this->xcaldecl = array();
2117
2118 $this->_createFormat();
2119 $this->_makeDtstamp();
2120 }
2121 /*********************************************************************************/
2122 /**
2123 * Property Name: ACTION
2124 */
2125 /**
2126 * creates formatted output for calendar component property action
2127 *
2128 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2129 * @since 2.4.8 - 2008-10-22
2130 * @return string
2131 */
2132 function createAction() {
2133 if( empty( $this->action )) return FALSE;
2134 if( empty( $this->action['value'] ))
2135 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE;
2136 $attributes = $this->_createParams( $this->action['params'] );
2137 return $this->_createElement( 'ACTION', $attributes, $this->action['value'] );
2138 }
2139 /**
2140 * set calendar component property action
2141 *
2142 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2143 * @since 2.4.8 - 2008-11-04
2144 * @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
2145 * @param mixed $params
2146 * @return bool
2147 */
2148 function setAction( $value, $params=FALSE ) {
2149 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2150 $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2151 return TRUE;
2152 }
2153 /*********************************************************************************/
2154 /**
2155 * Property Name: ATTACH
2156 */
2157 /**
2158 * creates formatted output for calendar component property attach
2159 *
2160 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2161 * @since 2.11.16 - 2012-02-04
2162 * @return string
2163 */
2164 function createAttach() {
2165 if( empty( $this->attach )) return FALSE;
2166 $output = null;
2167 foreach( $this->attach as $attachPart ) {
2168 if( !empty( $attachPart['value'] )) {
2169 $attributes = $this->_createParams( $attachPart['params'] );
2170 if(( 'xcal' != $this->format ) && isset( $attachPart['params']['VALUE'] ) && ( 'BINARY' == $attachPart['params']['VALUE'] )) {
2171 $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
2172 $str = 'ATTACH'.$attributes.$this->valueInit.$attachPart['value'];
2173 $output = substr( $str, 0, 75 ).$this->nl;
2174 $str = substr( $str, 75 );
2175 $output .= ' '.chunk_split( $str, 74, $this->nl.' ' );
2176 if( ' ' == substr( $output, -1 ))
2177 $output = rtrim( $output );
2178 if( $this->nl != substr( $output, ( 0 - strlen( $this->nl ))))
2179 $output .= $this->nl;
2180 return $output;
2181 }
2182 $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] );
2183 }
2184 elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' );
2185 }
2186 return $output;
2187 }
2188 /**
2189 * set calendar component property attach
2190 *
2191 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2192 * @since 2.5.1 - 2008-11-06
2193 * @param string $value
2194 * @param array $params, optional
2195 * @param integer $index, optional
2196 * @return bool
2197 */
2198 function setAttach( $value, $params=FALSE, $index=FALSE ) {
2199 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2200 iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index );
2201 return TRUE;
2202 }
2203 /*********************************************************************************/
2204 /**
2205 * Property Name: ATTENDEE
2206 */
2207 /**
2208 * creates formatted output for calendar component property attendee
2209 *
2210 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2211 * @since 2.11.12 - 2012-01-31
2212 * @return string
2213 */
2214 function createAttendee() {
2215 if( empty( $this->attendee )) return FALSE;
2216 $output = null;
2217 foreach( $this->attendee as $attendeePart ) { // start foreach 1
2218 if( empty( $attendeePart['value'] )) {
2219 if( $this->getConfig( 'allowEmpty' ))
2220 $output .= $this->_createElement( 'ATTENDEE' );
2221 continue;
2222 }
2223 $attendee1 = $attendee2 = null;
2224 foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2
2225 if( 'value' == $paramlabel )
2226 $attendee2 .= $paramvalue;
2227 elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif
2228 $mParams = array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' );
2229 foreach( $paramvalue as $pKey => $pValue ) { // fix (opt) quotes
2230 if( is_array( $pValue ) || in_array( $pKey, $mParams ))
2231 continue;
2232 if(( FALSE !== strpos( $pValue, ':' )) ||
2233 ( FALSE !== strpos( $pValue, ';' )) ||
2234 ( FALSE !== strpos( $pValue, ',' )))
2235 $paramvalue[$pKey] = '"'.$pValue.'"';
2236 }
2237 // set attenddee parameters in rfc2445 order
2238 if( isset( $paramvalue['CUTYPE'] ))
2239 $attendee1 .= $this->intAttrDelimiter.'CUTYPE='.$paramvalue['CUTYPE'];
2240 if( isset( $paramvalue['MEMBER'] )) {
2241 $attendee1 .= $this->intAttrDelimiter.'MEMBER=';
2242 foreach( $paramvalue['MEMBER'] as $cix => $opv )
2243 $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
2244 }
2245 if( isset( $paramvalue['ROLE'] ))
2246 $attendee1 .= $this->intAttrDelimiter.'ROLE='.$paramvalue['ROLE'];
2247 if( isset( $paramvalue['PARTSTAT'] ))
2248 $attendee1 .= $this->intAttrDelimiter.'PARTSTAT='.$paramvalue['PARTSTAT'];
2249 if( isset( $paramvalue['RSVP'] ))
2250 $attendee1 .= $this->intAttrDelimiter.'RSVP='.$paramvalue['RSVP'];
2251 if( isset( $paramvalue['DELEGATED-TO'] )) {
2252 $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO=';
2253 foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv )
2254 $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
2255 }
2256 if( isset( $paramvalue['DELEGATED-FROM'] )) {
2257 $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM=';
2258 foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv )
2259 $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
2260 }
2261 if( isset( $paramvalue['SENT-BY'] ))
2262 $attendee1 .= $this->intAttrDelimiter.'SENT-BY='.$paramvalue['SENT-BY'];
2263 if( isset( $paramvalue['CN'] ))
2264 $attendee1 .= $this->intAttrDelimiter.'CN='.$paramvalue['CN'];
2265 if( isset( $paramvalue['DIR'] )) {
2266 $delim = ( FALSE === strpos( $paramvalue['DIR'], '"' )) ? '"' : '';
2267 $attendee1 .= $this->intAttrDelimiter.'DIR='.$delim.$paramvalue['DIR'].$delim;
2268 }
2269 if( isset( $paramvalue['LANGUAGE'] ))
2270 $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE'];
2271 $xparams = array();
2272 foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3
2273 if( ctype_digit( (string) $optparamlabel )) {
2274 $xparams[] = $optparamvalue;
2275 continue;
2276 }
2277 if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' )))
2278 $xparams[$optparamlabel] = $optparamvalue;
2279 } // end foreach 3
2280 ksort( $xparams, SORT_STRING );
2281 foreach( $xparams as $paramKey => $paramValue ) {
2282 if( ctype_digit( (string) $paramKey ))
2283 $attendee1 .= $this->intAttrDelimiter.$paramValue;
2284 else
2285 $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue";
2286 } // end foreach 3
2287 } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue )))
2288 } // end foreach 2
2289 $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 );
2290 } // end foreach 1
2291 return $output;
2292 }
2293 /**
2294 * set calendar component property attach
2295 *
2296 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2297 * @since 2.12.18 - 2012-07-13
2298 * @param string $value
2299 * @param array $params, optional
2300 * @param integer $index, optional
2301 * @return bool
2302 */
2303 function setAttendee( $value, $params=FALSE, $index=FALSE ) {
2304 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2305 // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params
2306 if( !empty( $value )) {
2307 if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
2308 $value = 'MAILTO:'.$value;
2309 elseif( !empty( $value ))
2310 $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
2311 $value = str_replace( 'mailto:', 'MAILTO:', $value );
2312 }
2313 $params2 = array();
2314 if( is_array($params )) {
2315 $optarrays = array();
2316 foreach( $params as $optparamlabel => $optparamvalue ) {
2317 $optparamlabel = strtoupper( $optparamlabel );
2318 switch( $optparamlabel ) {
2319 case 'MEMBER':
2320 case 'DELEGATED-TO':
2321 case 'DELEGATED-FROM':
2322 if( !is_array( $optparamvalue ))
2323 $optparamvalue = array( $optparamvalue );
2324 foreach( $optparamvalue as $part ) {
2325 $part = trim( $part );
2326 if(( '"' == substr( $part, 0, 1 )) &&
2327 ( '"' == substr( $part, -1 )))
2328 $part = substr( $part, 1, ( strlen( $part ) - 2 ));
2329 if( 'mailto:' != strtolower( substr( $part, 0, 7 )))
2330 $part = "MAILTO:$part";
2331 else
2332 $part = 'MAILTO:'.substr( $part, 7 );
2333 $optarrays[$optparamlabel][] = $part;
2334 }
2335 break;
2336 default:
2337 if(( '"' == substr( $optparamvalue, 0, 1 )) &&
2338 ( '"' == substr( $optparamvalue, -1 )))
2339 $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 ));
2340 if( 'SENT-BY' == $optparamlabel ) {
2341 if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 )))
2342 $optparamvalue = "MAILTO:$optparamvalue";
2343 else
2344 $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 );
2345 }
2346 $params2[$optparamlabel] = $optparamvalue;
2347 break;
2348 } // end switch( $optparamlabel.. .
2349 } // end foreach( $optparam.. .
2350 foreach( $optarrays as $optparamlabel => $optparams )
2351 $params2[$optparamlabel] = $optparams;
2352 }
2353 // remove defaults
2354 iCalUtilityFunctions::_existRem( $params2, 'CUTYPE', 'INDIVIDUAL' );
2355 iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' );
2356 iCalUtilityFunctions::_existRem( $params2, 'ROLE', 'REQ-PARTICIPANT' );
2357 iCalUtilityFunctions::_existRem( $params2, 'RSVP', 'FALSE' );
2358 // check language setting
2359 if( isset( $params2['CN' ] )) {
2360 $lang = $this->getConfig( 'language' );
2361 if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang ))
2362 $params2['LANGUAGE' ] = $lang;
2363 }
2364 iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index );
2365 return TRUE;
2366 }
2367 /*********************************************************************************/
2368 /**
2369 * Property Name: CATEGORIES
2370 */
2371 /**
2372 * creates formatted output for calendar component property categories
2373 *
2374 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2375 * @since 2.16.2 - 2012-12-18
2376 * @return string
2377 */
2378 function createCategories() {
2379 if( empty( $this->categories )) return FALSE;
2380 $output = null;
2381 foreach( $this->categories as $category ) {
2382 if( empty( $category['value'] )) {
2383 if ( $this->getConfig( 'allowEmpty' ))
2384 $output .= $this->_createElement( 'CATEGORIES' );
2385 continue;
2386 }
2387 $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' ));
2388 if( is_array( $category['value'] )) {
2389 foreach( $category['value'] as $cix => $categoryPart )
2390 $category['value'][$cix] = iCalUtilityFunctions::_strrep( $categoryPart, $this->format, $this->nl );
2391 $content = implode( ',', $category['value'] );
2392 }
2393 else
2394 $content = iCalUtilityFunctions::_strrep( $category['value'], $this->format, $this->nl );
2395 $output .= $this->_createElement( 'CATEGORIES', $attributes, $content );
2396 }
2397 return $output;
2398 }
2399 /**
2400 * set calendar component property categories
2401 *
2402 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2403 * @since 2.5.1 - 2008-11-06
2404 * @param mixed $value
2405 * @param array $params, optional
2406 * @param integer $index, optional
2407 * @return bool
2408 */
2409 function setCategories( $value, $params=FALSE, $index=FALSE ) {
2410 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2411 iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index );
2412 return TRUE;
2413 }
2414 /*********************************************************************************/
2415 /**
2416 * Property Name: CLASS
2417 */
2418 /**
2419 * creates formatted output for calendar component property class
2420 *
2421 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2422 * @since 0.9.7 - 2006-11-20
2423 * @return string
2424 */
2425 function createClass() {
2426 if( empty( $this->class )) return FALSE;
2427 if( empty( $this->class['value'] ))
2428 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE;
2429 $attributes = $this->_createParams( $this->class['params'] );
2430 return $this->_createElement( 'CLASS', $attributes, $this->class['value'] );
2431 }
2432 /**
2433 * set calendar component property class
2434 *
2435 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2436 * @since 2.4.8 - 2008-11-04
2437 * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name
2438 * @param array $params optional
2439 * @return bool
2440 */
2441 function setClass( $value, $params=FALSE ) {
2442 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2443 $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2444 return TRUE;
2445 }
2446 /*********************************************************************************/
2447 /**
2448 * Property Name: COMMENT
2449 */
2450 /**
2451 * creates formatted output for calendar component property comment
2452 *
2453 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2454 * @since 2.16.2 - 2012-12-18
2455 * @return string
2456 */
2457 function createComment() {
2458 if( empty( $this->comment )) return FALSE;
2459 $output = null;
2460 foreach( $this->comment as $commentPart ) {
2461 if( empty( $commentPart['value'] )) {
2462 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' );
2463 continue;
2464 }
2465 $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' ));
2466 $content = iCalUtilityFunctions::_strrep( $commentPart['value'], $this->format, $this->nl );
2467 $output .= $this->_createElement( 'COMMENT', $attributes, $content );
2468 }
2469 return $output;
2470 }
2471 /**
2472 * set calendar component property comment
2473 *
2474 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2475 * @since 2.5.1 - 2008-11-06
2476 * @param string $value
2477 * @param array $params, optional
2478 * @param integer $index, optional
2479 * @return bool
2480 */
2481 function setComment( $value, $params=FALSE, $index=FALSE ) {
2482 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2483 iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index );
2484 return TRUE;
2485 }
2486 /*********************************************************************************/
2487 /**
2488 * Property Name: COMPLETED
2489 */
2490 /**
2491 * creates formatted output for calendar component property completed
2492 *
2493 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2494 * @since 2.4.8 - 2008-10-22
2495 * @return string
2496 */
2497 function createCompleted( ) {
2498 if( empty( $this->completed )) return FALSE;
2499 if( !isset( $this->completed['value']['year'] ) &&
2500 !isset( $this->completed['value']['month'] ) &&
2501 !isset( $this->completed['value']['day'] ) &&
2502 !isset( $this->completed['value']['hour'] ) &&
2503 !isset( $this->completed['value']['min'] ) &&
2504 !isset( $this->completed['value']['sec'] ))
2505 if( $this->getConfig( 'allowEmpty' ))
2506 return $this->_createElement( 'COMPLETED' );
2507 else return FALSE;
2508 $formatted = iCalUtilityFunctions::_date2strdate( $this->completed['value'], 7 );
2509 $attributes = $this->_createParams( $this->completed['params'] );
2510 return $this->_createElement( 'COMPLETED', $attributes, $formatted );
2511 }
2512 /**
2513 * set calendar component property completed
2514 *
2515 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2516 * @since 2.4.8 - 2008-10-23
2517 * @param mixed $year
2518 * @param mixed $month optional
2519 * @param int $day optional
2520 * @param int $hour optional
2521 * @param int $min optional
2522 * @param int $sec optional
2523 * @param array $params optional
2524 * @return bool
2525 */
2526 function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2527 if( empty( $year )) {
2528 if( $this->getConfig( 'allowEmpty' )) {
2529 $this->completed = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2530 return TRUE;
2531 }
2532 else
2533 return FALSE;
2534 }
2535 $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2536 return TRUE;
2537 }
2538 /*********************************************************************************/
2539 /**
2540 * Property Name: CONTACT
2541 */
2542 /**
2543 * creates formatted output for calendar component property contact
2544 *
2545 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2546 * @since 2.16.2 - 2012-12-18
2547 * @return string
2548 */
2549 function createContact() {
2550 if( empty( $this->contact )) return FALSE;
2551 $output = null;
2552 foreach( $this->contact as $contact ) {
2553 if( !empty( $contact['value'] )) {
2554 $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' ));
2555 $content = iCalUtilityFunctions::_strrep( $contact['value'], $this->format, $this->nl );
2556 $output .= $this->_createElement( 'CONTACT', $attributes, $content );
2557 }
2558 elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' );
2559 }
2560 return $output;
2561 }
2562 /**
2563 * set calendar component property contact
2564 *
2565 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2566 * @since 2.5.1 - 2008-11-05
2567 * @param string $value
2568 * @param array $params, optional
2569 * @param integer $index, optional
2570 * @return bool
2571 */
2572 function setContact( $value, $params=FALSE, $index=FALSE ) {
2573 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2574 iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index );
2575 return TRUE;
2576 }
2577 /*********************************************************************************/
2578 /**
2579 * Property Name: CREATED
2580 */
2581 /**
2582 * creates formatted output for calendar component property created
2583 *
2584 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2585 * @since 2.4.8 - 2008-10-21
2586 * @return string
2587 */
2588 function createCreated() {
2589 if( empty( $this->created )) return FALSE;
2590 $formatted = iCalUtilityFunctions::_date2strdate( $this->created['value'], 7 );
2591 $attributes = $this->_createParams( $this->created['params'] );
2592 return $this->_createElement( 'CREATED', $attributes, $formatted );
2593 }
2594 /**
2595 * set calendar component property created
2596 *
2597 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2598 * @since 2.4.8 - 2008-10-23
2599 * @param mixed $year optional
2600 * @param mixed $month optional
2601 * @param int $day optional
2602 * @param int $hour optional
2603 * @param int $min optional
2604 * @param int $sec optional
2605 * @param mixed $params optional
2606 * @return bool
2607 */
2608 function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2609 if( !isset( $year )) {
2610 $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
2611 }
2612 $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2613 return TRUE;
2614 }
2615 /*********************************************************************************/
2616 /**
2617 * Property Name: DESCRIPTION
2618 */
2619 /**
2620 * creates formatted output for calendar component property description
2621 *
2622 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2623 * @since 2.16.2 - 2012-12-18
2624 * @return string
2625 */
2626 function createDescription() {
2627 if( empty( $this->description )) return FALSE;
2628 $output = null;
2629 foreach( $this->description as $description ) {
2630 if( !empty( $description['value'] )) {
2631 $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' ));
2632 $content = iCalUtilityFunctions::_strrep( $description['value'], $this->format, $this->nl );
2633 $output .= $this->_createElement( 'DESCRIPTION', $attributes, $content );
2634 }
2635 elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' );
2636 }
2637 return $output;
2638 }
2639 /**
2640 * set calendar component property description
2641 *
2642 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2643 * @since 2.6.24 - 2010-11-06
2644 * @param string $value
2645 * @param array $params, optional
2646 * @param integer $index, optional
2647 * @return bool
2648 */
2649 function setDescription( $value, $params=FALSE, $index=FALSE ) {
2650 if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; }
2651 if( 'vjournal' != $this->objName )
2652 $index = 1;
2653 iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index );
2654 return TRUE;
2655 }
2656 /*********************************************************************************/
2657 /**
2658 * Property Name: DTEND
2659 */
2660 /**
2661 * creates formatted output for calendar component property dtend
2662 *
2663 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2664 * @since 2.14.4 - 2012-09-26
2665 * @return string
2666 */
2667 function createDtend() {
2668 if( empty( $this->dtend )) return FALSE;
2669 if( !isset( $this->dtend['value']['year'] ) &&
2670 !isset( $this->dtend['value']['month'] ) &&
2671 !isset( $this->dtend['value']['day'] ) &&
2672 !isset( $this->dtend['value']['hour'] ) &&
2673 !isset( $this->dtend['value']['min'] ) &&
2674 !isset( $this->dtend['value']['sec'] ))
2675 if( $this->getConfig( 'allowEmpty' ))
2676 return $this->_createElement( 'DTEND' );
2677 else return FALSE;
2678 $parno = ( isset( $this->dtend['params']['VALUE'] ) && ( 'DATE' == $this->dtend['params']['VALUE'] )) ? 3 : null;
2679 $formatted = iCalUtilityFunctions::_date2strdate( $this->dtend['value'], $parno );
2680 $attributes = $this->_createParams( $this->dtend['params'] );
2681 return $this->_createElement( 'DTEND', $attributes, $formatted );
2682 }
2683 /**
2684 * set calendar component property dtend
2685 *
2686 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2687 * @since 2.9.6 - 2011-05-14
2688 * @param mixed $year
2689 * @param mixed $month optional
2690 * @param int $day optional
2691 * @param int $hour optional
2692 * @param int $min optional
2693 * @param int $sec optional
2694 * @param string $tz optional
2695 * @param array params optional
2696 * @return bool
2697 */
2698 function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2699 if( empty( $year )) {
2700 if( $this->getConfig( 'allowEmpty' )) {
2701 $this->dtend = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2702 return TRUE;
2703 }
2704 else
2705 return FALSE;
2706 }
2707 $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
2708 return TRUE;
2709 }
2710 /*********************************************************************************/
2711 /**
2712 * Property Name: DTSTAMP
2713 */
2714 /**
2715 * creates formatted output for calendar component property dtstamp
2716 *
2717 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2718 * @since 2.4.4 - 2008-03-07
2719 * @return string
2720 */
2721 function createDtstamp() {
2722 if( !isset( $this->dtstamp['value']['year'] ) &&
2723 !isset( $this->dtstamp['value']['month'] ) &&
2724 !isset( $this->dtstamp['value']['day'] ) &&
2725 !isset( $this->dtstamp['value']['hour'] ) &&
2726 !isset( $this->dtstamp['value']['min'] ) &&
2727 !isset( $this->dtstamp['value']['sec'] ))
2728 $this->_makeDtstamp();
2729 $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstamp['value'], 7 );
2730 $attributes = $this->_createParams( $this->dtstamp['params'] );
2731 return $this->_createElement( 'DTSTAMP', $attributes, $formatted );
2732 }
2733 /**
2734 * computes datestamp for calendar component object instance dtstamp
2735 *
2736 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2737 * @since 2.14.1 - 2012-09-29
2738 * @return void
2739 */
2740 function _makeDtstamp() {
2741 $d = date( 'Y-m-d-H-i-s', mktime( date('H'), date('i'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y')));
2742 $date = explode( '-', $d );
2743 $this->dtstamp['value'] = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2], 'hour' => $date[3], 'min' => $date[4], 'sec' => $date[5], 'tz' => 'Z' );
2744 $this->dtstamp['params'] = null;
2745 }
2746 /**
2747 * set calendar component property dtstamp
2748 *
2749 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2750 * @since 2.4.8 - 2008-10-23
2751 * @param mixed $year
2752 * @param mixed $month optional
2753 * @param int $day optional
2754 * @param int $hour optional
2755 * @param int $min optional
2756 * @param int $sec optional
2757 * @param array $params optional
2758 * @return TRUE
2759 */
2760 function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2761 if( empty( $year ))
2762 $this->_makeDtstamp();
2763 else
2764 $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2765 return TRUE;
2766 }
2767 /*********************************************************************************/
2768 /**
2769 * Property Name: DTSTART
2770 */
2771 /**
2772 * creates formatted output for calendar component property dtstart
2773 *
2774 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2775 * @since 2.14.4 - 2012-09-26
2776 * @return string
2777 */
2778 function createDtstart() {
2779 if( empty( $this->dtstart )) return FALSE;
2780 if( !isset( $this->dtstart['value']['year'] ) &&
2781 !isset( $this->dtstart['value']['month'] ) &&
2782 !isset( $this->dtstart['value']['day'] ) &&
2783 !isset( $this->dtstart['value']['hour'] ) &&
2784 !isset( $this->dtstart['value']['min'] ) &&
2785 !isset( $this->dtstart['value']['sec'] )) {
2786 if( $this->getConfig( 'allowEmpty' ))
2787 return $this->_createElement( 'DTSTART' );
2788 else return FALSE;
2789 }
2790 if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
2791 unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] );
2792 $parno = ( isset( $this->dtstart['params']['VALUE'] ) && ( 'DATE' == $this->dtstart['params']['VALUE'] )) ? 3 : null;
2793 $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstart['value'], $parno );
2794 $attributes = $this->_createParams( $this->dtstart['params'] );
2795 return $this->_createElement( 'DTSTART', $attributes, $formatted );
2796 }
2797 /**
2798 * set calendar component property dtstart
2799 *
2800 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2801 * @since 2.6.22 - 2010-09-22
2802 * @param mixed $year
2803 * @param mixed $month optional
2804 * @param int $day optional
2805 * @param int $hour optional
2806 * @param int $min optional
2807 * @param int $sec optional
2808 * @param string $tz optional
2809 * @param array $params optional
2810 * @return bool
2811 */
2812 function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2813 if( empty( $year )) {
2814 if( $this->getConfig( 'allowEmpty' )) {
2815 $this->dtstart = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2816 return TRUE;
2817 }
2818 else
2819 return FALSE;
2820 }
2821 $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName, $this->getConfig( 'TZID' ));
2822 return TRUE;
2823 }
2824 /*********************************************************************************/
2825 /**
2826 * Property Name: DUE
2827 */
2828 /**
2829 * creates formatted output for calendar component property due
2830 *
2831 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2832 * @since 2.14.4 - 2012-09-26
2833 * @return string
2834 */
2835 function createDue() {
2836 if( empty( $this->due )) return FALSE;
2837 if( !isset( $this->due['value']['year'] ) &&
2838 !isset( $this->due['value']['month'] ) &&
2839 !isset( $this->due['value']['day'] ) &&
2840 !isset( $this->due['value']['hour'] ) &&
2841 !isset( $this->due['value']['min'] ) &&
2842 !isset( $this->due['value']['sec'] )) {
2843 if( $this->getConfig( 'allowEmpty' ))
2844 return $this->_createElement( 'DUE' );
2845 else
2846 return FALSE;
2847 }
2848 $parno = ( isset( $this->due['params']['VALUE'] ) && ( 'DATE' == $this->due['params']['VALUE'] )) ? 3 : null;
2849 $formatted = iCalUtilityFunctions::_date2strdate( $this->due['value'], $parno );
2850 $attributes = $this->_createParams( $this->due['params'] );
2851 return $this->_createElement( 'DUE', $attributes, $formatted );
2852 }
2853 /**
2854 * set calendar component property due
2855 *
2856 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2857 * @since 2.4.8 - 2008-11-04
2858 * @param mixed $year
2859 * @param mixed $month optional
2860 * @param int $day optional
2861 * @param int $hour optional
2862 * @param int $min optional
2863 * @param int $sec optional
2864 * @param array $params optional
2865 * @return bool
2866 */
2867 function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2868 if( empty( $year )) {
2869 if( $this->getConfig( 'allowEmpty' )) {
2870 $this->due = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2871 return TRUE;
2872 }
2873 else
2874 return FALSE;
2875 }
2876 $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
2877 return TRUE;
2878 }
2879 /*********************************************************************************/
2880 /**
2881 * Property Name: DURATION
2882 */
2883 /**
2884 * creates formatted output for calendar component property duration
2885 *
2886 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2887 * @since 2.4.8 - 2008-10-21
2888 * @return string
2889 */
2890 function createDuration() {
2891 if( empty( $this->duration )) return FALSE;
2892 if( !isset( $this->duration['value']['week'] ) &&
2893 !isset( $this->duration['value']['day'] ) &&
2894 !isset( $this->duration['value']['hour'] ) &&
2895 !isset( $this->duration['value']['min'] ) &&
2896 !isset( $this->duration['value']['sec'] ))
2897 if( $this->getConfig( 'allowEmpty' ))
2898 return $this->_createElement( 'DURATION', array(), null );
2899 else return FALSE;
2900 $attributes = $this->_createParams( $this->duration['params'] );
2901 return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_duration2str( $this->duration['value'] ));
2902 }
2903 /**
2904 * set calendar component property duration
2905 *
2906 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2907 * @since 2.4.8 - 2008-11-04
2908 * @param mixed $week
2909 * @param mixed $day optional
2910 * @param int $hour optional
2911 * @param int $min optional
2912 * @param int $sec optional
2913 * @param array $params optional
2914 * @return bool
2915 */
2916 function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2917 if( empty( $week )) if( $this->getConfig( 'allowEmpty' )) $week = null; else return FALSE;
2918 if( is_array( $week ) && ( 1 <= count( $week )))
2919 $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
2920 elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) {
2921 $week = trim( $week );
2922 if( in_array( substr( $week, 0, 1 ), array( '+', '-' )))
2923 $week = substr( $week, 1 );
2924 $this->duration = array( 'value' => iCalUtilityFunctions::_durationStr2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
2925 }
2926 elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec ))
2927 return FALSE;
2928 else
2929 $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params ));
2930 return TRUE;
2931 }
2932 /*********************************************************************************/
2933 /**
2934 * Property Name: EXDATE
2935 */
2936 /**
2937 * creates formatted output for calendar component property exdate
2938 *
2939 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2940 * @since 2.16.5 - 2012-12-28
2941 * @return string
2942 */
2943 function createExdate() {
2944 if( empty( $this->exdate )) return FALSE;
2945 $output = null;
2946 $exdates = array();
2947 foreach( $this->exdate as $theExdate ) {
2948 if( empty( $theExdate['value'] )) {
2949 if( $this->getConfig( 'allowEmpty' ))
2950 $output .= $this->_createElement( 'EXDATE' );
2951 continue;
2952 }
2953 if( 1 < count( $theExdate['value'] ))
2954 usort( $theExdate['value'], array( 'iCalUtilityFunctions', '_sortExdate1' ));
2955 $exdates[] = $theExdate;
2956 }
2957 if( 1 < count( $exdates ))
2958 usort( $exdates, array( 'iCalUtilityFunctions', '_sortExdate2' ));
2959 foreach( $exdates as $theExdate ) {
2960 $content = $attributes = null;
2961 foreach( $theExdate['value'] as $eix => $exdatePart ) {
2962 $parno = count( $exdatePart );
2963 $formatted = iCalUtilityFunctions::_date2strdate( $exdatePart, $parno );
2964 if( isset( $theExdate['params']['TZID'] ))
2965 $formatted = str_replace( 'Z', '', $formatted);
2966 if( 0 < $eix ) {
2967 if( isset( $theExdate['value'][0]['tz'] )) {
2968 if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) ||
2969 ( 'Z' == $theExdate['value'][0]['tz'] )) {
2970 if( 'Z' != substr( $formatted, -1 ))
2971 $formatted .= 'Z';
2972 }
2973 else
2974 $formatted = str_replace( 'Z', '', $formatted );
2975 }
2976 else
2977 $formatted = str_replace( 'Z', '', $formatted );
2978 } // end if( 0 < $eix )
2979 $content .= ( 0 < $eix ) ? ','.$formatted : $formatted;
2980 } // end foreach( $theExdate['value'] as $eix => $exdatePart )
2981 $attributes .= $this->_createParams( $theExdate['params'] );
2982 $output .= $this->_createElement( 'EXDATE', $attributes, $content );
2983 } // end foreach( $exdates as $theExdate )
2984 return $output;
2985 }
2986 /**
2987 * set calendar component property exdate
2988 *
2989 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2990 * @since 2.14.1 - 2012-10-02
2991 * @param array exdates
2992 * @param array $params, optional
2993 * @param integer $index, optional
2994 * @return bool
2995 */
2996 function setExdate( $exdates, $params=FALSE, $index=FALSE ) {
2997 if( empty( $exdates )) {
2998 if( $this->getConfig( 'allowEmpty' )) {
2999 iCalUtilityFunctions::_setMval( $this->exdate, null, $params, FALSE, $index );
3000 return TRUE;
3001 }
3002 else
3003 return FALSE;
3004 }
3005 $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
3006 $toZ = ( isset( $input['params']['TZID'] ) && in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) ? TRUE : FALSE;
3007 /* ev. check 1:st date and save ev. timezone **/
3008 iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] );
3009 iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter
3010 foreach( $exdates as $eix => $theExdate ) {
3011 iCalUtilityFunctions::_strDate2arr( $theExdate );
3012 if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) {
3013 if( isset( $theExdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theExdate['tz'] )) {
3014 if( isset( $input['params']['TZID'] ))
3015 $theExdate['tz'] = $input['params']['TZID'];
3016 else
3017 $input['params']['TZID'] = $theExdate['tz'];
3018 }
3019 $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno );
3020 }
3021 elseif( is_array( $theExdate )) {
3022 $d = iCalUtilityFunctions::_chkDateArr( $theExdate, $parno );
3023 if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
3024 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
3025 $exdatea = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
3026 unset( $exdatea['unparsedtext'] );
3027 }
3028 else
3029 $exdatea = $d;
3030 }
3031 elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18
3032 $exdatea = iCalUtilityFunctions::_strdate2date( $theExdate, $parno );
3033 unset( $exdatea['unparsedtext'] );
3034 }
3035 if( 3 == $parno )
3036 unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] );
3037 elseif( isset( $exdatea['tz'] ))
3038 $exdatea['tz'] = (string) $exdatea['tz'];
3039 if( isset( $input['params']['TZID'] ) ||
3040 ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) ||
3041 ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) ||
3042 ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
3043 unset( $exdatea['tz'] );
3044 if( $toZ ) // time zone Z
3045 $exdatea['tz'] = 'Z';
3046 $input['value'][] = $exdatea;
3047 }
3048 if( 0 >= count( $input['value'] ))
3049 return FALSE;
3050 if( 3 == $parno ) {
3051 $input['params']['VALUE'] = 'DATE';
3052 unset( $input['params']['TZID'] );
3053 }
3054 if( $toZ ) // time zone Z
3055 unset( $input['params']['TZID'] );
3056 iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index );
3057 return TRUE;
3058 }
3059 /*********************************************************************************/
3060 /**
3061 * Property Name: EXRULE
3062 */
3063 /**
3064 * creates formatted output for calendar component property exrule
3065 *
3066 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3067 * @since 2.4.8 - 2008-10-22
3068 * @return string
3069 */
3070 function createExrule() {
3071 if( empty( $this->exrule )) return FALSE;
3072 return $this->_format_recur( 'EXRULE', $this->exrule );
3073 }
3074 /**
3075 * set calendar component property exdate
3076 *
3077 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3078 * @since 2.5.1 - 2008-11-05
3079 * @param array $exruleset
3080 * @param array $params, optional
3081 * @param integer $index, optional
3082 * @return bool
3083 */
3084 function setExrule( $exruleset, $params=FALSE, $index=FALSE ) {
3085 if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = null; else return FALSE;
3086 iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index );
3087 return TRUE;
3088 }
3089 /*********************************************************************************/
3090 /**
3091 * Property Name: FREEBUSY
3092 */
3093 /**
3094 * creates formatted output for calendar component property freebusy
3095 *
3096 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3097 * @since 2.1.23 - 2012-02-16
3098 * @return string
3099 */
3100 function createFreebusy() {
3101 if( empty( $this->freebusy )) return FALSE;
3102 $output = null;
3103 foreach( $this->freebusy as $freebusyPart ) {
3104 if( empty( $freebusyPart['value'] ) || (( 1 == count( $freebusyPart['value'] )) && isset( $freebusyPart['value']['fbtype'] ))) {
3105 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' );
3106 continue;
3107 }
3108 $attributes = $content = null;
3109 if( isset( $freebusyPart['value']['fbtype'] )) {
3110 $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype'];
3111 unset( $freebusyPart['value']['fbtype'] );
3112 $freebusyPart['value'] = array_values( $freebusyPart['value'] );
3113 }
3114 else
3115 $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY';
3116 $attributes .= $this->_createParams( $freebusyPart['params'] );
3117 $fno = 1;
3118 $cnt = count( $freebusyPart['value']);
3119 foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) {
3120 $formatted = iCalUtilityFunctions::_date2strdate( $freebusyPeriod[0] );
3121 $content .= $formatted;
3122 $content .= '/';
3123 $cnt2 = count( $freebusyPeriod[1]);
3124 if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time
3125 $cnt2 = 7;
3126 elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration
3127 $cnt2 = 5;
3128 if(( 7 == $cnt2 ) && // period= -> date-time
3129 isset( $freebusyPeriod[1]['year'] ) &&
3130 isset( $freebusyPeriod[1]['month'] ) &&
3131 isset( $freebusyPeriod[1]['day'] )) {
3132 $content .= iCalUtilityFunctions::_date2strdate( $freebusyPeriod[1] );
3133 }
3134 else { // period= -> dur-time
3135 $content .= iCalUtilityFunctions::_duration2str( $freebusyPeriod[1] );
3136 }
3137 if( $fno < $cnt )
3138 $content .= ',';
3139 $fno++;
3140 }
3141 $output .= $this->_createElement( 'FREEBUSY', $attributes, $content );
3142 }
3143 return $output;
3144 }
3145 /**
3146 * set calendar component property freebusy
3147 *
3148 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3149 * @since 2.10.30 - 2012-01-16
3150 * @param string $fbType
3151 * @param array $fbValues
3152 * @param array $params, optional
3153 * @param integer $index, optional
3154 * @return bool
3155 */
3156 function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) {
3157 if( empty( $fbValues )) {
3158 if( $this->getConfig( 'allowEmpty' )) {
3159 iCalUtilityFunctions::_setMval( $this->freebusy, null, $params, FALSE, $index );
3160 return TRUE;
3161 }
3162 else
3163 return FALSE;
3164 }
3165 $fbType = strtoupper( $fbType );
3166 if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) &&
3167 ( 'X-' != substr( $fbType, 0, 2 )))
3168 $fbType = 'BUSY';
3169 $input = array( 'fbtype' => $fbType );
3170 foreach( $fbValues as $fbPeriod ) { // periods => period
3171 if( empty( $fbPeriod ))
3172 continue;
3173 $freebusyPeriod = array();
3174 foreach( $fbPeriod as $fbMember ) { // pairs => singlepart
3175 $freebusyPairMember = array();
3176 if( is_array( $fbMember )) {
3177 if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value
3178 $freebusyPairMember = iCalUtilityFunctions::_chkDateArr( $fbMember, 7 );
3179 $freebusyPairMember['tz'] = 'Z';
3180 }
3181 elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value
3182 $freebusyPairMember = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 );
3183 $freebusyPairMember['tz'] = 'Z';
3184 }
3185 else { // array format duration
3186 $freebusyPairMember = iCalUtilityFunctions::_duration2arr( $fbMember );
3187 }
3188 }
3189 elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration
3190 ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) {
3191 if( 'P' != $fbMember{0} )
3192 $fbmember = substr( $fbMember, 1 );
3193 $freebusyPairMember = iCalUtilityFunctions::_durationStr2arr( $fbMember );
3194 }
3195 elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18
3196 $freebusyPairMember = iCalUtilityFunctions::_strdate2date( $fbMember, 7 );
3197 unset( $freebusyPairMember['unparsedtext'] );
3198 $freebusyPairMember['tz'] = 'Z';
3199 }
3200 $freebusyPeriod[] = $freebusyPairMember;
3201 }
3202 $input[] = $freebusyPeriod;
3203 }
3204 iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index );
3205 return TRUE;
3206 }
3207 /*********************************************************************************/
3208 /**
3209 * Property Name: GEO
3210 */
3211 /**
3212 * creates formatted output for calendar component property geo
3213 *
3214 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3215 * @since 2.12.6 - 2012-04-21
3216 * @return string
3217 */
3218 function createGeo() {
3219 if( empty( $this->geo )) return FALSE;
3220 if( empty( $this->geo['value'] ))
3221 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE;
3222 $attributes = $this->_createParams( $this->geo['params'] );
3223 if( 0.0 < $this->geo['value']['latitude'] )
3224 $sign = '+';
3225 else
3226 $sign = ( 0.0 > $this->geo['value']['latitude'] ) ? '-' : '';
3227 $content = $sign.sprintf( "%09.6f", abs( $this->geo['value']['latitude'] )); // sprintf && lpad && float && sign !"#¤%&/(
3228 $content = rtrim( rtrim( $content, '0' ), '.' );
3229 if( 0.0 < $this->geo['value']['longitude'] )
3230 $sign = '+';
3231 else
3232 $sign = ( 0.0 > $this->geo['value']['longitude'] ) ? '-' : '';
3233 $content .= ';'.$sign.sprintf( '%8.6f', abs( $this->geo['value']['longitude'] )); // sprintf && lpad && float && sign !"#¤%&/(
3234 $content = rtrim( rtrim( $content, '0' ), '.' );
3235 return $this->_createElement( 'GEO', $attributes, $content );
3236 }
3237 /**
3238 * set calendar component property geo
3239 *
3240 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3241 * @since 2.12.5 - 2012-04-21
3242 * @param float $latitude
3243 * @param float $longitude
3244 * @param array $params optional
3245 * @return bool
3246 */
3247 function setGeo( $latitude, $longitude, $params=FALSE ) {
3248 if(( !empty( $latitude ) || ( 0 == $latitude )) &&
3249 ( !empty( $longitude ) || ( 0 == $longitude ))) {
3250 if( !is_array( $this->geo )) $this->geo = array();
3251 $this->geo['value']['latitude'] = (float) $latitude;
3252 $this->geo['value']['longitude'] = (float) $longitude;
3253 $this->geo['params'] = iCalUtilityFunctions::_setParams( $params );
3254 }
3255 elseif( $this->getConfig( 'allowEmpty' ))
3256 $this->geo = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
3257 else
3258 return FALSE;
3259 return TRUE;
3260 }
3261 /*********************************************************************************/
3262 /**
3263 * Property Name: LAST-MODIFIED
3264 */
3265 /**
3266 * creates formatted output for calendar component property last-modified
3267 *
3268 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3269 * @since 2.4.8 - 2008-10-21
3270 * @return string
3271 */
3272 function createLastModified() {
3273 if( empty( $this->lastmodified )) return FALSE;
3274 $attributes = $this->_createParams( $this->lastmodified['params'] );
3275 $formatted = iCalUtilityFunctions::_date2strdate( $this->lastmodified['value'], 7 );
3276 return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted );
3277 }
3278 /**
3279 * set calendar component property completed
3280 *
3281 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3282 * @since 2.4.8 - 2008-10-23
3283 * @param mixed $year optional
3284 * @param mixed $month optional
3285 * @param int $day optional
3286 * @param int $hour optional
3287 * @param int $min optional
3288 * @param int $sec optional
3289 * @param array $params optional
3290 * @return boll
3291 */
3292 function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
3293 if( empty( $year ))
3294 $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
3295 $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
3296 return TRUE;
3297 }
3298 /*********************************************************************************/
3299 /**
3300 * Property Name: LOCATION
3301 */
3302 /**
3303 * creates formatted output for calendar component property location
3304 *
3305 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3306 * @since 2.16.2 - 2012-12-18
3307 * @return string
3308 */
3309 function createLocation() {
3310 if( empty( $this->location )) return FALSE;
3311 if( empty( $this->location['value'] ))
3312 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE;
3313 $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' ));
3314 $content = iCalUtilityFunctions::_strrep( $this->location['value'], $this->format, $this->nl );
3315 return $this->_createElement( 'LOCATION', $attributes, $content );
3316 }
3317 /**
3318 * set calendar component property location
3319 '
3320 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3321 * @since 2.4.8 - 2008-11-04
3322 * @param string $value
3323 * @param array params optional
3324 * @return bool
3325 */
3326 function setLocation( $value, $params=FALSE ) {
3327 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3328 $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3329 return TRUE;
3330 }
3331 /*********************************************************************************/
3332 /**
3333 * Property Name: ORGANIZER
3334 */
3335 /**
3336 * creates formatted output for calendar component property organizer
3337 *
3338 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3339 * @since 2.6.33 - 2010-12-17
3340 * @return string
3341 */
3342 function createOrganizer() {
3343 if( empty( $this->organizer )) return FALSE;
3344 if( empty( $this->organizer['value'] ))
3345 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE;
3346 $attributes = $this->_createParams( $this->organizer['params']
3347 , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' ));
3348 return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] );
3349 }
3350 /**
3351 * set calendar component property organizer
3352 *
3353 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3354 * @since 2.12.18 - 2012-07-13
3355 * @param string $value
3356 * @param array params optional
3357 * @return bool
3358 */
3359 function setOrganizer( $value, $params=FALSE ) {
3360 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3361 if( !empty( $value )) {
3362 if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
3363 $value = 'MAILTO:'.$value;
3364 elseif( !empty( $value ))
3365 $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
3366 $value = str_replace( 'mailto:', 'MAILTO:', $value );
3367 }
3368 $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3369 if( isset( $this->organizer['params']['SENT-BY'] )){
3370 if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 )))
3371 $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY'];
3372 else
3373 $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 );
3374 }
3375 return TRUE;
3376 }
3377 /*********************************************************************************/
3378 /**
3379 * Property Name: PERCENT-COMPLETE
3380 */
3381 /**
3382 * creates formatted output for calendar component property percent-complete
3383 *
3384 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3385 * @since 2.9.3 - 2011-05-14
3386 * @return string
3387 */
3388 function createPercentComplete() {
3389 if( !isset($this->percentcomplete) || ( empty( $this->percentcomplete ) && !is_numeric( $this->percentcomplete ))) return FALSE;
3390 if( !isset( $this->percentcomplete['value'] ) || ( empty( $this->percentcomplete['value'] ) && !is_numeric( $this->percentcomplete['value'] )))
3391 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE;
3392 $attributes = $this->_createParams( $this->percentcomplete['params'] );
3393 return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] );
3394 }
3395 /**
3396 * set calendar component property percent-complete
3397 *
3398 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3399 * @since 2.9.3 - 2011-05-14
3400 * @param int $value
3401 * @param array $params optional
3402 * @return bool
3403 */
3404 function setPercentComplete( $value, $params=FALSE ) {
3405 if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3406 $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3407 return TRUE;
3408 }
3409 /*********************************************************************************/
3410 /**
3411 * Property Name: PRIORITY
3412 */
3413 /**
3414 * creates formatted output for calendar component property priority
3415 *
3416 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3417 * @since 2.9.3 - 2011-05-14
3418 * @return string
3419 */
3420 function createPriority() {
3421 if( !isset($this->priority) || ( empty( $this->priority ) && !is_numeric( $this->priority ))) return FALSE;
3422 if( !isset( $this->priority['value'] ) || ( empty( $this->priority['value'] ) && !is_numeric( $this->priority['value'] )))
3423 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE;
3424 $attributes = $this->_createParams( $this->priority['params'] );
3425 return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] );
3426 }
3427 /**
3428 * set calendar component property priority
3429 *
3430 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3431 * @since 2.9.3 - 2011-05-14
3432 * @param int $value
3433 * @param array $params optional
3434 * @return bool
3435 */
3436 function setPriority( $value, $params=FALSE ) {
3437 if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3438 $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3439 return TRUE;
3440 }
3441 /*********************************************************************************/
3442 /**
3443 * Property Name: RDATE
3444 */
3445 /**
3446 * creates formatted output for calendar component property rdate
3447 *
3448 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3449 * @since 2.16.9 - 2013-01-09
3450 * @return string
3451 */
3452 function createRdate() {
3453 if( empty( $this->rdate )) return FALSE;
3454 $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
3455 $output = null;
3456 $rdates = array();
3457 foreach( $this->rdate as $rpix => $theRdate ) {
3458 if( empty( $theRdate['value'] )) {
3459 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' );
3460 continue;
3461 }
3462 if( $utctime )
3463 unset( $theRdate['params']['TZID'] );
3464 if( 1 < count( $theRdate['value'] ))
3465 usort( $theRdate['value'], array( 'iCalUtilityFunctions', '_sortRdate1' ));
3466 $rdates[] = $theRdate;
3467 }
3468 if( 1 < count( $rdates ))
3469 usort( $rdates, array( 'iCalUtilityFunctions', '_sortRdate2' ));
3470 foreach( $rdates as $rpix => $theRdate ) {
3471 $attributes = $this->_createParams( $theRdate['params'] );
3472 $cnt = count( $theRdate['value'] );
3473 $content = null;
3474 $rno = 1;
3475 foreach( $theRdate['value'] as $rix => $rdatePart ) {
3476 $contentPart = null;
3477 if( is_array( $rdatePart ) &&
3478 isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD
3479 if( $utctime )
3480 unset( $rdatePart[0]['tz'] );
3481 $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[0] ); // PERIOD part 1
3482 if( $utctime || !empty( $theRdate['params']['TZID'] ))
3483 $formatted = str_replace( 'Z', '', $formatted);
3484 $contentPart .= $formatted;
3485 $contentPart .= '/';
3486 $cnt2 = count( $rdatePart[1]);
3487 if( array_key_exists( 'year', $rdatePart[1] )) {
3488 if( array_key_exists( 'hour', $rdatePart[1] ))
3489 $cnt2 = 7; // date-time
3490 else
3491 $cnt2 = 3; // date
3492 }
3493 elseif( array_key_exists( 'week', $rdatePart[1] )) // duration
3494 $cnt2 = 5;
3495 if(( 7 == $cnt2 ) && // period= -> date-time
3496 isset( $rdatePart[1]['year'] ) &&
3497 isset( $rdatePart[1]['month'] ) &&
3498 isset( $rdatePart[1]['day'] )) {
3499 if( $utctime )
3500 unset( $rdatePart[1]['tz'] );
3501 $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[1] ); // PERIOD part 2
3502 if( $utctime || !empty( $theRdate['params']['TZID'] ))
3503 $formatted = str_replace( 'Z', '', $formatted );
3504 $contentPart .= $formatted;
3505 }
3506 else { // period= -> dur-time
3507 $contentPart .= iCalUtilityFunctions::_duration2str( $rdatePart[1] );
3508 }
3509 } // PERIOD end
3510 else { // SINGLE date start
3511 if( $utctime )
3512 unset( $rdatePart['tz'] );
3513 $parno = ( isset( $theRdate['params']['VALUE'] ) && ( 'DATE' == isset( $theRdate['params']['VALUE'] ))) ? 3 : null;
3514 $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart, $parno );
3515 if( $utctime || !empty( $theRdate['params']['TZID'] ))
3516 $formatted = str_replace( 'Z', '', $formatted);
3517 $contentPart .= $formatted;
3518 }
3519 $content .= $contentPart;
3520 if( $rno < $cnt )
3521 $content .= ',';
3522 $rno++;
3523 }
3524 $output .= $this->_createElement( 'RDATE', $attributes, $content );
3525 }
3526 return $output;
3527 }
3528 /**
3529 * set calendar component property rdate
3530 *
3531 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3532 * @since 2.14.1 - 2012-10-04
3533 * @param array $rdates
3534 * @param array $params, optional
3535 * @param integer $index, optional
3536 * @return bool
3537 */
3538 function setRdate( $rdates, $params=FALSE, $index=FALSE ) {
3539 if( empty( $rdates )) {
3540 if( $this->getConfig( 'allowEmpty' )) {
3541 iCalUtilityFunctions::_setMval( $this->rdate, null, $params, FALSE, $index );
3542 return TRUE;
3543 }
3544 else
3545 return FALSE;
3546 }
3547 $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
3548 if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) {
3549 unset( $input['params']['TZID'] );
3550 $input['params']['VALUE'] = 'DATE-TIME';
3551 }
3552 $zArr = array( 'GMT', 'UTC', 'Z' );
3553 $toZ = ( isset( $params['TZID'] ) && in_array( strtoupper( $params['TZID'] ), $zArr )) ? TRUE : FALSE;
3554 /* check if PERIOD, if not set */
3555 if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) &&
3556 isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) &&
3557 isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) &&
3558 (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) ||
3559 iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) ||
3560 ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) &&
3561 ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] ))))))
3562 $input['params']['VALUE'] = 'PERIOD';
3563 /* check 1:st date, upd. $parno (opt) and save ev. timezone **/
3564 $date = reset( $rdates );
3565 if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD
3566 $date = reset( $date );
3567 iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] );
3568 iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default
3569 foreach( $rdates as $rpix => $theRdate ) {
3570 $inputa = null;
3571 iCalUtilityFunctions::_strDate2arr( $theRdate );
3572 if( is_array( $theRdate )) {
3573 if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD
3574 foreach( $theRdate as $rix => $rPeriod ) {
3575 iCalUtilityFunctions::_strDate2arr( $theRdate );
3576 if( is_array( $rPeriod )) {
3577 if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) { // timestamp
3578 if( isset( $rPeriod['tz'] ) && !iCalUtilityFunctions::_isOffset( $rPeriod['tz'] )) {
3579 if( isset( $input['params']['TZID'] ))
3580 $rPeriod['tz'] = $input['params']['TZID'];
3581 else
3582 $input['params']['TZID'] = $rPeriod['tz'];
3583 }
3584 $inputab = iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno );
3585 }
3586 elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) {
3587 $d = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_chkDateArr( $rPeriod, $parno ) : iCalUtilityFunctions::_chkDateArr( $rPeriod, 6 );
3588 if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
3589 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
3590 $inputab = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
3591 unset( $inputab['unparsedtext'] );
3592 }
3593 else
3594 $inputab = $d;
3595 }
3596 elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date
3597 $inputab = iCalUtilityFunctions::_strdate2date( reset( $rPeriod ), $parno );
3598 unset( $inputab['unparsedtext'] );
3599 }
3600 else // array format duration
3601 $inputab = iCalUtilityFunctions::_duration2arr( $rPeriod );
3602 }
3603 elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration
3604 ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) {
3605 if( 'P' != $rPeriod[0] )
3606 $rPeriod = substr( $rPeriod, 1 );
3607 $inputab = iCalUtilityFunctions::_durationStr2arr( $rPeriod );
3608 }
3609 elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18
3610 $inputab = iCalUtilityFunctions::_strdate2date( $rPeriod, $parno );
3611 unset( $inputab['unparsedtext'] );
3612 }
3613 if(( 0 == $rpix ) && ( 0 == $rix )) {
3614 if( isset( $inputab['tz'] ) && in_array( strtoupper( $inputab['tz'] ), $zArr )) {
3615 $inputab['tz'] = 'Z';
3616 $toZ = TRUE;
3617 }
3618 }
3619 else {
3620 if( isset( $inputa[0]['tz'] ) && ( 'Z' == $inputa[0]['tz'] ) && isset( $inputab['year'] ))
3621 $inputab['tz'] = 'Z';
3622 else
3623 unset( $inputab['tz'] );
3624 }
3625 if( $toZ && isset( $inputab['year'] ) )
3626 $inputab['tz'] = 'Z';
3627 $inputa[] = $inputab;
3628 }
3629 } // PERIOD end
3630 elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) { // timestamp
3631 if( isset( $theRdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theRdate['tz'] )) {
3632 if( isset( $input['params']['TZID'] ))
3633 $theRdate['tz'] = $input['params']['TZID'];
3634 else
3635 $input['params']['TZID'] = $theRdate['tz'];
3636 }
3637 $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno );
3638 }
3639 else { // date[-time]
3640 $inputa = iCalUtilityFunctions::_chkDateArr( $theRdate, $parno );
3641 if( isset( $inputa['tz'] ) && ( 'Z' != $inputa['tz'] ) && iCalUtilityFunctions::_isOffset( $inputa['tz'] )) {
3642 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $inputa['year'], $inputa['month'], $inputa['day'], $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] );
3643 $inputa = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
3644 unset( $inputa['unparsedtext'] );
3645 }
3646 }
3647 }
3648 elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18
3649 $inputa = iCalUtilityFunctions::_strdate2date( $theRdate, $parno );
3650 unset( $inputa['unparsedtext'] );
3651 if( $toZ )
3652 $inputa['tz'] = 'Z';
3653 }
3654 if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD
3655 if(( 0 == $rpix ) && !$toZ )
3656 $toZ = ( isset( $inputa['tz'] ) && in_array( strtoupper( $inputa['tz'] ), $zArr )) ? TRUE : FALSE;
3657 if( $toZ )
3658 $inputa['tz'] = 'Z';
3659 if( 3 == $parno )
3660 unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] );
3661 elseif( isset( $inputa['tz'] ))
3662 $inputa['tz'] = (string) $inputa['tz'];
3663 if( isset( $input['params']['TZID'] ) || ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))))
3664 if( !$toZ )
3665 unset( $inputa['tz'] );
3666 }
3667 $input['value'][] = $inputa;
3668 }
3669 if( 3 == $parno ) {
3670 $input['params']['VALUE'] = 'DATE';
3671 unset( $input['params']['TZID'] );
3672 }
3673 if( $toZ )
3674 unset( $input['params']['TZID'] );
3675 iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index );
3676 return TRUE;
3677 }
3678 /*********************************************************************************/
3679 /**
3680 * Property Name: RECURRENCE-ID
3681 */
3682 /**
3683 * creates formatted output for calendar component property recurrence-id
3684 *
3685 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3686 * @since 2.14.4 - 2012-09-26
3687 * @return string
3688 */
3689 function createRecurrenceid() {
3690 if( empty( $this->recurrenceid )) return FALSE;
3691 if( empty( $this->recurrenceid['value'] ))
3692 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE;
3693 $parno = ( isset( $this->recurrenceid['params']['VALUE'] ) && ( 'DATE' == $this->recurrenceid['params']['VALUE'] )) ? 3 : null;
3694 $formatted = iCalUtilityFunctions::_date2strdate( $this->recurrenceid['value'], $parno );
3695 $attributes = $this->_createParams( $this->recurrenceid['params'] );
3696 return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted );
3697 }
3698 /**
3699 * set calendar component property recurrence-id
3700 *
3701 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3702 * @since 2.9.6 - 2011-05-15
3703 * @param mixed $year
3704 * @param mixed $month optional
3705 * @param int $day optional
3706 * @param int $hour optional
3707 * @param int $min optional
3708 * @param int $sec optional
3709 * @param array $params optional
3710 * @return bool
3711 */
3712 function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
3713 if( empty( $year )) {
3714 if( $this->getConfig( 'allowEmpty' )) {
3715 $this->recurrenceid = array( 'value' => null, 'params' => null );
3716 return TRUE;
3717 }
3718 else
3719 return FALSE;
3720 }
3721 $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
3722 return TRUE;
3723 }
3724 /*********************************************************************************/
3725 /**
3726 * Property Name: RELATED-TO
3727 */
3728 /**
3729 * creates formatted output for calendar component property related-to
3730 *
3731 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3732 * @since 2.16.2 - 2012-12-18
3733 * @return string
3734 */
3735 function createRelatedTo() {
3736 if( empty( $this->relatedto )) return FALSE;
3737 $output = null;
3738 foreach( $this->relatedto as $relation ) {
3739 if( !empty( $relation['value'] ))
3740 $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ), iCalUtilityFunctions::_strrep( $relation['value'], $this->format, $this->nl ));
3741 elseif( $this->getConfig( 'allowEmpty' ))
3742 $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ));
3743 }
3744 return $output;
3745 }
3746 /**
3747 * set calendar component property related-to
3748 *
3749 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3750 * @since 2.11.24 - 2012-02-23
3751 * @param float $relid
3752 * @param array $params, optional
3753 * @param index $index, optional
3754 * @return bool
3755 */
3756 function setRelatedTo( $value, $params=FALSE, $index=FALSE ) {
3757 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3758 iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default
3759 iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index );
3760 return TRUE;
3761 }
3762 /*********************************************************************************/
3763 /**
3764 * Property Name: REPEAT
3765 */
3766 /**
3767 * creates formatted output for calendar component property repeat
3768 *
3769 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3770 * @since 2.9.3 - 2011-05-14
3771 * @return string
3772 */
3773 function createRepeat() {
3774 if( !isset( $this->repeat ) || ( empty( $this->repeat ) && !is_numeric( $this->repeat ))) return FALSE;
3775 if( !isset( $this->repeat['value']) || ( empty( $this->repeat['value'] ) && !is_numeric( $this->repeat['value'] )))
3776 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE;
3777 $attributes = $this->_createParams( $this->repeat['params'] );
3778 return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] );
3779 }
3780 /**
3781 * set calendar component property repeat
3782 *
3783 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3784 * @since 2.9.3 - 2011-05-14
3785 * @param string $value
3786 * @param array $params optional
3787 * @return void
3788 */
3789 function setRepeat( $value, $params=FALSE ) {
3790 if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3791 $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3792 return TRUE;
3793 }
3794 /*********************************************************************************/
3795 /**
3796 * Property Name: REQUEST-STATUS
3797 */
3798 /**
3799 * creates formatted output for calendar component property request-status
3800 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3801 * @since 2.16.2 - 2012-12-18
3802 * @return string
3803 */
3804 function createRequestStatus() {
3805 if( empty( $this->requeststatus )) return FALSE;
3806 $output = null;
3807 foreach( $this->requeststatus as $rstat ) {
3808 if( empty( $rstat['value']['statcode'] )) {
3809 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' );
3810 continue;
3811 }
3812 $attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' ));
3813 $content = number_format( (float) $rstat['value']['statcode'], 2, '.', '');
3814 $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['text'], $this->format, $this->nl );
3815 if( isset( $rstat['value']['extdata'] ))
3816 $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['extdata'], $this->format, $this->nl );
3817 $output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content );
3818 }
3819 return $output;
3820 }
3821 /**
3822 * set calendar component property request-status
3823 *
3824 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3825 * @since 2.5.1 - 2008-11-05
3826 * @param float $statcode
3827 * @param string $text
3828 * @param string $extdata, optional
3829 * @param array $params, optional
3830 * @param integer $index, optional
3831 * @return bool
3832 */
3833 function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) {
3834 if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = null; else return FALSE;
3835 $input = array( 'statcode' => $statcode, 'text' => $text );
3836 if( $extdata )
3837 $input['extdata'] = $extdata;
3838 iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index );
3839 return TRUE;
3840 }
3841 /*********************************************************************************/
3842 /**
3843 * Property Name: RESOURCES
3844 */
3845 /**
3846 * creates formatted output for calendar component property resources
3847 *
3848 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3849 * @since 2.16.2 - 2012-12-18
3850 * @return string
3851 */
3852 function createResources() {
3853 if( empty( $this->resources )) return FALSE;
3854 $output = null;
3855 foreach( $this->resources as $resource ) {
3856 if( empty( $resource['value'] )) {
3857 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' );
3858 continue;
3859 }
3860 $attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' ));
3861 if( is_array( $resource['value'] )) {
3862 foreach( $resource['value'] as $rix => $resourcePart )
3863 $resource['value'][$rix] = iCalUtilityFunctions::_strrep( $resourcePart, $this->format, $this->nl );
3864 $content = implode( ',', $resource['value'] );
3865 }
3866 else
3867 $content = iCalUtilityFunctions::_strrep( $resource['value'], $this->format, $this->nl );
3868 $output .= $this->_createElement( 'RESOURCES', $attributes, $content );
3869 }
3870 return $output;
3871 }
3872 /**
3873 * set calendar component property recources
3874 *
3875 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3876 * @since 2.5.1 - 2008-11-05
3877 * @param mixed $value
3878 * @param array $params, optional
3879 * @param integer $index, optional
3880 * @return bool
3881 */
3882 function setResources( $value, $params=FALSE, $index=FALSE ) {
3883 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3884 iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index );
3885 return TRUE;
3886 }
3887 /*********************************************************************************/
3888 /**
3889 * Property Name: RRULE
3890 */
3891 /**
3892 * creates formatted output for calendar component property rrule
3893 *
3894 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3895 * @since 2.4.8 - 2008-10-21
3896 * @return string
3897 */
3898 function createRrule() {
3899 if( empty( $this->rrule )) return FALSE;
3900 return $this->_format_recur( 'RRULE', $this->rrule );
3901 }
3902 /**
3903 * set calendar component property rrule
3904 *
3905 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3906 * @since 2.5.1 - 2008-11-05
3907 * @param array $rruleset
3908 * @param array $params, optional
3909 * @param integer $index, optional
3910 * @return void
3911 */
3912 function setRrule( $rruleset, $params=FALSE, $index=FALSE ) {
3913 if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = null; else return FALSE;
3914 iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index );
3915 return TRUE;
3916 }
3917 /*********************************************************************************/
3918 /**
3919 * Property Name: SEQUENCE
3920 */
3921 /**
3922 * creates formatted output for calendar component property sequence
3923 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3924 * @since 2.9.3 - 2011-05-14
3925 * @return string
3926 */
3927 function createSequence() {
3928 if( !isset( $this->sequence ) || ( empty( $this->sequence ) && !is_numeric( $this->sequence ))) return FALSE;
3929 if(( !isset($this->sequence['value'] ) || ( empty( $this->sequence['value'] ) && !is_numeric( $this->sequence['value'] ))) &&
3930 ( '0' != $this->sequence['value'] ))
3931 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE;
3932 $attributes = $this->_createParams( $this->sequence['params'] );
3933 return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] );
3934 }
3935 /**
3936 * set calendar component property sequence
3937 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3938 * @since 2.10.8 - 2011-09-19
3939 * @param int $value optional
3940 * @param array $params optional
3941 * @return bool
3942 */
3943 function setSequence( $value=FALSE, $params=FALSE ) {
3944 if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value ))
3945 $value = ( isset( $this->sequence['value'] ) && ( -1 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : '0';
3946 $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3947 return TRUE;
3948 }
3949 /*********************************************************************************/
3950 /**
3951 * Property Name: STATUS
3952 */
3953 /**
3954 * creates formatted output for calendar component property status
3955 *
3956 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3957 * @since 2.4.8 - 2008-10-21
3958 * @return string
3959 */
3960 function createStatus() {
3961 if( empty( $this->status )) return FALSE;
3962 if( empty( $this->status['value'] ))
3963 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE;
3964 $attributes = $this->_createParams( $this->status['params'] );
3965 return $this->_createElement( 'STATUS', $attributes, $this->status['value'] );
3966 }
3967 /**
3968 * set calendar component property status
3969 *
3970 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3971 * @since 2.4.8 - 2008-11-04
3972 * @param string $value
3973 * @param array $params optional
3974 * @return bool
3975 */
3976 function setStatus( $value, $params=FALSE ) {
3977 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3978 $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3979 return TRUE;
3980 }
3981 /*********************************************************************************/
3982 /**
3983 * Property Name: SUMMARY
3984 */
3985 /**
3986 * creates formatted output for calendar component property summary
3987 *
3988 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3989 * @since 2.16.2 - 2012-12-18
3990 * @return string
3991 */
3992 function createSummary() {
3993 if( empty( $this->summary )) return FALSE;
3994 if( empty( $this->summary['value'] ))
3995 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE;
3996 $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' ));
3997 $content = iCalUtilityFunctions::_strrep( $this->summary['value'], $this->format, $this->nl );
3998 return $this->_createElement( 'SUMMARY', $attributes, $content );
3999 }
4000 /**
4001 * set calendar component property summary
4002 *
4003 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4004 * @since 2.4.8 - 2008-11-04
4005 * @param string $value
4006 * @param string $params optional
4007 * @return bool
4008 */
4009 function setSummary( $value, $params=FALSE ) {
4010 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4011 $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4012 return TRUE;
4013 }
4014 /*********************************************************************************/
4015 /**
4016 * Property Name: TRANSP
4017 */
4018 /**
4019 * creates formatted output for calendar component property transp
4020 *
4021 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4022 * @since 2.4.8 - 2008-10-21
4023 * @return string
4024 */
4025 function createTransp() {
4026 if( empty( $this->transp )) return FALSE;
4027 if( empty( $this->transp['value'] ))
4028 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE;
4029 $attributes = $this->_createParams( $this->transp['params'] );
4030 return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] );
4031 }
4032 /**
4033 * set calendar component property transp
4034 *
4035 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4036 * @since 2.4.8 - 2008-11-04
4037 * @param string $value
4038 * @param string $params optional
4039 * @return bool
4040 */
4041 function setTransp( $value, $params=FALSE ) {
4042 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4043 $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4044 return TRUE;
4045 }
4046 /*********************************************************************************/
4047 /**
4048 * Property Name: TRIGGER
4049 */
4050 /**
4051 * creates formatted output for calendar component property trigger
4052 *
4053 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4054 * @since 2.4.16 - 2008-10-21
4055 * @return string
4056 */
4057 function createTrigger() {
4058 if( empty( $this->trigger )) return FALSE;
4059 if( empty( $this->trigger['value'] ))
4060 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE;
4061 $content = $attributes = null;
4062 if( isset( $this->trigger['value']['year'] ) &&
4063 isset( $this->trigger['value']['month'] ) &&
4064 isset( $this->trigger['value']['day'] ))
4065 $content .= iCalUtilityFunctions::_date2strdate( $this->trigger['value'] );
4066 else {
4067 if( TRUE !== $this->trigger['value']['relatedStart'] )
4068 $attributes .= $this->intAttrDelimiter.'RELATED=END';
4069 if( $this->trigger['value']['before'] )
4070 $content .= '-';
4071 $content .= iCalUtilityFunctions::_duration2str( $this->trigger['value'] );
4072 }
4073 $attributes .= $this->_createParams( $this->trigger['params'] );
4074 return $this->_createElement( 'TRIGGER', $attributes, $content );
4075 }
4076 /**
4077 * set calendar component property trigger
4078 *
4079 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4080 * @since 2.14.1 - 2012-09-20
4081 * @param mixed $year
4082 * @param mixed $month optional
4083 * @param int $day optional
4084 * @param int $week optional
4085 * @param int $hour optional
4086 * @param int $min optional
4087 * @param int $sec optional
4088 * @param bool $relatedStart optional
4089 * @param bool $before optional
4090 * @param array $params optional
4091 * @return bool
4092 */
4093 function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) {
4094 if( empty( $year ) && empty( $month ) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec ))
4095 if( $this->getConfig( 'allowEmpty' )) {
4096 $this->trigger = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
4097 return TRUE;
4098 }
4099 else
4100 return FALSE;
4101 if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp UTC
4102 $params = iCalUtilityFunctions::_setParams( $month );
4103 $date = iCalUtilityFunctions::_timestamp2date( $year, 7 );
4104 foreach( $date as $k => $v )
4105 $$k = $v;
4106 }
4107 elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) {
4108 $params = iCalUtilityFunctions::_setParams( $month );
4109 if(!(array_key_exists( 'year', $year ) && // exclude date-time
4110 array_key_exists( 'month', $year ) &&
4111 array_key_exists( 'day', $year ))) { // when this must be a duration
4112 if( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] )))
4113 $relatedStart = FALSE;
4114 else
4115 $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE;
4116 $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ? FALSE : TRUE;
4117 }
4118 $SSYY = ( array_key_exists( 'year', $year )) ? $year['year'] : null;
4119 $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null;
4120 $day = ( array_key_exists( 'day', $year )) ? $year['day'] : null;
4121 $week = ( array_key_exists( 'week', $year )) ? $year['week'] : null;
4122 $hour = ( array_key_exists( 'hour', $year )) ? $year['hour'] : 0; //null;
4123 $min = ( array_key_exists( 'min', $year )) ? $year['min'] : 0; //null;
4124 $sec = ( array_key_exists( 'sec', $year )) ? $year['sec'] : 0; //null;
4125 $year = $SSYY;
4126 }
4127 elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) { // duration or date in a string
4128 $params = iCalUtilityFunctions::_setParams( $month );
4129 if( in_array( $year[0], array( 'P', '+', '-' ))) { // duration
4130 $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) ? FALSE : TRUE;
4131 $before = ( '-' == $year[0] ) ? TRUE : FALSE;
4132 if( 'P' != $year[0] )
4133 $year = substr( $year, 1 );
4134 $date = iCalUtilityFunctions::_durationStr2arr( $year);
4135 }
4136 else // date
4137 $date = iCalUtilityFunctions::_strdate2date( $year, 7 );
4138 unset( $year, $month, $day, $date['unparsedtext'] );
4139 if( empty( $date ))
4140 $sec = 0;
4141 else
4142 foreach( $date as $k => $v )
4143 $$k = $v;
4144 }
4145 else // single values in function input parameters
4146 $params = iCalUtilityFunctions::_setParams( $params );
4147 if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date
4148 $params['VALUE'] = 'DATE-TIME';
4149 $hour = ( $hour ) ? $hour : 0;
4150 $min = ( $min ) ? $min : 0;
4151 $sec = ( $sec ) ? $sec : 0;
4152 $this->trigger = array( 'params' => $params );
4153 $this->trigger['value'] = array( 'year' => $year
4154 , 'month' => $month
4155 , 'day' => $day
4156 , 'hour' => $hour
4157 , 'min' => $min
4158 , 'sec' => $sec
4159 , 'tz' => 'Z' );
4160 return TRUE;
4161 }
4162 elseif(( empty( $year ) && empty( $month )) && // duration
4163 (( !empty( $week ) || ( 0 == $week )) ||
4164 ( !empty( $day ) || ( 0 == $day )) ||
4165 ( !empty( $hour ) || ( 0 == $hour )) ||
4166 ( !empty( $min ) || ( 0 == $min )) ||
4167 ( !empty( $sec ) || ( 0 == $sec )))) {
4168 unset( $params['RELATED'] ); // set at output creation (END only)
4169 unset( $params['VALUE'] ); // 'DURATION' default
4170 $this->trigger = array( 'params' => $params );
4171 $this->trigger['value'] = array();
4172 if( !empty( $week )) $this->trigger['value']['week'] = $week;
4173 if( !empty( $day )) $this->trigger['value']['day'] = $day;
4174 if( !empty( $hour )) $this->trigger['value']['hour'] = $hour;
4175 if( !empty( $min )) $this->trigger['value']['min'] = $min;
4176 if( !empty( $sec )) $this->trigger['value']['sec'] = $sec;
4177 if( empty( $this->trigger['value'] )) {
4178 $this->trigger['value']['sec'] = 0;
4179 $before = FALSE;
4180 }
4181 $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE;
4182 $before = ( FALSE !== $before ) ? TRUE : FALSE;
4183 $this->trigger['value']['relatedStart'] = $relatedStart;
4184 $this->trigger['value']['before'] = $before;
4185 return TRUE;
4186 }
4187 return FALSE;
4188 }
4189 /*********************************************************************************/
4190 /**
4191 * Property Name: TZID
4192 */
4193 /**
4194 * creates formatted output for calendar component property tzid
4195 *
4196 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4197 * @since 2.16.2 - 2012-12-18
4198 * @return string
4199 */
4200 function createTzid() {
4201 if( empty( $this->tzid )) return FALSE;
4202 if( empty( $this->tzid['value'] ))
4203 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE;
4204 $attributes = $this->_createParams( $this->tzid['params'] );
4205 return $this->_createElement( 'TZID', $attributes, iCalUtilityFunctions::_strrep( $this->tzid['value'], $this->format, $this->nl ));
4206 }
4207 /**
4208 * set calendar component property tzid
4209 *
4210 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4211 * @since 2.4.8 - 2008-11-04
4212 * @param string $value
4213 * @param array $params optional
4214 * @return bool
4215 */
4216 function setTzid( $value, $params=FALSE ) {
4217 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4218 $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4219 return TRUE;
4220 }
4221 /*********************************************************************************/
4222 /**
4223 * .. .
4224 * Property Name: TZNAME
4225 */
4226 /**
4227 * creates formatted output for calendar component property tzname
4228 *
4229 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4230 * @since 2.16.2 - 2012-12-18
4231 * @return string
4232 */
4233 function createTzname() {
4234 if( empty( $this->tzname )) return FALSE;
4235 $output = null;
4236 foreach( $this->tzname as $theName ) {
4237 if( !empty( $theName['value'] )) {
4238 $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' ));
4239 $output .= $this->_createElement( 'TZNAME', $attributes, iCalUtilityFunctions::_strrep( $theName['value'], $this->format, $this->nl ));
4240 }
4241 elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' );
4242 }
4243 return $output;
4244 }
4245 /**
4246 * set calendar component property tzname
4247 *
4248 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4249 * @since 2.5.1 - 2008-11-05
4250 * @param string $value
4251 * @param string $params, optional
4252 * @param integer $index, optional
4253 * @return bool
4254 */
4255 function setTzname( $value, $params=FALSE, $index=FALSE ) {
4256 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4257 iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index );
4258 return TRUE;
4259 }
4260 /*********************************************************************************/
4261 /**
4262 * Property Name: TZOFFSETFROM
4263 */
4264 /**
4265 * creates formatted output for calendar component property tzoffsetfrom
4266 *
4267 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4268 * @since 2.4.8 - 2008-10-21
4269 * @return string
4270 */
4271 function createTzoffsetfrom() {
4272 if( empty( $this->tzoffsetfrom )) return FALSE;
4273 if( empty( $this->tzoffsetfrom['value'] ))
4274 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE;
4275 $attributes = $this->_createParams( $this->tzoffsetfrom['params'] );
4276 return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] );
4277 }
4278 /**
4279 * set calendar component property tzoffsetfrom
4280 *
4281 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4282 * @since 2.4.8 - 2008-11-04
4283 * @param string $value
4284 * @param string $params optional
4285 * @return bool
4286 */
4287 function setTzoffsetfrom( $value, $params=FALSE ) {
4288 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4289 $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4290 return TRUE;
4291 }
4292 /*********************************************************************************/
4293 /**
4294 * Property Name: TZOFFSETTO
4295 */
4296 /**
4297 * creates formatted output for calendar component property tzoffsetto
4298 *
4299 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4300 * @since 2.4.8 - 2008-10-21
4301 * @return string
4302 */
4303 function createTzoffsetto() {
4304 if( empty( $this->tzoffsetto )) return FALSE;
4305 if( empty( $this->tzoffsetto['value'] ))
4306 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE;
4307 $attributes = $this->_createParams( $this->tzoffsetto['params'] );
4308 return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] );
4309 }
4310 /**
4311 * set calendar component property tzoffsetto
4312 *
4313 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4314 * @since 2.4.8 - 2008-11-04
4315 * @param string $value
4316 * @param string $params optional
4317 * @return bool
4318 */
4319 function setTzoffsetto( $value, $params=FALSE ) {
4320 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4321 $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4322 return TRUE;
4323 }
4324 /*********************************************************************************/
4325 /**
4326 * Property Name: TZURL
4327 */
4328 /**
4329 * creates formatted output for calendar component property tzurl
4330 *
4331 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4332 * @since 2.4.8 - 2008-10-21
4333 * @return string
4334 */
4335 function createTzurl() {
4336 if( empty( $this->tzurl )) return FALSE;
4337 if( empty( $this->tzurl['value'] ))
4338 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE;
4339 $attributes = $this->_createParams( $this->tzurl['params'] );
4340 return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] );
4341 }
4342 /**
4343 * set calendar component property tzurl
4344 *
4345 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4346 * @since 2.4.8 - 2008-11-04
4347 * @param string $value
4348 * @param string $params optional
4349 * @return boll
4350 */
4351 function setTzurl( $value, $params=FALSE ) {
4352 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4353 $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4354 return TRUE;
4355 }
4356 /*********************************************************************************/
4357 /**
4358 * Property Name: UID
4359 */
4360 /**
4361 * creates formatted output for calendar component property uid
4362 *
4363 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4364 * @since 0.9.7 - 2006-11-20
4365 * @return string
4366 */
4367 function createUid() {
4368 if( 0 >= count( $this->uid ))
4369 $this->_makeuid();
4370 $attributes = $this->_createParams( $this->uid['params'] );
4371 return $this->_createElement( 'UID', $attributes, $this->uid['value'] );
4372 }
4373 /**
4374 * create an unique id for this calendar component object instance
4375 *
4376 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4377 * @since 2.2.7 - 2007-09-04
4378 * @return void
4379 */
4380 function _makeUid() {
4381 $date = date('Ymd\THisT');
4382 $unique = substr(microtime(), 2, 4);
4383 $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
4384 $start = 0;
4385 $end = strlen( $base ) - 1;
4386 $length = 6;
4387 $str = null;
4388 for( $p = 0; $p < $length; $p++ )
4389 $unique .= $base{mt_rand( $start, $end )};
4390 $this->uid = array( 'params' => null );
4391 $this->uid['value'] = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' );
4392 }
4393 /**
4394 * set calendar component property uid
4395 *
4396 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4397 * @since 2.4.8 - 2008-11-04
4398 * @param string $value
4399 * @param string $params optional
4400 * @return bool
4401 */
4402 function setUid( $value, $params=FALSE ) {
4403 if( empty( $value )) return FALSE; // no allowEmpty check here !!!!
4404 $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4405 return TRUE;
4406 }
4407 /*********************************************************************************/
4408 /**
4409 * Property Name: URL
4410 */
4411 /**
4412 * creates formatted output for calendar component property url
4413 *
4414 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4415 * @since 2.4.8 - 2008-10-21
4416 * @return string
4417 */
4418 function createUrl() {
4419 if( empty( $this->url )) return FALSE;
4420 if( empty( $this->url['value'] ))
4421 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE;
4422 $attributes = $this->_createParams( $this->url['params'] );
4423 return $this->_createElement( 'URL', $attributes, $this->url['value'] );
4424 }
4425 /**
4426 * set calendar component property url
4427 *
4428 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4429 * @since 2.16.7 - 2013-01-11
4430 * @param string $value
4431 * @param string $params optional
4432 * @return bool
4433 */
4434 function setUrl( $value, $params=FALSE ) {
4435 if( !empty( $value )) {
4436 if( !filter_var( $value, FILTER_VALIDATE_URL ) && ( 'urn' != strtolower( substr( $value, 0, 3 ))))
4437 return FALSE;
4438 }
4439 elseif( $this->getConfig( 'allowEmpty' ))
4440 $value = null;
4441 else
4442 return FALSE;
4443 $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4444 return TRUE;
4445 }
4446 /*********************************************************************************/
4447 /**
4448 * Property Name: x-prop
4449 */
4450 /**
4451 * creates formatted output for calendar component property x-prop
4452 *
4453 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4454 * @since 2.16.2 - 2012-12-18
4455 * @return string
4456 */
4457 function createXprop() {
4458 if( empty( $this->xprop )) return FALSE;
4459 $output = null;
4460 foreach( $this->xprop as $label => $xpropPart ) {
4461 if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) {
4462 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $label );
4463 continue;
4464 }
4465 $attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
4466 if( is_array( $xpropPart['value'] )) {
4467 foreach( $xpropPart['value'] as $pix => $theXpart )
4468 $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep( $theXpart, $this->format, $this->format );
4469 $xpropPart['value'] = implode( ',', $xpropPart['value'] );
4470 }
4471 else
4472 $xpropPart['value'] = iCalUtilityFunctions::_strrep( $xpropPart['value'], $this->format, $this->nl );
4473 $output .= $this->_createElement( $label, $attributes, $xpropPart['value'] );
4474 }
4475 return $output;
4476 }
4477 /**
4478 * set calendar component property x-prop
4479 *
4480 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4481 * @since 2.11.9 - 2012-01-16
4482 * @param string $label
4483 * @param mixed $value
4484 * @param array $params optional
4485 * @return bool
4486 */
4487 function setXprop( $label, $value, $params=FALSE ) {
4488 if( empty( $label ))
4489 return FALSE;
4490 if( 'X-' != strtoupper( substr( $label, 0, 2 )))
4491 return FALSE;
4492 if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4493 $xprop = array( 'value' => $value );
4494 $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
4495 if( !is_array( $this->xprop )) $this->xprop = array();
4496 $this->xprop[strtoupper( $label )] = $xprop;
4497 return TRUE;
4498 }
4499 /*********************************************************************************/
4500 /*********************************************************************************/
4501 /**
4502 * create element format parts
4503 *
4504 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4505 * @since 2.0.6 - 2006-06-20
4506 * @return string
4507 */
4508 function _createFormat() {
4509 $objectname = null;
4510 switch( $this->format ) {
4511 case 'xcal':
4512 $objectname = ( isset( $this->timezonetype )) ?
4513 strtolower( $this->timezonetype ) : strtolower( $this->objName );
4514 $this->componentStart1 = $this->elementStart1 = '<';
4515 $this->componentStart2 = $this->elementStart2 = '>';
4516 $this->componentEnd1 = $this->elementEnd1 = '</';
4517 $this->componentEnd2 = $this->elementEnd2 = '>'.$this->nl;
4518 $this->intAttrDelimiter = '<!-- -->';
4519 $this->attributeDelimiter = $this->nl;
4520 $this->valueInit = null;
4521 break;
4522 default:
4523 $objectname = ( isset( $this->timezonetype )) ?
4524 strtoupper( $this->timezonetype ) : strtoupper( $this->objName );
4525 $this->componentStart1 = 'BEGIN:';
4526 $this->componentStart2 = null;
4527 $this->componentEnd1 = 'END:';
4528 $this->componentEnd2 = $this->nl;
4529 $this->elementStart1 = null;
4530 $this->elementStart2 = null;
4531 $this->elementEnd1 = null;
4532 $this->elementEnd2 = $this->nl;
4533 $this->intAttrDelimiter = '<!-- -->';
4534 $this->attributeDelimiter = ';';
4535 $this->valueInit = ':';
4536 break;
4537 }
4538 return $objectname;
4539 }
4540 /**
4541 * creates formatted output for calendar component property
4542 *
4543 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4544 * @since 2.16.2 - 2012-12-18
4545 * @param string $label property name
4546 * @param string $attributes property attributes
4547 * @param string $content property content (optional)
4548 * @return string
4549 */
4550 function _createElement( $label, $attributes=null, $content=FALSE ) {
4551 switch( $this->format ) {
4552 case 'xcal':
4553 $label = strtolower( $label );
4554 break;
4555 default:
4556 $label = strtoupper( $label );
4557 break;
4558 }
4559 $output = $this->elementStart1.$label;
4560 $categoriesAttrLang = null;
4561 $attachInlineBinary = FALSE;
4562 $attachfmttype = null;
4563 if (( 'xcal' == $this->format) && ( 'x-' == substr( $label, 0, 2 ))) {
4564 $this->xcaldecl[] = array( 'xmldecl' => 'ELEMENT'
4565 , 'ref' => $label
4566 , 'type2' => '(#PCDATA)' );
4567 }
4568 if( !empty( $attributes )) {
4569 $attributes = trim( $attributes );
4570 if ( 'xcal' == $this->format ) {
4571 $attributes2 = explode( $this->intAttrDelimiter, $attributes );
4572 $attributes = null;
4573 foreach( $attributes2 as $aix => $attribute ) {
4574 $attrKVarr = explode( '=', $attribute );
4575 if( empty( $attrKVarr[0] ))
4576 continue;
4577 if( !isset( $attrKVarr[1] )) {
4578 $attrValue = $attrKVarr[0];
4579 $attrKey = $aix;
4580 }
4581 elseif( 2 == count( $attrKVarr)) {
4582 $attrKey = strtolower( $attrKVarr[0] );
4583 $attrValue = $attrKVarr[1];
4584 }
4585 else {
4586 $attrKey = strtolower( $attrKVarr[0] );
4587 unset( $attrKVarr[0] );
4588 $attrValue = implode( '=', $attrKVarr );
4589 }
4590 if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) {
4591 $attachInlineBinary = TRUE;
4592 if( 'fmttype' == $attrKey )
4593 $attachfmttype = $attrKey.'='.$attrValue;
4594 continue;
4595 }
4596 elseif(( 'categories' == $label ) && ( 'language' == $attrKey ))
4597 $categoriesAttrLang = $attrKey.'='.$attrValue;
4598 else {
4599 $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
4600 $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null;
4601 if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) {
4602 $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 ));
4603 $attrValue = str_replace( '"', '', $attrValue );
4604 }
4605 $attributes .= '"'.htmlspecialchars( $attrValue ).'"';
4606 }
4607 }
4608 }
4609 else {
4610 $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
4611 }
4612 }
4613 if(( 'xcal' == $this->format) &&
4614 ((( 'attach' == $label ) && !$attachInlineBinary ) || ( in_array( $label, array( 'tzurl', 'url' ))))) {
4615 $pos = strrpos($content, "/");
4616 $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content;
4617 $this->xcaldecl[] = array( 'xmldecl' => 'ENTITY'
4618 , 'uri' => $docname
4619 , 'ref' => 'SYSTEM'
4620 , 'external' => $content
4621 , 'type' => 'NDATA'
4622 , 'type2' => 'BINERY' );
4623 $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
4624 $attributes .= 'uri="'.$docname.'"';
4625 $content = null;
4626 if( 'attach' == $label ) {
4627 $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes );
4628 $content = $this->nl.$this->_createElement( 'extref', $attributes, null );
4629 $attributes = null;
4630 }
4631 }
4632 elseif(( 'xcal' == $this->format) && ( 'attach' == $label ) && $attachInlineBinary ) {
4633 $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute
4634 }
4635 $output .= $attributes;
4636 if( !$content && ( '0' != $content )) {
4637 switch( $this->format ) {
4638 case 'xcal':
4639 $output .= ' /';
4640 $output .= $this->elementStart2.$this->nl;
4641 return $output;
4642 break;
4643 default:
4644 $output .= $this->elementStart2.$this->valueInit;
4645 return iCalUtilityFunctions::_size75( $output, $this->nl );
4646 break;
4647 }
4648 }
4649 $output .= $this->elementStart2;
4650 $output .= $this->valueInit.$content;
4651 switch( $this->format ) {
4652 case 'xcal':
4653 return $output.$this->elementEnd1.$label.$this->elementEnd2;
4654 break;
4655 default:
4656 return iCalUtilityFunctions::_size75( $output, $this->nl );
4657 break;
4658 }
4659 }
4660 /**
4661 * creates formatted output for calendar component property parameters
4662 *
4663 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4664 * @since 2.10.27 - 2012-01-16
4665 * @param array $params optional
4666 * @param array $ctrKeys optional
4667 * @return string
4668 */
4669 function _createParams( $params=array(), $ctrKeys=array() ) {
4670 if( !is_array( $params ) || empty( $params ))
4671 $params = array();
4672 $attrLANG = $attr1 = $attr2 = $lang = null;
4673 $CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ;
4674 $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ;
4675 $CNattrExist = $LANGattrExist = FALSE;
4676 $xparams = array();
4677 foreach( $params as $paramKey => $paramValue ) {
4678 if(( FALSE !== strpos( $paramValue, ':' )) ||
4679 ( FALSE !== strpos( $paramValue, ';' )) ||
4680 ( FALSE !== strpos( $paramValue, ',' )))
4681 $paramValue = '"'.$paramValue.'"';
4682 if( ctype_digit( (string) $paramKey )) {
4683 $xparams[] = $paramValue;
4684 continue;
4685 }
4686 $paramKey = strtoupper( $paramKey );
4687 if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' )))
4688 $xparams[$paramKey] = $paramValue;
4689 else
4690 $params[$paramKey] = $paramValue;
4691 }
4692 ksort( $xparams, SORT_STRING );
4693 foreach( $xparams as $paramKey => $paramValue ) {
4694 if( ctype_digit( (string) $paramKey ))
4695 $attr2 .= $this->intAttrDelimiter.$paramValue;
4696 else
4697 $attr2 .= $this->intAttrDelimiter."$paramKey=$paramValue";
4698 }
4699 if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) {
4700 $attr1 .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2;
4701 $attr2 = null;
4702 }
4703 if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) {
4704 if( !empty( $attr2 )) {
4705 $attr1 .= $attr2;
4706 $attr2 = null;
4707 }
4708 $attr1 .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING'];
4709 }
4710 if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys ))
4711 $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE'];
4712 if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) {
4713 $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID'];
4714 }
4715 if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys ))
4716 $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE'];
4717 if( isset( $params['RELTYPE'] ) && !in_array( 'RELTYPE', $ctrKeys ))
4718 $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE'];
4719 if( isset( $params['CN'] ) && $CNattrKey ) {
4720 $attr1 = $this->intAttrDelimiter.'CN='.$params['CN'];
4721 $CNattrExist = TRUE;
4722 }
4723 if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) {
4724 $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ? '' : '"';
4725 $attr1 .= $this->intAttrDelimiter.'DIR='.$delim.$params['DIR'].$delim;
4726 }
4727 if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys ))
4728 $attr1 .= $this->intAttrDelimiter.'SENT-BY='.$params['SENT-BY'];
4729 if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) {
4730 $delim = ( FALSE !== strpos( $params['ALTREP'], '"' )) ? '' : '"';
4731 $attr1 .= $this->intAttrDelimiter.'ALTREP='.$delim.$params['ALTREP'].$delim;
4732 }
4733 if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) {
4734 $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE'];
4735 $LANGattrExist = TRUE;
4736 }
4737 if( !$LANGattrExist ) {
4738 $lang = $this->getConfig( 'language' );
4739 if(( $CNattrExist || $LANGattrKey ) && $lang )
4740 $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang;
4741 }
4742 return $attr1.$attrLANG.$attr2;
4743 }
4744 /**
4745 * creates formatted output for calendar component property data value type recur
4746 *
4747 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4748 * @since 2.14.1 - 2012-10-06
4749 * @param array $recurlabel
4750 * @param array $recurdata
4751 * @return string
4752 */
4753 function _format_recur( $recurlabel, $recurdata ) {
4754 $output = null;
4755 foreach( $recurdata as $therule ) {
4756 if( empty( $therule['value'] )) {
4757 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel );
4758 continue;
4759 }
4760 $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null;
4761 $content1 = $content2 = null;
4762 foreach( $therule['value'] as $rulelabel => $rulevalue ) {
4763 switch( $rulelabel ) {
4764 case 'FREQ': {
4765 $content1 .= "FREQ=$rulevalue";
4766 break;
4767 }
4768 case 'UNTIL': {
4769 $parno = ( isset( $rulevalue['hour'] )) ? 7 : 3;
4770 $content2 .= ';UNTIL='.iCalUtilityFunctions::_date2strdate( $rulevalue, $parno );
4771 break;
4772 }
4773 case 'COUNT':
4774 case 'INTERVAL':
4775 case 'WKST': {
4776 $content2 .= ";$rulelabel=$rulevalue";
4777 break;
4778 }
4779 case 'BYSECOND':
4780 case 'BYMINUTE':
4781 case 'BYHOUR':
4782 case 'BYMONTHDAY':
4783 case 'BYYEARDAY':
4784 case 'BYWEEKNO':
4785 case 'BYMONTH':
4786 case 'BYSETPOS': {
4787 $content2 .= ";$rulelabel=";
4788 if( is_array( $rulevalue )) {
4789 foreach( $rulevalue as $vix => $valuePart ) {
4790 $content2 .= ( $vix ) ? ',' : null;
4791 $content2 .= $valuePart;
4792 }
4793 }
4794 else
4795 $content2 .= $rulevalue;
4796 break;
4797 }
4798 case 'BYDAY': {
4799 $content2 .= ";$rulelabel=";
4800 $bydaycnt = 0;
4801 foreach( $rulevalue as $vix => $valuePart ) {
4802 $content21 = $content22 = null;
4803 if( is_array( $valuePart )) {
4804 $content2 .= ( $bydaycnt ) ? ',' : null;
4805 foreach( $valuePart as $vix2 => $valuePart2 ) {
4806 if( 'DAY' != strtoupper( $vix2 ))
4807 $content21 .= $valuePart2;
4808 else
4809 $content22 .= $valuePart2;
4810 }
4811 $content2 .= $content21.$content22;
4812 $bydaycnt++;
4813 }
4814 else {
4815 $content2 .= ( $bydaycnt ) ? ',' : null;
4816 if( 'DAY' != strtoupper( $vix ))
4817 $content21 .= $valuePart;
4818 else {
4819 $content22 .= $valuePart;
4820 $bydaycnt++;
4821 }
4822 $content2 .= $content21.$content22;
4823 }
4824 }
4825 break;
4826 }
4827 default: {
4828 $content2 .= ";$rulelabel=$rulevalue";
4829 break;
4830 }
4831 }
4832 }
4833 $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 );
4834 }
4835 return $output;
4836 }
4837 /**
4838 * check if property not exists within component
4839 *
4840 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4841 * @since 2.5.1 - 2008-10-15
4842 * @param string $propName
4843 * @return bool
4844 */
4845 function _notExistProp( $propName ) {
4846 if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed
4847 $propName = strtolower( $propName );
4848 if( 'last-modified' == $propName ) { if( !isset( $this->lastmodified )) return TRUE; }
4849 elseif( 'percent-complete' == $propName ) { if( !isset( $this->percentcomplete )) return TRUE; }
4850 elseif( 'recurrence-id' == $propName ) { if( !isset( $this->recurrenceid )) return TRUE; }
4851 elseif( 'related-to' == $propName ) { if( !isset( $this->relatedto )) return TRUE; }
4852 elseif( 'request-status' == $propName ) { if( !isset( $this->requeststatus )) return TRUE; }
4853 elseif(( 'x-' != substr($propName,0,2)) && !isset( $this->$propName )) return TRUE;
4854 return FALSE;
4855 }
4856 /*********************************************************************************/
4857 /*********************************************************************************/
4858 /**
4859 * get general component config variables or info about subcomponents
4860 *
4861 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4862 * @since 2.9.6 - 2011-05-14
4863 * @param mixed $config
4864 * @return value
4865 */
4866 function getConfig( $config = FALSE) {
4867 if( !$config ) {
4868 $return = array();
4869 $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
4870 $return['FORMAT'] = $this->getConfig( 'FORMAT' );
4871 if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
4872 $return['LANGUAGE'] = $lang;
4873 $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
4874 $return['TZTD'] = $this->getConfig( 'TZID' );
4875 $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
4876 return $return;
4877 }
4878 switch( strtoupper( $config )) {
4879 case 'ALLOWEMPTY':
4880 return $this->allowEmpty;
4881 break;
4882 case 'COMPSINFO':
4883 unset( $this->compix );
4884 $info = array();
4885 if( isset( $this->components )) {
4886 foreach( $this->components as $cix => $component ) {
4887 if( empty( $component )) continue;
4888 $info[$cix]['ordno'] = $cix + 1;
4889 $info[$cix]['type'] = $component->objName;
4890 $info[$cix]['uid'] = $component->getProperty( 'uid' );
4891 $info[$cix]['props'] = $component->getConfig( 'propinfo' );
4892 $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
4893 }
4894 }
4895 return $info;
4896 break;
4897 case 'FORMAT':
4898 return $this->format;
4899 break;
4900 case 'LANGUAGE':
4901 // get language for calendar component as defined in [RFC 1766]
4902 return $this->language;
4903 break;
4904 case 'NL':
4905 case 'NEWLINECHAR':
4906 return $this->nl;
4907 break;
4908 case 'PROPINFO':
4909 $output = array();
4910 if( !in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
4911 if( empty( $this->uid['value'] )) $this->_makeuid();
4912 $output['UID'] = 1;
4913 if( empty( $this->dtstamp )) $this->_makeDtstamp();
4914 $output['DTSTAMP'] = 1;
4915 }
4916 if( !empty( $this->summary )) $output['SUMMARY'] = 1;
4917 if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description );
4918 if( !empty( $this->dtstart )) $output['DTSTART'] = 1;
4919 if( !empty( $this->dtend )) $output['DTEND'] = 1;
4920 if( !empty( $this->due )) $output['DUE'] = 1;
4921 if( !empty( $this->duration )) $output['DURATION'] = 1;
4922 if( !empty( $this->rrule )) $output['RRULE'] = count( $this->rrule );
4923 if( !empty( $this->rdate )) $output['RDATE'] = count( $this->rdate );
4924 if( !empty( $this->exdate )) $output['EXDATE'] = count( $this->exdate );
4925 if( !empty( $this->exrule )) $output['EXRULE'] = count( $this->exrule );
4926 if( !empty( $this->action )) $output['ACTION'] = 1;
4927 if( !empty( $this->attach )) $output['ATTACH'] = count( $this->attach );
4928 if( !empty( $this->attendee )) $output['ATTENDEE'] = count( $this->attendee );
4929 if( !empty( $this->categories )) $output['CATEGORIES'] = count( $this->categories );
4930 if( !empty( $this->class )) $output['CLASS'] = 1;
4931 if( !empty( $this->comment )) $output['COMMENT'] = count( $this->comment );
4932 if( !empty( $this->completed )) $output['COMPLETED'] = 1;
4933 if( !empty( $this->contact )) $output['CONTACT'] = count( $this->contact );
4934 if( !empty( $this->created )) $output['CREATED'] = 1;
4935 if( !empty( $this->freebusy )) $output['FREEBUSY'] = count( $this->freebusy );
4936 if( !empty( $this->geo )) $output['GEO'] = 1;
4937 if( !empty( $this->lastmodified )) $output['LAST-MODIFIED'] = 1;
4938 if( !empty( $this->location )) $output['LOCATION'] = 1;
4939 if( !empty( $this->organizer )) $output['ORGANIZER'] = 1;
4940 if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1;
4941 if( !empty( $this->priority )) $output['PRIORITY'] = 1;
4942 if( !empty( $this->recurrenceid )) $output['RECURRENCE-ID'] = 1;
4943 if( !empty( $this->relatedto )) $output['RELATED-TO'] = count( $this->relatedto );
4944 if( !empty( $this->repeat )) $output['REPEAT'] = 1;
4945 if( !empty( $this->requeststatus )) $output['REQUEST-STATUS'] = count( $this->requeststatus );
4946 if( !empty( $this->resources )) $output['RESOURCES'] = count( $this->resources );
4947 if( !empty( $this->sequence )) $output['SEQUENCE'] = 1;
4948 if( !empty( $this->sequence )) $output['SEQUENCE'] = 1;
4949 if( !empty( $this->status )) $output['STATUS'] = 1;
4950 if( !empty( $this->transp )) $output['TRANSP'] = 1;
4951 if( !empty( $this->trigger )) $output['TRIGGER'] = 1;
4952 if( !empty( $this->tzid )) $output['TZID'] = 1;
4953 if( !empty( $this->tzname )) $output['TZNAME'] = count( $this->tzname );
4954 if( !empty( $this->tzoffsetfrom )) $output['TZOFFSETFROM'] = 1;
4955 if( !empty( $this->tzoffsetto )) $output['TZOFFSETTO'] = 1;
4956 if( !empty( $this->tzurl )) $output['TZURL'] = 1;
4957 if( !empty( $this->url )) $output['URL'] = 1;
4958 if( !empty( $this->xprop )) $output['X-PROP'] = count( $this->xprop );
4959 return $output;
4960 break;
4961 case 'SETPROPERTYNAMES':
4962 return array_keys( $this->getConfig( 'propinfo' ));
4963 break;
4964 case 'TZID':
4965 return $this->dtzid;
4966 break;
4967 case 'UNIQUE_ID':
4968 if( empty( $this->unique_id ))
4969 $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
4970 return $this->unique_id;
4971 break;
4972 }
4973 }
4974 /**
4975 * general component config setting
4976 *
4977 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4978 * @since 2.10.18 - 2011-10-28
4979 * @param mixed $config
4980 * @param string $value
4981 * @param bool $softUpdate
4982 * @return void
4983 */
4984 function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) {
4985 if( is_array( $config )) {
4986 $ak = array_keys( $config );
4987 foreach( $ak as $k ) {
4988 if( 'NEWLINECHAR' == strtoupper( $k )) {
4989 if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] ))
4990 return FALSE;
4991 unset( $config[$k] );
4992 break;
4993 }
4994 }
4995 foreach( $config as $cKey => $cValue ) {
4996 if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate ))
4997 return FALSE;
4998 }
4999 return TRUE;
5000 }
5001 $res = FALSE;
5002 switch( strtoupper( $config )) {
5003 case 'ALLOWEMPTY':
5004 $this->allowEmpty = $value;
5005 $subcfg = array( 'ALLOWEMPTY' => $value );
5006 $res = TRUE;
5007 break;
5008 case 'FORMAT':
5009 $value = trim( strtolower( $value ));
5010 $this->format = $value;
5011 $this->_createFormat();
5012 $subcfg = array( 'FORMAT' => $value );
5013 $res = TRUE;
5014 break;
5015 case 'LANGUAGE':
5016 // set language for calendar component as defined in [RFC 1766]
5017 $value = trim( $value );
5018 if( empty( $this->language ) || !$softUpdate )
5019 $this->language = $value;
5020 $subcfg = array( 'LANGUAGE' => $value );
5021 $res = TRUE;
5022 break;
5023 case 'NL':
5024 case 'NEWLINECHAR':
5025 $this->nl = $value;
5026 $this->_createFormat();
5027 $subcfg = array( 'NL' => $value );
5028 $res = TRUE;
5029 break;
5030 case 'TZID':
5031 $this->dtzid = $value;
5032 $subcfg = array( 'TZID' => $value );
5033 $res = TRUE;
5034 break;
5035 case 'UNIQUE_ID':
5036 $value = trim( $value );
5037 $this->unique_id = $value;
5038 $subcfg = array( 'UNIQUE_ID' => $value );
5039 $res = TRUE;
5040 break;
5041 default: // any unvalid config key.. .
5042 return TRUE;
5043 }
5044 if( !$res ) return FALSE;
5045 if( isset( $subcfg ) && !empty( $this->components )) {
5046 foreach( $subcfg as $cfgkey => $cfgvalue ) {
5047 foreach( $this->components as $cix => $component ) {
5048 $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate );
5049 if( !$res )
5050 break 2;
5051 $this->components[$cix] = $component->copy(); // PHP4 compliant
5052 }
5053 }
5054 }
5055 return $res;
5056 }
5057 /*********************************************************************************/
5058 /**
5059 * delete component property value
5060 *
5061 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5062 * @since 2.8.8 - 2011-03-15
5063 * @param mixed $propName, bool FALSE => X-property
5064 * @param int $propix, optional, if specific property is wanted in case of multiply occurences
5065 * @return bool, if successfull delete TRUE
5066 */
5067 function deleteProperty( $propName=FALSE, $propix=FALSE ) {
5068 if( $this->_notExistProp( $propName )) return FALSE;
5069 $propName = strtoupper( $propName );
5070 if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE',
5071 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) {
5072 if( !$propix )
5073 $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
5074 $this->propdelix[$propName] = --$propix;
5075 }
5076 $return = FALSE;
5077 switch( $propName ) {
5078 case 'ACTION':
5079 if( !empty( $this->action )) {
5080 $this->action = '';
5081 $return = TRUE;
5082 }
5083 break;
5084 case 'ATTACH':
5085 return $this->deletePropertyM( $this->attach, $this->propdelix[$propName] );
5086 break;
5087 case 'ATTENDEE':
5088 return $this->deletePropertyM( $this->attendee, $this->propdelix[$propName] );
5089 break;
5090 case 'CATEGORIES':
5091 return $this->deletePropertyM( $this->categories, $this->propdelix[$propName] );
5092 break;
5093 case 'CLASS':
5094 if( !empty( $this->class )) {
5095 $this->class = '';
5096 $return = TRUE;
5097 }
5098 break;
5099 case 'COMMENT':
5100 return $this->deletePropertyM( $this->comment, $this->propdelix[$propName] );
5101 break;
5102 case 'COMPLETED':
5103 if( !empty( $this->completed )) {
5104 $this->completed = '';
5105 $return = TRUE;
5106 }
5107 break;
5108 case 'CONTACT':
5109 return $this->deletePropertyM( $this->contact, $this->propdelix[$propName] );
5110 break;
5111 case 'CREATED':
5112 if( !empty( $this->created )) {
5113 $this->created = '';
5114 $return = TRUE;
5115 }
5116 break;
5117 case 'DESCRIPTION':
5118 return $this->deletePropertyM( $this->description, $this->propdelix[$propName] );
5119 break;
5120 case 'DTEND':
5121 if( !empty( $this->dtend )) {
5122 $this->dtend = '';
5123 $return = TRUE;
5124 }
5125 break;
5126 case 'DTSTAMP':
5127 if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5128 return FALSE;
5129 if( !empty( $this->dtstamp )) {
5130 $this->dtstamp = '';
5131 $return = TRUE;
5132 }
5133 break;
5134 case 'DTSTART':
5135 if( !empty( $this->dtstart )) {
5136 $this->dtstart = '';
5137 $return = TRUE;
5138 }
5139 break;
5140 case 'DUE':
5141 if( !empty( $this->due )) {
5142 $this->due = '';
5143 $return = TRUE;
5144 }
5145 break;
5146 case 'DURATION':
5147 if( !empty( $this->duration )) {
5148 $this->duration = '';
5149 $return = TRUE;
5150 }
5151 break;
5152 case 'EXDATE':
5153 return $this->deletePropertyM( $this->exdate, $this->propdelix[$propName] );
5154 break;
5155 case 'EXRULE':
5156 return $this->deletePropertyM( $this->exrule, $this->propdelix[$propName] );
5157 break;
5158 case 'FREEBUSY':
5159 return $this->deletePropertyM( $this->freebusy, $this->propdelix[$propName] );
5160 break;
5161 case 'GEO':
5162 if( !empty( $this->geo )) {
5163 $this->geo = '';
5164 $return = TRUE;
5165 }
5166 break;
5167 case 'LAST-MODIFIED':
5168 if( !empty( $this->lastmodified )) {
5169 $this->lastmodified = '';
5170 $return = TRUE;
5171 }
5172 break;
5173 case 'LOCATION':
5174 if( !empty( $this->location )) {
5175 $this->location = '';
5176 $return = TRUE;
5177 }
5178 break;
5179 case 'ORGANIZER':
5180 if( !empty( $this->organizer )) {
5181 $this->organizer = '';
5182 $return = TRUE;
5183 }
5184 break;
5185 case 'PERCENT-COMPLETE':
5186 if( !empty( $this->percentcomplete )) {
5187 $this->percentcomplete = '';
5188 $return = TRUE;
5189 }
5190 break;
5191 case 'PRIORITY':
5192 if( !empty( $this->priority )) {
5193 $this->priority = '';
5194 $return = TRUE;
5195 }
5196 break;
5197 case 'RDATE':
5198 return $this->deletePropertyM( $this->rdate, $this->propdelix[$propName] );
5199 break;
5200 case 'RECURRENCE-ID':
5201 if( !empty( $this->recurrenceid )) {
5202 $this->recurrenceid = '';
5203 $return = TRUE;
5204 }
5205 break;
5206 case 'RELATED-TO':
5207 return $this->deletePropertyM( $this->relatedto, $this->propdelix[$propName] );
5208 break;
5209 case 'REPEAT':
5210 if( !empty( $this->repeat )) {
5211 $this->repeat = '';
5212 $return = TRUE;
5213 }
5214 break;
5215 case 'REQUEST-STATUS':
5216 return $this->deletePropertyM( $this->requeststatus, $this->propdelix[$propName] );
5217 break;
5218 case 'RESOURCES':
5219 return $this->deletePropertyM( $this->resources, $this->propdelix[$propName] );
5220 break;
5221 case 'RRULE':
5222 return $this->deletePropertyM( $this->rrule, $this->propdelix[$propName] );
5223 break;
5224 case 'SEQUENCE':
5225 if( !empty( $this->sequence )) {
5226 $this->sequence = '';
5227 $return = TRUE;
5228 }
5229 break;
5230 case 'STATUS':
5231 if( !empty( $this->status )) {
5232 $this->status = '';
5233 $return = TRUE;
5234 }
5235 break;
5236 case 'SUMMARY':
5237 if( !empty( $this->summary )) {
5238 $this->summary = '';
5239 $return = TRUE;
5240 }
5241 break;
5242 case 'TRANSP':
5243 if( !empty( $this->transp )) {
5244 $this->transp = '';
5245 $return = TRUE;
5246 }
5247 break;
5248 case 'TRIGGER':
5249 if( !empty( $this->trigger )) {
5250 $this->trigger = '';
5251 $return = TRUE;
5252 }
5253 break;
5254 case 'TZID':
5255 if( !empty( $this->tzid )) {
5256 $this->tzid = '';
5257 $return = TRUE;
5258 }
5259 break;
5260 case 'TZNAME':
5261 return $this->deletePropertyM( $this->tzname, $this->propdelix[$propName] );
5262 break;
5263 case 'TZOFFSETFROM':
5264 if( !empty( $this->tzoffsetfrom )) {
5265 $this->tzoffsetfrom = '';
5266 $return = TRUE;
5267 }
5268 break;
5269 case 'TZOFFSETTO':
5270 if( !empty( $this->tzoffsetto )) {
5271 $this->tzoffsetto = '';
5272 $return = TRUE;
5273 }
5274 break;
5275 case 'TZURL':
5276 if( !empty( $this->tzurl )) {
5277 $this->tzurl = '';
5278 $return = TRUE;
5279 }
5280 break;
5281 case 'UID':
5282 if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5283 return FALSE;
5284 if( !empty( $this->uid )) {
5285 $this->uid = '';
5286 $return = TRUE;
5287 }
5288 break;
5289 case 'URL':
5290 if( !empty( $this->url )) {
5291 $this->url = '';
5292 $return = TRUE;
5293 }
5294 break;
5295 default:
5296 $reduced = '';
5297 if( $propName != 'X-PROP' ) {
5298 if( !isset( $this->xprop[$propName] )) return FALSE;
5299 foreach( $this->xprop as $k => $a ) {
5300 if(( $k != $propName ) && !empty( $a ))
5301 $reduced[$k] = $a;
5302 }
5303 }
5304 else {
5305 if( count( $this->xprop ) <= $propix ) { unset( $this->propdelix[$propName] ); return FALSE; }
5306 $xpropno = 0;
5307 foreach( $this->xprop as $xpropkey => $xpropvalue ) {
5308 if( $propix != $xpropno )
5309 $reduced[$xpropkey] = $xpropvalue;
5310 $xpropno++;
5311 }
5312 }
5313 $this->xprop = $reduced;
5314 if( empty( $this->xprop )) {
5315 unset( $this->propdelix[$propName] );
5316 return FALSE;
5317 }
5318 return TRUE;
5319 }
5320 return $return;
5321 }
5322 /*********************************************************************************/
5323 /**
5324 * delete component property value, fixing components with multiple occurencies
5325 *
5326 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5327 * @since 2.8.8 - 2011-03-15
5328 * @param array $multiprop, reference to a component property
5329 * @param int $propix, reference to removal counter
5330 * @return bool TRUE
5331 */
5332 function deletePropertyM( & $multiprop, & $propix ) {
5333 if( isset( $multiprop[$propix] ))
5334 unset( $multiprop[$propix] );
5335 if( empty( $multiprop )) {
5336 $multiprop = '';
5337 unset( $propix );
5338 return FALSE;
5339 }
5340 else
5341 return TRUE;
5342 }
5343 /**
5344 * get component property value/params
5345 *
5346 * if property has multiply values, consequtive function calls are needed
5347 *
5348 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5349 * @since 2.12.4 - 2012-04-22
5350 * @param string $propName, optional
5351 * @param int @propix, optional, if specific property is wanted in case of multiply occurences
5352 * @param bool $inclParam=FALSE
5353 * @param bool $specform=FALSE
5354 * @return mixed
5355 */
5356 function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) {
5357 if( 'GEOLOCATION' == strtoupper( $propName )) {
5358 $content = $this->getProperty( 'LOCATION' );
5359 $content = ( !empty( $content )) ? $content.' ' : '';
5360 if(( FALSE === ( $geo = $this->getProperty( 'GEO' ))) || empty( $geo ))
5361 return FALSE;
5362 if( 0.0 < $geo['latitude'] )
5363 $sign = '+';
5364 else
5365 $sign = ( 0.0 > $geo['latitude'] ) ? '-' : '';
5366 $content .= $sign.sprintf( "%09.6f", abs( $geo['latitude'] )); // sprintf && lpad && float && sign !"#¤%&/(
5367 $content = rtrim( rtrim( $content, '0' ), '.' );
5368 if( 0.0 < $geo['longitude'] )
5369 $sign = '+';
5370 else
5371 $sign = ( 0.0 > $geo['longitude'] ) ? '-' : '';
5372 return $content.$sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/'; // sprintf && lpad && float && sign !"#¤%&/(
5373 }
5374 if( $this->_notExistProp( $propName )) return FALSE;
5375 $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
5376 if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE',
5377 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) {
5378 if( !$propix )
5379 $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
5380 $this->propix[$propName] = --$propix;
5381 }
5382 switch( $propName ) {
5383 case 'ACTION':
5384 if( !empty( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value'];
5385 break;
5386 case 'ATTACH':
5387 $ak = ( is_array( $this->attach )) ? array_keys( $this->attach ) : array();
5388 while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( $ak )))
5389 $propix++;
5390 $this->propix[$propName] = $propix;
5391 if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5392 return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value'];
5393 break;
5394 case 'ATTENDEE':
5395 $ak = ( is_array( $this->attendee )) ? array_keys( $this->attendee ) : array();
5396 while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( $ak )))
5397 $propix++;
5398 $this->propix[$propName] = $propix;
5399 if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5400 return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value'];
5401 break;
5402 case 'CATEGORIES':
5403 $ak = ( is_array( $this->categories )) ? array_keys( $this->categories ) : array();
5404 while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( $ak )))
5405 $propix++;
5406 $this->propix[$propName] = $propix;
5407 if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5408 return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value'];
5409 break;
5410 case 'CLASS':
5411 if( !empty( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value'];
5412 break;
5413 case 'COMMENT':
5414 $ak = ( is_array( $this->comment )) ? array_keys( $this->comment ) : array();
5415 while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( $ak )))
5416 $propix++;
5417 $this->propix[$propName] = $propix;
5418 if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5419 return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value'];
5420 break;
5421 case 'COMPLETED':
5422 if( !empty( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value'];
5423 break;
5424 case 'CONTACT':
5425 $ak = ( is_array( $this->contact )) ? array_keys( $this->contact ) : array();
5426 while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( $ak )))
5427 $propix++;
5428 $this->propix[$propName] = $propix;
5429 if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5430 return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value'];
5431 break;
5432 case 'CREATED':
5433 if( !empty( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value'];
5434 break;
5435 case 'DESCRIPTION':
5436 $ak = ( is_array( $this->description )) ? array_keys( $this->description ) : array();
5437 while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( $ak )))
5438 $propix++;
5439 $this->propix[$propName] = $propix;
5440 if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5441 return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value'];
5442 break;
5443 case 'DTEND':
5444 if( !empty( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value'];
5445 break;
5446 case 'DTSTAMP':
5447 if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5448 return;
5449 if( !isset( $this->dtstamp['value'] ))
5450 $this->_makeDtstamp();
5451 return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value'];
5452 break;
5453 case 'DTSTART':
5454 if( !empty( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value'];
5455 break;
5456 case 'DUE':
5457 if( !empty( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value'];
5458 break;
5459 case 'DURATION':
5460 if( !isset( $this->duration['value'] )) return FALSE;
5461 $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value'];
5462 return ( $inclParam ) ? array( 'value' => $value, 'params' => $this->duration['params'] ) : $value;
5463 break;
5464 case 'EXDATE':
5465 $ak = ( is_array( $this->exdate )) ? array_keys( $this->exdate ) : array();
5466 while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( $ak )))
5467 $propix++;
5468 $this->propix[$propName] = $propix;
5469 if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5470 return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value'];
5471 break;
5472 case 'EXRULE':
5473 $ak = ( is_array( $this->exrule )) ? array_keys( $this->exrule ) : array();
5474 while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( $ak )))
5475 $propix++;
5476 $this->propix[$propName] = $propix;
5477 if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5478 return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value'];
5479 break;
5480 case 'FREEBUSY':
5481 $ak = ( is_array( $this->freebusy )) ? array_keys( $this->freebusy ) : array();
5482 while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( $ak )))
5483 $propix++;
5484 $this->propix[$propName] = $propix;
5485 if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5486 return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value'];
5487 break;
5488 case 'GEO':
5489 if( !empty( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value'];
5490 break;
5491 case 'LAST-MODIFIED':
5492 if( !empty( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value'];
5493 break;
5494 case 'LOCATION':
5495 if( !empty( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value'];
5496 break;
5497 case 'ORGANIZER':
5498 if( !empty( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value'];
5499 break;
5500 case 'PERCENT-COMPLETE':
5501 if( !empty( $this->percentcomplete['value'] ) || ( isset( $this->percentcomplete['value'] ) && ( '0' == $this->percentcomplete['value'] ))) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value'];
5502 break;
5503 case 'PRIORITY':
5504 if( !empty( $this->priority['value'] ) || ( isset( $this->priority['value'] ) && ('0' == $this->priority['value'] ))) return ( $inclParam ) ? $this->priority : $this->priority['value'];
5505 break;
5506 case 'RDATE':
5507 $ak = ( is_array( $this->rdate )) ? array_keys( $this->rdate ) : array();
5508 while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( $ak )))
5509 $propix++;
5510 $this->propix[$propName] = $propix;
5511 if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5512 return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value'];
5513 break;
5514 case 'RECURRENCE-ID':
5515 if( !empty( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value'];
5516 break;
5517 case 'RELATED-TO':
5518 $ak = ( is_array( $this->relatedto )) ? array_keys( $this->relatedto ) : array();
5519 while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( $ak )))
5520 $propix++;
5521 $this->propix[$propName] = $propix;
5522 if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5523 return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value'];
5524 break;
5525 case 'REPEAT':
5526 if( !empty( $this->repeat['value'] ) || ( isset( $this->repeat['value'] ) && ( '0' == $this->repeat['value'] ))) return ( $inclParam ) ? $this->repeat : $this->repeat['value'];
5527 break;
5528 case 'REQUEST-STATUS':
5529 $ak = ( is_array( $this->requeststatus )) ? array_keys( $this->requeststatus ) : array();
5530 while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( $ak )))
5531 $propix++;
5532 $this->propix[$propName] = $propix;
5533 if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5534 return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value'];
5535 break;
5536 case 'RESOURCES':
5537 $ak = ( is_array( $this->resources )) ? array_keys( $this->resources ) : array();
5538 while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( $ak )))
5539 $propix++;
5540 $this->propix[$propName] = $propix;
5541 if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5542 return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value'];
5543 break;
5544 case 'RRULE':
5545 $ak = ( is_array( $this->rrule )) ? array_keys( $this->rrule ) : array();
5546 while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( $ak )))
5547 $propix++;
5548 $this->propix[$propName] = $propix;
5549 if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5550 return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value'];
5551 break;
5552 case 'SEQUENCE':
5553 if( isset( $this->sequence['value'] ) && ( isset( $this->sequence['value'] ) && ( '0' <= $this->sequence['value'] ))) return ( $inclParam ) ? $this->sequence : $this->sequence['value'];
5554 break;
5555 case 'STATUS':
5556 if( !empty( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value'];
5557 break;
5558 case 'SUMMARY':
5559 if( !empty( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value'];
5560 break;
5561 case 'TRANSP':
5562 if( !empty( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value'];
5563 break;
5564 case 'TRIGGER':
5565 if( !empty( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value'];
5566 break;
5567 case 'TZID':
5568 if( !empty( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value'];
5569 break;
5570 case 'TZNAME':
5571 $ak = ( is_array( $this->tzname )) ? array_keys( $this->tzname ) : array();
5572 while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( $ak )))
5573 $propix++;
5574 $this->propix[$propName] = $propix;
5575 if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5576 return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value'];
5577 break;
5578 case 'TZOFFSETFROM':
5579 if( !empty( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value'];
5580 break;
5581 case 'TZOFFSETTO':
5582 if( !empty( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value'];
5583 break;
5584 case 'TZURL':
5585 if( !empty( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value'];
5586 break;
5587 case 'UID':
5588 if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5589 return FALSE;
5590 if( empty( $this->uid['value'] ))
5591 $this->_makeuid();
5592 return ( $inclParam ) ? $this->uid : $this->uid['value'];
5593 break;
5594 case 'URL':
5595 if( !empty( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value'];
5596 break;
5597 default:
5598 if( $propName != 'X-PROP' ) {
5599 if( !isset( $this->xprop[$propName] )) return FALSE;
5600 return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
5601 : array( $propName, $this->xprop[$propName]['value'] );
5602 }
5603 else {
5604 if( empty( $this->xprop )) return FALSE;
5605 $xpropno = 0;
5606 foreach( $this->xprop as $xpropkey => $xpropvalue ) {
5607 if( $propix == $xpropno )
5608 return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
5609 : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
5610 else
5611 $xpropno++;
5612 }
5613 return FALSE; // not found ??
5614 }
5615 }
5616 return FALSE;
5617 }
5618 /**
5619 * returns calendar property unique values for 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO' or 'RESOURCES' and for each, number of occurrence
5620 *
5621 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5622 * @since 2.13.4 - 2012-08-07
5623 * @param string $propName
5624 * @param array $output, incremented result array
5625 */
5626 function _getProperties( $propName, & $output ) {
5627 if( empty( $output ))
5628 $output = array();
5629 if( !in_array( strtoupper( $propName ), array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' )))
5630 return $output;
5631 while( FALSE !== ( $content = $this->getProperty( $propName ))) {
5632 if( empty( $content ))
5633 continue;
5634 if( is_array( $content )) {
5635 foreach( $content as $part ) {
5636 if( FALSE !== strpos( $part, ',' )) {
5637 $part = explode( ',', $part );
5638 foreach( $part as $thePart ) {
5639 $thePart = trim( $thePart );
5640 if( !empty( $thePart )) {
5641 if( !isset( $output[$thePart] ))
5642 $output[$thePart] = 1;
5643 else
5644 $output[$thePart] += 1;
5645 }
5646 }
5647 }
5648 else {
5649 $part = trim( $part );
5650 if( !isset( $output[$part] ))
5651 $output[$part] = 1;
5652 else
5653 $output[$part] += 1;
5654 }
5655 }
5656 } // end if( is_array( $content ))
5657 elseif( FALSE !== strpos( $content, ',' )) {
5658 $content = explode( ',', $content );
5659 foreach( $content as $thePart ) {
5660 $thePart = trim( $thePart );
5661 if( !empty( $thePart )) {
5662 if( !isset( $output[$thePart] ))
5663 $output[$thePart] = 1;
5664 else
5665 $output[$thePart] += 1;
5666 }
5667 }
5668 } // end elseif( FALSE !== strpos( $content, ',' ))
5669 else {
5670 $content = trim( $content );
5671 if( !empty( $content )) {
5672 if( !isset( $output[$content] ))
5673 $output[$content] = 1;
5674 else
5675 $output[$content] += 1;
5676 }
5677 }
5678 }
5679 ksort( $output );
5680 }
5681 /**
5682 * general component property setting
5683 *
5684 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5685 * @since 2.5.1 - 2008-11-05
5686 * @param mixed $args variable number of function arguments,
5687 * first argument is ALWAYS component name,
5688 * second ALWAYS component value!
5689 * @return void
5690 */
5691 function setProperty() {
5692 $numargs = func_num_args();
5693 if( 1 > $numargs ) return FALSE;
5694 $arglist = func_get_args();
5695 if( $this->_notExistProp( $arglist[0] )) return FALSE;
5696 if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] )))
5697 return FALSE;
5698 $arglist[0] = strtoupper( $arglist[0] );
5699 for( $argix=$numargs; $argix < 12; $argix++ ) {
5700 if( !isset( $arglist[$argix] ))
5701 $arglist[$argix] = null;
5702 }
5703 switch( $arglist[0] ) {
5704 case 'ACTION':
5705 return $this->setAction( $arglist[1], $arglist[2] );
5706 case 'ATTACH':
5707 return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] );
5708 case 'ATTENDEE':
5709 return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] );
5710 case 'CATEGORIES':
5711 return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] );
5712 case 'CLASS':
5713 return $this->setClass( $arglist[1], $arglist[2] );
5714 case 'COMMENT':
5715 return $this->setComment( $arglist[1], $arglist[2], $arglist[3] );
5716 case 'COMPLETED':
5717 return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5718 case 'CONTACT':
5719 return $this->setContact( $arglist[1], $arglist[2], $arglist[3] );
5720 case 'CREATED':
5721 return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5722 case 'DESCRIPTION':
5723 return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] );
5724 case 'DTEND':
5725 return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5726 case 'DTSTAMP':
5727 return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5728 case 'DTSTART':
5729 return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5730 case 'DUE':
5731 return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5732 case 'DURATION':
5733 return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] );
5734 case 'EXDATE':
5735 return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] );
5736 case 'EXRULE':
5737 return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] );
5738 case 'FREEBUSY':
5739 return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] );
5740 case 'GEO':
5741 return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] );
5742 case 'LAST-MODIFIED':
5743 return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5744 case 'LOCATION':
5745 return $this->setLocation( $arglist[1], $arglist[2] );
5746 case 'ORGANIZER':
5747 return $this->setOrganizer( $arglist[1], $arglist[2] );
5748 case 'PERCENT-COMPLETE':
5749 return $this->setPercentComplete( $arglist[1], $arglist[2] );
5750 case 'PRIORITY':
5751 return $this->setPriority( $arglist[1], $arglist[2] );
5752 case 'RDATE':
5753 return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] );
5754 case 'RECURRENCE-ID':
5755 return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5756 case 'RELATED-TO':
5757 return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] );
5758 case 'REPEAT':
5759 return $this->setRepeat( $arglist[1], $arglist[2] );
5760 case 'REQUEST-STATUS':
5761 return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] );
5762 case 'RESOURCES':
5763 return $this->setResources( $arglist[1], $arglist[2], $arglist[3] );
5764 case 'RRULE':
5765 return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] );
5766 case 'SEQUENCE':
5767 return $this->setSequence( $arglist[1], $arglist[2] );
5768 case 'STATUS':
5769 return $this->setStatus( $arglist[1], $arglist[2] );
5770 case 'SUMMARY':
5771 return $this->setSummary( $arglist[1], $arglist[2] );
5772 case 'TRANSP':
5773 return $this->setTransp( $arglist[1], $arglist[2] );
5774 case 'TRIGGER':
5775 return $this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] );
5776 case 'TZID':
5777 return $this->setTzid( $arglist[1], $arglist[2] );
5778 case 'TZNAME':
5779 return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] );
5780 case 'TZOFFSETFROM':
5781 return $this->setTzoffsetfrom( $arglist[1], $arglist[2] );
5782 case 'TZOFFSETTO':
5783 return $this->setTzoffsetto( $arglist[1], $arglist[2] );
5784 case 'TZURL':
5785 return $this->setTzurl( $arglist[1], $arglist[2] );
5786 case 'UID':
5787 return $this->setUid( $arglist[1], $arglist[2] );
5788 case 'URL':
5789 return $this->setUrl( $arglist[1], $arglist[2] );
5790 default:
5791 return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
5792 }
5793 return FALSE;
5794 }
5795 /*********************************************************************************/
5796 /**
5797 * parse component unparsed data into properties
5798 *
5799 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5800 * @since 2.16.2 - 2012-12-18
5801 * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of strings
5802 * @return bool FALSE if error occurs during parsing
5803 *
5804 */
5805 function parse( $unparsedtext=null ) {
5806 $nl = $this->getConfig( 'nl' );
5807 if( !empty( $unparsedtext )) {
5808 if( is_array( $unparsedtext ))
5809 $unparsedtext = implode( '\n'.$nl, $unparsedtext );
5810 $unparsedtext = explode( $nl, iCalUtilityFunctions::convEolChar( $unparsedtext, $nl ));
5811 }
5812 elseif( !isset( $this->unparsed ))
5813 $unparsedtext = array();
5814 else
5815 $unparsedtext = $this->unparsed;
5816 /* skip leading (empty/invalid) lines */
5817 foreach( $unparsedtext as $lix => $line ) {
5818 $tst = trim( $line );
5819 if(( '\n' == $tst ) || empty( $tst ))
5820 unset( $unparsedtext[$lix] );
5821 else
5822 break;
5823 }
5824 $this->unparsed = array();
5825 $comp = & $this;
5826 $config = $this->getConfig();
5827 $compsync = $subsync = 0;
5828 foreach ( $unparsedtext as $lix => $line ) {
5829 if( 'END:VALARM' == strtoupper( substr( $line, 0, 10 ))) {
5830 if( 1 != $subsync ) return FALSE;
5831 $this->components[] = $comp->copy();
5832 $subsync--;
5833 }
5834 elseif( 'END:DAYLIGHT' == strtoupper( substr( $line, 0, 12 ))) {
5835 if( 1 != $subsync ) return FALSE;
5836 $this->components[] = $comp->copy();
5837 $subsync--;
5838 }
5839 elseif( 'END:STANDARD' == strtoupper( substr( $line, 0, 12 ))) {
5840 if( 1 != $subsync ) return FALSE;
5841 array_unshift( $this->components, $comp->copy());
5842 $subsync--;
5843 }
5844 elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) { // end:<component>
5845 if( 1 != $compsync ) return FALSE;
5846 if( 0 < $subsync )
5847 $this->components[] = $comp->copy();
5848 $compsync--;
5849 break; /* skip trailing empty lines */
5850 }
5851 elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) {
5852 $comp = new valarm( $config);
5853 $subsync++;
5854 }
5855 elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) {
5856 $comp = new vtimezone( 'standard', $config );
5857 $subsync++;
5858 }
5859 elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) {
5860 $comp = new vtimezone( 'daylight', $config );
5861 $subsync++;
5862 }
5863 elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) // begin:<component>
5864 $compsync++;
5865 else
5866 $comp->unparsed[] = $line;
5867 }
5868 if( 0 < $subsync )
5869 $this->components[] = $comp->copy();
5870 unset( $config );
5871 /* concatenate property values spread over several lines */
5872 $lastix = -1;
5873 $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed'
5874 , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart'
5875 , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo'
5876 , 'last-modified', 'location', 'organizer', 'percent-complete'
5877 , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat'
5878 , 'request-status', 'resources', 'rrule', 'sequence', 'status'
5879 , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom'
5880 , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' );
5881 $proprows = array();
5882 for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines
5883 $line = rtrim( $this->unparsed[$i], $nl );
5884 while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} ))
5885 $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl );
5886 $proprows[] = $line;
5887 }
5888 /* parse each property 'line' */
5889 $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' );
5890 $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' );
5891 $paramProto4 = array( 'crid:', 'news:', 'pres:' );
5892 foreach( $proprows as $line ) {
5893 if( '\n' == substr( $line, -2 ))
5894 $line = substr( $line, 0, -2 );
5895 /* get propname */
5896 $propname = null;
5897 $cix = 0;
5898 while( isset( $line[$cix] )) {
5899 if( in_array( $line[$cix], array( ':', ';' )))
5900 break;
5901 else
5902 $propname .= $line[$cix];
5903 $cix++;
5904 }
5905 if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) {
5906 $propname2 = $propname;
5907 $propname = 'X-';
5908 }
5909 if( !in_array( strtolower( $propname ), $propnames )) // skip non standard property names
5910 continue;
5911 /* rest of the line is opt.params and value */
5912 $line = substr( $line, $cix );
5913 /* separate attributes from value */
5914 $attr = array();
5915 $attrix = -1;
5916 $clen = strlen( $line );
5917 $WithinQuotes = FALSE;
5918 $cix = 0;
5919 while( FALSE !== substr( $line, $cix, 1 )) {
5920 if( ( ':' == $line[$cix] ) &&
5921 ( substr( $line,$cix, 3 ) != '://' ) &&
5922 ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) &&
5923 ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) &&
5924 ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) &&
5925 ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) &&
5926 !$WithinQuotes ) {
5927 $attrEnd = TRUE;
5928 if(( $cix < ( $clen - 4 )) &&
5929 ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
5930 for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
5931 if( '://' == substr( $line, $c2ix - 2, 3 )) {
5932 $attrEnd = FALSE;
5933 break; // an URI with a portnr!!
5934 }
5935 }
5936 }
5937 if( $attrEnd) {
5938 $line = substr( $line, ( $cix + 1 ));
5939 break;
5940 }
5941 $cix++;
5942 }
5943 if( '"' == $line[$cix] )
5944 $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE;
5945 if( ';' == $line[$cix] )
5946 $attr[++$attrix] = null;
5947 else
5948 $attr[$attrix] .= $line[$cix];
5949 $cix++;
5950 }
5951 /* make attributes in array format */
5952 $propattr = array();
5953 foreach( $attr as $attribute ) {
5954 $attrsplit = explode( '=', $attribute, 2 );
5955 if( 1 < count( $attrsplit ))
5956 $propattr[$attrsplit[0]] = $attrsplit[1];
5957 else
5958 $propattr[] = $attribute;
5959 }
5960 /* call setProperty( $propname.. . */
5961 switch( strtoupper( $propname )) {
5962 case 'ATTENDEE':
5963 foreach( $propattr as $pix => $attr ) {
5964 if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' )))
5965 continue;
5966 $attr2 = explode( ',', $attr );
5967 if( 1 < count( $attr2 ))
5968 $propattr[$pix] = $attr2;
5969 }
5970 $this->setProperty( $propname, $line, $propattr );
5971 break;
5972 case 'X-':
5973 $propname = ( isset( $propname2 )) ? $propname2 : $propname;
5974 unset( $propname2 );
5975 case 'CATEGORIES':
5976 case 'RESOURCES':
5977 if( FALSE !== strpos( $line, ',' )) {
5978 $content = array( 0 => '' );
5979 $cix = $lix = 0;
5980 while( FALSE !== substr( $line, $lix, 1 )) {
5981 if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) {
5982 $cix++;
5983 $content[$cix] = '';
5984 }
5985 else
5986 $content[$cix] .= $line[$lix];
5987 $lix++;
5988 }
5989 if( 1 < count( $content )) {
5990 $content = array_values( $content );
5991 foreach( $content as $cix => $contentPart )
5992 $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart );
5993 $this->setProperty( $propname, $content, $propattr );
5994 break;
5995 }
5996 else
5997 $line = reset( $content );
5998 }
5999 case 'COMMENT':
6000 case 'CONTACT':
6001 case 'DESCRIPTION':
6002 case 'LOCATION':
6003 case 'SUMMARY':
6004 if( empty( $line ))
6005 $propattr = null;
6006 $this->setProperty( $propname, iCalUtilityFunctions::_strunrep( $line ), $propattr );
6007 break;
6008 case 'REQUEST-STATUS':
6009 $values = explode( ';', $line, 3 );
6010 $values[1] = ( !isset( $values[1] )) ? null : iCalUtilityFunctions::_strunrep( $values[1] );
6011 $values[2] = ( !isset( $values[2] )) ? null : iCalUtilityFunctions::_strunrep( $values[2] );
6012 $this->setProperty( $propname
6013 , $values[0] // statcode
6014 , $values[1] // statdesc
6015 , $values[2] // extdata
6016 , $propattr );
6017 break;
6018 case 'FREEBUSY':
6019 $fbtype = ( isset( $propattr['FBTYPE'] )) ? $propattr['FBTYPE'] : ''; // force setting default, if missing
6020 unset( $propattr['FBTYPE'] );
6021 $values = explode( ',', $line );
6022 foreach( $values as $vix => $value ) {
6023 $value2 = explode( '/', $value );
6024 if( 1 < count( $value2 ))
6025 $values[$vix] = $value2;
6026 }
6027 $this->setProperty( $propname, $fbtype, $values, $propattr );
6028 break;
6029 case 'GEO':
6030 $value = explode( ';', $line, 2 );
6031 if( 2 > count( $value ))
6032 $value[1] = null;
6033 $this->setProperty( $propname, $value[0], $value[1], $propattr );
6034 break;
6035 case 'EXDATE':
6036 $values = ( !empty( $line )) ? explode( ',', $line ) : null;
6037 $this->setProperty( $propname, $values, $propattr );
6038 break;
6039 case 'RDATE':
6040 if( empty( $line )) {
6041 $this->setProperty( $propname, $line, $propattr );
6042 break;
6043 }
6044 $values = explode( ',', $line );
6045 foreach( $values as $vix => $value ) {
6046 $value2 = explode( '/', $value );
6047 if( 1 < count( $value2 ))
6048 $values[$vix] = $value2;
6049 }
6050 $this->setProperty( $propname, $values, $propattr );
6051 break;
6052 case 'EXRULE':
6053 case 'RRULE':
6054 $values = explode( ';', $line );
6055 $recur = array();
6056 foreach( $values as $value2 ) {
6057 if( empty( $value2 ))
6058 continue; // ;-char in ending position ???
6059 $value3 = explode( '=', $value2, 2 );
6060 $rulelabel = strtoupper( $value3[0] );
6061 switch( $rulelabel ) {
6062 case 'BYDAY': {
6063 $value4 = explode( ',', $value3[1] );
6064 if( 1 < count( $value4 )) {
6065 foreach( $value4 as $v5ix => $value5 ) {
6066 $value6 = array();
6067 $dayno = $dayname = null;
6068 $value5 = trim( (string) $value5 );
6069 if(( ctype_alpha( substr( $value5, -1 ))) &&
6070 ( ctype_alpha( substr( $value5, -2, 1 )))) {
6071 $dayname = substr( $value5, -2, 2 );
6072 if( 2 < strlen( $value5 ))
6073 $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
6074 }
6075 if( $dayno )
6076 $value6[] = $dayno;
6077 if( $dayname )
6078 $value6['DAY'] = $dayname;
6079 $value4[$v5ix] = $value6;
6080 }
6081 }
6082 else {
6083 $value4 = array();
6084 $dayno = $dayname = null;
6085 $value5 = trim( (string) $value3[1] );
6086 if(( ctype_alpha( substr( $value5, -1 ))) &&
6087 ( ctype_alpha( substr( $value5, -2, 1 )))) {
6088 $dayname = substr( $value5, -2, 2 );
6089 if( 2 < strlen( $value5 ))
6090 $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
6091 }
6092 if( $dayno )
6093 $value4[] = $dayno;
6094 if( $dayname )
6095 $value4['DAY'] = $dayname;
6096 }
6097 $recur[$rulelabel] = $value4;
6098 break;
6099 }
6100 default: {
6101 $value4 = explode( ',', $value3[1] );
6102 if( 1 < count( $value4 ))
6103 $value3[1] = $value4;
6104 $recur[$rulelabel] = $value3[1];
6105 break;
6106 }
6107 } // end - switch $rulelabel
6108 } // end - foreach( $values.. .
6109 $this->setProperty( $propname, $recur, $propattr );
6110 break;
6111 case 'ACTION':
6112 case 'CLASSIFICATION':
6113 case 'STATUS':
6114 case 'TRANSP':
6115 case 'UID':
6116 case 'TZID':
6117 case 'RELATED-TO':
6118 case 'TZNAME':
6119 $line = iCalUtilityFunctions::_strunrep( $line );
6120 default:
6121 $this->setProperty( $propname, $line, $propattr );
6122 break;
6123 } // end switch( $propname.. .
6124 } // end - foreach( $proprows.. .
6125 unset( $unparsedtext, $this->unparsed, $proprows );
6126 if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) {
6127 $ckeys = array_keys( $this->components );
6128 foreach( $ckeys as $ckey ) {
6129 if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
6130 $this->components[$ckey]->parse();
6131 }
6132 }
6133 }
6134 return TRUE;
6135 }
6136 /*********************************************************************************/
6137 /*********************************************************************************/
6138 /**
6139 * return a copy of this component
6140 *
6141 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6142 * @since 2.15.4 - 2012-10-18
6143 * @return object
6144 */
6145 function copy() {
6146 return unserialize( serialize( $this ));
6147 }
6148 /*********************************************************************************/
6149 /*********************************************************************************/
6150 /**
6151 * delete calendar subcomponent from component container
6152 *
6153 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6154 * @since 2.8.8 - 2011-03-15
6155 * @param mixed $arg1 ordno / component type / component uid
6156 * @param mixed $arg2 optional, ordno if arg1 = component type
6157 * @return void
6158 */
6159 function deleteComponent( $arg1, $arg2=FALSE ) {
6160 if( !isset( $this->components )) return FALSE;
6161 $argType = $index = null;
6162 if ( ctype_digit( (string) $arg1 )) {
6163 $argType = 'INDEX';
6164 $index = (int) $arg1 - 1;
6165 }
6166 elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
6167 $argType = strtolower( $arg1 );
6168 $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
6169 }
6170 $cix2dC = 0;
6171 foreach ( $this->components as $cix => $component) {
6172 if( empty( $component )) continue;
6173 if(( 'INDEX' == $argType ) && ( $index == $cix )) {
6174 unset( $this->components[$cix] );
6175 return TRUE;
6176 }
6177 elseif( $argType == $component->objName ) {
6178 if( $index == $cix2dC ) {
6179 unset( $this->components[$cix] );
6180 return TRUE;
6181 }
6182 $cix2dC++;
6183 }
6184 elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
6185 unset( $this->components[$cix] );
6186 return TRUE;
6187 }
6188 }
6189 return FALSE;
6190 }
6191 /**
6192 * get calendar component subcomponent from component container
6193 *
6194 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6195 * @since 2.8.8 - 2011-03-15
6196 * @param mixed $arg1 optional, ordno/component type/ component uid
6197 * @param mixed $arg2 optional, ordno if arg1 = component type
6198 * @return object
6199 */
6200 function getComponent ( $arg1=FALSE, $arg2=FALSE ) {
6201 if( !isset( $this->components )) return FALSE;
6202 $index = $argType = null;
6203 if ( !$arg1 ) {
6204 $argType = 'INDEX';
6205 $index = $this->compix['INDEX'] =
6206 ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
6207 }
6208 elseif ( ctype_digit( (string) $arg1 )) {
6209 $argType = 'INDEX';
6210 $index = (int) $arg1;
6211 unset( $this->compix );
6212 }
6213 elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
6214 unset( $this->compix['INDEX'] );
6215 $argType = strtolower( $arg1 );
6216 if( !$arg2 )
6217 $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
6218 else
6219 $index = (int) $arg2;
6220 }
6221 $index -= 1;
6222 $ckeys = array_keys( $this->components );
6223 if( !empty( $index) && ( $index > end( $ckeys )))
6224 return FALSE;
6225 $cix2gC = 0;
6226 foreach( $this->components as $cix => $component ) {
6227 if( empty( $component )) continue;
6228 if(( 'INDEX' == $argType ) && ( $index == $cix ))
6229 return $component->copy();
6230 elseif( $argType == $component->objName ) {
6231 if( $index == $cix2gC )
6232 return $component->copy();
6233 $cix2gC++;
6234 }
6235 elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' )))
6236 return $component->copy();
6237 }
6238 /* not found.. . */
6239 unset( $this->compix );
6240 return false;
6241 }
6242 /**
6243 * add calendar component as subcomponent to container for subcomponents
6244 *
6245 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6246 * @since 1.x.x - 2007-04-24
6247 * @param object $component calendar component
6248 * @return void
6249 */
6250 function addSubComponent ( $component ) {
6251 $this->setComponent( $component );
6252 }
6253 /**
6254 * create new calendar component subcomponent, already included within component
6255 *
6256 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6257 * @since 2.6.33 - 2011-01-03
6258 * @param string $compType subcomponent type
6259 * @return object (reference)
6260 */
6261 function & newComponent( $compType ) {
6262 $config = $this->getConfig();
6263 $keys = array_keys( $this->components );
6264 $ix = end( $keys) + 1;
6265 switch( strtoupper( $compType )) {
6266 case 'ALARM':
6267 case 'VALARM':
6268 $this->components[$ix] = new valarm( $config );
6269 break;
6270 case 'STANDARD':
6271 array_unshift( $this->components, new vtimezone( 'STANDARD', $config ));
6272 $ix = 0;
6273 break;
6274 case 'DAYLIGHT':
6275 $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config );
6276 break;
6277 default:
6278 return FALSE;
6279 }
6280 return $this->components[$ix];
6281 }
6282 /**
6283 * add calendar component as subcomponent to container for subcomponents
6284 *
6285 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6286 * @since 2.8.8 - 2011-03-15
6287 * @param object $component calendar component
6288 * @param mixed $arg1 optional, ordno/component type/ component uid
6289 * @param mixed $arg2 optional, ordno if arg1 = component type
6290 * @return bool
6291 */
6292 function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) {
6293 if( !isset( $this->components )) return FALSE;
6294 $component->setConfig( $this->getConfig(), FALSE, TRUE );
6295 if( !in_array( $component->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
6296 /* make sure dtstamp and uid is set */
6297 $dummy = $component->getProperty( 'dtstamp' );
6298 $dummy = $component->getProperty( 'uid' );
6299 }
6300 if( !$arg1 ) { // plain insert, last in chain
6301 $this->components[] = $component->copy();
6302 return TRUE;
6303 }
6304 $argType = $index = null;
6305 if ( ctype_digit( (string) $arg1 )) { // index insert/replace
6306 $argType = 'INDEX';
6307 $index = (int) $arg1 - 1;
6308 }
6309 elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
6310 $argType = strtolower( $arg1 );
6311 $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
6312 }
6313 // else if arg1 is set, arg1 must be an UID
6314 $cix2sC = 0;
6315 foreach ( $this->components as $cix => $component2 ) {
6316 if( empty( $component2 )) continue;
6317 if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
6318 $this->components[$cix] = $component->copy();
6319 return TRUE;
6320 }
6321 elseif( $argType == $component2->objName ) { // component Type index insert/replace
6322 if( $index == $cix2sC ) {
6323 $this->components[$cix] = $component->copy();
6324 return TRUE;
6325 }
6326 $cix2sC++;
6327 }
6328 elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
6329 $this->components[$cix] = $component->copy();
6330 return TRUE;
6331 }
6332 }
6333 /* arg1=index and not found.. . insert at index .. .*/
6334 if( 'INDEX' == $argType ) {
6335 $this->components[$index] = $component->copy();
6336 ksort( $this->components, SORT_NUMERIC );
6337 }
6338 else /* not found.. . insert last in chain anyway .. .*/
6339 $this->components[] = $component->copy();
6340 return TRUE;
6341 }
6342 /**
6343 * creates formatted output for subcomponents
6344 *
6345 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6346 * @since 2.11.20 - 2012-02-06
6347 * @param array $xcaldecl
6348 * @return string
6349 */
6350 function createSubComponent() {
6351 $output = null;
6352 if( 'vtimezone' == $this->objName ) { // sort subComponents, first standard, then daylight, in dtstart order
6353 $stdarr = $dlarr = array();
6354 foreach( $this->components as $component ) {
6355 if( empty( $component ))
6356 continue;
6357 $dt = $component->getProperty( 'dtstart' );
6358 $key = sprintf( '%04d%02d%02d%02d%02d%02d000', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] );
6359 if( 'standard' == $component->objName ) {
6360 while( isset( $stdarr[$key] ))
6361 $key += 1;
6362 $stdarr[$key] = $component->copy();
6363 }
6364 elseif( 'daylight' == $component->objName ) {
6365 while( isset( $dlarr[$key] ))
6366 $key += 1;
6367 $dlarr[$key] = $component->copy();
6368 }
6369 } // end foreach( $this->components as $component )
6370 $this->components = array();
6371 ksort( $stdarr, SORT_NUMERIC );
6372 foreach( $stdarr as $std )
6373 $this->components[] = $std->copy();
6374 unset( $stdarr );
6375 ksort( $dlarr, SORT_NUMERIC );
6376 foreach( $dlarr as $dl )
6377 $this->components[] = $dl->copy();
6378 unset( $dlarr );
6379 } // end if( 'vtimezone' == $this->objName )
6380 foreach( $this->components as $component ) {
6381 $component->setConfig( $this->getConfig(), FALSE, TRUE );
6382 $output .= $component->createComponent( $this->xcaldecl );
6383 }
6384 return $output;
6385 }
6386 }
6387 /*********************************************************************************/
6388 /*********************************************************************************/
6389 /**
6390 * class for calendar component VEVENT
6391 *
6392 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6393 * @since 2.5.1 - 2008-10-12
6394 */
6395 class vevent extends calendarComponent {
6396 var $attach;
6397 var $attendee;
6398 var $categories;
6399 var $comment;
6400 var $contact;
6401 var $class;
6402 var $created;
6403 var $description;
6404 var $dtend;
6405 var $dtstart;
6406 var $duration;
6407 var $exdate;
6408 var $exrule;
6409 var $geo;
6410 var $lastmodified;
6411 var $location;
6412 var $organizer;
6413 var $priority;
6414 var $rdate;
6415 var $recurrenceid;
6416 var $relatedto;
6417 var $requeststatus;
6418 var $resources;
6419 var $rrule;
6420 var $sequence;
6421 var $status;
6422 var $summary;
6423 var $transp;
6424 var $url;
6425 var $xprop;
6426 // component subcomponents container
6427 var $components;
6428 /**
6429 * constructor for calendar component VEVENT object
6430 *
6431 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6432 * @since 2.8.2 - 2011-05-01
6433 * @param array $config
6434 * @return void
6435 */
6436 function vevent( $config = array()) {
6437 $this->calendarComponent();
6438
6439 $this->attach = '';
6440 $this->attendee = '';
6441 $this->categories = '';
6442 $this->class = '';
6443 $this->comment = '';
6444 $this->contact = '';
6445 $this->created = '';
6446 $this->description = '';
6447 $this->dtstart = '';
6448 $this->dtend = '';
6449 $this->duration = '';
6450 $this->exdate = '';
6451 $this->exrule = '';
6452 $this->geo = '';
6453 $this->lastmodified = '';
6454 $this->location = '';
6455 $this->organizer = '';
6456 $this->priority = '';
6457 $this->rdate = '';
6458 $this->recurrenceid = '';
6459 $this->relatedto = '';
6460 $this->requeststatus = '';
6461 $this->resources = '';
6462 $this->rrule = '';
6463 $this->sequence = '';
6464 $this->status = '';
6465 $this->summary = '';
6466 $this->transp = '';
6467 $this->url = '';
6468 $this->xprop = '';
6469
6470 $this->components = array();
6471
6472 if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6473 $config['language'] = ICAL_LANG;
6474 if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6475 if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6476 if( !isset( $config['format'] )) $config['format'] = 'iCal';
6477 if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6478 $this->setConfig( $config );
6479
6480 }
6481 /**
6482 * create formatted output for calendar component VEVENT object instance
6483 *
6484 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6485 * @since 2.10.16 - 2011-10-28
6486 * @param array $xcaldecl
6487 * @return string
6488 */
6489 function createComponent( &$xcaldecl ) {
6490 $objectname = $this->_createFormat();
6491 $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6492 $component .= $this->createUid();
6493 $component .= $this->createDtstamp();
6494 $component .= $this->createAttach();
6495 $component .= $this->createAttendee();
6496 $component .= $this->createCategories();
6497 $component .= $this->createComment();
6498 $component .= $this->createContact();
6499 $component .= $this->createClass();
6500 $component .= $this->createCreated();
6501 $component .= $this->createDescription();
6502 $component .= $this->createDtstart();
6503 $component .= $this->createDtend();
6504 $component .= $this->createDuration();
6505 $component .= $this->createExdate();
6506 $component .= $this->createExrule();
6507 $component .= $this->createGeo();
6508 $component .= $this->createLastModified();
6509 $component .= $this->createLocation();
6510 $component .= $this->createOrganizer();
6511 $component .= $this->createPriority();
6512 $component .= $this->createRdate();
6513 $component .= $this->createRrule();
6514 $component .= $this->createRelatedTo();
6515 $component .= $this->createRequestStatus();
6516 $component .= $this->createRecurrenceid();
6517 $component .= $this->createResources();
6518 $component .= $this->createSequence();
6519 $component .= $this->createStatus();
6520 $component .= $this->createSummary();
6521 $component .= $this->createTransp();
6522 $component .= $this->createUrl();
6523 $component .= $this->createXprop();
6524 $component .= $this->createSubComponent();
6525 $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6526 if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6527 foreach( $this->xcaldecl as $localxcaldecl )
6528 $xcaldecl[] = $localxcaldecl;
6529 }
6530 return $component;
6531 }
6532 }
6533 /*********************************************************************************/
6534 /*********************************************************************************/
6535 /**
6536 * class for calendar component VTODO
6537 *
6538 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6539 * @since 2.5.1 - 2008-10-12
6540 */
6541 class vtodo extends calendarComponent {
6542 var $attach;
6543 var $attendee;
6544 var $categories;
6545 var $comment;
6546 var $completed;
6547 var $contact;
6548 var $class;
6549 var $created;
6550 var $description;
6551 var $dtstart;
6552 var $due;
6553 var $duration;
6554 var $exdate;
6555 var $exrule;
6556 var $geo;
6557 var $lastmodified;
6558 var $location;
6559 var $organizer;
6560 var $percentcomplete;
6561 var $priority;
6562 var $rdate;
6563 var $recurrenceid;
6564 var $relatedto;
6565 var $requeststatus;
6566 var $resources;
6567 var $rrule;
6568 var $sequence;
6569 var $status;
6570 var $summary;
6571 var $url;
6572 var $xprop;
6573 // component subcomponents container
6574 var $components;
6575 /**
6576 * constructor for calendar component VTODO object
6577 *
6578 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6579 * @since 2.8.2 - 2011-05-01
6580 * @param array $config
6581 * @return void
6582 */
6583 function vtodo( $config = array()) {
6584 $this->calendarComponent();
6585
6586 $this->attach = '';
6587 $this->attendee = '';
6588 $this->categories = '';
6589 $this->class = '';
6590 $this->comment = '';
6591 $this->completed = '';
6592 $this->contact = '';
6593 $this->created = '';
6594 $this->description = '';
6595 $this->dtstart = '';
6596 $this->due = '';
6597 $this->duration = '';
6598 $this->exdate = '';
6599 $this->exrule = '';
6600 $this->geo = '';
6601 $this->lastmodified = '';
6602 $this->location = '';
6603 $this->organizer = '';
6604 $this->percentcomplete = '';
6605 $this->priority = '';
6606 $this->rdate = '';
6607 $this->recurrenceid = '';
6608 $this->relatedto = '';
6609 $this->requeststatus = '';
6610 $this->resources = '';
6611 $this->rrule = '';
6612 $this->sequence = '';
6613 $this->status = '';
6614 $this->summary = '';
6615 $this->url = '';
6616 $this->xprop = '';
6617
6618 $this->components = array();
6619
6620 if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6621 $config['language'] = ICAL_LANG;
6622 if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6623 if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6624 if( !isset( $config['format'] )) $config['format'] = 'iCal';
6625 if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6626 $this->setConfig( $config );
6627
6628 }
6629 /**
6630 * create formatted output for calendar component VTODO object instance
6631 *
6632 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6633 * @since 2.5.1 - 2008-11-07
6634 * @param array $xcaldecl
6635 * @return string
6636 */
6637 function createComponent( &$xcaldecl ) {
6638 $objectname = $this->_createFormat();
6639 $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6640 $component .= $this->createUid();
6641 $component .= $this->createDtstamp();
6642 $component .= $this->createAttach();
6643 $component .= $this->createAttendee();
6644 $component .= $this->createCategories();
6645 $component .= $this->createClass();
6646 $component .= $this->createComment();
6647 $component .= $this->createCompleted();
6648 $component .= $this->createContact();
6649 $component .= $this->createCreated();
6650 $component .= $this->createDescription();
6651 $component .= $this->createDtstart();
6652 $component .= $this->createDue();
6653 $component .= $this->createDuration();
6654 $component .= $this->createExdate();
6655 $component .= $this->createExrule();
6656 $component .= $this->createGeo();
6657 $component .= $this->createLastModified();
6658 $component .= $this->createLocation();
6659 $component .= $this->createOrganizer();
6660 $component .= $this->createPercentComplete();
6661 $component .= $this->createPriority();
6662 $component .= $this->createRdate();
6663 $component .= $this->createRelatedTo();
6664 $component .= $this->createRequestStatus();
6665 $component .= $this->createRecurrenceid();
6666 $component .= $this->createResources();
6667 $component .= $this->createRrule();
6668 $component .= $this->createSequence();
6669 $component .= $this->createStatus();
6670 $component .= $this->createSummary();
6671 $component .= $this->createUrl();
6672 $component .= $this->createXprop();
6673 $component .= $this->createSubComponent();
6674 $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6675 if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6676 foreach( $this->xcaldecl as $localxcaldecl )
6677 $xcaldecl[] = $localxcaldecl;
6678 }
6679 return $component;
6680 }
6681 }
6682 /*********************************************************************************/
6683 /*********************************************************************************/
6684 /**
6685 * class for calendar component VJOURNAL
6686 *
6687 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6688 * @since 2.5.1 - 2008-10-12
6689 */
6690 class vjournal extends calendarComponent {
6691 var $attach;
6692 var $attendee;
6693 var $categories;
6694 var $comment;
6695 var $contact;
6696 var $class;
6697 var $created;
6698 var $description;
6699 var $dtstart;
6700 var $exdate;
6701 var $exrule;
6702 var $lastmodified;
6703 var $organizer;
6704 var $rdate;
6705 var $recurrenceid;
6706 var $relatedto;
6707 var $requeststatus;
6708 var $rrule;
6709 var $sequence;
6710 var $status;
6711 var $summary;
6712 var $url;
6713 var $xprop;
6714 /**
6715 * constructor for calendar component VJOURNAL object
6716 *
6717 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6718 * @since 2.8.2 - 2011-05-01
6719 * @param array $config
6720 * @return void
6721 */
6722 function vjournal( $config = array()) {
6723 $this->calendarComponent();
6724
6725 $this->attach = '';
6726 $this->attendee = '';
6727 $this->categories = '';
6728 $this->class = '';
6729 $this->comment = '';
6730 $this->contact = '';
6731 $this->created = '';
6732 $this->description = '';
6733 $this->dtstart = '';
6734 $this->exdate = '';
6735 $this->exrule = '';
6736 $this->lastmodified = '';
6737 $this->organizer = '';
6738 $this->rdate = '';
6739 $this->recurrenceid = '';
6740 $this->relatedto = '';
6741 $this->requeststatus = '';
6742 $this->rrule = '';
6743 $this->sequence = '';
6744 $this->status = '';
6745 $this->summary = '';
6746 $this->url = '';
6747 $this->xprop = '';
6748
6749 if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6750 $config['language'] = ICAL_LANG;
6751 if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6752 if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6753 if( !isset( $config['format'] )) $config['format'] = 'iCal';
6754 if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6755 $this->setConfig( $config );
6756
6757 }
6758 /**
6759 * create formatted output for calendar component VJOURNAL object instance
6760 *
6761 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6762 * @since 2.5.1 - 2008-10-12
6763 * @param array $xcaldecl
6764 * @return string
6765 */
6766 function createComponent( &$xcaldecl ) {
6767 $objectname = $this->_createFormat();
6768 $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6769 $component .= $this->createUid();
6770 $component .= $this->createDtstamp();
6771 $component .= $this->createAttach();
6772 $component .= $this->createAttendee();
6773 $component .= $this->createCategories();
6774 $component .= $this->createClass();
6775 $component .= $this->createComment();
6776 $component .= $this->createContact();
6777 $component .= $this->createCreated();
6778 $component .= $this->createDescription();
6779 $component .= $this->createDtstart();
6780 $component .= $this->createExdate();
6781 $component .= $this->createExrule();
6782 $component .= $this->createLastModified();
6783 $component .= $this->createOrganizer();
6784 $component .= $this->createRdate();
6785 $component .= $this->createRequestStatus();
6786 $component .= $this->createRecurrenceid();
6787 $component .= $this->createRelatedTo();
6788 $component .= $this->createRrule();
6789 $component .= $this->createSequence();
6790 $component .= $this->createStatus();
6791 $component .= $this->createSummary();
6792 $component .= $this->createUrl();
6793 $component .= $this->createXprop();
6794 $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6795 if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6796 foreach( $this->xcaldecl as $localxcaldecl )
6797 $xcaldecl[] = $localxcaldecl;
6798 }
6799 return $component;
6800 }
6801 }
6802 /*********************************************************************************/
6803 /*********************************************************************************/
6804 /**
6805 * class for calendar component VFREEBUSY
6806 *
6807 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6808 * @since 2.5.1 - 2008-10-12
6809 */
6810 class vfreebusy extends calendarComponent {
6811 var $attendee;
6812 var $comment;
6813 var $contact;
6814 var $dtend;
6815 var $dtstart;
6816 var $duration;
6817 var $freebusy;
6818 var $organizer;
6819 var $requeststatus;
6820 var $url;
6821 var $xprop;
6822 // component subcomponents container
6823 var $components;
6824 /**
6825 * constructor for calendar component VFREEBUSY object
6826 *
6827 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6828 * @since 2.8.2 - 2011-05-01
6829 * @param array $config
6830 * @return void
6831 */
6832 function vfreebusy( $config = array()) {
6833 $this->calendarComponent();
6834
6835 $this->attendee = '';
6836 $this->comment = '';
6837 $this->contact = '';
6838 $this->dtend = '';
6839 $this->dtstart = '';
6840 $this->duration = '';
6841 $this->freebusy = '';
6842 $this->organizer = '';
6843 $this->requeststatus = '';
6844 $this->url = '';
6845 $this->xprop = '';
6846
6847 if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6848 $config['language'] = ICAL_LANG;
6849 if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6850 if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6851 if( !isset( $config['format'] )) $config['format'] = 'iCal';
6852 if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6853 $this->setConfig( $config );
6854
6855 }
6856 /**
6857 * create formatted output for calendar component VFREEBUSY object instance
6858 *
6859 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6860 * @since 2.3.1 - 2007-11-19
6861 * @param array $xcaldecl
6862 * @return string
6863 */
6864 function createComponent( &$xcaldecl ) {
6865 $objectname = $this->_createFormat();
6866 $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6867 $component .= $this->createUid();
6868 $component .= $this->createDtstamp();
6869 $component .= $this->createAttendee();
6870 $component .= $this->createComment();
6871 $component .= $this->createContact();
6872 $component .= $this->createDtstart();
6873 $component .= $this->createDtend();
6874 $component .= $this->createDuration();
6875 $component .= $this->createFreebusy();
6876 $component .= $this->createOrganizer();
6877 $component .= $this->createRequestStatus();
6878 $component .= $this->createUrl();
6879 $component .= $this->createXprop();
6880 $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6881 if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6882 foreach( $this->xcaldecl as $localxcaldecl )
6883 $xcaldecl[] = $localxcaldecl;
6884 }
6885 return $component;
6886 }
6887 }
6888 /*********************************************************************************/
6889 /*********************************************************************************/
6890 /**
6891 * class for calendar component VALARM
6892 *
6893 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6894 * @since 2.5.1 - 2008-10-12
6895 */
6896 class valarm extends calendarComponent {
6897 var $action;
6898 var $attach;
6899 var $attendee;
6900 var $description;
6901 var $duration;
6902 var $repeat;
6903 var $summary;
6904 var $trigger;
6905 var $xprop;
6906 /**
6907 * constructor for calendar component VALARM object
6908 *
6909 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6910 * @since 2.8.2 - 2011-05-01
6911 * @param array $config
6912 * @return void
6913 */
6914 function valarm( $config = array()) {
6915 $this->calendarComponent();
6916
6917 $this->action = '';
6918 $this->attach = '';
6919 $this->attendee = '';
6920 $this->description = '';
6921 $this->duration = '';
6922 $this->repeat = '';
6923 $this->summary = '';
6924 $this->trigger = '';
6925 $this->xprop = '';
6926
6927 if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6928 $config['language'] = ICAL_LANG;
6929 if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6930 if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6931 if( !isset( $config['format'] )) $config['format'] = 'iCal';
6932 if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6933 $this->setConfig( $config );
6934
6935 }
6936 /**
6937 * create formatted output for calendar component VALARM object instance
6938 *
6939 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6940 * @since 2.5.1 - 2008-10-22
6941 * @param array $xcaldecl
6942 * @return string
6943 */
6944 function createComponent( &$xcaldecl ) {
6945 $objectname = $this->_createFormat();
6946 $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6947 $component .= $this->createAction();
6948 $component .= $this->createAttach();
6949 $component .= $this->createAttendee();
6950 $component .= $this->createDescription();
6951 $component .= $this->createDuration();
6952 $component .= $this->createRepeat();
6953 $component .= $this->createSummary();
6954 $component .= $this->createTrigger();
6955 $component .= $this->createXprop();
6956 $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6957 if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6958 foreach( $this->xcaldecl as $localxcaldecl )
6959 $xcaldecl[] = $localxcaldecl;
6960 }
6961 return $component;
6962 }
6963 }
6964 /**********************************************************************************
6965 /*********************************************************************************/
6966 /**
6967 * class for calendar component VTIMEZONE
6968 *
6969 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6970 * @since 2.5.1 - 2008-10-12
6971 */
6972 class vtimezone extends calendarComponent {
6973 var $timezonetype;
6974
6975 var $comment;
6976 var $dtstart;
6977 var $lastmodified;
6978 var $rdate;
6979 var $rrule;
6980 var $tzid;
6981 var $tzname;
6982 var $tzoffsetfrom;
6983 var $tzoffsetto;
6984 var $tzurl;
6985 var $xprop;
6986 // component subcomponents container
6987 var $components;
6988 /**
6989 * constructor for calendar component VTIMEZONE object
6990 *
6991 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6992 * @since 2.8.2 - 2011-05-01
6993 * @param mixed $timezonetype optional, default FALSE ( STANDARD / DAYLIGHT )
6994 * @param array $config
6995 * @return void
6996 */
6997 function vtimezone( $timezonetype=FALSE, $config = array()) {
6998 if( is_array( $timezonetype )) {
6999 $config = $timezonetype;
7000 $timezonetype = FALSE;
7001 }
7002 if( !$timezonetype )
7003 $this->timezonetype = 'VTIMEZONE';
7004 else
7005 $this->timezonetype = strtoupper( $timezonetype );
7006 $this->calendarComponent();
7007
7008 $this->comment = '';
7009 $this->dtstart = '';
7010 $this->lastmodified = '';
7011 $this->rdate = '';
7012 $this->rrule = '';
7013 $this->tzid = '';
7014 $this->tzname = '';
7015 $this->tzoffsetfrom = '';
7016 $this->tzoffsetto = '';
7017 $this->tzurl = '';
7018 $this->xprop = '';
7019
7020 $this->components = array();
7021
7022 if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
7023 $config['language'] = ICAL_LANG;
7024 if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
7025 if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
7026 if( !isset( $config['format'] )) $config['format'] = 'iCal';
7027 if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
7028 $this->setConfig( $config );
7029
7030 }
7031 /**
7032 * create formatted output for calendar component VTIMEZONE object instance
7033 *
7034 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7035 * @since 2.5.1 - 2008-10-25
7036 * @param array $xcaldecl
7037 * @return string
7038 */
7039 function createComponent( &$xcaldecl ) {
7040 $objectname = $this->_createFormat();
7041 $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
7042 $component .= $this->createTzid();
7043 $component .= $this->createLastModified();
7044 $component .= $this->createTzurl();
7045 $component .= $this->createDtstart();
7046 $component .= $this->createTzoffsetfrom();
7047 $component .= $this->createTzoffsetto();
7048 $component .= $this->createComment();
7049 $component .= $this->createRdate();
7050 $component .= $this->createRrule();
7051 $component .= $this->createTzname();
7052 $component .= $this->createXprop();
7053 $component .= $this->createSubComponent();
7054 $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
7055 if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
7056 foreach( $this->xcaldecl as $localxcaldecl )
7057 $xcaldecl[] = $localxcaldecl;
7058 }
7059 return $component;
7060 }
7061 }
7062 /*********************************************************************************/
7063 /*********************************************************************************/
7064 /**
7065 * moving all utility (static) functions to a utility class
7066 * 20111223 - move iCalUtilityFunctions class to the end of the iCalcreator class file
7067 *
7068 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7069 * @since 2.10.1 - 2011-07-16
7070 *
7071 */
7072 class iCalUtilityFunctions {
7073 // Store the single instance of iCalUtilityFunctions
7074 private static $m_pInstance;
7075
7076 // Private constructor to limit object instantiation to within the class
7077 private function __construct() {
7078 $m_pInstance = FALSE;
7079 }
7080
7081 // Getter method for creating/returning the single instance of this class
7082 public static function getInstance() {
7083 if (!self::$m_pInstance)
7084 self::$m_pInstance = new iCalUtilityFunctions();
7085
7086 return self::$m_pInstance;
7087 }
7088 /**
7089 * ensures internal date-time/date format (keyed array) for an input date-time/date array (keyed or unkeyed)
7090 *
7091 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7092 * @since 2.14.1 - 2012-09-27
7093 * @param array $datetime
7094 * @param int $parno optional, default FALSE
7095 * @return array
7096 */
7097 public static function _date_time_array( $datetime, $parno=FALSE ) {
7098 return iCalUtilityFunctions::_chkDateArr( $datetime, $parno );
7099 }
7100 public static function _chkDateArr( $datetime, $parno=FALSE ) {
7101 $output = array();
7102 foreach( $datetime as $dateKey => $datePart ) {
7103 switch ( $dateKey ) {
7104 case '0': case 'year': $output['year'] = $datePart; break;
7105 case '1': case 'month': $output['month'] = $datePart; break;
7106 case '2': case 'day': $output['day'] = $datePart; break;
7107 }
7108 if( 3 != $parno ) {
7109 switch ( $dateKey ) {
7110 case '0':
7111 case '1':
7112 case '2': break;
7113 case '3': case 'hour': $output['hour'] = $datePart; break;
7114 case '4': case 'min' : $output['min'] = $datePart; break;
7115 case '5': case 'sec' : $output['sec'] = $datePart; break;
7116 case '6': case 'tz' : $output['tz'] = $datePart; break;
7117 }
7118 }
7119 }
7120 if( 3 != $parno ) {
7121 if( !isset( $output['hour'] )) $output['hour'] = 0;
7122 if( !isset( $output['min'] )) $output['min'] = 0;
7123 if( !isset( $output['sec'] )) $output['sec'] = 0;
7124 if( isset( $output['tz'] ) &&
7125 (( '+0000' == $output['tz'] ) || ( '-0000' == $output['tz'] ) || ( '+000000' == $output['tz'] ) || ( '-000000' == $output['tz'] )))
7126 $output['tz'] = 'Z';
7127 }
7128 return $output;
7129 }
7130 /**
7131 * check date(-time) and params arrays for an opt. timezone and if it is a DATE-TIME or DATE (updates $parno and params)
7132 *
7133 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7134 * @since 2.10.30 - 2012-01-16
7135 * @param array $date, date to check
7136 * @param int $parno, no of date parts (i.e. year, month.. .)
7137 * @param array $params, property parameters
7138 * @return void
7139 */
7140 public static function _chkdatecfg( $theDate, & $parno, & $params ) {
7141 if( isset( $params['TZID'] ))
7142 $parno = 6;
7143 elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))
7144 $parno = 3;
7145 else {
7146 if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] ))
7147 $parno = 7;
7148 if( is_array( $theDate )) {
7149 if( isset( $theDate['timestamp'] ))
7150 $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null;
7151 else
7152 $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null;
7153 if( !empty( $tzid )) {
7154 $parno = 7;
7155 if( !iCalUtilityFunctions::_isOffset( $tzid ))
7156 $params['TZID'] = $tzid; // save only timezone
7157 }
7158 elseif( !$parno && ( 3 == count( $theDate )) &&
7159 ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )))
7160 $parno = 3;
7161 else
7162 $parno = 6;
7163 }
7164 else { // string
7165 $date = trim( $theDate );
7166 if( 'Z' == substr( $date, -1 ))
7167 $parno = 7; // UTC DATE-TIME
7168 elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) &&
7169 ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' ))))
7170 $parno = 3; // DATE
7171 $date = iCalUtilityFunctions::_strdate2date( $date, $parno );
7172 unset( $date['unparsedtext'] );
7173 if( !empty( $date['tz'] )) {
7174 $parno = 7;
7175 if( !iCalUtilityFunctions::_isOffset( $date['tz'] ))
7176 $params['TZID'] = $date['tz']; // save only timezone
7177 }
7178 elseif( empty( $parno ))
7179 $parno = 6;
7180 }
7181 if( isset( $params['TZID'] ))
7182 $parno = 6;
7183 }
7184 }
7185 /**
7186 * vcalendar sort callback function
7187 *
7188 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7189 * @since 2.16.2 - 2012-12-17
7190 * @param array $a
7191 * @param array $b
7192 * @return int
7193 */
7194 public static function _cmpfcn( $a, $b ) {
7195 if( empty( $a )) return -1;
7196 if( empty( $b )) return 1;
7197 if( 'vtimezone' == $a->objName ) {
7198 if( 'vtimezone' != $b->objName ) return -1;
7199 elseif( $a->srtk[0] <= $b->srtk[0] ) return -1;
7200 else return 1;
7201 }
7202 elseif( 'vtimezone' == $b->objName ) return 1;
7203 $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' );
7204 for( $k = 0; $k < 4 ; $k++ ) {
7205 if( empty( $a->srtk[$k] )) return -1;
7206 elseif( empty( $b->srtk[$k] )) return 1;
7207 if( is_array( $a->srtk[$k] )) {
7208 if( is_array( $b->srtk[$k] )) {
7209 foreach( $sortkeys as $key ) {
7210 if ( !isset( $a->srtk[$k][$key] )) return -1;
7211 elseif( !isset( $b->srtk[$k][$key] )) return 1;
7212 if ( empty( $a->srtk[$k][$key] )) return -1;
7213 elseif( empty( $b->srtk[$k][$key] )) return 1;
7214 if ( $a->srtk[$k][$key] == $b->srtk[$k][$key])
7215 continue;
7216 if (( (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] ))
7217 return -1;
7218 elseif(( (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] ))
7219 return 1;
7220 }
7221 }
7222 else return -1;
7223 }
7224 elseif( is_array( $b->srtk[$k] )) return 1;
7225 elseif( $a->srtk[$k] < $b->srtk[$k] ) return -1;
7226 elseif( $a->srtk[$k] > $b->srtk[$k] ) return 1;
7227 }
7228 return 0;
7229 }
7230 /**
7231 * byte oriented line folding fix
7232 *
7233 * remove any line-endings that may include spaces or tabs
7234 * and convert all line endings (iCal default '\r\n'),
7235 * takes care of '\r\n', '\r' and '\n' and mixed '\r\n'+'\r', '\r\n'+'\n'
7236 *
7237 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7238 * @since 2.12.17 - 2012-07-12
7239 * @param string $text
7240 * @param string $nl
7241 * @return string
7242 */
7243 public static function convEolChar( & $text, $nl ) {
7244 $outp = '';
7245 $cix = 0;
7246 while( isset( $text[$cix] )) {
7247 if( isset( $text[$cix + 2] ) && ( "\r" == $text[$cix] ) && ( "\n" == $text[$cix + 1] ) &&
7248 (( " " == $text[$cix + 2] ) || ( "\t" == $text[$cix + 2] ))) // 2 pos eolchar + ' ' or '\t'
7249 $cix += 2; // skip 3
7250 elseif( isset( $text[$cix + 1] ) && ( "\r" == $text[$cix] ) && ( "\n" == $text[$cix + 1] )) {
7251 $outp .= $nl; // 2 pos eolchar
7252 $cix += 1; // replace with $nl
7253 }
7254 elseif( isset( $text[$cix + 1] ) && (( "\r" == $text[$cix] ) || ( "\n" == $text[$cix] )) &&
7255 (( " " == $text[$cix + 1] ) || ( "\t" == $text[$cix + 1] ))) // 1 pos eolchar + ' ' or '\t'
7256 $cix += 1; // skip 2
7257 elseif(( "\r" == $text[$cix] ) || ( "\n" == $text[$cix] )) // 1 pos eolchar
7258 $outp .= $nl; // replace with $nl
7259 else
7260 $outp .= $text[$cix]; // add any other byte
7261 $cix += 1;
7262 }
7263 return $outp;
7264 }
7265 /**
7266 * create a calendar timezone and standard/daylight components
7267 *
7268 * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone:
7269 *
7270 * BEGIN:VTIMEZONE
7271 * TZID:Europe/Stockholm
7272 * BEGIN:STANDARD
7273 * DTSTART:20101031T020000
7274 * TZOFFSETFROM:+0200
7275 * TZOFFSETTO:+0100
7276 * TZNAME:CET
7277 * END:STANDARD
7278 * BEGIN:DAYLIGHT
7279 * DTSTART:20100328T030000
7280 * TZOFFSETFROM:+0100
7281 * TZOFFSETTO:+0200
7282 * TZNAME:CEST
7283 * END:DAYLIGHT
7284 * END:VTIMEZONE
7285 *
7286 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7287 * @since 2.16.1 - 2012-11-26
7288 * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi <icalcreator@onebigsystem.com>
7289 * Additional changes jpirkey
7290 * @param object $calendar, reference to an iCalcreator calendar instance
7291 * @param string $timezone, a PHP5 (DateTimeZone) valid timezone
7292 * @param array $xProp, *[x-propName => x-propValue], optional
7293 * @param int $from a unix timestamp
7294 * @param int $to a unix timestamp
7295 * @return bool
7296 */
7297 public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) {
7298 if( empty( $timezone ))
7299 return FALSE;
7300 if( !empty( $from ) && !is_int( $from ))
7301 return FALSE;
7302 if( !empty( $to ) && !is_int( $to ))
7303 return FALSE;
7304 try {
7305 $dtz = new DateTimeZone( $timezone );
7306 $transitions = $dtz->getTransitions();
7307 $utcTz = new DateTimeZone( 'UTC' );
7308 }
7309 catch( Exception $e ) { return FALSE; }
7310 if( empty( $to )) {
7311 $dates = array_keys( $calendar->getProperty( 'dtstart' ));
7312 if( empty( $dates ))
7313 $dates = array( date( 'Ymd' ));
7314 }
7315 if( !empty( $from ))
7316 $dateFrom = new DateTime( "@$from" ); // set lowest date (UTC)
7317 else {
7318 $from = reset( $dates ); // set lowest date to the lowest dtstart date
7319 $dateFrom = new DateTime( $from.'T000000', $dtz );
7320 $dateFrom->modify( '-1 month' ); // set $dateFrom to one month before the lowest date
7321 $dateFrom->setTimezone( $utcTz ); // convert local date to UTC
7322 }
7323 $dateFromYmd = $dateFrom->format('Y-m-d' );
7324 if( !empty( $to ))
7325 $dateTo = new DateTime( "@$to" ); // set end date (UTC)
7326 else {
7327 $to = end( $dates ); // set highest date to the highest dtstart date
7328 $dateTo = new DateTime( $to.'T235959', $dtz );
7329 $dateTo->modify( '+1 year' ); // set $dateTo to one year after the highest date
7330 $dateTo->setTimezone( $utcTz ); // convert local date to UTC
7331 }
7332 $dateToYmd = $dateTo->format('Y-m-d' );
7333 unset( $dtz );
7334 $transTemp = array();
7335 $prevOffsetfrom = 0;
7336 $stdIx = $dlghtIx = null;
7337 $prevTrans = FALSE;
7338 foreach( $transitions as $tix => $trans ) { // all transitions in date-time order!!
7339 $date = new DateTime( "@{$trans['ts']}" ); // set transition date (UTC)
7340 $transDateYmd = $date->format('Y-m-d' );
7341 if ( $transDateYmd < $dateFromYmd ) {
7342 $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom
7343 $prevTrans = $trans; // save it in case we don't find any that match
7344 $prevTrans['offsetfrom'] = ( 0 < $tix ) ? $transitions[$tix-1]['offset'] : 0;
7345 continue;
7346 }
7347 if( $transDateYmd > $dateToYmd )
7348 break; // loop always (?) breaks here
7349 if( !empty( $prevOffsetfrom ) || ( 0 == $prevOffsetfrom )) {
7350 $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom
7351 $date->modify( $trans['offsetfrom'].'seconds' ); // convert utc date to local date
7352 $d = $date->format( 'Y-n-j-G-i-s' ); // set date to array to ease up dtstart and (opt) rdate setting
7353 $d = explode( '-', $d );
7354 $trans['time'] = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
7355 }
7356 $prevOffsetfrom = $trans['offset'];
7357 if( TRUE !== $trans['isdst'] ) { // standard timezone
7358 if( !empty( $stdIx ) && isset( $transTemp[$stdIx]['offsetfrom'] ) && // check for any repeating rdate's (in order)
7359 ( $transTemp[$stdIx]['abbr'] == $trans['abbr'] ) &&
7360 ( $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] ) &&
7361 ( $transTemp[$stdIx]['offset'] == $trans['offset'] )) {
7362 $transTemp[$stdIx]['rdate'][] = $trans['time'];
7363 continue;
7364 }
7365 $stdIx = $tix;
7366 } // end standard timezone
7367 else { // daylight timezone
7368 if( !empty( $dlghtIx ) && isset( $transTemp[$dlghtIx]['offsetfrom'] ) && // check for any repeating rdate's (in order)
7369 ( $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] ) &&
7370 ( $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] ) &&
7371 ( $transTemp[$dlghtIx]['offset'] == $trans['offset'] )) {
7372 $transTemp[$dlghtIx]['rdate'][] = $trans['time'];
7373 continue;
7374 }
7375 $dlghtIx = $tix;
7376 } // end daylight timezone
7377 $transTemp[$tix] = $trans;
7378 } // end foreach( $transitions as $tix => $trans )
7379 $tz = & $calendar->newComponent( 'vtimezone' );
7380 $tz->setproperty( 'tzid', $timezone );
7381 if( !empty( $xProp )) {
7382 foreach( $xProp as $xPropName => $xPropValue )
7383 if( 'x-' == strtolower( substr( $xPropName, 0, 2 )))
7384 $tz->setproperty( $xPropName, $xPropValue );
7385 }
7386 if( empty( $transTemp )) { // if no match found
7387 if( $prevTrans ) { // then we use the last transition (before startdate) for the tz info
7388 $date = new DateTime( "@{$prevTrans['ts']}" ); // set transition date (UTC)
7389 $date->modify( $prevTrans['offsetfrom'].'seconds' ); // convert utc date to local date
7390 $d = $date->format( 'Y-n-j-G-i-s' ); // set date to array to ease up dtstart setting
7391 $d = explode( '-', $d );
7392 $prevTrans['time'] = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
7393 $transTemp[0] = $prevTrans;
7394 }
7395 else { // or we use the timezone identifier to BUILD the standard tz info (?)
7396 $date = new DateTime( 'now', new DateTimeZone( $timezone ));
7397 $transTemp[0] = array( 'time' => $date->format( 'Y-m-d\TH:i:s O' )
7398 , 'offset' => $date->format( 'Z' )
7399 , 'offsetfrom' => $date->format( 'Z' )
7400 , 'isdst' => FALSE );
7401 }
7402 }
7403 unset( $transitions, $date, $prevTrans );
7404 foreach( $transTemp as $tix => $trans ) {
7405 $type = ( TRUE !== $trans['isdst'] ) ? 'standard' : 'daylight';
7406 $scomp = & $tz->newComponent( $type );
7407 $scomp->setProperty( 'dtstart', $trans['time'] );
7408 // $scomp->setProperty( 'x-utc-timestamp', $tix.' : '.$trans['ts'] ); // test ###
7409 if( !empty( $trans['abbr'] ))
7410 $scomp->setProperty( 'tzname', $trans['abbr'] );
7411 if( isset( $trans['offsetfrom'] ))
7412 $scomp->setProperty( 'tzoffsetfrom', iCalUtilityFunctions::offsetSec2His( $trans['offsetfrom'] ));
7413 $scomp->setProperty( 'tzoffsetto', iCalUtilityFunctions::offsetSec2His( $trans['offset'] ));
7414 if( isset( $trans['rdate'] ))
7415 $scomp->setProperty( 'RDATE', $trans['rdate'] );
7416 }
7417 return TRUE;
7418 }
7419 /**
7420 * creates formatted output for calendar component property data value type date/date-time
7421 *
7422 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7423 * @since 2.14.1 - 2012-09-17
7424 * @param array $datetime
7425 * @param int $parno, optional, default 6
7426 * @return string
7427 */
7428 public static function _format_date_time( $datetime, $parno=6 ) {
7429 return iCalUtilityFunctions::_date2strdate( $datetime, $parno );
7430 }
7431 public static function _date2strdate( $datetime, $parno=6 ) {
7432 if( !isset( $datetime['year'] ) &&
7433 !isset( $datetime['month'] ) &&
7434 !isset( $datetime['day'] ) &&
7435 !isset( $datetime['hour'] ) &&
7436 !isset( $datetime['min'] ) &&
7437 !isset( $datetime['sec'] ))
7438 return;
7439 $output = null;
7440 foreach( $datetime as $dkey => & $dvalue )
7441 if( 'tz' != $dkey ) $dvalue = (integer) $dvalue;
7442 $output = sprintf( '%04d%02d%02d', $datetime['year'], $datetime['month'], $datetime['day'] );
7443 if( 3 == $parno )
7444 return $output;
7445 if( !isset( $datetime['hour'] )) $datetime['hour'] = 0;
7446 if( !isset( $datetime['min'] )) $datetime['min'] = 0;
7447 if( !isset( $datetime['sec'] )) $datetime['sec'] = 0;
7448 $output .= sprintf( 'T%02d%02d%02d', $datetime['hour'], $datetime['min'], $datetime['sec'] );
7449 if( isset( $datetime['tz'] ) && ( '' < trim( $datetime['tz'] ))) {
7450 $datetime['tz'] = trim( $datetime['tz'] );
7451 if( 'Z' == $datetime['tz'] )
7452 $parno = 7;
7453 elseif( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) {
7454 $parno = 7;
7455 $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] );
7456 try {
7457 $d = new DateTime( $output, new DateTimeZone( 'UTC' ));
7458 if( 0 != $offset ) // adjust för offset
7459 $d->modify( "$offset seconds" );
7460 $output = $d->format( 'Ymd\THis' );
7461 }
7462 catch( Exception $e ) {
7463 $output = date( 'Ymd\THis', mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year'] ));
7464 }
7465 }
7466 if( 7 == $parno )
7467 $output .= 'Z';
7468 }
7469 return $output;
7470 }
7471 /**
7472 * convert a date/datetime (array) to timestamp
7473 *
7474 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7475 * @since 2.14.1 - 2012-09-29
7476 * @param array $datetime datetime(/date)
7477 * @param string $wtz timezone
7478 * @return int
7479 */
7480 public static function _date2timestamp( $datetime, $wtz=null ) {
7481 if( !isset( $datetime['hour'] )) $datetime['hour'] = 0;
7482 if( !isset( $datetime['min'] )) $datetime['min'] = 0;
7483 if( !isset( $datetime['sec'] )) $datetime['sec'] = 0;
7484 if( empty( $wtz ) && ( !isset( $datetime['tz'] ) || empty( $datetime['tz'] )))
7485 return mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] );
7486 $output = $offset = 0;
7487 if( empty( $wtz )) {
7488 if( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) {
7489 $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ) * -1;
7490 $wtz = 'UTC';
7491 }
7492 else
7493 $wtz = $datetime['tz'];
7494 }
7495 if(( 'Z' == $wtz ) || ( 'GMT' == strtoupper( $wtz )))
7496 $wtz = 'UTC';
7497 try {
7498 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $datetime['year'], $datetime['month'], $datetime['day'], $datetime['hour'], $datetime['min'], $datetime['sec'] );
7499 $d = new DateTime( $strdate, new DateTimeZone( $wtz ));
7500 if( 0 != $offset ) // adjust for offset
7501 $d->modify( $offset.' seconds' );
7502 $output = $d->format( 'U' );
7503 unset( $d );
7504 }
7505 catch( Exception $e ) {
7506 $output = mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] );
7507 }
7508 return $output;
7509 }
7510 /**
7511 * ensures internal duration format for input in array format
7512 *
7513 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7514 * @since 2.14.1 - 2012-09-25
7515 * @param array $duration
7516 * @return array
7517 */
7518 public static function _duration_array( $duration ) {
7519 return iCalUtilityFunctions::_duration2arr( $duration );
7520 }
7521 public static function _duration2arr( $duration ) {
7522 $output = array();
7523 if( is_array( $duration ) &&
7524 ( 1 == count( $duration )) &&
7525 isset( $duration['sec'] ) &&
7526 ( 60 < $duration['sec'] )) {
7527 $durseconds = $duration['sec'];
7528 $output['week'] = (int) floor( $durseconds / ( 60 * 60 * 24 * 7 ));
7529 $durseconds = $durseconds % ( 60 * 60 * 24 * 7 );
7530 $output['day'] = (int) floor( $durseconds / ( 60 * 60 * 24 ));
7531 $durseconds = $durseconds % ( 60 * 60 * 24 );
7532 $output['hour'] = (int) floor( $durseconds / ( 60 * 60 ));
7533 $durseconds = $durseconds % ( 60 * 60 );
7534 $output['min'] = (int) floor( $durseconds / ( 60 ));
7535 $output['sec'] = ( $durseconds % ( 60 ));
7536 }
7537 else {
7538 foreach( $duration as $durKey => $durValue ) {
7539 if( empty( $durValue )) continue;
7540 switch ( $durKey ) {
7541 case '0': case 'week': $output['week'] = $durValue; break;
7542 case '1': case 'day': $output['day'] = $durValue; break;
7543 case '2': case 'hour': $output['hour'] = $durValue; break;
7544 case '3': case 'min': $output['min'] = $durValue; break;
7545 case '4': case 'sec': $output['sec'] = $durValue; break;
7546 }
7547 }
7548 }
7549 if( isset( $output['week'] ) && ( 0 < $output['week'] )) {
7550 unset( $output['day'], $output['hour'], $output['min'], $output['sec'] );
7551 return $output;
7552 }
7553 unset( $output['week'] );
7554 if( empty( $output['day'] ))
7555 unset( $output['day'] );
7556 if ( isset( $output['hour'] ) || isset( $output['min'] ) || isset( $output['sec'] )) {
7557 if( !isset( $output['hour'] )) $output['hour'] = 0;
7558 if( !isset( $output['min'] )) $output['min'] = 0;
7559 if( !isset( $output['sec'] )) $output['sec'] = 0;
7560 if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] ))
7561 unset( $output['hour'], $output['min'], $output['sec'] );
7562 }
7563 return $output;
7564 }
7565 /**
7566 * convert startdate+duration to a array format datetime
7567 *
7568 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7569 * @since 2.15.12 - 2012-10-31
7570 * @param array $startdate
7571 * @param array $duration
7572 * @return array, date format
7573 */
7574 public static function _duration2date( $startdate, $duration ) {
7575 $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE;
7576 $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0;
7577 $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0;
7578 $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0;
7579 $dtend = 0;
7580 if( isset( $duration['week'] )) $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 );
7581 if( isset( $duration['day'] )) $dtend += ( $duration['day'] * 24 * 60 * 60 );
7582 if( isset( $duration['hour'] )) $dtend += ( $duration['hour'] * 60 *60 );
7583 if( isset( $duration['min'] )) $dtend += ( $duration['min'] * 60 );
7584 if( isset( $duration['sec'] )) $dtend += $duration['sec'];
7585 $date = date( 'Y-m-d-H-i-s', mktime((int) $startdate['hour'], (int) $startdate['min'], (int) ( $startdate['sec'] + $dtend ), (int) $startdate['month'], (int) $startdate['day'], (int) $startdate['year'] ));
7586 $d = explode( '-', $date );
7587 $dtend2 = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
7588 if( isset( $startdate['tz'] ))
7589 $dtend2['tz'] = $startdate['tz'];
7590 if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] )))
7591 unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] );
7592 return $dtend2;
7593 }
7594 /**
7595 * ensures internal duration format for an input string (iCal) formatted duration
7596 *
7597 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7598 * @since 2.14.1 - 2012-09-25
7599 * @param string $duration
7600 * @return array
7601 */
7602 public static function _duration_string( $duration ) {
7603 return iCalUtilityFunctions::_durationStr2arr( $duration );
7604 }
7605 public static function _durationStr2arr( $duration ) {
7606 $duration = (string) trim( $duration );
7607 while( 'P' != strtoupper( substr( $duration, 0, 1 ))) {
7608 if( 0 < strlen( $duration ))
7609 $duration = substr( $duration, 1 );
7610 else
7611 return false; // no leading P !?!?
7612 }
7613 $duration = substr( $duration, 1 ); // skip P
7614 $duration = str_replace ( 't', 'T', $duration );
7615 $duration = str_replace ( 'T', '', $duration );
7616 $output = array();
7617 $val = null;
7618 for( $ix=0; $ix < strlen( $duration ); $ix++ ) {
7619 switch( strtoupper( substr( $duration, $ix, 1 ))) {
7620 case 'W':
7621 $output['week'] = $val;
7622 $val = null;
7623 break;
7624 case 'D':
7625 $output['day'] = $val;
7626 $val = null;
7627 break;
7628 case 'H':
7629 $output['hour'] = $val;
7630 $val = null;
7631 break;
7632 case 'M':
7633 $output['min'] = $val;
7634 $val = null;
7635 break;
7636 case 'S':
7637 $output['sec'] = $val;
7638 $val = null;
7639 break;
7640 default:
7641 if( !ctype_digit( substr( $duration, $ix, 1 )))
7642 return false; // unknown duration control character !?!?
7643 else
7644 $val .= substr( $duration, $ix, 1 );
7645 }
7646 }
7647 return iCalUtilityFunctions::_duration2arr( $output );
7648 }
7649 /**
7650 * creates formatted output for calendar component property data value type duration
7651 *
7652 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7653 * @since 2.15.8 - 2012-10-30
7654 * @param array $duration, array( week, day, hour, min, sec )
7655 * @return string
7656 */
7657 public static function _format_duration( $duration ) {
7658 return iCalUtilityFunctions::_duration2str( $duration );
7659 }
7660 public static function _duration2str( $duration ) {
7661 if( isset( $duration['week'] ) ||
7662 isset( $duration['day'] ) ||
7663 isset( $duration['hour'] ) ||
7664 isset( $duration['min'] ) ||
7665 isset( $duration['sec'] ))
7666 $ok = TRUE;
7667 else
7668 return;
7669 if( isset( $duration['week'] ) && ( 0 < $duration['week'] ))
7670 return 'P'.$duration['week'].'W';
7671 $output = 'P';
7672 if( isset($duration['day'] ) && ( 0 < $duration['day'] ))
7673 $output .= $duration['day'].'D';
7674 if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ||
7675 ( isset( $duration['min']) && ( 0 < $duration['min'] )) ||
7676 ( isset( $duration['sec']) && ( 0 < $duration['sec'] ))) {
7677 $output .= 'T';
7678 $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '0H';
7679 $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : '0M';
7680 $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : '0S';
7681 }
7682 if( 'P' == $output )
7683 $output = 'PT0H0M0S';
7684 return $output;
7685 }
7686 /**
7687 * removes expkey+expvalue from array and returns hitval (if found) else returns elseval
7688 *
7689 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7690 * @since 2.4.16 - 2008-11-08
7691 * @param array $array
7692 * @param string $expkey, expected key
7693 * @param string $expval, expected value
7694 * @param int $hitVal optional, return value if found
7695 * @param int $elseVal optional, return value if not found
7696 * @param int $preSet optional, return value if already preset
7697 * @return int
7698 */
7699 public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) {
7700 if( $preSet )
7701 return $preSet;
7702 if( !is_array( $array ) || ( 0 == count( $array )))
7703 return $elseVal;
7704 foreach( $array as $key => $value ) {
7705 if( strtoupper( $expkey ) == strtoupper( $key )) {
7706 if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) {
7707 unset( $array[$key] );
7708 return $hitVal;
7709 }
7710 }
7711 }
7712 return $elseVal;
7713 }
7714 /**
7715 * checks if input contains a (array formatted) date/time
7716 *
7717 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7718 * @since 2.11.8 - 2012-01-20
7719 * @param array $input
7720 * @return bool
7721 */
7722 public static function _isArrayDate( $input ) {
7723 if( !is_array( $input ))
7724 return FALSE;
7725 if( isset( $input['week'] ) || ( !in_array( count( $input ), array( 3, 6, 7 ))))
7726 return FALSE;
7727 if( 7 == count( $input ))
7728 return TRUE;
7729 if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
7730 return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
7731 if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] ))
7732 return FALSE;
7733 if( in_array( 0, $input ))
7734 return FALSE;
7735 if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] ))
7736 return FALSE;
7737 if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) &&
7738 checkdate( (int) $input[1], (int) $input[2], (int) $input[0] ))
7739 return TRUE;
7740 $input = iCalUtilityFunctions::_strdate2date( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y
7741 if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
7742 return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
7743 return FALSE;
7744 }
7745 /**
7746 * checks if input array contains a timestamp date
7747 *
7748 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7749 * @since 2.4.16 - 2008-10-18
7750 * @param array $input
7751 * @return bool
7752 */
7753 public static function _isArrayTimestampDate( $input ) {
7754 return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ;
7755 }
7756 /**
7757 * controls if input string contains (trailing) UTC/iCal offset
7758 *
7759 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7760 * @since 2.14.1 - 2012-09-21
7761 * @param string $input
7762 * @return bool
7763 */
7764 public static function _isOffset( $input ) {
7765 $input = trim( (string) $input );
7766 if( 'Z' == substr( $input, -1 ))
7767 return TRUE;
7768 elseif(( 5 <= strlen( $input )) &&
7769 ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) &&
7770 ( '0000' <= substr( $input, -4 )) && ( '9999' >= substr( $input, -4 )))
7771 return TRUE;
7772 elseif(( 7 <= strlen( $input )) &&
7773 ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) &&
7774 ( '000000' <= substr( $input, -6 )) && ( '999999' >= substr( $input, -6 )))
7775 return TRUE;
7776 return FALSE;
7777 }
7778 /**
7779 * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone
7780 * matching (MS) UCT offset and time zone descriptors
7781 *
7782 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7783 * @since 2.14.1 - 2012-09-16
7784 * @param string $timezone, input/output variable reference
7785 * @return bool
7786 */
7787 public static function ms2phpTZ( & $timezone ) {
7788 if( empty( $timezone ))
7789 return FALSE;
7790 $search = str_replace( '"', '', $timezone );
7791 $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search );
7792 if( '(UTC' != substr( $search, 0, 4 ))
7793 return FALSE;
7794 if( FALSE === ( $pos = strpos( $search, ')' )))
7795 return FALSE;
7796 $pos = strpos( $search, ')' );
7797 $searchOffset = substr( $search, 4, ( $pos - 4 ));
7798 $searchOffset = iCalUtilityFunctions::_tz2offset( str_replace( ':', '', $searchOffset ));
7799 while( ' ' ==substr( $search, ( $pos + 1 )))
7800 $pos += 1;
7801 $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos + 1 )) ));
7802 $searchWords = explode( ' ', $searchText );
7803 $timezone_abbreviations = DateTimeZone::listAbbreviations();
7804 $hits = array();
7805 foreach( $timezone_abbreviations as $name => $transitions ) {
7806 foreach( $transitions as $cnt => $transition ) {
7807 if( empty( $transition['offset'] ) ||
7808 empty( $transition['timezone_id'] ) ||
7809 ( $transition['offset'] != $searchOffset ))
7810 continue;
7811 $cWords = explode( '/', $transition['timezone_id'] );
7812 $cPrio = $hitCnt = $rank = 0;
7813 foreach( $cWords as $cWord ) {
7814 if( empty( $cWord ))
7815 continue;
7816 $cPrio += 1;
7817 $sPrio = 0;
7818 foreach( $searchWords as $sWord ) {
7819 if( empty( $sWord ) || ( 'time' == strtolower( $sWord )))
7820 continue;
7821 $sPrio += 1;
7822 if( strtolower( $cWord ) == strtolower( $sWord )) {
7823 $hitCnt += 1;
7824 $rank += ( $cPrio + $sPrio );
7825 }
7826 else
7827 $rank += 10;
7828 }
7829 }
7830 if( 0 < $hitCnt ) {
7831 $hits[$rank][] = $transition['timezone_id'];
7832 }
7833 }
7834 }
7835 unset( $timezone_abbreviations );
7836 if( empty( $hits ))
7837 return FALSE;
7838 ksort( $hits );
7839 foreach( $hits as $rank => $tzs ) {
7840 if( !empty( $tzs )) {
7841 $timezone = reset( $tzs );
7842 return TRUE;
7843 }
7844 }
7845 return FALSE;
7846 }
7847 /**
7848 * transforms offset in seconds to [-/+]hhmm[ss]
7849 *
7850 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7851 * @since 2011-05-02
7852 * @param string $seconds
7853 * @return string
7854 */
7855 public static function offsetSec2His( $seconds ) {
7856 if( '-' == substr( $seconds, 0, 1 )) {
7857 $prefix = '-';
7858 $seconds = substr( $seconds, 1 );
7859 }
7860 elseif( '+' == substr( $seconds, 0, 1 )) {
7861 $prefix = '+';
7862 $seconds = substr( $seconds, 1 );
7863 }
7864 else
7865 $prefix = '+';
7866 $output = '';
7867 $hour = (int) floor( $seconds / 3600 );
7868 if( 10 > $hour )
7869 $hour = '0'.$hour;
7870 $seconds = $seconds % 3600;
7871 $min = (int) floor( $seconds / 60 );
7872 if( 10 > $min )
7873 $min = '0'.$min;
7874 $output = $hour.$min;
7875 $seconds = $seconds % 60;
7876 if( 0 < $seconds) {
7877 if( 9 < $seconds)
7878 $output .= $seconds;
7879 else
7880 $output .= '0'.$seconds;
7881 }
7882 return $prefix.$output;
7883 }
7884 /**
7885 * updates an array with dates based on a recur pattern
7886 *
7887 * if missing, UNTIL is set 1 year from startdate (emergency break)
7888 *
7889 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7890 * @since 2.10.19 - 2011-10-31
7891 * @param array $result, array to update, array([timestamp] => timestamp)
7892 * @param array $recur, pattern for recurrency (only value part, params ignored)
7893 * @param array $wdate, component start date
7894 * @param array $startdate, start date
7895 * @param array $enddate, optional
7896 * @return void
7897 * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start
7898 */
7899 public static function _recur2date( & $result, $recur, $wdate, $startdate, $enddate=FALSE ) {
7900 foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v;
7901 $wdateStart = $wdate;
7902 $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
7903 $startdatets = iCalUtilityFunctions::_date2timestamp( $startdate );
7904 if( !$enddate ) {
7905 $enddate = $startdate;
7906 $enddate['year'] += 1;
7907 }
7908 // echo "recur __in_ comp start ".implode('-',$wdate)." period start ".implode('-',$startdate)." period end ".implode('-',$enddate)."<br />\n";print_r($recur);echo "<br />\n";//test###
7909 $endDatets = iCalUtilityFunctions::_date2timestamp( $enddate ); // fix break
7910 if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] ))
7911 $recur['UNTIL'] = $enddate; // create break
7912 if( isset( $recur['UNTIL'] )) {
7913 $tdatets = iCalUtilityFunctions::_date2timestamp( $recur['UNTIL'] );
7914 if( $endDatets > $tdatets ) {
7915 $endDatets = $tdatets; // emergency break
7916 $enddate = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
7917 }
7918 else
7919 $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
7920 }
7921 if( $wdatets > $endDatets ) {
7922 // echo "recur out of date ".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
7923 return array(); // nothing to do.. .
7924 }
7925 if( !isset( $recur['FREQ'] )) // "MUST be specified.. ."
7926 $recur['FREQ'] = 'DAILY'; // ??
7927 $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ??
7928 $weekStart = (int) date( 'W', ( $wdatets + $wkst ));
7929 if( !isset( $recur['INTERVAL'] ))
7930 $recur['INTERVAL'] = 1;
7931 $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence
7932 /* find out how to step up dates and set index for interval count */
7933 $step = array();
7934 if( 'YEARLY' == $recur['FREQ'] )
7935 $step['year'] = 1;
7936 elseif( 'MONTHLY' == $recur['FREQ'] )
7937 $step['month'] = 1;
7938 elseif( 'WEEKLY' == $recur['FREQ'] )
7939 $step['day'] = 7;
7940 else
7941 $step['day'] = 1;
7942 if( isset( $step['year'] ) && isset( $recur['BYMONTH'] ))
7943 $step = array( 'month' => 1 );
7944 if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ??
7945 $step = array( 'day' => 7 );
7946 if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] ))
7947 $step = array( 'day' => 1 );
7948 $intervalarr = array();
7949 if( 1 < $recur['INTERVAL'] ) {
7950 $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
7951 $intervalarr = array( $intervalix => 0 );
7952 }
7953 if( isset( $recur['BYSETPOS'] )) { // save start date + weekno
7954 $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
7955 // echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold<br />\n"; // test ###
7956 if( is_array( $recur['BYSETPOS'] )) {
7957 foreach( $recur['BYSETPOS'] as $bix => $bval )
7958 $recur['BYSETPOS'][$bix] = (int) $bval;
7959 }
7960 else
7961 $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] );
7962 if( 'YEARLY' == $recur['FREQ'] ) {
7963 $wdate['month'] = $wdate['day'] = 1; // start from beginning of year
7964 $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
7965 iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'year' => 1 )); // make sure to count whole last year
7966 }
7967 elseif( 'MONTHLY' == $recur['FREQ'] ) {
7968 $wdate['day'] = 1; // start from beginning of month
7969 $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
7970 iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'month' => 1 )); // make sure to count whole last month
7971 }
7972 else
7973 iCalUtilityFunctions::_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period
7974 // echo "BYSETPOS endDat++ =".implode('-',$enddate).' step='.var_export($step,TRUE)."<br />\n";//test###
7975 $bysetposWold = (int) date( 'W', ( $wdatets + $wkst ));
7976 $bysetposYold = $wdate['year'];
7977 $bysetposMold = $wdate['month'];
7978 $bysetposDold = $wdate['day'];
7979 }
7980 else
7981 iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
7982 $year_old = null;
7983 $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
7984 /* MAIN LOOP */
7985 // echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test
7986 while( TRUE ) {
7987 if( isset( $endDatets ) && ( $wdatets > $endDatets ))
7988 break;
7989 if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
7990 break;
7991 if( $year_old != $wdate['year'] ) {
7992 $year_old = $wdate['year'];
7993 $daycnts = array();
7994 $yeardays = $weekno = 0;
7995 $yeardaycnt = array();
7996 foreach( $daynames as $dn )
7997 $yeardaycnt[$dn] = 0;
7998 for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
7999 $daycnts[$m] = array();
8000 $weekdaycnt = array();
8001 foreach( $daynames as $dn )
8002 $weekdaycnt[$dn] = 0;
8003 $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
8004 for( $d = 1; $d <= $mcnt; $d++ ) {
8005 $daycnts[$m][$d] = array();
8006 if( isset( $recur['BYYEARDAY'] )) {
8007 $yeardays++;
8008 $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
8009 }
8010 if( isset( $recur['BYDAY'] )) {
8011 $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] ));
8012 $day = $daynames[$day];
8013 $daycnts[$m][$d]['DAY'] = $day;
8014 $weekdaycnt[$day]++;
8015 $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
8016 $yeardaycnt[$day]++;
8017 $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
8018 }
8019 if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
8020 $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year']));
8021 }
8022 }
8023 $daycnt = 0;
8024 $yeardaycnt = array();
8025 if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) {
8026 $weekno = null;
8027 for( $d=31; $d > 25; $d-- ) { // get last weekno for year
8028 if( !$weekno )
8029 $weekno = $daycnts[12][$d]['weekno_up'];
8030 elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) {
8031 $weekno = $daycnts[12][$d]['weekno_up'];
8032 break;
8033 }
8034 }
8035 }
8036 for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
8037 $weekdaycnt = array();
8038 foreach( $daynames as $dn )
8039 $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
8040 $monthcnt = 0;
8041 $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
8042 for( $d = $mcnt; $d > 0; $d-- ) {
8043 if( isset( $recur['BYYEARDAY'] )) {
8044 $daycnt -= 1;
8045 $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
8046 }
8047 if( isset( $recur['BYMONTHDAY'] )) {
8048 $monthcnt -= 1;
8049 $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
8050 }
8051 if( isset( $recur['BYDAY'] )) {
8052 $day = $daycnts[$m][$d]['DAY'];
8053 $weekdaycnt[$day] -= 1;
8054 $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
8055 $yeardaycnt[$day] -= 1;
8056 $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
8057 }
8058 if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
8059 $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1);
8060 }
8061 }
8062 }
8063 /* check interval */
8064 if( 1 < $recur['INTERVAL'] ) {
8065 /* create interval index */
8066 $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
8067 /* check interval */
8068 $currentKey = array_keys( $intervalarr );
8069 $currentKey = end( $currentKey ); // get last index
8070 if( $currentKey != $intervalix )
8071 $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 ));
8072 if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) &&
8073 ( 0 != $intervalarr[$intervalix] )) {
8074 /* step up date */
8075 // echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
8076 iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
8077 continue;
8078 }
8079 else // continue within the selected interval
8080 $intervalarr[$intervalix] = 0;
8081 // echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
8082 }
8083 $updateOK = TRUE;
8084 if( $updateOK && isset( $recur['BYMONTH'] ))
8085 $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH']
8086 , $wdate['month']
8087 ,($wdate['month'] - 13));
8088 if( $updateOK && isset( $recur['BYWEEKNO'] ))
8089 $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO']
8090 , $daycnts[$wdate['month']][$wdate['day']]['weekno_up']
8091 , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] );
8092 if( $updateOK && isset( $recur['BYYEARDAY'] ))
8093 $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY']
8094 , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up']
8095 , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] );
8096 if( $updateOK && isset( $recur['BYMONTHDAY'] ))
8097 $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY']
8098 , $wdate['day']
8099 , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] );
8100 // echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n";//test###
8101 if( $updateOK && isset( $recur['BYDAY'] )) {
8102 $updateOK = FALSE;
8103 $m = $wdate['month'];
8104 $d = $wdate['day'];
8105 if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no
8106 $daynoexists = $daynosw = $daynamesw = FALSE;
8107 if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] )
8108 $daynamesw = TRUE;
8109 if( isset( $recur['BYDAY'][0] )) {
8110 $daynoexists = TRUE;
8111 if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] ))
8112 $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
8113 , $daycnts[$m][$d]['monthdayno_up']
8114 , $daycnts[$m][$d]['monthdayno_down'] );
8115 elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
8116 $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
8117 , $daycnts[$m][$d]['yeardayno_up']
8118 , $daycnts[$m][$d]['yeardayno_down'] );
8119 }
8120 if(( $daynoexists && $daynosw && $daynamesw ) ||
8121 ( !$daynoexists && !$daynosw && $daynamesw )) {
8122 $updateOK = TRUE;
8123 // echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br />\n"; // test ###
8124 }
8125 // echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br />\n"; // test ###
8126 }
8127 else {
8128 foreach( $recur['BYDAY'] as $bydayvalue ) {
8129 $daynoexists = $daynosw = $daynamesw = FALSE;
8130 if( isset( $bydayvalue['DAY'] ) &&
8131 ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] ))
8132 $daynamesw = TRUE;
8133 if( isset( $bydayvalue[0] )) {
8134 $daynoexists = TRUE;
8135 if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) ||
8136 isset( $recur['BYMONTH'] ))
8137 $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
8138 , $daycnts[$m][$d]['monthdayno_up']
8139 , $daycnts[$m][$d]['monthdayno_down'] );
8140 elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
8141 $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
8142 , $daycnts[$m][$d]['yeardayno_up']
8143 , $daycnts[$m][$d]['yeardayno_down'] );
8144 }
8145 // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
8146 if(( $daynoexists && $daynosw && $daynamesw ) ||
8147 ( !$daynoexists && !$daynosw && $daynamesw )) {
8148 $updateOK = TRUE;
8149 break;
8150 }
8151 }
8152 }
8153 }
8154 // echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ###
8155 /* check BYSETPOS */
8156 if( $updateOK ) {
8157 if( isset( $recur['BYSETPOS'] ) &&
8158 ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) {
8159 if( isset( $recur['WEEKLY'] )) {
8160 if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] )
8161 $bysetposw1[] = $wdatets;
8162 else
8163 $bysetposw2[] = $wdatets;
8164 }
8165 else {
8166 if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
8167 ( $bysetposYold == $wdate['year'] )) ||
8168 ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) &&
8169 (( $bysetposYold == $wdate['year'] ) &&
8170 ( $bysetposMold == $wdate['month'] ))) ||
8171 ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
8172 (( $bysetposYold == $wdate['year'] ) &&
8173 ( $bysetposMold == $wdate['month']) &&
8174 ( $bysetposDold == $wdate['day'] )))) {
8175 // echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
8176 $bysetposymd1[] = $wdatets;
8177 }
8178 else {
8179 // echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
8180 $bysetposymd2[] = $wdatets;
8181 }
8182 }
8183 }
8184 else {
8185 /* update result array if BYSETPOS is set */
8186 $countcnt++;
8187 if( $startdatets <= $wdatets ) { // only output within period
8188 $result[$wdatets] = TRUE;
8189 // echo "recur ".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
8190 }
8191 // echo "recur undate ".date('Y-m-d H:i:s',$wdatets)." okdatstart ".date('Y-m-d H:i:s',$startdatets)."<br />\n";//test
8192 $updateOK = FALSE;
8193 }
8194 }
8195 /* step up date */
8196 iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
8197 /* check if BYSETPOS is set for updating result array */
8198 if( $updateOK && isset( $recur['BYSETPOS'] )) {
8199 $bysetpos = FALSE;
8200 if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
8201 ( $bysetposYold != $wdate['year'] )) {
8202 $bysetpos = TRUE;
8203 $bysetposYold = $wdate['year'];
8204 }
8205 elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] &&
8206 (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) {
8207 $bysetpos = TRUE;
8208 $bysetposYold = $wdate['year'];
8209 $bysetposMold = $wdate['month'];
8210 }
8211 elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) {
8212 $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
8213 if( $bysetposWold != $weekno ) {
8214 $bysetposWold = $weekno;
8215 $bysetpos = TRUE;
8216 }
8217 }
8218 elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
8219 (( $bysetposYold != $wdate['year'] ) ||
8220 ( $bysetposMold != $wdate['month'] ) ||
8221 ( $bysetposDold != $wdate['day'] ))) {
8222 $bysetpos = TRUE;
8223 $bysetposYold = $wdate['year'];
8224 $bysetposMold = $wdate['month'];
8225 $bysetposDold = $wdate['day'];
8226 }
8227 if( $bysetpos ) {
8228 if( isset( $recur['BYWEEKNO'] )) {
8229 $bysetposarr1 = & $bysetposw1;
8230 $bysetposarr2 = & $bysetposw2;
8231 }
8232 else {
8233 $bysetposarr1 = & $bysetposymd1;
8234 $bysetposarr2 = & $bysetposymd2;
8235 }
8236 // echo 'test före out startYMD (weekno)='.$wdateStart['year'].':'.$wdateStart['month'].':'.$wdateStart['day']." ($weekStart) "; // test ###
8237 foreach( $recur['BYSETPOS'] as $ix ) {
8238 if( 0 > $ix ) // both positive and negative BYSETPOS allowed
8239 $ix = ( count( $bysetposarr1 ) + $ix + 1);
8240 $ix--;
8241 if( isset( $bysetposarr1[$ix] )) {
8242 if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period
8243 // $testdate = iCalUtilityFunctions::_timestamp2date( $bysetposarr1[$ix], 6 ); // test ###
8244 // $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, $testdate['month'], $testdate['day'], $testdate['year'] )); // test ###
8245 // echo " testYMD (weekno)=".$testdate['year'].':'.$testdate['month'].':'.$testdate['day']." ($testweekno)"; // test ###
8246 $result[$bysetposarr1[$ix]] = TRUE;
8247 // echo " recur ".date('Y-m-d H:i:s',$bysetposarr1[$ix]); // test ###
8248 }
8249 $countcnt++;
8250 }
8251 if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
8252 break;
8253 }
8254 // echo "<br />\n"; // test ###
8255 $bysetposarr1 = $bysetposarr2;
8256 $bysetposarr2 = array();
8257 }
8258 }
8259 }
8260 }
8261 public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
8262 if( is_array( $BYvalue ) &&
8263 ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )))
8264 return TRUE;
8265 elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
8266 return TRUE;
8267 else
8268 return FALSE;
8269 }
8270 public static function _recurIntervalIx( $freq, $date, $wkst ) {
8271 /* create interval index */
8272 switch( $freq ) {
8273 case 'YEARLY':
8274 $intervalix = $date['year'];
8275 break;
8276 case 'MONTHLY':
8277 $intervalix = $date['year'].'-'.$date['month'];
8278 break;
8279 case 'WEEKLY':
8280 $wdatets = iCalUtilityFunctions::_date2timestamp( $date );
8281 $intervalix = (int) date( 'W', ( $wdatets + $wkst ));
8282 break;
8283 case 'DAILY':
8284 default:
8285 $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day'];
8286 break;
8287 }
8288 return $intervalix;
8289 }
8290 /**
8291 * convert input format for exrule and rrule to internal format
8292 *
8293 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8294 * @since 2.14.1 - 2012-09-24
8295 * @param array $rexrule
8296 * @return array
8297 */
8298 public static function _setRexrule( $rexrule ) {
8299 $input = array();
8300 if( empty( $rexrule ))
8301 return $input;
8302 foreach( $rexrule as $rexrulelabel => $rexrulevalue ) {
8303 $rexrulelabel = strtoupper( $rexrulelabel );
8304 if( 'UNTIL' != $rexrulelabel )
8305 $input[$rexrulelabel] = $rexrulevalue;
8306 else {
8307 iCalUtilityFunctions::_strDate2arr( $rexrulevalue );
8308 if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp, always date-time UTC
8309 $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 7, 'UTC' );
8310 elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) { // date or UTC date-time
8311 $parno = ( isset( $rexrulevalue['hour'] ) || isset( $rexrulevalue[4] )) ? 7 : 3;
8312 $d = iCalUtilityFunctions::_chkDateArr( $rexrulevalue, $parno );
8313 if(( 3 < $parno ) && isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
8314 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
8315 $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
8316 unset( $input[$rexrulelabel]['unparsedtext'] );
8317 }
8318 else
8319 $input[$rexrulelabel] = $d;
8320 }
8321 elseif( 8 <= strlen( trim( $rexrulevalue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC
8322 $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $rexrulevalue );
8323 unset( $input['$rexrulelabel']['unparsedtext'] );
8324 }
8325 if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] ))
8326 $input[$rexrulelabel]['tz'] = 'Z';
8327 }
8328 }
8329 /* set recurrence rule specification in rfc2445 order */
8330 $input2 = array();
8331 if( isset( $input['FREQ'] ))
8332 $input2['FREQ'] = $input['FREQ'];
8333 if( isset( $input['UNTIL'] ))
8334 $input2['UNTIL'] = $input['UNTIL'];
8335 elseif( isset( $input['COUNT'] ))
8336 $input2['COUNT'] = $input['COUNT'];
8337 if( isset( $input['INTERVAL'] ))
8338 $input2['INTERVAL'] = $input['INTERVAL'];
8339 if( isset( $input['BYSECOND'] ))
8340 $input2['BYSECOND'] = $input['BYSECOND'];
8341 if( isset( $input['BYMINUTE'] ))
8342 $input2['BYMINUTE'] = $input['BYMINUTE'];
8343 if( isset( $input['BYHOUR'] ))
8344 $input2['BYHOUR'] = $input['BYHOUR'];
8345 if( isset( $input['BYDAY'] )) {
8346 if( !is_array( $input['BYDAY'] )) // ensure upper case.. .
8347 $input2['BYDAY'] = strtoupper( $input['BYDAY'] );
8348 else {
8349 foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) {
8350 if( 'DAY' == strtoupper( $BYDAYx ))
8351 $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv );
8352 elseif( !is_array( $BYDAYv )) {
8353 $input2['BYDAY'][$BYDAYx] = $BYDAYv;
8354 }
8355 else {
8356 foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) {
8357 if( 'DAY' == strtoupper( $BYDAYx2 ))
8358 $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 );
8359 else
8360 $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2;
8361 }
8362 }
8363 }
8364 }
8365 }
8366 if( isset( $input['BYMONTHDAY'] ))
8367 $input2['BYMONTHDAY'] = $input['BYMONTHDAY'];
8368 if( isset( $input['BYYEARDAY'] ))
8369 $input2['BYYEARDAY'] = $input['BYYEARDAY'];
8370 if( isset( $input['BYWEEKNO'] ))
8371 $input2['BYWEEKNO'] = $input['BYWEEKNO'];
8372 if( isset( $input['BYMONTH'] ))
8373 $input2['BYMONTH'] = $input['BYMONTH'];
8374 if( isset( $input['BYSETPOS'] ))
8375 $input2['BYSETPOS'] = $input['BYSETPOS'];
8376 if( isset( $input['WKST'] ))
8377 $input2['WKST'] = $input['WKST'];
8378 return $input2;
8379 }
8380 /**
8381 * convert format for input date to internal date with parameters
8382 *
8383 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8384 * @since 2.14.1 - 2012-10-15
8385 * @param mixed $year
8386 * @param mixed $month optional
8387 * @param int $day optional
8388 * @param int $hour optional
8389 * @param int $min optional
8390 * @param int $sec optional
8391 * @param string $tz optional
8392 * @param array $params optional
8393 * @param string $caller optional
8394 * @param string $objName optional
8395 * @param string $tzid optional
8396 * @return array
8397 */
8398 public static function _setDate( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE, $caller=null, $objName=null, $tzid=FALSE ) {
8399 $input = $parno = null;
8400 $localtime = (( 'dtstart' == $caller ) && in_array( $objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
8401 iCalUtilityFunctions::_strDate2arr( $year );
8402 if( iCalUtilityFunctions::_isArrayDate( $year )) {
8403 $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, $parno );
8404 if( 100 > $input['value']['year'] )
8405 $input['value']['year'] += 2000;
8406 if( $localtime )
8407 unset( $month['VALUE'], $month['TZID'] );
8408 elseif( !isset( $month['TZID'] ) && isset( $tzid ))
8409 $month['TZID'] = $tzid;
8410 if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] ))
8411 unset( $month['TZID'] );
8412 elseif( isset( $month['TZID'] ) && iCalUtilityFunctions::_isOffset( $month['TZID'] )) {
8413 $input['value']['tz'] = $month['TZID'];
8414 unset( $month['TZID'] );
8415 }
8416 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
8417 $hitval = ( isset( $input['value']['tz'] )) ? 7 : 6;
8418 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
8419 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $input['value'] ), $parno );
8420 if(( 3 != $parno ) && isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
8421 $d = $input['value'];
8422 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
8423 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno );
8424 unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
8425 }
8426 if( isset( $input['value']['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
8427 $input['params']['TZID'] = $input['value']['tz'];
8428 unset( $input['value']['tz'] );
8429 }
8430 } // end if( iCalUtilityFunctions::_isArrayDate( $year ))
8431 elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
8432 if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
8433 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
8434 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
8435 $hitval = 7;
8436 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno );
8437 if( !isset( $input['params']['TZID'] ) && !empty( $tzid ))
8438 $input['params']['TZID'] = $tzid;
8439 if( isset( $year['tz'] )) {
8440 $parno = 6;
8441 if( !iCalUtilityFunctions::_isOffset( $year['tz'] ))
8442 $input['params']['TZID'] = $year['tz'];
8443 }
8444 elseif( isset( $input['params']['TZID'] )) {
8445 $year['tz'] = $input['params']['TZID'];
8446 $parno = 6;
8447 if( iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
8448 unset( $input['params']['TZID'] );
8449 $parno = 7;
8450 }
8451 }
8452 $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno );
8453 } // end elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year ))
8454 elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone]
8455 if( $localtime )
8456 unset( $month['VALUE'], $month['TZID'] );
8457 elseif( !isset( $month['TZID'] ) && !empty( $tzid ))
8458 $month['TZID'] = $tzid;
8459 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
8460 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno );
8461 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno );
8462 $input['value'] = iCalUtilityFunctions::_strdate2date( $year, $parno );
8463 unset( $input['value']['unparsedtext'] );
8464 if( isset( $input['value']['tz'] )) {
8465 if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
8466 $d = $input['value'];
8467 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
8468 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
8469 unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
8470 }
8471 else {
8472 $input['params']['TZID'] = $input['value']['tz'];
8473 unset( $input['value']['tz'] );
8474 }
8475 }
8476 elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
8477 $d = $input['value'];
8478 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $input['params']['TZID'] );
8479 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
8480 unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
8481 }
8482 } // end elseif( 8 <= strlen( trim( $year )))
8483 else {
8484 if( is_array( $params ))
8485 $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
8486 elseif( is_array( $tz )) {
8487 $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' ));
8488 $tz = FALSE;
8489 }
8490 elseif( is_array( $hour )) {
8491 $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' ));
8492 $hour = $min = $sec = $tz = FALSE;
8493 }
8494 if( $localtime )
8495 unset ( $input['params']['VALUE'], $input['params']['TZID'] );
8496 elseif( !isset( $tz ) && !isset( $input['params']['TZID'] ) && !empty( $tzid ))
8497 $input['params']['TZID'] = $tzid;
8498 elseif( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz ))
8499 unset( $input['params']['TZID'] );
8500 elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
8501 $tz = $input['params']['TZID'];
8502 unset( $input['params']['TZID'] );
8503 }
8504 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
8505 $hitval = ( iCalUtilityFunctions::_isOffset( $tz )) ? 7 : 6;
8506 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno );
8507 $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day );
8508 if( 3 != $parno ) {
8509 $input['value']['hour'] = ( $hour ) ? $hour : '0';
8510 $input['value']['min'] = ( $min ) ? $min : '0';
8511 $input['value']['sec'] = ( $sec ) ? $sec : '0';
8512 if( !empty( $tz ))
8513 $input['value']['tz'] = $tz;
8514 $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], $parno );
8515 if( !empty( $tz ) && !iCalUtilityFunctions::_isOffset( $tz ))
8516 $strdate .= ( 'Z' == $tz ) ? $tz : ' '.$tz;
8517 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno );
8518 unset( $input['value']['unparsedtext'] );
8519 if( isset( $input['value']['tz'] )) {
8520 if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
8521 $d = $input['value'];
8522 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
8523 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
8524 unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
8525 }
8526 else {
8527 $input['params']['TZID'] = $input['value']['tz'];
8528 unset( $input['value']['tz'] );
8529 }
8530 }
8531 elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
8532 $d = $input['value'];
8533 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $input['params']['TZID'] );
8534 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
8535 unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
8536 }
8537 }
8538 } // end else (i.e. using all arguments)
8539 if(( 3 == $parno ) || ( isset( $input['params']['VALUE'] ) && ( 'DATE' == $input['params']['VALUE'] ))) {
8540 $input['params']['VALUE'] = 'DATE';
8541 unset( $input['value']['hour'], $input['value']['min'], $input['value']['sec'], $input['value']['tz'], $input['params']['TZID'] );
8542 }
8543 elseif( isset( $input['params']['TZID'] )) {
8544 if(( 'UTC' == strtoupper( $input['params']['TZID'] )) || ( 'GMT' == strtoupper( $input['params']['TZID'] ))) {
8545 $input['value']['tz'] = 'Z';
8546 unset( $input['params']['TZID'] );
8547 }
8548 else
8549 unset( $input['value']['tz'] );
8550 }
8551 elseif( isset( $input['value']['tz'] )) {
8552 if(( 'UTC' == strtoupper( $input['value']['tz'] )) || ( 'GMT' == strtoupper( $input['value']['tz'] )))
8553 $input['value']['tz'] = 'Z';
8554 if( 'Z' != $input['value']['tz'] ) {
8555 $input['params']['TZID'] = $input['value']['tz'];
8556 unset( $input['value']['tz'] );
8557 }
8558 else
8559 unset( $input['params']['TZID'] );
8560 }
8561 if( $localtime )
8562 unset( $input['value']['tz'], $input['params']['TZID'] );
8563 return $input;
8564 }
8565 /**
8566 * convert format for input date (UTC) to internal date with parameters
8567 *
8568 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8569 * @since 2.14.4 - 2012-10-06
8570 * @param mixed $year
8571 * @param mixed $month optional
8572 * @param int $day optional
8573 * @param int $hour optional
8574 * @param int $min optional
8575 * @param int $sec optional
8576 * @param array $params optional
8577 * @return array
8578 */
8579 public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
8580 $input = null;
8581 iCalUtilityFunctions::_strDate2arr( $year );
8582 if( iCalUtilityFunctions::_isArrayDate( $year )) {
8583 $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, 7 );
8584 if( isset( $input['value']['year'] ) && ( 100 > $input['value']['year'] ))
8585 $input['value']['year'] += 2000;
8586 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
8587 if( isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
8588 $d = $input['value'];
8589 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
8590 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
8591 unset( $input['value']['unparsedtext'] );
8592 }
8593 }
8594 elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
8595 $year['tz'] = 'UTC';
8596 $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 );
8597 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
8598 }
8599 elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
8600 $input['value'] = iCalUtilityFunctions::_strdate2date( $year, 7 );
8601 unset( $input['value']['unparsedtext'] );
8602 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
8603 }
8604 else {
8605 $input['value'] = array( 'year' => $year
8606 , 'month' => $month
8607 , 'day' => $day
8608 , 'hour' => $hour
8609 , 'min' => $min
8610 , 'sec' => $sec );
8611 if( isset( $tz )) $input['value']['tz'] = $tz;
8612 if(( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) ||
8613 ( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))) {
8614 if( !isset( $tz ) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))
8615 $input['value']['tz'] = $input['params']['TZID'];
8616 unset( $input['params']['TZID'] );
8617 $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], 7 );
8618 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
8619 unset( $input['value']['unparsedtext'] );
8620 }
8621 $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
8622 }
8623 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default
8624 if( !isset( $input['value']['hour'] )) $input['value']['hour'] = 0;
8625 if( !isset( $input['value']['min'] )) $input['value']['min'] = 0;
8626 if( !isset( $input['value']['sec'] )) $input['value']['sec'] = 0;
8627 $input['value']['tz'] = 'Z';
8628 return $input;
8629 }
8630 /**
8631 * check index and set (an indexed) content in multiple value array
8632 *
8633 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8634 * @since 2.6.12 - 2011-01-03
8635 * @param array $valArr
8636 * @param mixed $value
8637 * @param array $params
8638 * @param array $defaults
8639 * @param int $index
8640 * @return void
8641 */
8642 public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) {
8643 if( !is_array( $valArr )) $valArr = array();
8644 if( $index )
8645 $index = $index - 1;
8646 elseif( 0 < count( $valArr )) {
8647 $keys = array_keys( $valArr );
8648 $index = end( $keys ) + 1;
8649 }
8650 else
8651 $index = 0;
8652 $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults ));
8653 ksort( $valArr );
8654 }
8655 /**
8656 * set input (formatted) parameters- component property attributes
8657 *
8658 * default parameters can be set, if missing
8659 *
8660 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8661 * @since 1.x.x - 2007-05-01
8662 * @param array $params
8663 * @param array $defaults
8664 * @return array
8665 */
8666 public static function _setParams( $params, $defaults=FALSE ) {
8667 if( !is_array( $params))
8668 $params = array();
8669 $input = array();
8670 foreach( $params as $paramKey => $paramValue ) {
8671 if( is_array( $paramValue )) {
8672 foreach( $paramValue as $pkey => $pValue ) {
8673 if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 )))
8674 $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 ));
8675 }
8676 }
8677 elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 )))
8678 $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 ));
8679 if( 'VALUE' == strtoupper( $paramKey ))
8680 $input['VALUE'] = strtoupper( $paramValue );
8681 else
8682 $input[strtoupper( $paramKey )] = $paramValue;
8683 }
8684 if( is_array( $defaults )) {
8685 foreach( $defaults as $paramKey => $paramValue ) {
8686 if( !isset( $input[$paramKey] ))
8687 $input[$paramKey] = $paramValue;
8688 }
8689 }
8690 return (0 < count( $input )) ? $input : null;
8691 }
8692 /**
8693 * break lines at pos 75
8694 *
8695 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
8696 * break. Long content lines SHOULD be split into a multiple line
8697 * representations using a line "folding" technique. That is, a long
8698 * line can be split between any two characters by inserting a CRLF
8699 * immediately followed by a single linear white space character (i.e.,
8700 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
8701 * of CRLF followed immediately by a single linear white space character
8702 * is ignored (i.e., removed) when processing the content type.
8703 *
8704 * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where
8705 * the reserved expression "\n" in the arg $string could be broken up by the
8706 * folding of lines, causing ambiguity in the return string.
8707 *
8708 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8709 * @since 2.16.2 - 2012-12-18
8710 * @param string $value
8711 * @return string
8712 */
8713 public static function _size75( $string, $nl ) {
8714 $tmp = $string;
8715 $string = '';
8716 $cCnt = $x = 0;
8717 while( TRUE ) {
8718 if( !isset( $tmp[$x] )) {
8719 $string .= $nl; // loop breakes here
8720 break;
8721 }
8722 elseif(( 74 <= $cCnt ) && ( '\\' == $tmp[$x] ) && ( 'n' == $tmp[$x+1] )) {
8723 $string .= $nl.' \n'; // don't break lines inside '\n'
8724 $x += 2;
8725 if( !isset( $tmp[$x] )) {
8726 $string .= $nl;
8727 break;
8728 }
8729 $cCnt = 3;
8730 }
8731 elseif( 75 <= $cCnt ) {
8732 $string .= $nl.' ';
8733 $cCnt = 1;
8734 }
8735 $byte = ord( $tmp[$x] );
8736 $string .= $tmp[$x];
8737 switch( TRUE ) { // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
8738 case(( $byte >= 0x20 ) && ( $byte <= 0x7F )): // characters U-00000000 - U-0000007F (same as ASCII)
8739 $cCnt += 1;
8740 break; // add a one byte character
8741 case(( $byte & 0xE0) == 0xC0 ): // characters U-00000080 - U-000007FF, mask 110XXXXX
8742 if( isset( $tmp[$x+1] )) {
8743 $cCnt += 1;
8744 $string .= $tmp[$x+1];
8745 $x += 1; // add a two bytes character
8746 }
8747 break;
8748 case(( $byte & 0xF0 ) == 0xE0 ): // characters U-00000800 - U-0000FFFF, mask 1110XXXX
8749 if( isset( $tmp[$x+2] )) {
8750 $cCnt += 1;
8751 $string .= $tmp[$x+1].$tmp[$x+2];
8752 $x += 2; // add a three bytes character
8753 }
8754 break;
8755 case(( $byte & 0xF8 ) == 0xF0 ): // characters U-00010000 - U-001FFFFF, mask 11110XXX
8756 if( isset( $tmp[$x+3] )) {
8757 $cCnt += 1;
8758 $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3];
8759 $x += 3; // add a four bytes character
8760 }
8761 break;
8762 case(( $byte & 0xFC ) == 0xF8 ): // characters U-00200000 - U-03FFFFFF, mask 111110XX
8763 if( isset( $tmp[$x+4] )) {
8764 $cCnt += 1;
8765 $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4];
8766 $x += 4; // add a five bytes character
8767 }
8768 break;
8769 case(( $byte & 0xFE ) == 0xFC ): // characters U-04000000 - U-7FFFFFFF, mask 1111110X
8770 if( isset( $tmp[$x+5] )) {
8771 $cCnt += 1;
8772 $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4].$tmp[$x+5];
8773 $x += 5; // add a six bytes character
8774 }
8775 default: // add any other byte without counting up $cCnt
8776 break;
8777 } // end switch( TRUE )
8778 $x += 1; // next 'byte' to test
8779 } // end while( TRUE ) {
8780 return $string;
8781 }
8782 /**
8783 * sort callback functions for exdate
8784 *
8785 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8786 * @since 2.16.11 - 2013-01-12
8787 * @param array $a
8788 * @param array $b
8789 * @return int
8790 */
8791 public static function _sortExdate1( $a, $b ) {
8792 $as = sprintf( '%04d%02d%02d', $a['year'], $a['month'], $a['day'] );
8793 $as .= ( isset( $a['hour'] )) ? sprintf( '%02d%02d%02d', $a['hour'], $a['min'], $a['sec'] ) : '';
8794 $bs = sprintf( '%04d%02d%02d', $b['year'], $b['month'], $b['day'] );
8795 $bs .= ( isset( $b['hour'] )) ? sprintf( '%02d%02d%02d', $b['hour'], $b['min'], $b['sec'] ) : '';
8796 return strcmp( $as, $bs );
8797 }
8798 public static function _sortExdate2( $a, $b ) {
8799 $val = reset( $a['value'] );
8800 $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
8801 $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
8802 $val = reset( $b['value'] );
8803 $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
8804 $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
8805 return strcmp( $as, $bs );
8806 }
8807 /**
8808 * sort callback functions for rdate
8809 *
8810 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8811 * @since 2.16.9 - 2013-01-12
8812 * @param array $a
8813 * @param array $b
8814 * @return int
8815 */
8816 public static function _sortRdate1( $a, $b ) {
8817 $val = isset( $a['year'] ) ? $a : $a[0];
8818 $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
8819 $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
8820 $val = isset( $b['year'] ) ? $b : $b[0];
8821 $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
8822 $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
8823 return strcmp( $as, $bs );
8824 }
8825 public static function _sortRdate2( $a, $b ) {
8826 $val = isset( $a['value'][0]['year'] ) ? $a['value'][0] : $a['value'][0][0];
8827 $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
8828 $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
8829 $val = isset( $b['value'][0]['year'] ) ? $b['value'][0] : $b['value'][0][0];
8830 $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
8831 $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
8832 return strcmp( $as, $bs );
8833 }
8834 /**
8835 * step date, return updated date, array and timpstamp
8836 *
8837 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8838 * @since 2.14.1 - 2012-09-24
8839 * @param array $date, date to step
8840 * @param int $timestamp
8841 * @param array $step, default array( 'day' => 1 )
8842 * @return void
8843 */
8844 public static function _stepdate( &$date, &$timestamp, $step=array( 'day' => 1 )) {
8845 if( !isset( $date['hour'] )) $date['hour'] = 0;
8846 if( !isset( $date['min'] )) $date['min'] = 0;
8847 if( !isset( $date['sec'] )) $date['sec'] = 0;
8848 foreach( $step as $stepix => $stepvalue )
8849 $date[$stepix] += $stepvalue;
8850 $timestamp = mktime( $date['hour'], $date['min'], $date['sec'], $date['month'], $date['day'], $date['year'] );
8851 $d = date( 'Y-m-d-H-i-s', $timestamp);
8852 $d = explode( '-', $d );
8853 $date = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
8854 foreach( $date as $k => $v )
8855 $date[$k] = (int) $v;
8856 }
8857 /**
8858 * convert a date from specific string to array format
8859 *
8860 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8861 * @since 2.11.8 - 2012-01-27
8862 * @param mixed $input
8863 * @return bool, TRUE on success
8864 */
8865 public static function _strDate2arr( & $input ) {
8866 if( is_array( $input ))
8867 return FALSE;
8868 if( 5 > strlen( (string) $input ))
8869 return FALSE;
8870 $work = $input;
8871 if( 2 == substr_count( $work, '-' ))
8872 $work = str_replace( '-', '', $work );
8873 if( 2 == substr_count( $work, '/' ))
8874 $work = str_replace( '/', '', $work );
8875 if( !ctype_digit( substr( $work, 0, 8 )))
8876 return FALSE;
8877 $temp = array( 'year' => (int) substr( $work, 0, 4 )
8878 , 'month' => (int) substr( $work, 4, 2 )
8879 , 'day' => (int) substr( $work, 6, 2 ));
8880 if( !checkdate( $temp['month'], $temp['day'], $temp['year'] ))
8881 return FALSE;
8882 if( 8 == strlen( $work )) {
8883 $input = $temp;
8884 return TRUE;
8885 }
8886 if(( ' ' == substr( $work, 8, 1 )) || ( 'T' == substr( $work, 8, 1 )) || ( 't' == substr( $work, 8, 1 )))
8887 $work = substr( $work, 9 );
8888 elseif( ctype_digit( substr( $work, 8, 1 )))
8889 $work = substr( $work, 8 );
8890 else
8891 return FALSE;
8892 if( 2 == substr_count( $work, ':' ))
8893 $work = str_replace( ':', '', $work );
8894 if( !ctype_digit( substr( $work, 0, 4 )))
8895 return FALSE;
8896 $temp['hour'] = substr( $work, 0, 2 );
8897 $temp['min'] = substr( $work, 2, 2 );
8898 if((( 0 > $temp['hour'] ) || ( $temp['hour'] > 23 )) ||
8899 (( 0 > $temp['min'] ) || ( $temp['min'] > 59 )))
8900 return FALSE;
8901 if( ctype_digit( substr( $work, 4, 2 ))) {
8902 $temp['sec'] = substr( $work, 4, 2 );
8903 if(( 0 > $temp['sec'] ) || ( $temp['sec'] > 59 ))
8904 return FALSE;
8905 $len = 6;
8906 }
8907 else {
8908 $temp['sec'] = 0;
8909 $len = 4;
8910 }
8911 if( $len < strlen( $work))
8912 $temp['tz'] = trim( substr( $work, 6 ));
8913 $input = $temp;
8914 return TRUE;
8915 }
8916 /**
8917 * ensures internal date-time/date format for input date-time/date in string fromat
8918 *
8919 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8920 * @since 2.14.1 - 2012-10-07
8921 * Modified to also return original string value by Yitzchok Lavi <icalcreator@onebigsystem.com>
8922 * @param array $datetime
8923 * @param int $parno optional, default FALSE
8924 * @param moxed $wtz optional, default null
8925 * @return array
8926 */
8927 public static function _date_time_string( $datetime, $parno=FALSE ) {
8928 return iCalUtilityFunctions::_strdate2date( $datetime, $parno, null );
8929 }
8930 public static function _strdate2date( $datetime, $parno=FALSE, $wtz=null ) {
8931 // save original input string to return it later
8932 $unparseddatetime = $datetime;
8933 $datetime = (string) trim( $datetime );
8934 $tz = null;
8935 $offset = 0;
8936 $tzSts = FALSE;
8937 $len = strlen( $datetime );
8938 if( 'Z' == substr( $datetime, -1 )) {
8939 $tz = 'Z';
8940 $datetime = trim( substr( $datetime, 0, ( $len - 1 )));
8941 $tzSts = TRUE;
8942 $len = 88;
8943 }
8944 if( iCalUtilityFunctions::_isOffset( substr( $datetime, -5, 5 ))) { // [+/-]NNNN offset
8945 $tz = substr( $datetime, -5, 5 );
8946 $datetime = trim( substr( $datetime, 0, ($len - 5)));
8947 $len = strlen( $datetime );
8948 }
8949 elseif( iCalUtilityFunctions::_isOffset( substr( $datetime, -7, 7 ))) { // [+/-]NNNNNN offset
8950 $tz = substr( $datetime, -7, 7 );
8951 $datetime = trim( substr( $datetime, 0, ($len - 7)));
8952 $len = strlen( $datetime );
8953 }
8954 elseif( empty( $wtz ) && ctype_digit( substr( $datetime, 0, 4 )) && ctype_digit( substr( $datetime, -2, 2 )) && iCalUtilityFunctions::_strDate2arr( $datetime )) {
8955 $output = $datetime;
8956 if( !empty( $tz ))
8957 $output['tz'] = 'Z';
8958 $output['unparsedtext'] = $unparseddatetime;
8959 return $output;
8960 }
8961 else {
8962 $cx = $tx = 0; // find any trailing timezone or offset
8963 for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) {
8964 $char = substr( $datetime, $cx, 1 );
8965 if(( ' ' == $char) || ctype_digit( $char ))
8966 break; // if exists, tz ends here.. . ?
8967 else
8968 $tx--; // tz length counter
8969 }
8970 if( 0 > $tx ) { // if any
8971 $tz = substr( $datetime, $tx );
8972 $datetime = trim( substr( $datetime, 0, $len + $tx ));
8973 $len = strlen( $datetime );
8974 }
8975 if(( 17 <= $len ) || // long textual datetime
8976 ( ctype_digit( substr( $datetime, 0, 8 )) && ( 'T' == substr( $datetime, 8, 1 )) && ctype_digit( substr( $datetime, -6, 6 ))) ||
8977 ( ctype_digit( substr( $datetime, 0, 14 )))) {
8978 $len = 88;
8979 $tzSts = TRUE;
8980 }
8981 else
8982 $tz = null; // no tz for Y-m-d dates
8983 }
8984 if( empty( $tz ) && !empty( $wtz ))
8985 $tz = $wtz;
8986 if( 17 >= $len ) // any Y-m-d textual date
8987 $tz = null;
8988 if( !empty( $tz ) && ( 17 < $len )) { // tz set AND long textual datetime
8989 if(( 'Z' != $tz ) && ( iCalUtilityFunctions::_isOffset( $tz ))) {
8990 $offset = (string) iCalUtilityFunctions::_tz2offset( $tz ) * -1;
8991 $tz = 'UTC';
8992 $tzSts = TRUE;
8993 }
8994 elseif( !empty( $wtz ))
8995 $tzSts = TRUE;
8996 $tz = trim( $tz );
8997 if(( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz )))
8998 $tz = 'UTC';
8999 if( 0 < substr_count( $datetime, '-' ))
9000 $datetime = str_replace( '-', '/', $datetime );
9001 try {
9002 $d = new DateTime( $datetime, new DateTimeZone( $tz ));
9003 if( 0 != $offset ) // adjust for offset
9004 $d->modify( $offset.' seconds' );
9005 $datestring = $d->format( 'Y-m-d-H-i-s' );
9006 unset( $d );
9007 }
9008 catch( Exception $e ) {
9009 $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime ));
9010 }
9011 } // end if( !empty( $tz ) && ( 17 < $len ))
9012 else
9013 $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime ));
9014 // echo "<tr><td>&nbsp;<td colspan='3'>_strdate2date input=$datetime, tz=$tz, offset=$offset, wtz=$wtz, len=$len, prepDate=$datestring\n";
9015 if( 'UTC' == $tz )
9016 $tz = 'Z';
9017 $d = explode( '-', $datestring );
9018 $output = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2] );
9019 if((( FALSE !== $parno ) && ( 3 != $parno )) || // parno is set to 6 or 7
9020 (( FALSE === $parno ) && ( 'Z' == $tz )) || // parno is not set and UTC
9021 (( FALSE === $parno ) && ( 'Z' != $tz ) && ( 0 != $d[3] + $d[4] + $d[5] ) && ( 17 < $len ))) { // !parno and !UTC and 0 != hour+min+sec and long input text
9022 $output['hour'] = $d[3];
9023 $output['min'] = $d[4];
9024 $output['sec'] = $d[5];
9025 if(( $tzSts || ( 7 == $parno )) && !empty( $tz ))
9026 $output['tz'] = $tz;
9027 }
9028 // return original string in the array in case strtotime failed to make sense of it
9029 $output['unparsedtext'] = $unparseddatetime;
9030 return $output;
9031 }
9032 /********************************************************************************/
9033 /**
9034 * special characters management output
9035 *
9036 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9037 * @since 2.16.2 - 2012-12-18
9038 * @param string $string
9039 * @param string $format
9040 * @param string $nl
9041 * @return string
9042 */
9043 public static function _strrep( $string, $format, $nl ) {
9044 switch( $format ) {
9045 case 'xcal':
9046 $string = str_replace( '\n', $nl, $string);
9047 $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string ))));
9048 break;
9049 default:
9050 $pos = 0;
9051 $specChars = array( 'n', 'N', 'r', ',', ';' );
9052 while( isset( $string[$pos] )) {
9053 if( FALSE === ( $pos = strpos( $string, "\\", $pos )))
9054 break;
9055 if( !in_array( substr( $string, $pos, 1 ), $specChars )) {
9056 $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 ));
9057 $pos += 1;
9058 }
9059 $pos += 1;
9060 }
9061 if( FALSE !== strpos( $string, '"' ))
9062 $string = str_replace('"', "'", $string);
9063 if( FALSE !== strpos( $string, ',' ))
9064 $string = str_replace(',', '\,', $string);
9065 if( FALSE !== strpos( $string, ';' ))
9066 $string = str_replace(';', '\;', $string);
9067 if( FALSE !== strpos( $string, "\r\n" ))
9068 $string = str_replace( "\r\n", '\n', $string);
9069 elseif( FALSE !== strpos( $string, "\r" ))
9070 $string = str_replace( "\r", '\n', $string);
9071 elseif( FALSE !== strpos( $string, "\n" ))
9072 $string = str_replace( "\n", '\n', $string);
9073 if( FALSE !== strpos( $string, '\N' ))
9074 $string = str_replace( '\N', '\n', $string);
9075 // if( FALSE !== strpos( $string, $nl ))
9076 $string = str_replace( $nl, '\n', $string);
9077 break;
9078 }
9079 return $string;
9080 }
9081 /**
9082 * special characters management input (from iCal file)
9083 *
9084 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9085 * @since 2.16.2 - 2012-12-18
9086 * @param string $string
9087 * @return string
9088 */
9089 public static function _strunrep( $string ) {
9090 $string = str_replace( '\\\\', '\\', $string);
9091 $string = str_replace( '\,', ',', $string);
9092 $string = str_replace( '\;', ';', $string);
9093 // $string = str_replace( '\n', $nl, $string); // ??
9094 return $string;
9095 }
9096 /**
9097 * convert timestamp to date array, default UTC or adjusted for offset/timezone
9098 *
9099 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9100 * @since 2.15.1 - 2012-10-17
9101 * @param mixed $timestamp
9102 * @param int $parno
9103 * @param string $wtz
9104 * @return array
9105 */
9106 public static function _timestamp2date( $timestamp, $parno=6, $wtz=null ) {
9107 if( is_array( $timestamp )) {
9108 $tz = ( isset( $timestamp['tz'] )) ? $timestamp['tz'] : $wtz;
9109 $timestamp = $timestamp['timestamp'];
9110 }
9111 $tz = ( isset( $tz )) ? $tz : $wtz;
9112 if( empty( $tz ) || ( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz )))
9113 $tz = 'UTC';
9114 elseif( iCalUtilityFunctions::_isOffset( $tz )) {
9115 $offset = iCalUtilityFunctions::_tz2offset( $tz );
9116 $tz = 'UTC';
9117 }
9118 try {
9119 $d = new DateTime( "@$timestamp" ); // set UTC date
9120 if( isset( $offset ) && ( 0 != $offset )) // adjust for offset
9121 $d->modify( $offset.' seconds' );
9122 elseif( 'UTC' != $tz )
9123 $d->setTimezone( new DateTimeZone( $tz )); // convert to local date
9124 $date = $d->format( 'Y-m-d-H-i-s' );
9125 unset( $d );
9126 }
9127 catch( Exception $e ) {
9128 $date = date( 'Y-m-d-H-i-s', $timestamp );
9129 }
9130 $date = explode( '-', $date );
9131 $output = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2] );
9132 if( 3 != $parno ) {
9133 $output['hour'] = $date[3];
9134 $output['min'] = $date[4];
9135 $output['sec'] = $date[5];
9136 if( 'UTC' == $tz && ( !isset( $offset ) || ( 0 == $offset )))
9137 $output['tz'] = 'Z';
9138 }
9139 return $output;
9140 }
9141 /**
9142 * convert timestamp (seconds) to duration in array format
9143 *
9144 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9145 * @since 2.6.23 - 2010-10-23
9146 * @param int $timestamp
9147 * @return array, duration format
9148 */
9149 public static function _timestamp2duration( $timestamp ) {
9150 $dur = array();
9151 $dur['week'] = (int) floor( $timestamp / ( 7 * 24 * 60 * 60 ));
9152 $timestamp = $timestamp % ( 7 * 24 * 60 * 60 );
9153 $dur['day'] = (int) floor( $timestamp / ( 24 * 60 * 60 ));
9154 $timestamp = $timestamp % ( 24 * 60 * 60 );
9155 $dur['hour'] = (int) floor( $timestamp / ( 60 * 60 ));
9156 $timestamp = $timestamp % ( 60 * 60 );
9157 $dur['min'] = (int) floor( $timestamp / ( 60 ));
9158 $dur['sec'] = (int) $timestamp % ( 60 );
9159 return $dur;
9160 }
9161 /**
9162 * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0)
9163 *
9164 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9165 * @since 2.15.1 - 2012-10-17
9166 * @param mixed $date, date to alter
9167 * @param string $tzFrom, PHP valid 'from' timezone
9168 * @param string $tzTo, PHP valid 'to' timezone, default 'UTC'
9169 * @param string $format, date output format, default 'Ymd\THis'
9170 * @return bool
9171 */
9172 public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) {
9173 if( is_array( $date ) && isset( $date['timestamp'] )) {
9174 try {
9175 $d = new DateTime( "@{$date['timestamp']}" ); // set UTC date
9176 $d->setTimezone(new DateTimeZone( $tzFrom )); // convert to 'from' date
9177 }
9178 catch( Exception $e ) { return FALSE; }
9179 }
9180 else {
9181 if( iCalUtilityFunctions::_isArrayDate( $date )) {
9182 if( isset( $date['tz'] ))
9183 unset( $date['tz'] );
9184 $date = iCalUtilityFunctions::_date2strdate( iCalUtilityFunctions::_chkDateArr( $date ));
9185 }
9186 if( 'Z' == substr( $date, -1 ))
9187 $date = substr( $date, 0, ( strlen( $date ) - 2 ));
9188 try { $d = new DateTime( $date, new DateTimeZone( $tzFrom )); }
9189 catch( Exception $e ) { return FALSE; }
9190 }
9191 try { $d->setTimezone( new DateTimeZone( $tzTo )); }
9192 catch( Exception $e ) { return FALSE; }
9193 $date = $d->format( $format );
9194 return TRUE;
9195 }
9196 /**
9197 * convert offset, [+/-]HHmm[ss], to seconds, used when correcting UTC to localtime or v.v.
9198 *
9199 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9200 * @since 2.11.4 - 2012-01-11
9201 * @param string $offset
9202 * @return integer
9203 */
9204 public static function _tz2offset( $tz ) {
9205 $tz = trim( (string) $tz );
9206 $offset = 0;
9207 if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) ||
9208 (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) ||
9209 (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) ||
9210 (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 ))))
9211 return $offset;
9212 $hours2sec = (int) substr( $tz, 1, 2 ) * 3600;
9213 $min2sec = (int) substr( $tz, 3, 2 ) * 60;
9214 $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00';
9215 $offset = $hours2sec + $min2sec + $sec;
9216 $offset = ('-' == substr( $tz, 0, 1 )) ? $offset * -1 : $offset;
9217 return $offset;
9218 }
9219 }
9220 /*********************************************************************************/
9221 /* iCalcreator vCard helper functions */
9222 /*********************************************************************************/
9223 /**
9224 * convert single ATTENDEE, CONTACT or ORGANIZER (in email format) to vCard
9225 * returns vCard/TRUE or if directory (if set) or file write is unvalid, FALSE
9226 *
9227 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9228 * @since 2.12.2 - 2012-07-11
9229 * @param object $email
9230 * $param string $version, vCard version (default 2.1)
9231 * $param string $directory, where to save vCards (default FALSE)
9232 * $param string $ext, vCard file extension (default 'vcf')
9233 * @return mixed
9234 */
9235 function iCal2vCard( $email, $version='2.1', $directory=FALSE, $ext='vcf' ) {
9236 if( FALSE === ( $pos = strpos( $email, '@' )))
9237 return FALSE;
9238 if( $directory ) {
9239 if( DIRECTORY_SEPARATOR != substr( $directory, ( 0 - strlen( DIRECTORY_SEPARATOR ))))
9240 $directory .= DIRECTORY_SEPARATOR;
9241 if( !is_dir( $directory ) || !is_writable( $directory ))
9242 return FALSE;
9243 }
9244 /* prepare vCard */
9245 $email = str_replace( 'MAILTO:', '', $email );
9246 $name = $person = substr( $email, 0, $pos );
9247 if( ctype_upper( $name ) || ctype_lower( $name ))
9248 $name = array( $name );
9249 else {
9250 if( FALSE !== ( $pos = strpos( $name, '.' ))) {
9251 $name = explode( '.', $name );
9252 foreach( $name as $k => $part )
9253 $name[$k] = ucfirst( $part );
9254 }
9255 else { // split camelCase
9256 $chars = $name;
9257 $name = array( $chars[0] );
9258 $k = 0;
9259 $x = 1;
9260 while( FALSE !== ( $char = substr( $chars, $x, 1 ))) {
9261 if( ctype_upper( $char )) {
9262 $k += 1;
9263 $name[$k] = '';
9264 }
9265 $name[$k] .= $char;
9266 $x++;
9267 }
9268 }
9269 }
9270 $nl = "\r\n";
9271 $FN = 'FN:'.implode( ' ', $name ).$nl;
9272 $name = array_reverse( $name );
9273 $N = 'N:'.array_shift( $name );
9274 $scCnt = 0;
9275 while( NULL != ( $part = array_shift( $name ))) {
9276 if(( '4.0' != $version ) || ( 4 > $scCnt ))
9277 $scCnt += 1;
9278 $N .= ';'.$part;
9279 }
9280 while(( '4.0' == $version ) && ( 4 > $scCnt )) {
9281 $N .= ';';
9282 $scCnt += 1;
9283 }
9284 $N .= $nl;
9285 $EMAIL = 'EMAIL:'.$email.$nl;
9286 /* create vCard */
9287 $vCard = 'BEGIN:VCARD'.$nl;
9288 $vCard .= "VERSION:$version$nl";
9289 $vCard .= 'PRODID:-//kigkonsult.se '.ICALCREATOR_VERSION."//$nl";
9290 $vCard .= $N;
9291 $vCard .= $FN;
9292 $vCard .= $EMAIL;
9293 $vCard .= 'REV:'.gmdate( 'Ymd\THis\Z' ).$nl;
9294 $vCard .= 'END:VCARD'.$nl;
9295 /* save each vCard as (unique) single file */
9296 if( $directory ) {
9297 $fname = $directory.preg_replace( '/[^a-z0-9.]/i', '', $email );
9298 $cnt = 1;
9299 $dbl = '';
9300 while( is_file ( $fname.$dbl.'.'.$ext )) {
9301 $cnt += 1;
9302 $dbl = "_$cnt";
9303 }
9304 if( FALSE === file_put_contents( $fname, $fname.$dbl.'.'.$ext ))
9305 return FALSE;
9306 return TRUE;
9307 }
9308 /* return vCard */
9309 else
9310 return $vCard;
9311 }
9312 /**
9313 * convert ATTENDEEs, CONTACTs and ORGANIZERs (in email format) to vCards
9314 *
9315 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9316 * @since 2.12.2 - 2012-05-07
9317 * @param object $calendar, iCalcreator vcalendar instance reference
9318 * $param string $version, vCard version (default 2.1)
9319 * $param string $directory, where to save vCards (default FALSE)
9320 * $param string $ext, vCard file extension (default 'vcf')
9321 * @return mixed
9322 */
9323 function iCal2vCards( & $calendar, $version='2.1', $directory=FALSE, $ext='vcf' ) {
9324 $hits = array();
9325 $vCardP = array( 'ATTENDEE', 'CONTACT', 'ORGANIZER' );
9326 foreach( $vCardP as $prop ) {
9327 $hits2 = $calendar->getProperty( $prop );
9328 foreach( $hits2 as $propValue => $occCnt ) {
9329 if( FALSE === ( $pos = strpos( $propValue, '@' )))
9330 continue;
9331 $propValue = str_replace( 'MAILTO:', '', $propValue );
9332 if( isset( $hits[$propValue] ))
9333 $hits[$propValue] += $occCnt;
9334 else
9335 $hits[$propValue] = $occCnt;
9336 }
9337 }
9338 if( empty( $hits ))
9339 return FALSE;
9340 ksort( $hits );
9341 $output = '';
9342 foreach( $hits as $email => $skip ) {
9343 $res = iCal2vCard( $email, $version, $directory, $ext );
9344 if( $directory && !$res )
9345 return FALSE;
9346 elseif( !$res )
9347 return $res;
9348 else
9349 $output .= $res;
9350 }
9351 if( $directory )
9352 return TRUE;
9353 if( !empty( $output ))
9354 return $output;
9355 return FALSE;
9356 }
9357 /*********************************************************************************/
9358 /* iCalcreator XML (rfc6321) helper functions */
9359 /*********************************************************************************/
9360 /**
9361 * format iCal XML output, rfc6321, using PHP SimpleXMLElement
9362 *
9363 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9364 * @since 2.15.6 - 2012-10-19
9365 * @param object $calendar, iCalcreator vcalendar instance reference
9366 * @return string
9367 */
9368 function iCal2XML( & $calendar ) {
9369 /** fix an SimpleXMLElement instance and create root element */
9370 $xmlstr = '<?xml version="1.0" encoding="utf-8"?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0">';
9371 $xmlstr .= '<!-- created utilizing kigkonsult.se '.ICALCREATOR_VERSION.' iCal2XMl (rfc6321) -->';
9372 $xmlstr .= '</icalendar>';
9373 $xml = new SimpleXMLElement( $xmlstr );
9374 $vcalendar = $xml->addChild( 'vcalendar' );
9375 /** fix calendar properties */
9376 $properties = $vcalendar->addChild( 'properties' );
9377 $calProps = array( 'prodid', 'version', 'calscale', 'method' );
9378 foreach( $calProps as $calProp ) {
9379 if( FALSE !== ( $content = $calendar->getProperty( $calProp )))
9380 _addXMLchild( $properties, $calProp, 'text', $content );
9381 }
9382 while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE )))
9383 _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
9384 $langCal = $calendar->getConfig( 'language' );
9385 /** prepare to fix components with properties */
9386 $components = $vcalendar->addChild( 'components' );
9387 $comps = array( 'vtimezone', 'vevent', 'vtodo', 'vjournal', 'vfreebusy' );
9388 foreach( $comps as $compName ) {
9389 switch( $compName ) {
9390 case 'vevent':
9391 case 'vtodo':
9392 $subComps = array( 'valarm' );
9393 break;
9394 case 'vjournal':
9395 case 'vfreebusy':
9396 $subComps = array();
9397 break;
9398 case 'vtimezone':
9399 $subComps = array( 'standard', 'daylight' );
9400 break;
9401 } // end switch( $compName )
9402 /** fix component properties */
9403 while( FALSE !== ( $component = $calendar->getComponent( $compName ))) {
9404 $child = $components->addChild( $compName );
9405 $properties = $child->addChild( 'properties' );
9406 $langComp = $component->getConfig( 'language' );
9407 $props = $component->getConfig( 'setPropertyNames' );
9408 foreach( $props as $prop ) {
9409 switch( strtolower( $prop )) {
9410 case 'attach': // may occur multiple times, below
9411 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9412 $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri';
9413 unset( $content['params']['VALUE'] );
9414 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9415 }
9416 break;
9417 case 'attendee':
9418 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9419 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
9420 if( $langComp )
9421 $content['params']['LANGUAGE'] = $langComp;
9422 elseif( $langCal )
9423 $content['params']['LANGUAGE'] = $langCal;
9424 }
9425 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
9426 }
9427 break;
9428 case 'exdate':
9429 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9430 $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time';
9431 unset( $content['params']['VALUE'] );
9432 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9433 }
9434 break;
9435 case 'freebusy':
9436 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9437 if( is_array( $content ) && isset( $content['value']['fbtype'] )) {
9438 $content['params']['FBTYPE'] = $content['value']['fbtype'];
9439 unset( $content['value']['fbtype'] );
9440 }
9441 _addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] );
9442 }
9443 break;
9444 case 'request-status':
9445 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9446 if( !isset( $content['params']['LANGUAGE'] )) {
9447 if( $langComp )
9448 $content['params']['LANGUAGE'] = $langComp;
9449 elseif( $langCal )
9450 $content['params']['LANGUAGE'] = $langCal;
9451 }
9452 _addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] );
9453 }
9454 break;
9455 case 'rdate':
9456 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9457 $type = 'date-time';
9458 if( isset( $content['params']['VALUE'] )) {
9459 if( 'DATE' == $content['params']['VALUE'] )
9460 $type = 'date';
9461 elseif( 'PERIOD' == $content['params']['VALUE'] )
9462 $type = 'period';
9463 }
9464 unset( $content['params']['VALUE'] );
9465 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9466 }
9467 break;
9468 case 'categories':
9469 case 'comment':
9470 case 'contact':
9471 case 'description':
9472 case 'related-to':
9473 case 'resources':
9474 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9475 if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
9476 if( $langComp )
9477 $content['params']['LANGUAGE'] = $langComp;
9478 elseif( $langCal )
9479 $content['params']['LANGUAGE'] = $langCal;
9480 }
9481 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
9482 }
9483 break;
9484 case 'x-prop':
9485 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9486 _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
9487 break;
9488 case 'created': // single occurence below, if set
9489 case 'completed':
9490 case 'dtstamp':
9491 case 'last-modified':
9492 $utcDate = TRUE;
9493 case 'dtstart':
9494 case 'dtend':
9495 case 'due':
9496 case 'recurrence-id':
9497 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9498 $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time';
9499 unset( $content['params']['VALUE'] );
9500 if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] ))
9501 unset( $content['params']['TZID'] );
9502 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9503 }
9504 unset( $utcDate );
9505 break;
9506 case 'duration':
9507 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9508 if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] ))
9509 $content['params']['RELATED'] = 'END';
9510 _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
9511 }
9512 break;
9513 case 'rrule':
9514 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9515 _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
9516 break;
9517 case 'class':
9518 case 'location':
9519 case 'status':
9520 case 'summary':
9521 case 'transp':
9522 case 'tzid':
9523 case 'uid':
9524 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9525 if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) {
9526 if( $langComp )
9527 $content['params']['LANGUAGE'] = $langComp;
9528 elseif( $langCal )
9529 $content['params']['LANGUAGE'] = $langCal;
9530 }
9531 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
9532 }
9533 break;
9534 case 'geo':
9535 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9536 _addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] );
9537 break;
9538 case 'organizer':
9539 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9540 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
9541 if( $langComp )
9542 $content['params']['LANGUAGE'] = $langComp;
9543 elseif( $langCal )
9544 $content['params']['LANGUAGE'] = $langCal;
9545 }
9546 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
9547 }
9548 break;
9549 case 'percent-complete':
9550 case 'priority':
9551 case 'sequence':
9552 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9553 _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
9554 break;
9555 case 'tzurl':
9556 case 'url':
9557 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9558 _addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] );
9559 break;
9560 } // end switch( $prop )
9561 } // end foreach( $props as $prop )
9562 /** fix subComponent properties, if any */
9563 foreach( $subComps as $subCompName ) {
9564 while( FALSE !== ( $subcomp = $component->getComponent( $subCompName ))) {
9565 $child2 = $child->addChild( $subCompName );
9566 $properties = $child2->addChild( 'properties' );
9567 $langComp = $subcomp->getConfig( 'language' );
9568 $subCompProps = $subcomp->getConfig( 'setPropertyNames' );
9569 foreach( $subCompProps as $prop ) {
9570 switch( strtolower( $prop )) {
9571 case 'attach': // may occur multiple times, below
9572 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9573 $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri';
9574 unset( $content['params']['VALUE'] );
9575 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9576 }
9577 break;
9578 case 'attendee':
9579 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9580 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
9581 if( $langComp )
9582 $content['params']['LANGUAGE'] = $langComp;
9583 elseif( $langCal )
9584 $content['params']['LANGUAGE'] = $langCal;
9585 }
9586 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
9587 }
9588 break;
9589 case 'comment':
9590 case 'tzname':
9591 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9592 if( !isset( $content['params']['LANGUAGE'] )) {
9593 if( $langComp )
9594 $content['params']['LANGUAGE'] = $langComp;
9595 elseif( $langCal )
9596 $content['params']['LANGUAGE'] = $langCal;
9597 }
9598 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
9599 }
9600 break;
9601 case 'rdate':
9602 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9603 $type = 'date-time';
9604 if( isset( $content['params']['VALUE'] )) {
9605 if( 'DATE' == $content['params']['VALUE'] )
9606 $type = 'date';
9607 elseif( 'PERIOD' == $content['params']['VALUE'] )
9608 $type = 'period';
9609 }
9610 unset( $content['params']['VALUE'] );
9611 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9612 }
9613 break;
9614 case 'x-prop':
9615 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9616 _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
9617 break;
9618 case 'action': // single occurence below, if set
9619 case 'description':
9620 case 'summary':
9621 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9622 if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
9623 if( $langComp )
9624 $content['params']['LANGUAGE'] = $langComp;
9625 elseif( $langCal )
9626 $content['params']['LANGUAGE'] = $langCal;
9627 }
9628 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
9629 }
9630 break;
9631 case 'dtstart':
9632 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9633 unset( $content['value']['tz'], $content['params']['VALUE'] ); // always local time
9634 _addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] );
9635 }
9636 break;
9637 case 'duration':
9638 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9639 _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
9640 break;
9641 case 'repeat':
9642 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9643 _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
9644 break;
9645 case 'trigger':
9646 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9647 if( isset( $content['value']['year'] ) &&
9648 isset( $content['value']['month'] ) &&
9649 isset( $content['value']['day'] ))
9650 $type = 'date-time';
9651 else {
9652 $type = 'duration';
9653 if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] ))
9654 $content['params']['RELATED'] = 'END';
9655 }
9656 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9657 }
9658 break;
9659 case 'tzoffsetto':
9660 case 'tzoffsetfrom':
9661 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9662 _addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] );
9663 break;
9664 case 'rrule':
9665 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9666 _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
9667 break;
9668 } // switch( $prop )
9669 } // end foreach( $subCompProps as $prop )
9670 } // end while( FALSE !== ( $subcomp = $component->getComponent( subCompName )))
9671 } // end foreach( $subCombs as $subCompName )
9672 } // end while( FALSE !== ( $component = $calendar->getComponent( $compName )))
9673 } // end foreach( $comps as $compName)
9674 return $xml->asXML();
9675 }
9676 /**
9677 * Add children to a SimpleXMLelement
9678 *
9679 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9680 * @since 2.15.5 - 2012-10-19
9681 * @param object $parent, reference to a SimpleXMLelement node
9682 * @param string $name, new element node name
9683 * @param string $type, content type, subelement(-s) name
9684 * @param string $content, new subelement content
9685 * @param array $params, new element 'attributes'
9686 * @return void
9687 */
9688 function _addXMLchild( & $parent, $name, $type, $content, $params=array()) {
9689 /** create new child node */
9690 $name = strtolower( $name );
9691 $child = $parent->addChild( $name );
9692 if( isset( $params['VALUE'] ))
9693 unset( $params['VALUE'] );
9694 if( !empty( $params )) {
9695 $parameters = $child->addChild( 'parameters' );
9696 foreach( $params as $param => $parVal ) {
9697 $param = strtolower( $param );
9698 if( 'x-' == substr( $param, 0, 2 )) {
9699 $p1 = $parameters->addChild( $param );
9700 $p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal ));
9701 }
9702 else {
9703 $p1 = $parameters->addChild( $param );
9704 switch( $param ) {
9705 case 'altrep':
9706 case 'dir': $ptype = 'uri'; break;
9707 case 'delegated-from':
9708 case 'delegated-to':
9709 case 'member':
9710 case 'sent-by': $ptype = 'cal-address'; break;
9711 case 'rsvp': $ptype = 'boolean'; break ;
9712 default: $ptype = 'text'; break;
9713 }
9714 if( is_array( $parVal )) {
9715 foreach( $parVal as $pV )
9716 $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV ));
9717 }
9718 else
9719 $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal ));
9720 }
9721 }
9722 }
9723 if( empty( $content ) && ( '0' != $content ))
9724 return;
9725 /** store content */
9726 switch( $type ) {
9727 case 'binary':
9728 $v = $child->addChild( $type, $content );
9729 break;
9730 case 'boolean':
9731 break;
9732 case 'cal-address':
9733 $v = $child->addChild( $type, $content );
9734 break;
9735 case 'date':
9736 if( array_key_exists( 'year', $content ))
9737 $content = array( $content );
9738 foreach( $content as $date ) {
9739 $str = sprintf( '%04d-%02d-%02d', $date['year'], $date['month'], $date['day'] );
9740 $v = $child->addChild( $type, $str );
9741 }
9742 break;
9743 case 'date-time':
9744 if( array_key_exists( 'year', $content ))
9745 $content = array( $content );
9746 foreach( $content as $dt ) {
9747 if( !isset( $dt['hour'] )) $dt['hour'] = 0;
9748 if( !isset( $dt['min'] )) $dt['min'] = 0;
9749 if( !isset( $dt['sec'] )) $dt['sec'] = 0;
9750 $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] );
9751 if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] ))
9752 $str .= 'Z';
9753 $v = $child->addChild( $type, $str );
9754 }
9755 break;
9756 case 'duration':
9757 $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : '';
9758 $v = $child->addChild( $type, $output.iCalUtilityFunctions::_duration2str( $content ) );
9759 break;
9760 case 'geo':
9761 $v1 = $child->addChild( 'latitude', number_format( (float) $content['latitude'], 6, '.', '' ));
9762 $v1 = $child->addChild( 'longitude', number_format( (float) $content['longitude'], 6, '.', '' ));
9763 break;
9764 case 'integer':
9765 $v = $child->addChild( $type, $content );
9766 break;
9767 case 'period':
9768 if( !is_array( $content ))
9769 break;
9770 foreach( $content as $period ) {
9771 $v1 = $child->addChild( $type );
9772 $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $period[0]['year'], $period[0]['month'], $period[0]['day'], $period[0]['hour'], $period[0]['min'], $period[0]['sec'] );
9773 if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] ))
9774 $str .= 'Z';
9775 $v2 = $v1->addChild( 'start', $str );
9776 if( array_key_exists( 'year', $period[1] )) {
9777 $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $period[1]['year'], $period[1]['month'], $period[1]['day'], $period[1]['hour'], $period[1]['min'], $period[1]['sec'] );
9778 if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] ))
9779 $str .= 'Z';
9780 $v2 = $v1->addChild( 'end', $str );
9781 }
9782 else
9783 $v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_duration2str( $period[1] ));
9784 }
9785 break;
9786 case 'recur':
9787 foreach( $content as $rulelabel => $rulevalue ) {
9788 $rulelabel = strtolower( $rulelabel );
9789 switch( $rulelabel ) {
9790 case 'until':
9791 if( isset( $rulevalue['hour'] ))
9792 $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02dZ', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'], $rulevalue['hour'], $rulevalue['min'], $rulevalue['sec'] );
9793 else
9794 $str = sprintf( '%04d-%02d-%02d', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'] );
9795 $v = $child->addChild( $rulelabel, $str );
9796 break;
9797 case 'bysecond':
9798 case 'byminute':
9799 case 'byhour':
9800 case 'bymonthday':
9801 case 'byyearday':
9802 case 'byweekno':
9803 case 'bymonth':
9804 case 'bysetpos': {
9805 if( is_array( $rulevalue )) {
9806 foreach( $rulevalue as $vix => $valuePart )
9807 $v = $child->addChild( $rulelabel, $valuePart );
9808 }
9809 else
9810 $v = $child->addChild( $rulelabel, $rulevalue );
9811 break;
9812 }
9813 case 'byday': {
9814 if( isset( $rulevalue['DAY'] )) {
9815 $str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : '';
9816 $str .= $rulevalue['DAY'];
9817 $p = $child->addChild( $rulelabel, $str );
9818 }
9819 else {
9820 foreach( $rulevalue as $valuePart ) {
9821 if( isset( $valuePart['DAY'] )) {
9822 $str = ( isset( $valuePart[0] )) ? $valuePart[0] : '';
9823 $str .= $valuePart['DAY'];
9824 $p = $child->addChild( $rulelabel, $str );
9825 }
9826 else
9827 $p = $child->addChild( $rulelabel, $valuePart );
9828 }
9829 }
9830 break;
9831 }
9832 case 'freq':
9833 case 'count':
9834 case 'interval':
9835 case 'wkst':
9836 default:
9837 $p = $child->addChild( $rulelabel, $rulevalue );
9838 break;
9839 } // end switch( $rulelabel )
9840 } // end foreach( $content as $rulelabel => $rulevalue )
9841 break;
9842 case 'rstatus':
9843 $v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', ''));
9844 $v = $child->addChild( 'description', htmlspecialchars( $content['text'] ));
9845 if( isset( $content['extdata'] ))
9846 $v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] ));
9847 break;
9848 case 'text':
9849 if( !is_array( $content ))
9850 $content = array( $content );
9851 foreach( $content as $part )
9852 $v = $child->addChild( $type, htmlspecialchars( $part ));
9853 break;
9854 case 'time':
9855 break;
9856 case 'uri':
9857 $v = $child->addChild( $type, $content );
9858 break;
9859 case 'utc-offset':
9860 if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) {
9861 $str = substr( $content, 0, 1 );
9862 $content = substr( $content, 1 );
9863 }
9864 else
9865 $str = '+';
9866 $str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 );
9867 if( 4 < strlen( $content ))
9868 $str .= ':'.substr( $content, 4 );
9869 $v = $child->addChild( $type, $str );
9870 break;
9871 case 'unknown':
9872 default:
9873 if( is_array( $content ))
9874 $content = implode( '', $content );
9875 $v = $child->addChild( 'unknown', htmlspecialchars( $content ));
9876 break;
9877 }
9878 }
9879 /**
9880 * parse xml string into iCalcreator instance
9881 *
9882 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9883 * @since 2.11.2 - 2012-01-31
9884 * @param string $xmlstr
9885 * @param array $iCalcfg iCalcreator config array (opt)
9886 * @return mixed iCalcreator instance or FALSE on error
9887 */
9888 function & XMLstr2iCal( $xmlstr, $iCalcfg=array()) {
9889 libxml_use_internal_errors( TRUE );
9890 $xml = simplexml_load_string( $xmlstr );
9891 if( !$xml ) {
9892 $str = '';
9893 $return = FALSE;
9894 foreach( libxml_get_errors() as $error ) {
9895 switch ( $error->level ) {
9896 case LIBXML_ERR_FATAL: $str .= ' FATAL '; break;
9897 case LIBXML_ERR_ERROR: $str .= ' ERROR '; break;
9898 case LIBXML_ERR_WARNING:
9899 default: $str .= ' WARNING '; break;
9900 }
9901 $str .= PHP_EOL.'Error when loading XML';
9902 if( !empty( $error->file ))
9903 $str .= ', file:'.$error->file.', ';
9904 $str .= ', line:'.$error->line;
9905 $str .= ', ('.$error->code.') '.$error->message;
9906 }
9907 error_log( $str );
9908 if( LIBXML_ERR_WARNING != $error->level )
9909 return $return;
9910 libxml_clear_errors();
9911 }
9912 return xml2iCal( $xml, $iCalcfg );
9913 }
9914 /**
9915 * parse xml file into iCalcreator instance
9916 *
9917 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9918 * @since 2.11.2 - 2012-01-20
9919 * @param string $xmlfile
9920 * @param array$iCalcfg iCalcreator config array (opt)
9921 * @return mixediCalcreator instance or FALSE on error
9922 */
9923 function & XMLfile2iCal( $xmlfile, $iCalcfg=array()) {
9924 libxml_use_internal_errors( TRUE );
9925 $xml = simplexml_load_file( $xmlfile );
9926 if( !$xml ) {
9927 $str = '';
9928 foreach( libxml_get_errors() as $error ) {
9929 switch ( $error->level ) {
9930 case LIBXML_ERR_FATAL: $str .= 'FATAL '; break;
9931 case LIBXML_ERR_ERROR: $str .= 'ERROR '; break;
9932 case LIBXML_ERR_WARNING:
9933 default: $str .= 'WARNING '; break;
9934 }
9935 $str .= 'Failed loading XML'.PHP_EOL;
9936 if( !empty( $error->file ))
9937 $str .= ' file:'.$error->file.', ';
9938 $str .= 'line:'.$error->line.PHP_EOL;
9939 $str .= '('.$error->code.') '.$error->message.PHP_EOL;
9940 }
9941 error_log( $str );
9942 if( LIBXML_ERR_WARNING != $error->level )
9943 return FALSE;
9944 libxml_clear_errors();
9945 }
9946 return xml2iCal( $xml, $iCalcfg );
9947 }
9948 /**
9949 * parse SimpleXMLElement instance into iCalcreator instance
9950 *
9951 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9952 * @since 2.11.2 - 2012-01-27
9953 * @param object $xmlobj SimpleXMLElement
9954 * @param array $iCalcfg iCalcreator config array (opt)
9955 * @return mixed iCalcreator instance or FALSE on error
9956 */
9957 function & XML2iCal( $xmlobj, $iCalcfg=array()) {
9958 $iCal = new vcalendar( $iCalcfg );
9959 foreach( $xmlobj->children() as $icalendar ) { // vcalendar
9960 foreach( $icalendar->children() as $calPart ) { // calendar properties and components
9961 if( 'components' == $calPart->getName()) {
9962 foreach( $calPart->children() as $component ) { // single components
9963 if( 0 < $component->count())
9964 _getXMLComponents( $iCal, $component );
9965 }
9966 }
9967 elseif(( 'properties' == $calPart->getName()) && ( 0 < $calPart->count())) {
9968 foreach( $calPart->children() as $calProp ) { // calendar properties
9969 $propName = $calProp->getName();
9970 if(( 'calscale' != $propName ) && ( 'method' != $propName ) && ( 'x-' != substr( $propName,0,2 )))
9971 continue;
9972 $params = array();
9973 foreach( $calProp->children() as $calPropElem ) { // single calendar property
9974 if( 'parameters' == $calPropElem->getName())
9975 $params = _getXMLParams( $calPropElem );
9976 else
9977 $iCal->setProperty( $propName, reset( $calPropElem ), $params );
9978 } // end foreach( $calProp->children() as $calPropElem )
9979 } // end foreach( $calPart->properties->children() as $calProp )
9980 } // end if( 0 < $calPart->properties->count())
9981 } // end foreach( $icalendar->children() as $calPart )
9982 } // end foreach( $xmlobj->children() as $icalendar )
9983 return $iCal;
9984 }
9985 /**
9986 * parse SimpleXMLElement instance property parameters and return iCalcreator property parameter array
9987 *
9988 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9989 * @since 2.11.2 - 2012-01-15
9990 * @param object $parameters SimpleXMLElement
9991 * @return array iCalcreator property parameter array
9992 */
9993 function _getXMLParams( & $parameters ) {
9994 if( 1 > $parameters->count())
9995 return array();
9996 $params = array();
9997 foreach( $parameters->children() as $parameter ) { // single parameter key
9998 $key = strtoupper( $parameter->getName());
9999 $value = array();
10000 foreach( $parameter->children() as $paramValue ) // skip parameter value type
10001 $value[] = reset( $paramValue );
10002 if( 2 > count( $value ))
10003 $params[$key] = html_entity_decode( reset( $value ));
10004 else
10005 $params[$key] = $value;
10006 }
10007 return $params;
10008 }
10009 /**
10010 * parse SimpleXMLElement instance components, create iCalcreator component and update
10011 *
10012 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
10013 * @since 2.11.2 - 2012-01-15
10014 * @param array $iCal iCalcreator calendar instance
10015 * @param object $component SimpleXMLElement
10016 * @return void
10017 */
10018 function _getXMLComponents( & $iCal, & $component ) {
10019 $compName = $component->getName();
10020 $comp = & $iCal->newComponent( $compName );
10021 $subComponents = array( 'valarm', 'standard', 'daylight' );
10022 foreach( $component->children() as $compPart ) { // properties and (opt) subComponents
10023 if( 1 > $compPart->count())
10024 continue;
10025 if( in_array( $compPart->getName(), $subComponents ))
10026 _getXMLComponents( $comp, $compPart );
10027 elseif( 'properties' == $compPart->getName()) {
10028 foreach( $compPart->children() as $property ) // properties as single property
10029 _getXMLProperties( $comp, $property );
10030 }
10031 } // end foreach( $component->children() as $compPart )
10032 }
10033 /**
10034 * parse SimpleXMLElement instance property, create iCalcreator component property
10035 *
10036 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
10037 * @since 2.11.2 - 2012-01-27
10038 * @param array $iCal iCalcreator calendar instance
10039 * @param object $component SimpleXMLElement
10040 * @return void
10041 */
10042 function _getXMLProperties( & $iCal, & $property ) {
10043 $propName = $property->getName();
10044 $value = $params = array();
10045 $valueType = '';
10046 foreach( $property->children() as $propPart ) { // calendar property parameters (opt) and value(-s)
10047 $valueType = $propPart->getName();
10048 if( 'parameters' == $valueType) {
10049 $params = _getXMLParams( $propPart );
10050 continue;
10051 }
10052 switch( $valueType ) {
10053 case 'binary':
10054 $value = reset( $propPart );
10055 break;
10056 case 'boolean':
10057 break;
10058 case 'cal-address':
10059 $value = reset( $propPart );
10060 break;
10061 case 'date':
10062 $params['VALUE'] = 'DATE';
10063 case 'date-time':
10064 if(( 'exdate' == $propName ) || ( 'rdate' == $propName ))
10065 $value[] = reset( $propPart );
10066 else
10067 $value = reset( $propPart );
10068 break;
10069 case 'duration':
10070 $value = reset( $propPart );
10071 break;
10072 // case 'geo':
10073 case 'latitude':
10074 case 'longitude':
10075 $value[$valueType] = reset( $propPart );
10076 break;
10077 case 'integer':
10078 $value = reset( $propPart );
10079 break;
10080 case 'period':
10081 if( 'rdate' == $propName )
10082 $params['VALUE'] = 'PERIOD';
10083 $pData = array();
10084 foreach( $propPart->children() as $periodPart )
10085 $pData[] = reset( $periodPart );
10086 if( !empty( $pData ))
10087 $value[] = $pData;
10088 break;
10089 // case 'rrule':
10090 case 'freq':
10091 case 'count':
10092 case 'until':
10093 case 'interval':
10094 case 'wkst':
10095 $value[$valueType] = reset( $propPart );
10096 break;
10097 case 'bysecond':
10098 case 'byminute':
10099 case 'byhour':
10100 case 'bymonthday':
10101 case 'byyearday':
10102 case 'byweekno':
10103 case 'bymonth':
10104 case 'bysetpos':
10105 $value[$valueType][] = reset( $propPart );
10106 break;
10107 case 'byday':
10108 $byday = reset( $propPart );
10109 if( 2 == strlen( $byday ))
10110 $value[$valueType][] = array( 'DAY' => $byday );
10111 else {
10112 $day = substr( $byday, -2 );
10113 $key = substr( $byday, 0, ( strlen( $byday ) - 2 ));
10114 $value[$valueType][] = array( $key, 'DAY' => $day );
10115 }
10116 break;
10117 // case 'rstatus':
10118 case 'code':
10119 $value[0] = reset( $propPart );
10120 break;
10121 case 'description':
10122 $value[1] = reset( $propPart );
10123 break;
10124 case 'data':
10125 $value[2] = reset( $propPart );
10126 break;
10127 case 'text':
10128 $text = str_replace( array( "\r\n", "\n\r", "\r", "\n"), '\n', reset( $propPart ));
10129 $value['text'][] = html_entity_decode( $text );
10130 break;
10131 case 'time':
10132 break;
10133 case 'uri':
10134 $value = reset( $propPart );
10135 break;
10136 case 'utc-offset':
10137 $value = str_replace( ':', '', reset( $propPart ));
10138 break;
10139 case 'unknown':
10140 default:
10141 $value = html_entity_decode( reset( $propPart ));
10142 break;
10143 } // end switch( $valueType )
10144 } // end foreach( $property->children() as $propPart )
10145 if( 'freebusy' == $propName ) {
10146 $fbtype = $params['FBTYPE'];
10147 unset( $params['FBTYPE'] );
10148 $iCal->setProperty( $propName, $fbtype, $value, $params );
10149 }
10150 elseif( 'geo' == $propName )
10151 $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params );
10152 elseif( 'request-status' == $propName ) {
10153 if( !isset( $value[2] ))
10154 $value[2] = FALSE;
10155 $iCal->setProperty( $propName, $value[0], $value[1], $value[2], $params );
10156 }
10157 else {
10158 if( isset( $value['text'] ) && is_array( $value['text'] )) {
10159 if(( 'categories' == $propName ) || ( 'resources' == $propName ))
10160 $value = $value['text'];
10161 else
10162 $value = reset( $value['text'] );
10163 }
10164 $iCal->setProperty( $propName, $value, $params );
10165 }
10166 }
10167 /*********************************************************************************/
10168 /* Additional functions to use with vtimezone components */
10169 /*********************************************************************************/
10170 /**
10171 * For use with
10172 * iCalcreator (kigkonsult.se/iCalcreator/index.php)
10173 * copyright (c) 2011 Yitzchok Lavi
10174 * icalcreator@onebigsystem.com
10175 *
10176 * This library is free software; you can redistribute it and/or
10177 * modify it under the terms of the GNU Lesser General Public
10178 * License as published by the Free Software Foundation; either
10179 * version 2.1 of the License, or (at your option) any later version.
10180 *
10181 * This library is distributed in the hope that it will be useful,
10182 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10183 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10184 * Lesser General Public License for more details.
10185 *
10186 * You should have received a copy of the GNU Lesser General Public
10187 * License along with this library; if not, write to the Free Software
10188 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
10189 */
10190 /**
10191 * Additional functions to use with vtimezone components
10192 *
10193 * Before calling the functions, set time zone 'GMT' ('date_default_timezone_set')!
10194 *
10195 * @author Yitzchok Lavi <icalcreator@onebigsystem.com>
10196 * adjusted for iCalcreator Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
10197 * @version 1.0.2 - 2011-02-24
10198 *
10199 */
10200 /**
10201 * Returns array with the offset information from UTC for a (UTC) datetime/timestamp in the
10202 * timezone, according to the VTIMEZONE information in the input array.
10203 *
10204 * $param array $timezonesarray, output from function getTimezonesAsDateArrays (below)
10205 * $param string $tzid, time zone identifier
10206 * $param mixed $timestamp, timestamp or a UTC datetime (in array format)
10207 * @return array, time zone data with keys for 'offsetHis', 'offsetSec' and 'tzname'
10208 *
10209 */
10210 function getTzOffsetForDate($timezonesarray, $tzid, $timestamp) {
10211 if( is_array( $timestamp )) {
10212 //$disp = sprintf( '%04d%02d%02d %02d%02d%02d', $timestamp['year'], $timestamp['month'], $timestamp['day'], $timestamp['hour'], $timestamp['min'], $timestamp['sec'] ); // test ###
10213 $timestamp = gmmktime(
10214 $timestamp['hour'],
10215 $timestamp['min'],
10216 $timestamp['sec'],
10217 $timestamp['month'],
10218 $timestamp['day'],
10219 $timestamp['year']
10220 ) ;
10221 // echo '<td colspan="4">&nbsp;'."\n".'<tr><td>&nbsp;<td class="r">'.$timestamp.'<td class="r">'.$disp.'<td colspan="4">&nbsp;'."\n".'<tr><td colspan="3">&nbsp;'; // test ###
10222 }
10223 $tzoffset = array();
10224 // something to return if all goes wrong (such as if $tzid doesn't find us an array of dates)
10225 $tzoffset['offsetHis'] = '+0000';
10226 $tzoffset['offsetSec'] = 0;
10227 $tzoffset['tzname'] = '?';
10228 if( !isset( $timezonesarray[$tzid] ))
10229 return $tzoffset;
10230 $tzdatearray = $timezonesarray[$tzid];
10231 if ( is_array($tzdatearray) ) {
10232 sort($tzdatearray); // just in case
10233 if ( $timestamp < $tzdatearray[0]['timestamp'] ) {
10234 // our date is before the first change
10235 $tzoffset['offsetHis'] = $tzdatearray[0]['tzbefore']['offsetHis'] ;
10236 $tzoffset['offsetSec'] = $tzdatearray[0]['tzbefore']['offsetSec'] ;
10237 $tzoffset['tzname'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; // we don't know the tzname in this case
10238 } elseif ( $timestamp >= $tzdatearray[count($tzdatearray)-1]['timestamp'] ) {
10239 // our date is after the last change (we do this so our scan can stop at the last record but one)
10240 $tzoffset['offsetHis'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetHis'] ;
10241 $tzoffset['offsetSec'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetSec'] ;
10242 $tzoffset['tzname'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['tzname'] ;
10243 } else {
10244 // our date somewhere in between
10245 // loop through the list of dates and stop at the one where the timestamp is before our date and the next one is after it
10246 // we don't include the last date in our loop as there isn't one after it to check
10247 for ( $i = 0 ; $i <= count($tzdatearray)-2 ; $i++ ) {
10248 if(( $timestamp >= $tzdatearray[$i]['timestamp'] ) && ( $timestamp < $tzdatearray[$i+1]['timestamp'] )) {
10249 $tzoffset['offsetHis'] = $tzdatearray[$i]['tzafter']['offsetHis'] ;
10250 $tzoffset['offsetSec'] = $tzdatearray[$i]['tzafter']['offsetSec'] ;
10251 $tzoffset['tzname'] = $tzdatearray[$i]['tzafter']['tzname'] ;
10252 break;
10253 }
10254 }
10255 }
10256 }
10257 return $tzoffset;
10258 }
10259 /**
10260 * Returns an array containing all the timezone data in the vcalendar object
10261 *
10262 * @param object $vcalendar, iCalcreator calendar instance
10263 * @return array, time zone transition timestamp, array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname)
10264 * based on the timezone data in the vcalendar object
10265 *
10266 */
10267 function getTimezonesAsDateArrays($vcalendar) {
10268 $timezonedata = array();
10269 while( $vtz = $vcalendar->getComponent( 'vtimezone' )) {
10270 $tzid = $vtz->getProperty('tzid');
10271 $alltzdates = array();
10272 while ( $vtzc = $vtz->getComponent( 'standard' )) {
10273 $newtzdates = expandTimezoneDates($vtzc);
10274 $alltzdates = array_merge($alltzdates, $newtzdates);
10275 }
10276 while ( $vtzc = $vtz->getComponent( 'daylight' )) {
10277 $newtzdates = expandTimezoneDates($vtzc);
10278 $alltzdates = array_merge($alltzdates, $newtzdates);
10279 }
10280 sort($alltzdates);
10281 $timezonedata[$tzid] = $alltzdates;
10282 }
10283 return $timezonedata;
10284 }
10285 /**
10286 * Returns an array containing time zone data from vtimezone standard/daylight instances
10287 *
10288 * @param object $vtzc, an iCalcreator calendar standard/daylight instance
10289 * @return array, time zone data; array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname)
10290 *
10291 */
10292 function expandTimezoneDates($vtzc) {
10293 $tzdates = array();
10294 // prepare time zone "description" to attach to each change
10295 $tzbefore = array();
10296 $tzbefore['offsetHis'] = $vtzc->getProperty('tzoffsetfrom') ;
10297 $tzbefore['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzbefore['offsetHis']);
10298 if(( '-' != substr( (string) $tzbefore['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzbefore['offsetSec'], 0, 1 )))
10299 $tzbefore['offsetSec'] = '+'.$tzbefore['offsetSec'];
10300 $tzafter = array();
10301 $tzafter['offsetHis'] = $vtzc->getProperty('tzoffsetto') ;
10302 $tzafter['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzafter['offsetHis']);
10303 if(( '-' != substr( (string) $tzafter['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzafter['offsetSec'], 0, 1 )))
10304 $tzafter['offsetSec'] = '+'.$tzafter['offsetSec'];
10305 if( FALSE === ( $tzafter['tzname'] = $vtzc->getProperty('tzname')))
10306 $tzafter['tzname'] = $tzafter['offsetHis'];
10307 // find out where to start from
10308 $dtstart = $vtzc->getProperty('dtstart');
10309 $dtstarttimestamp = mktime(
10310 $dtstart['hour'],
10311 $dtstart['min'],
10312 $dtstart['sec'],
10313 $dtstart['month'],
10314 $dtstart['day'],
10315 $dtstart['year']
10316 ) ;
10317 if( !isset( $dtstart['unparsedtext'] )) // ??
10318 $dtstart['unparsedtext'] = sprintf( '%04d%02d%02dT%02d%02d%02d', $dtstart['year'], $dtstart['month'], $dtstart['day'], $dtstart['hour'], $dtstart['min'], $dtstart['sec'] );
10319 if ( $dtstarttimestamp == 0 ) {
10320 // it seems that the dtstart string may not have parsed correctly
10321 // let's set a timestamp starting from 1902, using the time part of the original string
10322 // so that the time will change at the right time of day
10323 // at worst we'll get midnight again
10324 $origdtstartsplit = explode('T',$dtstart['unparsedtext']) ;
10325 $dtstarttimestamp = strtotime("19020101",0);
10326 $dtstarttimestamp = strtotime($origdtstartsplit[1],$dtstarttimestamp);
10327 }
10328 // the date (in dtstart and opt RDATE/RRULE) is ALWAYS LOCAL (not utc!!), adjust from 'utc' to 'local' timestamp
10329 $diff = -1 * $tzbefore['offsetSec'];
10330 $dtstarttimestamp += $diff;
10331 // add this (start) change to the array of changes
10332 $tzdates[] = array(
10333 'timestamp' => $dtstarttimestamp,
10334 'tzbefore' => $tzbefore,
10335 'tzafter' => $tzafter
10336 );
10337 $datearray = getdate($dtstarttimestamp);
10338 // save original array to use time parts, because strtotime (used below) apparently loses the time
10339 $changetime = $datearray ;
10340 // generate dates according to an RRULE line
10341 $rrule = $vtzc->getProperty('rrule') ;
10342 if ( is_array($rrule) ) {
10343 if ( $rrule['FREQ'] == 'YEARLY' ) {
10344 // calculate transition dates starting from DTSTART
10345 $offsetchangetimestamp = $dtstarttimestamp;
10346 // calculate transition dates until 10 years in the future
10347 $stoptimestamp = strtotime("+10 year",time());
10348 // if UNTIL is set, calculate until then (however far ahead)
10349 if ( isset( $rrule['UNTIL'] ) && ( $rrule['UNTIL'] != '' )) {
10350 $stoptimestamp = mktime(
10351 $rrule['UNTIL']['hour'],
10352 $rrule['UNTIL']['min'],
10353 $rrule['UNTIL']['sec'],
10354 $rrule['UNTIL']['month'],
10355 $rrule['UNTIL']['day'],
10356 $rrule['UNTIL']['year']
10357 ) ;
10358 }
10359 $count = 0 ;
10360 $stopcount = isset( $rrule['COUNT'] ) ? $rrule['COUNT'] : 0 ;
10361 $daynames = array(
10362 'SU' => 'Sunday',
10363 'MO' => 'Monday',
10364 'TU' => 'Tuesday',
10365 'WE' => 'Wednesday',
10366 'TH' => 'Thursday',
10367 'FR' => 'Friday',
10368 'SA' => 'Saturday'
10369 );
10370 // repeat so long as we're between DTSTART and UNTIL, or we haven't prepared COUNT dates
10371 while ( $offsetchangetimestamp < $stoptimestamp && ( $stopcount == 0 || $count < $stopcount ) ) {
10372 // break up the timestamp into its parts
10373 $datearray = getdate($offsetchangetimestamp);
10374 if ( isset( $rrule['BYMONTH'] ) && ( $rrule['BYMONTH'] != 0 )) {
10375 // set the month
10376 $datearray['mon'] = $rrule['BYMONTH'] ;
10377 }
10378 if ( isset( $rrule['BYMONTHDAY'] ) && ( $rrule['BYMONTHDAY'] != 0 )) {
10379 // set specific day of month
10380 $datearray['mday'] = $rrule['BYMONTHDAY'];
10381 } elseif ( is_array($rrule['BYDAY']) ) {
10382 // find the Xth WKDAY in the month
10383 // the starting point for this process is the first of the month set above
10384 $datearray['mday'] = 1 ;
10385 // turn $datearray as it is now back into a timestamp
10386 $offsetchangetimestamp = mktime(
10387 $datearray['hours'],
10388 $datearray['minutes'],
10389 $datearray['seconds'],
10390 $datearray['mon'],
10391 $datearray['mday'],
10392 $datearray['year']
10393 );
10394 if ($rrule['BYDAY'][0] > 0) {
10395 // to find Xth WKDAY in month, we find last WKDAY in month before
10396 // we do that by finding first WKDAY in this month and going back one week
10397 // then we add X weeks (below)
10398 $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp);
10399 $offsetchangetimestamp = strtotime("-1 week",$offsetchangetimestamp);
10400 } else {
10401 // to find Xth WKDAY before the end of the month, we find the first WKDAY in the following month
10402 // we do that by going forward one month and going to WKDAY there
10403 // then we subtract X weeks (below)
10404 $offsetchangetimestamp = strtotime("+1 month",$offsetchangetimestamp);
10405 $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp);
10406 }
10407 // now move forward or back the appropriate number of weeks, into the month we want
10408 $offsetchangetimestamp = strtotime($rrule['BYDAY'][0] . " week",$offsetchangetimestamp);
10409 $datearray = getdate($offsetchangetimestamp);
10410 }
10411 // convert the date parts back into a timestamp, setting the time parts according to the
10412 // original time data which we stored
10413 $offsetchangetimestamp = mktime(
10414 $changetime['hours'],
10415 $changetime['minutes'],
10416 $changetime['seconds'] + $diff,
10417 $datearray['mon'],
10418 $datearray['mday'],
10419 $datearray['year']
10420 );
10421 // add this change to the array of changes
10422 $tzdates[] = array(
10423 'timestamp' => $offsetchangetimestamp,
10424 'tzbefore' => $tzbefore,
10425 'tzafter' => $tzafter
10426 );
10427 // update counters (timestamp and count)
10428 $offsetchangetimestamp = strtotime("+" . (( isset( $rrule['INTERVAL'] ) && ( $rrule['INTERVAL'] != 0 )) ? $rrule['INTERVAL'] : 1 ) . " year",$offsetchangetimestamp);
10429 $count += 1 ;
10430 }
10431 }
10432 }
10433 // generate dates according to RDATE lines
10434 while ($rdates = $vtzc->getProperty('rdate')) {
10435 if ( is_array($rdates) ) {
10436
10437 foreach ( $rdates as $rdate ) {
10438 // convert the explicit change date to a timestamp
10439 $offsetchangetimestamp = mktime(
10440 $rdate['hour'],
10441 $rdate['min'],
10442 $rdate['sec'] + $diff,
10443 $rdate['month'],
10444 $rdate['day'],
10445 $rdate['year']
10446 ) ;
10447 // add this change to the array of changes
10448 $tzdates[] = array(
10449 'timestamp' => $offsetchangetimestamp,
10450 'tzbefore' => $tzbefore,
10451 'tzafter' => $tzafter
10452 );
10453 }
10454 }
10455 }
10456 return $tzdates;
10457 }
10458 ?>