2 /*********************************************************************************/
5 * copyright (c) 2007-2013 Kjell-Inge Gustafsson kigkonsult
6 * kigkonsult.se/iCalcreator/index.php
10 * This file is a PHP implementation of rfc2445/rfc5545.
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.
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.
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
26 /*********************************************************************************/
27 /*********************************************************************************/
29 /*********************************************************************************/
30 /* your local language code */
31 // define( 'ICAL_LANG', 'sv' );
34 $langstr = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
35 $pos = strpos( $langstr, ';' );
37 $langstr = substr( $langstr, 0, $pos );
38 $pos = strpos( $langstr, ',' );
40 $pos = strpos( $langstr, ',' );
41 $langstr = substr( $langstr, 0, $pos );
43 define( 'ICAL_LANG', $langstr );
46 /*********************************************************************************/
47 /* version, do NOT remove!! */
48 define( 'ICALCREATOR_VERSION', 'iCalcreator 2.16.12' );
49 /*********************************************************************************/
50 /*********************************************************************************/
54 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
55 * @since 2.9.6 - 2011-05-14
58 // calendar property variables
64 // container for calendar components
66 // component config variables
77 // component internal variables
78 var $attributeDelimiter;
80 // component xCal declaration container
83 * constructor for calendar object
85 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
86 * @since 2.9.6 - 2011-05-14
87 * @param array $config
90 function vcalendar ( $config = array()) {
91 $this->_makeVersion();
92 $this->calscale
= null;
94 $this->_makeUnique_id();
96 $this->xprop
= array();
97 $this->language
= null;
98 $this->directory
= null;
99 $this->filename
= null;
103 * language = <Text identifying a language, as defined in [RFC 1766]>
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 );
113 $this->xcaldecl
= array();
114 $this->components
= array();
116 /*********************************************************************************/
118 * Property Name: CALSCALE
121 * creates formatted output for calendar property calscale
123 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
124 * @since 2.10.16 - 2011-10-28
127 function createCalscale() {
128 if( empty( $this->calscale
)) return FALSE;
129 switch( $this->format
) {
131 return $this->nl
.' calscale="'.$this->calscale
.'"';
134 return 'CALSCALE:'.$this->calscale
.$this->nl
;
139 * set calendar property calscale
141 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
142 * @since 2.4.8 - 2008-10-21
143 * @param string $value
146 function setCalscale( $value ) {
147 if( empty( $value )) return FALSE;
148 $this->calscale
= $value;
150 /*********************************************************************************/
152 * Property Name: METHOD
155 * creates formatted output for calendar property method
157 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
158 * @since 2.10.16 - 2011-10-28
161 function createMethod() {
162 if( empty( $this->method
)) return FALSE;
163 switch( $this->format
) {
165 return $this->nl
.' method="'.$this->method
.'"';
168 return 'METHOD:'.$this->method
.$this->nl
;
173 * set calendar property method
175 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
176 * @since 2.4.8 - 2008-20-23
177 * @param string $value
180 function setMethod( $value ) {
181 if( empty( $value )) return FALSE;
182 $this->method
= $value;
185 /*********************************************************************************/
187 * Property Name: PRODID
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.. .
194 * creates formatted output for calendar property prodid
196 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
197 * @since 2.12.11 - 2012-05-13
200 function createProdid() {
201 if( !isset( $this->prodid
))
202 $this->_makeProdid();
203 switch( $this->format
) {
205 return $this->nl
.' prodid="'.$this->prodid
.'"';
208 $toolbox = new calendarComponent();
209 $toolbox->setConfig( $this->getConfig());
210 return $toolbox->_createElement( 'PRODID', '', $this->prodid
);
215 * make default value for calendar prodid
217 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
218 * @since 2.6.8 - 2009-12-30
221 function _makeProdid() {
222 $this->prodid
= '-//'.$this->unique_id
.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION
.'//'.strtoupper( $this->language
);
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].
231 * make default unique_id for calendar prodid
233 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
234 * @since 0.3.0 - 2006-08-10
237 function _makeUnique_id() {
238 $this->unique_id
= ( isset( $_SERVER['SERVER_NAME'] )) ?
gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
240 /*********************************************************************************/
242 * Property Name: VERSION
244 * Description: A value of "2.0" corresponds to this memo.
247 * creates formatted output for calendar property version
250 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
251 * @since 2.10.16 - 2011-10-28
254 function createVersion() {
255 if( empty( $this->version
))
256 $this->_makeVersion();
257 switch( $this->format
) {
259 return $this->nl
.' version="'.$this->version
.'"';
262 return 'VERSION:'.$this->version
.$this->nl
;
267 * set default calendar version
269 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
270 * @since 0.3.0 - 2006-08-10
273 function _makeVersion() {
274 $this->version
= '2.0';
277 * set calendar version
279 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
280 * @since 2.4.8 - 2008-10-23
281 * @param string $value
284 function setVersion( $value ) {
285 if( empty( $value )) return FALSE;
286 $this->version
= $value;
289 /*********************************************************************************/
291 * Property Name: x-prop
294 * creates formatted output for calendar property x-prop, iCal format only
296 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
297 * @since 2.16.2 - 2012-12-18
300 function createXprop() {
301 if( empty( $this->xprop
) ||
!is_array( $this->xprop
)) return FALSE;
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 );
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'] );
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;
327 * set calendar property x-prop
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
336 function setXprop( $label, $value, $params=FALSE ) {
339 if( 'X-' != strtoupper( substr( $label, 0, 2 )))
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;
348 /*********************************************************************************/
350 * delete calendar property value
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
358 function deleteProperty( $propName=FALSE, $propix=FALSE ) {
359 $propName = ( $propName ) ?
strtoupper( $propName ) : 'X-PROP';
361 $propix = ( isset( $this->propdelix
[$propName] ) && ( 'X-PROP' != $propName )) ?
$this->propdelix
[$propName] +
2 : 1;
362 $this->propdelix
[$propName] = --$propix;
364 switch( $propName ) {
366 if( isset( $this->calscale
)) {
367 $this->calscale
= null;
372 if( isset( $this->method
)) {
373 $this->method
= null;
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 ))
387 if( count( $this->xprop
) <= $propix ) return FALSE;
389 foreach( $this->xprop
as $xpropkey => $xpropvalue ) {
390 if( $propix != $xpropno )
391 $reduced[$xpropkey] = $xpropvalue;
395 $this->xprop
= $reduced;
396 if( empty( $this->xprop
)) {
397 unset( $this->propdelix
[$propName] );
405 * get calendar property value/params
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
414 function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) {
415 $propName = ( $propName ) ?
strtoupper( $propName ) : 'X-PROP';
416 if( 'X-PROP' == $propName ) {
418 $propix = ( isset( $this->propix
[$propName] )) ?
$this->propix
[$propName] +
2 : 1;
419 $this->propix
[$propName] = --$propix;
422 $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' );
423 switch( $propName ) {
435 case 'RECURRENCE-ID-UID':
441 foreach ( $this->components
as $cix => $component) {
442 if( !in_array( $component->objName
, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' )))
444 if( in_array( strtoupper( $propName ), $mProps )) {
445 $component->_getProperties( $propName, $output );
448 elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) {
449 if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' )))
450 $content = $component->getProperty( 'UID' );
452 elseif( 'GEOLOCATION' == $propName ) {
453 $content = $component->getProperty( 'LOCATION' );
454 $content = ( !empty( $content )) ?
$content.' ' : '';
455 if(( FALSE === ( $geo = $component->getProperty( 'GEO' ))) ||
empty( $geo ))
457 if( 0.0 < $geo['latitude'] )
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'] )
466 $sign = ( 0.0 > $geo['longitude'] ) ?
'-' : '';
467 $content .= $sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/';
469 elseif( FALSE === ( $content = $component->getProperty( $propName )))
471 if(( FALSE === $content ) ||
empty( $content ))
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] ))
482 foreach( $content as $partValue => $partCount ) {
483 if( !isset( $output[$partValue] ))
484 $output[$partValue] = $partCount;
486 $output[$partValue] +
= $partCount;
489 } // end elseif( is_array( $content )) {
490 elseif( !isset( $output[$content] ))
491 $output[$content] = 1;
493 $output[$content] +
= 1;
494 } // end foreach ( $this->components as $cix => $component)
495 if( !empty( $output ))
500 return ( !empty( $this->calscale
)) ?
$this->calscale
: FALSE;
503 return ( !empty( $this->method
)) ?
$this->method
: FALSE;
506 if( empty( $this->prodid
))
507 $this->_makeProdid();
508 return $this->prodid
;
511 return ( !empty( $this->version
)) ?
$this->version
: FALSE;
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'] );
520 if( empty( $this->xprop
)) return FALSE;
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'] );
529 unset( $this->propix
[$propName] );
530 return FALSE; // not found ??
536 * general vcalendar property setting
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!
545 function setProperty () {
546 $numargs = func_num_args();
549 $arglist = func_get_args();
550 $arglist[0] = strtoupper( $arglist[0] );
551 switch( $arglist[0] ) {
553 return $this->setCalscale( $arglist[1] );
555 return $this->setMethod( $arglist[1] );
557 return $this->setVersion( $arglist[1] );
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] );
565 /*********************************************************************************/
567 * get vcalendar config values or * calendar components
569 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
570 * @since 2.11.7 - 2012-01-12
571 * @param mixed $config
574 function getConfig( $config = FALSE ) {
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' );
593 switch( strtoupper( $config )) {
595 return $this->allowEmpty
;
598 unset( $this->compix
);
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' );
611 return $this->delimiter
;
614 if( empty( $this->directory
) && ( '0' != $this->directory
))
615 $this->directory
= '.';
616 return $this->directory
;
619 return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' );
622 return array( $this->getConfig( 'directory' )
623 , $this->getConfig( 'filename' )
624 , $this->getConfig( 'filesize' ));
627 if( empty( $this->filename
) && ( '0' != $this->filename
)) {
628 if( 'xcal' == $this->format
)
629 $this->filename
= date( 'YmdHis' ).'.xml'; // recommended xcs.. .
631 $this->filename
= date( 'YmdHis' ).'.ics';
633 return $this->filename
;
637 if( empty( $this->url
)) {
638 $dirfile = $this->getConfig( 'dirfile' );
639 if( !is_file( $dirfile ) ||
( FALSE === ( $size = filesize( $dirfile ))))
646 return ( $this->format
== 'xcal' ) ?
'xCal' : 'iCal';
649 /* get language for calendar component as defined in [RFC 1766] */
650 return $this->language
;
660 return $this->unique_id
;
663 if( !empty( $this->url
))
671 * general vcalendar config setting
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
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] ))
686 unset( $config[$k] );
688 elseif( 'NEWLINECHAR' == strtoupper( $k )) {
689 if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] ))
691 unset( $config[$k] );
694 foreach( $config as $cKey => $cValue ) {
695 if( FALSE === $this->setConfig( $cKey, $cValue ))
701 switch( strtoupper( $config )) {
703 $this->allowEmpty
= $value;
704 $subcfg = array( 'ALLOWEMPTY' => $value );
708 $this->delimiter
= $value;
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 */
719 $this->directory
= $value;
727 $value = trim( $value );
728 if( !empty( $this->url
)) {
729 /* remote directory+file -> URL */
730 $this->filename
= $value;
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 )) {
738 $this->filename
= $value;
744 elseif( is_readable($this->getConfig( 'directory' ) ) ||
is_writable( $this->getConfig( 'directory' ) )) {
745 /* read- or writable directory */
746 $this->filename
= $value;
753 $value = trim( strtolower( $value ));
754 if( 'xcal' == $value ) {
755 $this->format
= 'xcal';
756 $this->attributeDelimiter
= $this->nl
;
757 $this->valueInit
= null;
760 $this->format
= null;
761 $this->attributeDelimiter
= ';';
762 $this->valueInit
= ':';
764 $subcfg = array( 'FORMAT' => $value );
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 );
777 if( 'xcal' == $value ) {
778 $this->attributeDelimiter
= $this->nl
;
779 $this->valueInit
= null;
782 $this->attributeDelimiter
= ';';
783 $this->valueInit
= ':';
785 $subcfg = array( 'NL' => $value );
789 $this->dtzid
= $value;
790 $subcfg = array( 'TZID' => $value );
794 $value = trim( $value );
795 $this->unique_id
= $value;
796 $this->_makeProdid();
797 $subcfg = array( 'UNIQUE_ID' => $value );
801 /* remote file - URL */
802 $value = str_replace( array( 'HTTP://', 'WEBCAL://', 'webcal://' ), 'http://', trim( $value ));
803 if( 'http://' != substr( $value, 0, 7 ))
807 $s2 = $this->directory
;
808 $this->directory
= null;
809 $parts = pathinfo( $value );
810 if( FALSE === $this->setConfig( 'filename', $parts['basename'] )) {
812 $this->directory
= $s2;
818 default: // any unvalid config key.. .
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 );
828 $this->components
[$cix] = $component->copy(); // PHP4 compliant
834 /*********************************************************************************/
836 * add calendar component to container
838 * alias to setComponent
840 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
841 * @since 1.x.x - 2007-04-24
842 * @param object $component calendar component
845 function addComponent( $component ) {
846 $this->setComponent( $component );
849 * delete calendar component from container
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
857 function deleteComponent( $arg1, $arg2=FALSE ) {
858 $argType = $index = null;
859 if ( ctype_digit( (string) $arg1 )) {
861 $index = (int) $arg1 - 1;
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;
868 foreach ( $this->components
as $cix => $component) {
869 if( empty( $component )) continue;
870 if(( 'INDEX' == $argType ) && ( $index == $cix )) {
871 unset( $this->components
[$cix] );
874 elseif( $argType == $component->objName
) {
875 if( $index == $cix1dC ) {
876 unset( $this->components
[$cix] );
881 elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
882 unset( $this->components
[$cix] );
889 * get calendar component from container
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
897 function getComponent( $arg1=FALSE, $arg2=FALSE ) {
898 $index = $argType = null;
899 if ( !$arg1 ) { // first or next in component chain
901 $index = $this->compix
['INDEX'] = ( isset( $this->compix
['INDEX'] )) ?
$this->compix
['INDEX'] +
1 : 1;
903 elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain
905 $index = (int) $arg1;
906 unset( $this->compix
);
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' );
915 elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name
916 unset( $this->compix
['INDEX'] );
917 $argType = strtolower( $arg1 );
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;
923 elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument
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;
931 $ckeys = array_keys( $this->components
);
932 if( !empty( $index) && ( $index > end( $ckeys )))
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();
944 elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
946 foreach( $arg1 as $pName => $pValue ) {
947 $pName = strtoupper( $pName );
948 if( !in_array( $pName, $dateProps ) && !in_array( $pName, $otherProps ))
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;
956 } // end if(.. .// multiple occurrence
957 if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence
958 $hit[] = FALSE; // missing property
961 if( 'SUMMARY' == $pName ) { // exists within (any case)
962 $hit[] = ( FALSE !== stripos( $value, $pValue )) ?
TRUE : FALSE;
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'] );
974 $pValue = substr( $pValue, 0, 8 );
976 $hit[] = ( $pValue == $valuedate ) ?
TRUE : FALSE;
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 ) {
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();
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();
1003 } // end foreach ( $this->components.. .
1005 unset( $this->compix
);
1009 * create new calendar component, already included within calendar
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)
1016 function & newComponent( $compType ) {
1017 $config = $this->getConfig();
1018 $keys = array_keys( $this->components
);
1019 $ix = end( $keys) +
1;
1020 switch( strtoupper( $compType )) {
1023 $this->components
[$ix] = new vevent( $config );
1027 $this->components
[$ix] = new vtodo( $config );
1031 $this->components
[$ix] = new vjournal( $config );
1035 $this->components
[$ix] = new vfreebusy( $config );
1039 array_unshift( $this->components
, new vtimezone( $config ));
1045 return $this->components
[$ix];
1048 * select components from calendar on date or selectOption basis
1050 * Ensure DTSTART is set for every component.
1051 * No date controls occurs.
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
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';
1094 $cType = array_unique( $cType );
1096 elseif( !empty( $cType )) {
1097 $cType = strtolower( $cType );
1098 if( !in_array( $cType, $validTypes ))
1099 $cType = array( 'vevent' );
1101 $cType = array( $cType );
1104 $cType = $validTypes;
1105 if( 0 >= count( $cType ))
1106 $cType = $validTypes;
1107 if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination
1109 if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination
1111 /* iterate components */
1113 $this->sort( 'UID' );
1115 $recurridList = array();
1116 foreach ( $this->components
as $cix => $component ) {
1117 if( empty( $component )) continue;
1119 /* deselect unvalid type components */
1120 if( !in_array( $component->objName
, $cType ))
1122 $start = $component->getProperty( 'dtstart' );
1123 /* select due when dtstart is missing */
1124 if( empty( $start ) && ( $component->objName
== 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' ))))
1126 if( empty( $start ))
1128 $compUID = $component->getProperty( 'UID' );
1129 if( $compUIDcmp != $compUID ) {
1130 $compUIDcmp = $compUID;
1131 unset( $exdatelist, $recurridList );
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 )) {
1141 $endDateFormat = ( isset( $end['hour'] )) ?
'Y-m-d H:i:s' : 'Y-m-d';
1143 if( empty( $end ) && ( $component->objName
== 'vtodo' )) {
1144 $end = $component->getProperty( 'due' );
1145 if( !empty( $end )) {
1147 $endDateFormat = ( isset( $end['hour'] )) ?
'Y-m-d H:i:s' : 'Y-m-d';
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 );
1159 $end['min'] = $end['sec'] = 59;
1161 if( empty( $end )) {
1162 $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format
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 ###
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 );
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 );
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
1205 $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id)
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 );
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 )));
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 ));
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 )
1252 } // end elseif( $split ) - else use component date
1253 elseif( $recurrid && !$flat && !$any && !$split )
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
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 ))
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 );
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
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
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] );
1301 if( 0 < count( $recurlist )) {
1302 ksort( $recurlist );
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
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
1313 if( isset( $exdatelist[$checkDate] )) // check excluded dates
1315 if( $startWdate >= $recurkey ) // exclude component start date
1317 $rstart = $recurkey;
1318 $rend = $recurkey +
$durvalue;
1319 /* add repeating components within valid dates to output array, only start date set */
1321 if( !isset( $result[$compUID] )) // only one comp
1322 $result[$compUID] = $component2->copy(); // copy to output
1324 /* add repeating components within valid dates to output array, one each day */
1327 if( $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
1336 if( isset( $exdatelist[$checkDate] )) // exclude any recurrence START date, found in exdatelist
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 );
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 ));
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 *//
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 ));
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 ))
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;
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] );
1415 $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index, hoping they are in hour order.. .
1417 if( empty( $result[$y][$m] ))
1418 unset( $result[$y][$m] );
1420 ksort( $result[$y][$m] );
1422 if( empty( $result[$y] ))
1423 unset( $result[$y] );
1425 ksort( $result[$y] );
1427 if( empty( $result ))
1431 } // end elseif( !$flat )
1432 if( 0 >= count( $result ))
1437 * select components from calendar on based on specific property value(-s)
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)
1444 function selectComponents2( $selectOptions ) {
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 ))
1451 $uid = $component3->getProperty( 'UID' );
1452 foreach( $selectOptions as $propName => $pvalue ) {
1453 $propName = strtoupper( $propName );
1454 if( !in_array( $propName, $allowedProperties ))
1456 if( !is_array( $pvalue ))
1457 $pvalue = array( $pvalue );
1458 if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) {
1459 $output[$uid][] = $component3->copy();
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();
1473 } // end elseif( // multiple occurrence?
1474 elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single occurrence
1476 if( is_array( $d )) {
1477 foreach( $d as $part ) {
1478 if( in_array( $part, $pvalue ) && !isset( $output[$uid] ))
1479 $output[$uid][] = $component3->copy();
1482 elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) {
1483 foreach( $pvalue as $pval ) {
1484 if( FALSE !== stripos( $d, $pval )) {
1485 $output[$uid][] = $component3->copy();
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
1497 foreach( $output as $uid => $components ) {
1498 foreach( $components as $component )
1499 $output2[] = $component;
1506 * add calendar component to container
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
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' );
1522 if( !$arg1 ) { // plain insert, last in chain
1523 $this->components
[] = $component->copy();
1526 $argType = $index = null;
1527 if ( ctype_digit( (string) $arg1 )) { // index insert/replace
1529 $index = (int) $arg1 - 1;
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;
1535 // else if arg1 is set, arg1 must be an UID
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();
1543 elseif( $argType == $component2->objName
) { // component Type index insert/replace
1544 if( $index == $cix1sC ) {
1545 $this->components
[$cix] = $component->copy();
1550 elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
1551 $this->components
[$cix] = $component->copy();
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
);
1560 else /* not found.. . insert last in chain anyway .. .*/
1561 $this->components
[] = $component->copy();
1565 * sort iCal compoments
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
1571 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1572 * @since 2.16.4 - 2012-12-17
1573 * @param string $sortArg, optional
1577 function sort( $sortArg=FALSE ) {
1578 if( is_array( $this->components
)) {
1580 $sortArg = strtoupper( $sortArg );
1581 if( !in_array( $sortArg, array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'DTSTAMP', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' )))
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' )))
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' );
1602 elseif( 'RELATED-TO' == $sortArg )
1603 $c->srtk
[0] = $c->getProperty( 'uid' );
1605 elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) {
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
;
1614 $c->srtk
[1] = $c->srtk
[2] = PHP_INT_MAX
;
1619 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) {
1620 $c->srtk
[0] = iCalUtilityFunctions
::_strdate2date( $d[1] );
1621 unset( $c->srtk
[0]['unparsedtext'] );
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'] );
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'] );
1634 elseif( FALSE === ( $c->srtk
[1] = $c->getProperty( 'due' )))
1635 if( FALSE === ( $c->srtk
[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE )))
1638 if( FALSE === ( $c->srtk
[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp
1639 if( FALSE === ( $c->srtk
[2] = $c->getProperty( 'dtstamp' )))
1641 if( FALSE === ( $c->srtk
[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid
1643 } // end foreach( $this->components as & $c
1645 usort( $this->components
, array( 'iCalUtilityFunctions', '_cmpfcn' ));
1649 * parse iCal text/file into vcalendar, components, properties and parameters
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
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' );
1664 if( FALSE === ( $rows = file_get_contents( $filename )))
1665 return FALSE; /* err 1 */
1667 elseif( is_array( $unparsedtext ))
1668 $rows = implode( '\n'.$nl, $unparsedtext );
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' ))
1677 unset( $rows[$lix] );
1679 $rcnt = count( $rows );
1680 if( 3 > $rcnt ) /* err 10 */
1682 /* skip trailing empty lines and ensure an end row */
1683 $lix = array_keys( $rows );
1686 $tst = trim( $rows[$lix] );
1687 if(( '\n' == $tst ) ||
empty( $tst )) {
1688 unset( $rows[$lix] );
1692 if( FALSE === stripos( $rows[$lix], 'END:VCALENDAR' ))
1693 $rows[] = 'END:VCALENDAR';
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 ))) {
1706 elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) {
1708 $this->components
[] = $comp->copy();
1713 elseif( 1 != $calsync )
1714 return FALSE; /* err 20 */
1715 elseif( in_array( strtoupper( substr( $line, 0, 6 )), $endtxt )) {
1716 $this->components
[] = $comp->copy();
1720 if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) {
1721 $comp = new vevent( $config );
1724 elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) {
1725 $comp = new vfreebusy( $config );
1728 elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) {
1729 $comp = new vjournal( $config );
1732 elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) {
1733 $comp = new vtodo( $config );
1736 elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) {
1737 $comp = new vtimezone( $config );
1740 else { /* update component with unparsed data */
1741 $comp->unparsed
[] = $line;
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;
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 */
1765 while( FALSE !== ( $char = substr( $line, $cix, 1 ))) {
1766 if( in_array( $char, array( ':', ';' )))
1772 /* skip non standard property names */
1773 if(( 'x-' != strtolower( substr( $propname, 0, 2 ))) && !in_array( strtolower( $propname ), $propnames ))
1775 /* ignore version/prodid properties */
1776 if( in_array( strtolower( $propname ), array( 'version', 'prodid' )))
1778 /* rest of the line is opt.params and value */
1779 $line = substr( $line, $cix);
1780 /* separate attributes from value */
1783 $strlen = strlen( $line );
1784 $WithinQuotes = FALSE;
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:' ) &&
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 )) {
1800 break; // an URI with a portnr!!
1805 $line = substr( $line, ( $cix +
1 ));
1809 if( '"' == $line[$cix] )
1810 $WithinQuotes = ( FALSE === $WithinQuotes ) ?
TRUE : FALSE;
1811 if( ';' == $line[$cix] )
1812 $attr[++
$attrix] = null;
1814 $attr[$attrix] .= $line[$cix];
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];
1824 $propattr[] = $attribute;
1826 /* update Property */
1827 if( FALSE !== strpos( $line, ',' )) {
1828 $content = array( 0 => '' );
1830 while( FALSE !== substr( $line, $lix, 1 )) {
1831 if(( 0 < $lix ) && ( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) {
1833 $content[$cix] = '';
1836 $content[$cix] .= $line[$lix];
1839 if( 1 < count( $content )) {
1840 foreach( $content as $cix => $contentPart )
1841 $content[$cix] = iCalUtilityFunctions
::_strunrep( $contentPart );
1842 $this->setProperty( $propname, $content, $propattr );
1846 $line = reset( $content );
1847 $line = iCalUtilityFunctions
::_strunrep( $line );
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();
1863 return FALSE; /* err 91 or something.. . */
1866 /*********************************************************************************/
1868 * creates formatted output for calendar object instance
1870 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1871 * @since 2.10.16 - 2011-10-28
1874 function createCalendar() {
1875 $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = '';
1876 switch( $this->format
) {
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';
1884 $calendarStart = 'BEGIN:VCALENDAR'.$this->nl
;
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();
1895 foreach( $this->components
as $component ) {
1896 if( empty( $component )) continue;
1897 $component->setConfig( $this->getConfig(), FALSE, TRUE );
1898 $calendar .= $component->createComponent( $this->xcaldecl
);
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.' ';
1922 $calendarxCaldecl .= $declValue.' ';
1923 $old_xcaldecl['uri'][] = $declValue;
1926 $calendarxCaldecl .= $declValue.' ';
1927 $old_xcaldecl['ref'][] = $declValue;
1929 case 'external': // no 4
1930 $calendarxCaldecl .= '"'.$declValue.'" ';
1931 $old_xcaldecl['external'][] = $declValue;
1933 case 'type': // no 5
1934 $calendarxCaldecl .= $declValue.' ';
1936 case 'type2': // no 6
1937 $calendarxCaldecl .= $declValue;
1941 $calendarxCaldecl .= '>';
1943 $calendarxCaldecl .= $this->nl
.']';
1945 switch( $this->format
) {
1947 $calendar .= '</vcalendar>'.$this->nl
;
1950 $calendar .= 'END:VCALENDAR'.$this->nl
;
1953 return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar;
1956 * a HTTP redirect header is sent with created, updated and/or parsed calendar
1958 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1959 * @since 2.10.24 - 2011-12-23
1960 * @param bool $utf8Encode
1964 function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE ) {
1965 $filename = $this->getConfig( 'filename' );
1966 $output = $this->createCalendar();
1968 $output = utf8_encode( $output );
1970 $output = gzencode( $output, 9 );
1971 header( 'Content-Encoding: gzip' );
1972 header( 'Vary: *' );
1973 header( 'Content-Length: '.strlen( $output ));
1975 if( 'xcal' == $this->format
)
1976 header( 'Content-Type: application/calendar+xml; charset=utf-8' );
1978 header( 'Content-Type: text/calendar; charset=utf-8' );
1979 header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
1980 header( 'Cache-Control: max-age=10' );
1984 * save content in a file
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
1993 function saveCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE ) {
1995 $this->setConfig( 'directory', $directory );
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' );
2004 if( FALSE === fwrite( $iCalFile, $this->createCalendar() ))
2006 fclose( $iCalFile );
2013 * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent
2014 * else FALSE is returned
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
2024 function useCachedCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE, $timeout=3600) {
2025 if ( $directory && ctype_digit( (string) $directory ) && !$filename ) {
2026 $timeout = (int) $directory;
2030 $this->setConfig( 'directory', $directory );
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 )
2038 $dirfile = $this->getConfig( 'dirfile' );
2039 if( time() - filemtime( $dirfile ) < $timeout) {
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' );
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' );
2063 /*********************************************************************************/
2064 /*********************************************************************************/
2066 * abstract class for calendar components
2068 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2069 * @since 2.9.6 - 2011-05-14
2071 class calendarComponent
{
2072 // component property variables
2076 // component config variables
2082 var $objName; // created automatically at instance creation
2083 var $dtzid; // default (local) timezone
2084 // component internal variables
2085 var $componentStart1;
2086 var $componentStart2;
2093 var $intAttrDelimiter;
2094 var $attributeDelimiter;
2096 // component xCal declaration container
2099 * constructor for calendar component object
2101 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2102 * @since 2.9.6 - 2011-05-17
2104 function calendarComponent() {
2105 $this->objName
= ( isset( $this->timezonetype
)) ?
2106 strtolower( $this->timezonetype
) : get_class ( $this );
2107 $this->uid
= array();
2108 $this->dtstamp
= array();
2110 $this->language
= null;
2112 $this->unique_id
= null;
2113 $this->format
= null;
2114 $this->dtzid
= null;
2115 $this->allowEmpty
= TRUE;
2116 $this->xcaldecl
= array();
2118 $this->_createFormat();
2119 $this->_makeDtstamp();
2121 /*********************************************************************************/
2123 * Property Name: ACTION
2126 * creates formatted output for calendar component property action
2128 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2129 * @since 2.4.8 - 2008-10-22
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'] );
2140 * set calendar component property action
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
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 ));
2153 /*********************************************************************************/
2155 * Property Name: ATTACH
2158 * creates formatted output for calendar component property attach
2160 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2161 * @since 2.11.16 - 2012-02-04
2164 function createAttach() {
2165 if( empty( $this->attach
)) return FALSE;
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
;
2182 $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] );
2184 elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' );
2189 * set calendar component property attach
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
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 );
2203 /*********************************************************************************/
2205 * Property Name: ATTENDEE
2208 * creates formatted output for calendar component property attendee
2210 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2211 * @since 2.11.12 - 2012-01-31
2214 function createAttendee() {
2215 if( empty( $this->attendee
)) return FALSE;
2217 foreach( $this->attendee
as $attendeePart ) { // start foreach 1
2218 if( empty( $attendeePart['value'] )) {
2219 if( $this->getConfig( 'allowEmpty' ))
2220 $output .= $this->_createElement( 'ATTENDEE' );
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 ))
2232 if(( FALSE !== strpos( $pValue, ':' )) ||
2233 ( FALSE !== strpos( $pValue, ';' )) ||
2234 ( FALSE !== strpos( $pValue, ',' )))
2235 $paramvalue[$pKey] = '"'.$pValue.'"';
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.'"' ;
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.'"' ;
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.'"' ;
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;
2269 if( isset( $paramvalue['LANGUAGE'] ))
2270 $attendee1 .= $this->intAttrDelimiter
.'LANGUAGE='.$paramvalue['LANGUAGE'];
2272 foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3
2273 if( ctype_digit( (string) $optparamlabel )) {
2274 $xparams[] = $optparamvalue;
2277 if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' )))
2278 $xparams[$optparamlabel] = $optparamvalue;
2280 ksort( $xparams, SORT_STRING
);
2281 foreach( $xparams as $paramKey => $paramValue ) {
2282 if( ctype_digit( (string) $paramKey ))
2283 $attendee1 .= $this->intAttrDelimiter
.$paramValue;
2285 $attendee1 .= $this->intAttrDelimiter
."$paramKey=$paramValue";
2287 } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue )))
2289 $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 );
2294 * set calendar component property attach
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
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 );
2314 if( is_array($params )) {
2315 $optarrays = array();
2316 foreach( $params as $optparamlabel => $optparamvalue ) {
2317 $optparamlabel = strtoupper( $optparamlabel );
2318 switch( $optparamlabel ) {
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";
2332 $part = 'MAILTO:'.substr( $part, 7 );
2333 $optarrays[$optparamlabel][] = $part;
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";
2344 $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 );
2346 $params2[$optparamlabel] = $optparamvalue;
2348 } // end switch( $optparamlabel.. .
2349 } // end foreach( $optparam.. .
2350 foreach( $optarrays as $optparamlabel => $optparams )
2351 $params2[$optparamlabel] = $optparams;
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;
2364 iCalUtilityFunctions
::_setMval( $this->attendee
, $value, $params2, FALSE, $index );
2367 /*********************************************************************************/
2369 * Property Name: CATEGORIES
2372 * creates formatted output for calendar component property categories
2374 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2375 * @since 2.16.2 - 2012-12-18
2378 function createCategories() {
2379 if( empty( $this->categories
)) return FALSE;
2381 foreach( $this->categories
as $category ) {
2382 if( empty( $category['value'] )) {
2383 if ( $this->getConfig( 'allowEmpty' ))
2384 $output .= $this->_createElement( 'CATEGORIES' );
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'] );
2394 $content = iCalUtilityFunctions
::_strrep( $category['value'], $this->format
, $this->nl
);
2395 $output .= $this->_createElement( 'CATEGORIES', $attributes, $content );
2400 * set calendar component property categories
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
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 );
2414 /*********************************************************************************/
2416 * Property Name: CLASS
2419 * creates formatted output for calendar component property class
2421 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2422 * @since 0.9.7 - 2006-11-20
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'] );
2433 * set calendar component property class
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
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 ));
2446 /*********************************************************************************/
2448 * Property Name: COMMENT
2451 * creates formatted output for calendar component property comment
2453 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2454 * @since 2.16.2 - 2012-12-18
2457 function createComment() {
2458 if( empty( $this->comment
)) return FALSE;
2460 foreach( $this->comment
as $commentPart ) {
2461 if( empty( $commentPart['value'] )) {
2462 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' );
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 );
2472 * set calendar component property comment
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
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 );
2486 /*********************************************************************************/
2488 * Property Name: COMPLETED
2491 * creates formatted output for calendar component property completed
2493 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2494 * @since 2.4.8 - 2008-10-22
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' );
2508 $formatted = iCalUtilityFunctions
::_date2strdate( $this->completed
['value'], 7 );
2509 $attributes = $this->_createParams( $this->completed
['params'] );
2510 return $this->_createElement( 'COMPLETED', $attributes, $formatted );
2513 * set calendar component property completed
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
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 ));
2535 $this->completed
= iCalUtilityFunctions
::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2538 /*********************************************************************************/
2540 * Property Name: CONTACT
2543 * creates formatted output for calendar component property contact
2545 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2546 * @since 2.16.2 - 2012-12-18
2549 function createContact() {
2550 if( empty( $this->contact
)) return FALSE;
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 );
2558 elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' );
2563 * set calendar component property contact
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
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 );
2577 /*********************************************************************************/
2579 * Property Name: CREATED
2582 * creates formatted output for calendar component property created
2584 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2585 * @since 2.4.8 - 2008-10-21
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 );
2595 * set calendar component property created
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
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' )));
2612 $this->created
= iCalUtilityFunctions
::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2615 /*********************************************************************************/
2617 * Property Name: DESCRIPTION
2620 * creates formatted output for calendar component property description
2622 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2623 * @since 2.16.2 - 2012-12-18
2626 function createDescription() {
2627 if( empty( $this->description
)) return FALSE;
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 );
2635 elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' );
2640 * set calendar component property description
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
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
)
2653 iCalUtilityFunctions
::_setMval( $this->description
, $value, $params, FALSE, $index );
2656 /*********************************************************************************/
2658 * Property Name: DTEND
2661 * creates formatted output for calendar component property dtend
2663 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2664 * @since 2.14.4 - 2012-09-26
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' );
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 );
2684 * set calendar component property dtend
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
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 ));
2707 $this->dtend
= iCalUtilityFunctions
::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
2710 /*********************************************************************************/
2712 * Property Name: DTSTAMP
2715 * creates formatted output for calendar component property dtstamp
2717 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2718 * @since 2.4.4 - 2008-03-07
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 );
2734 * computes datestamp for calendar component object instance dtstamp
2736 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2737 * @since 2.14.1 - 2012-09-29
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;
2747 * set calendar component property dtstamp
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
2760 function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2762 $this->_makeDtstamp();
2764 $this->dtstamp
= iCalUtilityFunctions
::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2767 /*********************************************************************************/
2769 * Property Name: DTSTART
2772 * creates formatted output for calendar component property dtstart
2774 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2775 * @since 2.14.4 - 2012-09-26
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' );
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 );
2798 * set calendar component property dtstart
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
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 ));
2821 $this->dtstart
= iCalUtilityFunctions
::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName
, $this->getConfig( 'TZID' ));
2824 /*********************************************************************************/
2826 * Property Name: DUE
2829 * creates formatted output for calendar component property due
2831 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2832 * @since 2.14.4 - 2012-09-26
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' );
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 );
2854 * set calendar component property due
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
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 ));
2876 $this->due
= iCalUtilityFunctions
::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
2879 /*********************************************************************************/
2881 * Property Name: DURATION
2884 * creates formatted output for calendar component property duration
2886 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2887 * @since 2.4.8 - 2008-10-21
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 );
2900 $attributes = $this->_createParams( $this->duration
['params'] );
2901 return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions
::_duration2str( $this->duration
['value'] ));
2904 * set calendar component property duration
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
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 ));
2926 elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec ))
2929 $this->duration
= array( 'value' => iCalUtilityFunctions
::_duration2arr( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions
::_setParams( $params ));
2932 /*********************************************************************************/
2934 * Property Name: EXDATE
2937 * creates formatted output for calendar component property exdate
2939 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2940 * @since 2.16.5 - 2012-12-28
2943 function createExdate() {
2944 if( empty( $this->exdate
)) return FALSE;
2947 foreach( $this->exdate
as $theExdate ) {
2948 if( empty( $theExdate['value'] )) {
2949 if( $this->getConfig( 'allowEmpty' ))
2950 $output .= $this->_createElement( 'EXDATE' );
2953 if( 1 < count( $theExdate['value'] ))
2954 usort( $theExdate['value'], array( 'iCalUtilityFunctions', '_sortExdate1' ));
2955 $exdates[] = $theExdate;
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);
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 ))
2974 $formatted = str_replace( 'Z', '', $formatted );
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 )
2987 * set calendar component property exdate
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
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 );
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'];
3017 $input['params']['TZID'] = $theExdate['tz'];
3019 $exdatea = iCalUtilityFunctions
::_timestamp2date( $theExdate, $parno );
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'] );
3031 elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18
3032 $exdatea = iCalUtilityFunctions
::_strdate2date( $theExdate, $parno );
3033 unset( $exdatea['unparsedtext'] );
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;
3048 if( 0 >= count( $input['value'] ))
3051 $input['params']['VALUE'] = 'DATE';
3052 unset( $input['params']['TZID'] );
3054 if( $toZ ) // time zone Z
3055 unset( $input['params']['TZID'] );
3056 iCalUtilityFunctions
::_setMval( $this->exdate
, $input['value'], $input['params'], FALSE, $index );
3059 /*********************************************************************************/
3061 * Property Name: EXRULE
3064 * creates formatted output for calendar component property exrule
3066 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3067 * @since 2.4.8 - 2008-10-22
3070 function createExrule() {
3071 if( empty( $this->exrule
)) return FALSE;
3072 return $this->_format_recur( 'EXRULE', $this->exrule
);
3075 * set calendar component property exdate
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
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 );
3089 /*********************************************************************************/
3091 * Property Name: FREEBUSY
3094 * creates formatted output for calendar component property freebusy
3096 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3097 * @since 2.1.23 - 2012-02-16
3100 function createFreebusy() {
3101 if( empty( $this->freebusy
)) return FALSE;
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' );
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'] );
3115 $attributes .= $this->intAttrDelimiter
.'FBTYPE=BUSY';
3116 $attributes .= $this->_createParams( $freebusyPart['params'] );
3118 $cnt = count( $freebusyPart['value']);
3119 foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) {
3120 $formatted = iCalUtilityFunctions
::_date2strdate( $freebusyPeriod[0] );
3121 $content .= $formatted;
3123 $cnt2 = count( $freebusyPeriod[1]);
3124 if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time
3126 elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration
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] );
3134 else { // period= -> dur-time
3135 $content .= iCalUtilityFunctions
::_duration2str( $freebusyPeriod[1] );
3141 $output .= $this->_createElement( 'FREEBUSY', $attributes, $content );
3146 * set calendar component property freebusy
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
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 );
3165 $fbType = strtoupper( $fbType );
3166 if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) &&
3167 ( 'X-' != substr( $fbType, 0, 2 )))
3169 $input = array( 'fbtype' => $fbType );
3170 foreach( $fbValues as $fbPeriod ) { // periods => period
3171 if( empty( $fbPeriod ))
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';
3181 elseif( iCalUtilityFunctions
::_isArrayTimestampDate( $fbMember )) { // timestamp value
3182 $freebusyPairMember = iCalUtilityFunctions
::_timestamp2date( $fbMember['timestamp'], 7 );
3183 $freebusyPairMember['tz'] = 'Z';
3185 else { // array format duration
3186 $freebusyPairMember = iCalUtilityFunctions
::_duration2arr( $fbMember );
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 );
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';
3200 $freebusyPeriod[] = $freebusyPairMember;
3202 $input[] = $freebusyPeriod;
3204 iCalUtilityFunctions
::_setMval( $this->freebusy
, $input, $params, FALSE, $index );
3207 /*********************************************************************************/
3209 * Property Name: GEO
3212 * creates formatted output for calendar component property geo
3214 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3215 * @since 2.12.6 - 2012-04-21
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'] )
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'] )
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 );
3238 * set calendar component property geo
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
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 );
3255 elseif( $this->getConfig( 'allowEmpty' ))
3256 $this->geo
= array( 'value' => null, 'params' => iCalUtilityFunctions
::_setParams( $params ) );
3261 /*********************************************************************************/
3263 * Property Name: LAST-MODIFIED
3266 * creates formatted output for calendar component property last-modified
3268 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3269 * @since 2.4.8 - 2008-10-21
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 );
3279 * set calendar component property completed
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
3292 function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
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 );
3298 /*********************************************************************************/
3300 * Property Name: LOCATION
3303 * creates formatted output for calendar component property location
3305 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3306 * @since 2.16.2 - 2012-12-18
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 );
3318 * set calendar component property location
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
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 ));
3331 /*********************************************************************************/
3333 * Property Name: ORGANIZER
3336 * creates formatted output for calendar component property organizer
3338 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3339 * @since 2.6.33 - 2010-12-17
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'] );
3351 * set calendar component property organizer
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
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 );
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'];
3373 $this->organizer
['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer
['params']['SENT-BY'], 7 );
3377 /*********************************************************************************/
3379 * Property Name: PERCENT-COMPLETE
3382 * creates formatted output for calendar component property percent-complete
3384 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3385 * @since 2.9.3 - 2011-05-14
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'] );
3396 * set calendar component property percent-complete
3398 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3399 * @since 2.9.3 - 2011-05-14
3401 * @param array $params optional
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 ));
3409 /*********************************************************************************/
3411 * Property Name: PRIORITY
3414 * creates formatted output for calendar component property priority
3416 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3417 * @since 2.9.3 - 2011-05-14
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'] );
3428 * set calendar component property priority
3430 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3431 * @since 2.9.3 - 2011-05-14
3433 * @param array $params optional
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 ));
3441 /*********************************************************************************/
3443 * Property Name: RDATE
3446 * creates formatted output for calendar component property rdate
3448 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3449 * @since 2.16.9 - 2013-01-09
3452 function createRdate() {
3453 if( empty( $this->rdate
)) return FALSE;
3454 $utctime = ( in_array( $this->objName
, array( 'vtimezone', 'standard', 'daylight' ))) ?
TRUE : FALSE;
3457 foreach( $this->rdate
as $rpix => $theRdate ) {
3458 if( empty( $theRdate['value'] )) {
3459 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' );
3463 unset( $theRdate['params']['TZID'] );
3464 if( 1 < count( $theRdate['value'] ))
3465 usort( $theRdate['value'], array( 'iCalUtilityFunctions', '_sortRdate1' ));
3466 $rdates[] = $theRdate;
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'] );
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
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
3493 elseif( array_key_exists( 'week', $rdatePart[1] )) // duration
3495 if(( 7 == $cnt2 ) && // period= -> date-time
3496 isset( $rdatePart[1]['year'] ) &&
3497 isset( $rdatePart[1]['month'] ) &&
3498 isset( $rdatePart[1]['day'] )) {
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;
3506 else { // period= -> dur-time
3507 $contentPart .= iCalUtilityFunctions
::_duration2str( $rdatePart[1] );
3510 else { // SINGLE date start
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;
3519 $content .= $contentPart;
3524 $output .= $this->_createElement( 'RDATE', $attributes, $content );
3529 * set calendar component property rdate
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
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 );
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';
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 ) {
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'];
3582 $input['params']['TZID'] = $rPeriod['tz'];
3584 $inputab = iCalUtilityFunctions
::_timestamp2date( $rPeriod, $parno );
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'] );
3596 elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date
3597 $inputab = iCalUtilityFunctions
::_strdate2date( reset( $rPeriod ), $parno );
3598 unset( $inputab['unparsedtext'] );
3600 else // array format duration
3601 $inputab = iCalUtilityFunctions
::_duration2arr( $rPeriod );
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 );
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'] );
3613 if(( 0 == $rpix ) && ( 0 == $rix )) {
3614 if( isset( $inputab['tz'] ) && in_array( strtoupper( $inputab['tz'] ), $zArr )) {
3615 $inputab['tz'] = 'Z';
3620 if( isset( $inputa[0]['tz'] ) && ( 'Z' == $inputa[0]['tz'] ) && isset( $inputab['year'] ))
3621 $inputab['tz'] = 'Z';
3623 unset( $inputab['tz'] );
3625 if( $toZ && isset( $inputab['year'] ) )
3626 $inputab['tz'] = 'Z';
3627 $inputa[] = $inputab;
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'];
3635 $input['params']['TZID'] = $theRdate['tz'];
3637 $inputa = iCalUtilityFunctions
::_timestamp2date( $theRdate, $parno );
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'] );
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'] );
3652 $inputa['tz'] = 'Z';
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;
3658 $inputa['tz'] = 'Z';
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'] ))))
3665 unset( $inputa['tz'] );
3667 $input['value'][] = $inputa;
3670 $input['params']['VALUE'] = 'DATE';
3671 unset( $input['params']['TZID'] );
3674 unset( $input['params']['TZID'] );
3675 iCalUtilityFunctions
::_setMval( $this->rdate
, $input['value'], $input['params'], FALSE, $index );
3678 /*********************************************************************************/
3680 * Property Name: RECURRENCE-ID
3683 * creates formatted output for calendar component property recurrence-id
3685 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3686 * @since 2.14.4 - 2012-09-26
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 );
3699 * set calendar component property recurrence-id
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
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 );
3721 $this->recurrenceid
= iCalUtilityFunctions
::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
3724 /*********************************************************************************/
3726 * Property Name: RELATED-TO
3729 * creates formatted output for calendar component property related-to
3731 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3732 * @since 2.16.2 - 2012-12-18
3735 function createRelatedTo() {
3736 if( empty( $this->relatedto
)) return FALSE;
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'] ));
3747 * set calendar component property related-to
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
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 );
3762 /*********************************************************************************/
3764 * Property Name: REPEAT
3767 * creates formatted output for calendar component property repeat
3769 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3770 * @since 2.9.3 - 2011-05-14
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'] );
3781 * set calendar component property repeat
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
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 ));
3794 /*********************************************************************************/
3796 * Property Name: REQUEST-STATUS
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
3804 function createRequestStatus() {
3805 if( empty( $this->requeststatus
)) return FALSE;
3807 foreach( $this->requeststatus
as $rstat ) {
3808 if( empty( $rstat['value']['statcode'] )) {
3809 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' );
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 );
3822 * set calendar component property request-status
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
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 );
3837 $input['extdata'] = $extdata;
3838 iCalUtilityFunctions
::_setMval( $this->requeststatus
, $input, $params, FALSE, $index );
3841 /*********************************************************************************/
3843 * Property Name: RESOURCES
3846 * creates formatted output for calendar component property resources
3848 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3849 * @since 2.16.2 - 2012-12-18
3852 function createResources() {
3853 if( empty( $this->resources
)) return FALSE;
3855 foreach( $this->resources
as $resource ) {
3856 if( empty( $resource['value'] )) {
3857 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' );
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'] );
3867 $content = iCalUtilityFunctions
::_strrep( $resource['value'], $this->format
, $this->nl
);
3868 $output .= $this->_createElement( 'RESOURCES', $attributes, $content );
3873 * set calendar component property recources
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
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 );
3887 /*********************************************************************************/
3889 * Property Name: RRULE
3892 * creates formatted output for calendar component property rrule
3894 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3895 * @since 2.4.8 - 2008-10-21
3898 function createRrule() {
3899 if( empty( $this->rrule
)) return FALSE;
3900 return $this->_format_recur( 'RRULE', $this->rrule
);
3903 * set calendar component property rrule
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
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 );
3917 /*********************************************************************************/
3919 * Property Name: SEQUENCE
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
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'] );
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
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 ));
3949 /*********************************************************************************/
3951 * Property Name: STATUS
3954 * creates formatted output for calendar component property status
3956 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3957 * @since 2.4.8 - 2008-10-21
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'] );
3968 * set calendar component property status
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
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 ));
3981 /*********************************************************************************/
3983 * Property Name: SUMMARY
3986 * creates formatted output for calendar component property summary
3988 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3989 * @since 2.16.2 - 2012-12-18
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 );
4001 * set calendar component property summary
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
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 ));
4014 /*********************************************************************************/
4016 * Property Name: TRANSP
4019 * creates formatted output for calendar component property transp
4021 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4022 * @since 2.4.8 - 2008-10-21
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'] );
4033 * set calendar component property transp
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
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 ));
4046 /*********************************************************************************/
4048 * Property Name: TRIGGER
4051 * creates formatted output for calendar component property trigger
4053 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4054 * @since 2.4.16 - 2008-10-21
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'] );
4067 if( TRUE !== $this->trigger
['value']['relatedStart'] )
4068 $attributes .= $this->intAttrDelimiter
.'RELATED=END';
4069 if( $this->trigger
['value']['before'] )
4071 $content .= iCalUtilityFunctions
::_duration2str( $this->trigger
['value'] );
4073 $attributes .= $this->_createParams( $this->trigger
['params'] );
4074 return $this->_createElement( 'TRIGGER', $attributes, $content );
4077 * set calendar component property trigger
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
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 ) );
4101 if( iCalUtilityFunctions
::_isArrayTimestampDate( $year )) { // timestamp UTC
4102 $params = iCalUtilityFunctions
::_setParams( $month );
4103 $date = iCalUtilityFunctions
::_timestamp2date( $year, 7 );
4104 foreach( $date as $k => $v )
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;
4115 $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ?
FALSE : TRUE;
4116 $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ?
FALSE : TRUE;
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;
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);
4137 $date = iCalUtilityFunctions
::_strdate2date( $year, 7 );
4138 unset( $year, $month, $day, $date['unparsedtext'] );
4142 foreach( $date as $k => $v )
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
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;
4181 $relatedStart = ( FALSE !== $relatedStart ) ?
TRUE : FALSE;
4182 $before = ( FALSE !== $before ) ?
TRUE : FALSE;
4183 $this->trigger
['value']['relatedStart'] = $relatedStart;
4184 $this->trigger
['value']['before'] = $before;
4189 /*********************************************************************************/
4191 * Property Name: TZID
4194 * creates formatted output for calendar component property tzid
4196 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4197 * @since 2.16.2 - 2012-12-18
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
));
4208 * set calendar component property tzid
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
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 ));
4221 /*********************************************************************************/
4224 * Property Name: TZNAME
4227 * creates formatted output for calendar component property tzname
4229 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4230 * @since 2.16.2 - 2012-12-18
4233 function createTzname() {
4234 if( empty( $this->tzname
)) return FALSE;
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
));
4241 elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' );
4246 * set calendar component property tzname
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
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 );
4260 /*********************************************************************************/
4262 * Property Name: TZOFFSETFROM
4265 * creates formatted output for calendar component property tzoffsetfrom
4267 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4268 * @since 2.4.8 - 2008-10-21
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'] );
4279 * set calendar component property tzoffsetfrom
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
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 ));
4292 /*********************************************************************************/
4294 * Property Name: TZOFFSETTO
4297 * creates formatted output for calendar component property tzoffsetto
4299 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4300 * @since 2.4.8 - 2008-10-21
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'] );
4311 * set calendar component property tzoffsetto
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
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 ));
4324 /*********************************************************************************/
4326 * Property Name: TZURL
4329 * creates formatted output for calendar component property tzurl
4331 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4332 * @since 2.4.8 - 2008-10-21
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'] );
4343 * set calendar component property tzurl
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
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 ));
4356 /*********************************************************************************/
4358 * Property Name: UID
4361 * creates formatted output for calendar component property uid
4363 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4364 * @since 0.9.7 - 2006-11-20
4367 function createUid() {
4368 if( 0 >= count( $this->uid
))
4370 $attributes = $this->_createParams( $this->uid
['params'] );
4371 return $this->_createElement( 'UID', $attributes, $this->uid
['value'] );
4374 * create an unique id for this calendar component object instance
4376 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4377 * @since 2.2.7 - 2007-09-04
4380 function _makeUid() {
4381 $date = date('Ymd\THisT');
4382 $unique = substr(microtime(), 2, 4);
4383 $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
4385 $end = strlen( $base ) - 1;
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' );
4394 * set calendar component property uid
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
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 ));
4407 /*********************************************************************************/
4409 * Property Name: URL
4412 * creates formatted output for calendar component property url
4414 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4415 * @since 2.4.8 - 2008-10-21
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'] );
4426 * set calendar component property url
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
4434 function setUrl( $value, $params=FALSE ) {
4435 if( !empty( $value )) {
4436 if( !filter_var( $value, FILTER_VALIDATE_URL
) && ( 'urn' != strtolower( substr( $value, 0, 3 ))))
4439 elseif( $this->getConfig( 'allowEmpty' ))
4443 $this->url
= array( 'value' => $value, 'params' => iCalUtilityFunctions
::_setParams( $params ));
4446 /*********************************************************************************/
4448 * Property Name: x-prop
4451 * creates formatted output for calendar component property x-prop
4453 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4454 * @since 2.16.2 - 2012-12-18
4457 function createXprop() {
4458 if( empty( $this->xprop
)) return FALSE;
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 );
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'] );
4472 $xpropPart['value'] = iCalUtilityFunctions
::_strrep( $xpropPart['value'], $this->format
, $this->nl
);
4473 $output .= $this->_createElement( $label, $attributes, $xpropPart['value'] );
4478 * set calendar component property x-prop
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
4487 function setXprop( $label, $value, $params=FALSE ) {
4488 if( empty( $label ))
4490 if( 'X-' != strtoupper( substr( $label, 0, 2 )))
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;
4499 /*********************************************************************************/
4500 /*********************************************************************************/
4502 * create element format parts
4504 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4505 * @since 2.0.6 - 2006-06-20
4508 function _createFormat() {
4510 switch( $this->format
) {
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;
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
= ':';
4541 * creates formatted output for calendar component property
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)
4550 function _createElement( $label, $attributes=null, $content=FALSE ) {
4551 switch( $this->format
) {
4553 $label = strtolower( $label );
4556 $label = strtoupper( $label );
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'
4566 , 'type2' => '(#PCDATA)' );
4568 if( !empty( $attributes )) {
4569 $attributes = trim( $attributes );
4570 if ( 'xcal' == $this->format
) {
4571 $attributes2 = explode( $this->intAttrDelimiter
, $attributes );
4573 foreach( $attributes2 as $aix => $attribute ) {
4574 $attrKVarr = explode( '=', $attribute );
4575 if( empty( $attrKVarr[0] ))
4577 if( !isset( $attrKVarr[1] )) {
4578 $attrValue = $attrKVarr[0];
4581 elseif( 2 == count( $attrKVarr)) {
4582 $attrKey = strtolower( $attrKVarr[0] );
4583 $attrValue = $attrKVarr[1];
4586 $attrKey = strtolower( $attrKVarr[0] );
4587 unset( $attrKVarr[0] );
4588 $attrValue = implode( '=', $attrKVarr );
4590 if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) {
4591 $attachInlineBinary = TRUE;
4592 if( 'fmttype' == $attrKey )
4593 $attachfmttype = $attrKey.'='.$attrValue;
4596 elseif(( 'categories' == $label ) && ( 'language' == $attrKey ))
4597 $categoriesAttrLang = $attrKey.'='.$attrValue;
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 );
4605 $attributes .= '"'.htmlspecialchars( $attrValue ).'"';
4610 $attributes = str_replace( $this->intAttrDelimiter
, $this->attributeDelimiter
, $attributes );
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'
4620 , 'external' => $content
4622 , 'type2' => 'BINERY' );
4623 $attributes .= ( empty( $attributes )) ?
' ' : $this->attributeDelimiter
.' ';
4624 $attributes .= 'uri="'.$docname.'"';
4626 if( 'attach' == $label ) {
4627 $attributes = str_replace( $this->attributeDelimiter
, $this->intAttrDelimiter
, $attributes );
4628 $content = $this->nl
.$this->_createElement( 'extref', $attributes, null );
4632 elseif(( 'xcal' == $this->format
) && ( 'attach' == $label ) && $attachInlineBinary ) {
4633 $content = $this->nl
.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute
4635 $output .= $attributes;
4636 if( !$content && ( '0' != $content )) {
4637 switch( $this->format
) {
4640 $output .= $this->elementStart2
.$this->nl
;
4644 $output .= $this->elementStart2
.$this->valueInit
;
4645 return iCalUtilityFunctions
::_size75( $output, $this->nl
);
4649 $output .= $this->elementStart2
;
4650 $output .= $this->valueInit
.$content;
4651 switch( $this->format
) {
4653 return $output.$this->elementEnd1
.$label.$this->elementEnd2
;
4656 return iCalUtilityFunctions
::_size75( $output, $this->nl
);
4661 * creates formatted output for calendar component property parameters
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
4669 function _createParams( $params=array(), $ctrKeys=array() ) {
4670 if( !is_array( $params ) ||
empty( $params ))
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;
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;
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;
4690 $params[$paramKey] = $paramValue;
4692 ksort( $xparams, SORT_STRING
);
4693 foreach( $xparams as $paramKey => $paramValue ) {
4694 if( ctype_digit( (string) $paramKey ))
4695 $attr2 .= $this->intAttrDelimiter
.$paramValue;
4697 $attr2 .= $this->intAttrDelimiter
."$paramKey=$paramValue";
4699 if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) {
4700 $attr1 .= $this->intAttrDelimiter
.'FMTTYPE='.$params['FMTTYPE'].$attr2;
4703 if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) {
4704 if( !empty( $attr2 )) {
4708 $attr1 .= $this->intAttrDelimiter
.'ENCODING='.$params['ENCODING'];
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'];
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;
4723 if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) {
4724 $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ?
'' : '"';
4725 $attr1 .= $this->intAttrDelimiter
.'DIR='.$delim.$params['DIR'].$delim;
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;
4733 if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) {
4734 $attrLANG .= $this->intAttrDelimiter
.'LANGUAGE='.$params['LANGUAGE'];
4735 $LANGattrExist = TRUE;
4737 if( !$LANGattrExist ) {
4738 $lang = $this->getConfig( 'language' );
4739 if(( $CNattrExist ||
$LANGattrKey ) && $lang )
4740 $attrLANG .= $this->intAttrDelimiter
.'LANGUAGE='.$lang;
4742 return $attr1.$attrLANG.$attr2;
4745 * creates formatted output for calendar component property data value type recur
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
4753 function _format_recur( $recurlabel, $recurdata ) {
4755 foreach( $recurdata as $therule ) {
4756 if( empty( $therule['value'] )) {
4757 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel );
4760 $attributes = ( isset( $therule['params'] )) ?
$this->_createParams( $therule['params'] ) : null;
4761 $content1 = $content2 = null;
4762 foreach( $therule['value'] as $rulelabel => $rulevalue ) {
4763 switch( $rulelabel ) {
4765 $content1 .= "FREQ=$rulevalue";
4769 $parno = ( isset( $rulevalue['hour'] )) ?
7 : 3;
4770 $content2 .= ';UNTIL='.iCalUtilityFunctions
::_date2strdate( $rulevalue, $parno );
4776 $content2 .= ";$rulelabel=$rulevalue";
4787 $content2 .= ";$rulelabel=";
4788 if( is_array( $rulevalue )) {
4789 foreach( $rulevalue as $vix => $valuePart ) {
4790 $content2 .= ( $vix ) ?
',' : null;
4791 $content2 .= $valuePart;
4795 $content2 .= $rulevalue;
4799 $content2 .= ";$rulelabel=";
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;
4809 $content22 .= $valuePart2;
4811 $content2 .= $content21.$content22;
4815 $content2 .= ( $bydaycnt ) ?
',' : null;
4816 if( 'DAY' != strtoupper( $vix ))
4817 $content21 .= $valuePart;
4819 $content22 .= $valuePart;
4822 $content2 .= $content21.$content22;
4828 $content2 .= ";$rulelabel=$rulevalue";
4833 $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 );
4838 * check if property not exists within component
4840 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4841 * @since 2.5.1 - 2008-10-15
4842 * @param string $propName
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;
4856 /*********************************************************************************/
4857 /*********************************************************************************/
4859 * get general component config variables or info about subcomponents
4861 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4862 * @since 2.9.6 - 2011-05-14
4863 * @param mixed $config
4866 function getConfig( $config = FALSE) {
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' );
4878 switch( strtoupper( $config )) {
4880 return $this->allowEmpty
;
4883 unset( $this->compix
);
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' );
4898 return $this->format
;
4901 // get language for calendar component as defined in [RFC 1766]
4902 return $this->language
;
4910 if( !in_array( $this->objName
, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
4911 if( empty( $this->uid
['value'] )) $this->_makeuid();
4913 if( empty( $this->dtstamp
)) $this->_makeDtstamp();
4914 $output['DTSTAMP'] = 1;
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
);
4961 case 'SETPROPERTYNAMES':
4962 return array_keys( $this->getConfig( 'propinfo' ));
4965 return $this->dtzid
;
4968 if( empty( $this->unique_id
))
4969 $this->unique_id
= ( isset( $_SERVER['SERVER_NAME'] )) ?
gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
4970 return $this->unique_id
;
4975 * general component config setting
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
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] ))
4991 unset( $config[$k] );
4995 foreach( $config as $cKey => $cValue ) {
4996 if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate ))
5002 switch( strtoupper( $config )) {
5004 $this->allowEmpty
= $value;
5005 $subcfg = array( 'ALLOWEMPTY' => $value );
5009 $value = trim( strtolower( $value ));
5010 $this->format
= $value;
5011 $this->_createFormat();
5012 $subcfg = array( 'FORMAT' => $value );
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 );
5026 $this->_createFormat();
5027 $subcfg = array( 'NL' => $value );
5031 $this->dtzid
= $value;
5032 $subcfg = array( 'TZID' => $value );
5036 $value = trim( $value );
5037 $this->unique_id
= $value;
5038 $subcfg = array( 'UNIQUE_ID' => $value );
5041 default: // any unvalid config key.. .
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 );
5051 $this->components
[$cix] = $component->copy(); // PHP4 compliant
5057 /*********************************************************************************/
5059 * delete component property value
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
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' ))) {
5073 $propix = ( isset( $this->propdelix
[$propName] ) && ( 'X-PROP' != $propName )) ?
$this->propdelix
[$propName] +
2 : 1;
5074 $this->propdelix
[$propName] = --$propix;
5077 switch( $propName ) {
5079 if( !empty( $this->action
)) {
5085 return $this->deletePropertyM( $this->attach
, $this->propdelix
[$propName] );
5088 return $this->deletePropertyM( $this->attendee
, $this->propdelix
[$propName] );
5091 return $this->deletePropertyM( $this->categories
, $this->propdelix
[$propName] );
5094 if( !empty( $this->class )) {
5100 return $this->deletePropertyM( $this->comment
, $this->propdelix
[$propName] );
5103 if( !empty( $this->completed
)) {
5104 $this->completed
= '';
5109 return $this->deletePropertyM( $this->contact
, $this->propdelix
[$propName] );
5112 if( !empty( $this->created
)) {
5113 $this->created
= '';
5118 return $this->deletePropertyM( $this->description
, $this->propdelix
[$propName] );
5121 if( !empty( $this->dtend
)) {
5127 if( in_array( $this->objName
, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5129 if( !empty( $this->dtstamp
)) {
5130 $this->dtstamp
= '';
5135 if( !empty( $this->dtstart
)) {
5136 $this->dtstart
= '';
5141 if( !empty( $this->due
)) {
5147 if( !empty( $this->duration
)) {
5148 $this->duration
= '';
5153 return $this->deletePropertyM( $this->exdate
, $this->propdelix
[$propName] );
5156 return $this->deletePropertyM( $this->exrule
, $this->propdelix
[$propName] );
5159 return $this->deletePropertyM( $this->freebusy
, $this->propdelix
[$propName] );
5162 if( !empty( $this->geo
)) {
5167 case 'LAST-MODIFIED':
5168 if( !empty( $this->lastmodified
)) {
5169 $this->lastmodified
= '';
5174 if( !empty( $this->location
)) {
5175 $this->location
= '';
5180 if( !empty( $this->organizer
)) {
5181 $this->organizer
= '';
5185 case 'PERCENT-COMPLETE':
5186 if( !empty( $this->percentcomplete
)) {
5187 $this->percentcomplete
= '';
5192 if( !empty( $this->priority
)) {
5193 $this->priority
= '';
5198 return $this->deletePropertyM( $this->rdate
, $this->propdelix
[$propName] );
5200 case 'RECURRENCE-ID':
5201 if( !empty( $this->recurrenceid
)) {
5202 $this->recurrenceid
= '';
5207 return $this->deletePropertyM( $this->relatedto
, $this->propdelix
[$propName] );
5210 if( !empty( $this->repeat
)) {
5215 case 'REQUEST-STATUS':
5216 return $this->deletePropertyM( $this->requeststatus
, $this->propdelix
[$propName] );
5219 return $this->deletePropertyM( $this->resources
, $this->propdelix
[$propName] );
5222 return $this->deletePropertyM( $this->rrule
, $this->propdelix
[$propName] );
5225 if( !empty( $this->sequence
)) {
5226 $this->sequence
= '';
5231 if( !empty( $this->status
)) {
5237 if( !empty( $this->summary
)) {
5238 $this->summary
= '';
5243 if( !empty( $this->transp
)) {
5249 if( !empty( $this->trigger
)) {
5250 $this->trigger
= '';
5255 if( !empty( $this->tzid
)) {
5261 return $this->deletePropertyM( $this->tzname
, $this->propdelix
[$propName] );
5263 case 'TZOFFSETFROM':
5264 if( !empty( $this->tzoffsetfrom
)) {
5265 $this->tzoffsetfrom
= '';
5270 if( !empty( $this->tzoffsetto
)) {
5271 $this->tzoffsetto
= '';
5276 if( !empty( $this->tzurl
)) {
5282 if( in_array( $this->objName
, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5284 if( !empty( $this->uid
)) {
5290 if( !empty( $this->url
)) {
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 ))
5305 if( count( $this->xprop
) <= $propix ) { unset( $this->propdelix
[$propName] ); return FALSE; }
5307 foreach( $this->xprop
as $xpropkey => $xpropvalue ) {
5308 if( $propix != $xpropno )
5309 $reduced[$xpropkey] = $xpropvalue;
5313 $this->xprop
= $reduced;
5314 if( empty( $this->xprop
)) {
5315 unset( $this->propdelix
[$propName] );
5322 /*********************************************************************************/
5324 * delete component property value, fixing components with multiple occurencies
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
5332 function deletePropertyM( & $multiprop, & $propix ) {
5333 if( isset( $multiprop[$propix] ))
5334 unset( $multiprop[$propix] );
5335 if( empty( $multiprop )) {
5344 * get component property value/params
5346 * if property has multiply values, consequtive function calls are needed
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
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 ))
5362 if( 0.0 < $geo['latitude'] )
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'] )
5371 $sign = ( 0.0 > $geo['longitude'] ) ?
'-' : '';
5372 return $content.$sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/'; // sprintf && lpad && float && sign !"#¤%&/(
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' ))) {
5379 $propix = ( isset( $this->propix
[$propName] )) ?
$this->propix
[$propName] +
2 : 1;
5380 $this->propix
[$propName] = --$propix;
5382 switch( $propName ) {
5384 if( !empty( $this->action
['value'] )) return ( $inclParam ) ?
$this->action
: $this->action
['value'];
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 )))
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'];
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 )))
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'];
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 )))
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'];
5411 if( !empty( $this->class['value'] )) return ( $inclParam ) ?
$this->class : $this->class['value'];
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 )))
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'];
5422 if( !empty( $this->completed
['value'] )) return ( $inclParam ) ?
$this->completed
: $this->completed
['value'];
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 )))
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'];
5433 if( !empty( $this->created
['value'] )) return ( $inclParam ) ?
$this->created
: $this->created
['value'];
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 )))
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'];
5444 if( !empty( $this->dtend
['value'] )) return ( $inclParam ) ?
$this->dtend
: $this->dtend
['value'];
5447 if( in_array( $this->objName
, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5449 if( !isset( $this->dtstamp
['value'] ))
5450 $this->_makeDtstamp();
5451 return ( $inclParam ) ?
$this->dtstamp
: $this->dtstamp
['value'];
5454 if( !empty( $this->dtstart
['value'] )) return ( $inclParam ) ?
$this->dtstart
: $this->dtstart
['value'];
5457 if( !empty( $this->due
['value'] )) return ( $inclParam ) ?
$this->due
: $this->due
['value'];
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;
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 )))
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'];
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 )))
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'];
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 )))
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'];
5489 if( !empty( $this->geo
['value'] )) return ( $inclParam ) ?
$this->geo
: $this->geo
['value'];
5491 case 'LAST-MODIFIED':
5492 if( !empty( $this->lastmodified
['value'] )) return ( $inclParam ) ?
$this->lastmodified
: $this->lastmodified
['value'];
5495 if( !empty( $this->location
['value'] )) return ( $inclParam ) ?
$this->location
: $this->location
['value'];
5498 if( !empty( $this->organizer
['value'] )) return ( $inclParam ) ?
$this->organizer
: $this->organizer
['value'];
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'];
5504 if( !empty( $this->priority
['value'] ) ||
( isset( $this->priority
['value'] ) && ('0' == $this->priority
['value'] ))) return ( $inclParam ) ?
$this->priority
: $this->priority
['value'];
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 )))
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'];
5514 case 'RECURRENCE-ID':
5515 if( !empty( $this->recurrenceid
['value'] )) return ( $inclParam ) ?
$this->recurrenceid
: $this->recurrenceid
['value'];
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 )))
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'];
5526 if( !empty( $this->repeat
['value'] ) ||
( isset( $this->repeat
['value'] ) && ( '0' == $this->repeat
['value'] ))) return ( $inclParam ) ?
$this->repeat
: $this->repeat
['value'];
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 )))
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'];
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 )))
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'];
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 )))
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'];
5553 if( isset( $this->sequence
['value'] ) && ( isset( $this->sequence
['value'] ) && ( '0' <= $this->sequence
['value'] ))) return ( $inclParam ) ?
$this->sequence
: $this->sequence
['value'];
5556 if( !empty( $this->status
['value'] )) return ( $inclParam ) ?
$this->status
: $this->status
['value'];
5559 if( !empty( $this->summary
['value'] )) return ( $inclParam ) ?
$this->summary
: $this->summary
['value'];
5562 if( !empty( $this->transp
['value'] )) return ( $inclParam ) ?
$this->transp
: $this->transp
['value'];
5565 if( !empty( $this->trigger
['value'] )) return ( $inclParam ) ?
$this->trigger
: $this->trigger
['value'];
5568 if( !empty( $this->tzid
['value'] )) return ( $inclParam ) ?
$this->tzid
: $this->tzid
['value'];
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 )))
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'];
5578 case 'TZOFFSETFROM':
5579 if( !empty( $this->tzoffsetfrom
['value'] )) return ( $inclParam ) ?
$this->tzoffsetfrom
: $this->tzoffsetfrom
['value'];
5582 if( !empty( $this->tzoffsetto
['value'] )) return ( $inclParam ) ?
$this->tzoffsetto
: $this->tzoffsetto
['value'];
5585 if( !empty( $this->tzurl
['value'] )) return ( $inclParam ) ?
$this->tzurl
: $this->tzurl
['value'];
5588 if( in_array( $this->objName
, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5590 if( empty( $this->uid
['value'] ))
5592 return ( $inclParam ) ?
$this->uid
: $this->uid
['value'];
5595 if( !empty( $this->url
['value'] )) return ( $inclParam ) ?
$this->url
: $this->url
['value'];
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'] );
5604 if( empty( $this->xprop
)) return FALSE;
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'] );
5613 return FALSE; // not found ??
5619 * returns calendar property unique values for 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO' or 'RESOURCES' and for each, number of occurrence
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
5626 function _getProperties( $propName, & $output ) {
5627 if( empty( $output ))
5629 if( !in_array( strtoupper( $propName ), array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' )))
5631 while( FALSE !== ( $content = $this->getProperty( $propName ))) {
5632 if( empty( $content ))
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;
5644 $output[$thePart] +
= 1;
5649 $part = trim( $part );
5650 if( !isset( $output[$part] ))
5653 $output[$part] +
= 1;
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;
5665 $output[$thePart] +
= 1;
5668 } // end elseif( FALSE !== strpos( $content, ',' ))
5670 $content = trim( $content );
5671 if( !empty( $content )) {
5672 if( !isset( $output[$content] ))
5673 $output[$content] = 1;
5675 $output[$content] +
= 1;
5682 * general component property setting
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!
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] )))
5698 $arglist[0] = strtoupper( $arglist[0] );
5699 for( $argix=$numargs; $argix < 12; $argix++
) {
5700 if( !isset( $arglist[$argix] ))
5701 $arglist[$argix] = null;
5703 switch( $arglist[0] ) {
5705 return $this->setAction( $arglist[1], $arglist[2] );
5707 return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] );
5709 return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] );
5711 return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] );
5713 return $this->setClass( $arglist[1], $arglist[2] );
5715 return $this->setComment( $arglist[1], $arglist[2], $arglist[3] );
5717 return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5719 return $this->setContact( $arglist[1], $arglist[2], $arglist[3] );
5721 return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5723 return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] );
5725 return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5727 return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5729 return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5731 return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5733 return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] );
5735 return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] );
5737 return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] );
5739 return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] );
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] );
5745 return $this->setLocation( $arglist[1], $arglist[2] );
5747 return $this->setOrganizer( $arglist[1], $arglist[2] );
5748 case 'PERCENT-COMPLETE':
5749 return $this->setPercentComplete( $arglist[1], $arglist[2] );
5751 return $this->setPriority( $arglist[1], $arglist[2] );
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] );
5757 return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] );
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] );
5763 return $this->setResources( $arglist[1], $arglist[2], $arglist[3] );
5765 return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] );
5767 return $this->setSequence( $arglist[1], $arglist[2] );
5769 return $this->setStatus( $arglist[1], $arglist[2] );
5771 return $this->setSummary( $arglist[1], $arglist[2] );
5773 return $this->setTransp( $arglist[1], $arglist[2] );
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] );
5777 return $this->setTzid( $arglist[1], $arglist[2] );
5779 return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] );
5780 case 'TZOFFSETFROM':
5781 return $this->setTzoffsetfrom( $arglist[1], $arglist[2] );
5783 return $this->setTzoffsetto( $arglist[1], $arglist[2] );
5785 return $this->setTzurl( $arglist[1], $arglist[2] );
5787 return $this->setUid( $arglist[1], $arglist[2] );
5789 return $this->setUrl( $arglist[1], $arglist[2] );
5791 return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
5795 /*********************************************************************************/
5797 * parse component unparsed data into properties
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
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 ));
5812 elseif( !isset( $this->unparsed
))
5813 $unparsedtext = array();
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] );
5824 $this->unparsed
= array();
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();
5834 elseif( 'END:DAYLIGHT' == strtoupper( substr( $line, 0, 12 ))) {
5835 if( 1 != $subsync ) return FALSE;
5836 $this->components
[] = $comp->copy();
5839 elseif( 'END:STANDARD' == strtoupper( substr( $line, 0, 12 ))) {
5840 if( 1 != $subsync ) return FALSE;
5841 array_unshift( $this->components
, $comp->copy());
5844 elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) { // end:<component>
5845 if( 1 != $compsync ) return FALSE;
5847 $this->components
[] = $comp->copy();
5849 break; /* skip trailing empty lines */
5851 elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) {
5852 $comp = new valarm( $config);
5855 elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) {
5856 $comp = new vtimezone( 'standard', $config );
5859 elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) {
5860 $comp = new vtimezone( 'daylight', $config );
5863 elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) // begin:<component>
5866 $comp->unparsed
[] = $line;
5869 $this->components
[] = $comp->copy();
5871 /* concatenate property values spread over several lines */
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;
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 );
5898 while( isset( $line[$cix] )) {
5899 if( in_array( $line[$cix], array( ':', ';' )))
5902 $propname .= $line[$cix];
5905 if(( 'x-' == substr( $propname, 0, 2 )) ||
( 'X-' == substr( $propname, 0, 2 ))) {
5906 $propname2 = $propname;
5909 if( !in_array( strtolower( $propname ), $propnames )) // skip non standard property names
5911 /* rest of the line is opt.params and value */
5912 $line = substr( $line, $cix );
5913 /* separate attributes from value */
5916 $clen = strlen( $line );
5917 $WithinQuotes = FALSE;
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:' ) &&
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 )) {
5933 break; // an URI with a portnr!!
5938 $line = substr( $line, ( $cix +
1 ));
5943 if( '"' == $line[$cix] )
5944 $WithinQuotes = ( FALSE === $WithinQuotes ) ?
TRUE : FALSE;
5945 if( ';' == $line[$cix] )
5946 $attr[++
$attrix] = null;
5948 $attr[$attrix] .= $line[$cix];
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];
5958 $propattr[] = $attribute;
5960 /* call setProperty( $propname.. . */
5961 switch( strtoupper( $propname )) {
5963 foreach( $propattr as $pix => $attr ) {
5964 if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' )))
5966 $attr2 = explode( ',', $attr );
5967 if( 1 < count( $attr2 ))
5968 $propattr[$pix] = $attr2;
5970 $this->setProperty( $propname, $line, $propattr );
5973 $propname = ( isset( $propname2 )) ?
$propname2 : $propname;
5974 unset( $propname2 );
5977 if( FALSE !== strpos( $line, ',' )) {
5978 $content = array( 0 => '' );
5980 while( FALSE !== substr( $line, $lix, 1 )) {
5981 if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) {
5983 $content[$cix] = '';
5986 $content[$cix] .= $line[$lix];
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 );
5997 $line = reset( $content );
6006 $this->setProperty( $propname, iCalUtilityFunctions
::_strunrep( $line ), $propattr );
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
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;
6027 $this->setProperty( $propname, $fbtype, $values, $propattr );
6030 $value = explode( ';', $line, 2 );
6031 if( 2 > count( $value ))
6033 $this->setProperty( $propname, $value[0], $value[1], $propattr );
6036 $values = ( !empty( $line )) ?
explode( ',', $line ) : null;
6037 $this->setProperty( $propname, $values, $propattr );
6040 if( empty( $line )) {
6041 $this->setProperty( $propname, $line, $propattr );
6044 $values = explode( ',', $line );
6045 foreach( $values as $vix => $value ) {
6046 $value2 = explode( '/', $value );
6047 if( 1 < count( $value2 ))
6048 $values[$vix] = $value2;
6050 $this->setProperty( $propname, $values, $propattr );
6054 $values = explode( ';', $line );
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 ) {
6063 $value4 = explode( ',', $value3[1] );
6064 if( 1 < count( $value4 )) {
6065 foreach( $value4 as $v5ix => $value5 ) {
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 ));
6078 $value6['DAY'] = $dayname;
6079 $value4[$v5ix] = $value6;
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 ));
6095 $value4['DAY'] = $dayname;
6097 $recur[$rulelabel] = $value4;
6101 $value4 = explode( ',', $value3[1] );
6102 if( 1 < count( $value4 ))
6103 $value3[1] = $value4;
6104 $recur[$rulelabel] = $value3[1];
6107 } // end - switch $rulelabel
6108 } // end - foreach( $values.. .
6109 $this->setProperty( $propname, $recur, $propattr );
6112 case 'CLASSIFICATION':
6119 $line = iCalUtilityFunctions
::_strunrep( $line );
6121 $this->setProperty( $propname, $line, $propattr );
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();
6136 /*********************************************************************************/
6137 /*********************************************************************************/
6139 * return a copy of this component
6141 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6142 * @since 2.15.4 - 2012-10-18
6146 return unserialize( serialize( $this ));
6148 /*********************************************************************************/
6149 /*********************************************************************************/
6151 * delete calendar subcomponent from component container
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
6159 function deleteComponent( $arg1, $arg2=FALSE ) {
6160 if( !isset( $this->components
)) return FALSE;
6161 $argType = $index = null;
6162 if ( ctype_digit( (string) $arg1 )) {
6164 $index = (int) $arg1 - 1;
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;
6171 foreach ( $this->components
as $cix => $component) {
6172 if( empty( $component )) continue;
6173 if(( 'INDEX' == $argType ) && ( $index == $cix )) {
6174 unset( $this->components
[$cix] );
6177 elseif( $argType == $component->objName
) {
6178 if( $index == $cix2dC ) {
6179 unset( $this->components
[$cix] );
6184 elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
6185 unset( $this->components
[$cix] );
6192 * get calendar component subcomponent from component container
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
6200 function getComponent ( $arg1=FALSE, $arg2=FALSE ) {
6201 if( !isset( $this->components
)) return FALSE;
6202 $index = $argType = null;
6205 $index = $this->compix
['INDEX'] =
6206 ( isset( $this->compix
['INDEX'] )) ?
$this->compix
['INDEX'] +
1 : 1;
6208 elseif ( ctype_digit( (string) $arg1 )) {
6210 $index = (int) $arg1;
6211 unset( $this->compix
);
6213 elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
6214 unset( $this->compix
['INDEX'] );
6215 $argType = strtolower( $arg1 );
6217 $index = $this->compix
[$argType] = ( isset( $this->compix
[$argType] )) ?
$this->compix
[$argType] +
1 : 1;
6219 $index = (int) $arg2;
6222 $ckeys = array_keys( $this->components
);
6223 if( !empty( $index) && ( $index > end( $ckeys )))
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();
6235 elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' )))
6236 return $component->copy();
6239 unset( $this->compix
);
6243 * add calendar component as subcomponent to container for subcomponents
6245 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6246 * @since 1.x.x - 2007-04-24
6247 * @param object $component calendar component
6250 function addSubComponent ( $component ) {
6251 $this->setComponent( $component );
6254 * create new calendar component subcomponent, already included within component
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)
6261 function & newComponent( $compType ) {
6262 $config = $this->getConfig();
6263 $keys = array_keys( $this->components
);
6264 $ix = end( $keys) +
1;
6265 switch( strtoupper( $compType )) {
6268 $this->components
[$ix] = new valarm( $config );
6271 array_unshift( $this->components
, new vtimezone( 'STANDARD', $config ));
6275 $this->components
[$ix] = new vtimezone( 'DAYLIGHT', $config );
6280 return $this->components
[$ix];
6283 * add calendar component as subcomponent to container for subcomponents
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
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' );
6300 if( !$arg1 ) { // plain insert, last in chain
6301 $this->components
[] = $component->copy();
6304 $argType = $index = null;
6305 if ( ctype_digit( (string) $arg1 )) { // index insert/replace
6307 $index = (int) $arg1 - 1;
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;
6313 // else if arg1 is set, arg1 must be an UID
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();
6321 elseif( $argType == $component2->objName
) { // component Type index insert/replace
6322 if( $index == $cix2sC ) {
6323 $this->components
[$cix] = $component->copy();
6328 elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
6329 $this->components
[$cix] = $component->copy();
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
);
6338 else /* not found.. . insert last in chain anyway .. .*/
6339 $this->components
[] = $component->copy();
6343 * creates formatted output for subcomponents
6345 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6346 * @since 2.11.20 - 2012-02-06
6347 * @param array $xcaldecl
6350 function createSubComponent() {
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 ))
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] ))
6362 $stdarr[$key] = $component->copy();
6364 elseif( 'daylight' == $component->objName
) {
6365 while( isset( $dlarr[$key] ))
6367 $dlarr[$key] = $component->copy();
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();
6375 ksort( $dlarr, SORT_NUMERIC
);
6376 foreach( $dlarr as $dl )
6377 $this->components
[] = $dl->copy();
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
);
6387 /*********************************************************************************/
6388 /*********************************************************************************/
6390 * class for calendar component VEVENT
6392 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6393 * @since 2.5.1 - 2008-10-12
6395 class vevent
extends calendarComponent
{
6426 // component subcomponents container
6429 * constructor for calendar component VEVENT object
6431 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6432 * @since 2.8.2 - 2011-05-01
6433 * @param array $config
6436 function vevent( $config = array()) {
6437 $this->calendarComponent();
6440 $this->attendee
= '';
6441 $this->categories
= '';
6443 $this->comment
= '';
6444 $this->contact
= '';
6445 $this->created
= '';
6446 $this->description
= '';
6447 $this->dtstart
= '';
6449 $this->duration
= '';
6453 $this->lastmodified
= '';
6454 $this->location
= '';
6455 $this->organizer
= '';
6456 $this->priority
= '';
6458 $this->recurrenceid
= '';
6459 $this->relatedto
= '';
6460 $this->requeststatus
= '';
6461 $this->resources
= '';
6463 $this->sequence
= '';
6465 $this->summary
= '';
6470 $this->components
= array();
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 );
6482 * create formatted output for calendar component VEVENT object instance
6484 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6485 * @since 2.10.16 - 2011-10-28
6486 * @param array $xcaldecl
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;
6533 /*********************************************************************************/
6534 /*********************************************************************************/
6536 * class for calendar component VTODO
6538 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6539 * @since 2.5.1 - 2008-10-12
6541 class vtodo
extends calendarComponent
{
6560 var $percentcomplete;
6573 // component subcomponents container
6576 * constructor for calendar component VTODO object
6578 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6579 * @since 2.8.2 - 2011-05-01
6580 * @param array $config
6583 function vtodo( $config = array()) {
6584 $this->calendarComponent();
6587 $this->attendee
= '';
6588 $this->categories
= '';
6590 $this->comment
= '';
6591 $this->completed
= '';
6592 $this->contact
= '';
6593 $this->created
= '';
6594 $this->description
= '';
6595 $this->dtstart
= '';
6597 $this->duration
= '';
6601 $this->lastmodified
= '';
6602 $this->location
= '';
6603 $this->organizer
= '';
6604 $this->percentcomplete
= '';
6605 $this->priority
= '';
6607 $this->recurrenceid
= '';
6608 $this->relatedto
= '';
6609 $this->requeststatus
= '';
6610 $this->resources
= '';
6612 $this->sequence
= '';
6614 $this->summary
= '';
6618 $this->components
= array();
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 );
6630 * create formatted output for calendar component VTODO object instance
6632 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6633 * @since 2.5.1 - 2008-11-07
6634 * @param array $xcaldecl
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;
6682 /*********************************************************************************/
6683 /*********************************************************************************/
6685 * class for calendar component VJOURNAL
6687 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6688 * @since 2.5.1 - 2008-10-12
6690 class vjournal
extends calendarComponent
{
6715 * constructor for calendar component VJOURNAL object
6717 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6718 * @since 2.8.2 - 2011-05-01
6719 * @param array $config
6722 function vjournal( $config = array()) {
6723 $this->calendarComponent();
6726 $this->attendee
= '';
6727 $this->categories
= '';
6729 $this->comment
= '';
6730 $this->contact
= '';
6731 $this->created
= '';
6732 $this->description
= '';
6733 $this->dtstart
= '';
6736 $this->lastmodified
= '';
6737 $this->organizer
= '';
6739 $this->recurrenceid
= '';
6740 $this->relatedto
= '';
6741 $this->requeststatus
= '';
6743 $this->sequence
= '';
6745 $this->summary
= '';
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 );
6759 * create formatted output for calendar component VJOURNAL object instance
6761 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6762 * @since 2.5.1 - 2008-10-12
6763 * @param array $xcaldecl
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;
6802 /*********************************************************************************/
6803 /*********************************************************************************/
6805 * class for calendar component VFREEBUSY
6807 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6808 * @since 2.5.1 - 2008-10-12
6810 class vfreebusy
extends calendarComponent
{
6822 // component subcomponents container
6825 * constructor for calendar component VFREEBUSY object
6827 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6828 * @since 2.8.2 - 2011-05-01
6829 * @param array $config
6832 function vfreebusy( $config = array()) {
6833 $this->calendarComponent();
6835 $this->attendee
= '';
6836 $this->comment
= '';
6837 $this->contact
= '';
6839 $this->dtstart
= '';
6840 $this->duration
= '';
6841 $this->freebusy
= '';
6842 $this->organizer
= '';
6843 $this->requeststatus
= '';
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 );
6857 * create formatted output for calendar component VFREEBUSY object instance
6859 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6860 * @since 2.3.1 - 2007-11-19
6861 * @param array $xcaldecl
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;
6888 /*********************************************************************************/
6889 /*********************************************************************************/
6891 * class for calendar component VALARM
6893 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6894 * @since 2.5.1 - 2008-10-12
6896 class valarm
extends calendarComponent
{
6907 * constructor for calendar component VALARM object
6909 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6910 * @since 2.8.2 - 2011-05-01
6911 * @param array $config
6914 function valarm( $config = array()) {
6915 $this->calendarComponent();
6919 $this->attendee
= '';
6920 $this->description
= '';
6921 $this->duration
= '';
6923 $this->summary
= '';
6924 $this->trigger
= '';
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 );
6937 * create formatted output for calendar component VALARM object instance
6939 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6940 * @since 2.5.1 - 2008-10-22
6941 * @param array $xcaldecl
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;
6964 /**********************************************************************************
6965 /*********************************************************************************/
6967 * class for calendar component VTIMEZONE
6969 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6970 * @since 2.5.1 - 2008-10-12
6972 class vtimezone
extends calendarComponent
{
6986 // component subcomponents container
6989 * constructor for calendar component VTIMEZONE object
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
6997 function vtimezone( $timezonetype=FALSE, $config = array()) {
6998 if( is_array( $timezonetype )) {
6999 $config = $timezonetype;
7000 $timezonetype = FALSE;
7002 if( !$timezonetype )
7003 $this->timezonetype
= 'VTIMEZONE';
7005 $this->timezonetype
= strtoupper( $timezonetype );
7006 $this->calendarComponent();
7008 $this->comment
= '';
7009 $this->dtstart
= '';
7010 $this->lastmodified
= '';
7015 $this->tzoffsetfrom
= '';
7016 $this->tzoffsetto
= '';
7020 $this->components
= array();
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 );
7032 * create formatted output for calendar component VTIMEZONE object instance
7034 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7035 * @since 2.5.1 - 2008-10-25
7036 * @param array $xcaldecl
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;
7062 /*********************************************************************************/
7063 /*********************************************************************************/
7065 * moving all utility (static) functions to a utility class
7066 * 20111223 - move iCalUtilityFunctions class to the end of the iCalcreator class file
7068 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7069 * @since 2.10.1 - 2011-07-16
7072 class iCalUtilityFunctions
{
7073 // Store the single instance of iCalUtilityFunctions
7074 private static $m_pInstance;
7076 // Private constructor to limit object instantiation to within the class
7077 private function __construct() {
7078 $m_pInstance = FALSE;
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();
7086 return self
::$m_pInstance;
7089 * ensures internal date-time/date format (keyed array) for an input date-time/date array (keyed or unkeyed)
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
7097 public static function _date_time_array( $datetime, $parno=FALSE ) {
7098 return iCalUtilityFunctions
::_chkDateArr( $datetime, $parno );
7100 public static function _chkDateArr( $datetime, $parno=FALSE ) {
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;
7109 switch ( $dateKey ) {
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;
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';
7131 * check date(-time) and params arrays for an opt. timezone and if it is a DATE-TIME or DATE (updates $parno and params)
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
7140 public static function _chkdatecfg( $theDate, & $parno, & $params ) {
7141 if( isset( $params['TZID'] ))
7143 elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))
7146 if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] ))
7148 if( is_array( $theDate )) {
7149 if( isset( $theDate['timestamp'] ))
7150 $tzid = ( isset( $theDate['tz'] )) ?
$theDate['tz'] : null;
7152 $tzid = ( isset( $theDate['tz'] )) ?
$theDate['tz'] : ( 7 == count( $theDate )) ?
end( $theDate ) : null;
7153 if( !empty( $tzid )) {
7155 if( !iCalUtilityFunctions
::_isOffset( $tzid ))
7156 $params['TZID'] = $tzid; // save only timezone
7158 elseif( !$parno && ( 3 == count( $theDate )) &&
7159 ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )))
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' ))))
7171 $date = iCalUtilityFunctions
::_strdate2date( $date, $parno );
7172 unset( $date['unparsedtext'] );
7173 if( !empty( $date['tz'] )) {
7175 if( !iCalUtilityFunctions
::_isOffset( $date['tz'] ))
7176 $params['TZID'] = $date['tz']; // save only timezone
7178 elseif( empty( $parno ))
7181 if( isset( $params['TZID'] ))
7186 * vcalendar sort callback function
7188 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7189 * @since 2.16.2 - 2012-12-17
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;
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])
7216 if (( (int) $a->srtk
[$k][$key] ) < ((int) $b->srtk
[$k][$key] ))
7218 elseif(( (int) $a->srtk
[$k][$key] ) > ((int) $b->srtk
[$k][$key] ))
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;
7231 * byte oriented line folding fix
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'
7237 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7238 * @since 2.12.17 - 2012-07-12
7239 * @param string $text
7243 public static function convEolChar( & $text, $nl ) {
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
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
7260 $outp .= $text[$cix]; // add any other byte
7266 * create a calendar timezone and standard/daylight components
7268 * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone:
7271 * TZID:Europe/Stockholm
7273 * DTSTART:20101031T020000
7274 * TZOFFSETFROM:+0200
7279 * DTSTART:20100328T030000
7280 * TZOFFSETFROM:+0100
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
7297 public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) {
7298 if( empty( $timezone ))
7300 if( !empty( $from ) && !is_int( $from ))
7302 if( !empty( $to ) && !is_int( $to ))
7305 $dtz = new DateTimeZone( $timezone );
7306 $transitions = $dtz->getTransitions();
7307 $utcTz = new DateTimeZone( 'UTC' );
7309 catch( Exception
$e ) { return FALSE; }
7311 $dates = array_keys( $calendar->getProperty( 'dtstart' ));
7312 if( empty( $dates ))
7313 $dates = array( date( 'Ymd' ));
7315 if( !empty( $from ))
7316 $dateFrom = new DateTime( "@$from" ); // set lowest date (UTC)
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
7323 $dateFromYmd = $dateFrom->format('Y-m-d' );
7325 $dateTo = new DateTime( "@$to" ); // set end date (UTC)
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
7332 $dateToYmd = $dateTo->format('Y-m-d' );
7334 $transTemp = array();
7335 $prevOffsetfrom = 0;
7336 $stdIx = $dlghtIx = null;
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;
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] );
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'];
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'];
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 );
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;
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 );
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'] );
7420 * creates formatted output for calendar component property data value type date/date-time
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
7428 public static function _format_date_time( $datetime, $parno=6 ) {
7429 return iCalUtilityFunctions
::_date2strdate( $datetime, $parno );
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'] ))
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'] );
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'] )
7453 elseif( iCalUtilityFunctions
::_isOffset( $datetime['tz'] )) {
7455 $offset = iCalUtilityFunctions
::_tz2offset( $datetime['tz'] );
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' );
7462 catch( Exception
$e ) {
7463 $output = date( 'Ymd\THis', mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year'] ));
7472 * convert a date/datetime (array) to timestamp
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
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;
7493 $wtz = $datetime['tz'];
7495 if(( 'Z' == $wtz ) ||
( 'GMT' == strtoupper( $wtz )))
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' );
7505 catch( Exception
$e ) {
7506 $output = mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] );
7511 * ensures internal duration format for input in array format
7513 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7514 * @since 2.14.1 - 2012-09-25
7515 * @param array $duration
7518 public static function _duration_array( $duration ) {
7519 return iCalUtilityFunctions
::_duration2arr( $duration );
7521 public static function _duration2arr( $duration ) {
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 ));
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;
7549 if( isset( $output['week'] ) && ( 0 < $output['week'] )) {
7550 unset( $output['day'], $output['hour'], $output['min'], $output['sec'] );
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'] );
7566 * convert startdate+duration to a array format datetime
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
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;
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'] );
7595 * ensures internal duration format for an input string (iCal) formatted duration
7597 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7598 * @since 2.14.1 - 2012-09-25
7599 * @param string $duration
7602 public static function _duration_string( $duration ) {
7603 return iCalUtilityFunctions
::_durationStr2arr( $duration );
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 );
7611 return false; // no leading P !?!?
7613 $duration = substr( $duration, 1 ); // skip P
7614 $duration = str_replace ( 't', 'T', $duration );
7615 $duration = str_replace ( 'T', '', $duration );
7618 for( $ix=0; $ix < strlen( $duration ); $ix++
) {
7619 switch( strtoupper( substr( $duration, $ix, 1 ))) {
7621 $output['week'] = $val;
7625 $output['day'] = $val;
7629 $output['hour'] = $val;
7633 $output['min'] = $val;
7637 $output['sec'] = $val;
7641 if( !ctype_digit( substr( $duration, $ix, 1 )))
7642 return false; // unknown duration control character !?!?
7644 $val .= substr( $duration, $ix, 1 );
7647 return iCalUtilityFunctions
::_duration2arr( $output );
7650 * creates formatted output for calendar component property data value type duration
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 )
7657 public static function _format_duration( $duration ) {
7658 return iCalUtilityFunctions
::_duration2str( $duration );
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'] ))
7669 if( isset( $duration['week'] ) && ( 0 < $duration['week'] ))
7670 return 'P'.$duration['week'].'W';
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'] ))) {
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';
7682 if( 'P' == $output )
7683 $output = 'PT0H0M0S';
7687 * removes expkey+expvalue from array and returns hitval (if found) else returns elseval
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
7699 public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) {
7702 if( !is_array( $array ) ||
( 0 == count( $array )))
7704 foreach( $array as $key => $value ) {
7705 if( strtoupper( $expkey ) == strtoupper( $key )) {
7706 if( !$expval ||
( strtoupper( $expval ) == strtoupper( $array[$key] ))) {
7707 unset( $array[$key] );
7715 * checks if input contains a (array formatted) date/time
7717 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7718 * @since 2.11.8 - 2012-01-20
7719 * @param array $input
7722 public static function _isArrayDate( $input ) {
7723 if( !is_array( $input ))
7725 if( isset( $input['week'] ) ||
( !in_array( count( $input ), array( 3, 6, 7 ))))
7727 if( 7 == count( $input ))
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'] ))
7733 if( in_array( 0, $input ))
7735 if(( 1970 > $input[0] ) ||
( 12 < $input[1] ) ||
( 31 < $input[2] ))
7737 if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) &&
7738 checkdate( (int) $input[1], (int) $input[2], (int) $input[0] ))
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'] );
7746 * checks if input array contains a timestamp date
7748 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7749 * @since 2.4.16 - 2008-10-18
7750 * @param array $input
7753 public static function _isArrayTimestampDate( $input ) {
7754 return ( is_array( $input ) && isset( $input['timestamp'] )) ?
TRUE : FALSE ;
7757 * controls if input string contains (trailing) UTC/iCal offset
7759 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7760 * @since 2.14.1 - 2012-09-21
7761 * @param string $input
7764 public static function _isOffset( $input ) {
7765 $input = trim( (string) $input );
7766 if( 'Z' == substr( $input, -1 ))
7768 elseif(( 5 <= strlen( $input )) &&
7769 ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) &&
7770 ( '0000' <= substr( $input, -4 )) && ( '9999' >= substr( $input, -4 )))
7772 elseif(( 7 <= strlen( $input )) &&
7773 ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) &&
7774 ( '000000' <= substr( $input, -6 )) && ( '999999' >= substr( $input, -6 )))
7779 * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone
7780 * matching (MS) UCT offset and time zone descriptors
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
7787 public static function ms2phpTZ( & $timezone ) {
7788 if( empty( $timezone ))
7790 $search = str_replace( '"', '', $timezone );
7791 $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search );
7792 if( '(UTC' != substr( $search, 0, 4 ))
7794 if( FALSE === ( $pos = strpos( $search, ')' )))
7796 $pos = strpos( $search, ')' );
7797 $searchOffset = substr( $search, 4, ( $pos - 4 ));
7798 $searchOffset = iCalUtilityFunctions
::_tz2offset( str_replace( ':', '', $searchOffset ));
7799 while( ' ' ==substr( $search, ( $pos +
1 )))
7801 $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos +
1 )) ));
7802 $searchWords = explode( ' ', $searchText );
7803 $timezone_abbreviations = DateTimeZone
::listAbbreviations();
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 ))
7811 $cWords = explode( '/', $transition['timezone_id'] );
7812 $cPrio = $hitCnt = $rank = 0;
7813 foreach( $cWords as $cWord ) {
7814 if( empty( $cWord ))
7818 foreach( $searchWords as $sWord ) {
7819 if( empty( $sWord ) ||
( 'time' == strtolower( $sWord )))
7822 if( strtolower( $cWord ) == strtolower( $sWord )) {
7824 $rank +
= ( $cPrio +
$sPrio );
7831 $hits[$rank][] = $transition['timezone_id'];
7835 unset( $timezone_abbreviations );
7839 foreach( $hits as $rank => $tzs ) {
7840 if( !empty( $tzs )) {
7841 $timezone = reset( $tzs );
7848 * transforms offset in seconds to [-/+]hhmm[ss]
7850 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
7852 * @param string $seconds
7855 public static function offsetSec2His( $seconds ) {
7856 if( '-' == substr( $seconds, 0, 1 )) {
7858 $seconds = substr( $seconds, 1 );
7860 elseif( '+' == substr( $seconds, 0, 1 )) {
7862 $seconds = substr( $seconds, 1 );
7867 $hour = (int) floor( $seconds / 3600 );
7870 $seconds = $seconds %
3600;
7871 $min = (int) floor( $seconds / 60 );
7874 $output = $hour.$min;
7875 $seconds = $seconds %
60;
7878 $output .= $seconds;
7880 $output .= '0'.$seconds;
7882 return $prefix.$output;
7885 * updates an array with dates based on a recur pattern
7887 * if missing, UNTIL is set 1 year from startdate (emergency break)
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
7897 * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start
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 );
7905 $enddate = $startdate;
7906 $enddate['year'] +
= 1;
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 );
7919 $recur['UNTIL'] = iCalUtilityFunctions
::_timestamp2date( $endDatets, 6 );
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.. .
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 */
7934 if( 'YEARLY' == $recur['FREQ'] )
7936 elseif( 'MONTHLY' == $recur['FREQ'] )
7938 elseif( 'WEEKLY' == $recur['FREQ'] )
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 );
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;
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
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
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'];
7981 iCalUtilityFunctions
::_stepdate( $wdate, $wdatets, $step);
7983 $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
7985 // echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test
7987 if( isset( $endDatets ) && ( $wdatets > $endDatets ))
7989 if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
7991 if( $year_old != $wdate['year'] ) {
7992 $year_old = $wdate['year'];
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'] )) {
8008 $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
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];
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']));
8024 $yeardaycnt = array();
8025 if( isset( $recur['BYWEEKNO'] ) ||
( $recur['FREQ'] == 'WEEKLY' )) {
8027 for( $d=31; $d > 25; $d-- ) { // get last weekno for year
8029 $weekno = $daycnts[12][$d]['weekno_up'];
8030 elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) {
8031 $weekno = $daycnts[12][$d]['weekno_up'];
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;
8041 $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
8042 for( $d = $mcnt; $d > 0; $d-- ) {
8043 if( isset( $recur['BYYEARDAY'] )) {
8045 $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
8047 if( isset( $recur['BYMONTHDAY'] )) {
8049 $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
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];
8058 if( isset( $recur['BYWEEKNO'] ) ||
( $recur['FREQ'] == 'WEEKLY' ))
8059 $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1);
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] )) {
8075 // echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
8076 iCalUtilityFunctions
::_stepdate( $wdate, $wdatets, $step);
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
8084 if( $updateOK && isset( $recur['BYMONTH'] ))
8085 $updateOK = iCalUtilityFunctions
::_recurBYcntcheck( $recur['BYMONTH']
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']
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'] )) {
8103 $m = $wdate['month'];
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'] )
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'] );
8120 if(( $daynoexists && $daynosw && $daynamesw ) ||
8121 ( !$daynoexists && !$daynosw && $daynamesw )) {
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 ###
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 ###
8128 foreach( $recur['BYDAY'] as $bydayvalue ) {
8129 $daynoexists = $daynosw = $daynamesw = FALSE;
8130 if( isset( $bydayvalue['DAY'] ) &&
8131 ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] ))
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'] );
8145 // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
8146 if(( $daynoexists && $daynosw && $daynamesw ) ||
8147 ( !$daynoexists && !$daynosw && $daynamesw )) {
8154 // echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ###
8155 /* check BYSETPOS */
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;
8163 $bysetposw2[] = $wdatets;
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;
8179 // echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
8180 $bysetposymd2[] = $wdatets;
8185 /* update result array if BYSETPOS is set */
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
8191 // echo "recur undate ".date('Y-m-d H:i:s',$wdatets)." okdatstart ".date('Y-m-d H:i:s',$startdatets)."<br />\n";//test
8196 iCalUtilityFunctions
::_stepdate( $wdate, $wdatets, $step);
8197 /* check if BYSETPOS is set for updating result array */
8198 if( $updateOK && isset( $recur['BYSETPOS'] )) {
8200 if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
8201 ( $bysetposYold != $wdate['year'] )) {
8203 $bysetposYold = $wdate['year'];
8205 elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] &&
8206 (( $bysetposYold != $wdate['year'] ) ||
( $bysetposMold != $wdate['month'] )))) {
8208 $bysetposYold = $wdate['year'];
8209 $bysetposMold = $wdate['month'];
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;
8218 elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
8219 (( $bysetposYold != $wdate['year'] ) ||
8220 ( $bysetposMold != $wdate['month'] ) ||
8221 ( $bysetposDold != $wdate['day'] ))) {
8223 $bysetposYold = $wdate['year'];
8224 $bysetposMold = $wdate['month'];
8225 $bysetposDold = $wdate['day'];
8228 if( isset( $recur['BYWEEKNO'] )) {
8229 $bysetposarr1 = & $bysetposw1;
8230 $bysetposarr2 = & $bysetposw2;
8233 $bysetposarr1 = & $bysetposymd1;
8234 $bysetposarr2 = & $bysetposymd2;
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);
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 ###
8251 if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
8254 // echo "<br />\n"; // test ###
8255 $bysetposarr1 = $bysetposarr2;
8256 $bysetposarr2 = array();
8261 public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
8262 if( is_array( $BYvalue ) &&
8263 ( in_array( $upValue, $BYvalue ) ||
in_array( $downValue, $BYvalue )))
8265 elseif(( $BYvalue == $upValue ) ||
( $BYvalue == $downValue ))
8270 public static function _recurIntervalIx( $freq, $date, $wkst ) {
8271 /* create interval index */
8274 $intervalix = $date['year'];
8277 $intervalix = $date['year'].'-'.$date['month'];
8280 $wdatets = iCalUtilityFunctions
::_date2timestamp( $date );
8281 $intervalix = (int) date( 'W', ( $wdatets +
$wkst ));
8285 $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day'];
8291 * convert input format for exrule and rrule to internal format
8293 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8294 * @since 2.14.1 - 2012-09-24
8295 * @param array $rexrule
8298 public static function _setRexrule( $rexrule ) {
8300 if( empty( $rexrule ))
8302 foreach( $rexrule as $rexrulelabel => $rexrulevalue ) {
8303 $rexrulelabel = strtoupper( $rexrulelabel );
8304 if( 'UNTIL' != $rexrulelabel )
8305 $input[$rexrulelabel] = $rexrulevalue;
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'] );
8319 $input[$rexrulelabel] = $d;
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'] );
8325 if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] ))
8326 $input[$rexrulelabel]['tz'] = 'Z';
8329 /* set recurrence rule specification in rfc2445 order */
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'] );
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;
8356 foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) {
8357 if( 'DAY' == strtoupper( $BYDAYx2 ))
8358 $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 );
8360 $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2;
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'];
8381 * convert format for input date to internal date with parameters
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
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;
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'] );
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'] );
8426 if( isset( $input['value']['tz'] ) && !iCalUtilityFunctions
::_isOffset( $input['value']['tz'] )) {
8427 $input['params']['TZID'] = $input['value']['tz'];
8428 unset( $input['value']['tz'] );
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 );
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'] )) {
8441 if( !iCalUtilityFunctions
::_isOffset( $year['tz'] ))
8442 $input['params']['TZID'] = $year['tz'];
8444 elseif( isset( $input['params']['TZID'] )) {
8445 $year['tz'] = $input['params']['TZID'];
8447 if( iCalUtilityFunctions
::_isOffset( $input['params']['TZID'] )) {
8448 unset( $input['params']['TZID'] );
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]
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'] );
8472 $input['params']['TZID'] = $input['value']['tz'];
8473 unset( $input['value']['tz'] );
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'] );
8482 } // end elseif( 8 <= strlen( trim( $year )))
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' ));
8490 elseif( is_array( $hour )) {
8491 $input['params'] = iCalUtilityFunctions
::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' ));
8492 $hour = $min = $sec = $tz = FALSE;
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'] );
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 );
8509 $input['value']['hour'] = ( $hour ) ?
$hour : '0';
8510 $input['value']['min'] = ( $min ) ?
$min : '0';
8511 $input['value']['sec'] = ( $sec ) ?
$sec : '0';
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'] );
8527 $input['params']['TZID'] = $input['value']['tz'];
8528 unset( $input['value']['tz'] );
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'] );
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'] );
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'] );
8549 unset( $input['value']['tz'] );
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'] );
8559 unset( $input['params']['TZID'] );
8562 unset( $input['value']['tz'], $input['params']['TZID'] );
8566 * convert format for input date (UTC) to internal date with parameters
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
8579 public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
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'] );
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' ));
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' ));
8605 $input['value'] = array( 'year' => $year
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'] );
8621 $input['params'] = iCalUtilityFunctions
::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
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';
8631 * check index and set (an indexed) content in multiple value array
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
8642 public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) {
8643 if( !is_array( $valArr )) $valArr = array();
8645 $index = $index - 1;
8646 elseif( 0 < count( $valArr )) {
8647 $keys = array_keys( $valArr );
8648 $index = end( $keys ) +
1;
8652 $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions
::_setParams( $params, $defaults ));
8656 * set input (formatted) parameters- component property attributes
8658 * default parameters can be set, if missing
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
8666 public static function _setParams( $params, $defaults=FALSE ) {
8667 if( !is_array( $params))
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 ));
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 );
8682 $input[strtoupper( $paramKey )] = $paramValue;
8684 if( is_array( $defaults )) {
8685 foreach( $defaults as $paramKey => $paramValue ) {
8686 if( !isset( $input[$paramKey] ))
8687 $input[$paramKey] = $paramValue;
8690 return (0 < count( $input )) ?
$input : null;
8693 * break lines at pos 75
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.
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.
8708 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8709 * @since 2.16.2 - 2012-12-18
8710 * @param string $value
8713 public static function _size75( $string, $nl ) {
8718 if( !isset( $tmp[$x] )) {
8719 $string .= $nl; // loop breakes here
8722 elseif(( 74 <= $cCnt ) && ( '\\' == $tmp[$x] ) && ( 'n' == $tmp[$x+
1] )) {
8723 $string .= $nl.' \n'; // don't break lines inside '\n'
8725 if( !isset( $tmp[$x] )) {
8731 elseif( 75 <= $cCnt ) {
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)
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] )) {
8744 $string .= $tmp[$x+
1];
8745 $x +
= 1; // add a two bytes character
8748 case(( $byte & 0xF0 ) == 0xE0 ): // characters U-00000800 - U-0000FFFF, mask 1110XXXX
8749 if( isset( $tmp[$x+
2] )) {
8751 $string .= $tmp[$x+
1].$tmp[$x+
2];
8752 $x +
= 2; // add a three bytes character
8755 case(( $byte & 0xF8 ) == 0xF0 ): // characters U-00010000 - U-001FFFFF, mask 11110XXX
8756 if( isset( $tmp[$x+
3] )) {
8758 $string .= $tmp[$x+
1].$tmp[$x+
2].$tmp[$x+
3];
8759 $x +
= 3; // add a four bytes character
8762 case(( $byte & 0xFC ) == 0xF8 ): // characters U-00200000 - U-03FFFFFF, mask 111110XX
8763 if( isset( $tmp[$x+
4] )) {
8765 $string .= $tmp[$x+
1].$tmp[$x+
2].$tmp[$x+
3].$tmp[$x+
4];
8766 $x +
= 4; // add a five bytes character
8769 case(( $byte & 0xFE ) == 0xFC ): // characters U-04000000 - U-7FFFFFFF, mask 1111110X
8770 if( isset( $tmp[$x+
5] )) {
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
8775 default: // add any other byte without counting up $cCnt
8777 } // end switch( TRUE )
8778 $x +
= 1; // next 'byte' to test
8779 } // end while( TRUE ) {
8783 * sort callback functions for exdate
8785 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8786 * @since 2.16.11 - 2013-01-12
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 );
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 );
8808 * sort callback functions for rdate
8810 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
8811 * @since 2.16.9 - 2013-01-12
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 );
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 );
8835 * step date, return updated date, array and timpstamp
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 )
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;
8858 * convert a date from specific string to array format
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
8865 public static function _strDate2arr( & $input ) {
8866 if( is_array( $input ))
8868 if( 5 > strlen( (string) $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 )))
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'] ))
8882 if( 8 == strlen( $work )) {
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 );
8892 if( 2 == substr_count( $work, ':' ))
8893 $work = str_replace( ':', '', $work );
8894 if( !ctype_digit( substr( $work, 0, 4 )))
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 )))
8901 if( ctype_digit( substr( $work, 4, 2 ))) {
8902 $temp['sec'] = substr( $work, 4, 2 );
8903 if(( 0 > $temp['sec'] ) ||
( $temp['sec'] > 59 ))
8911 if( $len < strlen( $work))
8912 $temp['tz'] = trim( substr( $work, 6 ));
8917 * ensures internal date-time/date format for input date-time/date in string fromat
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
8927 public static function _date_time_string( $datetime, $parno=FALSE ) {
8928 return iCalUtilityFunctions
::_strdate2date( $datetime, $parno, null );
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 );
8937 $len = strlen( $datetime );
8938 if( 'Z' == substr( $datetime, -1 )) {
8940 $datetime = trim( substr( $datetime, 0, ( $len - 1 )));
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 );
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 );
8954 elseif( empty( $wtz ) && ctype_digit( substr( $datetime, 0, 4 )) && ctype_digit( substr( $datetime, -2, 2 )) && iCalUtilityFunctions
::_strDate2arr( $datetime )) {
8955 $output = $datetime;
8957 $output['tz'] = 'Z';
8958 $output['unparsedtext'] = $unparseddatetime;
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.. . ?
8968 $tx--; // tz length counter
8970 if( 0 > $tx ) { // if any
8971 $tz = substr( $datetime, $tx );
8972 $datetime = trim( substr( $datetime, 0, $len +
$tx ));
8973 $len = strlen( $datetime );
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 )))) {
8982 $tz = null; // no tz for Y-m-d dates
8984 if( empty( $tz ) && !empty( $wtz ))
8986 if( 17 >= $len ) // any Y-m-d textual date
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;
8994 elseif( !empty( $wtz ))
8997 if(( 'Z' == $tz ) ||
( 'GMT' == strtoupper( $tz )))
8999 if( 0 < substr_count( $datetime, '-' ))
9000 $datetime = str_replace( '-', '/', $datetime );
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' );
9008 catch( Exception
$e ) {
9009 $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime ));
9011 } // end if( !empty( $tz ) && ( 17 < $len ))
9013 $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime ));
9014 // echo "<tr><td> <td colspan='3'>_strdate2date input=$datetime, tz=$tz, offset=$offset, wtz=$wtz, len=$len, prepDate=$datestring\n";
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;
9028 // return original string in the array in case strtotime failed to make sense of it
9029 $output['unparsedtext'] = $unparseddatetime;
9032 /********************************************************************************/
9034 * special characters management output
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
9043 public static function _strrep( $string, $format, $nl ) {
9046 $string = str_replace( '\n', $nl, $string);
9047 $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string ))));
9051 $specChars = array( 'n', 'N', 'r', ',', ';' );
9052 while( isset( $string[$pos] )) {
9053 if( FALSE === ( $pos = strpos( $string, "\\", $pos )))
9055 if( !in_array( substr( $string, $pos, 1 ), $specChars )) {
9056 $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos +
1 ));
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);
9082 * special characters management input (from iCal file)
9084 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9085 * @since 2.16.2 - 2012-12-18
9086 * @param string $string
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); // ??
9097 * convert timestamp to date array, default UTC or adjusted for offset/timezone
9099 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9100 * @since 2.15.1 - 2012-10-17
9101 * @param mixed $timestamp
9103 * @param string $wtz
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'];
9111 $tz = ( isset( $tz )) ?
$tz : $wtz;
9112 if( empty( $tz ) ||
( 'Z' == $tz ) ||
( 'GMT' == strtoupper( $tz )))
9114 elseif( iCalUtilityFunctions
::_isOffset( $tz )) {
9115 $offset = iCalUtilityFunctions
::_tz2offset( $tz );
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' );
9127 catch( Exception
$e ) {
9128 $date = date( 'Y-m-d-H-i-s', $timestamp );
9130 $date = explode( '-', $date );
9131 $output = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2] );
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';
9142 * convert timestamp (seconds) to duration in array format
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
9149 public static function _timestamp2duration( $timestamp ) {
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 );
9162 * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0)
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'
9172 public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) {
9173 if( is_array( $date ) && isset( $date['timestamp'] )) {
9175 $d = new DateTime( "@{$date['timestamp']}" ); // set UTC date
9176 $d->setTimezone(new DateTimeZone( $tzFrom )); // convert to 'from' date
9178 catch( Exception
$e ) { return FALSE; }
9181 if( iCalUtilityFunctions
::_isArrayDate( $date )) {
9182 if( isset( $date['tz'] ))
9183 unset( $date['tz'] );
9184 $date = iCalUtilityFunctions
::_date2strdate( iCalUtilityFunctions
::_chkDateArr( $date ));
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; }
9191 try { $d->setTimezone( new DateTimeZone( $tzTo )); }
9192 catch( Exception
$e ) { return FALSE; }
9193 $date = $d->format( $format );
9197 * convert offset, [+/-]HHmm[ss], to seconds, used when correcting UTC to localtime or v.v.
9199 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
9200 * @since 2.11.4 - 2012-01-11
9201 * @param string $offset
9204 public static function _tz2offset( $tz ) {
9205 $tz = trim( (string) $tz );
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 ))))
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;
9220 /*********************************************************************************/
9221 /* iCalcreator vCard helper functions */
9222 /*********************************************************************************/
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
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')
9235 function iCal2vCard( $email, $version='2.1', $directory=FALSE, $ext='vcf' ) {
9236 if( FALSE === ( $pos = strpos( $email, '@' )))
9239 if( DIRECTORY_SEPARATOR
!= substr( $directory, ( 0 - strlen( DIRECTORY_SEPARATOR
))))
9240 $directory .= DIRECTORY_SEPARATOR
;
9241 if( !is_dir( $directory ) ||
!is_writable( $directory ))
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 );
9250 if( FALSE !== ( $pos = strpos( $name, '.' ))) {
9251 $name = explode( '.', $name );
9252 foreach( $name as $k => $part )
9253 $name[$k] = ucfirst( $part );
9255 else { // split camelCase
9257 $name = array( $chars[0] );
9260 while( FALSE !== ( $char = substr( $chars, $x, 1 ))) {
9261 if( ctype_upper( $char )) {
9271 $FN = 'FN:'.implode( ' ', $name ).$nl;
9272 $name = array_reverse( $name );
9273 $N = 'N:'.array_shift( $name );
9275 while( NULL != ( $part = array_shift( $name ))) {
9276 if(( '4.0' != $version ) ||
( 4 > $scCnt ))
9280 while(( '4.0' == $version ) && ( 4 > $scCnt )) {
9285 $EMAIL = 'EMAIL:'.$email.$nl;
9287 $vCard = 'BEGIN:VCARD'.$nl;
9288 $vCard .= "VERSION:$version$nl";
9289 $vCard .= 'PRODID:-//kigkonsult.se '.ICALCREATOR_VERSION
."//$nl";
9293 $vCard .= 'REV:'.gmdate( 'Ymd\THis\Z' ).$nl;
9294 $vCard .= 'END:VCARD'.$nl;
9295 /* save each vCard as (unique) single file */
9297 $fname = $directory.preg_replace( '/[^a-z0-9.]/i', '', $email );
9300 while( is_file ( $fname.$dbl.'.'.$ext )) {
9304 if( FALSE === file_put_contents( $fname, $fname.$dbl.'.'.$ext ))
9313 * convert ATTENDEEs, CONTACTs and ORGANIZERs (in email format) to vCards
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')
9323 function iCal2vCards( & $calendar, $version='2.1', $directory=FALSE, $ext='vcf' ) {
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, '@' )))
9331 $propValue = str_replace( 'MAILTO:', '', $propValue );
9332 if( isset( $hits[$propValue] ))
9333 $hits[$propValue] +
= $occCnt;
9335 $hits[$propValue] = $occCnt;
9342 foreach( $hits as $email => $skip ) {
9343 $res = iCal2vCard( $email, $version, $directory, $ext );
9344 if( $directory && !$res )
9353 if( !empty( $output ))
9357 /*********************************************************************************/
9358 /* iCalcreator XML (rfc6321) helper functions */
9359 /*********************************************************************************/
9361 * format iCal XML output, rfc6321, using PHP SimpleXMLElement
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
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 );
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 ) {
9392 $subComps = array( 'valarm' );
9396 $subComps = array();
9399 $subComps = array( 'standard', 'daylight' );
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'] );
9418 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9419 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
9421 $content['params']['LANGUAGE'] = $langComp;
9423 $content['params']['LANGUAGE'] = $langCal;
9425 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
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'] );
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'] );
9441 _addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] );
9444 case 'request-status':
9445 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9446 if( !isset( $content['params']['LANGUAGE'] )) {
9448 $content['params']['LANGUAGE'] = $langComp;
9450 $content['params']['LANGUAGE'] = $langCal;
9452 _addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] );
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'] )
9461 elseif( 'PERIOD' == $content['params']['VALUE'] )
9464 unset( $content['params']['VALUE'] );
9465 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9474 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9475 if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
9477 $content['params']['LANGUAGE'] = $langComp;
9479 $content['params']['LANGUAGE'] = $langCal;
9481 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
9485 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9486 _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
9488 case 'created': // single occurence below, if set
9491 case 'last-modified':
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'] );
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'] );
9514 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9515 _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
9524 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9525 if((( 'location' == $prop ) ||
( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) {
9527 $content['params']['LANGUAGE'] = $langComp;
9529 $content['params']['LANGUAGE'] = $langCal;
9531 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
9535 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9536 _addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] );
9539 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
9540 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
9542 $content['params']['LANGUAGE'] = $langComp;
9544 $content['params']['LANGUAGE'] = $langCal;
9546 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
9549 case 'percent-complete':
9552 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9553 _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
9557 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
9558 _addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] );
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'] );
9579 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9580 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
9582 $content['params']['LANGUAGE'] = $langComp;
9584 $content['params']['LANGUAGE'] = $langCal;
9586 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
9591 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9592 if( !isset( $content['params']['LANGUAGE'] )) {
9594 $content['params']['LANGUAGE'] = $langComp;
9596 $content['params']['LANGUAGE'] = $langCal;
9598 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
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'] )
9607 elseif( 'PERIOD' == $content['params']['VALUE'] )
9610 unset( $content['params']['VALUE'] );
9611 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9615 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9616 _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
9618 case 'action': // single occurence below, if set
9621 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
9622 if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
9624 $content['params']['LANGUAGE'] = $langComp;
9626 $content['params']['LANGUAGE'] = $langCal;
9628 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
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'] );
9638 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9639 _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
9642 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9643 _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
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';
9653 if( !isset( $content['value']['relatedStart'] ) ||
( TRUE !== $content['value']['relatedStart'] ))
9654 $content['params']['RELATED'] = 'END';
9656 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
9660 case 'tzoffsetfrom':
9661 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9662 _addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] );
9665 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
9666 _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
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();
9677 * Add children to a SimpleXMLelement
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'
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 ));
9703 $p1 = $parameters->addChild( $param );
9706 case 'dir': $ptype = 'uri'; break;
9707 case 'delegated-from':
9708 case 'delegated-to':
9710 case 'sent-by': $ptype = 'cal-address'; break;
9711 case 'rsvp': $ptype = 'boolean'; break ;
9712 default: $ptype = 'text'; break;
9714 if( is_array( $parVal )) {
9715 foreach( $parVal as $pV )
9716 $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV ));
9719 $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal ));
9723 if( empty( $content ) && ( '0' != $content ))
9725 /** store content */
9728 $v = $child->addChild( $type, $content );
9733 $v = $child->addChild( $type, $content );
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 );
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'] ))
9753 $v = $child->addChild( $type, $str );
9757 $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ?
'-' : '';
9758 $v = $child->addChild( $type, $output.iCalUtilityFunctions
::_duration2str( $content ) );
9761 $v1 = $child->addChild( 'latitude', number_format( (float) $content['latitude'], 6, '.', '' ));
9762 $v1 = $child->addChild( 'longitude', number_format( (float) $content['longitude'], 6, '.', '' ));
9765 $v = $child->addChild( $type, $content );
9768 if( !is_array( $content ))
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'] ))
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'] ))
9780 $v2 = $v1->addChild( 'end', $str );
9783 $v2 = $v1->addChild( 'duration', iCalUtilityFunctions
::_duration2str( $period[1] ));
9787 foreach( $content as $rulelabel => $rulevalue ) {
9788 $rulelabel = strtolower( $rulelabel );
9789 switch( $rulelabel ) {
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'] );
9794 $str = sprintf( '%04d-%02d-%02d', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'] );
9795 $v = $child->addChild( $rulelabel, $str );
9805 if( is_array( $rulevalue )) {
9806 foreach( $rulevalue as $vix => $valuePart )
9807 $v = $child->addChild( $rulelabel, $valuePart );
9810 $v = $child->addChild( $rulelabel, $rulevalue );
9814 if( isset( $rulevalue['DAY'] )) {
9815 $str = ( isset( $rulevalue[0] )) ?
$rulevalue[0] : '';
9816 $str .= $rulevalue['DAY'];
9817 $p = $child->addChild( $rulelabel, $str );
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 );
9827 $p = $child->addChild( $rulelabel, $valuePart );
9837 $p = $child->addChild( $rulelabel, $rulevalue );
9839 } // end switch( $rulelabel )
9840 } // end foreach( $content as $rulelabel => $rulevalue )
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'] ));
9849 if( !is_array( $content ))
9850 $content = array( $content );
9851 foreach( $content as $part )
9852 $v = $child->addChild( $type, htmlspecialchars( $part ));
9857 $v = $child->addChild( $type, $content );
9860 if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) {
9861 $str = substr( $content, 0, 1 );
9862 $content = substr( $content, 1 );
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 );
9873 if( is_array( $content ))
9874 $content = implode( '', $content );
9875 $v = $child->addChild( 'unknown', htmlspecialchars( $content ));
9880 * parse xml string into iCalcreator instance
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
9888 function & XMLstr2iCal( $xmlstr, $iCalcfg=array()) {
9889 libxml_use_internal_errors( TRUE );
9890 $xml = simplexml_load_string( $xmlstr );
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;
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
;
9908 if( LIBXML_ERR_WARNING
!= $error->level
)
9910 libxml_clear_errors();
9912 return xml2iCal( $xml, $iCalcfg );
9915 * parse xml file into iCalcreator instance
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
9923 function & XMLfile2iCal( $xmlfile, $iCalcfg=array()) {
9924 libxml_use_internal_errors( TRUE );
9925 $xml = simplexml_load_file( $xmlfile );
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;
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
;
9942 if( LIBXML_ERR_WARNING
!= $error->level
)
9944 libxml_clear_errors();
9946 return xml2iCal( $xml, $iCalcfg );
9949 * parse SimpleXMLElement instance into iCalcreator instance
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
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 );
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 )))
9973 foreach( $calProp->children() as $calPropElem ) { // single calendar property
9974 if( 'parameters' == $calPropElem->getName())
9975 $params = _getXMLParams( $calPropElem );
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 )
9986 * parse SimpleXMLElement instance property parameters and return iCalcreator property parameter array
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
9993 function _getXMLParams( & $parameters ) {
9994 if( 1 > $parameters->count())
9997 foreach( $parameters->children() as $parameter ) { // single parameter key
9998 $key = strtoupper( $parameter->getName());
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 ));
10005 $params[$key] = $value;
10010 * parse SimpleXMLElement instance components, create iCalcreator component and update
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
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())
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 );
10031 } // end foreach( $component->children() as $compPart )
10034 * parse SimpleXMLElement instance property, create iCalcreator component property
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
10042 function _getXMLProperties( & $iCal, & $property ) {
10043 $propName = $property->getName();
10044 $value = $params = array();
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 );
10052 switch( $valueType ) {
10054 $value = reset( $propPart );
10058 case 'cal-address':
10059 $value = reset( $propPart );
10062 $params['VALUE'] = 'DATE';
10064 if(( 'exdate' == $propName ) ||
( 'rdate' == $propName ))
10065 $value[] = reset( $propPart );
10067 $value = reset( $propPart );
10070 $value = reset( $propPart );
10075 $value[$valueType] = reset( $propPart );
10078 $value = reset( $propPart );
10081 if( 'rdate' == $propName )
10082 $params['VALUE'] = 'PERIOD';
10084 foreach( $propPart->children() as $periodPart )
10085 $pData[] = reset( $periodPart );
10086 if( !empty( $pData ))
10095 $value[$valueType] = reset( $propPart );
10105 $value[$valueType][] = reset( $propPart );
10108 $byday = reset( $propPart );
10109 if( 2 == strlen( $byday ))
10110 $value[$valueType][] = array( 'DAY' => $byday );
10112 $day = substr( $byday, -2 );
10113 $key = substr( $byday, 0, ( strlen( $byday ) - 2 ));
10114 $value[$valueType][] = array( $key, 'DAY' => $day );
10119 $value[0] = reset( $propPart );
10121 case 'description':
10122 $value[1] = reset( $propPart );
10125 $value[2] = reset( $propPart );
10128 $text = str_replace( array( "\r\n", "\n\r", "\r", "\n"), '\n', reset( $propPart ));
10129 $value['text'][] = html_entity_decode( $text );
10134 $value = reset( $propPart );
10137 $value = str_replace( ':', '', reset( $propPart ));
10141 $value = html_entity_decode( reset( $propPart ));
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 );
10150 elseif( 'geo' == $propName )
10151 $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params );
10152 elseif( 'request-status' == $propName ) {
10153 if( !isset( $value[2] ))
10155 $iCal->setProperty( $propName, $value[0], $value[1], $value[2], $params );
10158 if( isset( $value['text'] ) && is_array( $value['text'] )) {
10159 if(( 'categories' == $propName ) ||
( 'resources' == $propName ))
10160 $value = $value['text'];
10162 $value = reset( $value['text'] );
10164 $iCal->setProperty( $propName, $value, $params );
10167 /*********************************************************************************/
10168 /* Additional functions to use with vtimezone components */
10169 /*********************************************************************************/
10172 * iCalcreator (kigkonsult.se/iCalcreator/index.php)
10173 * copyright (c) 2011 Yitzchok Lavi
10174 * icalcreator@onebigsystem.com
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.
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.
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
10191 * Additional functions to use with vtimezone components
10193 * Before calling the functions, set time zone 'GMT' ('date_default_timezone_set')!
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
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.
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'
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'],
10217 $timestamp['month'],
10221 // echo '<td colspan="4"> '."\n".'<tr><td> <td class="r">'.$timestamp.'<td class="r">'.$disp.'<td colspan="4"> '."\n".'<tr><td colspan="3"> '; // test ###
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] ))
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'] ;
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'] ;
10260 * Returns an array containing all the timezone data in the vcalendar object
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
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);
10276 while ( $vtzc = $vtz->getComponent( 'daylight' )) {
10277 $newtzdates = expandTimezoneDates($vtzc);
10278 $alltzdates = array_merge($alltzdates, $newtzdates);
10281 $timezonedata[$tzid] = $alltzdates;
10283 return $timezonedata;
10286 * Returns an array containing time zone data from vtimezone standard/daylight instances
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)
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(
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);
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
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']
10360 $stopcount = isset( $rrule['COUNT'] ) ?
$rrule['COUNT'] : 0 ;
10365 'WE' => 'Wednesday',
10366 'TH' => 'Thursday',
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 )) {
10376 $datearray['mon'] = $rrule['BYMONTH'] ;
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'],
10391 $datearray['mday'],
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);
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);
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);
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,
10418 $datearray['mday'],
10421 // add this change to the array of changes
10422 $tzdates[] = array(
10423 'timestamp' => $offsetchangetimestamp,
10424 'tzbefore' => $tzbefore,
10425 'tzafter' => $tzafter
10427 // update counters (timestamp and count)
10428 $offsetchangetimestamp = strtotime("+" . (( isset( $rrule['INTERVAL'] ) && ( $rrule['INTERVAL'] != 0 )) ?
$rrule['INTERVAL'] : 1 ) . " year",$offsetchangetimestamp);
10433 // generate dates according to RDATE lines
10434 while ($rdates = $vtzc->getProperty('rdate')) {
10435 if ( is_array($rdates) ) {
10437 foreach ( $rdates as $rdate ) {
10438 // convert the explicit change date to a timestamp
10439 $offsetchangetimestamp = mktime(
10442 $rdate['sec'] +
$diff,
10447 // add this change to the array of changes
10448 $tzdates[] = array(
10449 'timestamp' => $offsetchangetimestamp,
10450 'tzbefore' => $tzbefore,
10451 'tzafter' => $tzafter