Changeset 7660
- Timestamp:
- 12/17/12 14:22:24 (11 years ago)
- Location:
- trunk/prototype
- Files:
-
- 1 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/prototype/modules/calendar/interceptors/DAViCalAdapter.php
r6962 r7660 233 233 } 234 234 if(isset($params['calendar'])) 235 { 236 require_once ROOTPATH.'/plugins/icalcreator/iCalUtilityFunctions.class.php'; 235 { 237 236 require_once ROOTPATH.'/plugins/icalcreator/iCalcreator.class.php'; 238 237 239 $vcalendar = new icalCreator( );238 $vcalendar = new vcalendar( ); 240 239 $vcalendar->parse($data); 241 240 $vcalendar->sort(); -
trunk/prototype/modules/calendar/interceptors/DBMapping.php
r7655 r7660 835 835 foreach ($result as $i => $v) { 836 836 837 $currentTimezone = isset($original['criteria']['timezones'][$v['calendar']]) ? $original['criteria']['timezones'][$v['calendar']] : $v['timezone'];837 $currentTimezone = (isset($v['calendar']) && isset($original['criteria']['timezones'][$v['calendar']])) ? $original['criteria']['timezones'][$v['calendar']] : $v['timezone']; 838 838 839 839 $date->setTimestamp((int) ($v['startTime'] / 1000)); … … 863 863 $result[$i]['historic'] = Controller::find(array('concept' => 'calendarHistoric'), false, array('filter' => array('=', 'schedulable', $v['id']) , 'deepness' => 2)); 864 864 865 } 866 867 868 if(isset( $v['occurrences'] ) && count( $v['occurrences'] ) > 0){ 869 870 foreach( $result[$i]['occurrences'] as &$o){ 871 872 $date->setTimestamp((int) ($o / 1000)); 873 $o = ($date->getTimestamp() + $date->getOffset()).'000'; 874 875 } 865 876 } 866 877 … … 1107 1118 "THEN rep.id = occ.repeat_id AND occ.occurrence - al.alarm_offset >= '$target' AND occ.occurrence - al.alarm_offset <= '".( $target + 86400000)."' ". 1108 1119 "ELSE (co.range_start - al.alarm_offset) >= '$target' AND (co.range_start - al.alarm_offset) <= '".( $target + 86400000)."' END"); 1109 1120 1110 1121 if(is_array($al)) 1111 1122 foreach( $al as $v ) 1112 1123 $params[] = array('schedulable' => $v); 1113 1124 1114 1125 else 1115 1126 $params = false; 1116 1127 1117 1128 return false; 1118 1129 } -
trunk/prototype/plugins/icalcreator/iCalcreator.class.php
r7655 r7660 2 2 /*********************************************************************************/ 3 3 /** 4 * iCalcreator class v2.10.55 * copyright (c) 2007-201 1Kjell-Inge Gustafsson kigkonsult6 * www.kigkonsult.se/iCalcreator/index.php4 * iCalcreator v2.16.1 5 * copyright (c) 2007-2012 Kjell-Inge Gustafsson kigkonsult 6 * kigkonsult.se/iCalcreator/index.php 7 7 * ical@kigkonsult.se 8 8 * 9 9 * Description: 10 * This file is a PHP implementation of RFC 2445.10 * This file is a PHP implementation of rfc2445/rfc5545. 11 11 * 12 12 * This library is free software; you can redistribute it and/or … … 45 45 */ 46 46 /*********************************************************************************/ 47 /* only for phpversion 5.1 and later, */48 /* date management, default timezone setting */49 /* since 2.6.36 - 2010-12-31 */50 if( substr( phpversion(), 0, 3 ) >= '5.1' )51 // && ( 'UTC' == date_default_timezone_get()))52 date_default_timezone_set( 'Europe/Stockholm' );53 /*********************************************************************************/54 /* since 2.6.22 - 2010-09-25, do NOT remove!! */55 require_once ROOTPATH.'/plugins/icalcreator/iCalUtilityFunctions.class.php';56 /*********************************************************************************/57 47 /* version, do NOT remove!! */ 58 define( 'ICALCREATOR_VERSION', 'iCalcreator 2.1 0.5' );48 define( 'ICALCREATOR_VERSION', 'iCalcreator 2.16.1' ); 59 49 /*********************************************************************************/ 60 50 /*********************************************************************************/ … … 65 55 * @since 2.9.6 - 2011-05-14 66 56 */ 67 class icalCreator {57 class vcalendar { 68 58 // calendar property variables 69 59 var $calscale; … … 98 88 * @return void 99 89 */ 100 function icalCreator ( $config = array()) {90 function vcalendar ( $config = array()) { 101 91 $this->_makeVersion(); 102 92 $this->calscale = null; … … 132 122 * 133 123 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 134 * @since 2. 4.8 - 2008-10-21124 * @since 2.10.16 - 2011-10-28 135 125 * @return string 136 126 */ … … 139 129 switch( $this->format ) { 140 130 case 'xcal': 141 return ' calscale="'.$this->calscale.'"'.$this->nl;131 return $this->nl.' calscale="'.$this->calscale.'"'; 142 132 break; 143 133 default: … … 166 156 * 167 157 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 168 * @since 0.9.7 - 2006-11-20158 * @since 2.10.16 - 2011-10-28 169 159 * @return string 170 160 */ … … 173 163 switch( $this->format ) { 174 164 case 'xcal': 175 return ' method="'.$this->method.'"'.$this->nl;165 return $this->nl.' method="'.$this->method.'"'; 176 166 break; 177 167 default: … … 205 195 * 206 196 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 207 * @since 0.9.7 - 2006-11-20197 * @since 2.12.11 - 2012-05-13 208 198 * @return string 209 199 */ … … 213 203 switch( $this->format ) { 214 204 case 'xcal': 215 return ' prodid="'.$this->prodid.'"'.$this->nl;205 return $this->nl.' prodid="'.$this->prodid.'"'; 216 206 break; 217 207 default: 218 return 'PRODID:'.$this->prodid.$this->nl; 208 $toolbox = new calendarComponent(); 209 $toolbox->setConfig( $this->getConfig()); 210 return $toolbox->_createElement( 'PRODID', '', $this->prodid ); 219 211 break; 220 212 } … … 257 249 * 258 250 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 259 * @since 0.9.7 - 2006-11-20251 * @since 2.10.16 - 2011-10-28 260 252 * @return string 261 253 */ … … 265 257 switch( $this->format ) { 266 258 case 'xcal': 267 return ' version="'.$this->version.'"'.$this->nl;259 return $this->nl.' version="'.$this->version.'"'; 268 260 break; 269 261 default: … … 303 295 * 304 296 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 305 * @since 2. 9.3 - 2011-05-14297 * @since 2.10.16 - 2011-11-01 306 298 * @return string 307 299 */ 308 300 function createXprop() { 309 if( 'xcal' == $this->format )310 return false;311 301 if( empty( $this->xprop ) || !is_array( $this->xprop )) return FALSE; 312 302 $output = null; … … 327 317 $xpropPart['value'] = $toolbox->_strrep( $xpropPart['value'] ); 328 318 $output .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] ); 319 if( is_array( $toolbox->xcaldecl ) && ( 0 < count( $toolbox->xcaldecl ))) { 320 foreach( $toolbox->xcaldecl as $localxcaldecl ) 321 $this->xcaldecl[] = $localxcaldecl; 322 } 329 323 } 330 324 return $output; … … 334 328 * 335 329 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 336 * @since 2. 9.3 - 2011-05-14330 * @since 2.11.9 - 2012-01-16 337 331 * @param string $label 338 332 * @param string $value … … 341 335 */ 342 336 function setXprop( $label, $value, $params=FALSE ) { 343 if( empty( $label )) return FALSE; 337 if( empty( $label )) 338 return FALSE; 339 if( 'X-' != strtoupper( substr( $label, 0, 2 ))) 340 return FALSE; 344 341 if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; 345 342 $xprop = array( 'value' => $value ); … … 393 390 if( $propix != $xpropno ) 394 391 $reduced[$xpropkey] = $xpropvalue; 395 ++$xpropno;392 $xpropno++; 396 393 } 397 394 } … … 409 406 * 410 407 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 411 * @since 2. 8.8 - 2011-04-16408 * @since 2.13.4 - 2012-08-08 412 409 * @param string $propName, optional 413 410 * @param int $propix, optional, if specific property is wanted in case of multiply occurences … … 419 416 if( 'X-PROP' == $propName ) { 420 417 if( !$propix ) 421 $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;418 $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; 422 419 $this->propix[$propName] = --$propix; 423 420 } 421 else 422 $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' ); 424 423 switch( $propName ) { 425 424 case 'ATTENDEE': 426 425 case 'CATEGORIES': 426 case 'CONTACT': 427 427 case 'DTSTART': 428 case 'GEOLOCATION': 428 429 case 'LOCATION': 429 430 case 'ORGANIZER': … … 433 434 case 'SUMMARY': 434 435 case 'RECURRENCE-ID-UID': 436 case 'RELATED-TO': 435 437 case 'R-UID': 436 438 case 'UID': 437 $output = array(); 439 case 'URL': 440 $output = array(); 438 441 foreach ( $this->components as $cix => $component) { 439 442 if( !in_array( $component->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' ))) 440 443 continue; 441 if( ( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName)) {444 if( in_array( strtoupper( $propName ), $mProps )) { 442 445 $component->_getProperties( $propName, $output ); 443 446 continue; … … 447 450 $content = $component->getProperty( 'UID' ); 448 451 } 452 elseif( 'GEOLOCATION' == $propName ) { 453 $content = $component->getProperty( 'LOCATION' ); 454 $content = ( !empty( $content )) ? $content.' ' : ''; 455 if(( FALSE === ( $geo = $component->getProperty( 'GEO' ))) || empty( $geo )) 456 continue; 457 if( 0.0 < $geo['latitude'] ) 458 $sign = '+'; 459 else 460 $sign = ( 0.0 > $geo['latitude'] ) ? '-' : ''; 461 $content .= ' '.$sign.sprintf( "%09.6f", abs( $geo['latitude'] )); 462 $content = rtrim( rtrim( $content, '0' ), '.' ); 463 if( 0.0 < $geo['longitude'] ) 464 $sign = '+'; 465 else 466 $sign = ( 0.0 > $geo['longitude'] ) ? '-' : ''; 467 $content .= $sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/'; 468 } 449 469 elseif( FALSE === ( $content = $component->getProperty( $propName ))) 450 470 continue; 451 if( FALSE === $content)471 if(( FALSE === $content ) || empty( $content )) 452 472 continue; 453 473 elseif( is_array( $content )) { … … 477 497 return $output; 478 498 break; 479 480 499 case 'CALSCALE': 481 500 return ( !empty( $this->calscale )) ? $this->calscale : FALSE; … … 506 525 : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); 507 526 else 508 ++$xpropno;527 $xpropno++; 509 528 } 510 529 unset( $this->propix[$propName] ); … … 549 568 * 550 569 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 551 * @since 2. 9.6 - 2011-05-14570 * @since 2.11.7 - 2012-01-12 552 571 * @param mixed $config 553 572 * @return value … … 593 612 break; 594 613 case 'DIRECTORY': 595 if( empty( $this->directory ) )614 if( empty( $this->directory ) && ( '0' != $this->directory )) 596 615 $this->directory = '.'; 597 616 return $this->directory; … … 606 625 break; 607 626 case 'FILENAME': 608 if( empty( $this->filename ) ) {627 if( empty( $this->filename ) && ( '0' != $this->filename )) { 609 628 if( 'xcal' == $this->format ) 610 629 $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. . … … 653 672 * 654 673 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 655 * @since 2. 9.6 - 2011-05-14674 * @since 2.12.12 - 2012-05-13 656 675 * @param mixed $config 657 676 * @param string $value … … 660 679 function setConfig( $config, $value = FALSE) { 661 680 if( is_array( $config )) { 681 $ak = array_keys( $config ); 682 foreach( $ak as $k ) { 683 if( 'DIRECTORY' == strtoupper( $k )) { 684 if( FALSE === $this->setConfig( 'DIRECTORY', $config[$k] )) 685 return FALSE; 686 unset( $config[$k] ); 687 } 688 elseif( 'NEWLINECHAR' == strtoupper( $k )) { 689 if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] )) 690 return FALSE; 691 unset( $config[$k] ); 692 } 693 } 662 694 foreach( $config as $cKey => $cValue ) { 663 695 if( FALSE === $this->setConfig( $cKey, $cValue )) … … 733 765 $res = TRUE; 734 766 break; 735 case 'LANGUAGE': 736 // set language for calendar component as defined in [RFC 1766] 767 case 'LANGUAGE': // set language for calendar component as defined in [RFC 1766] 737 768 $value = trim( $value ); 738 769 $this->language = $value; 770 $this->_makeProdid(); 739 771 $subcfg = array( 'LANGUAGE' => $value ); 740 772 $res = TRUE; … … 743 775 case 'NEWLINECHAR': 744 776 $this->nl = $value; 777 if( 'xcal' == $value ) { 778 $this->attributeDelimiter = $this->nl; 779 $this->valueInit = null; 780 } 781 else { 782 $this->attributeDelimiter = ';'; 783 $this->valueInit = ':'; 784 } 745 785 $subcfg = array( 'NL' => $value ); 746 786 $res = TRUE; … … 754 794 $value = trim( $value ); 755 795 $this->unique_id = $value; 796 $this->_makeProdid(); 756 797 $subcfg = array( 'UNIQUE_ID' => $value ); 757 798 $res = TRUE; … … 829 870 return TRUE; 830 871 } 831 ++$cix1dC;872 $cix1dC++; 832 873 } 833 874 elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { … … 842 883 * 843 884 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 844 * @since 2. 9.1 - 2011-04-16885 * @since 2.13.5 - 2012-08-08 845 886 * @param mixed $arg1 optional, ordno/component type/ component uid 846 887 * @param mixed $arg2 optional, ordno if arg1 = component type … … 862 903 $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1; 863 904 $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' ); 864 $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID' ); 905 $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' ); 906 $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' ); 865 907 } 866 908 elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name … … 891 933 if( $index == $cix1gC ) 892 934 return $component->copy(); 893 ++$cix1gC;935 $cix1gC++; 894 936 } 895 937 elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) 896 $hit = FALSE;938 $hit = array(); 897 939 foreach( $arg1 as $pName => $pValue ) { 898 940 $pName = strtoupper( $pName ); 899 941 if( !in_array( $pName, $dateProps ) && !in_array( $pName, $otherProps )) 900 942 continue; 901 if( ( 'ATTENDEE' == $pName ) || ( 'CATEGORIES' == $pName ) || ( 'RESOURCES' == $pName )) { // multiple ocurrence may occur943 if( in_array( $pName, $mProps )) { // multiple occurrence 902 944 $propValues = array(); 903 945 $component->_getProperties( $pName, $propValues ); 904 946 $propValues = array_keys( $propValues ); 905 $hit = ( in_array( $pValue, $propValues )) ? TRUE : FALSE;947 $hit[] = ( in_array( $pValue, $propValues )) ? TRUE : FALSE; 906 948 continue; 907 } // end if( ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName )) { // multiple ocurrence may occur908 if( FALSE === ( $value = $component->getProperty( $pName ))) { // single oc urrency909 $hit = FALSE; // missing property949 } // end if(.. .// multiple occurrence 950 if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence 951 $hit[] = FALSE; // missing property 910 952 continue; 911 953 } 912 954 if( 'SUMMARY' == $pName ) { // exists within (any case) 913 $hit = ( FALSE !== stripos( $d, $pValue )) ? TRUE : FALSE;955 $hit[] = ( FALSE !== stripos( $value, $pValue )) ? TRUE : FALSE; 914 956 continue; 915 957 } … … 925 967 $pValue = substr( $pValue, 0, 8 ); 926 968 } 927 $hit = ( $pValue == $valuedate ) ? TRUE : FALSE;969 $hit[] = ( $pValue == $valuedate ) ? TRUE : FALSE; 928 970 continue; 929 971 } … … 934 976 foreach( $part as $subPart ) { 935 977 if( $pValue == $subPart ) { 936 $hit = TRUE;937 continue 2;978 $hit[] = TRUE; 979 continue 3; 938 980 } 939 981 } 940 } 941 $hit = FALSE; // no hit in property982 } // end foreach( $value as $part ) 983 $hit[] = FALSE; // no hit in property 942 984 } // end foreach( $arg1 as $pName => $pValue ) 943 if( $hit) {985 if( in_array( TRUE, $hit )) { 944 986 if( $index == $cix1gC ) 945 987 return $component->copy(); 946 ++$cix1gC;988 $cix1gC++; 947 989 } 948 990 } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) … … 950 992 if( $index == $cix1gC ) 951 993 return $component->copy(); 952 ++$cix1gC;994 $cix1gC++; 953 995 } 954 996 } // end foreach ( $this->components.. . … … 1003 1045 * 1004 1046 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 1005 * @since 2. 9.7 - 2011-06-041006 * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions1007 * @param int $startM optional,start Month, default current Month1008 * @param int $startD optional, start Day,default current Day1009 * @param int $endY optional, end Year,default $startY1010 * @param int $endY optional, endMonth, default $startM1011 * @param int $endY optional, end Day,default $startD1012 * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)1013 * @param bool $flat optional,FALSE (default) => output : array[Year][Month][Day][]1014 * TRUE=> output : array[] (ignores split)1015 * @param bool $any optional, TRUE (default) - select component that take placewithin period1016 * FALSE - only componentsthat starts within period1017 * @param bool $split optional, TRUE (default) - one component copy every day it take placeduring the1018 * period (implies flat=FALSE)1019 * FALSE- one occurance of component only in output array1047 * @since 2.11.22 - 2012-02-13 1048 * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] ) 1049 * @param int $startM optional, start Month, default current Month 1050 * @param int $startD optional, start Day, default current Day 1051 * @param int $endY optional, end Year, default $startY 1052 * @param int $endY optional, end Month, default $startM 1053 * @param int $endY optional, end Day, default $startD 1054 * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s) 1055 * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][] 1056 * TRUE => output : array[] (ignores split) 1057 * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period 1058 * FALSE - only component(-s) that starts within period 1059 * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the 1060 * period (implies flat=FALSE) 1061 * FALSE - one occurance of component only in output array 1020 1062 * @return array or FALSE 1021 1063 */ … … 1056 1098 if( 0 >= count( $cType )) 1057 1099 $cType = $validTypes; 1100 if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination 1101 $split = FALSE; 1058 1102 if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination 1059 1103 $split = FALSE; … … 1064 1108 unset( $start ); 1065 1109 /* deselect unvalid type components */ 1066 if( !in_array( $component->objName, $cType )) continue; 1110 if( !in_array( $component->objName, $cType )) 1111 continue; 1067 1112 $start = $component->getProperty( 'dtstart' ); 1068 1113 /* select due when dtstart is missing */ 1069 1114 if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' )))) 1070 1115 continue; 1071 $dtendExist = $dueExist = $durationExist = $endAllDayEvent = FALSE; 1116 if( empty( $start )) 1117 continue; 1118 $dtendExist = $dueExist = $durationExist = $endAllDayEvent = $recurrid = FALSE; 1072 1119 unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend, $endDateFormat ); // clean up 1073 1120 $startWdate = iCalUtilityFunctions::_date2timestamp( $start ); … … 1113 1160 $endWdate = iCalUtilityFunctions::_date2timestamp( $end ); 1114 1161 } 1115 $rdurWsecs = $endWdate - $startWdate; // compute componentduration in seconds1162 $rdurWsecs = $endWdate - $startWdate; // compute event (component) duration in seconds 1116 1163 /* make a list of optional exclude dates for component occurence from exrule and exdate */ 1117 1164 $exdatelist = array(); … … 1123 1170 foreach( $exdate as $theExdate ) { 1124 1171 $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate ); 1125 $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate ) 1172 $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate )); // on a day-basis !!! 1126 1173 if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate )) 1127 1174 $exdatelist[$exWdate] = TRUE; 1128 } 1129 } 1130 /* if 'any' components, check repeating components, removing all excluding dates */ 1175 } // end - foreach( $exdate as $theExdate ) 1176 } // end - check exdate 1177 $compUID = $component->getProperty( 'UID' ); 1178 /* check recurrence-id (with sequence), remove hit with reccurr-id date */ 1179 if(( FALSE !== ( $recurrid = $component->getProperty( 'recurrence-id' ))) && 1180 ( FALSE !== ( $sequence = $component->getProperty( 'sequence' ))) ) { 1181 $recurrid = iCalUtilityFunctions::_date2timestamp( $recurrid ); 1182 $recurrid = mktime( 0, 0, 0, date( 'm', $recurrid ), date( 'd', $recurrid ), date( 'Y', $recurrid )); // on a day-basis !!! 1183 $endD = $recurrid + $rdurWsecs; 1184 do { 1185 if( date( 'Ymd', $startWdate ) != date( 'Ymd', $recurrid )) 1186 $exdatelist[$recurrid] = TRUE; // exclude all other days than startdate 1187 $wd = getdate( $recurrid ); 1188 if( isset( $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] )) 1189 unset( $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] ); // remove from output, dtstart etc added below 1190 if( $split && ( $recurrid <= $endD )) 1191 $recurrid = mktime( 0, 0, 0, date( 'm', $recurrid ), date( 'd', $recurrid ) + 1, date( 'Y', $recurrid )); // step one day 1192 else 1193 break; 1194 } while( TRUE ); 1195 } // end recurrence-id/sequence test 1196 /* select only components with.. . */ 1197 if(( !$any && ( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) || // (dt)start within the period 1198 ( $any && ( $startWdate < $endDate ) && ( $endWdate >= $startDate ))) { // occurs within the period 1199 /* add the selected component (WITHIN valid dates) to output array */ 1200 if( $flat ) { // any=true/false, ignores split 1201 if( !$recurrid ) 1202 $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id) 1203 } 1204 elseif( $split ) { // split the original component 1205 if( $endWdate > $endDate ) 1206 $endWdate = $endDate; // use period end date 1207 $rstart = $startWdate; 1208 if( $rstart < $startDate ) 1209 $rstart = $startDate; // use period start date 1210 $startYMD = date( 'Ymd', $rstart ); 1211 $endYMD = date( 'Ymd', $endWdate ); 1212 $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! 1213 while( date( 'Ymd', $rstart ) <= $endYMD ) { // iterate 1214 $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! 1215 if( isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist 1216 $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day 1217 continue; 1218 } 1219 if( date( 'Ymd', $rstart ) > $startYMD ) // date after dtstart 1220 $datestring = date( $startDateFormat, mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ))); 1221 else 1222 $datestring = date( $startDateFormat, $rstart ); 1223 if( isset( $start['tz'] )) 1224 $datestring .= ' '.$start['tz']; 1225 // echo "X-CURRENT-DTSTART 3 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component->setProperty( 'X-CNT', $tcnt ); // test ### 1226 $component->setProperty( 'X-CURRENT-DTSTART', $datestring ); 1227 if( $dtendExist || $dueExist || $durationExist ) { 1228 if( date( 'Ymd', $rstart ) < $endYMD ) // not the last day 1229 $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )); 1230 else 1231 $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! 1232 if( $endAllDayEvent && $dtendExist ) 1233 $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day 1234 $datestring = date( $endDateFormat, $tend ); 1235 if( isset( $end['tz'] )) 1236 $datestring .= ' '.$end['tz']; 1237 $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE'; 1238 $component->setProperty( $propName, $datestring ); 1239 } // end if( $dtendExist || $dueExist || $durationExist ) 1240 $wd = getdate( $rstart ); 1241 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output 1242 $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day 1243 } // end while( $rstart <= $endWdate ) 1244 } // end if( $split ) - else use component date 1245 elseif( $recurrid && !$flat && !$any && !$split ) 1246 $continue = TRUE; 1247 else { // !$flat && !$split, i.e. no flat array and DTSTART within period 1248 $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!! 1249 if( !$any || !isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist 1250 $wd = getdate( $startWdate ); 1251 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output 1252 } 1253 } 1254 } // end if(( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) 1255 1256 /* if 'any' components, check components with reccurrence rules, removing all excluding dates */ 1131 1257 if( TRUE === $any ) { 1132 1258 /* make a list of optional repeating dates for component occurence, rrule, rdate */ … … 1160 1286 } 1161 1287 } 1162 } 1288 } // end - check rdate 1163 1289 if( 0 < count( $recurlist )) { 1164 1290 ksort( $recurlist ); 1165 1291 $xRecurrence = 1; 1292 $component2 = $component->copy(); 1293 $compUID = $component2->getProperty( 'UID' ); 1166 1294 foreach( $recurlist as $recurkey => $durvalue ) { 1167 1295 // echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCalUtilityFunctions::offsetSec2His( $durvalue )."<br />\n"; // test ###; … … 1173 1301 if( $startWdate >= $recurkey ) // exclude component start date 1174 1302 continue; 1175 $component2 = $component->copy();1176 1303 $rstart = $recurkey; 1177 1304 $rend = $recurkey + $durvalue; 1178 1305 /* add repeating components within valid dates to output array, only start date set */ 1179 1306 if( $flat ) { 1180 $datestring = date( $startDateFormat, $recurkey ); 1181 if( isset( $start['tz'] )) 1182 $datestring .= ' '.$start['tz']; 1183 // echo "X-CURRENT-DTSTART 0 =$datestring tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ### 1184 $component2->setProperty( 'X-CURRENT-DTSTART', $datestring ); 1185 if( $dtendExist || $dueExist || $durationExist ) { 1186 $datestring = date( $endDateFormat, $recurkey + $durvalue ); // fixa korrekt sluttid 1187 if( isset( $end['tz'] )) 1188 $datestring .= ' '.$end['tz']; 1189 $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE'; 1190 $component2->setProperty( $propName, $datestring ); 1191 } // end if( $dtendExist || $dueExist || $durationExist ) 1192 $component2->setProperty( 'X-RECURRENCE', ++$xRecurrence ); 1193 $result[$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output 1307 if( !isset( $result[$compUID] )) // only one comp 1308 $result[$compUID] = $component2->copy(); // copy to output 1194 1309 } 1195 1310 /* add repeating components within valid dates to output array, one each day */ … … 1219 1334 else 1220 1335 $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! 1336 if( $endAllDayEvent && $dtendExist ) 1337 $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day 1221 1338 $datestring = date( $endDateFormat, $tend ); 1222 1339 if( isset( $end['tz'] )) … … 1227 1344 $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); 1228 1345 $wd = getdate( $rstart ); 1229 $result[$wd['year']][$wd['mon']][$wd['mday']][$comp onent2->getProperty( 'UID' )] = $component2->copy(); // copy to output1346 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output 1230 1347 } // end if( $checkDate > $startYMD ) { // date after dtstart 1231 1348 $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day … … 1233 1350 $xRecurrence += 1; 1234 1351 } // end elseif( $split ) 1235 elseif( $rstart >= $startDate ) { // date within period //* flat=FALSE && split=FALSE *//1352 elseif( $rstart >= $startDate ) { // date within period //* flat=FALSE && split=FALSE => one comp every recur startdate *// 1236 1353 $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! 1237 1354 if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist … … 1243 1360 $component2->setProperty( 'X-CURRENT-DTSTART', $datestring ); 1244 1361 if( $dtendExist || $dueExist || $durationExist ) { 1245 $ rstart +=$rdurWsecs;1246 if( date( 'Ymd', $ rstart) < date( 'Ymd', $endWdate ))1247 $tend = mktime( 23, 59, 59, date( 'm', $ rstart ), date( 'd', $rstart ), date( 'Y', $rstart));1362 $tend = $rstart + $rdurWsecs; 1363 if( date( 'Ymd', $tend ) < date( 'Ymd', $endWdate )) 1364 $tend = mktime( 23, 59, 59, date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend )); 1248 1365 else 1249 $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! 1366 $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend ) ); // on a day-basis !!! 1367 if( $endAllDayEvent && $dtendExist ) 1368 $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day 1250 1369 $datestring = date( $endDateFormat, $tend ); 1251 1370 if( isset( $end['tz'] )) … … 1256 1375 $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); 1257 1376 $wd = getdate( $rstart ); 1258 $result[$wd['year']][$wd['mon']][$wd['mday']][$comp onent2->getProperty( 'UID' )] = $component2->copy(); // copy to output1377 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output 1259 1378 } // end if( !isset( $exdatelist[$checkDate] )) 1260 1379 } // end elseif( $rstart >= $startDate ) … … 1265 1384 continue; 1266 1385 } // end if( TRUE === $any ) 1267 /* deselect components with startdate not within period */1268 elseif(( $startWdate < $startDate ) || ( $startWdate > $endDate ))1269 continue;1270 /* add the selected component (WITHIN valid dates) to output array */1271 if( $flat )1272 $result[$component->getProperty( 'UID' )] = $component->copy(); // copy to output;1273 elseif( $split ) { // split the original component1274 if( $endWdate > $endDate )1275 $endWdate = $endDate; // use period end date1276 $rstart = $startWdate;1277 if( $rstart < $startDate )1278 $rstart = $startDate; // use period start date1279 $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!1280 if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist1281 foreach( array( 'X-CURRENT-DTSTART','X-CURRENT-DTEND','X-CURRENT-DUE','X-RECURRENCE' ) as $propName )1282 $component->deleteProperty( $propName ); // remove any x-props, if set1283 while( $rstart <= $endWdate ) { // iterate1284 if( $rstart > $startWdate ) { // if NOT startdate, set X-properties1285 $datestring = date( $startDateFormat, mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )));1286 if( isset( $start['tz'] ))1287 $datestring .= ' '.$start['tz'];1288 // echo "X-CURRENT-DTSTART 3 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component->setProperty( 'X-CNT', $tcnt ); // test ###1289 $component->setProperty( 'X-CURRENT-DTSTART', $datestring );1290 if( $dtendExist || $dueExist || $durationExist ) {1291 if( date( 'Ymd', $rstart ) < date( 'Ymd', $endWdate ))1292 $tend = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ));1293 else1294 $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!1295 $datestring = date( $endDateFormat, $tend );1296 if( isset( $end['tz'] ))1297 $datestring .= ' '.$end['tz'];1298 $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';1299 $component->setProperty( $propName, $datestring );1300 } // end if( $dtendExist || $dueExist || $durationExist )1301 } // end if( $rstart > $startWdate )1302 $wd = getdate( $rstart );1303 $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty( 'UID' )] = $component->copy(); // copy to output1304 $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day1305 } // end while( $rstart <= $endWdate )1306 } // end if( !isset( $exdatelist[$checkDate] ))1307 } // end if( $split ) - else use component date1308 elseif( $startWdate >= $startDate ) { // within period1309 $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!!1310 if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist1311 foreach( array( 'X-CURRENT-DTSTART','X-CURRENT-DTEND','X-CURRENT-DUE','X-RECURRENCE' ) as $propName )1312 $component->deleteProperty( $propName ); // remove any x-props, if set1313 $wd = getdate( $startWdate );1314 $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty( 'UID' )] = $component->copy(); // copy to output1315 }1316 }1317 1386 } // end foreach ( $this->components as $cix => $component ) 1318 1387 if( 0 >= count( $result )) return FALSE; … … 1320 1389 foreach( $result as $y => $yeararr ) { 1321 1390 foreach( $yeararr as $m => $montharr ) { 1322 foreach( $montharr as $d => $dayarr ) 1323 $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index 1324 ksort( $result[$y][$m] ); 1325 } 1326 ksort( $result[$y] ); 1327 } 1328 ksort( $result ); 1391 foreach( $montharr as $d => $dayarr ) { 1392 if( empty( $result[$y][$m][$d] )) 1393 unset( $result[$y][$m][$d] ); 1394 else 1395 $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index, hoping they are in hour order.. . 1396 } 1397 if( empty( $result[$y][$m] )) 1398 unset( $result[$y][$m] ); 1399 else 1400 ksort( $result[$y][$m] ); 1401 } 1402 if( empty( $result[$y] )) 1403 unset( $result[$y] ); 1404 else 1405 ksort( $result[$y] ); 1406 } 1407 if( empty( $result )) 1408 unset( $result ); 1409 else 1410 ksort( $result ); 1329 1411 } // end elseif( !$flat ) 1412 if( 0 >= count( $result )) 1413 return FALSE; 1330 1414 return $result; 1331 1415 } 1332 1416 /** 1333 * select components from calendar on based on Categories, Location, Resources and/or Summary1334 * 1335 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 1336 * @since 2. 8.8 - 2011-05-031417 * select components from calendar on based on specific property value(-s) 1418 * 1419 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 1420 * @since 2.13.4 - 2012-08-07 1337 1421 * @param array $selectOptions, (string) key => (mixed) value, (key=propertyName) 1338 1422 * @return array … … 1340 1424 function selectComponents2( $selectOptions ) { 1341 1425 $output = array(); 1342 $allowedProperties = array( 'ATTENDEE', 'CATEGORIES', 'LOCATION', 'ORGANIZER', 'RESOURCES', 'PRIORITY', 'STATUS', 'SUMMARY', 'UID' ); 1426 $allowedComps = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' ); 1427 $allowedProperties = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' ); 1343 1428 foreach( $this->components as $cix => $component3 ) { 1344 if( !in_array( $component3->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' )))1429 if( !in_array( $component3->objName, $allowedComps )) 1345 1430 continue; 1346 1431 $uid = $component3->getProperty( 'UID' ); … … 1355 1440 continue; 1356 1441 } 1357 elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( ' RESOURCES' == $propName )) {1442 elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'CONTACT' == $propName ) || ( 'RELATED-TO' == $propName ) || ( 'RESOURCES' == $propName )) { // multiple occurrence? 1358 1443 $propValues = array(); 1359 1444 $component3->_getProperties( $propName, $propValues ); … … 1366 1451 } 1367 1452 continue; 1368 } // end elseif( ( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName ))1369 elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single oc urrence1453 } // end elseif( // multiple occurrence? 1454 elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single occurrence 1370 1455 continue; 1371 1456 if( is_array( $d )) { … … 1436 1521 return TRUE; 1437 1522 } 1438 ++$cix1sC;1523 $cix1sC++; 1439 1524 } 1440 1525 elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace … … 1456 1541 * 1457 1542 * ascending sort on properties (if exist) x-current-dtstart, dtstart, 1458 * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid 1459 * if no arguments, otherwise sorting on argument CATEGORIES, LOCATION, SUMMARY or RESOURCES1460 * 1461 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 1462 * @since 2. 8.4 - 2011-06-021543 * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid if called without arguments, 1544 * otherwise sorting on specific (argument) property values 1545 * 1546 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 1547 * @since 2.13.4 - 2012-08-07 1463 1548 * @param string $sortArg, optional 1464 1549 * @return void … … 1469 1554 if( $sortArg ) { 1470 1555 $sortArg = strtoupper( $sortArg ); 1471 if( !in_array( $sortArg, array( 'ATTENDEE', 'CATEGORIES', ' DTSTAMP', 'LOCATION', 'ORGANIZER', 'RESOURCES', 'PRIORITY', 'STATUS', 'SUMMARY' )))1556 if( !in_array( $sortArg, array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'DTSTAMP', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'URL' ))) 1472 1557 $sortArg = FALSE; 1473 1558 } … … 1481 1566 } 1482 1567 elseif( $sortArg ) { 1483 if(( 'ATTENDEE' == $sortArg ) || ( 'CATEGORIES' == $sortArg ) || ( ' RESOURCES'== $sortArg )) {1568 if(( 'ATTENDEE' == $sortArg ) || ( 'CATEGORIES' == $sortArg ) || ( 'CONTACT' == $sortArg ) || ( 'RELATED-TO' == $sortArg ) || ( 'RESOURCES' == $sortArg )) { 1484 1569 $propValues = array(); 1485 1570 $c->_getProperties( $sortArg, $propValues ); 1486 $c->srtk[0] = reset( array_keys( $propValues )); 1571 if( !empty( $propValues )) { 1572 $sk = array_keys( $propValues ); 1573 $c->srtk[0] = $sk[0]; 1574 if( 'RELATED-TO' == $sortArg ) 1575 $c->srtk[0] .= $c->getProperty( 'uid' ); 1576 } 1577 elseif( 'RELATED-TO' == $sortArg ) 1578 $c->srtk[0] = $c->getProperty( 'uid' ); 1487 1579 } 1488 1580 elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) … … 1490 1582 continue; 1491 1583 } 1492 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) 1493 $c->srtk[0] = iCalUtilityFunctions::_date_time_string( $d[1] ); 1584 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) { 1585 $c->srtk[0] = iCalUtilityFunctions::_strdate2date( $d[1] ); 1586 unset( $c->srtk[0]['unparsedtext'] ); 1587 } 1494 1588 elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' ))) 1495 1589 $c->srtk[1] = 0; // sortkey 0 : dtstart 1496 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) 1497 $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] ); // sortkey 1 : dtend/due(/dtstart+duration) 1590 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) { 1591 $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); // sortkey 1 : dtend/due(/dtstart+duration) 1592 unset( $c->srtk[1]['unparsedtext'] ); 1593 } 1498 1594 elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) { 1499 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) 1500 $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] ); 1595 if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) { 1596 $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); 1597 unset( $c->srtk[1]['unparsedtext'] ); 1598 } 1501 1599 elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' ))) 1502 1600 if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE ))) … … 1523 1621 elseif( 'vtimezone' == $b->objName ) return 1; 1524 1622 $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' ); 1525 for( $k = 0; $k < 4 ; ++$k) {1623 for( $k = 0; $k < 4 ; $k++ ) { 1526 1624 if( empty( $a->srtk[$k] )) return -1; 1527 1625 elseif( empty( $b->srtk[$k] )) return 1; … … 1529 1627 if( is_array( $b->srtk[$k] )) { 1530 1628 foreach( $sortkeys as $key ) { 1629 if ( !isset( $a->srtk[$k][$key] )) return -1; 1630 elseif( !isset( $b->srtk[$k][$key] )) return 1; 1531 1631 if ( empty( $a->srtk[$k][$key] )) return -1; 1532 1632 elseif( empty( $b->srtk[$k][$key] )) return 1; … … 1551 1651 * 1552 1652 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 1553 * @since 2. 8.2 - 2011-05-211653 * @since 2.15.10 - 2012-10-28 1554 1654 * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings 1555 1655 * @return bool FALSE if error occurs during parsing … … 1570 1670 else 1571 1671 $rows = & $unparsedtext; 1572 /* identify BEGIN:VCALENDAR, MUST be first row */1573 if( 'BEGIN:VCALENDAR' != strtoupper( substr( $rows, 0, 15 )))1574 return FALSE; /* err 8 */1575 1672 /* fix line folding */ 1576 $eolchars = array( "\r\n", "\n\r", "\n", "\r" ); // check all line endings 1577 $EOLmark = FALSE; 1578 foreach( $eolchars as $eolchar ) { 1579 if( !$EOLmark && ( FALSE !== strpos( $rows, $eolchar ))) { 1580 $rows = str_replace( $eolchar." ", '', $rows ); 1581 $rows = str_replace( $eolchar."\t", '', $rows ); 1582 if( $eolchar != $nl ) 1583 $rows = str_replace( $eolchar, $nl, $rows ); 1584 $EOLmark = TRUE; 1585 } 1586 } 1587 $tmp = explode( $nl, $rows ); 1588 $rows = array(); 1589 foreach( $tmp as $tmpr ) 1590 if( !empty( $tmpr )) 1591 $rows[] = $tmpr; 1592 /* skip trailing empty lines */ 1593 $lix = count( $rows ) - 1; 1594 while( empty( $rows[$lix] ) && ( 0 < $lix )) 1595 $lix -= 1; 1596 /* identify ending END:VCALENDAR row, MUST be last row */ 1597 if( 'END:VCALENDAR' != strtoupper( substr( $rows[$lix], 0, 13 ))) 1598 return FALSE; /* err 9 */ 1599 if( 3 > count( $rows )) 1600 return FALSE; /* err 10 */ 1673 $rows = explode( $nl, iCalUtilityFunctions::convEolChar( $rows, $nl )); 1674 /* skip leading (empty/invalid) lines */ 1675 foreach( $rows as $lix => $line ) { 1676 if( FALSE !== stripos( $line, 'BEGIN:VCALENDAR' )) 1677 break; 1678 unset( $rows[$lix] ); 1679 } 1680 $rcnt = count( $rows ); 1681 if( 3 > $rcnt ) /* err 10 */ 1682 return FALSE; 1683 /* skip trailing empty lines and ensure an end row */ 1684 $lix = array_keys( $rows ); 1685 $lix = end( $lix ); 1686 while( 3 < $lix ) { 1687 $tst = trim( $rows[$lix] ); 1688 if(( '\n' == $tst ) || empty( $tst )) { 1689 unset( $rows[$lix] ); 1690 $lix--; 1691 continue; 1692 } 1693 if( FALSE === stripos( $rows[$lix], 'END:VCALENDAR' )) 1694 $rows[] = 'END:VCALENDAR'; 1695 break; 1696 } 1601 1697 $comp = & $this; 1602 $calsync = 0;1698 $calsync = $compsync = 0; 1603 1699 /* identify components and update unparsed data within component */ 1604 1700 $config = $this->getConfig(); 1605 foreach( $rows as $line ) { 1606 if( '' == trim( $line )) 1701 $endtxt = array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ); 1702 foreach( $rows as $lix => $line ) { 1703 if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) { 1704 $calsync++; 1607 1705 continue; 1608 if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) {1609 ++$calsync;1610 continue;1611 1706 } 1612 1707 elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) { 1708 if( 0 < $compsync ) 1709 $this->components[] = $comp->copy(); 1710 $compsync--; 1613 1711 $calsync--; 1614 1712 break; … … 1616 1714 elseif( 1 != $calsync ) 1617 1715 return FALSE; /* err 20 */ 1618 elseif( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ))) {1716 elseif( in_array( strtoupper( substr( $line, 0, 6 )), $endtxt )) { 1619 1717 $this->components[] = $comp->copy(); 1718 $compsync--; 1620 1719 continue; 1621 1720 } 1622 1623 if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) 1721 if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) { 1624 1722 $comp = new vevent( $config ); 1625 elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) 1723 $compsync++; 1724 } 1725 elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) { 1626 1726 $comp = new vfreebusy( $config ); 1627 elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) 1727 $compsync++; 1728 } 1729 elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) { 1628 1730 $comp = new vjournal( $config ); 1629 elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) 1731 $compsync++; 1732 } 1733 elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) { 1630 1734 $comp = new vtodo( $config ); 1631 elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) 1735 $compsync++; 1736 } 1737 elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) { 1632 1738 $comp = new vtimezone( $config ); 1633 else /* update component with unparsed data */ 1739 $compsync++; 1740 } 1741 else { /* update component with unparsed data */ 1634 1742 $comp->unparsed[] = $line; 1635 } // end - foreach( rows.. . 1636 unset( $config ); 1743 } 1744 } // end foreach( $rows as $line ) 1745 unset( $config, $endtxt ); 1637 1746 /* parse data for calendar (this) object */ 1638 1747 if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) { 1639 1748 /* concatenate property values spread over several lines */ 1640 $lastix = -1;1641 1749 $propnames = array( 'calscale','method','prodid','version','x-' ); 1642 1750 $proprows = array(); 1643 foreach( $this->unparsed as $line ) { 1644 if( '' == trim( $line )) 1645 continue; 1646 $newProp = FALSE; 1647 foreach ( $propnames as $propname ) { 1648 if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) { 1649 $newProp = TRUE; 1650 break; 1651 } 1652 } 1653 if( $newProp ) { 1654 $newProp = FALSE; 1655 ++$lastix; 1656 $proprows[$lastix] = $line; 1657 } 1658 else 1659 $proprows[$lastix] .= '!"#€%&/()=?'.$line; 1660 } 1751 for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines 1752 $line = rtrim( $this->unparsed[$i], $nl ); 1753 while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} )) 1754 $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl ); 1755 $proprows[] = $line; 1756 } 1757 $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' ); 1758 $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ); 1759 $paramProto4 = array( 'crid:', 'news:', 'pres:' ); 1661 1760 foreach( $proprows as $line ) { 1662 $line = str_replace( '!"#€%&/()=? ', '', $line );1663 $line = str_replace( '!"#€%&/()=?', '', $line );1664 1761 if( '\n' == substr( $line, -2 )) 1665 $line = substr( $line, 0, strlen( $line ) -2 );1762 $line = substr( $line, 0, -2 ); 1666 1763 /* get property name */ 1667 $cix = $propname = null; 1668 for( $cix=0, $clen = strlen( $line ); $cix < $clen; ++$cix ) { 1669 if( in_array( $line[$cix], array( ':', ';' ))) 1764 $propname = ''; 1765 $cix = 0; 1766 while( FALSE !== ( $char = substr( $line, $cix, 1 ))) { 1767 if( in_array( $char, array( ':', ';' ))) 1670 1768 break; 1671 1769 else 1672 $propname .= $line[$cix]; 1673 } 1770 $propname .= $char; 1771 $cix++; 1772 } 1773 /* skip non standard property names */ 1774 if(( 'x-' != strtolower( substr( $propname, 0, 2 ))) && !in_array( strtolower( $propname ), $propnames )) 1775 continue; 1674 1776 /* ignore version/prodid properties */ 1675 if( in_array( strto upper( $propname ), array( 'VERSION', 'PRODID' )))1777 if( in_array( strtolower( $propname ), array( 'version', 'prodid' ))) 1676 1778 continue; 1779 /* rest of the line is opt.params and value */ 1677 1780 $line = substr( $line, $cix); 1678 1781 /* separate attributes from value */ 1679 $attr = array(); 1680 $attrix = -1; 1681 $strlen = strlen( $line ); 1682 for( $cix=0; $cix < $strlen; ++$cix ) { 1683 if(( ':' == $line[$cix] ) && 1684 ( '://' != substr( $line, $cix, 3 )) && 1685 ( !in_array( strtolower( substr( $line, $cix - 3, 4 )), array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ))) && 1686 ( !in_array( strtolower( substr( $line, $cix - 4, 5 )), array( 'crid:', 'news:', 'pres:' ))) && 1687 ( 'mailto:' != strtolower( substr( $line, $cix - 6, 7 )))) { 1782 $attr = array(); 1783 $attrix = -1; 1784 $strlen = strlen( $line ); 1785 $WithinQuotes = FALSE; 1786 $cix = 0; 1787 while( FALSE !== substr( $line, $cix, 1 )) { 1788 if( ( ':' == $line[$cix] ) && 1789 ( substr( $line,$cix, 3 ) != '://' ) && 1790 ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) && 1791 ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) && 1792 ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) && 1793 ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) && 1794 !$WithinQuotes ) { 1688 1795 $attrEnd = TRUE; 1689 1796 if(( $cix < ( $strlen - 4 )) && … … 1697 1804 } 1698 1805 if( $attrEnd) { 1699 $line = substr( $line, $cix + 1);1806 $line = substr( $line, ( $cix + 1 )); 1700 1807 break; 1701 1808 } 1702 1809 } 1810 if( '"' == $line[$cix] ) 1811 $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE; 1703 1812 if( ';' == $line[$cix] ) 1704 1813 $attr[++$attrix] = null; 1705 1814 else 1706 1815 $attr[$attrix] .= $line[$cix]; 1707 }1708 1816 $cix++; 1817 } 1709 1818 /* make attributes in array format */ 1710 1819 $propattr = array(); … … 1718 1827 /* update Property */ 1719 1828 if( FALSE !== strpos( $line, ',' )) { 1720 $content = explode( ',', $line ); 1721 $clen = count( $content ); 1722 for( $cix = 0; $cix < $clen; ++$cix ) { 1723 if( "\\" == substr( $content[$cix], -1 )) { 1724 $content[$cix] .= ','.$content[$cix + 1]; 1725 unset( $content[$cix + 1] ); 1726 ++$cix; } 1829 $content = array( 0 => '' ); 1830 $cix = $lix = 0; 1831 while( FALSE !== substr( $line, $lix, 1 )) { 1832 if(( 0 < $lix ) && ( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { 1833 $cix++; 1834 $content[$cix] = ''; 1835 } 1836 else 1837 $content[$cix] .= $line[$lix]; 1838 $lix++; 1727 1839 } 1728 1840 if( 1 < count( $content )) { … … 1736 1848 $line = calendarComponent::_strunrep( $line ); 1737 1849 } 1738 $this->setProperty( $propname, trim( $line), $propattr );1850 $this->setProperty( $propname, rtrim( $line, "\x00..\x1F" ), $propattr ); 1739 1851 } // end - foreach( $this->unparsed.. . 1740 1852 } // end - if( is_array( $this->unparsed.. . … … 1758 1870 * 1759 1871 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 1760 * @since 2. 8.1 - 2011-03-121872 * @since 2.10.16 - 2011-10-28 1761 1873 * @return string 1762 1874 */ 1763 1875 function createCalendar() { 1764 $calendarInit 1 = $calendarInit2 = $calendarxCaldecl = $calendarStart = $calendar = null;1876 $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = ''; 1765 1877 switch( $this->format ) { 1766 1878 case 'xcal': 1767 $calendarInit 1= '<?xml version="1.0" encoding="UTF-8"?>'.$this->nl.1768 '<!DOCTYPE iCalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'.$this->nl.1879 $calendarInit = '<?xml version="1.0" encoding="UTF-8"?>'.$this->nl. 1880 '<!DOCTYPE vcalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'.$this->nl. 1769 1881 '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"'; 1770 $calendarInit2 = '>'.$this->nl; 1771 $calendarStart = '<vcalendar'; 1882 $calendarStart = '>'.$this->nl.'<vcalendar'; 1772 1883 break; 1773 1884 default: … … 1779 1890 $calendarStart .= $this->createCalscale(); 1780 1891 $calendarStart .= $this->createMethod(); 1781 switch( $this->format ) { 1782 case 'xcal': 1783 $nlstrlen = strlen( $this->nl ); 1784 if( $this->nl == substr( $calendarStart, ( 0 - $nlstrlen ))) 1785 $calendarStart = substr( $calendarStart, 0, ( strlen( $calendarStart ) - $nlstrlen )); 1786 $calendarStart .= '>'.$this->nl; 1787 break; 1788 default: 1789 break; 1790 } 1892 if( 'xcal' == $this->format ) 1893 $calendarStart .= '>'.$this->nl; 1791 1894 $calendar .= $this->createXprop(); 1895 1792 1896 foreach( $this->components as $component ) { 1793 1897 if( empty( $component )) continue; … … 1795 1899 $calendar .= $component->createComponent( $this->xcaldecl ); 1796 1900 } 1797 if(( 0 < count( $this->xcaldecl )) && ( 'xcal' == $this->format)) { // xCal only1798 $calendarInit 1 .= $this->nl.'['.$this->nl;1799 $old_xcaldecl = array();1901 if(( 'xcal' == $this->format ) && ( 0 < count( $this->xcaldecl ))) { // xCal only 1902 $calendarInit .= ' ['; 1903 $old_xcaldecl = array(); 1800 1904 foreach( $this->xcaldecl as $declix => $declPart ) { 1801 if(( 0 < count( $old_xcaldecl)) && 1802 ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) && 1905 if(( 0 < count( $old_xcaldecl)) && 1906 isset( $declPart['uri'] ) && isset( $declPart['external'] ) && 1907 isset( $old_xcaldecl['uri'] ) && isset( $old_xcaldecl['external'] ) && 1908 ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) && 1803 1909 ( in_array( $declPart['external'], $old_xcaldecl['external'] ))) 1804 1910 continue; // no duplicate uri and ext. references 1805 $calendarxCaldecl .= '<!'; 1911 if(( 0 < count( $old_xcaldecl)) && 1912 !isset( $declPart['uri'] ) && !isset( $declPart['uri'] ) && 1913 isset( $declPart['ref'] ) && isset( $old_xcaldecl['ref'] ) && 1914 ( in_array( $declPart['ref'], $old_xcaldecl['ref'] ))) 1915 continue; // no duplicate element declarations 1916 $calendarxCaldecl .= $this->nl.'<!'; 1806 1917 foreach( $declPart as $declKey => $declValue ) { 1807 1918 switch( $declKey ) { // index … … 1815 1926 case 'ref': // no 3 1816 1927 $calendarxCaldecl .= $declValue.' '; 1928 $old_xcaldecl['ref'][] = $declValue; 1817 1929 break; 1818 1930 case 'external': // no 4 … … 1828 1940 } 1829 1941 } 1830 $calendarxCaldecl .= '>' .$this->nl;1831 } 1832 $calendar Init2 = ']'.$calendarInit2;1942 $calendarxCaldecl .= '>'; 1943 } 1944 $calendarxCaldecl .= $this->nl.']'; 1833 1945 } 1834 1946 switch( $this->format ) { … … 1840 1952 break; 1841 1953 } 1842 return $calendarInit 1.$calendarxCaldecl.$calendarInit2.$calendarStart.$calendar;1954 return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar; 1843 1955 } 1844 1956 /** … … 1846 1958 * 1847 1959 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 1848 * @since 2. 9.12 - 2011-07-131960 * @since 2.10.24 - 2011-12-23 1849 1961 * @param bool $utf8Encode 1850 1962 * @param bool $gzip … … 1858 1970 if( $gzip ) { 1859 1971 $output = gzencode( $output, 9 ); 1860 header( 'Content-Encoding: gzip' );1861 header( 'Vary: *' );1862 }1863 $filesize = strlen( $output );1972 header( 'Content-Encoding: gzip' ); 1973 header( 'Vary: *' ); 1974 header( 'Content-Length: '.strlen( $output )); 1975 } 1864 1976 if( 'xcal' == $this->format ) 1865 1977 header( 'Content-Type: application/calendar+xml; charset=utf-8' ); 1866 1978 else 1867 1979 header( 'Content-Type: text/calendar; charset=utf-8' ); 1868 header( 'Content-Length: '.$filesize );1869 1980 header( 'Content-Disposition: attachment; filename="'.$filename.'"' ); 1870 1981 header( 'Cache-Control: max-age=10' ); 1871 echo $output; 1872 die(); 1982 die( $output ); 1873 1983 } 1874 1984 /** … … 2050 2160 * 2051 2161 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2052 * @since 0.9.7 - 2006-11-232162 * @since 2.11.16 - 2012-02-04 2053 2163 * @return string 2054 2164 */ … … 2057 2167 $output = null; 2058 2168 foreach( $this->attach as $attachPart ) { 2059 if( !empty( $attachPart['value'] )) {2169 if( !empty( $attachPart['value'] )) { 2060 2170 $attributes = $this->_createParams( $attachPart['params'] ); 2171 if(( 'xcal' != $this->format ) && isset( $attachPart['params']['VALUE'] ) && ( 'BINARY' == $attachPart['params']['VALUE'] )) { 2172 $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); 2173 $str = 'ATTACH'.$attributes.$this->valueInit.$attachPart['value']; 2174 $output = substr( $str, 0, 75 ).$this->nl; 2175 $str = substr( $str, 75 ); 2176 $output .= ' '.chunk_split( $str, 74, $this->nl.' ' ); 2177 if( ' ' == substr( $output, -1 )) 2178 $output = rtrim( $output ); 2179 if( $this->nl != substr( $output, ( 0 - strlen( $this->nl )))) 2180 $output .= $this->nl; 2181 return $output; 2182 } 2061 2183 $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] ); 2062 2184 } … … 2088 2210 * 2089 2211 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2090 * @since 2. 9.8 - 2011-05-302212 * @since 2.11.12 - 2012-01-31 2091 2213 * @return string 2092 2214 */ … … 2105 2227 $attendee2 .= $paramvalue; 2106 2228 elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif 2229 $mParams = array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ); 2230 foreach( $paramvalue as $pKey => $pValue ) { // fix (opt) quotes 2231 if( is_array( $pValue ) || in_array( $pKey, $mParams )) 2232 continue; 2233 if(( FALSE !== strpos( $pValue, ':' )) || 2234 ( FALSE !== strpos( $pValue, ';' )) || 2235 ( FALSE !== strpos( $pValue, ',' ))) 2236 $paramvalue[$pKey] = '"'.$pValue.'"'; 2237 } 2107 2238 // set attenddee parameters in rfc2445 order 2108 2239 if( isset( $paramvalue['CUTYPE'] )) … … 2111 2242 $attendee1 .= $this->intAttrDelimiter.'MEMBER='; 2112 2243 foreach( $paramvalue['MEMBER'] as $cix => $opv ) 2113 $attendee1 .= ( $cix ) ? ', 2244 $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; 2114 2245 } 2115 2246 if( isset( $paramvalue['ROLE'] )) … … 2122 2253 $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO='; 2123 2254 foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv ) 2124 $attendee1 .= ( $cix ) ? ', 2255 $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; 2125 2256 } 2126 2257 if( isset( $paramvalue['DELEGATED-FROM'] )) { 2127 2258 $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM='; 2128 2259 foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv ) 2129 $attendee1 .= ( $cix ) ? ', 2260 $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; 2130 2261 } 2131 2262 if( isset( $paramvalue['SENT-BY'] )) 2132 $attendee1 .= $this->intAttrDelimiter.'SENT-BY= "'.$paramvalue['SENT-BY'].'"';2263 $attendee1 .= $this->intAttrDelimiter.'SENT-BY='.$paramvalue['SENT-BY']; 2133 2264 if( isset( $paramvalue['CN'] )) 2134 $attendee1 .= $this->intAttrDelimiter.'CN="'.$paramvalue['CN'].'"'; 2135 if( isset( $paramvalue['DIR'] )) 2136 $attendee1 .= $this->intAttrDelimiter.'DIR="'.$paramvalue['DIR'].'"'; 2265 $attendee1 .= $this->intAttrDelimiter.'CN='.$paramvalue['CN']; 2266 if( isset( $paramvalue['DIR'] )) { 2267 $delim = ( FALSE === strpos( $paramvalue['DIR'], '"' )) ? '"' : ''; 2268 $attendee1 .= $this->intAttrDelimiter.'DIR='.$delim.$paramvalue['DIR'].$delim; 2269 } 2137 2270 if( isset( $paramvalue['LANGUAGE'] )) 2138 2271 $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE']; … … 2163 2296 * 2164 2297 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2165 * @since 2. 6.34 - 2010-12-182298 * @since 2.12.18 - 2012-07-13 2166 2299 * @param string $value 2167 2300 * @param array $params, optional … … 2172 2305 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; 2173 2306 // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params 2174 if( FALSE !== ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) 2175 $value = strtoupper( substr( $value, 0, $pos )).substr( $value, $pos ); 2176 elseif( !empty( $value )) 2177 $value = 'MAILTO:'.$value; 2307 if( !empty( $value )) { 2308 if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) 2309 $value = 'MAILTO:'.$value; 2310 elseif( !empty( $value )) 2311 $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); 2312 $value = str_replace( 'mailto:', 'MAILTO:', $value ); 2313 } 2178 2314 $params2 = array(); 2179 2315 if( is_array($params )) { … … 2371 2507 return $this->_createElement( 'COMPLETED' ); 2372 2508 else return FALSE; 2373 $formatted = iCalUtilityFunctions::_ format_date_time( $this->completed['value'], 7 );2509 $formatted = iCalUtilityFunctions::_date2strdate( $this->completed['value'], 7 ); 2374 2510 $attributes = $this->_createParams( $this->completed['params'] ); 2375 2511 return $this->_createElement( 'COMPLETED', $attributes, $formatted ); … … 2453 2589 function createCreated() { 2454 2590 if( empty( $this->created )) return FALSE; 2455 $formatted = iCalUtilityFunctions::_ format_date_time( $this->created['value'], 7 );2591 $formatted = iCalUtilityFunctions::_date2strdate( $this->created['value'], 7 ); 2456 2592 $attributes = $this->_createParams( $this->created['params'] ); 2457 2593 return $this->_createElement( 'CREATED', $attributes, $formatted ); … … 2527 2663 * 2528 2664 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2529 * @since 2. 9.6 - 2011-05-142665 * @since 2.14.4 - 2012-09-26 2530 2666 * @return string 2531 2667 */ … … 2541 2677 return $this->_createElement( 'DTEND' ); 2542 2678 else return FALSE; 2543 $formatted = iCalUtilityFunctions::_format_date_time( $this->dtend['value'] ); 2544 if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) && 2545 ( !isset( $this->dtend['params']['VALUE'] ) || ( $this->dtend['params']['VALUE'] != 'DATE' )) && 2546 !isset( $this->dtend['params']['TZID'] )) 2547 $this->dtend['params']['TZID'] = $tzid; 2679 $parno = ( isset( $this->dtend['params']['VALUE'] ) && ( 'DATE' == $this->dtend['params']['VALUE'] )) ? 3 : null; 2680 $formatted = iCalUtilityFunctions::_date2strdate( $this->dtend['value'], $parno ); 2548 2681 $attributes = $this->_createParams( $this->dtend['params'] ); 2549 2682 return $this->_createElement( 'DTEND', $attributes, $formatted ); … … 2595 2728 !isset( $this->dtstamp['value']['sec'] )) 2596 2729 $this->_makeDtstamp(); 2597 $formatted = iCalUtilityFunctions::_ format_date_time( $this->dtstamp['value'], 7 );2730 $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstamp['value'], 7 ); 2598 2731 $attributes = $this->_createParams( $this->dtstamp['params'] ); 2599 2732 return $this->_createElement( 'DTSTAMP', $attributes, $formatted ); … … 2603 2736 * 2604 2737 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2605 * @since 2. 6.25 - 2010-11-092738 * @since 2.14.1 - 2012-09-29 2606 2739 * @return void 2607 2740 */ 2608 2741 function _makeDtstamp() { 2609 $d = mktime( date('H'), date('m'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y')); 2610 $this->dtstamp['value'] = array( 'year' => date( 'Y', $d ) 2611 , 'month' => date( 'm', $d ) 2612 , 'day' => date( 'd', $d ) 2613 , 'hour' => date( 'H', $d ) 2614 , 'min' => date( 'i', $d ) 2615 , 'sec' => date( 's', $d )); 2742 $d = date( 'Y-m-d-H-i-s', mktime( date('H'), date('i'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y'))); 2743 $date = explode( '-', $d ); 2744 $this->dtstamp['value'] = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2], 'hour' => $date[3], 'min' => $date[4], 'sec' => $date[5], 'tz' => 'Z' ); 2616 2745 $this->dtstamp['params'] = null; 2617 2746 } … … 2645 2774 * 2646 2775 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2647 * @since 2. 9.6 - 2011-05-152776 * @since 2.14.4 - 2012-09-26 2648 2777 * @return string 2649 2778 */ … … 2661 2790 } 2662 2791 if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) 2663 unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] ); 2664 elseif(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) && 2665 ( !isset( $this->dtstart['params']['VALUE'] ) || ( $this->dtstart['params']['VALUE'] != 'DATE' )) && 2666 !isset( $this->dtstart['params']['TZID'] )) 2667 $this->dtstart['params']['TZID'] = $tzid; 2668 $formatted = iCalUtilityFunctions::_format_date_time( $this->dtstart['value'] ); 2792 unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] ); 2793 $parno = ( isset( $this->dtstart['params']['VALUE'] ) && ( 'DATE' == $this->dtstart['params']['VALUE'] )) ? 3 : null; 2794 $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstart['value'], $parno ); 2669 2795 $attributes = $this->_createParams( $this->dtstart['params'] ); 2670 2796 return $this->_createElement( 'DTSTART', $attributes, $formatted ); … … 2705 2831 * 2706 2832 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2707 * @since 2. 4.8 - 2008-10-222833 * @since 2.14.4 - 2012-09-26 2708 2834 * @return string 2709 2835 */ … … 2721 2847 return FALSE; 2722 2848 } 2723 $formatted = iCalUtilityFunctions::_format_date_time( $this->due['value'] ); 2724 if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) && 2725 ( !isset( $this->due['params']['VALUE'] ) || ( $this->due['params']['VALUE'] != 'DATE' )) && 2726 !isset( $this->due['params']['TZID'] )) 2727 $this->due['params']['TZID'] = $tzid; 2849 $parno = ( isset( $this->due['params']['VALUE'] ) && ( 'DATE' == $this->due['params']['VALUE'] )) ? 3 : null; 2850 $formatted = iCalUtilityFunctions::_date2strdate( $this->due['value'], $parno ); 2728 2851 $attributes = $this->_createParams( $this->due['params'] ); 2729 2852 return $this->_createElement( 'DUE', $attributes, $formatted ); … … 2777 2900 else return FALSE; 2778 2901 $attributes = $this->_createParams( $this->duration['params'] ); 2779 return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_ format_duration( $this->duration['value'] ));2902 return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_duration2str( $this->duration['value'] )); 2780 2903 } 2781 2904 /** … … 2795 2918 if( empty( $week )) if( $this->getConfig( 'allowEmpty' )) $week = null; else return FALSE; 2796 2919 if( is_array( $week ) && ( 1 <= count( $week ))) 2797 $this->duration = array( 'value' => iCalUtilityFunctions::_duration _array( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));2920 $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); 2798 2921 elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) { 2799 2922 $week = trim( $week ); 2800 2923 if( in_array( substr( $week, 0, 1 ), array( '+', '-' ))) 2801 2924 $week = substr( $week, 1 ); 2802 $this->duration = array( 'value' => iCalUtilityFunctions::_duration _string( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));2925 $this->duration = array( 'value' => iCalUtilityFunctions::_durationStr2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); 2803 2926 } 2804 2927 elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec )) 2805 2928 return FALSE; 2806 2929 else 2807 $this->duration = array( 'value' => iCalUtilityFunctions::_duration _array( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params ));2930 $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params )); 2808 2931 return TRUE; 2809 2932 } … … 2830 2953 foreach( $theExdate['value'] as $eix => $exdatePart ) { 2831 2954 $parno = count( $exdatePart ); 2832 $formatted = iCalUtilityFunctions::_ format_date_time( $exdatePart, $parno );2955 $formatted = iCalUtilityFunctions::_date2strdate( $exdatePart, $parno ); 2833 2956 if( isset( $theExdate['params']['TZID'] )) 2834 2957 $formatted = str_replace( 'Z', '', $formatted); … … 2857 2980 * 2858 2981 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2859 * @since 2. 5.1 - 2008-11-052982 * @since 2.14.1 - 2012-10-02 2860 2983 * @param array exdates 2861 2984 * @param array $params, optional … … 2873 2996 } 2874 2997 $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); 2998 $toZ = ( isset( $input['params']['TZID'] ) && in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) ? TRUE : FALSE; 2875 2999 /* ev. check 1:st date and save ev. timezone **/ 2876 3000 iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] ); 2877 3001 iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter 2878 3002 foreach( $exdates as $eix => $theExdate ) { 2879 if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) 3003 iCalUtilityFunctions::_strDate2arr( $theExdate ); 3004 if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) { 3005 if( isset( $theExdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theExdate['tz'] )) { 3006 if( isset( $input['params']['TZID'] )) 3007 $theExdate['tz'] = $input['params']['TZID']; 3008 else 3009 $input['params']['TZID'] = $theExdate['tz']; 3010 } 2880 3011 $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno ); 2881 elseif( is_array( $theExdate )) 2882 $exdatea = iCalUtilityFunctions::_date_time_array( $theExdate, $parno ); 2883 elseif( 8 <= strlen( trim( $theExdate ))) // ex. 2006-08-03 10:12:18 2884 $exdatea = iCalUtilityFunctions::_date_time_string( $theExdate, $parno ); 3012 } 3013 elseif( is_array( $theExdate )) { 3014 $d = iCalUtilityFunctions::_chkDateArr( $theExdate, $parno ); 3015 if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { 3016 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); 3017 $exdatea = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 3018 unset( $exdatea['unparsedtext'] ); 3019 } 3020 else 3021 $exdatea = $d; 3022 } 3023 elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18 3024 $exdatea = iCalUtilityFunctions::_strdate2date( $theExdate, $parno ); 3025 unset( $exdatea['unparsedtext'] ); 3026 } 2885 3027 if( 3 == $parno ) 2886 3028 unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] ); … … 2892 3034 ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] ))) 2893 3035 unset( $exdatea['tz'] ); 3036 if( $toZ ) // time zone Z 3037 $exdatea['tz'] = 'Z'; 2894 3038 $input['value'][] = $exdatea; 2895 3039 } … … 2900 3044 unset( $input['params']['TZID'] ); 2901 3045 } 3046 if( $toZ ) // time zone Z 3047 unset( $input['params']['TZID'] ); 2902 3048 iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index ); 2903 3049 return TRUE; … … 2941 3087 * 2942 3088 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2943 * @since 2. 4.8 - 2008-10-223089 * @since 2.1.23 - 2012-02-16 2944 3090 * @return string 2945 3091 */ … … 2948 3094 $output = null; 2949 3095 foreach( $this->freebusy as $freebusyPart ) { 2950 if( empty( $freebusyPart['value'] ) ) {3096 if( empty( $freebusyPart['value'] ) || (( 1 == count( $freebusyPart['value'] )) && isset( $freebusyPart['value']['fbtype'] ))) { 2951 3097 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' ); 2952 3098 continue; … … 2954 3100 $attributes = $content = null; 2955 3101 if( isset( $freebusyPart['value']['fbtype'] )) { 2956 $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype'];3102 $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype']; 2957 3103 unset( $freebusyPart['value']['fbtype'] ); 2958 3104 $freebusyPart['value'] = array_values( $freebusyPart['value'] ); … … 2964 3110 $cnt = count( $freebusyPart['value']); 2965 3111 foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) { 2966 $formatted = iCalUtilityFunctions::_ format_date_time( $freebusyPeriod[0] );3112 $formatted = iCalUtilityFunctions::_date2strdate( $freebusyPeriod[0] ); 2967 3113 $content .= $formatted; 2968 3114 $content .= '/'; … … 2976 3122 isset( $freebusyPeriod[1]['month'] ) && 2977 3123 isset( $freebusyPeriod[1]['day'] )) { 2978 $content .= iCalUtilityFunctions::_ format_date_time( $freebusyPeriod[1] );3124 $content .= iCalUtilityFunctions::_date2strdate( $freebusyPeriod[1] ); 2979 3125 } 2980 3126 else { // period= -> dur-time 2981 $content .= iCalUtilityFunctions::_ format_duration( $freebusyPeriod[1] );3127 $content .= iCalUtilityFunctions::_duration2str( $freebusyPeriod[1] ); 2982 3128 } 2983 3129 if( $fno < $cnt ) 2984 3130 $content .= ','; 2985 ++$fno;3131 $fno++; 2986 3132 } 2987 3133 $output .= $this->_createElement( 'FREEBUSY', $attributes, $content ); … … 2993 3139 * 2994 3140 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 2995 * @since 2. 8.10 - 2011-03-243141 * @since 2.10.30 - 2012-01-16 2996 3142 * @param string $fbType 2997 3143 * @param array $fbValues … … 3022 3168 if( is_array( $fbMember )) { 3023 3169 if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value 3024 $freebusyPairMember = iCalUtilityFunctions::_ date_time_array( $fbMember, 7 );3170 $freebusyPairMember = iCalUtilityFunctions::_chkDateArr( $fbMember, 7 ); 3025 3171 $freebusyPairMember['tz'] = 'Z'; 3026 3172 } … … 3030 3176 } 3031 3177 else { // array format duration 3032 $freebusyPairMember = iCalUtilityFunctions::_duration _array( $fbMember );3178 $freebusyPairMember = iCalUtilityFunctions::_duration2arr( $fbMember ); 3033 3179 } 3034 3180 } … … 3037 3183 if( 'P' != $fbMember{0} ) 3038 3184 $fbmember = substr( $fbMember, 1 ); 3039 $freebusyPairMember = iCalUtilityFunctions::_duration _string( $fbMember );3185 $freebusyPairMember = iCalUtilityFunctions::_durationStr2arr( $fbMember ); 3040 3186 } 3041 3187 elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18 3042 $freebusyPairMember = iCalUtilityFunctions::_date_time_string( $fbMember, 7 ); 3188 $freebusyPairMember = iCalUtilityFunctions::_strdate2date( $fbMember, 7 ); 3189 unset( $freebusyPairMember['unparsedtext'] ); 3043 3190 $freebusyPairMember['tz'] = 'Z'; 3044 3191 } … … 3058 3205 * 3059 3206 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3060 * @since 2. 4.8 - 2008-10-213207 * @since 2.12.6 - 2012-04-21 3061 3208 * @return string 3062 3209 */ … … 3066 3213 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE; 3067 3214 $attributes = $this->_createParams( $this->geo['params'] ); 3068 $content = null; 3069 $content .= number_format( (float) $this->geo['value']['latitude'], 6, '.', ''); 3070 $content .= ';'; 3071 $content .= number_format( (float) $this->geo['value']['longitude'], 6, '.', ''); 3215 if( 0.0 < $this->geo['value']['latitude'] ) 3216 $sign = '+'; 3217 else 3218 $sign = ( 0.0 > $this->geo['value']['latitude'] ) ? '-' : ''; 3219 $content = $sign.sprintf( "%09.6f", abs( $this->geo['value']['latitude'] )); // sprintf && lpad && float && sign !"#€%&/( 3220 $content = rtrim( rtrim( $content, '0' ), '.' ); 3221 if( 0.0 < $this->geo['value']['longitude'] ) 3222 $sign = '+'; 3223 else 3224 $sign = ( 0.0 > $this->geo['value']['longitude'] ) ? '-' : ''; 3225 $content .= ';'.$sign.sprintf( '%8.6f', abs( $this->geo['value']['longitude'] )); // sprintf && lpad && float && sign !"#€%&/( 3226 $content = rtrim( rtrim( $content, '0' ), '.' ); 3072 3227 return $this->_createElement( 'GEO', $attributes, $content ); 3073 3228 } … … 3076 3231 * 3077 3232 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3078 * @since 2. 4.8 - 2008-11-043233 * @since 2.12.5 - 2012-04-21 3079 3234 * @param float $latitude 3080 3235 * @param float $longitude … … 3083 3238 */ 3084 3239 function setGeo( $latitude, $longitude, $params=FALSE ) { 3085 if( !empty( $latitude ) && !empty( $longitude )) { 3240 if(( !empty( $latitude ) || ( 0 == $latitude )) && 3241 ( !empty( $longitude ) || ( 0 == $longitude ))) { 3086 3242 if( !is_array( $this->geo )) $this->geo = array(); 3087 $this->geo['value']['latitude'] = $latitude;3088 $this->geo['value']['longitude'] = $longitude;3243 $this->geo['value']['latitude'] = (float) $latitude; 3244 $this->geo['value']['longitude'] = (float) $longitude; 3089 3245 $this->geo['params'] = iCalUtilityFunctions::_setParams( $params ); 3090 3246 } … … 3109 3265 if( empty( $this->lastmodified )) return FALSE; 3110 3266 $attributes = $this->_createParams( $this->lastmodified['params'] ); 3111 $formatted = iCalUtilityFunctions::_ format_date_time( $this->lastmodified['value'], 7 );3267 $formatted = iCalUtilityFunctions::_date2strdate( $this->lastmodified['value'], 7 ); 3112 3268 return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted ); 3113 3269 } … … 3188 3344 * 3189 3345 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3190 * @since 2. 6.27 - 2010-11-293346 * @since 2.12.18 - 2012-07-13 3191 3347 * @param string $value 3192 3348 * @param array params optional … … 3195 3351 function setOrganizer( $value, $params=FALSE ) { 3196 3352 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; 3197 if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) 3198 $value = 'MAILTO:'.$value; 3199 else 3200 $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); 3201 $value = str_replace( 'mailto:', 'MAILTO:', $value ); 3353 if( !empty( $value )) { 3354 if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) 3355 $value = 'MAILTO:'.$value; 3356 elseif( !empty( $value )) 3357 $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); 3358 $value = str_replace( 'mailto:', 'MAILTO:', $value ); 3359 } 3202 3360 $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); 3203 3361 if( isset( $this->organizer['params']['SENT-BY'] )){ … … 3281 3439 * 3282 3440 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3283 * @since 2. 4.16 - 2008-10-263441 * @since 2.14.1 - 2012-10-04 3284 3442 * @return string 3285 3443 */ … … 3290 3448 if( $utctime ) 3291 3449 unset( $this->rdate['params']['TZID'] ); 3292 foreach( $this->rdate as $ theRdate ) {3450 foreach( $this->rdate as $rpix => $theRdate ) { 3293 3451 if( empty( $theRdate['value'] )) { 3294 3452 if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' ); … … 3301 3459 $content = null; 3302 3460 $rno = 1; 3303 foreach( $theRdate['value'] as $r pix => $rdatePart ) {3461 foreach( $theRdate['value'] as $rix => $rdatePart ) { 3304 3462 $contentPart = null; 3305 3463 if( is_array( $rdatePart ) && … … 3307 3465 if( $utctime ) 3308 3466 unset( $rdatePart[0]['tz'] ); 3309 $formatted = iCalUtilityFunctions::_ format_date_time( $rdatePart[0]); // PERIOD part 13467 $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[0] ); // PERIOD part 1 3310 3468 if( $utctime || !empty( $theRdate['params']['TZID'] )) 3311 3469 $formatted = str_replace( 'Z', '', $formatted); 3312 if( 0 < $rpix ) {3313 if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) {3314 if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z';3315 }3316 else3317 $formatted = str_replace( 'Z', '', $formatted );3318 }3319 3470 $contentPart .= $formatted; 3320 3471 $contentPart .= '/'; … … 3334 3485 if( $utctime ) 3335 3486 unset( $rdatePart[1]['tz'] ); 3336 $formatted = iCalUtilityFunctions::_ format_date_time( $rdatePart[1] ); // PERIOD part 23487 $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[1] ); // PERIOD part 2 3337 3488 if( $utctime || !empty( $theRdate['params']['TZID'] )) 3338 $formatted = str_replace( 'Z', '', $formatted);3339 if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) {3340 if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z';3341 }3342 else3343 3489 $formatted = str_replace( 'Z', '', $formatted ); 3344 3490 $contentPart .= $formatted; 3345 3491 } 3346 3492 else { // period= -> dur-time 3347 $contentPart .= iCalUtilityFunctions::_ format_duration( $rdatePart[1] );3493 $contentPart .= iCalUtilityFunctions::_duration2str( $rdatePart[1] ); 3348 3494 } 3349 3495 } // PERIOD end … … 3351 3497 if( $utctime ) 3352 3498 unset( $rdatePart['tz'] ); 3353 $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart); 3499 $parno = ( isset( $theRdate['params']['VALUE'] ) && ( 'DATE' == isset( $theRdate['params']['VALUE'] ))) ? 3 : null; 3500 $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart, $parno ); 3354 3501 if( $utctime || !empty( $theRdate['params']['TZID'] )) 3355 3502 $formatted = str_replace( 'Z', '', $formatted); 3356 if( !$utctime && ( 0 < $rpix )) {3357 if( !empty( $theRdate['value'][0]['tz'] ) && iCalUtilityFunctions::_isOffset( $theRdate['value'][0]['tz'] )) {3358 if( 'Z' != substr( $formatted, -1 ))3359 $formatted .= 'Z';3360 }3361 else3362 $formatted = str_replace( 'Z', '', $formatted );3363 }3364 3503 $contentPart .= $formatted; 3365 3504 } … … 3367 3506 if( $rno < $cnt ) 3368 3507 $content .= ','; 3369 ++$rno;3508 $rno++; 3370 3509 } 3371 3510 $output .= $this->_createElement( 'RDATE', $attributes, $content ); … … 3377 3516 * 3378 3517 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3379 * @since 2. 5.1 - 2008-11-073518 * @since 2.14.1 - 2012-10-04 3380 3519 * @param array $rdates 3381 3520 * @param array $params, optional … … 3397 3536 $input['params']['VALUE'] = 'DATE-TIME'; 3398 3537 } 3538 $zArr = array( 'GMT', 'UTC', 'Z' ); 3539 $toZ = ( isset( $params['TZID'] ) && in_array( strtoupper( $params['TZID'] ), $zArr )) ? TRUE : FALSE; 3399 3540 /* check if PERIOD, if not set */ 3400 3541 if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) && … … 3411 3552 $date = reset( $date ); 3412 3553 iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] ); 3413 if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))3414 unset( $input['params']['TZID'] );3415 3554 iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default 3416 3555 foreach( $rdates as $rpix => $theRdate ) { 3417 3556 $inputa = null; 3557 iCalUtilityFunctions::_strDate2arr( $theRdate ); 3418 3558 if( is_array( $theRdate )) { 3419 3559 if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD 3420 3560 foreach( $theRdate as $rix => $rPeriod ) { 3561 iCalUtilityFunctions::_strDate2arr( $theRdate ); 3421 3562 if( is_array( $rPeriod )) { 3422 if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) // timestamp 3423 $inputab = ( isset( $rPeriod['tz'] )) ? iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ) : iCalUtilityFunctions::_timestamp2date( $rPeriod, 6 ); 3424 elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) 3425 $inputab = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_date_time_array( $rPeriod, $parno ) : iCalUtilityFunctions::_date_time_array( $rPeriod, 6 ); 3426 elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) // text-date 3427 $inputab = iCalUtilityFunctions::_date_time_string( reset( $rPeriod ), $parno ); 3563 if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) { // timestamp 3564 if( isset( $rPeriod['tz'] ) && !iCalUtilityFunctions::_isOffset( $rPeriod['tz'] )) { 3565 if( isset( $input['params']['TZID'] )) 3566 $rPeriod['tz'] = $input['params']['TZID']; 3567 else 3568 $input['params']['TZID'] = $rPeriod['tz']; 3569 } 3570 $inputab = iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ); 3571 } 3572 elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) { 3573 $d = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_chkDateArr( $rPeriod, $parno ) : iCalUtilityFunctions::_chkDateArr( $rPeriod, 6 ); 3574 if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { 3575 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); 3576 $inputab = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 3577 unset( $inputab['unparsedtext'] ); 3578 } 3579 else 3580 $inputab = $d; 3581 } 3582 elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date 3583 $inputab = iCalUtilityFunctions::_strdate2date( reset( $rPeriod ), $parno ); 3584 unset( $inputab['unparsedtext'] ); 3585 } 3428 3586 else // array format duration 3429 $inputab = iCalUtilityFunctions::_duration_array( $rPeriod );3587 $inputab = iCalUtilityFunctions::_duration2arr( $rPeriod ); 3430 3588 } 3431 3589 elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration 3432 3590 ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) { 3433 3591 if( 'P' != $rPeriod[0] ) 3434 $rPeriod = substr( $rPeriod, 1 );3435 $inputab = iCalUtilityFunctions::_duration_string( $rPeriod );3592 $rPeriod = substr( $rPeriod, 1 ); 3593 $inputab = iCalUtilityFunctions::_durationStr2arr( $rPeriod ); 3436 3594 } 3437 elseif( 8 <= strlen( trim( $rPeriod ))) // text date ex. 2006-08-03 10:12:18 3438 $inputab = iCalUtilityFunctions::_date_time_string( $rPeriod, $parno ); 3439 if( isset( $input['params']['TZID'] ) || 3440 ( isset( $inputab['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputab['tz'] )) || 3441 ( isset( $inputa[0] ) && ( !isset( $inputa[0]['tz'] ))) || 3442 ( isset( $inputa[0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputa[0]['tz'] ))) 3443 unset( $inputab['tz'] ); 3444 $inputa[] = $inputab; 3595 elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18 3596 $inputab = iCalUtilityFunctions::_strdate2date( $rPeriod, $parno ); 3597 unset( $inputab['unparsedtext'] ); 3598 } 3599 if(( 0 == $rpix ) && ( 0 == $rix )) { 3600 if( isset( $inputab['tz'] ) && in_array( strtoupper( $inputab['tz'] ), $zArr )) { 3601 $inputab['tz'] = 'Z'; 3602 $toZ = TRUE; 3603 } 3604 } 3605 else { 3606 if( isset( $inputa[0]['tz'] ) && ( 'Z' == $inputa[0]['tz'] ) && isset( $inputab['year'] )) 3607 $inputab['tz'] = 'Z'; 3608 else 3609 unset( $inputab['tz'] ); 3610 } 3611 if( $toZ && isset( $inputab['year'] ) ) 3612 $inputab['tz'] = 'Z'; 3613 $inputa[] = $inputab; 3445 3614 } 3446 3615 } // PERIOD end 3447 elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) // timestamp 3616 elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) { // timestamp 3617 if( isset( $theRdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theRdate['tz'] )) { 3618 if( isset( $input['params']['TZID'] )) 3619 $theRdate['tz'] = $input['params']['TZID']; 3620 else 3621 $input['params']['TZID'] = $theRdate['tz']; 3622 } 3448 3623 $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno ); 3449 else // date[-time] 3450 $inputa = iCalUtilityFunctions::_date_time_array( $theRdate, $parno ); 3451 } 3452 elseif( 8 <= strlen( trim( $theRdate ))) // text date ex. 2006-08-03 10:12:18 3453 $inputa = iCalUtilityFunctions::_date_time_string( $theRdate, $parno ); 3624 } 3625 else { // date[-time] 3626 $inputa = iCalUtilityFunctions::_chkDateArr( $theRdate, $parno ); 3627 if( isset( $inputa['tz'] ) && ( 'Z' != $inputa['tz'] ) && iCalUtilityFunctions::_isOffset( $inputa['tz'] )) { 3628 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $inputa['year'], $inputa['month'], $inputa['day'], $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] ); 3629 $inputa = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 3630 unset( $inputa['unparsedtext'] ); 3631 } 3632 } 3633 } 3634 elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18 3635 $inputa = iCalUtilityFunctions::_strdate2date( $theRdate, $parno ); 3636 unset( $inputa['unparsedtext'] ); 3637 if( $toZ ) 3638 $inputa['tz'] = 'Z'; 3639 } 3454 3640 if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD 3641 if(( 0 == $rpix ) && !$toZ ) 3642 $toZ = ( isset( $inputa['tz'] ) && in_array( strtoupper( $inputa['tz'] ), $zArr )) ? TRUE : FALSE; 3643 if( $toZ ) 3644 $inputa['tz'] = 'Z'; 3455 3645 if( 3 == $parno ) 3456 3646 unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] ); 3457 3647 elseif( isset( $inputa['tz'] )) 3458 $inputa['tz'] = (string) $inputa['tz']; 3459 if( isset( $input['params']['TZID'] ) || 3460 ( isset( $inputa['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputa['tz'] )) || 3461 ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) || 3462 ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] ))) 3463 unset( $inputa['tz'] ); 3464 } 3465 $input['value'][] = $inputa; 3648 $inputa['tz'] = (string) $inputa['tz']; 3649 if( isset( $input['params']['TZID'] ) || ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] )))) 3650 if( !$toZ ) 3651 unset( $inputa['tz'] ); 3652 } 3653 $input['value'][] = $inputa; 3466 3654 } 3467 3655 if( 3 == $parno ) { … … 3469 3657 unset( $input['params']['TZID'] ); 3470 3658 } 3659 if( $toZ ) 3660 unset( $input['params']['TZID'] ); 3471 3661 iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index ); 3472 3662 return TRUE; … … 3480 3670 * 3481 3671 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3482 * @since 2. 9.6 - 2011-05-153672 * @since 2.14.4 - 2012-09-26 3483 3673 * @return string 3484 3674 */ … … 3487 3677 if( empty( $this->recurrenceid['value'] )) 3488 3678 return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE; 3489 $formatted = iCalUtilityFunctions::_format_date_time( $this->recurrenceid['value'] ); 3490 if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) && 3491 ( !isset( $this->recurrenceid['params']['VALUE'] ) || ( $this->recurrenceid['params']['VALUE'] != 'DATE' )) && 3492 !isset( $this->recurrenceid['params']['TZID'] )) 3493 $this->recurrenceid['params']['TZID'] = $tzid; 3679 $parno = ( isset( $this->recurrenceid['params']['VALUE'] ) && ( 'DATE' == $this->recurrenceid['params']['VALUE'] )) ? 3 : null; 3680 $formatted = iCalUtilityFunctions::_date2strdate( $this->recurrenceid['value'], $parno ); 3494 3681 $attributes = $this->_createParams( $this->recurrenceid['params'] ); 3495 3682 return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted ); … … 3529 3716 * 3530 3717 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3531 * @since 2. 4.8 - 2008-10-233718 * @since 2.11.24 - 2012-02-23 3532 3719 * @return string 3533 3720 */ … … 3536 3723 $output = null; 3537 3724 foreach( $this->relatedto as $relation ) { 3538 if( empty( $relation['value'] )) { 3539 if( $this->getConfig( 'allowEmpty' )) $output.= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] )); 3540 continue; 3541 } 3542 $attributes = $this->_createParams( $relation['params'] ); 3543 $content = ( 'xcal' != $this->format ) ? '<' : ''; 3544 $content .= $this->_strrep( $relation['value'] ); 3545 $content .= ( 'xcal' != $this->format ) ? '>' : ''; 3546 $output .= $this->_createElement( 'RELATED-TO', $attributes, $content ); 3725 if( !empty( $relation['value'] )) 3726 $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ), $this->_strrep( $relation['value'] ) ); 3727 elseif( $this->getConfig( 'allowEmpty' )) 3728 $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] )); 3547 3729 } 3548 3730 return $output; … … 3552 3734 * 3553 3735 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3554 * @since 2. 5.1 - 2008-11-073736 * @since 2.11.24 - 2012-02-23 3555 3737 * @param float $relid 3556 3738 * @param array $params, optional … … 3560 3742 function setRelatedTo( $value, $params=FALSE, $index=FALSE ) { 3561 3743 if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; 3562 if(( '<' == substr( $value, 0, 1 )) && ( '>' == substr( $value, -1 )))3563 $value = substr( $value, 1, ( strlen( $value ) - 2 ));3564 3744 iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default 3565 3745 iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index ); … … 3585 3765 } 3586 3766 /** 3587 * set calendar component property transp3767 * set calendar component property repeat 3588 3768 * 3589 3769 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> … … 3742 3922 * set calendar component property sequence 3743 3923 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3744 * @since 2. 9.3 - 2011-05-143924 * @since 2.10.8 - 2011-09-19 3745 3925 * @param int $value optional 3746 3926 * @param array $params optional … … 3749 3929 function setSequence( $value=FALSE, $params=FALSE ) { 3750 3930 if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value )) 3751 $value = ( isset( $this->sequence['value'] ) && ( 0 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : 1;3931 $value = ( isset( $this->sequence['value'] ) && ( -1 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : '0'; 3752 3932 $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); 3753 3933 return TRUE; … … 3869 4049 isset( $this->trigger['value']['month'] ) && 3870 4050 isset( $this->trigger['value']['day'] )) 3871 $content .= iCalUtilityFunctions::_ format_date_time( $this->trigger['value'] );4051 $content .= iCalUtilityFunctions::_date2strdate( $this->trigger['value'] ); 3872 4052 else { 3873 4053 if( TRUE !== $this->trigger['value']['relatedStart'] ) … … 3875 4055 if( $this->trigger['value']['before'] ) 3876 4056 $content .= '-'; 3877 $content .= iCalUtilityFunctions::_ format_duration( $this->trigger['value'] );4057 $content .= iCalUtilityFunctions::_duration2str( $this->trigger['value'] ); 3878 4058 } 3879 4059 $attributes .= $this->_createParams( $this->trigger['params'] ); … … 3884 4064 * 3885 4065 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 3886 * @since 2. 9.9 - 2011-06-174066 * @since 2.14.1 - 2012-09-20 3887 4067 * @param mixed $year 3888 4068 * @param mixed $month optional … … 3905 4085 else 3906 4086 return FALSE; 3907 if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp 4087 if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp UTC 3908 4088 $params = iCalUtilityFunctions::_setParams( $month ); 3909 4089 $date = iCalUtilityFunctions::_timestamp2date( $year, 7 ); … … 3938 4118 if( 'P' != $year[0] ) 3939 4119 $year = substr( $year, 1 ); 3940 $date = iCalUtilityFunctions::_duration _string( $year);4120 $date = iCalUtilityFunctions::_durationStr2arr( $year); 3941 4121 } 3942 4122 else // date 3943 $date = iCalUtilityFunctions::_ date_time_string( $year, 7 );3944 unset( $year, $month, $day );4123 $date = iCalUtilityFunctions::_strdate2date( $year, 7 ); 4124 unset( $year, $month, $day, $date['unparsedtext'] ); 3945 4125 if( empty( $date )) 3946 4126 $sec = 0; … … 4278 4458 * 4279 4459 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 4280 * @since 2. 9.3 - 2011-05-144460 * @since 2.11.9 - 2012-01-16 4281 4461 * @param string $label 4282 4462 * @param mixed $value … … 4285 4465 */ 4286 4466 function setXprop( $label, $value, $params=FALSE ) { 4287 if( empty( $label )) return; 4467 if( empty( $label )) 4468 return FALSE; 4469 if( 'X-' != strtoupper( substr( $label, 0, 2 ))) 4470 return FALSE; 4288 4471 if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; 4289 4472 $xprop = array( 'value' => $value ); … … 4338 4521 * 4339 4522 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 4340 * @since 2. 6.22 - 2010-12-064523 * @since 2.10.16 - 2011-10-28 4341 4524 * @param string $label property name 4342 4525 * @param string $attributes property attributes … … 4357 4540 $attachInlineBinary = FALSE; 4358 4541 $attachfmttype = null; 4542 if (( 'xcal' == $this->format) && ( 'x-' == substr( $label, 0, 2 ))) { 4543 $this->xcaldecl[] = array( 'xmldecl' => 'ELEMENT' 4544 , 'ref' => $label 4545 , 'type2' => '(#PCDATA)' ); 4546 } 4359 4547 if( !empty( $attributes )) { 4360 4548 $attributes = trim( $attributes ); 4361 if ( 'xcal' == $this->format ) {4549 if ( 'xcal' == $this->format ) { 4362 4550 $attributes2 = explode( $this->intAttrDelimiter, $attributes ); 4363 4551 $attributes = null; 4364 foreach( $attributes2 as $a ttribute ) {4552 foreach( $attributes2 as $aix => $attribute ) { 4365 4553 $attrKVarr = explode( '=', $attribute ); 4366 4554 if( empty( $attrKVarr[0] )) … … 4368 4556 if( !isset( $attrKVarr[1] )) { 4369 4557 $attrValue = $attrKVarr[0]; 4370 $attrKey = null;4558 $attrKey = $aix; 4371 4559 } 4372 4560 elseif( 2 == count( $attrKVarr)) { … … 4402 4590 } 4403 4591 } 4404 if(( (( 'attach' == $label ) && !$attachInlineBinary ) ||4405 ( in_array( $label, array( 'tzurl', 'url' )))) && ( 'xcal' == $this->format)) {4592 if(( 'xcal' == $this->format) && 4593 ((( 'attach' == $label ) && !$attachInlineBinary ) || ( in_array( $label, array( 'tzurl', 'url' ))))) { 4406 4594 $pos = strrpos($content, "/"); 4407 4595 $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content; … … 4417 4605 if( 'attach' == $label ) { 4418 4606 $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes ); 4419 $content = $this-> _createElement( 'extref', $attributes, null );4607 $content = $this->nl.$this->_createElement( 'extref', $attributes, null ); 4420 4608 $attributes = null; 4421 4609 } 4422 4610 } 4423 elseif(( ' attach' == $label ) && $attachInlineBinary && ( 'xcal' == $this->format)) {4611 elseif(( 'xcal' == $this->format) && ( 'attach' == $label ) && $attachInlineBinary ) { 4424 4612 $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute 4425 4613 } … … 4429 4617 case 'xcal': 4430 4618 $output .= ' /'; 4431 $output .= $this->elementStart2 ;4619 $output .= $this->elementStart2.$this->nl; 4432 4620 return $output; 4433 4621 break; … … 4453 4641 * 4454 4642 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 4455 * @since 2. 6.33 - 2010-12-184643 * @since 2.10.27 - 2012-01-16 4456 4644 * @param array $params optional 4457 4645 * @param array $ctrKeys optional … … 4467 4655 $xparams = array(); 4468 4656 foreach( $params as $paramKey => $paramValue ) { 4657 if(( FALSE !== strpos( $paramValue, ':' )) || 4658 ( FALSE !== strpos( $paramValue, ';' )) || 4659 ( FALSE !== strpos( $paramValue, ',' ))) 4660 $paramValue = '"'.$paramValue.'"'; 4469 4661 if( ctype_digit( (string) $paramKey )) { 4470 4662 $xparams[] = $paramValue; … … 4497 4689 if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys )) 4498 4690 $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE']; 4499 if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) 4691 if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) { 4500 4692 $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID']; 4693 } 4501 4694 if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys )) 4502 4695 $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE']; … … 4504 4697 $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE']; 4505 4698 if( isset( $params['CN'] ) && $CNattrKey ) { 4506 $attr1 = $this->intAttrDelimiter.'CN= "'.$params['CN'].'"';4699 $attr1 = $this->intAttrDelimiter.'CN='.$params['CN']; 4507 4700 $CNattrExist = TRUE; 4508 4701 } 4509 if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) 4510 $attr1 .= $this->intAttrDelimiter.'DIR="'.$params['DIR'].'"'; 4702 if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) { 4703 $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ? '' : '"'; 4704 $attr1 .= $this->intAttrDelimiter.'DIR='.$delim.$params['DIR'].$delim; 4705 } 4511 4706 if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys )) 4512 $attr1 .= $this->intAttrDelimiter.'SENT-BY="'.$params['SENT-BY'].'"'; 4513 if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) 4514 $attr1 .= $this->intAttrDelimiter.'ALTREP="'.$params['ALTREP'].'"'; 4707 $attr1 .= $this->intAttrDelimiter.'SENT-BY='.$params['SENT-BY']; 4708 if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) { 4709 $delim = ( FALSE !== strpos( $params['ALTREP'], '"' )) ? '' : '"'; 4710 $attr1 .= $this->intAttrDelimiter.'ALTREP='.$delim.$params['ALTREP'].$delim; 4711 } 4515 4712 if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) { 4516 4713 $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE']; … … 4528 4725 * 4529 4726 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 4530 * @since 2. 4.8 - 2008-10-224727 * @since 2.14.1 - 2012-10-06 4531 4728 * @param array $recurlabel 4532 4729 * @param array $recurdata … … 4549 4746 } 4550 4747 case 'UNTIL': { 4551 $ content2 .= ";UNTIL=";4552 $content2 .= iCalUtilityFunctions::_format_date_time( $rulevalue);4748 $parno = ( isset( $rulevalue['hour'] )) ? 7 : 3; 4749 $content2 .= ';UNTIL='.iCalUtilityFunctions::_date2strdate( $rulevalue, $parno ); 4553 4750 break; 4554 4751 } … … 4592 4789 } 4593 4790 $content2 .= $content21.$content22; 4594 ++$bydaycnt;4791 $bydaycnt++; 4595 4792 } 4596 4793 else { … … 4600 4797 else { 4601 4798 $content22 .= $valuePart; 4602 ++$bydaycnt;4799 $bydaycnt++; 4603 4800 } 4604 4801 $content2 .= $content21.$content22; … … 4691 4888 $output = array(); 4692 4889 if( !in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) { 4693 if( empty( $this->uid['value'] )) $this->_makeuid();4890 if( empty( $this->uid['value'] )) $this->_makeuid(); 4694 4891 $output['UID'] = 1; 4695 } 4696 if( !empty( $this->dtstamp )) $output['DTSTAMP'] = 1; 4892 if( empty( $this->dtstamp )) $this->_makeDtstamp(); 4893 $output['DTSTAMP'] = 1; 4894 } 4697 4895 if( !empty( $this->summary )) $output['SUMMARY'] = 1; 4698 4896 if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description ); … … 4740 4938 return $output; 4741 4939 break; 4940 case 'SETPROPERTYNAMES': 4941 return array_keys( $this->getConfig( 'propinfo' )); 4942 break; 4742 4943 case 'TZID': 4743 4944 return $this->dtzid; … … 4754 4955 * 4755 4956 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 4756 * @since 2. 9.6 - 2011-05-144957 * @since 2.10.18 - 2011-10-28 4757 4958 * @param mixed $config 4758 4959 * @param string $value … … 4762 4963 function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) { 4763 4964 if( is_array( $config )) { 4965 $ak = array_keys( $config ); 4966 foreach( $ak as $k ) { 4967 if( 'NEWLINECHAR' == strtoupper( $k )) { 4968 if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] )) 4969 return FALSE; 4970 unset( $config[$k] ); 4971 break; 4972 } 4973 } 4764 4974 foreach( $config as $cKey => $cValue ) { 4765 4975 if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate )) … … 4793 5003 case 'NEWLINECHAR': 4794 5004 $this->nl = $value; 5005 $this->_createFormat(); 4795 5006 $subcfg = array( 'NL' => $value ); 4796 5007 $res = TRUE; … … 5076 5287 if( $propix != $xpropno ) 5077 5288 $reduced[$xpropkey] = $xpropvalue; 5078 ++$xpropno;5289 $xpropno++; 5079 5290 } 5080 5291 } … … 5115 5326 * 5116 5327 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 5117 * @since 2.1 0.1 - 2011-07-165328 * @since 2.12.4 - 2012-04-22 5118 5329 * @param string $propName, optional 5119 5330 * @param int @propix, optional, if specific property is wanted in case of multiply occurences … … 5123 5334 */ 5124 5335 function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) { 5336 if( 'GEOLOCATION' == strtoupper( $propName )) { 5337 $content = $this->getProperty( 'LOCATION' ); 5338 $content = ( !empty( $content )) ? $content.' ' : ''; 5339 if(( FALSE === ( $geo = $this->getProperty( 'GEO' ))) || empty( $geo )) 5340 return FALSE; 5341 if( 0.0 < $geo['latitude'] ) 5342 $sign = '+'; 5343 else 5344 $sign = ( 0.0 > $geo['latitude'] ) ? '-' : ''; 5345 $content .= $sign.sprintf( "%09.6f", abs( $geo['latitude'] )); // sprintf && lpad && float && sign !"#€%&/( 5346 $content = rtrim( rtrim( $content, '0' ), '.' ); 5347 if( 0.0 < $geo['longitude'] ) 5348 $sign = '+'; 5349 else 5350 $sign = ( 0.0 > $geo['longitude'] ) ? '-' : ''; 5351 return $content.$sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/'; // sprintf && lpad && float && sign !"#€%&/( 5352 } 5125 5353 if( $this->_notExistProp( $propName )) return FALSE; 5126 5354 $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; … … 5138 5366 $ak = ( is_array( $this->attach )) ? array_keys( $this->attach ) : array(); 5139 5367 while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( $ak ))) 5140 ++$propix; 5368 $propix++; 5369 $this->propix[$propName] = $propix; 5141 5370 if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5142 5371 return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value']; … … 5145 5374 $ak = ( is_array( $this->attendee )) ? array_keys( $this->attendee ) : array(); 5146 5375 while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( $ak ))) 5147 ++$propix; 5376 $propix++; 5377 $this->propix[$propName] = $propix; 5148 5378 if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5149 5379 return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value']; … … 5152 5382 $ak = ( is_array( $this->categories )) ? array_keys( $this->categories ) : array(); 5153 5383 while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( $ak ))) 5154 ++$propix; 5384 $propix++; 5385 $this->propix[$propName] = $propix; 5155 5386 if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5156 5387 return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value']; … … 5162 5393 $ak = ( is_array( $this->comment )) ? array_keys( $this->comment ) : array(); 5163 5394 while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( $ak ))) 5164 ++$propix; 5395 $propix++; 5396 $this->propix[$propName] = $propix; 5165 5397 if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5166 5398 return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value']; … … 5172 5404 $ak = ( is_array( $this->contact )) ? array_keys( $this->contact ) : array(); 5173 5405 while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( $ak ))) 5174 ++$propix; 5406 $propix++; 5407 $this->propix[$propName] = $propix; 5175 5408 if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5176 5409 return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value']; … … 5182 5415 $ak = ( is_array( $this->description )) ? array_keys( $this->description ) : array(); 5183 5416 while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( $ak ))) 5184 ++$propix; 5417 $propix++; 5418 $this->propix[$propName] = $propix; 5185 5419 if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5186 5420 return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value']; … … 5210 5444 $ak = ( is_array( $this->exdate )) ? array_keys( $this->exdate ) : array(); 5211 5445 while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( $ak ))) 5212 ++$propix; 5446 $propix++; 5447 $this->propix[$propName] = $propix; 5213 5448 if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5214 5449 return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value']; … … 5217 5452 $ak = ( is_array( $this->exrule )) ? array_keys( $this->exrule ) : array(); 5218 5453 while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( $ak ))) 5219 ++$propix; 5454 $propix++; 5455 $this->propix[$propName] = $propix; 5220 5456 if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5221 5457 return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value']; … … 5224 5460 $ak = ( is_array( $this->freebusy )) ? array_keys( $this->freebusy ) : array(); 5225 5461 while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( $ak ))) 5226 ++$propix; 5462 $propix++; 5463 $this->propix[$propName] = $propix; 5227 5464 if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5228 5465 return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value']; … … 5250 5487 while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( $ak ))) 5251 5488 $propix++; 5489 $this->propix[$propName] = $propix; 5252 5490 if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5253 5491 return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value']; … … 5260 5498 while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( $ak ))) 5261 5499 $propix++; 5500 $this->propix[$propName] = $propix; 5262 5501 if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5263 5502 return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value']; … … 5269 5508 $ak = ( is_array( $this->requeststatus )) ? array_keys( $this->requeststatus ) : array(); 5270 5509 while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( $ak ))) 5271 ++$propix; 5510 $propix++; 5511 $this->propix[$propName] = $propix; 5272 5512 if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5273 5513 return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value']; … … 5276 5516 $ak = ( is_array( $this->resources )) ? array_keys( $this->resources ) : array(); 5277 5517 while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( $ak ))) 5278 ++$propix; 5518 $propix++; 5519 $this->propix[$propName] = $propix; 5279 5520 if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5280 5521 return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value']; … … 5283 5524 $ak = ( is_array( $this->rrule )) ? array_keys( $this->rrule ) : array(); 5284 5525 while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( $ak ))) 5285 ++$propix; 5526 $propix++; 5527 $this->propix[$propName] = $propix; 5286 5528 if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5287 5529 return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value']; … … 5308 5550 $ak = ( is_array( $this->tzname )) ? array_keys( $this->tzname ) : array(); 5309 5551 while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( $ak ))) 5310 ++$propix; 5552 $propix++; 5553 $this->propix[$propName] = $propix; 5311 5554 if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } 5312 5555 return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value']; … … 5345 5588 : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); 5346 5589 else 5347 ++$xpropno;5590 $xpropno++; 5348 5591 } 5349 5592 return FALSE; // not found ?? … … 5353 5596 } 5354 5597 /** 5355 * returns calendar property unique values for ' CATEGORIES', 'RESOURCES' or 'ATTENDEE' and each number of ocurrence5356 * 5357 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 5358 * @since 2. 8.8 - 2011-04-135598 * returns calendar property unique values for 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO' or 'RESOURCES' and for each, number of occurrence 5599 * 5600 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 5601 * @since 2.13.4 - 2012-08-07 5359 5602 * @param string $propName 5360 5603 * @param array $output, incremented result array 5361 5604 */ 5362 5605 function _getProperties( $propName, & $output ) { 5363 if( !in_array( strtoupper( $propName ), array( 'ATTENDEE', 'CATEGORIES', 'RESOURCES' ))) 5364 return output; 5606 if( empty( $output )) 5607 $output = array(); 5608 if( !in_array( strtoupper( $propName ), array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' ))) 5609 return $output; 5365 5610 while( FALSE !== ( $content = $this->getProperty( $propName ))) { 5611 if( empty( $content )) 5612 continue; 5366 5613 if( is_array( $content )) { 5367 5614 foreach( $content as $part ) { … … 5386 5633 } 5387 5634 } 5388 } 5635 } // end if( is_array( $content )) 5389 5636 elseif( FALSE !== strpos( $content, ',' )) { 5390 5637 $content = explode( ',', $content ); … … 5398 5645 } 5399 5646 } 5400 } 5647 } // end elseif( FALSE !== strpos( $content, ',' )) 5401 5648 else { 5402 5649 $content = trim( $content ); … … 5410 5657 } 5411 5658 ksort( $output ); 5412 return $output;5413 5659 } 5414 5660 /** … … 5430 5676 return FALSE; 5431 5677 $arglist[0] = strtoupper( $arglist[0] ); 5432 for( $argix=$numargs; $argix < 12; ++$argix) {5678 for( $argix=$numargs; $argix < 12; $argix++ ) { 5433 5679 if( !isset( $arglist[$argix] )) 5434 5680 $arglist[$argix] = null; … … 5531 5777 * 5532 5778 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 5533 * @since 2.1 0.2 - 2011-07-175779 * @since 2.15.10 - 2012-10-28 5534 5780 * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of strings 5535 5781 * @return bool FALSE if error occurs during parsing … … 5537 5783 */ 5538 5784 function parse( $unparsedtext=null ) { 5785 $nl = $this->getConfig( 'nl' ); 5539 5786 if( !empty( $unparsedtext )) { 5540 $nl = $this->getConfig( 'nl' );5541 5787 if( is_array( $unparsedtext )) 5542 5788 $unparsedtext = implode( '\n'.$nl, $unparsedtext ); 5543 /* fix line folding */ 5544 $eolchars = array( "\r\n", "\n\r", "\n", "\r" ); // check all line endings 5545 $EOLmark = FALSE; 5546 foreach( $eolchars as $eolchar ) { 5547 if( !$EOLmark && ( FALSE !== strpos( $unparsedtext, $eolchar ))) { 5548 $unparsedtext = str_replace( $eolchar." ", '', $unparsedtext ); 5549 $unparsedtext = str_replace( $eolchar."\t", '', $unparsedtext ); 5550 if( $eolchar != $nl ) 5551 $unparsedtext = str_replace( $eolchar, $nl, $unparsedtext ); 5552 $EOLmark = TRUE; 5553 } 5554 } 5555 $tmp = explode( $nl, $unparsedtext ); 5556 $unparsedtext = array(); 5557 foreach( $tmp as $tmpr ) 5558 if( !empty( $tmpr )) 5559 $unparsedtext[] = $tmpr; 5789 $unparsedtext = explode( $nl, iCalUtilityFunctions::convEolChar( $unparsedtext, $nl )); 5560 5790 } 5561 5791 elseif( !isset( $this->unparsed )) … … 5563 5793 else 5564 5794 $unparsedtext = $this->unparsed; 5795 /* skip leading (empty/invalid) lines */ 5796 foreach( $unparsedtext as $lix => $line ) { 5797 $tst = trim( $line ); 5798 if(( '\n' == $tst ) || empty( $tst )) 5799 unset( $unparsedtext[$lix] ); 5800 else 5801 break; 5802 } 5565 5803 $this->unparsed = array(); 5566 $comp = & $this; 5567 $config = $this->getConfig(); 5568 foreach ( $unparsedtext as $line ) { 5569 // echo $comp->objName.": $line<br />"; // test ### 5570 if( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VA', 'END:DA' ))) 5571 $this->components[] = $comp->copy(); 5572 elseif( 'END:ST' == strtoupper( substr( $line, 0, 6 ))) 5804 $comp = & $this; 5805 $config = $this->getConfig(); 5806 $compsync = $subsync = 0; 5807 foreach ( $unparsedtext as $lix => $line ) { 5808 if( 'END:VALARM' == strtoupper( substr( $line, 0, 10 ))) { 5809 if( 1 != $subsync ) return FALSE; 5810 $this->components[] = $comp->copy(); 5811 $subsync--; 5812 } 5813 elseif( 'END:DAYLIGHT' == strtoupper( substr( $line, 0, 12 ))) { 5814 if( 1 != $subsync ) return FALSE; 5815 $this->components[] = $comp->copy(); 5816 $subsync--; 5817 } 5818 elseif( 'END:STANDARD' == strtoupper( substr( $line, 0, 12 ))) { 5819 if( 1 != $subsync ) return FALSE; 5573 5820 array_unshift( $this->components, $comp->copy()); 5574 elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) 5575 break; 5576 elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) 5821 $subsync--; 5822 } 5823 elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) { // end:<component> 5824 if( 1 != $compsync ) return FALSE; 5825 if( 0 < $subsync ) 5826 $this->components[] = $comp->copy(); 5827 $compsync--; 5828 break; /* skip trailing empty lines */ 5829 } 5830 elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) { 5577 5831 $comp = new valarm( $config); 5578 elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) 5832 $subsync++; 5833 } 5834 elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) { 5579 5835 $comp = new vtimezone( 'standard', $config ); 5580 elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) 5836 $subsync++; 5837 } 5838 elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) { 5581 5839 $comp = new vtimezone( 'daylight', $config ); 5582 elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) 5583 continue; 5584 else { 5585 $comp->unparsed[] = $line; 5586 // echo $comp->objName.": $line<br />\n"; // test ### 5587 } 5588 } 5840 $subsync++; 5841 } 5842 elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) // begin:<component> 5843 $compsync++; 5844 else 5845 $comp->unparsed[] = $line; 5846 } 5847 if( 0 < $subsync ) 5848 $this->components[] = $comp->copy(); 5589 5849 unset( $config ); 5590 // echo $this->objName.'<br />'.var_export( $this->unparsed, TRUE )."<br />\n"; // test ###5591 5850 /* concatenate property values spread over several lines */ 5592 5851 $lastix = -1; … … 5600 5859 , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' ); 5601 5860 $proprows = array(); 5602 foreach( $this->unparsed as $line ) { 5603 $newProp = FALSE; 5604 foreach ( $propnames as $propname ) { 5605 if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) { 5606 $newProp = TRUE; 5607 break; 5608 } 5609 } 5610 if( $newProp ) { 5611 if( -1 < $lastix ) 5612 $proprows[$lastix] = $proprows[$lastix]; 5613 $newProp = FALSE; 5614 ++$lastix; 5615 $proprows[$lastix] = $line; 5616 } 5617 else 5618 $proprows[$lastix] .= '!"#€%&/()=?'.$line; 5861 for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines 5862 $line = rtrim( $this->unparsed[$i], $nl ); 5863 while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} )) 5864 $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl ); 5865 $proprows[] = $line; 5619 5866 } 5620 5867 /* parse each property 'line' */ 5868 $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' ); 5869 $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ); 5870 $paramProto4 = array( 'crid:', 'news:', 'pres:' ); 5621 5871 foreach( $proprows as $line ) { 5622 $line = str_replace( '!"#€%&/()=? ', '', $line );5623 $line = str_replace( '!"#€%&/()=?', '', $line );5624 5872 if( '\n' == substr( $line, -2 )) 5625 $line = substr( $line, 0, strlen( $line ) - 2 ); 5626 /* get propname, (problem with x-properties, otherwise in previous loop) */ 5627 $cix = $propname = null; 5628 for( $cix=0, $clen = strlen( $line ); $cix < $clen; ++$cix ) { 5873 $line = substr( $line, 0, -2 ); 5874 /* get propname */ 5875 $propname = null; 5876 $cix = 0; 5877 while( isset( $line[$cix] )) { 5629 5878 if( in_array( $line[$cix], array( ':', ';' ))) 5630 5879 break; 5631 else {5880 else 5632 5881 $propname .= $line[$cix]; 5633 }5882 $cix++; 5634 5883 } 5635 5884 if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) { … … 5637 5886 $propname = 'X-'; 5638 5887 } 5888 if( !in_array( strtolower( $propname ), $propnames )) // skip non standard property names 5889 continue; 5639 5890 /* rest of the line is opt.params and value */ 5640 5891 $line = substr( $line, $cix ); 5641 5892 /* separate attributes from value */ 5642 $attr = array(); 5643 $attrix = -1; 5644 $clen = strlen( $line ); 5645 for( $cix=0; $cix < $clen; ++$cix ) { 5646 if(( ':' == $line[$cix] ) && 5647 ( '://' != substr( $line, $cix, 3 )) && 5648 ( !in_array( strtolower( substr( $line, $cix - 3, 4 )), array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ))) && 5649 ( !in_array( strtolower( substr( $line, $cix - 4, 5 )), array( 'crid:', 'news:', 'pres:' ))) && 5650 ( 'mailto:' != strtolower( substr( $line, $cix - 6, 7 )))) { 5893 $attr = array(); 5894 $attrix = -1; 5895 $clen = strlen( $line ); 5896 $WithinQuotes = FALSE; 5897 $cix = 0; 5898 while( FALSE !== substr( $line, $cix, 1 )) { 5899 if( ( ':' == $line[$cix] ) && 5900 ( substr( $line,$cix, 3 ) != '://' ) && 5901 ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) && 5902 ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) && 5903 ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) && 5904 ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) && 5905 !$WithinQuotes ) { 5651 5906 $attrEnd = TRUE; 5652 5907 if(( $cix < ( $clen - 4 )) && … … 5660 5915 } 5661 5916 if( $attrEnd) { 5662 $line = substr( $line, $cix + 1);5917 $line = substr( $line, ( $cix + 1 )); 5663 5918 break; 5664 5919 } 5665 } 5920 $cix++; 5921 } 5922 if( '"' == $line[$cix] ) 5923 $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE; 5666 5924 if( ';' == $line[$cix] ) 5667 5925 $attr[++$attrix] = null; 5668 5926 else 5669 5927 $attr[$attrix] .= $line[$cix]; 5928 $cix++; 5670 5929 } 5671 5930 /* make attributes in array format */ … … 5682 5941 case 'ATTENDEE': 5683 5942 foreach( $propattr as $pix => $attr ) { 5943 if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ))) 5944 continue; 5684 5945 $attr2 = explode( ',', $attr ); 5685 5946 if( 1 < count( $attr2 )) … … 5688 5949 $this->setProperty( $propname, $line, $propattr ); 5689 5950 break; 5951 case 'X-': 5952 $propname = ( isset( $propname2 )) ? $propname2 : $propname; 5953 unset( $propname2 ); 5690 5954 case 'CATEGORIES': 5691 5955 case 'RESOURCES': 5692 5956 if( FALSE !== strpos( $line, ',' )) { 5693 $content = explode( ',', $line ); 5694 $clen = count( $content ); 5695 for( $cix = 0; $cix < $clen; ++$cix ) { 5696 if( "\\" == substr($content[$cix], -1)) { 5697 $content[$cix] .= ','.$content[$cix + 1]; 5698 unset($content[$cix + 1]); 5699 ++$cix; 5957 $content = array( 0 => '' ); 5958 $cix = $lix = 0; 5959 while( FALSE !== substr( $line, $lix, 1 )) { 5960 if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { 5961 $cix++; 5962 $content[$cix] = ''; 5700 5963 } 5964 else 5965 $content[$cix] .= $line[$lix]; 5966 $lix++; 5701 5967 } 5702 5968 if( 1 < count( $content )) { … … 5710 5976 $line = reset( $content ); 5711 5977 } 5712 case 'X-':5713 $propname = ( isset( $propname2 )) ? $propname2 : $propname;5714 5978 case 'COMMENT': 5715 5979 case 'CONTACT': … … 5720 5984 $propattr = null; 5721 5985 $this->setProperty( $propname, calendarComponent::_strunrep( $line ), $propattr ); 5722 unset( $propname2 );5723 5986 break; 5724 5987 case 'REQUEST-STATUS': … … 5856 6119 * 5857 6120 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 5858 * @since 2. 8.8 - 2011-03-156121 * @since 2.15.4 - 2012-10-18 5859 6122 * @return object 5860 6123 */ 5861 6124 function copy() { 5862 $serialized_contents = serialize( $this ); 5863 $copy = unserialize( $serialized_contents ); 5864 return $copy; 6125 return unserialize( serialize( $this )); 5865 6126 } 5866 6127 /*********************************************************************************/ … … 5898 6159 return TRUE; 5899 6160 } 5900 ++$cix2dC;6161 $cix2dC++; 5901 6162 } 5902 6163 elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { … … 5949 6210 if( $index == $cix2gC ) 5950 6211 return $component->copy(); 5951 ++$cix2gC;6212 $cix2gC++; 5952 6213 } 5953 6214 elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' ))) … … 6042 6303 return TRUE; 6043 6304 } 6044 ++$cix2sC;6305 $cix2sC++; 6045 6306 } 6046 6307 elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace … … 6062 6323 * 6063 6324 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 6064 * @since 2.6.27 - 2010-12-12 6325 * @since 2.11.20 - 2012-02-06 6326 * @param array $xcaldecl 6065 6327 * @return string 6066 6328 */ 6067 6329 function createSubComponent() { 6068 6330 $output = null; 6331 if( 'vtimezone' == $this->objName ) { // sort subComponents, first standard, then daylight, in dtstart order 6332 $stdarr = $dlarr = array(); 6333 foreach( $this->components as $component ) { 6334 if( empty( $component )) 6335 continue; 6336 $dt = $component->getProperty( 'dtstart' ); 6337 $key = sprintf( '%04d%02d%02d%02d%02d%02d000', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] ); 6338 if( 'standard' == $component->objName ) { 6339 while( isset( $stdarr[$key] )) 6340 $key += 1; 6341 $stdarr[$key] = $component->copy(); 6342 } 6343 elseif( 'daylight' == $component->objName ) { 6344 while( isset( $dlarr[$key] )) 6345 $key += 1; 6346 $dlarr[$key] = $component->copy(); 6347 } 6348 } // end foreach( $this->components as $component ) 6349 $this->components = array(); 6350 ksort( $stdarr, SORT_NUMERIC ); 6351 foreach( $stdarr as $std ) 6352 $this->components[] = $std->copy(); 6353 unset( $stdarr ); 6354 ksort( $dlarr, SORT_NUMERIC ); 6355 foreach( $dlarr as $dl ) 6356 $this->components[] = $dl->copy(); 6357 unset( $dlarr ); 6358 } // end if( 'vtimezone' == $this->objName ) 6069 6359 foreach( $this->components as $component ) { 6070 if( empty( $component )) continue;6071 6360 $component->setConfig( $this->getConfig(), FALSE, TRUE ); 6072 6361 $output .= $component->createComponent( $this->xcaldecl ); … … 6090 6379 * the reserved expression "\n" in the arg $string could be broken up by the 6091 6380 * folding of lines, causing ambiguity in the return string. 6092 * Fix uses var $breakAtChar=75 and breaks the line at $breakAtChar-1 if need be. 6093 * 6094 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 6095 * @since 2.6.13 - 2010-12-06 6381 * 6382 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 6383 * @since 2.12.17 - 2012-07-15 6096 6384 * @param string $value 6097 6385 * @return string 6098 6386 */ 6099 6387 function _size75( $string ) { 6100 $tmp = $string; 6101 $string = null; 6102 /* if PHP is config with mb_string.. . */ 6103 if( defined( MB_OVERLOAD_STRING )) { 6104 $strlen = mb_strlen( $tmp ); 6105 while( $strlen > 75 ) { 6106 $breakAtChar = 75; 6107 if( substr( $tmp, ( $breakAtChar - 1 ), strlen( '\n' )) == '\n' ) 6108 $breakAtChar = $breakAtChar - 1; 6109 $string .= mb_substr( $tmp, 0, $breakAtChar ); 6110 if( '\n' == substr( $string, ( 0 - strlen( '\n' )))) 6111 $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n' ))); 6112 if( $this->nl != mb_substr( $string, ( 0 - strlen( $this->nl )))) 6113 $string .= $this->nl; 6114 $tmp = ' '.mb_substr( $tmp, $breakAtChar ); 6115 $strlen = mb_strlen( $tmp ); 6116 } // end while 6117 if( 0 < $strlen ) { 6118 $string .= $tmp; // the rest 6119 if( '\n' == substr( $string, ( 0 - strlen( '\n' )))) 6120 $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n' ))); 6121 if( $this->nl != mb_substr( $string, ( 0 - strlen( $this->nl )))) 6122 $string .= $this->nl; 6123 } 6124 return $string; 6125 } 6126 /* if PHP is not config with mb_string.. . */ 6127 $eolcharlen = strlen( '\n' ); 6388 $tmp = $string; 6389 $string = ''; 6390 $cCnt = $x = 0; 6128 6391 while( TRUE ) { 6129 $bytecnt = strlen( $tmp ); 6130 $charCnt = $ix = 0; 6131 for( $ix = 0; $ix < $bytecnt; ++$ix ) { 6132 if(( 73 < $charCnt ) && ( '\n' == substr( $tmp, $ix, $eolcharlen ))) { 6133 $ix += $eolcharlen; 6134 break; // break when '\n' and eol 6135 } 6136 elseif( 74 < $charCnt ) 6137 break; // always break for-loop here 6138 else { 6139 $byte = ord( $tmp[$ix] ); 6140 if ($byte <= 127) { // add a one byte character 6141 $string .= substr( $tmp, $ix, 1 ); 6142 $charCnt += 1; 6392 if( !isset( $tmp[$x] )) { 6393 $string .= $this->nl; // loop breakes here 6394 break; 6395 } 6396 elseif(( 74 <= $cCnt ) && ( '\\' == $tmp[$x] ) && ( 'n' == $tmp[$x+1] )) { 6397 $string .= $this->nl.' \n'; // don't break lines inside '\n' 6398 $x += 2; 6399 if( !isset( $tmp[$x] )) { 6400 $string .= $this->nl; 6401 break; 6402 } 6403 $cCnt = 3; 6404 } 6405 elseif( 75 <= $cCnt ) { 6406 $string .= $this->nl.' '; 6407 $cCnt = 1; 6408 } 6409 $byte = ord( $tmp[$x] ); 6410 $string .= $tmp[$x]; 6411 switch( TRUE ) { // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 6412 case(( $byte >= 0x20 ) && ( $byte <= 0x7F )): // characters U-00000000 - U-0000007F (same as ASCII) 6413 $cCnt += 1; 6414 break; // add a one byte character 6415 case(( $byte & 0xE0) == 0xC0 ): // characters U-00000080 - U-000007FF, mask 110XXXXX 6416 if( isset( $tmp[$x+1] )) { 6417 $cCnt += 1; 6418 $string .= $tmp[$x+1]; 6419 $x += 1; // add a two bytes character 6143 6420 } 6144 else if ($byte >= 194 && $byte <= 223) { // start byte in two byte character 6145 $string .= substr( $tmp, $ix, 2 ); // add a two bytes character 6146 $charCnt += 1; 6421 break; 6422 case(( $byte & 0xF0 ) == 0xE0 ): // characters U-00000800 - U-0000FFFF, mask 1110XXXX 6423 if( isset( $tmp[$x+2] )) { 6424 $cCnt += 1; 6425 $string .= $tmp[$x+1].$tmp[$x+2]; 6426 $x += 2; // add a three bytes character 6147 6427 } 6148 else if ($byte >= 224 && $byte <= 239) { // start byte in three bytes character 6149 $string .= substr( $tmp, $ix, 3 ); // add a three bytes character 6150 $charCnt += 1; 6428 break; 6429 case(( $byte & 0xF8 ) == 0xF0 ): // characters U-00010000 - U-001FFFFF, mask 11110XXX 6430 if( isset( $tmp[$x+3] )) { 6431 $cCnt += 1; 6432 $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3]; 6433 $x += 3; // add a four bytes character 6151 6434 } 6152 else if ($byte >= 240 && $byte <= 244) { // start byte in four bytes character 6153 $string .= substr( $tmp, $ix, 4 ); // add a four bytes character 6154 $charCnt += 1; 6435 break; 6436 case(( $byte & 0xFC ) == 0xF8 ): // characters U-00200000 - U-03FFFFFF, mask 111110XX 6437 if( isset( $tmp[$x+4] )) { 6438 $cCnt += 1; 6439 $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4]; 6440 $x += 4; // add a five bytes character 6155 6441 } 6156 } 6157 } // end for 6158 if( '\n' == substr( $string, ( 0 - strlen( '\n' )))) 6159 $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n' ))); 6160 if( $this->nl != substr( $string, ( 0 - strlen( $this->nl )))) 6161 $string .= $this->nl; 6162 $tmp = substr( $tmp, $ix ); 6163 if( empty( $tmp )) 6164 break; // while-loop breakes here 6165 else 6166 $tmp = ' '.$tmp; 6167 } // end while 6168 if( !empty( $tmp )) { 6169 if( '\n' == substr( $string, ( 0 - strlen( '\n' )))) 6170 $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n' ))).$this->nl; 6171 if( $this->nl != substr( $string, ( 0 - strlen( $this->nl )))) 6172 $string .= $this->nl; 6173 } 6442 break; 6443 case(( $byte & 0xFE ) == 0xFC ): // characters U-04000000 - U-7FFFFFFF, mask 1111110X 6444 if( isset( $tmp[$x+5] )) { 6445 $cCnt += 1; 6446 $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4].$tmp[$x+5]; 6447 $x += 5; // add a six bytes character 6448 } 6449 default: // add any other byte without counting up $cCnt 6450 break; 6451 } // end switch( TRUE ) 6452 $x += 1; // next 'byte' to test 6453 } // end while( TRUE ) { 6174 6454 return $string; 6175 6455 } … … 6178 6458 * 6179 6459 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 6180 * @since 2. 6.15 - 2010-09-246460 * @since 2.12.16 - 2012-07-16 6181 6461 * @param string $string 6182 6462 * @return string … … 6191 6471 $pos = 0; 6192 6472 $specChars = array( 'n', 'N', 'r', ',', ';' ); 6193 while( $pos <= strlen( $string )) { 6194 $pos = strpos( $string, "\\", $pos ); 6195 if( FALSE === $pos ) 6473 while( isset( $string[$pos] )) { 6474 if( FALSE === ( $pos = strpos( $string, "\\", $pos ))) 6196 6475 break; 6197 6476 if( !in_array( substr( $string, $pos, 1 ), $specChars )) { … … 6207 6486 if( FALSE !== strpos( $string, ';' )) 6208 6487 $string = str_replace(';', '\;', $string); 6209 6210 6488 if( FALSE !== strpos( $string, "\r\n" )) 6211 6489 $string = str_replace( "\r\n", '\n', $string); 6212 6490 elseif( FALSE !== strpos( $string, "\r" )) 6213 6491 $string = str_replace( "\r", '\n', $string); 6214 6215 6492 elseif( FALSE !== strpos( $string, "\n" )) 6216 6493 $string = str_replace( "\n", '\n', $string); 6217 6218 6494 if( FALSE !== strpos( $string, '\N' )) 6219 6495 $string = str_replace( '\N', '\n', $string); … … 6338 6614 * 6339 6615 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 6340 * @since 2. 5.1 - 2008-11-076616 * @since 2.10.16 - 2011-10-28 6341 6617 * @param array $xcaldecl 6342 6618 * @return string … … 6810 7086 $component .= $this->createXprop(); 6811 7087 $component .= $this->componentEnd1.$objectname.$this->componentEnd2; 7088 if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { 7089 foreach( $this->xcaldecl as $localxcaldecl ) 7090 $xcaldecl[] = $localxcaldecl; 7091 } 6812 7092 return $component; 6813 7093 } … … 6911 7191 } 6912 7192 } 7193 /*********************************************************************************/ 7194 /*********************************************************************************/ 7195 /** 7196 * moving all utility (static) functions to a utility class 7197 * 20111223 - move iCalUtilityFunctions class to the end of the iCalcreator class file 7198 * 7199 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7200 * @since 2.10.1 - 2011-07-16 7201 * 7202 */ 7203 class iCalUtilityFunctions { 7204 // Store the single instance of iCalUtilityFunctions 7205 private static $m_pInstance; 7206 7207 // Private constructor to limit object instantiation to within the class 7208 private function __construct() { 7209 $m_pInstance = FALSE; 7210 } 7211 7212 // Getter method for creating/returning the single instance of this class 7213 public static function getInstance() { 7214 if (!self::$m_pInstance) 7215 self::$m_pInstance = new iCalUtilityFunctions(); 7216 7217 return self::$m_pInstance; 7218 } 7219 /** 7220 * ensures internal date-time/date format (keyed array) for an input date-time/date array (keyed or unkeyed) 7221 * 7222 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7223 * @since 2.14.1 - 2012-09-27 7224 * @param array $datetime 7225 * @param int $parno optional, default FALSE 7226 * @return array 7227 */ 7228 public static function _date_time_array( $datetime, $parno=FALSE ) { 7229 return iCalUtilityFunctions::_chkDateArr( $datetime, $parno ); 7230 } 7231 public static function _chkDateArr( $datetime, $parno=FALSE ) { 7232 $output = array(); 7233 foreach( $datetime as $dateKey => $datePart ) { 7234 switch ( $dateKey ) { 7235 case '0': case 'year': $output['year'] = $datePart; break; 7236 case '1': case 'month': $output['month'] = $datePart; break; 7237 case '2': case 'day': $output['day'] = $datePart; break; 7238 } 7239 if( 3 != $parno ) { 7240 switch ( $dateKey ) { 7241 case '0': 7242 case '1': 7243 case '2': break; 7244 case '3': case 'hour': $output['hour'] = $datePart; break; 7245 case '4': case 'min' : $output['min'] = $datePart; break; 7246 case '5': case 'sec' : $output['sec'] = $datePart; break; 7247 case '6': case 'tz' : $output['tz'] = $datePart; break; 7248 } 7249 } 7250 } 7251 if( 3 != $parno ) { 7252 if( !isset( $output['hour'] )) $output['hour'] = 0; 7253 if( !isset( $output['min'] )) $output['min'] = 0; 7254 if( !isset( $output['sec'] )) $output['sec'] = 0; 7255 if( isset( $output['tz'] ) && 7256 (( '+0000' == $output['tz'] ) || ( '-0000' == $output['tz'] ) || ( '+000000' == $output['tz'] ) || ( '-000000' == $output['tz'] ))) 7257 $output['tz'] = 'Z'; 7258 } 7259 return $output; 7260 } 7261 /** 7262 * check date(-time) and params arrays for an opt. timezone and if it is a DATE-TIME or DATE (updates $parno and params) 7263 * 7264 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7265 * @since 2.10.30 - 2012-01-16 7266 * @param array $date, date to check 7267 * @param int $parno, no of date parts (i.e. year, month.. .) 7268 * @param array $params, property parameters 7269 * @return void 7270 */ 7271 public static function _chkdatecfg( $theDate, & $parno, & $params ) { 7272 if( isset( $params['TZID'] )) 7273 $parno = 6; 7274 elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )) 7275 $parno = 3; 7276 else { 7277 if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] )) 7278 $parno = 7; 7279 if( is_array( $theDate )) { 7280 if( isset( $theDate['timestamp'] )) 7281 $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null; 7282 else 7283 $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null; 7284 if( !empty( $tzid )) { 7285 $parno = 7; 7286 if( !iCalUtilityFunctions::_isOffset( $tzid )) 7287 $params['TZID'] = $tzid; // save only timezone 7288 } 7289 elseif( !$parno && ( 3 == count( $theDate )) && 7290 ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))) 7291 $parno = 3; 7292 else 7293 $parno = 6; 7294 } 7295 else { // string 7296 $date = trim( $theDate ); 7297 if( 'Z' == substr( $date, -1 )) 7298 $parno = 7; // UTC DATE-TIME 7299 elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) && 7300 ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' )))) 7301 $parno = 3; // DATE 7302 $date = iCalUtilityFunctions::_strdate2date( $date, $parno ); 7303 unset( $date['unparsedtext'] ); 7304 if( !empty( $date['tz'] )) { 7305 $parno = 7; 7306 if( !iCalUtilityFunctions::_isOffset( $date['tz'] )) 7307 $params['TZID'] = $date['tz']; // save only timezone 7308 } 7309 elseif( empty( $parno )) 7310 $parno = 6; 7311 } 7312 if( isset( $params['TZID'] )) 7313 $parno = 6; 7314 } 7315 } 7316 /** 7317 * byte oriented line folding fix 7318 * 7319 * remove any line-endings that may include spaces or tabs 7320 * and convert all line endings (iCal default '\r\n'), 7321 * takes care of '\r\n', '\r' and '\n' and mixed '\r\n'+'\r', '\r\n'+'\n' 7322 * 7323 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7324 * @since 2.12.17 - 2012-07-12 7325 * @param string $text 7326 * @param string $nl 7327 * @return string 7328 */ 7329 public static function convEolChar( & $text, $nl ) { 7330 $outp = ''; 7331 $cix = 0; 7332 while( isset( $text[$cix] )) { 7333 if( isset( $text[$cix + 2] ) && ( "\r" == $text[$cix] ) && ( "\n" == $text[$cix + 1] ) && 7334 (( " " == $text[$cix + 2] ) || ( "\t" == $text[$cix + 2] ))) // 2 pos eolchar + ' ' or '\t' 7335 $cix += 2; // skip 3 7336 elseif( isset( $text[$cix + 1] ) && ( "\r" == $text[$cix] ) && ( "\n" == $text[$cix + 1] )) { 7337 $outp .= $nl; // 2 pos eolchar 7338 $cix += 1; // replace with $nl 7339 } 7340 elseif( isset( $text[$cix + 1] ) && (( "\r" == $text[$cix] ) || ( "\n" == $text[$cix] )) && 7341 (( " " == $text[$cix + 1] ) || ( "\t" == $text[$cix + 1] ))) // 1 pos eolchar + ' ' or '\t' 7342 $cix += 1; // skip 2 7343 elseif(( "\r" == $text[$cix] ) || ( "\n" == $text[$cix] )) // 1 pos eolchar 7344 $outp .= $nl; // replace with $nl 7345 else 7346 $outp .= $text[$cix]; // add any other byte 7347 $cix += 1; 7348 } 7349 return $outp; 7350 } 7351 /** 7352 * create a calendar timezone and standard/daylight components 7353 * 7354 * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone: 7355 * 7356 * BEGIN:VTIMEZONE 7357 * TZID:Europe/Stockholm 7358 * BEGIN:STANDARD 7359 * DTSTART:20101031T020000 7360 * TZOFFSETFROM:+0200 7361 * TZOFFSETTO:+0100 7362 * TZNAME:CET 7363 * END:STANDARD 7364 * BEGIN:DAYLIGHT 7365 * DTSTART:20100328T030000 7366 * TZOFFSETFROM:+0100 7367 * TZOFFSETTO:+0200 7368 * TZNAME:CEST 7369 * END:DAYLIGHT 7370 * END:VTIMEZONE 7371 * 7372 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7373 * @since 2.16.1 - 2012-11-26 7374 * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi <icalcreator@onebigsystem.com> 7375 * Additional changes jpirkey 7376 * @param object $calendar, reference to an iCalcreator calendar instance 7377 * @param string $timezone, a PHP5 (DateTimeZone) valid timezone 7378 * @param array $xProp, *[x-propName => x-propValue], optional 7379 * @param int $from a unix timestamp 7380 * @param int $to a unix timestamp 7381 * @return bool 7382 */ 7383 public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) { 7384 if( empty( $timezone )) 7385 return FALSE; 7386 if( !empty( $from ) && !is_int( $from )) 7387 return FALSE; 7388 if( !empty( $to ) && !is_int( $to )) 7389 return FALSE; 7390 try { 7391 $dtz = new DateTimeZone( $timezone ); 7392 $transitions = $dtz->getTransitions(); 7393 $utcTz = new DateTimeZone( 'UTC' ); 7394 } 7395 catch( Exception $e ) { return FALSE; } 7396 if( empty( $to )) { 7397 $dates = array_keys( $calendar->getProperty( 'dtstart' )); 7398 if( empty( $dates )) 7399 $dates = array( date( 'Ymd' )); 7400 } 7401 if( !empty( $from )) 7402 $dateFrom = new DateTime( "@$from" ); // set lowest date (UTC) 7403 else { 7404 $from = reset( $dates ); // set lowest date to the lowest dtstart date 7405 $dateFrom = new DateTime( $from.'T000000', $dtz ); 7406 $dateFrom->modify( '-1 month' ); // set $dateFrom to one month before the lowest date 7407 $dateFrom->setTimezone( $utcTz ); // convert local date to UTC 7408 } 7409 $dateFromYmd = $dateFrom->format('Y-m-d' ); 7410 if( !empty( $to )) 7411 $dateTo = new DateTime( "@$to" ); // set end date (UTC) 7412 else { 7413 $to = end( $dates ); // set highest date to the highest dtstart date 7414 $dateTo = new DateTime( $to.'T235959', $dtz ); 7415 $dateTo->modify( '+1 year' ); // set $dateTo to one year after the highest date 7416 $dateTo->setTimezone( $utcTz ); // convert local date to UTC 7417 } 7418 $dateToYmd = $dateTo->format('Y-m-d' ); 7419 unset( $dtz ); 7420 $transTemp = array(); 7421 $prevOffsetfrom = 0; 7422 $stdIx = $dlghtIx = null; 7423 $prevTrans = FALSE; 7424 foreach( $transitions as $tix => $trans ) { // all transitions in date-time order!! 7425 $date = new DateTime( "@{$trans['ts']}" ); // set transition date (UTC) 7426 $transDateYmd = $date->format('Y-m-d' ); 7427 if ( $transDateYmd < $dateFromYmd ) { 7428 $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom 7429 $prevTrans = $trans; // save it in case we don't find any that match 7430 $prevTrans['offsetfrom'] = ( 0 < $tix ) ? $transitions[$tix-1]['offset'] : 0; 7431 continue; 7432 } 7433 if( $transDateYmd > $dateToYmd ) 7434 break; // loop always (?) breaks here 7435 if( !empty( $prevOffsetfrom ) || ( 0 == $prevOffsetfrom )) { 7436 $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom 7437 $date->modify( $trans['offsetfrom'].'seconds' ); // convert utc date to local date 7438 $d = $date->format( 'Y-n-j-G-i-s' ); // set date to array to ease up dtstart and (opt) rdate setting 7439 $d = explode( '-', $d ); 7440 $trans['time'] = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); 7441 } 7442 $prevOffsetfrom = $trans['offset']; 7443 if( TRUE !== $trans['isdst'] ) { // standard timezone 7444 if( !empty( $stdIx ) && isset( $transTemp[$stdIx]['offsetfrom'] ) && // check for any repeating rdate's (in order) 7445 ( $transTemp[$stdIx]['abbr'] == $trans['abbr'] ) && 7446 ( $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] ) && 7447 ( $transTemp[$stdIx]['offset'] == $trans['offset'] )) { 7448 $transTemp[$stdIx]['rdate'][] = $trans['time']; 7449 continue; 7450 } 7451 $stdIx = $tix; 7452 } // end standard timezone 7453 else { // daylight timezone 7454 if( !empty( $dlghtIx ) && isset( $transTemp[$dlghtIx]['offsetfrom'] ) && // check for any repeating rdate's (in order) 7455 ( $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] ) && 7456 ( $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] ) && 7457 ( $transTemp[$dlghtIx]['offset'] == $trans['offset'] )) { 7458 $transTemp[$dlghtIx]['rdate'][] = $trans['time']; 7459 continue; 7460 } 7461 $dlghtIx = $tix; 7462 } // end daylight timezone 7463 $transTemp[$tix] = $trans; 7464 } // end foreach( $transitions as $tix => $trans ) 7465 $tz = & $calendar->newComponent( 'vtimezone' ); 7466 $tz->setproperty( 'tzid', $timezone ); 7467 if( !empty( $xProp )) { 7468 foreach( $xProp as $xPropName => $xPropValue ) 7469 if( 'x-' == strtolower( substr( $xPropName, 0, 2 ))) 7470 $tz->setproperty( $xPropName, $xPropValue ); 7471 } 7472 if( empty( $transTemp )) { // if no match found 7473 if( $prevTrans ) { // then we use the last transition (before startdate) for the tz info 7474 $date = new DateTime( "@{$prevTrans['ts']}" ); // set transition date (UTC) 7475 $date->modify( $prevTrans['offsetfrom'].'seconds' ); // convert utc date to local date 7476 $d = $date->format( 'Y-n-j-G-i-s' ); // set date to array to ease up dtstart setting 7477 $d = explode( '-', $d ); 7478 $prevTrans['time'] = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); 7479 $transTemp[0] = $prevTrans; 7480 } 7481 else { // or we use the timezone identifier to BUILD the standard tz info (?) 7482 $date = new DateTime( 'now', new DateTimeZone( $timezone )); 7483 $transTemp[0] = array( 'time' => $date->format( 'Y-m-d\TH:i:s O' ) 7484 , 'offset' => $date->format( 'Z' ) 7485 , 'offsetfrom' => $date->format( 'Z' ) 7486 , 'isdst' => FALSE ); 7487 } 7488 } 7489 unset( $transitions, $date, $prevTrans ); 7490 foreach( $transTemp as $tix => $trans ) { 7491 $type = ( TRUE !== $trans['isdst'] ) ? 'standard' : 'daylight'; 7492 $scomp = & $tz->newComponent( $type ); 7493 $scomp->setProperty( 'dtstart', $trans['time'] ); 7494 // $scomp->setProperty( 'x-utc-timestamp', $tix.' : '.$trans['ts'] ); // test ### 7495 if( !empty( $trans['abbr'] )) 7496 $scomp->setProperty( 'tzname', $trans['abbr'] ); 7497 if( isset( $trans['offsetfrom'] )) 7498 $scomp->setProperty( 'tzoffsetfrom', iCalUtilityFunctions::offsetSec2His( $trans['offsetfrom'] )); 7499 $scomp->setProperty( 'tzoffsetto', iCalUtilityFunctions::offsetSec2His( $trans['offset'] )); 7500 if( isset( $trans['rdate'] )) 7501 $scomp->setProperty( 'RDATE', $trans['rdate'] ); 7502 } 7503 return TRUE; 7504 } 7505 /** 7506 * creates formatted output for calendar component property data value type date/date-time 7507 * 7508 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7509 * @since 2.14.1 - 2012-09-17 7510 * @param array $datetime 7511 * @param int $parno, optional, default 6 7512 * @return string 7513 */ 7514 public static function _format_date_time( $datetime, $parno=6 ) { 7515 return iCalUtilityFunctions::_date2strdate( $datetime, $parno ); 7516 } 7517 public static function _date2strdate( $datetime, $parno=6 ) { 7518 if( !isset( $datetime['year'] ) && 7519 !isset( $datetime['month'] ) && 7520 !isset( $datetime['day'] ) && 7521 !isset( $datetime['hour'] ) && 7522 !isset( $datetime['min'] ) && 7523 !isset( $datetime['sec'] )) 7524 return; 7525 $output = null; 7526 foreach( $datetime as $dkey => & $dvalue ) 7527 if( 'tz' != $dkey ) $dvalue = (integer) $dvalue; 7528 $output = sprintf( '%04d%02d%02d', $datetime['year'], $datetime['month'], $datetime['day'] ); 7529 if( 3 == $parno ) 7530 return $output; 7531 if( !isset( $datetime['hour'] )) $datetime['hour'] = 0; 7532 if( !isset( $datetime['min'] )) $datetime['min'] = 0; 7533 if( !isset( $datetime['sec'] )) $datetime['sec'] = 0; 7534 $output .= sprintf( 'T%02d%02d%02d', $datetime['hour'], $datetime['min'], $datetime['sec'] ); 7535 if( isset( $datetime['tz'] ) && ( '' < trim( $datetime['tz'] ))) { 7536 $datetime['tz'] = trim( $datetime['tz'] ); 7537 if( 'Z' == $datetime['tz'] ) 7538 $parno = 7; 7539 elseif( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) { 7540 $parno = 7; 7541 $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ); 7542 try { 7543 $d = new DateTime( $output, new DateTimeZone( 'UTC' )); 7544 if( 0 != $offset ) // adjust för offset 7545 $d->modify( "$offset seconds" ); 7546 $output = $d->format( 'Ymd\THis' ); 7547 } 7548 catch( Exception $e ) { 7549 $output = date( 'Ymd\THis', mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year'] )); 7550 } 7551 } 7552 if( 7 == $parno ) 7553 $output .= 'Z'; 7554 } 7555 return $output; 7556 } 7557 /** 7558 * convert a date/datetime (array) to timestamp 7559 * 7560 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7561 * @since 2.14.1 - 2012-09-29 7562 * @param array $datetime datetime(/date) 7563 * @param string $wtz timezone 7564 * @return int 7565 */ 7566 public static function _date2timestamp( $datetime, $wtz=null ) { 7567 if( !isset( $datetime['hour'] )) $datetime['hour'] = 0; 7568 if( !isset( $datetime['min'] )) $datetime['min'] = 0; 7569 if( !isset( $datetime['sec'] )) $datetime['sec'] = 0; 7570 if( empty( $wtz ) && ( !isset( $datetime['tz'] ) || empty( $datetime['tz'] ))) 7571 return mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] ); 7572 $output = $offset = 0; 7573 if( empty( $wtz )) { 7574 if( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) { 7575 $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ) * -1; 7576 $wtz = 'UTC'; 7577 } 7578 else 7579 $wtz = $datetime['tz']; 7580 } 7581 if(( 'Z' == $wtz ) || ( 'GMT' == strtoupper( $wtz ))) 7582 $wtz = 'UTC'; 7583 try { 7584 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $datetime['year'], $datetime['month'], $datetime['day'], $datetime['hour'], $datetime['min'], $datetime['sec'] ); 7585 $d = new DateTime( $strdate, new DateTimeZone( $wtz )); 7586 if( 0 != $offset ) // adjust for offset 7587 $d->modify( $offset.' seconds' ); 7588 $output = $d->format( 'U' ); 7589 unset( $d ); 7590 } 7591 catch( Exception $e ) { 7592 $output = mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] ); 7593 } 7594 return $output; 7595 } 7596 /** 7597 * ensures internal duration format for input in array format 7598 * 7599 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7600 * @since 2.14.1 - 2012-09-25 7601 * @param array $duration 7602 * @return array 7603 */ 7604 public static function _duration_array( $duration ) { 7605 return iCalUtilityFunctions::_duration2arr( $duration ); 7606 } 7607 public static function _duration2arr( $duration ) { 7608 $output = array(); 7609 if( is_array( $duration ) && 7610 ( 1 == count( $duration )) && 7611 isset( $duration['sec'] ) && 7612 ( 60 < $duration['sec'] )) { 7613 $durseconds = $duration['sec']; 7614 $output['week'] = (int) floor( $durseconds / ( 60 * 60 * 24 * 7 )); 7615 $durseconds = $durseconds % ( 60 * 60 * 24 * 7 ); 7616 $output['day'] = (int) floor( $durseconds / ( 60 * 60 * 24 )); 7617 $durseconds = $durseconds % ( 60 * 60 * 24 ); 7618 $output['hour'] = (int) floor( $durseconds / ( 60 * 60 )); 7619 $durseconds = $durseconds % ( 60 * 60 ); 7620 $output['min'] = (int) floor( $durseconds / ( 60 )); 7621 $output['sec'] = ( $durseconds % ( 60 )); 7622 } 7623 else { 7624 foreach( $duration as $durKey => $durValue ) { 7625 if( empty( $durValue )) continue; 7626 switch ( $durKey ) { 7627 case '0': case 'week': $output['week'] = $durValue; break; 7628 case '1': case 'day': $output['day'] = $durValue; break; 7629 case '2': case 'hour': $output['hour'] = $durValue; break; 7630 case '3': case 'min': $output['min'] = $durValue; break; 7631 case '4': case 'sec': $output['sec'] = $durValue; break; 7632 } 7633 } 7634 } 7635 if( isset( $output['week'] ) && ( 0 < $output['week'] )) { 7636 unset( $output['day'], $output['hour'], $output['min'], $output['sec'] ); 7637 return $output; 7638 } 7639 unset( $output['week'] ); 7640 if( empty( $output['day'] )) 7641 unset( $output['day'] ); 7642 if ( isset( $output['hour'] ) || isset( $output['min'] ) || isset( $output['sec'] )) { 7643 if( !isset( $output['hour'] )) $output['hour'] = 0; 7644 if( !isset( $output['min'] )) $output['min'] = 0; 7645 if( !isset( $output['sec'] )) $output['sec'] = 0; 7646 if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] )) 7647 unset( $output['hour'], $output['min'], $output['sec'] ); 7648 } 7649 return $output; 7650 } 7651 /** 7652 * convert startdate+duration to a array format datetime 7653 * 7654 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7655 * @since 2.15.12 - 2012-10-31 7656 * @param array $startdate 7657 * @param array $duration 7658 * @return array, date format 7659 */ 7660 public static function _duration2date( $startdate, $duration ) { 7661 $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE; 7662 $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0; 7663 $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0; 7664 $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0; 7665 $dtend = 0; 7666 if( isset( $duration['week'] )) $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 ); 7667 if( isset( $duration['day'] )) $dtend += ( $duration['day'] * 24 * 60 * 60 ); 7668 if( isset( $duration['hour'] )) $dtend += ( $duration['hour'] * 60 *60 ); 7669 if( isset( $duration['min'] )) $dtend += ( $duration['min'] * 60 ); 7670 if( isset( $duration['sec'] )) $dtend += $duration['sec']; 7671 $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'] )); 7672 $d = explode( '-', $date ); 7673 $dtend2 = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); 7674 if( isset( $startdate['tz'] )) 7675 $dtend2['tz'] = $startdate['tz']; 7676 if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] ))) 7677 unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] ); 7678 return $dtend2; 7679 } 7680 /** 7681 * ensures internal duration format for an input string (iCal) formatted duration 7682 * 7683 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7684 * @since 2.14.1 - 2012-09-25 7685 * @param string $duration 7686 * @return array 7687 */ 7688 public static function _duration_string( $duration ) { 7689 return iCalUtilityFunctions::_durationStr2arr( $duration ); 7690 } 7691 public static function _durationStr2arr( $duration ) { 7692 $duration = (string) trim( $duration ); 7693 while( 'P' != strtoupper( substr( $duration, 0, 1 ))) { 7694 if( 0 < strlen( $duration )) 7695 $duration = substr( $duration, 1 ); 7696 else 7697 return false; // no leading P !?!? 7698 } 7699 $duration = substr( $duration, 1 ); // skip P 7700 $duration = str_replace ( 't', 'T', $duration ); 7701 $duration = str_replace ( 'T', '', $duration ); 7702 $output = array(); 7703 $val = null; 7704 for( $ix=0; $ix < strlen( $duration ); $ix++ ) { 7705 switch( strtoupper( substr( $duration, $ix, 1 ))) { 7706 case 'W': 7707 $output['week'] = $val; 7708 $val = null; 7709 break; 7710 case 'D': 7711 $output['day'] = $val; 7712 $val = null; 7713 break; 7714 case 'H': 7715 $output['hour'] = $val; 7716 $val = null; 7717 break; 7718 case 'M': 7719 $output['min'] = $val; 7720 $val = null; 7721 break; 7722 case 'S': 7723 $output['sec'] = $val; 7724 $val = null; 7725 break; 7726 default: 7727 if( !ctype_digit( substr( $duration, $ix, 1 ))) 7728 return false; // unknown duration control character !?!? 7729 else 7730 $val .= substr( $duration, $ix, 1 ); 7731 } 7732 } 7733 return iCalUtilityFunctions::_duration2arr( $output ); 7734 } 7735 /** 7736 * creates formatted output for calendar component property data value type duration 7737 * 7738 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7739 * @since 2.15.8 - 2012-10-30 7740 * @param array $duration, array( week, day, hour, min, sec ) 7741 * @return string 7742 */ 7743 public static function _format_duration( $duration ) { 7744 return iCalUtilityFunctions::_duration2str( $duration ); 7745 } 7746 public static function _duration2str( $duration ) { 7747 if( isset( $duration['week'] ) || 7748 isset( $duration['day'] ) || 7749 isset( $duration['hour'] ) || 7750 isset( $duration['min'] ) || 7751 isset( $duration['sec'] )) 7752 $ok = TRUE; 7753 else 7754 return; 7755 if( isset( $duration['week'] ) && ( 0 < $duration['week'] )) 7756 return 'P'.$duration['week'].'W'; 7757 $output = 'P'; 7758 if( isset($duration['day'] ) && ( 0 < $duration['day'] )) 7759 $output .= $duration['day'].'D'; 7760 if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) || 7761 ( isset( $duration['min']) && ( 0 < $duration['min'] )) || 7762 ( isset( $duration['sec']) && ( 0 < $duration['sec'] ))) { 7763 $output .= 'T'; 7764 $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '0H'; 7765 $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : '0M'; 7766 $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : '0S'; 7767 } 7768 if( 'P' == $output ) 7769 $output = 'PT0H0M0S'; 7770 return $output; 7771 } 7772 /** 7773 * removes expkey+expvalue from array and returns hitval (if found) else returns elseval 7774 * 7775 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7776 * @since 2.4.16 - 2008-11-08 7777 * @param array $array 7778 * @param string $expkey, expected key 7779 * @param string $expval, expected value 7780 * @param int $hitVal optional, return value if found 7781 * @param int $elseVal optional, return value if not found 7782 * @param int $preSet optional, return value if already preset 7783 * @return int 7784 */ 7785 public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) { 7786 if( $preSet ) 7787 return $preSet; 7788 if( !is_array( $array ) || ( 0 == count( $array ))) 7789 return $elseVal; 7790 foreach( $array as $key => $value ) { 7791 if( strtoupper( $expkey ) == strtoupper( $key )) { 7792 if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) { 7793 unset( $array[$key] ); 7794 return $hitVal; 7795 } 7796 } 7797 } 7798 return $elseVal; 7799 } 7800 /** 7801 * checks if input contains a (array formatted) date/time 7802 * 7803 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7804 * @since 2.11.8 - 2012-01-20 7805 * @param array $input 7806 * @return bool 7807 */ 7808 public static function _isArrayDate( $input ) { 7809 if( !is_array( $input )) 7810 return FALSE; 7811 if( isset( $input['week'] ) || ( !in_array( count( $input ), array( 3, 6, 7 )))) 7812 return FALSE; 7813 if( 7 == count( $input )) 7814 return TRUE; 7815 if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) 7816 return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); 7817 if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] )) 7818 return FALSE; 7819 if( in_array( 0, $input )) 7820 return FALSE; 7821 if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] )) 7822 return FALSE; 7823 if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) && 7824 checkdate( (int) $input[1], (int) $input[2], (int) $input[0] )) 7825 return TRUE; 7826 $input = iCalUtilityFunctions::_strdate2date( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y 7827 if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) 7828 return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); 7829 return FALSE; 7830 } 7831 /** 7832 * checks if input array contains a timestamp date 7833 * 7834 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7835 * @since 2.4.16 - 2008-10-18 7836 * @param array $input 7837 * @return bool 7838 */ 7839 public static function _isArrayTimestampDate( $input ) { 7840 return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ; 7841 } 7842 /** 7843 * controls if input string contains (trailing) UTC/iCal offset 7844 * 7845 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7846 * @since 2.14.1 - 2012-09-21 7847 * @param string $input 7848 * @return bool 7849 */ 7850 public static function _isOffset( $input ) { 7851 $input = trim( (string) $input ); 7852 if( 'Z' == substr( $input, -1 )) 7853 return TRUE; 7854 elseif(( 5 <= strlen( $input )) && 7855 ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) && 7856 ( '0000' <= substr( $input, -4 )) && ( '9999' >= substr( $input, -4 ))) 7857 return TRUE; 7858 elseif(( 7 <= strlen( $input )) && 7859 ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) && 7860 ( '000000' <= substr( $input, -6 )) && ( '999999' >= substr( $input, -6 ))) 7861 return TRUE; 7862 return FALSE; 7863 } 7864 /** 7865 * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone 7866 * matching (MS) UCT offset and time zone descriptors 7867 * 7868 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7869 * @since 2.14.1 - 2012-09-16 7870 * @param string $timezone, input/output variable reference 7871 * @return bool 7872 */ 7873 public static function ms2phpTZ( & $timezone ) { 7874 if( empty( $timezone )) 7875 return FALSE; 7876 $search = str_replace( '"', '', $timezone ); 7877 $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search ); 7878 if( '(UTC' != substr( $search, 0, 4 )) 7879 return FALSE; 7880 if( FALSE === ( $pos = strpos( $search, ')' ))) 7881 return FALSE; 7882 $pos = strpos( $search, ')' ); 7883 $searchOffset = substr( $search, 4, ( $pos - 4 )); 7884 $searchOffset = iCalUtilityFunctions::_tz2offset( str_replace( ':', '', $searchOffset )); 7885 while( ' ' ==substr( $search, ( $pos + 1 ))) 7886 $pos += 1; 7887 $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos + 1 )) )); 7888 $searchWords = explode( ' ', $searchText ); 7889 $timezone_abbreviations = DateTimeZone::listAbbreviations(); 7890 $hits = array(); 7891 foreach( $timezone_abbreviations as $name => $transitions ) { 7892 foreach( $transitions as $cnt => $transition ) { 7893 if( empty( $transition['offset'] ) || 7894 empty( $transition['timezone_id'] ) || 7895 ( $transition['offset'] != $searchOffset )) 7896 continue; 7897 $cWords = explode( '/', $transition['timezone_id'] ); 7898 $cPrio = $hitCnt = $rank = 0; 7899 foreach( $cWords as $cWord ) { 7900 if( empty( $cWord )) 7901 continue; 7902 $cPrio += 1; 7903 $sPrio = 0; 7904 foreach( $searchWords as $sWord ) { 7905 if( empty( $sWord ) || ( 'time' == strtolower( $sWord ))) 7906 continue; 7907 $sPrio += 1; 7908 if( strtolower( $cWord ) == strtolower( $sWord )) { 7909 $hitCnt += 1; 7910 $rank += ( $cPrio + $sPrio ); 7911 } 7912 else 7913 $rank += 10; 7914 } 7915 } 7916 if( 0 < $hitCnt ) { 7917 $hits[$rank][] = $transition['timezone_id']; 7918 } 7919 } 7920 } 7921 unset( $timezone_abbreviations ); 7922 if( empty( $hits )) 7923 return FALSE; 7924 ksort( $hits ); 7925 foreach( $hits as $rank => $tzs ) { 7926 if( !empty( $tzs )) { 7927 $timezone = reset( $tzs ); 7928 return TRUE; 7929 } 7930 } 7931 return FALSE; 7932 } 7933 /** 7934 * transforms offset in seconds to [-/+]hhmm[ss] 7935 * 7936 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7937 * @since 2011-05-02 7938 * @param string $seconds 7939 * @return string 7940 */ 7941 public static function offsetSec2His( $seconds ) { 7942 if( '-' == substr( $seconds, 0, 1 )) { 7943 $prefix = '-'; 7944 $seconds = substr( $seconds, 1 ); 7945 } 7946 elseif( '+' == substr( $seconds, 0, 1 )) { 7947 $prefix = '+'; 7948 $seconds = substr( $seconds, 1 ); 7949 } 7950 else 7951 $prefix = '+'; 7952 $output = ''; 7953 $hour = (int) floor( $seconds / 3600 ); 7954 if( 10 > $hour ) 7955 $hour = '0'.$hour; 7956 $seconds = $seconds % 3600; 7957 $min = (int) floor( $seconds / 60 ); 7958 if( 10 > $min ) 7959 $min = '0'.$min; 7960 $output = $hour.$min; 7961 $seconds = $seconds % 60; 7962 if( 0 < $seconds) { 7963 if( 9 < $seconds) 7964 $output .= $seconds; 7965 else 7966 $output .= '0'.$seconds; 7967 } 7968 return $prefix.$output; 7969 } 7970 /** 7971 * updates an array with dates based on a recur pattern 7972 * 7973 * if missing, UNTIL is set 1 year from startdate (emergency break) 7974 * 7975 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 7976 * @since 2.10.19 - 2011-10-31 7977 * @param array $result, array to update, array([timestamp] => timestamp) 7978 * @param array $recur, pattern for recurrency (only value part, params ignored) 7979 * @param array $wdate, component start date 7980 * @param array $startdate, start date 7981 * @param array $enddate, optional 7982 * @return void 7983 * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start 7984 */ 7985 public static function _recur2date( & $result, $recur, $wdate, $startdate, $enddate=FALSE ) { 7986 foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v; 7987 $wdateStart = $wdate; 7988 $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); 7989 $startdatets = iCalUtilityFunctions::_date2timestamp( $startdate ); 7990 if( !$enddate ) { 7991 $enddate = $startdate; 7992 $enddate['year'] += 1; 7993 } 7994 // echo "recur __in_ comp start ".implode('-',$wdate)." period start ".implode('-',$startdate)." period end ".implode('-',$enddate)."<br />\n";print_r($recur);echo "<br />\n";//test### 7995 $endDatets = iCalUtilityFunctions::_date2timestamp( $enddate ); // fix break 7996 if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] )) 7997 $recur['UNTIL'] = $enddate; // create break 7998 if( isset( $recur['UNTIL'] )) { 7999 $tdatets = iCalUtilityFunctions::_date2timestamp( $recur['UNTIL'] ); 8000 if( $endDatets > $tdatets ) { 8001 $endDatets = $tdatets; // emergency break 8002 $enddate = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 ); 8003 } 8004 else 8005 $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 ); 8006 } 8007 if( $wdatets > $endDatets ) { 8008 // echo "recur out of date ".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test 8009 return array(); // nothing to do.. . 8010 } 8011 if( !isset( $recur['FREQ'] )) // "MUST be specified.. ." 8012 $recur['FREQ'] = 'DAILY'; // ?? 8013 $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ?? 8014 $weekStart = (int) date( 'W', ( $wdatets + $wkst )); 8015 if( !isset( $recur['INTERVAL'] )) 8016 $recur['INTERVAL'] = 1; 8017 $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence 8018 /* find out how to step up dates and set index for interval count */ 8019 $step = array(); 8020 if( 'YEARLY' == $recur['FREQ'] ) 8021 $step['year'] = 1; 8022 elseif( 'MONTHLY' == $recur['FREQ'] ) 8023 $step['month'] = 1; 8024 elseif( 'WEEKLY' == $recur['FREQ'] ) 8025 $step['day'] = 7; 8026 else 8027 $step['day'] = 1; 8028 if( isset( $step['year'] ) && isset( $recur['BYMONTH'] )) 8029 $step = array( 'month' => 1 ); 8030 if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ?? 8031 $step = array( 'day' => 7 ); 8032 if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] )) 8033 $step = array( 'day' => 1 ); 8034 $intervalarr = array(); 8035 if( 1 < $recur['INTERVAL'] ) { 8036 $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); 8037 $intervalarr = array( $intervalix => 0 ); 8038 } 8039 if( isset( $recur['BYSETPOS'] )) { // save start date + weekno 8040 $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array(); 8041 // echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold<br />\n"; // test ### 8042 if( is_array( $recur['BYSETPOS'] )) { 8043 foreach( $recur['BYSETPOS'] as $bix => $bval ) 8044 $recur['BYSETPOS'][$bix] = (int) $bval; 8045 } 8046 else 8047 $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] ); 8048 if( 'YEARLY' == $recur['FREQ'] ) { 8049 $wdate['month'] = $wdate['day'] = 1; // start from beginning of year 8050 $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); 8051 iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'year' => 1 )); // make sure to count whole last year 8052 } 8053 elseif( 'MONTHLY' == $recur['FREQ'] ) { 8054 $wdate['day'] = 1; // start from beginning of month 8055 $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); 8056 iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'month' => 1 )); // make sure to count whole last month 8057 } 8058 else 8059 iCalUtilityFunctions::_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period 8060 // echo "BYSETPOS endDat++ =".implode('-',$enddate).' step='.var_export($step,TRUE)."<br />\n";//test### 8061 $bysetposWold = (int) date( 'W', ( $wdatets + $wkst )); 8062 $bysetposYold = $wdate['year']; 8063 $bysetposMold = $wdate['month']; 8064 $bysetposDold = $wdate['day']; 8065 } 8066 else 8067 iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); 8068 $year_old = null; 8069 $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' ); 8070 /* MAIN LOOP */ 8071 // echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test 8072 while( TRUE ) { 8073 if( isset( $endDatets ) && ( $wdatets > $endDatets )) 8074 break; 8075 if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) 8076 break; 8077 if( $year_old != $wdate['year'] ) { 8078 $year_old = $wdate['year']; 8079 $daycnts = array(); 8080 $yeardays = $weekno = 0; 8081 $yeardaycnt = array(); 8082 foreach( $daynames as $dn ) 8083 $yeardaycnt[$dn] = 0; 8084 for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters 8085 $daycnts[$m] = array(); 8086 $weekdaycnt = array(); 8087 foreach( $daynames as $dn ) 8088 $weekdaycnt[$dn] = 0; 8089 $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); 8090 for( $d = 1; $d <= $mcnt; $d++ ) { 8091 $daycnts[$m][$d] = array(); 8092 if( isset( $recur['BYYEARDAY'] )) { 8093 $yeardays++; 8094 $daycnts[$m][$d]['yearcnt_up'] = $yeardays; 8095 } 8096 if( isset( $recur['BYDAY'] )) { 8097 $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] )); 8098 $day = $daynames[$day]; 8099 $daycnts[$m][$d]['DAY'] = $day; 8100 $weekdaycnt[$day]++; 8101 $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day]; 8102 $yeardaycnt[$day]++; 8103 $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day]; 8104 } 8105 if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) 8106 $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year'])); 8107 } 8108 } 8109 $daycnt = 0; 8110 $yeardaycnt = array(); 8111 if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) { 8112 $weekno = null; 8113 for( $d=31; $d > 25; $d-- ) { // get last weekno for year 8114 if( !$weekno ) 8115 $weekno = $daycnts[12][$d]['weekno_up']; 8116 elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) { 8117 $weekno = $daycnts[12][$d]['weekno_up']; 8118 break; 8119 } 8120 } 8121 } 8122 for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters 8123 $weekdaycnt = array(); 8124 foreach( $daynames as $dn ) 8125 $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0; 8126 $monthcnt = 0; 8127 $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); 8128 for( $d = $mcnt; $d > 0; $d-- ) { 8129 if( isset( $recur['BYYEARDAY'] )) { 8130 $daycnt -= 1; 8131 $daycnts[$m][$d]['yearcnt_down'] = $daycnt; 8132 } 8133 if( isset( $recur['BYMONTHDAY'] )) { 8134 $monthcnt -= 1; 8135 $daycnts[$m][$d]['monthcnt_down'] = $monthcnt; 8136 } 8137 if( isset( $recur['BYDAY'] )) { 8138 $day = $daycnts[$m][$d]['DAY']; 8139 $weekdaycnt[$day] -= 1; 8140 $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day]; 8141 $yeardaycnt[$day] -= 1; 8142 $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day]; 8143 } 8144 if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) 8145 $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1); 8146 } 8147 } 8148 } 8149 /* check interval */ 8150 if( 1 < $recur['INTERVAL'] ) { 8151 /* create interval index */ 8152 $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); 8153 /* check interval */ 8154 $currentKey = array_keys( $intervalarr ); 8155 $currentKey = end( $currentKey ); // get last index 8156 if( $currentKey != $intervalix ) 8157 $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 )); 8158 if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) && 8159 ( 0 != $intervalarr[$intervalix] )) { 8160 /* step up date */ 8161 // echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test 8162 iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); 8163 continue; 8164 } 8165 else // continue within the selected interval 8166 $intervalarr[$intervalix] = 0; 8167 // echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test 8168 } 8169 $updateOK = TRUE; 8170 if( $updateOK && isset( $recur['BYMONTH'] )) 8171 $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH'] 8172 , $wdate['month'] 8173 ,($wdate['month'] - 13)); 8174 if( $updateOK && isset( $recur['BYWEEKNO'] )) 8175 $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO'] 8176 , $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] 8177 , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] ); 8178 if( $updateOK && isset( $recur['BYYEARDAY'] )) 8179 $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY'] 8180 , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up'] 8181 , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] ); 8182 if( $updateOK && isset( $recur['BYMONTHDAY'] )) 8183 $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY'] 8184 , $wdate['day'] 8185 , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] ); 8186 // echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n";//test### 8187 if( $updateOK && isset( $recur['BYDAY'] )) { 8188 $updateOK = FALSE; 8189 $m = $wdate['month']; 8190 $d = $wdate['day']; 8191 if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no 8192 $daynoexists = $daynosw = $daynamesw = FALSE; 8193 if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] ) 8194 $daynamesw = TRUE; 8195 if( isset( $recur['BYDAY'][0] )) { 8196 $daynoexists = TRUE; 8197 if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] )) 8198 $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] 8199 , $daycnts[$m][$d]['monthdayno_up'] 8200 , $daycnts[$m][$d]['monthdayno_down'] ); 8201 elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) 8202 $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] 8203 , $daycnts[$m][$d]['yeardayno_up'] 8204 , $daycnts[$m][$d]['yeardayno_down'] ); 8205 } 8206 if(( $daynoexists && $daynosw && $daynamesw ) || 8207 ( !$daynoexists && !$daynosw && $daynamesw )) { 8208 $updateOK = TRUE; 8209 // 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 ### 8210 } 8211 //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 ### 8212 } 8213 else { 8214 foreach( $recur['BYDAY'] as $bydayvalue ) { 8215 $daynoexists = $daynosw = $daynamesw = FALSE; 8216 if( isset( $bydayvalue['DAY'] ) && 8217 ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] )) 8218 $daynamesw = TRUE; 8219 if( isset( $bydayvalue[0] )) { 8220 $daynoexists = TRUE; 8221 if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || 8222 isset( $recur['BYMONTH'] )) 8223 $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] 8224 , $daycnts[$m][$d]['monthdayno_up'] 8225 , $daycnts[$m][$d]['monthdayno_down'] ); 8226 elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) 8227 $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] 8228 , $daycnts[$m][$d]['yeardayno_up'] 8229 , $daycnts[$m][$d]['yeardayno_down'] ); 8230 } 8231 // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ### 8232 if(( $daynoexists && $daynosw && $daynamesw ) || 8233 ( !$daynoexists && !$daynosw && $daynamesw )) { 8234 $updateOK = TRUE; 8235 break; 8236 } 8237 } 8238 } 8239 } 8240 // echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ### 8241 /* check BYSETPOS */ 8242 if( $updateOK ) { 8243 if( isset( $recur['BYSETPOS'] ) && 8244 ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) { 8245 if( isset( $recur['WEEKLY'] )) { 8246 if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] ) 8247 $bysetposw1[] = $wdatets; 8248 else 8249 $bysetposw2[] = $wdatets; 8250 } 8251 else { 8252 if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && 8253 ( $bysetposYold == $wdate['year'] )) || 8254 ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) && 8255 (( $bysetposYold == $wdate['year'] ) && 8256 ( $bysetposMold == $wdate['month'] ))) || 8257 ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && 8258 (( $bysetposYold == $wdate['year'] ) && 8259 ( $bysetposMold == $wdate['month']) && 8260 ( $bysetposDold == $wdate['day'] )))) { 8261 // echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test 8262 $bysetposymd1[] = $wdatets; 8263 } 8264 else { 8265 // echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test 8266 $bysetposymd2[] = $wdatets; 8267 } 8268 } 8269 } 8270 else { 8271 /* update result array if BYSETPOS is set */ 8272 $countcnt++; 8273 if( $startdatets <= $wdatets ) { // only output within period 8274 $result[$wdatets] = TRUE; 8275 // echo "recur ".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test 8276 } 8277 // echo "recur undate ".date('Y-m-d H:i:s',$wdatets)." okdatstart ".date('Y-m-d H:i:s',$startdatets)."<br />\n";//test 8278 $updateOK = FALSE; 8279 } 8280 } 8281 /* step up date */ 8282 iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); 8283 /* check if BYSETPOS is set for updating result array */ 8284 if( $updateOK && isset( $recur['BYSETPOS'] )) { 8285 $bysetpos = FALSE; 8286 if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && 8287 ( $bysetposYold != $wdate['year'] )) { 8288 $bysetpos = TRUE; 8289 $bysetposYold = $wdate['year']; 8290 } 8291 elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] && 8292 (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) { 8293 $bysetpos = TRUE; 8294 $bysetposYold = $wdate['year']; 8295 $bysetposMold = $wdate['month']; 8296 } 8297 elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) { 8298 $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year'])); 8299 if( $bysetposWold != $weekno ) { 8300 $bysetposWold = $weekno; 8301 $bysetpos = TRUE; 8302 } 8303 } 8304 elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && 8305 (( $bysetposYold != $wdate['year'] ) || 8306 ( $bysetposMold != $wdate['month'] ) || 8307 ( $bysetposDold != $wdate['day'] ))) { 8308 $bysetpos = TRUE; 8309 $bysetposYold = $wdate['year']; 8310 $bysetposMold = $wdate['month']; 8311 $bysetposDold = $wdate['day']; 8312 } 8313 if( $bysetpos ) { 8314 if( isset( $recur['BYWEEKNO'] )) { 8315 $bysetposarr1 = & $bysetposw1; 8316 $bysetposarr2 = & $bysetposw2; 8317 } 8318 else { 8319 $bysetposarr1 = & $bysetposymd1; 8320 $bysetposarr2 = & $bysetposymd2; 8321 } 8322 // echo 'test före out startYMD (weekno)='.$wdateStart['year'].':'.$wdateStart['month'].':'.$wdateStart['day']." ($weekStart) "; // test ### 8323 foreach( $recur['BYSETPOS'] as $ix ) { 8324 if( 0 > $ix ) // both positive and negative BYSETPOS allowed 8325 $ix = ( count( $bysetposarr1 ) + $ix + 1); 8326 $ix--; 8327 if( isset( $bysetposarr1[$ix] )) { 8328 if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period 8329 // $testdate = iCalUtilityFunctions::_timestamp2date( $bysetposarr1[$ix], 6 ); // test ### 8330 // $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, $testdate['month'], $testdate['day'], $testdate['year'] )); // test ### 8331 // echo " testYMD (weekno)=".$testdate['year'].':'.$testdate['month'].':'.$testdate['day']." ($testweekno)"; // test ### 8332 $result[$bysetposarr1[$ix]] = TRUE; 8333 // echo " recur ".date('Y-m-d H:i:s',$bysetposarr1[$ix]); // test ### 8334 } 8335 $countcnt++; 8336 } 8337 if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) 8338 break; 8339 } 8340 // echo "<br />\n"; // test ### 8341 $bysetposarr1 = $bysetposarr2; 8342 $bysetposarr2 = array(); 8343 } 8344 } 8345 } 8346 } 8347 public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) { 8348 if( is_array( $BYvalue ) && 8349 ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue ))) 8350 return TRUE; 8351 elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue )) 8352 return TRUE; 8353 else 8354 return FALSE; 8355 } 8356 public static function _recurIntervalIx( $freq, $date, $wkst ) { 8357 /* create interval index */ 8358 switch( $freq ) { 8359 case 'YEARLY': 8360 $intervalix = $date['year']; 8361 break; 8362 case 'MONTHLY': 8363 $intervalix = $date['year'].'-'.$date['month']; 8364 break; 8365 case 'WEEKLY': 8366 $wdatets = iCalUtilityFunctions::_date2timestamp( $date ); 8367 $intervalix = (int) date( 'W', ( $wdatets + $wkst )); 8368 break; 8369 case 'DAILY': 8370 default: 8371 $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day']; 8372 break; 8373 } 8374 return $intervalix; 8375 } 8376 /** 8377 * convert input format for exrule and rrule to internal format 8378 * 8379 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8380 * @since 2.14.1 - 2012-09-24 8381 * @param array $rexrule 8382 * @return array 8383 */ 8384 public static function _setRexrule( $rexrule ) { 8385 $input = array(); 8386 if( empty( $rexrule )) 8387 return $input; 8388 foreach( $rexrule as $rexrulelabel => $rexrulevalue ) { 8389 $rexrulelabel = strtoupper( $rexrulelabel ); 8390 if( 'UNTIL' != $rexrulelabel ) 8391 $input[$rexrulelabel] = $rexrulevalue; 8392 else { 8393 iCalUtilityFunctions::_strDate2arr( $rexrulevalue ); 8394 if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp, always date-time UTC 8395 $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 7, 'UTC' ); 8396 elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) { // date or UTC date-time 8397 $parno = ( isset( $rexrulevalue['hour'] ) || isset( $rexrulevalue[4] )) ? 7 : 3; 8398 $d = iCalUtilityFunctions::_chkDateArr( $rexrulevalue, $parno ); 8399 if(( 3 < $parno ) && isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { 8400 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); 8401 $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 8402 unset( $input[$rexrulelabel]['unparsedtext'] ); 8403 } 8404 else 8405 $input[$rexrulelabel] = $d; 8406 } 8407 elseif( 8 <= strlen( trim( $rexrulevalue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC 8408 $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $rexrulevalue ); 8409 unset( $input['$rexrulelabel']['unparsedtext'] ); 8410 } 8411 if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] )) 8412 $input[$rexrulelabel]['tz'] = 'Z'; 8413 } 8414 } 8415 /* set recurrence rule specification in rfc2445 order */ 8416 $input2 = array(); 8417 if( isset( $input['FREQ'] )) 8418 $input2['FREQ'] = $input['FREQ']; 8419 if( isset( $input['UNTIL'] )) 8420 $input2['UNTIL'] = $input['UNTIL']; 8421 elseif( isset( $input['COUNT'] )) 8422 $input2['COUNT'] = $input['COUNT']; 8423 if( isset( $input['INTERVAL'] )) 8424 $input2['INTERVAL'] = $input['INTERVAL']; 8425 if( isset( $input['BYSECOND'] )) 8426 $input2['BYSECOND'] = $input['BYSECOND']; 8427 if( isset( $input['BYMINUTE'] )) 8428 $input2['BYMINUTE'] = $input['BYMINUTE']; 8429 if( isset( $input['BYHOUR'] )) 8430 $input2['BYHOUR'] = $input['BYHOUR']; 8431 if( isset( $input['BYDAY'] )) { 8432 if( !is_array( $input['BYDAY'] )) // ensure upper case.. . 8433 $input2['BYDAY'] = strtoupper( $input['BYDAY'] ); 8434 else { 8435 foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) { 8436 if( 'DAY' == strtoupper( $BYDAYx )) 8437 $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv ); 8438 elseif( !is_array( $BYDAYv )) { 8439 $input2['BYDAY'][$BYDAYx] = $BYDAYv; 8440 } 8441 else { 8442 foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) { 8443 if( 'DAY' == strtoupper( $BYDAYx2 )) 8444 $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 ); 8445 else 8446 $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2; 8447 } 8448 } 8449 } 8450 } 8451 } 8452 if( isset( $input['BYMONTHDAY'] )) 8453 $input2['BYMONTHDAY'] = $input['BYMONTHDAY']; 8454 if( isset( $input['BYYEARDAY'] )) 8455 $input2['BYYEARDAY'] = $input['BYYEARDAY']; 8456 if( isset( $input['BYWEEKNO'] )) 8457 $input2['BYWEEKNO'] = $input['BYWEEKNO']; 8458 if( isset( $input['BYMONTH'] )) 8459 $input2['BYMONTH'] = $input['BYMONTH']; 8460 if( isset( $input['BYSETPOS'] )) 8461 $input2['BYSETPOS'] = $input['BYSETPOS']; 8462 if( isset( $input['WKST'] )) 8463 $input2['WKST'] = $input['WKST']; 8464 return $input2; 8465 } 8466 /** 8467 * convert format for input date to internal date with parameters 8468 * 8469 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8470 * @since 2.14.1 - 2012-10-15 8471 * @param mixed $year 8472 * @param mixed $month optional 8473 * @param int $day optional 8474 * @param int $hour optional 8475 * @param int $min optional 8476 * @param int $sec optional 8477 * @param string $tz optional 8478 * @param array $params optional 8479 * @param string $caller optional 8480 * @param string $objName optional 8481 * @param string $tzid optional 8482 * @return array 8483 */ 8484 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 ) { 8485 $input = $parno = null; 8486 $localtime = (( 'dtstart' == $caller ) && in_array( $objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE; 8487 iCalUtilityFunctions::_strDate2arr( $year ); 8488 if( iCalUtilityFunctions::_isArrayDate( $year )) { 8489 $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, $parno ); 8490 if( 100 > $input['value']['year'] ) 8491 $input['value']['year'] += 2000; 8492 if( $localtime ) 8493 unset( $month['VALUE'], $month['TZID'] ); 8494 elseif( !isset( $month['TZID'] ) && isset( $tzid )) 8495 $month['TZID'] = $tzid; 8496 if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) 8497 unset( $month['TZID'] ); 8498 elseif( isset( $month['TZID'] ) && iCalUtilityFunctions::_isOffset( $month['TZID'] )) { 8499 $input['value']['tz'] = $month['TZID']; 8500 unset( $month['TZID'] ); 8501 } 8502 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); 8503 $hitval = ( isset( $input['value']['tz'] )) ? 7 : 6; 8504 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval ); 8505 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $input['value'] ), $parno ); 8506 if(( 3 != $parno ) && isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { 8507 $d = $input['value']; 8508 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); 8509 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno ); 8510 unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); 8511 } 8512 if( isset( $input['value']['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { 8513 $input['params']['TZID'] = $input['value']['tz']; 8514 unset( $input['value']['tz'] ); 8515 } 8516 } // end if( iCalUtilityFunctions::_isArrayDate( $year )) 8517 elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { 8518 if( $localtime ) unset ( $month['VALUE'], $month['TZID'] ); 8519 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); 8520 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); 8521 $hitval = 7; 8522 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno ); 8523 if( !isset( $input['params']['TZID'] ) && !empty( $tzid )) 8524 $input['params']['TZID'] = $tzid; 8525 if( isset( $year['tz'] )) { 8526 $parno = 6; 8527 if( !iCalUtilityFunctions::_isOffset( $year['tz'] )) 8528 $input['params']['TZID'] = $year['tz']; 8529 } 8530 elseif( isset( $input['params']['TZID'] )) { 8531 $year['tz'] = $input['params']['TZID']; 8532 $parno = 6; 8533 if( iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { 8534 unset( $input['params']['TZID'] ); 8535 $parno = 7; 8536 } 8537 } 8538 $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno ); 8539 } // end elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) 8540 elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone] 8541 if( $localtime ) 8542 unset( $month['VALUE'], $month['TZID'] ); 8543 elseif( !isset( $month['TZID'] ) && !empty( $tzid )) 8544 $month['TZID'] = $tzid; 8545 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); 8546 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno ); 8547 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno ); 8548 $input['value'] = iCalUtilityFunctions::_strdate2date( $year, $parno ); 8549 unset( $input['value']['unparsedtext'] ); 8550 if( isset( $input['value']['tz'] )) { 8551 if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { 8552 $d = $input['value']; 8553 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); 8554 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 8555 unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); 8556 } 8557 else { 8558 $input['params']['TZID'] = $input['value']['tz']; 8559 unset( $input['value']['tz'] ); 8560 } 8561 } 8562 elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { 8563 $d = $input['value']; 8564 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $input['params']['TZID'] ); 8565 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 8566 unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); 8567 } 8568 } // end elseif( 8 <= strlen( trim( $year ))) 8569 else { 8570 if( is_array( $params )) 8571 $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); 8572 elseif( is_array( $tz )) { 8573 $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' )); 8574 $tz = FALSE; 8575 } 8576 elseif( is_array( $hour )) { 8577 $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' )); 8578 $hour = $min = $sec = $tz = FALSE; 8579 } 8580 if( $localtime ) 8581 unset ( $input['params']['VALUE'], $input['params']['TZID'] ); 8582 elseif( !isset( $tz ) && !isset( $input['params']['TZID'] ) && !empty( $tzid )) 8583 $input['params']['TZID'] = $tzid; 8584 elseif( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) 8585 unset( $input['params']['TZID'] ); 8586 elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { 8587 $tz = $input['params']['TZID']; 8588 unset( $input['params']['TZID'] ); 8589 } 8590 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); 8591 $hitval = ( iCalUtilityFunctions::_isOffset( $tz )) ? 7 : 6; 8592 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno ); 8593 $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day ); 8594 if( 3 != $parno ) { 8595 $input['value']['hour'] = ( $hour ) ? $hour : '0'; 8596 $input['value']['min'] = ( $min ) ? $min : '0'; 8597 $input['value']['sec'] = ( $sec ) ? $sec : '0'; 8598 if( !empty( $tz )) 8599 $input['value']['tz'] = $tz; 8600 $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], $parno ); 8601 if( !empty( $tz ) && !iCalUtilityFunctions::_isOffset( $tz )) 8602 $strdate .= ( 'Z' == $tz ) ? $tz : ' '.$tz; 8603 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno ); 8604 unset( $input['value']['unparsedtext'] ); 8605 if( isset( $input['value']['tz'] )) { 8606 if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { 8607 $d = $input['value']; 8608 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); 8609 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 8610 unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); 8611 } 8612 else { 8613 $input['params']['TZID'] = $input['value']['tz']; 8614 unset( $input['value']['tz'] ); 8615 } 8616 } 8617 elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { 8618 $d = $input['value']; 8619 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $input['params']['TZID'] ); 8620 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 8621 unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); 8622 } 8623 } 8624 } // end else (i.e. using all arguments) 8625 if(( 3 == $parno ) || ( isset( $input['params']['VALUE'] ) && ( 'DATE' == $input['params']['VALUE'] ))) { 8626 $input['params']['VALUE'] = 'DATE'; 8627 unset( $input['value']['hour'], $input['value']['min'], $input['value']['sec'], $input['value']['tz'], $input['params']['TZID'] ); 8628 } 8629 elseif( isset( $input['params']['TZID'] )) { 8630 if(( 'UTC' == strtoupper( $input['params']['TZID'] )) || ( 'GMT' == strtoupper( $input['params']['TZID'] ))) { 8631 $input['value']['tz'] = 'Z'; 8632 unset( $input['params']['TZID'] ); 8633 } 8634 else 8635 unset( $input['value']['tz'] ); 8636 } 8637 elseif( isset( $input['value']['tz'] )) { 8638 if(( 'UTC' == strtoupper( $input['value']['tz'] )) || ( 'GMT' == strtoupper( $input['value']['tz'] ))) 8639 $input['value']['tz'] = 'Z'; 8640 if( 'Z' != $input['value']['tz'] ) { 8641 $input['params']['TZID'] = $input['value']['tz']; 8642 unset( $input['value']['tz'] ); 8643 } 8644 else 8645 unset( $input['params']['TZID'] ); 8646 } 8647 if( $localtime ) 8648 unset( $input['value']['tz'], $input['params']['TZID'] ); 8649 return $input; 8650 } 8651 /** 8652 * convert format for input date (UTC) to internal date with parameters 8653 * 8654 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8655 * @since 2.14.4 - 2012-10-06 8656 * @param mixed $year 8657 * @param mixed $month optional 8658 * @param int $day optional 8659 * @param int $hour optional 8660 * @param int $min optional 8661 * @param int $sec optional 8662 * @param array $params optional 8663 * @return array 8664 */ 8665 public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { 8666 $input = null; 8667 iCalUtilityFunctions::_strDate2arr( $year ); 8668 if( iCalUtilityFunctions::_isArrayDate( $year )) { 8669 $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, 7 ); 8670 if( isset( $input['value']['year'] ) && ( 100 > $input['value']['year'] )) 8671 $input['value']['year'] += 2000; 8672 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); 8673 if( isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { 8674 $d = $input['value']; 8675 $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); 8676 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 8677 unset( $input['value']['unparsedtext'] ); 8678 } 8679 } 8680 elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { 8681 $year['tz'] = 'UTC'; 8682 $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 ); 8683 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); 8684 } 8685 elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18 8686 $input['value'] = iCalUtilityFunctions::_strdate2date( $year, 7 ); 8687 unset( $input['value']['unparsedtext'] ); 8688 $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); 8689 } 8690 else { 8691 $input['value'] = array( 'year' => $year 8692 , 'month' => $month 8693 , 'day' => $day 8694 , 'hour' => $hour 8695 , 'min' => $min 8696 , 'sec' => $sec ); 8697 if( isset( $tz )) $input['value']['tz'] = $tz; 8698 if(( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) || 8699 ( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))) { 8700 if( !isset( $tz ) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) 8701 $input['value']['tz'] = $input['params']['TZID']; 8702 unset( $input['params']['TZID'] ); 8703 $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], 7 ); 8704 $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); 8705 unset( $input['value']['unparsedtext'] ); 8706 } 8707 $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); 8708 } 8709 $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default 8710 if( !isset( $input['value']['hour'] )) $input['value']['hour'] = 0; 8711 if( !isset( $input['value']['min'] )) $input['value']['min'] = 0; 8712 if( !isset( $input['value']['sec'] )) $input['value']['sec'] = 0; 8713 $input['value']['tz'] = 'Z'; 8714 return $input; 8715 } 8716 /** 8717 * check index and set (an indexed) content in multiple value array 8718 * 8719 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8720 * @since 2.6.12 - 2011-01-03 8721 * @param array $valArr 8722 * @param mixed $value 8723 * @param array $params 8724 * @param array $defaults 8725 * @param int $index 8726 * @return void 8727 */ 8728 public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) { 8729 if( !is_array( $valArr )) $valArr = array(); 8730 if( $index ) 8731 $index = $index - 1; 8732 elseif( 0 < count( $valArr )) { 8733 $keys = array_keys( $valArr ); 8734 $index = end( $keys ) + 1; 8735 } 8736 else 8737 $index = 0; 8738 $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults )); 8739 ksort( $valArr ); 8740 } 8741 /** 8742 * set input (formatted) parameters- component property attributes 8743 * 8744 * default parameters can be set, if missing 8745 * 8746 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8747 * @since 1.x.x - 2007-05-01 8748 * @param array $params 8749 * @param array $defaults 8750 * @return array 8751 */ 8752 public static function _setParams( $params, $defaults=FALSE ) { 8753 if( !is_array( $params)) 8754 $params = array(); 8755 $input = array(); 8756 foreach( $params as $paramKey => $paramValue ) { 8757 if( is_array( $paramValue )) { 8758 foreach( $paramValue as $pkey => $pValue ) { 8759 if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 ))) 8760 $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 )); 8761 } 8762 } 8763 elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 ))) 8764 $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 )); 8765 if( 'VALUE' == strtoupper( $paramKey )) 8766 $input['VALUE'] = strtoupper( $paramValue ); 8767 else 8768 $input[strtoupper( $paramKey )] = $paramValue; 8769 } 8770 if( is_array( $defaults )) { 8771 foreach( $defaults as $paramKey => $paramValue ) { 8772 if( !isset( $input[$paramKey] )) 8773 $input[$paramKey] = $paramValue; 8774 } 8775 } 8776 return (0 < count( $input )) ? $input : null; 8777 } 8778 /** 8779 * step date, return updated date, array and timpstamp 8780 * 8781 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8782 * @since 2.14.1 - 2012-09-24 8783 * @param array $date, date to step 8784 * @param int $timestamp 8785 * @param array $step, default array( 'day' => 1 ) 8786 * @return void 8787 */ 8788 public static function _stepdate( &$date, &$timestamp, $step=array( 'day' => 1 )) { 8789 if( !isset( $date['hour'] )) $date['hour'] = 0; 8790 if( !isset( $date['min'] )) $date['min'] = 0; 8791 if( !isset( $date['sec'] )) $date['sec'] = 0; 8792 foreach( $step as $stepix => $stepvalue ) 8793 $date[$stepix] += $stepvalue; 8794 $timestamp = mktime( $date['hour'], $date['min'], $date['sec'], $date['month'], $date['day'], $date['year'] ); 8795 $d = date( 'Y-m-d-H-i-s', $timestamp); 8796 $d = explode( '-', $d ); 8797 $date = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); 8798 foreach( $date as $k => $v ) 8799 $date[$k] = (int) $v; 8800 } 8801 /** 8802 * convert a date from specific string to array format 8803 * 8804 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8805 * @since 2.11.8 - 2012-01-27 8806 * @param mixed $input 8807 * @return bool, TRUE on success 8808 */ 8809 public static function _strDate2arr( & $input ) { 8810 if( is_array( $input )) 8811 return FALSE; 8812 if( 5 > strlen( (string) $input )) 8813 return FALSE; 8814 $work = $input; 8815 if( 2 == substr_count( $work, '-' )) 8816 $work = str_replace( '-', '', $work ); 8817 if( 2 == substr_count( $work, '/' )) 8818 $work = str_replace( '/', '', $work ); 8819 if( !ctype_digit( substr( $work, 0, 8 ))) 8820 return FALSE; 8821 $temp = array( 'year' => (int) substr( $work, 0, 4 ) 8822 , 'month' => (int) substr( $work, 4, 2 ) 8823 , 'day' => (int) substr( $work, 6, 2 )); 8824 if( !checkdate( $temp['month'], $temp['day'], $temp['year'] )) 8825 return FALSE; 8826 if( 8 == strlen( $work )) { 8827 $input = $temp; 8828 return TRUE; 8829 } 8830 if(( ' ' == substr( $work, 8, 1 )) || ( 'T' == substr( $work, 8, 1 )) || ( 't' == substr( $work, 8, 1 ))) 8831 $work = substr( $work, 9 ); 8832 elseif( ctype_digit( substr( $work, 8, 1 ))) 8833 $work = substr( $work, 8 ); 8834 else 8835 return FALSE; 8836 if( 2 == substr_count( $work, ':' )) 8837 $work = str_replace( ':', '', $work ); 8838 if( !ctype_digit( substr( $work, 0, 4 ))) 8839 return FALSE; 8840 $temp['hour'] = substr( $work, 0, 2 ); 8841 $temp['min'] = substr( $work, 2, 2 ); 8842 if((( 0 > $temp['hour'] ) || ( $temp['hour'] > 23 )) || 8843 (( 0 > $temp['min'] ) || ( $temp['min'] > 59 ))) 8844 return FALSE; 8845 if( ctype_digit( substr( $work, 4, 2 ))) { 8846 $temp['sec'] = substr( $work, 4, 2 ); 8847 if(( 0 > $temp['sec'] ) || ( $temp['sec'] > 59 )) 8848 return FALSE; 8849 $len = 6; 8850 } 8851 else { 8852 $temp['sec'] = 0; 8853 $len = 4; 8854 } 8855 if( $len < strlen( $work)) 8856 $temp['tz'] = trim( substr( $work, 6 )); 8857 $input = $temp; 8858 return TRUE; 8859 } 8860 /** 8861 * ensures internal date-time/date format for input date-time/date in string fromat 8862 * 8863 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8864 * @since 2.14.1 - 2012-10-07 8865 * Modified to also return original string value by Yitzchok Lavi <icalcreator@onebigsystem.com> 8866 * @param array $datetime 8867 * @param int $parno optional, default FALSE 8868 * @param moxed $wtz optional, default null 8869 * @return array 8870 */ 8871 public static function _date_time_string( $datetime, $parno=FALSE ) { 8872 return iCalUtilityFunctions::_strdate2date( $datetime, $parno, null ); 8873 } 8874 public static function _strdate2date( $datetime, $parno=FALSE, $wtz=null ) { 8875 // save original input string to return it later 8876 $unparseddatetime = $datetime; 8877 $datetime = (string) trim( $datetime ); 8878 $tz = null; 8879 $offset = 0; 8880 $tzSts = FALSE; 8881 $len = strlen( $datetime ); 8882 if( 'Z' == substr( $datetime, -1 )) { 8883 $tz = 'Z'; 8884 $datetime = trim( substr( $datetime, 0, ( $len - 1 ))); 8885 $tzSts = TRUE; 8886 $len = 88; 8887 } 8888 if( iCalUtilityFunctions::_isOffset( substr( $datetime, -5, 5 ))) { // [+/-]NNNN offset 8889 $tz = substr( $datetime, -5, 5 ); 8890 $datetime = trim( substr( $datetime, 0, ($len - 5))); 8891 $len = strlen( $datetime ); 8892 } 8893 elseif( iCalUtilityFunctions::_isOffset( substr( $datetime, -7, 7 ))) { // [+/-]NNNNNN offset 8894 $tz = substr( $datetime, -7, 7 ); 8895 $datetime = trim( substr( $datetime, 0, ($len - 7))); 8896 $len = strlen( $datetime ); 8897 } 8898 elseif( empty( $wtz ) && ctype_digit( substr( $datetime, 0, 4 )) && ctype_digit( substr( $datetime, -2, 2 )) && iCalUtilityFunctions::_strDate2arr( $datetime )) { 8899 $output = $datetime; 8900 if( !empty( $tz )) 8901 $output['tz'] = 'Z'; 8902 $output['unparsedtext'] = $unparseddatetime; 8903 return $output; 8904 } 8905 else { 8906 $cx = $tx = 0; // find any trailing timezone or offset 8907 for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) { 8908 $char = substr( $datetime, $cx, 1 ); 8909 if(( ' ' == $char) || ctype_digit( $char )) 8910 break; // if exists, tz ends here.. . ? 8911 else 8912 $tx--; // tz length counter 8913 } 8914 if( 0 > $tx ) { // if any 8915 $tz = substr( $datetime, $tx ); 8916 $datetime = trim( substr( $datetime, 0, $len + $tx )); 8917 $len = strlen( $datetime ); 8918 } 8919 if(( 17 <= $len ) || // long textual datetime 8920 ( ctype_digit( substr( $datetime, 0, 8 )) && ( 'T' == substr( $datetime, 8, 1 )) && ctype_digit( substr( $datetime, -6, 6 ))) || 8921 ( ctype_digit( substr( $datetime, 0, 14 )))) { 8922 $len = 88; 8923 $tzSts = TRUE; 8924 } 8925 else 8926 $tz = null; // no tz for Y-m-d dates 8927 } 8928 if( empty( $tz ) && !empty( $wtz )) 8929 $tz = $wtz; 8930 if( 17 >= $len ) // any Y-m-d textual date 8931 $tz = null; 8932 if( !empty( $tz ) && ( 17 < $len )) { // tz set AND long textual datetime 8933 if(( 'Z' != $tz ) && ( iCalUtilityFunctions::_isOffset( $tz ))) { 8934 $offset = (string) iCalUtilityFunctions::_tz2offset( $tz ) * -1; 8935 $tz = 'UTC'; 8936 $tzSts = TRUE; 8937 } 8938 elseif( !empty( $wtz )) 8939 $tzSts = TRUE; 8940 $tz = trim( $tz ); 8941 if(( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz ))) 8942 $tz = 'UTC'; 8943 if( 0 < substr_count( $datetime, '-' )) 8944 $datetime = str_replace( '-', '/', $datetime ); 8945 try { 8946 $d = new DateTime( $datetime, new DateTimeZone( $tz )); 8947 if( 0 != $offset ) // adjust for offset 8948 $d->modify( $offset.' seconds' ); 8949 $datestring = $d->format( 'Y-m-d-H-i-s' ); 8950 unset( $d ); 8951 } 8952 catch( Exception $e ) { 8953 $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime )); 8954 } 8955 } // end if( !empty( $tz ) && ( 17 < $len )) 8956 else 8957 $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime )); 8958 // echo "<tr><td> <td colspan='3'>_strdate2date input=$datetime, tz=$tz, offset=$offset, wtz=$wtz, len=$len, prepDate=$datestring\n"; 8959 if( 'UTC' == $tz ) 8960 $tz = 'Z'; 8961 $d = explode( '-', $datestring ); 8962 $output = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2] ); 8963 if((( FALSE !== $parno ) && ( 3 != $parno )) || // parno is set to 6 or 7 8964 (( FALSE === $parno ) && ( 'Z' == $tz )) || // parno is not set and UTC 8965 (( 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 8966 $output['hour'] = $d[3]; 8967 $output['min'] = $d[4]; 8968 $output['sec'] = $d[5]; 8969 if(( $tzSts || ( 7 == $parno )) && !empty( $tz )) 8970 $output['tz'] = $tz; 8971 } 8972 // return original string in the array in case strtotime failed to make sense of it 8973 $output['unparsedtext'] = $unparseddatetime; 8974 return $output; 8975 } 8976 /** 8977 * convert timestamp to date array, default UTC or adjusted for offset/timezone 8978 * 8979 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 8980 * @since 2.15.1 - 2012-10-17 8981 * @param mixed $timestamp 8982 * @param int $parno 8983 * @param string $wtz 8984 * @return array 8985 */ 8986 public static function _timestamp2date( $timestamp, $parno=6, $wtz=null ) { 8987 if( is_array( $timestamp )) { 8988 $tz = ( isset( $timestamp['tz'] )) ? $timestamp['tz'] : $wtz; 8989 $timestamp = $timestamp['timestamp']; 8990 } 8991 $tz = ( isset( $tz )) ? $tz : $wtz; 8992 if( empty( $tz ) || ( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz ))) 8993 $tz = 'UTC'; 8994 elseif( iCalUtilityFunctions::_isOffset( $tz )) { 8995 $offset = iCalUtilityFunctions::_tz2offset( $tz ); 8996 $tz = 'UTC'; 8997 } 8998 try { 8999 $d = new DateTime( "@$timestamp" ); // set UTC date 9000 if( isset( $offset ) && ( 0 != $offset )) // adjust for offset 9001 $d->modify( $offset.' seconds' ); 9002 elseif( 'UTC' != $tz ) 9003 $d->setTimezone( new DateTimeZone( $tz )); // convert to local date 9004 $date = $d->format( 'Y-m-d-H-i-s' ); 9005 unset( $d ); 9006 } 9007 catch( Exception $e ) { 9008 $date = date( 'Y-m-d-H-i-s', $timestamp ); 9009 } 9010 $date = explode( '-', $date ); 9011 $output = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2] ); 9012 if( 3 != $parno ) { 9013 $output['hour'] = $date[3]; 9014 $output['min'] = $date[4]; 9015 $output['sec'] = $date[5]; 9016 if( 'UTC' == $tz && ( !isset( $offset ) || ( 0 == $offset ))) 9017 $output['tz'] = 'Z'; 9018 } 9019 return $output; 9020 } 9021 /** 9022 * convert timestamp (seconds) to duration in array format 9023 * 9024 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9025 * @since 2.6.23 - 2010-10-23 9026 * @param int $timestamp 9027 * @return array, duration format 9028 */ 9029 public static function _timestamp2duration( $timestamp ) { 9030 $dur = array(); 9031 $dur['week'] = (int) floor( $timestamp / ( 7 * 24 * 60 * 60 )); 9032 $timestamp = $timestamp % ( 7 * 24 * 60 * 60 ); 9033 $dur['day'] = (int) floor( $timestamp / ( 24 * 60 * 60 )); 9034 $timestamp = $timestamp % ( 24 * 60 * 60 ); 9035 $dur['hour'] = (int) floor( $timestamp / ( 60 * 60 )); 9036 $timestamp = $timestamp % ( 60 * 60 ); 9037 $dur['min'] = (int) floor( $timestamp / ( 60 )); 9038 $dur['sec'] = (int) $timestamp % ( 60 ); 9039 return $dur; 9040 } 9041 /** 9042 * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0) 9043 * 9044 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9045 * @since 2.15.1 - 2012-10-17 9046 * @param mixed $date, date to alter 9047 * @param string $tzFrom, PHP valid 'from' timezone 9048 * @param string $tzTo, PHP valid 'to' timezone, default 'UTC' 9049 * @param string $format, date output format, default 'Ymd\THis' 9050 * @return bool 9051 */ 9052 public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) { 9053 if( is_array( $date ) && isset( $date['timestamp'] )) { 9054 try { 9055 $d = new DateTime( "@{$date['timestamp']}" ); // set UTC date 9056 $d->setTimezone(new DateTimeZone( $tzFrom )); // convert to 'from' date 9057 } 9058 catch( Exception $e ) { return FALSE; } 9059 } 9060 else { 9061 if( iCalUtilityFunctions::_isArrayDate( $date )) { 9062 if( isset( $date['tz'] )) 9063 unset( $date['tz'] ); 9064 $date = iCalUtilityFunctions::_date2strdate( iCalUtilityFunctions::_chkDateArr( $date )); 9065 } 9066 if( 'Z' == substr( $date, -1 )) 9067 $date = substr( $date, 0, ( strlen( $date ) - 2 )); 9068 try { $d = new DateTime( $date, new DateTimeZone( $tzFrom )); } 9069 catch( Exception $e ) { return FALSE; } 9070 } 9071 try { $d->setTimezone( new DateTimeZone( $tzTo )); } 9072 catch( Exception $e ) { return FALSE; } 9073 $date = $d->format( $format ); 9074 return TRUE; 9075 } 9076 /** 9077 * convert offset, [+/-]HHmm[ss], to seconds used when correcting UTC to localtime or v.v. 9078 * 9079 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9080 * @since 2.11.4 - 2012-01-11 9081 * @param string $offset 9082 * @return integer 9083 */ 9084 public static function _tz2offset( $tz ) { 9085 $tz = trim( (string) $tz ); 9086 $offset = 0; 9087 if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) || 9088 (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) || 9089 (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) || 9090 (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 )))) 9091 return $offset; 9092 $hours2sec = (int) substr( $tz, 1, 2 ) * 3600; 9093 $min2sec = (int) substr( $tz, 3, 2 ) * 60; 9094 $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00'; 9095 $offset = $hours2sec + $min2sec + $sec; 9096 $offset = ('-' == substr( $tz, 0, 1 )) ? $offset * -1 : $offset; 9097 return $offset; 9098 } 9099 } 9100 /*********************************************************************************/ 9101 /* iCalcreator vCard helper functions */ 9102 /*********************************************************************************/ 9103 /** 9104 * convert single ATTENDEE, CONTACT or ORGANIZER (in email format) to vCard 9105 * returns vCard/TRUE or if directory (if set) or file write is unvalid, FALSE 9106 * 9107 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9108 * @since 2.12.2 - 2012-07-11 9109 * @param object $email 9110 * $param string $version, vCard version (default 2.1) 9111 * $param string $directory, where to save vCards (default FALSE) 9112 * $param string $ext, vCard file extension (default 'vcf') 9113 * @return mixed 9114 */ 9115 function iCal2vCard( $email, $version='2.1', $directory=FALSE, $ext='vcf' ) { 9116 if( FALSE === ( $pos = strpos( $email, '@' ))) 9117 return FALSE; 9118 if( $directory ) { 9119 if( DIRECTORY_SEPARATOR != substr( $directory, ( 0 - strlen( DIRECTORY_SEPARATOR )))) 9120 $directory .= DIRECTORY_SEPARATOR; 9121 if( !is_dir( $directory ) || !is_writable( $directory )) 9122 return FALSE; 9123 } 9124 /* prepare vCard */ 9125 $email = str_replace( 'MAILTO:', '', $email ); 9126 $name = $person = substr( $email, 0, $pos ); 9127 if( ctype_upper( $name ) || ctype_lower( $name )) 9128 $name = array( $name ); 9129 else { 9130 if( FALSE !== ( $pos = strpos( $name, '.' ))) { 9131 $name = explode( '.', $name ); 9132 foreach( $name as $k => $part ) 9133 $name[$k] = ucfirst( $part ); 9134 } 9135 else { // split camelCase 9136 $chars = $name; 9137 $name = array( $chars[0] ); 9138 $k = 0; 9139 $x = 1; 9140 while( FALSE !== ( $char = substr( $chars, $x, 1 ))) { 9141 if( ctype_upper( $char )) { 9142 $k += 1; 9143 $name[$k] = ''; 9144 } 9145 $name[$k] .= $char; 9146 $x++; 9147 } 9148 } 9149 } 9150 $nl = "\r\n"; 9151 $FN = 'FN:'.implode( ' ', $name ).$nl; 9152 $name = array_reverse( $name ); 9153 $N = 'N:'.array_shift( $name ); 9154 $scCnt = 0; 9155 while( NULL != ( $part = array_shift( $name ))) { 9156 if(( '4.0' != $version ) || ( 4 > $scCnt )) 9157 $scCnt += 1; 9158 $N .= ';'.$part; 9159 } 9160 while(( '4.0' == $version ) && ( 4 > $scCnt )) { 9161 $N .= ';'; 9162 $scCnt += 1; 9163 } 9164 $N .= $nl; 9165 $EMAIL = 'EMAIL:'.$email.$nl; 9166 /* create vCard */ 9167 $vCard = 'BEGIN:VCARD'.$nl; 9168 $vCard .= "VERSION:$version$nl"; 9169 $vCard .= 'PRODID:-//kigkonsult.se '.ICALCREATOR_VERSION."//$nl"; 9170 $vCard .= $N; 9171 $vCard .= $FN; 9172 $vCard .= $EMAIL; 9173 $vCard .= 'REV:'.gmdate( 'Ymd\THis\Z' ).$nl; 9174 $vCard .= 'END:VCARD'.$nl; 9175 /* save each vCard as (unique) single file */ 9176 if( $directory ) { 9177 $fname = $directory.preg_replace( '/[^a-z0-9.]/i', '', $email ); 9178 $cnt = 1; 9179 $dbl = ''; 9180 while( is_file ( $fname.$dbl.'.'.$ext )) { 9181 $cnt += 1; 9182 $dbl = "_$cnt"; 9183 } 9184 if( FALSE === file_put_contents( $fname, $fname.$dbl.'.'.$ext )) 9185 return FALSE; 9186 return TRUE; 9187 } 9188 /* return vCard */ 9189 else 9190 return $vCard; 9191 } 9192 /** 9193 * convert ATTENDEEs, CONTACTs and ORGANIZERs (in email format) to vCards 9194 * 9195 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9196 * @since 2.12.2 - 2012-05-07 9197 * @param object $calendar, iCalcreator vcalendar instance reference 9198 * $param string $version, vCard version (default 2.1) 9199 * $param string $directory, where to save vCards (default FALSE) 9200 * $param string $ext, vCard file extension (default 'vcf') 9201 * @return mixed 9202 */ 9203 function iCal2vCards( & $calendar, $version='2.1', $directory=FALSE, $ext='vcf' ) { 9204 $hits = array(); 9205 $vCardP = array( 'ATTENDEE', 'CONTACT', 'ORGANIZER' ); 9206 foreach( $vCardP as $prop ) { 9207 $hits2 = $calendar->getProperty( $prop ); 9208 foreach( $hits2 as $propValue => $occCnt ) { 9209 if( FALSE === ( $pos = strpos( $propValue, '@' ))) 9210 continue; 9211 $propValue = str_replace( 'MAILTO:', '', $propValue ); 9212 if( isset( $hits[$propValue] )) 9213 $hits[$propValue] += $occCnt; 9214 else 9215 $hits[$propValue] = $occCnt; 9216 } 9217 } 9218 if( empty( $hits )) 9219 return FALSE; 9220 ksort( $hits ); 9221 $output = ''; 9222 foreach( $hits as $email => $skip ) { 9223 $res = iCal2vCard( $email, $version, $directory, $ext ); 9224 if( $directory && !$res ) 9225 return FALSE; 9226 elseif( !$res ) 9227 return $res; 9228 else 9229 $output .= $res; 9230 } 9231 if( $directory ) 9232 return TRUE; 9233 if( !empty( $output )) 9234 return $output; 9235 return FALSE; 9236 } 9237 /*********************************************************************************/ 9238 /* iCalcreator XML (rfc6321) helper functions */ 9239 /*********************************************************************************/ 9240 /** 9241 * format iCal XML output, rfc6321, using PHP SimpleXMLElement 9242 * 9243 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9244 * @since 2.15.6 - 2012-10-19 9245 * @param object $calendar, iCalcreator vcalendar instance reference 9246 * @return string 9247 */ 9248 function iCal2XML( & $calendar ) { 9249 /** fix an SimpleXMLElement instance and create root element */ 9250 $xmlstr = '<?xml version="1.0" encoding="utf-8"?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0">'; 9251 $xmlstr .= '<!-- created utilizing kigkonsult.se '.ICALCREATOR_VERSION.' iCal2XMl (rfc6321) -->'; 9252 $xmlstr .= '</icalendar>'; 9253 $xml = new SimpleXMLElement( $xmlstr ); 9254 $vcalendar = $xml->addChild( 'vcalendar' ); 9255 /** fix calendar properties */ 9256 $properties = $vcalendar->addChild( 'properties' ); 9257 $calProps = array( 'prodid', 'version', 'calscale', 'method' ); 9258 foreach( $calProps as $calProp ) { 9259 if( FALSE !== ( $content = $calendar->getProperty( $calProp ))) 9260 _addXMLchild( $properties, $calProp, 'text', $content ); 9261 } 9262 while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE ))) 9263 _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); 9264 $langCal = $calendar->getConfig( 'language' ); 9265 /** prepare to fix components with properties */ 9266 $components = $vcalendar->addChild( 'components' ); 9267 $comps = array( 'vtimezone', 'vevent', 'vtodo', 'vjournal', 'vfreebusy' ); 9268 foreach( $comps as $compName ) { 9269 switch( $compName ) { 9270 case 'vevent': 9271 case 'vtodo': 9272 $subComps = array( 'valarm' ); 9273 break; 9274 case 'vjournal': 9275 case 'vfreebusy': 9276 $subComps = array(); 9277 break; 9278 case 'vtimezone': 9279 $subComps = array( 'standard', 'daylight' ); 9280 break; 9281 } // end switch( $compName ) 9282 /** fix component properties */ 9283 while( FALSE !== ( $component = $calendar->getComponent( $compName ))) { 9284 $child = $components->addChild( $compName ); 9285 $properties = $child->addChild( 'properties' ); 9286 $langComp = $component->getConfig( 'language' ); 9287 $props = $component->getConfig( 'setPropertyNames' ); 9288 foreach( $props as $prop ) { 9289 switch( strtolower( $prop )) { 9290 case 'attach': // may occur multiple times, below 9291 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9292 $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; 9293 unset( $content['params']['VALUE'] ); 9294 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); 9295 } 9296 break; 9297 case 'attendee': 9298 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9299 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { 9300 if( $langComp ) 9301 $content['params']['LANGUAGE'] = $langComp; 9302 elseif( $langCal ) 9303 $content['params']['LANGUAGE'] = $langCal; 9304 } 9305 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); 9306 } 9307 break; 9308 case 'exdate': 9309 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9310 $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; 9311 unset( $content['params']['VALUE'] ); 9312 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); 9313 } 9314 break; 9315 case 'freebusy': 9316 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9317 if( is_array( $content ) && isset( $content['value']['fbtype'] )) { 9318 $content['params']['FBTYPE'] = $content['value']['fbtype']; 9319 unset( $content['value']['fbtype'] ); 9320 } 9321 _addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] ); 9322 } 9323 break; 9324 case 'request-status': 9325 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9326 if( !isset( $content['params']['LANGUAGE'] )) { 9327 if( $langComp ) 9328 $content['params']['LANGUAGE'] = $langComp; 9329 elseif( $langCal ) 9330 $content['params']['LANGUAGE'] = $langCal; 9331 } 9332 _addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] ); 9333 } 9334 break; 9335 case 'rdate': 9336 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9337 $type = 'date-time'; 9338 if( isset( $content['params']['VALUE'] )) { 9339 if( 'DATE' == $content['params']['VALUE'] ) 9340 $type = 'date'; 9341 elseif( 'PERIOD' == $content['params']['VALUE'] ) 9342 $type = 'period'; 9343 } 9344 unset( $content['params']['VALUE'] ); 9345 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); 9346 } 9347 break; 9348 case 'categories': 9349 case 'comment': 9350 case 'contact': 9351 case 'description': 9352 case 'related-to': 9353 case 'resources': 9354 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9355 if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { 9356 if( $langComp ) 9357 $content['params']['LANGUAGE'] = $langComp; 9358 elseif( $langCal ) 9359 $content['params']['LANGUAGE'] = $langCal; 9360 } 9361 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); 9362 } 9363 break; 9364 case 'x-prop': 9365 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) 9366 _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); 9367 break; 9368 case 'created': // single occurence below, if set 9369 case 'completed': 9370 case 'dtstamp': 9371 case 'last-modified': 9372 $utcDate = TRUE; 9373 case 'dtstart': 9374 case 'dtend': 9375 case 'due': 9376 case 'recurrence-id': 9377 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9378 $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; 9379 unset( $content['params']['VALUE'] ); 9380 if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] )) 9381 unset( $content['params']['TZID'] ); 9382 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); 9383 } 9384 unset( $utcDate ); 9385 break; 9386 case 'duration': 9387 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9388 if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] )) 9389 $content['params']['RELATED'] = 'END'; 9390 _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); 9391 } 9392 break; 9393 case 'rrule': 9394 while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) 9395 _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); 9396 break; 9397 case 'class': 9398 case 'location': 9399 case 'status': 9400 case 'summary': 9401 case 'transp': 9402 case 'tzid': 9403 case 'uid': 9404 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9405 if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) { 9406 if( $langComp ) 9407 $content['params']['LANGUAGE'] = $langComp; 9408 elseif( $langCal ) 9409 $content['params']['LANGUAGE'] = $langCal; 9410 } 9411 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); 9412 } 9413 break; 9414 case 'geo': 9415 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) 9416 _addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] ); 9417 break; 9418 case 'organizer': 9419 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { 9420 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { 9421 if( $langComp ) 9422 $content['params']['LANGUAGE'] = $langComp; 9423 elseif( $langCal ) 9424 $content['params']['LANGUAGE'] = $langCal; 9425 } 9426 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); 9427 } 9428 break; 9429 case 'percent-complete': 9430 case 'priority': 9431 case 'sequence': 9432 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) 9433 _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); 9434 break; 9435 case 'tzurl': 9436 case 'url': 9437 if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) 9438 _addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] ); 9439 break; 9440 } // end switch( $prop ) 9441 } // end foreach( $props as $prop ) 9442 /** fix subComponent properties, if any */ 9443 foreach( $subComps as $subCompName ) { 9444 while( FALSE !== ( $subcomp = $component->getComponent( $subCompName ))) { 9445 $child2 = $child->addChild( $subCompName ); 9446 $properties = $child2->addChild( 'properties' ); 9447 $langComp = $subcomp->getConfig( 'language' ); 9448 $subCompProps = $subcomp->getConfig( 'setPropertyNames' ); 9449 foreach( $subCompProps as $prop ) { 9450 switch( strtolower( $prop )) { 9451 case 'attach': // may occur multiple times, below 9452 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { 9453 $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; 9454 unset( $content['params']['VALUE'] ); 9455 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); 9456 } 9457 break; 9458 case 'attendee': 9459 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { 9460 if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { 9461 if( $langComp ) 9462 $content['params']['LANGUAGE'] = $langComp; 9463 elseif( $langCal ) 9464 $content['params']['LANGUAGE'] = $langCal; 9465 } 9466 _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); 9467 } 9468 break; 9469 case 'comment': 9470 case 'tzname': 9471 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { 9472 if( !isset( $content['params']['LANGUAGE'] )) { 9473 if( $langComp ) 9474 $content['params']['LANGUAGE'] = $langComp; 9475 elseif( $langCal ) 9476 $content['params']['LANGUAGE'] = $langCal; 9477 } 9478 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); 9479 } 9480 break; 9481 case 'rdate': 9482 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { 9483 $type = 'date-time'; 9484 if( isset( $content['params']['VALUE'] )) { 9485 if( 'DATE' == $content['params']['VALUE'] ) 9486 $type = 'date'; 9487 elseif( 'PERIOD' == $content['params']['VALUE'] ) 9488 $type = 'period'; 9489 } 9490 unset( $content['params']['VALUE'] ); 9491 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); 9492 } 9493 break; 9494 case 'x-prop': 9495 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) 9496 _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); 9497 break; 9498 case 'action': // single occurence below, if set 9499 case 'description': 9500 case 'summary': 9501 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { 9502 if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { 9503 if( $langComp ) 9504 $content['params']['LANGUAGE'] = $langComp; 9505 elseif( $langCal ) 9506 $content['params']['LANGUAGE'] = $langCal; 9507 } 9508 _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); 9509 } 9510 break; 9511 case 'dtstart': 9512 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { 9513 unset( $content['value']['tz'], $content['params']['VALUE'] ); // always local time 9514 _addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] ); 9515 } 9516 break; 9517 case 'duration': 9518 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) 9519 _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); 9520 break; 9521 case 'repeat': 9522 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) 9523 _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); 9524 break; 9525 case 'trigger': 9526 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { 9527 if( isset( $content['value']['year'] ) && 9528 isset( $content['value']['month'] ) && 9529 isset( $content['value']['day'] )) 9530 $type = 'date-time'; 9531 else { 9532 $type = 'duration'; 9533 if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] )) 9534 $content['params']['RELATED'] = 'END'; 9535 } 9536 _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); 9537 } 9538 break; 9539 case 'tzoffsetto': 9540 case 'tzoffsetfrom': 9541 if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) 9542 _addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] ); 9543 break; 9544 case 'rrule': 9545 while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) 9546 _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); 9547 break; 9548 } // switch( $prop ) 9549 } // end foreach( $subCompProps as $prop ) 9550 } // end while( FALSE !== ( $subcomp = $component->getComponent( subCompName ))) 9551 } // end foreach( $subCombs as $subCompName ) 9552 } // end while( FALSE !== ( $component = $calendar->getComponent( $compName ))) 9553 } // end foreach( $comps as $compName) 9554 return $xml->asXML(); 9555 } 9556 /** 9557 * Add children to a SimpleXMLelement 9558 * 9559 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9560 * @since 2.15.5 - 2012-10-19 9561 * @param object $parent, reference to a SimpleXMLelement node 9562 * @param string $name, new element node name 9563 * @param string $type, content type, subelement(-s) name 9564 * @param string $content, new subelement content 9565 * @param array $params, new element 'attributes' 9566 * @return void 9567 */ 9568 function _addXMLchild( & $parent, $name, $type, $content, $params=array()) { 9569 /** create new child node */ 9570 $name = strtolower( $name ); 9571 $child = $parent->addChild( $name ); 9572 if( isset( $params['VALUE'] )) 9573 unset( $params['VALUE'] ); 9574 if( !empty( $params )) { 9575 $parameters = $child->addChild( 'parameters' ); 9576 foreach( $params as $param => $parVal ) { 9577 $param = strtolower( $param ); 9578 if( 'x-' == substr( $param, 0, 2 )) { 9579 $p1 = $parameters->addChild( $param ); 9580 $p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal )); 9581 } 9582 else { 9583 $p1 = $parameters->addChild( $param ); 9584 switch( $param ) { 9585 case 'altrep': 9586 case 'dir': $ptype = 'uri'; break; 9587 case 'delegated-from': 9588 case 'delegated-to': 9589 case 'member': 9590 case 'sent-by': $ptype = 'cal-address'; break; 9591 case 'rsvp': $ptype = 'boolean'; break ; 9592 default: $ptype = 'text'; break; 9593 } 9594 if( is_array( $parVal )) { 9595 foreach( $parVal as $pV ) 9596 $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV )); 9597 } 9598 else 9599 $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal )); 9600 } 9601 } 9602 } 9603 if( empty( $content ) && ( '0' != $content )) 9604 return; 9605 /** store content */ 9606 switch( $type ) { 9607 case 'binary': 9608 $v = $child->addChild( $type, $content ); 9609 break; 9610 case 'boolean': 9611 break; 9612 case 'cal-address': 9613 $v = $child->addChild( $type, $content ); 9614 break; 9615 case 'date': 9616 if( array_key_exists( 'year', $content )) 9617 $content = array( $content ); 9618 foreach( $content as $date ) { 9619 $str = sprintf( '%04d-%02d-%02d', $date['year'], $date['month'], $date['day'] ); 9620 $v = $child->addChild( $type, $str ); 9621 } 9622 break; 9623 case 'date-time': 9624 if( array_key_exists( 'year', $content )) 9625 $content = array( $content ); 9626 foreach( $content as $dt ) { 9627 if( !isset( $dt['hour'] )) $dt['hour'] = 0; 9628 if( !isset( $dt['min'] )) $dt['min'] = 0; 9629 if( !isset( $dt['sec'] )) $dt['sec'] = 0; 9630 $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] ); 9631 if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] )) 9632 $str .= 'Z'; 9633 $v = $child->addChild( $type, $str ); 9634 } 9635 break; 9636 case 'duration': 9637 $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : ''; 9638 $v = $child->addChild( $type, $output.iCalUtilityFunctions::_duration2str( $content ) ); 9639 break; 9640 case 'geo': 9641 $v1 = $child->addChild( 'latitude', number_format( (float) $content['latitude'], 6, '.', '' )); 9642 $v1 = $child->addChild( 'longitude', number_format( (float) $content['longitude'], 6, '.', '' )); 9643 break; 9644 case 'integer': 9645 $v = $child->addChild( $type, $content ); 9646 break; 9647 case 'period': 9648 if( !is_array( $content )) 9649 break; 9650 foreach( $content as $period ) { 9651 $v1 = $child->addChild( $type ); 9652 $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'] ); 9653 if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] )) 9654 $str .= 'Z'; 9655 $v2 = $v1->addChild( 'start', $str ); 9656 if( array_key_exists( 'year', $period[1] )) { 9657 $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'] ); 9658 if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] )) 9659 $str .= 'Z'; 9660 $v2 = $v1->addChild( 'end', $str ); 9661 } 9662 else 9663 $v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_duration2str( $period[1] )); 9664 } 9665 break; 9666 case 'recur': 9667 foreach( $content as $rulelabel => $rulevalue ) { 9668 $rulelabel = strtolower( $rulelabel ); 9669 switch( $rulelabel ) { 9670 case 'until': 9671 if( isset( $rulevalue['hour'] )) 9672 $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02dZ', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'], $rulevalue['hour'], $rulevalue['min'], $rulevalue['sec'] ); 9673 else 9674 $str = sprintf( '%04d-%02d-%02d', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'] ); 9675 $v = $child->addChild( $rulelabel, $str ); 9676 break; 9677 case 'bysecond': 9678 case 'byminute': 9679 case 'byhour': 9680 case 'bymonthday': 9681 case 'byyearday': 9682 case 'byweekno': 9683 case 'bymonth': 9684 case 'bysetpos': { 9685 if( is_array( $rulevalue )) { 9686 foreach( $rulevalue as $vix => $valuePart ) 9687 $v = $child->addChild( $rulelabel, $valuePart ); 9688 } 9689 else 9690 $v = $child->addChild( $rulelabel, $rulevalue ); 9691 break; 9692 } 9693 case 'byday': { 9694 if( isset( $rulevalue['DAY'] )) { 9695 $str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : ''; 9696 $str .= $rulevalue['DAY']; 9697 $p = $child->addChild( $rulelabel, $str ); 9698 } 9699 else { 9700 foreach( $rulevalue as $valuePart ) { 9701 if( isset( $valuePart['DAY'] )) { 9702 $str = ( isset( $valuePart[0] )) ? $valuePart[0] : ''; 9703 $str .= $valuePart['DAY']; 9704 $p = $child->addChild( $rulelabel, $str ); 9705 } 9706 else 9707 $p = $child->addChild( $rulelabel, $valuePart ); 9708 } 9709 } 9710 break; 9711 } 9712 case 'freq': 9713 case 'count': 9714 case 'interval': 9715 case 'wkst': 9716 default: 9717 $p = $child->addChild( $rulelabel, $rulevalue ); 9718 break; 9719 } // end switch( $rulelabel ) 9720 } // end foreach( $content as $rulelabel => $rulevalue ) 9721 break; 9722 case 'rstatus': 9723 $v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', '')); 9724 $v = $child->addChild( 'description', htmlspecialchars( $content['text'] )); 9725 if( isset( $content['extdata'] )) 9726 $v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] )); 9727 break; 9728 case 'text': 9729 if( !is_array( $content )) 9730 $content = array( $content ); 9731 foreach( $content as $part ) 9732 $v = $child->addChild( $type, htmlspecialchars( $part )); 9733 break; 9734 case 'time': 9735 break; 9736 case 'uri': 9737 $v = $child->addChild( $type, $content ); 9738 break; 9739 case 'utc-offset': 9740 if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) { 9741 $str = substr( $content, 0, 1 ); 9742 $content = substr( $content, 1 ); 9743 } 9744 else 9745 $str = '+'; 9746 $str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 ); 9747 if( 4 < strlen( $content )) 9748 $str .= ':'.substr( $content, 4 ); 9749 $v = $child->addChild( $type, $str ); 9750 break; 9751 case 'unknown': 9752 default: 9753 if( is_array( $content )) 9754 $content = implode( '', $content ); 9755 $v = $child->addChild( 'unknown', htmlspecialchars( $content )); 9756 break; 9757 } 9758 } 9759 /** 9760 * parse xml string into iCalcreator instance 9761 * 9762 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9763 * @since 2.11.2 - 2012-01-31 9764 * @param string $xmlstr 9765 * @param array $iCalcfg iCalcreator config array (opt) 9766 * @return mixed iCalcreator instance or FALSE on error 9767 */ 9768 function & XMLstr2iCal( $xmlstr, $iCalcfg=array()) { 9769 libxml_use_internal_errors( TRUE ); 9770 $xml = simplexml_load_string( $xmlstr ); 9771 if( !$xml ) { 9772 $str = ''; 9773 $return = FALSE; 9774 foreach( libxml_get_errors() as $error ) { 9775 switch ( $error->level ) { 9776 case LIBXML_ERR_FATAL: $str .= ' FATAL '; break; 9777 case LIBXML_ERR_ERROR: $str .= ' ERROR '; break; 9778 case LIBXML_ERR_WARNING: 9779 default: $str .= ' WARNING '; break; 9780 } 9781 $str .= PHP_EOL.'Error when loading XML'; 9782 if( !empty( $error->file )) 9783 $str .= ', file:'.$error->file.', '; 9784 $str .= ', line:'.$error->line; 9785 $str .= ', ('.$error->code.') '.$error->message; 9786 } 9787 error_log( $str ); 9788 if( LIBXML_ERR_WARNING != $error->level ) 9789 return $return; 9790 libxml_clear_errors(); 9791 } 9792 return xml2iCal( $xml, $iCalcfg ); 9793 } 9794 /** 9795 * parse xml file into iCalcreator instance 9796 * 9797 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9798 * @since 2.11.2 - 2012-01-20 9799 * @param string $xmlfile 9800 * @param array$iCalcfg iCalcreator config array (opt) 9801 * @return mixediCalcreator instance or FALSE on error 9802 */ 9803 function & XMLfile2iCal( $xmlfile, $iCalcfg=array()) { 9804 libxml_use_internal_errors( TRUE ); 9805 $xml = simplexml_load_file( $xmlfile ); 9806 if( !$xml ) { 9807 $str = ''; 9808 foreach( libxml_get_errors() as $error ) { 9809 switch ( $error->level ) { 9810 case LIBXML_ERR_FATAL: $str .= 'FATAL '; break; 9811 case LIBXML_ERR_ERROR: $str .= 'ERROR '; break; 9812 case LIBXML_ERR_WARNING: 9813 default: $str .= 'WARNING '; break; 9814 } 9815 $str .= 'Failed loading XML'.PHP_EOL; 9816 if( !empty( $error->file )) 9817 $str .= ' file:'.$error->file.', '; 9818 $str .= 'line:'.$error->line.PHP_EOL; 9819 $str .= '('.$error->code.') '.$error->message.PHP_EOL; 9820 } 9821 error_log( $str ); 9822 if( LIBXML_ERR_WARNING != $error->level ) 9823 return FALSE; 9824 libxml_clear_errors(); 9825 } 9826 return xml2iCal( $xml, $iCalcfg ); 9827 } 9828 /** 9829 * parse SimpleXMLElement instance into iCalcreator instance 9830 * 9831 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9832 * @since 2.11.2 - 2012-01-27 9833 * @param object $xmlobj SimpleXMLElement 9834 * @param array $iCalcfg iCalcreator config array (opt) 9835 * @return mixed iCalcreator instance or FALSE on error 9836 */ 9837 function & XML2iCal( $xmlobj, $iCalcfg=array()) { 9838 $iCal = new vcalendar( $iCalcfg ); 9839 foreach( $xmlobj->children() as $icalendar ) { // vcalendar 9840 foreach( $icalendar->children() as $calPart ) { // calendar properties and components 9841 if( 'components' == $calPart->getName()) { 9842 foreach( $calPart->children() as $component ) { // single components 9843 if( 0 < $component->count()) 9844 _getXMLComponents( $iCal, $component ); 9845 } 9846 } 9847 elseif(( 'properties' == $calPart->getName()) && ( 0 < $calPart->count())) { 9848 foreach( $calPart->children() as $calProp ) { // calendar properties 9849 $propName = $calProp->getName(); 9850 if(( 'calscale' != $propName ) && ( 'method' != $propName ) && ( 'x-' != substr( $propName,0,2 ))) 9851 continue; 9852 $params = array(); 9853 foreach( $calProp->children() as $calPropElem ) { // single calendar property 9854 if( 'parameters' == $calPropElem->getName()) 9855 $params = _getXMLParams( $calPropElem ); 9856 else 9857 $iCal->setProperty( $propName, reset( $calPropElem ), $params ); 9858 } // end foreach( $calProp->children() as $calPropElem ) 9859 } // end foreach( $calPart->properties->children() as $calProp ) 9860 } // end if( 0 < $calPart->properties->count()) 9861 } // end foreach( $icalendar->children() as $calPart ) 9862 } // end foreach( $xmlobj->children() as $icalendar ) 9863 return $iCal; 9864 } 9865 /** 9866 * parse SimpleXMLElement instance property parameters and return iCalcreator property parameter array 9867 * 9868 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9869 * @since 2.11.2 - 2012-01-15 9870 * @param object $parameters SimpleXMLElement 9871 * @return array iCalcreator property parameter array 9872 */ 9873 function _getXMLParams( & $parameters ) { 9874 if( 1 > $parameters->count()) 9875 return array(); 9876 $params = array(); 9877 foreach( $parameters->children() as $parameter ) { // single parameter key 9878 $key = strtoupper( $parameter->getName()); 9879 $value = array(); 9880 foreach( $parameter->children() as $paramValue ) // skip parameter value type 9881 $value[] = reset( $paramValue ); 9882 if( 2 > count( $value )) 9883 $params[$key] = html_entity_decode( reset( $value )); 9884 else 9885 $params[$key] = $value; 9886 } 9887 return $params; 9888 } 9889 /** 9890 * parse SimpleXMLElement instance components, create iCalcreator component and update 9891 * 9892 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9893 * @since 2.11.2 - 2012-01-15 9894 * @param array $iCal iCalcreator calendar instance 9895 * @param object $component SimpleXMLElement 9896 * @return void 9897 */ 9898 function _getXMLComponents( & $iCal, & $component ) { 9899 $compName = $component->getName(); 9900 $comp = & $iCal->newComponent( $compName ); 9901 $subComponents = array( 'valarm', 'standard', 'daylight' ); 9902 foreach( $component->children() as $compPart ) { // properties and (opt) subComponents 9903 if( 1 > $compPart->count()) 9904 continue; 9905 if( in_array( $compPart->getName(), $subComponents )) 9906 _getXMLComponents( $comp, $compPart ); 9907 elseif( 'properties' == $compPart->getName()) { 9908 foreach( $compPart->children() as $property ) // properties as single property 9909 _getXMLProperties( $comp, $property ); 9910 } 9911 } // end foreach( $component->children() as $compPart ) 9912 } 9913 /** 9914 * parse SimpleXMLElement instance property, create iCalcreator component property 9915 * 9916 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 9917 * @since 2.11.2 - 2012-01-27 9918 * @param array $iCal iCalcreator calendar instance 9919 * @param object $component SimpleXMLElement 9920 * @return void 9921 */ 9922 function _getXMLProperties( & $iCal, & $property ) { 9923 $propName = $property->getName(); 9924 $value = $params = array(); 9925 $valueType = ''; 9926 foreach( $property->children() as $propPart ) { // calendar property parameters (opt) and value(-s) 9927 $valueType = $propPart->getName(); 9928 if( 'parameters' == $valueType) { 9929 $params = _getXMLParams( $propPart ); 9930 continue; 9931 } 9932 switch( $valueType ) { 9933 case 'binary': 9934 $value = reset( $propPart ); 9935 break; 9936 case 'boolean': 9937 break; 9938 case 'cal-address': 9939 $value = reset( $propPart ); 9940 break; 9941 case 'date': 9942 $params['VALUE'] = 'DATE'; 9943 case 'date-time': 9944 if(( 'exdate' == $propName ) || ( 'rdate' == $propName )) 9945 $value[] = reset( $propPart ); 9946 else 9947 $value = reset( $propPart ); 9948 break; 9949 case 'duration': 9950 $value = reset( $propPart ); 9951 break; 9952 // case 'geo': 9953 case 'latitude': 9954 case 'longitude': 9955 $value[$valueType] = reset( $propPart ); 9956 break; 9957 case 'integer': 9958 $value = reset( $propPart ); 9959 break; 9960 case 'period': 9961 if( 'rdate' == $propName ) 9962 $params['VALUE'] = 'PERIOD'; 9963 $pData = array(); 9964 foreach( $propPart->children() as $periodPart ) 9965 $pData[] = reset( $periodPart ); 9966 if( !empty( $pData )) 9967 $value[] = $pData; 9968 break; 9969 // case 'rrule': 9970 case 'freq': 9971 case 'count': 9972 case 'until': 9973 case 'interval': 9974 case 'wkst': 9975 $value[$valueType] = reset( $propPart ); 9976 break; 9977 case 'bysecond': 9978 case 'byminute': 9979 case 'byhour': 9980 case 'bymonthday': 9981 case 'byyearday': 9982 case 'byweekno': 9983 case 'bymonth': 9984 case 'bysetpos': 9985 $value[$valueType][] = reset( $propPart ); 9986 break; 9987 case 'byday': 9988 $byday = reset( $propPart ); 9989 if( 2 == strlen( $byday )) 9990 $value[$valueType][] = array( 'DAY' => $byday ); 9991 else { 9992 $day = substr( $byday, -2 ); 9993 $key = substr( $byday, 0, ( strlen( $byday ) - 2 )); 9994 $value[$valueType][] = array( $key, 'DAY' => $day ); 9995 } 9996 break; 9997 // case 'rstatus': 9998 case 'code': 9999 $value[0] = reset( $propPart ); 10000 break; 10001 case 'description': 10002 $value[1] = reset( $propPart ); 10003 break; 10004 case 'data': 10005 $value[2] = reset( $propPart ); 10006 break; 10007 case 'text': 10008 $text = str_replace( array( "\r\n", "\n\r", "\r", "\n"), '\n', reset( $propPart )); 10009 $value['text'][] = html_entity_decode( $text ); 10010 break; 10011 case 'time': 10012 break; 10013 case 'uri': 10014 $value = reset( $propPart ); 10015 break; 10016 case 'utc-offset': 10017 $value = str_replace( ':', '', reset( $propPart )); 10018 break; 10019 case 'unknown': 10020 default: 10021 $value = html_entity_decode( reset( $propPart )); 10022 break; 10023 } // end switch( $valueType ) 10024 } // end foreach( $property->children() as $propPart ) 10025 if( 'freebusy' == $propName ) { 10026 $fbtype = $params['FBTYPE']; 10027 unset( $params['FBTYPE'] ); 10028 $iCal->setProperty( $propName, $fbtype, $value, $params ); 10029 } 10030 elseif( 'geo' == $propName ) 10031 $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params ); 10032 elseif( 'request-status' == $propName ) { 10033 if( !isset( $value[2] )) 10034 $value[2] = FALSE; 10035 $iCal->setProperty( $propName, $value[0], $value[1], $value[2], $params ); 10036 } 10037 else { 10038 if( isset( $value['text'] ) && is_array( $value['text'] )) { 10039 if(( 'categories' == $propName ) || ( 'resources' == $propName )) 10040 $value = $value['text']; 10041 else 10042 $value = reset( $value['text'] ); 10043 } 10044 $iCal->setProperty( $propName, $value, $params ); 10045 } 10046 } 10047 /*********************************************************************************/ 10048 /* Additional functions to use with vtimezone components */ 10049 /*********************************************************************************/ 10050 /** 10051 * For use with 10052 * iCalcreator (kigkonsult.se/iCalcreator/index.php) 10053 * copyright (c) 2011 Yitzchok Lavi 10054 * icalcreator@onebigsystem.com 10055 * 10056 * This library is free software; you can redistribute it and/or 10057 * modify it under the terms of the GNU Lesser General Public 10058 * License as published by the Free Software Foundation; either 10059 * version 2.1 of the License, or (at your option) any later version. 10060 * 10061 * This library is distributed in the hope that it will be useful, 10062 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10063 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10064 * Lesser General Public License for more details. 10065 * 10066 * You should have received a copy of the GNU Lesser General Public 10067 * License along with this library; if not, write to the Free Software 10068 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 10069 */ 10070 /** 10071 * Additional functions to use with vtimezone components 10072 * 10073 * Before calling the functions, set time zone 'GMT' ('date_default_timezone_set')! 10074 * 10075 * @author Yitzchok Lavi <icalcreator@onebigsystem.com> 10076 * adjusted for iCalcreator Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> 10077 * @version 1.0.2 - 2011-02-24 10078 * 10079 */ 10080 /** 10081 * Returns array with the offset information from UTC for a (UTC) datetime/timestamp in the 10082 * timezone, according to the VTIMEZONE information in the input array. 10083 * 10084 * $param array $timezonesarray, output from function getTimezonesAsDateArrays (below) 10085 * $param string $tzid, time zone identifier 10086 * $param mixed $timestamp, timestamp or a UTC datetime (in array format) 10087 * @return array, time zone data with keys for 'offsetHis', 'offsetSec' and 'tzname' 10088 * 10089 */ 10090 function getTzOffsetForDate($timezonesarray, $tzid, $timestamp) { 10091 if( is_array( $timestamp )) { 10092 //$disp = sprintf( '%04d%02d%02d %02d%02d%02d', $timestamp['year'], $timestamp['month'], $timestamp['day'], $timestamp['hour'], $timestamp['min'], $timestamp['sec'] ); // test ### 10093 $timestamp = gmmktime( 10094 $timestamp['hour'], 10095 $timestamp['min'], 10096 $timestamp['sec'], 10097 $timestamp['month'], 10098 $timestamp['day'], 10099 $timestamp['year'] 10100 ) ; 10101 //echo '<td colspan="4"> '."\n".'<tr><td> <td class="r">'.$timestamp.'<td class="r">'.$disp.'<td colspan="4"> '."\n".'<tr><td colspan="3"> '; // test ### 10102 } 10103 $tzoffset = array(); 10104 // something to return if all goes wrong (such as if $tzid doesn't find us an array of dates) 10105 $tzoffset['offsetHis'] = '+0000'; 10106 $tzoffset['offsetSec'] = 0; 10107 $tzoffset['tzname'] = '?'; 10108 if( !isset( $timezonesarray[$tzid] )) 10109 return $tzoffset; 10110 $tzdatearray = $timezonesarray[$tzid]; 10111 if ( is_array($tzdatearray) ) { 10112 sort($tzdatearray); // just in case 10113 if ( $timestamp < $tzdatearray[0]['timestamp'] ) { 10114 // our date is before the first change 10115 $tzoffset['offsetHis'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; 10116 $tzoffset['offsetSec'] = $tzdatearray[0]['tzbefore']['offsetSec'] ; 10117 $tzoffset['tzname'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; // we don't know the tzname in this case 10118 } elseif ( $timestamp >= $tzdatearray[count($tzdatearray)-1]['timestamp'] ) { 10119 // our date is after the last change (we do this so our scan can stop at the last record but one) 10120 $tzoffset['offsetHis'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetHis'] ; 10121 $tzoffset['offsetSec'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetSec'] ; 10122 $tzoffset['tzname'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['tzname'] ; 10123 } else { 10124 // our date somewhere in between 10125 // 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 10126 // we don't include the last date in our loop as there isn't one after it to check 10127 for ( $i = 0 ; $i <= count($tzdatearray)-2 ; $i++ ) { 10128 if(( $timestamp >= $tzdatearray[$i]['timestamp'] ) && ( $timestamp < $tzdatearray[$i+1]['timestamp'] )) { 10129 $tzoffset['offsetHis'] = $tzdatearray[$i]['tzafter']['offsetHis'] ; 10130 $tzoffset['offsetSec'] = $tzdatearray[$i]['tzafter']['offsetSec'] ; 10131 $tzoffset['tzname'] = $tzdatearray[$i]['tzafter']['tzname'] ; 10132 break; 10133 } 10134 } 10135 } 10136 } 10137 return $tzoffset; 10138 } 10139 /** 10140 * Returns an array containing all the timezone data in the vcalendar object 10141 * 10142 * @param object $vcalendar, iCalcreator calendar instance 10143 * @return array, time zone transition timestamp, array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname) 10144 * based on the timezone data in the vcalendar object 10145 * 10146 */ 10147 function getTimezonesAsDateArrays($vcalendar) { 10148 $timezonedata = array(); 10149 while( $vtz = $vcalendar->getComponent( 'vtimezone' )) { 10150 $tzid = $vtz->getProperty('tzid'); 10151 $alltzdates = array(); 10152 while ( $vtzc = $vtz->getComponent( 'standard' )) { 10153 $newtzdates = expandTimezoneDates($vtzc); 10154 $alltzdates = array_merge($alltzdates, $newtzdates); 10155 } 10156 while ( $vtzc = $vtz->getComponent( 'daylight' )) { 10157 $newtzdates = expandTimezoneDates($vtzc); 10158 $alltzdates = array_merge($alltzdates, $newtzdates); 10159 } 10160 sort($alltzdates); 10161 $timezonedata[$tzid] = $alltzdates; 10162 } 10163 return $timezonedata; 10164 } 10165 /** 10166 * Returns an array containing time zone data from vtimezone standard/daylight instances 10167 * 10168 * @param object $vtzc, an iCalcreator calendar standard/daylight instance 10169 * @return array, time zone data; array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname) 10170 * 10171 */ 10172 function expandTimezoneDates($vtzc) { 10173 $tzdates = array(); 10174 // prepare time zone "description" to attach to each change 10175 $tzbefore = array(); 10176 $tzbefore['offsetHis'] = $vtzc->getProperty('tzoffsetfrom') ; 10177 $tzbefore['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzbefore['offsetHis']); 10178 if(( '-' != substr( (string) $tzbefore['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzbefore['offsetSec'], 0, 1 ))) 10179 $tzbefore['offsetSec'] = '+'.$tzbefore['offsetSec']; 10180 $tzafter = array(); 10181 $tzafter['offsetHis'] = $vtzc->getProperty('tzoffsetto') ; 10182 $tzafter['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzafter['offsetHis']); 10183 if(( '-' != substr( (string) $tzafter['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzafter['offsetSec'], 0, 1 ))) 10184 $tzafter['offsetSec'] = '+'.$tzafter['offsetSec']; 10185 if( FALSE === ( $tzafter['tzname'] = $vtzc->getProperty('tzname'))) 10186 $tzafter['tzname'] = $tzafter['offsetHis']; 10187 // find out where to start from 10188 $dtstart = $vtzc->getProperty('dtstart'); 10189 $dtstarttimestamp = mktime( 10190 $dtstart['hour'], 10191 $dtstart['min'], 10192 $dtstart['sec'], 10193 $dtstart['month'], 10194 $dtstart['day'], 10195 $dtstart['year'] 10196 ) ; 10197 if( !isset( $dtstart['unparsedtext'] )) // ?? 10198 $dtstart['unparsedtext'] = sprintf( '%04d%02d%02dT%02d%02d%02d', $dtstart['year'], $dtstart['month'], $dtstart['day'], $dtstart['hour'], $dtstart['min'], $dtstart['sec'] ); 10199 if ( $dtstarttimestamp == 0 ) { 10200 // it seems that the dtstart string may not have parsed correctly 10201 // let's set a timestamp starting from 1902, using the time part of the original string 10202 // so that the time will change at the right time of day 10203 // at worst we'll get midnight again 10204 $origdtstartsplit = explode('T',$dtstart['unparsedtext']) ; 10205 $dtstarttimestamp = strtotime("19020101",0); 10206 $dtstarttimestamp = strtotime($origdtstartsplit[1],$dtstarttimestamp); 10207 } 10208 // the date (in dtstart and opt RDATE/RRULE) is ALWAYS LOCAL (not utc!!), adjust from 'utc' to 'local' timestamp 10209 $diff = -1 * $tzbefore['offsetSec']; 10210 $dtstarttimestamp += $diff; 10211 // add this (start) change to the array of changes 10212 $tzdates[] = array( 10213 'timestamp' => $dtstarttimestamp, 10214 'tzbefore' => $tzbefore, 10215 'tzafter' => $tzafter 10216 ); 10217 $datearray = getdate($dtstarttimestamp); 10218 // save original array to use time parts, because strtotime (used below) apparently loses the time 10219 $changetime = $datearray ; 10220 // generate dates according to an RRULE line 10221 $rrule = $vtzc->getProperty('rrule') ; 10222 if ( is_array($rrule) ) { 10223 if ( $rrule['FREQ'] == 'YEARLY' ) { 10224 // calculate transition dates starting from DTSTART 10225 $offsetchangetimestamp = $dtstarttimestamp; 10226 // calculate transition dates until 10 years in the future 10227 $stoptimestamp = strtotime("+10 year",time()); 10228 // if UNTIL is set, calculate until then (however far ahead) 10229 if ( isset( $rrule['UNTIL'] ) && ( $rrule['UNTIL'] != '' )) { 10230 $stoptimestamp = mktime( 10231 $rrule['UNTIL']['hour'], 10232 $rrule['UNTIL']['min'], 10233 $rrule['UNTIL']['sec'], 10234 $rrule['UNTIL']['month'], 10235 $rrule['UNTIL']['day'], 10236 $rrule['UNTIL']['year'] 10237 ) ; 10238 } 10239 $count = 0 ; 10240 $stopcount = isset( $rrule['COUNT'] ) ? $rrule['COUNT'] : 0 ; 10241 $daynames = array( 10242 'SU' => 'Sunday', 10243 'MO' => 'Monday', 10244 'TU' => 'Tuesday', 10245 'WE' => 'Wednesday', 10246 'TH' => 'Thursday', 10247 'FR' => 'Friday', 10248 'SA' => 'Saturday' 10249 ); 10250 // repeat so long as we're between DTSTART and UNTIL, or we haven't prepared COUNT dates 10251 while ( $offsetchangetimestamp < $stoptimestamp && ( $stopcount == 0 || $count < $stopcount ) ) { 10252 // break up the timestamp into its parts 10253 $datearray = getdate($offsetchangetimestamp); 10254 if ( isset( $rrule['BYMONTH'] ) && ( $rrule['BYMONTH'] != 0 )) { 10255 // set the month 10256 $datearray['mon'] = $rrule['BYMONTH'] ; 10257 } 10258 if ( isset( $rrule['BYMONTHDAY'] ) && ( $rrule['BYMONTHDAY'] != 0 )) { 10259 // set specific day of month 10260 $datearray['mday'] = $rrule['BYMONTHDAY']; 10261 } elseif ( is_array($rrule['BYDAY']) ) { 10262 // find the Xth WKDAY in the month 10263 // the starting point for this process is the first of the month set above 10264 $datearray['mday'] = 1 ; 10265 // turn $datearray as it is now back into a timestamp 10266 $offsetchangetimestamp = mktime( 10267 $datearray['hours'], 10268 $datearray['minutes'], 10269 $datearray['seconds'], 10270 $datearray['mon'], 10271 $datearray['mday'], 10272 $datearray['year'] 10273 ); 10274 if ($rrule['BYDAY'][0] > 0) { 10275 // to find Xth WKDAY in month, we find last WKDAY in month before 10276 // we do that by finding first WKDAY in this month and going back one week 10277 // then we add X weeks (below) 10278 $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp); 10279 $offsetchangetimestamp = strtotime("-1 week",$offsetchangetimestamp); 10280 } else { 10281 // to find Xth WKDAY before the end of the month, we find the first WKDAY in the following month 10282 // we do that by going forward one month and going to WKDAY there 10283 // then we subtract X weeks (below) 10284 $offsetchangetimestamp = strtotime("+1 month",$offsetchangetimestamp); 10285 $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp); 10286 } 10287 // now move forward or back the appropriate number of weeks, into the month we want 10288 $offsetchangetimestamp = strtotime($rrule['BYDAY'][0] . " week",$offsetchangetimestamp); 10289 $datearray = getdate($offsetchangetimestamp); 10290 } 10291 // convert the date parts back into a timestamp, setting the time parts according to the 10292 // original time data which we stored 10293 $offsetchangetimestamp = mktime( 10294 $changetime['hours'], 10295 $changetime['minutes'], 10296 $changetime['seconds'] + $diff, 10297 $datearray['mon'], 10298 $datearray['mday'], 10299 $datearray['year'] 10300 ); 10301 // add this change to the array of changes 10302 $tzdates[] = array( 10303 'timestamp' => $offsetchangetimestamp, 10304 'tzbefore' => $tzbefore, 10305 'tzafter' => $tzafter 10306 ); 10307 // update counters (timestamp and count) 10308 $offsetchangetimestamp = strtotime("+" . (( isset( $rrule['INTERVAL'] ) && ( $rrule['INTERVAL'] != 0 )) ? $rrule['INTERVAL'] : 1 ) . " year",$offsetchangetimestamp); 10309 $count += 1 ; 10310 } 10311 } 10312 } 10313 // generate dates according to RDATE lines 10314 while ($rdates = $vtzc->getProperty('rdate')) { 10315 if ( is_array($rdates) ) { 10316 10317 foreach ( $rdates as $rdate ) { 10318 // convert the explicit change date to a timestamp 10319 $offsetchangetimestamp = mktime( 10320 $rdate['hour'], 10321 $rdate['min'], 10322 $rdate['sec'] + $diff, 10323 $rdate['month'], 10324 $rdate['day'], 10325 $rdate['year'] 10326 ) ; 10327 // add this change to the array of changes 10328 $tzdates[] = array( 10329 'timestamp' => $offsetchangetimestamp, 10330 'tzbefore' => $tzbefore, 10331 'tzafter' => $tzafter 10332 ); 10333 } 10334 } 10335 } 10336 return $tzdates; 10337 } 6913 10338 ?> -
trunk/prototype/services/iCal.php
r7579 r7660 1 1 <?php 2 2 3 require_once ROOTPATH . '/plugins/icalcreator/iCalUtilityFunctions.class.php';3 //require_once ROOTPATH . '/plugins/icalcreator/iCalUtilityFunctions.class.php'; 4 4 require_once ROOTPATH . '/plugins/icalcreator/iCalcreator.class.php'; 5 5 require_once ROOTPATH . '/modules/calendar/constants.php'; … … 31 31 protected function createIcal($data, $params = false ) 32 32 { 33 $ical = new icalCreator();33 $ical = new vcalendar(); 34 34 $ical->setProperty('method', isset($params['method']) ? $params['method'] : 'PUBLISH' ); 35 35 … … 136 136 $date = $r->next(); 137 137 138 $timezone = &$ical->newComponent('vtimezone');138 $timezone = $ical->newComponent('vtimezone'); 139 139 $timezone->setProperty('tzid',$apTimezone ); 140 140 141 $standard = &$timezone->newComponent( "standard" );141 $standard = $timezone->newComponent( "standard" ); 142 142 $standard->setProperty( "tzoffsetfrom", $timezoneDayligth['standardFrom'] ); 143 143 $standard->setProperty( "tzoffsetto", $timezoneDayligth['standardTo'] ); … … 154 154 $standard->setProperty('rrule', $rrule); 155 155 156 $daylight = &$timezone->newComponent( "daylight" );156 $daylight = $timezone->newComponent( "daylight" ); 157 157 158 158 $daylight->setProperty( "tzoffsetfrom", $timezoneDayligth['daylightFrom'] ); … … 261 261 protected function createCompatibleIcal($data, $params = false ) 262 262 { 263 $ical = new icalCreator();263 $ical = new vcalendar(); 264 264 $ical->setProperty('method', isset($params['method']) ? $params['method'] : 'PUBLISH' ); 265 265 … … 527 527 public function parse($data, $params = false) { 528 528 Config::regSet('noAlarm', TRUE); //Evita o envio de notificação 529 $vcalendar = new icalCreator( );529 $vcalendar = new vcalendar( ); 530 530 $vcalendar->parse(trim($data)); 531 531 $vcalendar->sort(); … … 1168 1168 1169 1169 public function analize($data, $params = false) { 1170 $vcalendar = new icalCreator( );1170 $vcalendar = new vcalendar( ); 1171 1171 $vcalendar->parse(trim($data)); 1172 1172 $vcalendar->sort(); … … 1611 1611 $repeat = array(); 1612 1612 $repeat['schedulable'] = $eventID; 1613 foreach ($rrule as $i => $v) { 1613 $repeat['startTime'] = $schedulable['startTime']; 1614 foreach ($rrule as $i => $v) { 1614 1615 if (strtolower($i) == 'freq') 1615 1616 $repeat['frequency'] = $v; 1616 1617 else if (strtolower($i) == 'until') 1617 $repeat['endTime'] = $v; 1618 { 1619 $repeat['endTime'] = strtotime($v['year'].'-'.$v['month'].'-'.$v['day'].' '.$v['hour'].':'.$v['min'].':'.$v['sec'].' '.$v['tz']) .'000' ; 1620 } 1618 1621 else 1619 1622 $repeat[strtolower($i)] = $v; … … 1622 1625 $interation['repeat://' . $repeatID] = $repeat; 1623 1626 } 1624 1625 1627 1626 1628 $schedulable['calendar'] = $params['calendar'];
Note: See TracChangeset
for help on using the changeset viewer.