Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,254 @@
<?php
/**
* iCalcreator, a PHP rfc2445/rfc5545 solution.
*
* This file is a part of iCalcreator.
*
* Copyright (c) 2007-2017 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
* Link http://kigkonsult.se/iCalcreator/index.php
* Package iCalcreator
* Version 2.24
* License Subject matter of licence is the software iCalcreator.
* The above copyright, link, package and version notices,
* this licence notice and the [rfc5545] PRODID as implemented and
* invoked in iCalcreator shall be included in all copies or
* substantial portions of the iCalcreator.
* iCalcreator can be used either under the terms of
* a proprietary license, available at <https://kigkonsult.se/>
* or the GNU Affero General Public License, version 3:
* iCalcreator is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* iCalcreator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
* License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
namespace kigkonsult\iCalcreator\util;
/**
* iCalcreator attendee support class
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-02
*/
class utilAttendee {
/**
* Return string after a cal-address check, prefix mail address with MAILTO
*
* Acceps other prefix ftp://, http://, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// etc
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-03-06
* @param string $value
* @param bool $trimQuotes
* @return string
* @static
* @TODO fix in util::splitContent() ??
*/
public static function calAddressCheck( $value, $trimQuotes=true ) {
static $MAILTOCOLON = 'MAILTO:';
$value = trim( $value );
if( $trimQuotes )
$value = trim( $value, util::$QQ );
switch( true ) {
case( empty( $value )) :
break;
case( 0 == strcasecmp( $MAILTOCOLON, substr( $value, 0, 7 ))) :
$value = $MAILTOCOLON . substr( $value, 7 ); // accept mailto:
break;
case( false !== ( $pos = strpos( substr( $value, 0, 9 ), util::$COLON ))) :
break; // accept (as is) from list above
case( filter_var( $value, FILTER_VALIDATE_EMAIL )) :
$value = $MAILTOCOLON . $value; // accept mail address
break;
default : // accept as is...
break;
}
return $value;
}
/**
* Return formatted output for calendar component property attendee
*
* @param array $attendeeData
* @param bool $allowEmpty
* @return string
* @static
*/
public static function formatAttendee( array $attendeeData, $allowEmpty ) {
static $FMTQVALUE = '"%s"';
static $FMTKEYVALUE = ';%s=%s';
static $FMTKEYEQ = ';%s=';
static $FMTDIREQ = ';%s=%s%s%s';
$output = null;
foreach( $attendeeData as $ax => $attendeePart ) {
if( empty( $attendeePart[util::$LCvalue] )) {
if( $allowEmpty )
$output .= util::createElement( util::$ATTENDEE );
continue;
}
$attributes = $content = null;
foreach( $attendeePart as $pLabel => $pValue ) {
if( util::$LCvalue == $pLabel ) {
$content .= $pValue;
continue;
}
if(( util::$LCparams != $pLabel ) ||
( ! is_array( $pValue )))
continue;
foreach( $pValue as $pLabel2 => $pValue2 ) { // fix (opt) quotes
if( is_array( $pValue2 ) ||
in_array( $pLabel2, util::$ATTENDEEPARKEYS ))
continue; // DELEGATED-FROM, DELEGATED-TO, MEMBER
if(( false !== strpos( $pValue2, util::$COLON )) ||
( false !== strpos( $pValue2, util::$SEMIC )) ||
( false !== strpos( $pValue2, util::$COMMA )))
$pValue[$pLabel2] = sprintf( $FMTQVALUE, $pValue2 );
}
/* set attenddee parameters in rfc2445 order */
if( isset( $pValue[util::$CUTYPE] ))
$attributes .= sprintf( $FMTKEYVALUE,
util::$CUTYPE,
$pValue[util::$CUTYPE] );
if( isset( $pValue[util::$MEMBER] ))
$attributes .= sprintf( $FMTKEYEQ,
util::$MEMBER ) .
self::getQuotedListItems( $pValue[util::$MEMBER] );
if( isset( $pValue[util::$ROLE] ))
$attributes .= sprintf( $FMTKEYVALUE,
util::$ROLE,
$pValue[util::$ROLE] );
if( isset( $pValue[util::$PARTSTAT] ))
$attributes .= sprintf( $FMTKEYVALUE,
util::$PARTSTAT,
$pValue[util::$PARTSTAT] );
if( isset( $pValue[util::$RSVP] ))
$attributes .= sprintf( $FMTKEYVALUE,
util::$RSVP,
$pValue[util::$RSVP] );
if( isset( $pValue[util::$DELEGATED_TO] ))
$attributes .= sprintf( $FMTKEYEQ,
util::$DELEGATED_TO ) .
self::getQuotedListItems( $pValue[util::$DELEGATED_TO] );
if( isset( $pValue[util::$DELEGATED_FROM] ))
$attributes .= sprintf( $FMTKEYEQ,
util::$DELEGATED_FROM ) .
self::getQuotedListItems( $pValue[util::$DELEGATED_FROM] );
if( isset( $pValue[util::$SENT_BY] ))
$attributes .= sprintf( $FMTKEYVALUE,
util::$SENT_BY,
$pValue[util::$SENT_BY] );
if( isset( $pValue[util::$CN] ))
$attributes .= sprintf( $FMTKEYVALUE,
util::$CN,
$pValue[util::$CN] );
if( isset( $pValue[util::$DIR] )) {
$delim = ( false === strpos( $pValue[util::$DIR], util::$QQ )) ? util::$QQ : null;
$attributes .= sprintf( $FMTDIREQ, util::$DIR,
$delim,
$pValue[util::$DIR],
$delim );
}
if( isset( $pValue[util::$LANGUAGE] ))
$attributes .= sprintf( $FMTKEYVALUE,
util::$LANGUAGE,
$pValue[util::$LANGUAGE] );
$xparams = [];
foreach( $pValue as $pLabel2 => $pValue2 ) {
if( ctype_digit( (string) $pLabel2 ))
$xparams[] = $pValue2;
elseif( ! in_array( $pLabel2, util::$ATTENDEEPARALLKEYS ))
$xparams[$pLabel2] = $pValue2;
}
if( empty( $xparams ))
continue;
ksort( $xparams, SORT_STRING );
foreach( $xparams as $pLabel2 => $pValue2 ) {
if( ctype_digit( (string) $pLabel2 ))
$attributes .= util::$SEMIC . $pValue2; // ??
else
$attributes .= sprintf( $FMTKEYVALUE, $pLabel2,
$pValue2 );
}
} // end foreach( $attendeePart )) as $pLabel => $pValue )
$output .= util::createElement( util::$ATTENDEE,
$attributes,
$content );
} // end foreach( $attendeeData as $ax => $attendeePart )
return $output;
}
/**
* Return string of comma-separated quoted array members
*
* @param array $list
* @return string
* @access private
* @static
*/
private static function getQuotedListItems( array $list ) {
static $FMTQVALUE = '"%s"';
static $FMTCOMMAQVALUE = ',"%s"';
$strList = null;
foreach( $list as $x => $v )
$strList .= ( 0 < $x )
? sprintf( $FMTCOMMAQVALUE, $v )
: sprintf( $FMTQVALUE, $v );
return $strList;
}
/**
* Return formatted output for calendar component property attendee
*
* @param array $params
* @param string $objName
* @param string $lang
* @return array
* @static
*/
public static function prepAttendeeParams( $params, $objName, $lang ) {
static $NONXPROPCOMPS = null;
if( is_null( $NONXPROPCOMPS ))
$NONXPROPCOMPS = [util::$LCVFREEBUSY, util::$LCVALARM];
$params2 = [];
if( is_array( $params )) {
$optArr = [];
$params = array_change_key_case( $params, CASE_UPPER );
foreach( $params as $pLabel => $optParamValue ) {
if( ! util::isXprefixed( $pLabel ) &&
in_array( $objName, $NONXPROPCOMPS ))
continue;
switch( $pLabel ) {
case util::$MEMBER:
case util::$DELEGATED_TO:
case util::$DELEGATED_FROM:
if( ! is_array( $optParamValue ))
$optParamValue = [$optParamValue];
foreach(( array_keys( $optParamValue )) as $px )
$optArr[$pLabel][] = self::calAddressCheck( $optParamValue[$px] );
break;
default:
if( util::$SENT_BY == $pLabel )
$optParamValue = self::calAddressCheck( $optParamValue );
else
$optParamValue = trim( $optParamValue, util::$QQ );
$params2[$pLabel] = $optParamValue;
break;
} // end switch( $pLabel.. .
} // end foreach( $params as $pLabel => $optParamValue )
foreach( $optArr as $pLabel => $pValue )
$params2[$pLabel] = $pValue;
} // end if( is_array($params ))
// remove defaults
util::existRem( $params2, util::$CUTYPE, util::$INDIVIDUAL );
util::existRem( $params2, util::$PARTSTAT, util::$NEEDS_ACTION );
util::existRem( $params2, util::$ROLE, util::$REQ_PARTICIPANT );
util::existRem( $params2, util::$RSVP, util::$false );
// check language setting
if( isset( $params2[util::$CN ] ) &&
! isset( $params2[util::$LANGUAGE ] ) &&
! empty( $lang ))
$params2[util::$LANGUAGE ] = $lang;
return $params2;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* iCalcreator, a PHP rfc2445/rfc5545 solution.
*
* This file is a part of iCalcreator.
*
* Copyright (c) 2007-2017 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
* Link http://kigkonsult.se/iCalcreator/index.php
* Package iCalcreator
* Version 2.24
* License Subject matter of licence is the software iCalcreator.
* The above copyright, link, package and version notices,
* this licence notice and the [rfc5545] PRODID as implemented and
* invoked in iCalcreator shall be included in all copies or
* substantial portions of the iCalcreator.
* iCalcreator can be used either under the terms of
* a proprietary license, available at <https://kigkonsult.se/>
* or the GNU Affero General Public License, version 3:
* iCalcreator is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* iCalcreator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
* License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
namespace kigkonsult\iCalcreator\util;
/**
* iCalcreator geo support class
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-02
*/
class utilGeo {
/**
* @var string GEO vars: output format for geo latitude and longitude (before rtrim) etc
* @access public
* @static
*/
public static $geoLatFmt = '%09.6f';
public static $geoLongFmt = '%8.6f';
public static $LATITUDE = 'latitude';
public static $LONGITUDE = 'longitude';
/**
* Return formatted geo output
*
* @param float $ll
* @param string $format
* @return string
* @access public
* @static
*/
public static function geo2str2( $ll, $format ) {
if( 0.0 < $ll )
$sign = util::$PLUS;
else
$sign = ( 0.0 > $ll ) ? util::$MINUS : null;
return rtrim( rtrim( $sign . sprintf( $format, abs( $ll )),
util::$ZERO ),
util::$DOT );
}
}

View File

@@ -0,0 +1,873 @@
<?php
/**
* iCalcreator, a PHP rfc2445/rfc5545 solution.
*
* This file is a part of iCalcreator.
*
* Copyright (c) 2007-2017 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
* Link http://kigkonsult.se/iCalcreator/index.php
* Package iCalcreator
* Version 2.24
* License Subject matter of licence is the software iCalcreator.
* The above copyright, link, package and version notices,
* this licence notice and the [rfc5545] PRODID as implemented and
* invoked in iCalcreator shall be included in all copies or
* substantial portions of the iCalcreator.
* iCalcreator can be used either under the terms of
* a proprietary license, available at <https://kigkonsult.se/>
* or the GNU Affero General Public License, version 3:
* iCalcreator is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* iCalcreator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
* License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
namespace kigkonsult\iCalcreator\util;
/**
* iCalcreator recur support class
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.18 - 2017-06-14
*/
class utilRecur {
/**
* Static values for recurrence FREQuence
* @access private
* @static
*/
private static $DAILY = 'DAILY';
private static $WEEKLY = 'WEEKLY';
private static $MONTHLY = 'MONTHLY';
private static $YEARLY = 'YEARLY';
//private static $SECONDLY = 'SECONDLY';
//private static $MINUTELY = 'MINUTELY';
//private static $HOURLY = 'HOURLY';
private static $DAYNAMES = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
private static $YEARCNT_UP = 'yearcnt_up';
private static $YEARCNT_DOWN = 'yearcnt_down';
private static $MONTHDAYNO_UP = 'monthdayno_up';
private static $MONTHDAYNO_DOWN = 'monthdayno_down';
private static $MONTHCNT_DOWN = 'monthcnt_down';
private static $YEARDAYNO_UP = 'yeardayno_up';
private static $YEARDAYNO_DOWN = 'yeardayno_down';
private static $WEEKNO_UP = 'weekno_up';
private static $WEEKNO_DOWN = 'weekno_down';
private static $W = 'W';
/**
* Sort recur dates
* @param array $byDayA
* @param array $byDayB
* @return int
* @access private
* @static
*/
private static function recurBydaySort( $byDayA, $byDayB ) {
static $days = ['SU' => 0,
'MO' => 1,
'TU' => 2,
'WE' => 3,
'TH' => 4,
'FR' => 5,
'SA' => 6];
return ( $days[substr( $byDayA, -2 )] < $days[substr( $byDayB, -2 )] ) ? -1 : 1;
}
/**
* Return formatted output for calendar component property data value type recur
*
* @param string $recurlabel
* @param array $recurData
* @param bool $allowEmpty
* @return string
* @static
*/
public static function formatRecur( $recurlabel, $recurData, $allowEmpty ) {
static $FMTFREQEQ = 'FREQ=%s';
static $FMTDEFAULTEQ = ';%s=%s';
static $FMTOTHEREQ = ';%s=';
static $RECURBYDAYSORTER = null;
static $SP0 = '';
if( is_null( $RECURBYDAYSORTER ))
$RECURBYDAYSORTER = [get_class(), 'recurBydaySort'];
if( empty( $recurData ))
return null;
$output = null;
foreach( $recurData as $rx => $theRule ) {
if( empty( $theRule[util::$LCvalue] )) {
if( $allowEmpty )
$output .= util::createElement( $recurlabel );
continue;
}
$attributes = ( isset( $theRule[util::$LCparams] ))
? util::createParams( $theRule[util::$LCparams] )
: null;
$content1 = $content2 = null;
foreach( $theRule[util::$LCvalue] as $ruleLabel => $ruleValue ) {
$ruleLabel = strtoupper( $ruleLabel );
switch( $ruleLabel ) {
case util::$FREQ :
$content1 .= sprintf( $FMTFREQEQ, $ruleValue );
break;
case util::$UNTIL :
$parno = ( isset( $ruleValue[util::$LCHOUR] )) ? 7 : 3;
$content2 .= sprintf( $FMTDEFAULTEQ, util::$UNTIL,
util::date2strdate( $ruleValue,
$parno ));
break;
case util::$COUNT :
case util::$INTERVAL :
case util::$WKST :
$content2 .= sprintf( $FMTDEFAULTEQ, $ruleLabel, $ruleValue );
break;
case util::$BYDAY :
$byday = [$SP0];
$bx = 0;
foreach( $ruleValue as $bix => $bydayPart ) {
if( ! empty( $byday[$bx] ) && // new day
! ctype_digit( substr( $byday[$bx], -1 )))
$byday[++$bx] = $SP0;
if( ! is_array( $bydayPart )) // day without order number
$byday[$bx] .= (string) $bydayPart;
else { // day with order number
foreach( $bydayPart as $bix2 => $bydayPart2 )
$byday[$bx] .= (string) $bydayPart2;
}
} // end foreach( $ruleValue as $bix => $bydayPart )
if( 1 < count( $byday ))
usort( $byday, $RECURBYDAYSORTER );
$content2 .= sprintf( $FMTDEFAULTEQ, util::$BYDAY,
implode( util::$COMMA,
$byday ));
break;
default : // BYSECOND/BYMINUTE/BYHOUR/BYMONTHDAY/BYYEARDAY/BYWEEKNO/BYMONTH/BYSETPOS...
if( is_array( $ruleValue )) {
$content2 .= sprintf( $FMTOTHEREQ, $ruleLabel );
$content2 .= implode( util::$COMMA, $ruleValue );
}
else
$content2 .= sprintf( $FMTDEFAULTEQ, $ruleLabel, $ruleValue );
break;
} // end switch( $ruleLabel )
} // end foreach( $theRule[util::$LCvalue] )) as $ruleLabel => $ruleValue )
$output .= util::createElement( $recurlabel,
$attributes,
$content1 . $content2 );
} // end foreach( $recurData as $rx => $theRule )
return $output;
}
/**
* Convert input format for EXRULE and RRULE to internal format
*
* @param array $rexrule
* @return array
* @static
*/
public static function setRexrule( $rexrule ) {
static $BYSECOND = 'BYSECOND';
static $BYMINUTE = 'BYMINUTE';
static $BYHOUR = 'BYHOUR';
$input = [];
if( empty( $rexrule ))
return $input;
$rexrule = array_change_key_case( $rexrule, CASE_UPPER );
foreach( $rexrule as $rexruleLabel => $rexruleValue ) {
if( util::$UNTIL != $rexruleLabel )
$input[$rexruleLabel] = $rexruleValue;
else {
util::strDate2arr( $rexruleValue );
if( util::isArrayTimestampDate( $rexruleValue )) // timestamp, always date-time UTC
$input[$rexruleLabel] = util::timestamp2date( $rexruleValue, 7, util::$UTC );
elseif( util::isArrayDate( $rexruleValue )) { // date or UTC date-time
$parno = ( isset( $rexruleValue[util::$LCHOUR] ) ||
isset( $rexruleValue[4] )) ? 7 : 3;
$d = util::chkDateArr( $rexruleValue, $parno );
if(( 3 < $parno ) &&
isset( $d[util::$LCtz] ) &&
( util::$Z != $d[util::$LCtz] ) &&
util::isOffset( $d[util::$LCtz] )) {
$input[$rexruleLabel] = util::strDate2ArrayDate( sprintf( util::$YMDHISE,
(int) $d[util::$LCYEAR],
(int) $d[util::$LCMONTH],
(int) $d[util::$LCDAY],
(int) $d[util::$LCHOUR],
(int) $d[util::$LCMIN],
(int) $d[util::$LCSEC],
$d[util::$LCtz] ),
7 );
unset( $input[$rexruleLabel][util::$UNPARSEDTEXT] );
}
else
$input[$rexruleLabel] = $d;
}
elseif( 8 <= strlen( trim( $rexruleValue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC
$input[$rexruleLabel] = util::strDate2ArrayDate( $rexruleValue );
unset( $input[$rexruleLabel][util::$UNPARSEDTEXT] );
}
if(( 3 < count( $input[$rexruleLabel] )) &&
! isset( $input[$rexruleLabel][util::$LCtz] ))
$input[$rexruleLabel][util::$LCtz] = util::$Z;
}
} // end foreach( $rexrule as $rexruleLabel => $rexruleValue )
/* set recurrence rule specification in rfc2445 order */
$input2 = [];
if( isset( $input[util::$FREQ] ))
$input2[util::$FREQ] = $input[util::$FREQ];
if( isset( $input[util::$UNTIL] ))
$input2[util::$UNTIL] = $input[util::$UNTIL];
elseif( isset( $input[util::$COUNT] ))
$input2[util::$COUNT] = $input[util::$COUNT];
if( isset( $input[util::$INTERVAL] ))
$input2[util::$INTERVAL] = $input[util::$INTERVAL];
if( isset( $input[$BYSECOND] ))
$input2[$BYSECOND] = $input[$BYSECOND];
if( isset( $input[$BYMINUTE] ))
$input2[$BYMINUTE] = $input[$BYMINUTE];
if( isset( $input[$BYHOUR] ))
$input2[$BYHOUR] = $input[$BYHOUR];
if( isset( $input[util::$BYDAY] )) {
if( ! is_array( $input[util::$BYDAY] )) // ensure upper case.. .
$input2[util::$BYDAY] = strtoupper( $input[util::$BYDAY] );
else {
foreach( $input[util::$BYDAY] as $BYDAYx => $BYDAYv ) {
if( 0 == strcasecmp( util::$DAY, $BYDAYx ))
$input2[util::$BYDAY][util::$DAY] = strtoupper( $BYDAYv );
elseif( ! is_array( $BYDAYv ))
$input2[util::$BYDAY][$BYDAYx] = $BYDAYv;
else {
foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) {
if( 0 == strcasecmp( util::$DAY, $BYDAYx2 ))
$input2[util::$BYDAY][$BYDAYx][util::$DAY] = strtoupper( $BYDAYv2 );
else
$input2[util::$BYDAY][$BYDAYx][$BYDAYx2] = $BYDAYv2;
}
}
}
}
} // end if( isset( $input[util::$BYDAY] ))
if( isset( $input[util::$BYMONTHDAY] ))
$input2[util::$BYMONTHDAY] = $input[util::$BYMONTHDAY];
if( isset( $input[util::$BYYEARDAY] ))
$input2[util::$BYYEARDAY] = $input[util::$BYYEARDAY];
if( isset( $input[util::$BYWEEKNO] ))
$input2[util::$BYWEEKNO] = $input[util::$BYWEEKNO];
if( isset( $input[util::$BYMONTH] ))
$input2[util::$BYMONTH] = $input[util::$BYMONTH];
if( isset( $input[util::$BYSETPOS] ))
$input2[util::$BYSETPOS] = $input[util::$BYSETPOS];
if( isset( $input[util::$WKST] ))
$input2[util::$WKST] = $input[util::$WKST];
return $input2;
}
/**
* Update array $result with dates based on a recur pattern
*
* If missing, UNTIL is set 1 year from startdate (emergency break)
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.21.11 - 2015-03-10
* @param array $result array to update, array([Y-m-d] => bool)
* @param array $recur pattern for recurrency (only value part, params ignored)
* @param mixed $wdate component start date, string / array / (datetime) obj
* @param mixed $fcnStart start date, string / array / (datetime) obj
* @param mixed $fcnEnd end date, string / array / (datetime) obj
* @static
* @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start OR not at all
*/
public static function recur2date( & $result,
$recur,
$wdate,
$fcnStart,
$fcnEnd=false ) {
static $YEAR2DAYARR = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'];
static $SU = 'SU';
self::reFormatDate( $wdate );
$wdateYMD = sprintf( util::$YMD, $wdate[util::$LCYEAR],
$wdate[util::$LCMONTH],
$wdate[util::$LCDAY] );
$wdateHis = sprintf( util::$HIS, $wdate[util::$LCHOUR],
$wdate[util::$LCMIN],
$wdate[util::$LCSEC] );
$untilHis = $wdateHis;
self::reFormatDate( $fcnStart );
$fcnStartYMD = sprintf( util::$YMD, $fcnStart[util::$LCYEAR],
$fcnStart[util::$LCMONTH],
$fcnStart[util::$LCDAY] );
if( ! empty( $fcnEnd ))
self::reFormatDate( $fcnEnd );
else {
$fcnEnd = $fcnStart;
$fcnEnd[util::$LCYEAR] += 1;
}
$fcnEndYMD = sprintf( util::$YMD, $fcnEnd[util::$LCYEAR],
$fcnEnd[util::$LCMONTH],
$fcnEnd[util::$LCDAY] );
// echo "<b>recur _in_ comp</b> start ".implode('-',$wdate)." period start ".implode('-',$fcnStart)." period end ".implode('-',$fcnEnd)."<br>\n";
// echo 'recur='.str_replace( [PHP_EOL, ' '], null, var_export( $recur, true ))."<br> \n"; // test ###
if( ! isset( $recur[util::$COUNT] ) &&
! isset( $recur[util::$UNTIL] ))
$recur[util::$UNTIL] = $fcnEnd; // create break
if( isset( $recur[util::$UNTIL] )) {
foreach( $recur[util::$UNTIL] as $k => $v ) {
if( ctype_digit( $v ))
$recur[util::$UNTIL][$k] = (int) $v;
}
unset( $recur[util::$UNTIL][util::$LCtz] );
if( $fcnEnd > $recur[util::$UNTIL] ) {
$fcnEnd = $recur[util::$UNTIL]; // emergency break
$fcnEndYMD = sprintf( util::$YMD, $fcnEnd[util::$LCYEAR],
$fcnEnd[util::$LCMONTH],
$fcnEnd[util::$LCDAY] );
}
if( isset( $recur[util::$UNTIL][util::$LCHOUR] ))
$untilHis = sprintf( util::$HIS, $recur[util::$UNTIL][util::$LCHOUR],
$recur[util::$UNTIL][util::$LCMIN],
$recur[util::$UNTIL][util::$LCSEC] );
else
$untilHis = sprintf( util::$HIS, 23, 59, 59 );
// echo 'recurUNTIL='.str_replace( [PHP_EOL, ' '], '', var_export( $recur['UNTIL'], true )).", untilHis={$untilHis}<br> \n"; // test ###
} // end if( isset( $recur[util::$UNTIL] ))
// echo 'fcnEnd:'.$fcnEndYMD.$untilHis."<br>\n"; // test ###
if( $wdateYMD > $fcnEndYMD ) {
// echo 'recur out of date, '.implode('-',$wdate).', end='.implode('-',$fcnEnd)."<br>\n"; // test ###
return []; // nothing to do.. .
}
if( ! isset( $recur[util::$FREQ] )) // "MUST be specified.. ."
$recur[util::$FREQ] = self::$DAILY; // ??
$wkst = ( isset( $recur[util::$WKST] ) &&
( $SU == $recur[util::$WKST] )) ? 24*60*60 : 0; // ??
if( ! isset( $recur[util::$INTERVAL] ))
$recur[util::$INTERVAL] = 1;
$recurCount = ( ! isset( $recur[util::$BYSETPOS] )) ? 1 : 0; // DTSTART counts as the first occurrence
/* find out how to step up dates and set index for interval count */
$step = [];
if( self::$YEARLY == $recur[util::$FREQ] )
$step[util::$LCYEAR] = 1;
elseif( self::$MONTHLY == $recur[util::$FREQ] )
$step[util::$LCMONTH] = 1;
elseif( self::$WEEKLY == $recur[util::$FREQ] )
$step[util::$LCDAY] = 7;
else
$step[util::$LCDAY] = 1;
if( isset( $step[util::$LCYEAR] ) && isset( $recur[util::$BYMONTH] ))
$step = [util::$LCMONTH => 1];
if( empty( $step ) && isset( $recur[util::$BYWEEKNO] )) // ??
$step = [util::$LCDAY => 7];
if( isset( $recur[util::$BYYEARDAY] ) ||
isset( $recur[util::$BYMONTHDAY] ) ||
isset( $recur[util::$BYDAY] ))
$step = [util::$LCDAY => 1];
$intervalarr = [];
if( 1 < $recur[util::$INTERVAL] ) {
$intervalix = self::recurIntervalIx( $recur[util::$FREQ],
$wdate,
$wkst );
$intervalarr = [$intervalix => 0];
}
if( isset( $recur[util::$BYSETPOS] )) { // save start date + weekno
$bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = [];
if( is_array( $recur[util::$BYSETPOS] )) {
foreach( $recur[util::$BYSETPOS] as $bix => $bval )
$recur[util::$BYSETPOS][$bix] = (int) $bval;
}
else
$recur[util::$BYSETPOS] = [(int) $recur[util::$BYSETPOS]];
if( self::$YEARLY == $recur[util::$FREQ] ) {
$wdate[util::$LCMONTH] = $wdate[util::$LCDAY] = 1; // start from beginning of year
$wdateYMD = sprintf( util::$YMD, $wdate[util::$LCYEAR],
$wdate[util::$LCMONTH],
$wdate[util::$LCDAY] );
self::stepdate( $fcnEnd, $fcnEndYMD, [util::$LCYEAR => 1] ); // make sure to count last year
}
elseif( self::$MONTHLY == $recur[util::$FREQ] ) {
$wdate[util::$LCDAY] = 1; // start from beginning of month
$wdateYMD = sprintf( util::$YMD, $wdate[util::$LCYEAR],
$wdate[util::$LCMONTH],
$wdate[util::$LCDAY] );
self::stepdate( $fcnEnd, $fcnEndYMD, [util::$LCMONTH => 1] ); // make sure to count last month
}
else
self::stepdate( $fcnEnd, $fcnEndYMD, $step); // make sure to count whole last period
// echo "BYSETPOS endDat =".implode('-',$fcnEnd).' step='.var_export($step,true)."<br>\n"; // test ######
$bysetposWold = (int) date( self::$W,
mktime( 0,
0,
$wkst,
$wdate[util::$LCMONTH],
$wdate[util::$LCDAY],
$wdate[util::$LCYEAR] ));
$bysetposYold = $wdate[util::$LCYEAR];
$bysetposMold = $wdate[util::$LCMONTH];
$bysetposDold = $wdate[util::$LCDAY];
} // end if( isset( $recur[util::$BYSETPOS] ))
else
self::stepdate( $wdate, $wdateYMD, $step);
$year_old = null;
/* MAIN LOOP */
while( true ) {
// $txt = ( isset( $recur[util::$COUNT] )) ? '. recurCount : ' . $recurCount . ', COUNT : ' . $recur[util::$COUNT] : null; // test ###
// echo "recur start while:<b>{$wdateYMD}</b>, end:{$fcnEndYMD}{$txt}<br>\n"; // test ###
if( $wdateYMD.$wdateHis > $fcnEndYMD.$untilHis )
break;
if( isset( $recur[util::$COUNT] ) &&
( $recurCount >= $recur[util::$COUNT] ))
break;
if( $year_old != $wdate[util::$LCYEAR] ) { // $year_old=null 1:st time
$year_old = $wdate[util::$LCYEAR];
$daycnts = self::initDayCnts( $wdate, $recur, $wkst );
}
/* check interval */
if( 1 < $recur[util::$INTERVAL] ) {
/* create interval index */
$intervalix = self::recurIntervalIx( $recur[util::$FREQ],
$wdate,
$wkst );
/* check interval */
$currentKey = array_keys( $intervalarr );
$currentKey = end( $currentKey ); // get last index
if( $currentKey != $intervalix )
$intervalarr = [$intervalix => ( $intervalarr[$currentKey] + 1 )];
if(( $recur[util::$INTERVAL] != $intervalarr[$intervalix] ) &&
( 0 != $intervalarr[$intervalix] )) {
/* step up date */
// echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br>\n"; // test ###
self::stepdate( $wdate, $wdateYMD, $step);
continue;
}
else // continue within the selected interval
$intervalarr[$intervalix] = 0;
// echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br>\n"; // test ###
} // endif( 1 < $recur['INTERVAL'] )
$updateOK = true;
if( $updateOK && isset( $recur[util::$BYMONTH] ))
$updateOK = self::recurBYcntcheck( $recur[util::$BYMONTH],
$wdate[util::$LCMONTH],
( $wdate[util::$LCMONTH] - 13 ));
if( $updateOK && isset( $recur[util::$BYWEEKNO] ))
$updateOK = self::recurBYcntcheck( $recur[util::$BYWEEKNO],
$daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$WEEKNO_UP],
$daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$WEEKNO_DOWN] );
if( $updateOK && isset( $recur[util::$BYYEARDAY] ))
$updateOK = self::recurBYcntcheck( $recur[util::$BYYEARDAY],
$daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$YEARCNT_UP],
$daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$YEARCNT_DOWN] );
if( $updateOK && isset( $recur[util::$BYMONTHDAY] ))
$updateOK = self::recurBYcntcheck( $recur[util::$BYMONTHDAY],
$wdate[util::$LCDAY],
$daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$MONTHCNT_DOWN] );
// echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'true' : 'false'; echo "<br>\n"; // test ######
if( $updateOK && isset( $recur[util::$BYDAY] )) {
$updateOK = false;
$m = $wdate[util::$LCMONTH];
$d = $wdate[util::$LCDAY];
if( isset( $recur[util::$BYDAY][util::$DAY] )) { // single day, opt with year/month day order no
$daynoexists = $daynosw = $daynamesw = false;
if( $recur[util::$BYDAY][util::$DAY] == $daycnts[$m][$d][util::$DAY] )
$daynamesw = true;
if( isset( $recur[util::$BYDAY][0] )) {
$daynoexists = true;
if(( isset( $recur[util::$FREQ] ) &&
( $recur[util::$FREQ] == self::$MONTHLY )) ||
isset( $recur[util::$BYMONTH] ))
$daynosw = self::recurBYcntcheck( $recur[util::$BYDAY][0],
$daycnts[$m][$d][self::$MONTHDAYNO_UP],
$daycnts[$m][$d][self::$MONTHDAYNO_DOWN] );
elseif( isset( $recur[util::$FREQ] ) &&
( $recur[util::$FREQ] == self::$YEARLY ))
$daynosw = self::recurBYcntcheck( $recur[util::$BYDAY][0],
$daycnts[$m][$d][self::$YEARDAYNO_UP],
$daycnts[$m][$d][self::$YEARDAYNO_DOWN] );
}
if(( $daynoexists && $daynosw && $daynamesw ) ||
( ! $daynoexists && ! $daynosw && $daynamesw )) {
$updateOK = true;
// echo "m=$m d=$d day=".$daycnts[$m][$d][util::$DAY]." yeardayno_up=".$daycnts[$m][$d][self::$YEARDAYNO_UP]." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br>\n"; // test ###
}
// echo "m=$m d=$d day=".$daycnts[$m][$d][util::$DAY]." yeardayno_up=".$daycnts[$m][$d][self::$YEARDAYNO_UP]." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br>\n"; // test ###
}
else {
foreach( $recur[util::$BYDAY] as $bydayvalue ) {
$daynoexists = $daynosw = $daynamesw = false;
if( isset( $bydayvalue[util::$DAY] ) &&
( $bydayvalue[util::$DAY] == $daycnts[$m][$d][util::$DAY] ))
$daynamesw = true;
if( isset( $bydayvalue[0] )) {
$daynoexists = true;
if(( isset( $recur[util::$FREQ] ) &&
( $recur[util::$FREQ] == self::$MONTHLY )) ||
isset( $recur[util::$BYMONTH] ))
$daynosw = self::recurBYcntcheck( $bydayvalue['0'],
$daycnts[$m][$d][self::$MONTHDAYNO_UP],
$daycnts[$m][$d][self::$MONTHDAYNO_DOWN] );
elseif( isset( $recur[util::$FREQ] ) &&
( $recur[util::$FREQ] == self::$YEARLY ))
$daynosw = self::recurBYcntcheck( $bydayvalue['0'],
$daycnts[$m][$d][self::$YEARDAYNO_UP],
$daycnts[$m][$d][self::$YEARDAYNO_DOWN] );
}
// echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br>\n"; // test ###
if(( $daynoexists && $daynosw && $daynamesw ) ||
( ! $daynoexists && ! $daynosw && $daynamesw )) {
$updateOK = true;
break;
}
}
}
}
// echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'true' : 'false'; echo "<br>\n"; // test ###
/* check BYSETPOS */
if( $updateOK ) {
if( isset( $recur[util::$BYSETPOS] ) &&
( in_array( $recur[util::$FREQ], $YEAR2DAYARR))) {
if( isset( $recur[self::$WEEKLY] )) {
if( $bysetposWold == $daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$WEEKNO_UP] )
$bysetposw1[] = $wdateYMD;
else
$bysetposw2[] = $wdateYMD;
}
else {
if(( isset( $recur[util::$FREQ] ) &&
( self::$YEARLY == $recur[util::$FREQ] ) &&
( $bysetposYold == $wdate[util::$LCYEAR] )) ||
( isset( $recur[util::$FREQ] ) &&
( self::$MONTHLY == $recur[util::$FREQ] ) &&
(( $bysetposYold == $wdate[util::$LCYEAR] ) &&
( $bysetposMold == $wdate[util::$LCMONTH] ))) ||
( isset( $recur[util::$FREQ] ) &&
( self::$DAILY == $recur[util::$FREQ] ) &&
(( $bysetposYold == $wdate[util::$LCYEAR] ) &&
( $bysetposMold == $wdate[util::$LCMONTH]) &&
( $bysetposDold == $wdate[util::$LCDAY] )))) {
// echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."<br>\n"; // test ###
$bysetposymd1[] = $wdateYMD;
}
else {
// echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."<br>\n"; // test ###
$bysetposymd2[] = $wdateYMD;
}
} // end else
}
else {
if( checkdate((int) $wdate[util::$LCMONTH],
(int) $wdate[util::$LCDAY],
(int) $wdate[util::$LCYEAR] )) {
/* update result array if BYSETPOS is not set */
$recurCount++;
if( $fcnStartYMD <= $wdateYMD ) { // only output within period
$result[$wdateYMD] = true;
// echo "recur $wdateYMD, recurCount:{$recurCount}<br>\n"; // test ###
}
}
// else echo "recur, no date $wdateYMD<br>\n"; // test ###
$updateOK = false;
}
}
/* step up date */
self::stepdate( $wdate, $wdateYMD, $step);
/* check if BYSETPOS is set for updating result array */
if( $updateOK && isset( $recur[util::$BYSETPOS] )) {
$bysetpos = false;
if( isset( $recur[util::$FREQ] ) &&
( self::$YEARLY == $recur[util::$FREQ] ) &&
( $bysetposYold != $wdate[util::$LCYEAR] )) {
$bysetpos = true;
$bysetposYold = $wdate[util::$LCYEAR];
}
elseif( isset( $recur[util::$FREQ] ) &&
( self::$MONTHLY == $recur[util::$FREQ] &&
(( $bysetposYold != $wdate[util::$LCYEAR] ) ||
( $bysetposMold != $wdate[util::$LCMONTH] )))) {
$bysetpos = true;
$bysetposYold = $wdate[util::$LCYEAR];
$bysetposMold = $wdate[util::$LCMONTH];
}
elseif( isset( $recur[util::$FREQ] ) &&
( self::$WEEKLY == $recur[util::$FREQ] )) {
$weekno = (int) date( self::$W,
mktime( 0,
0,
$wkst,
$wdate[util::$LCMONTH],
$wdate[util::$LCDAY],
$wdate[util::$LCYEAR] ));
if( $bysetposWold != $weekno ) {
$bysetposWold = $weekno;
$bysetpos = true;
}
}
elseif( isset( $recur[util::$FREQ] ) &&
( self::$DAILY == $recur[util::$FREQ] ) &&
(( $bysetposYold != $wdate[util::$LCYEAR] ) ||
( $bysetposMold != $wdate[util::$LCMONTH] ) ||
( $bysetposDold != $wdate[util::$LCDAY] ))) {
$bysetpos = true;
$bysetposYold = $wdate[util::$LCYEAR];
$bysetposMold = $wdate[util::$LCMONTH];
$bysetposDold = $wdate[util::$LCDAY];
}
if( $bysetpos ) {
if( isset( $recur[util::$BYWEEKNO] )) {
$bysetposarr1 = & $bysetposw1;
$bysetposarr2 = & $bysetposw2;
}
else {
$bysetposarr1 = & $bysetposymd1;
$bysetposarr2 = & $bysetposymd2;
}
foreach( $recur[util::$BYSETPOS] as $ix ) {
if( 0 > $ix ) // both positive and negative BYSETPOS allowed
$ix = ( count( $bysetposarr1 ) + $ix + 1);
$ix--;
if( isset( $bysetposarr1[$ix] )) {
if( $fcnStartYMD <= $bysetposarr1[$ix] ) { // only output within period
// $testweekno = (int) date( $W, mktime( 0, 0, $wkst, (int) substr( $bysetposarr1[$ix], 4, 2 ), (int) substr( $bysetposarr1[$ix], 6, 2 ), (int) substr( $bysetposarr1[$ix], 0, 3 ))); // test ###
// echo " testYMD (weekno)=$bysetposarr1[$ix] ($testweekno)"; // test ###
$result[$bysetposarr1[$ix]] = true;
}
$recurCount++;
}
if( isset( $recur[util::$COUNT] ) && ( $recurCount >= $recur[util::$COUNT] ))
break;
}
// echo "<br>\n"; // test ###
$bysetposarr1 = $bysetposarr2;
$bysetposarr2 = [];
} // end if( $bysetpos )
} // end if( $updateOK && isset( $recur['BYSETPOS'] ))
} // end while( true )
// echo 'output='.str_replace( [PHP_EOL, ' '], '', var_export( $result, true ))."<br> \n"; // test ###
}
/**
* Checking BYDAY (etc) hits, recur2date help function
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.6.12 - 2011-01-03
* @param array $BYvalue
* @param int $upValue
* @param int $downValue
* @return bool
* @access private
* @static
*/
private static function recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
if( is_array( $BYvalue ) &&
( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )))
return true;
elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
return true;
else
return false;
}
/**
* (re-)Calculate internal index, recur2date help function
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.6.12 - 2011-01-03
* @param string $freq
* @param array $date
* @param int $wkst
* @return bool
* @access private
* @static
*/
private static function recurIntervalIx( $freq, $date, $wkst ) {
/* create interval index */
switch( $freq ) {
case self::$YEARLY :
$intervalix = $date[util::$LCYEAR];
break;
case self::$MONTHLY :
$intervalix = $date[util::$LCYEAR] . util::$MINUS . $date[util::$LCMONTH];
break;
case self::$WEEKLY :
$intervalix = (int) date( self::$W,
mktime( 0,
0,
$wkst,
(int) $date[util::$LCMONTH],
(int) $date[util::$LCDAY],
(int) $date[util::$LCYEAR] ));
break;
case self::$DAILY :
default:
$intervalix = $date[util::$LCYEAR] .
util::$MINUS .
$date[util::$LCMONTH] .
util::$MINUS .
$date[util::$LCDAY];
break;
}
return $intervalix;
}
/**
* Return updated date, array and timpstamp
*
* @param array $date date to step
* @param string $dateYMD date YMD
* @param array $step default array( util::$LCDAY => 1 )
* @return void
* @access private
* @static
*/
private static function stepdate( & $date, & $dateYMD, $step=null ) {
static $t = 't';
if( is_null( $step ))
$step = [util::$LCDAY => 1];
if( ! isset( $date[util::$LCHOUR] ))
$date[util::$LCHOUR] = 0;
if( ! isset( $date[util::$LCMIN] ))
$date[util::$LCMIN] = 0;
if( ! isset( $date[util::$LCSEC] ))
$date[util::$LCSEC] = 0;
if( isset( $step[util::$LCDAY] ))
$mcnt = date( $t,
mktime( (int) $date[util::$LCHOUR],
(int) $date[util::$LCMIN],
(int) $date[util::$LCSEC],
(int) $date[util::$LCMONTH],
(int) $date[util::$LCDAY],
(int) $date[util::$LCYEAR] ));
foreach( $step as $stepix => $stepvalue )
$date[$stepix] += $stepvalue;
if( isset( $step[util::$LCMONTH] )) {
if( 12 < $date[util::$LCMONTH] ) {
$date[util::$LCYEAR] += 1;
$date[util::$LCMONTH] -= 12;
}
}
elseif( isset( $step[util::$LCDAY] )) {
if( $mcnt < $date[util::$LCDAY] ) {
$date[util::$LCDAY] -= $mcnt;
$date[util::$LCMONTH] += 1;
if( 12 < $date[util::$LCMONTH] ) {
$date[util::$LCYEAR] += 1;
$date[util::$LCMONTH] -= 12;
}
}
}
$dateYMD = sprintf( util::$YMD, (int) $date[util::$LCYEAR],
(int) $date[util::$LCMONTH],
(int) $date[util::$LCDAY] );
}
/**
* Return initiated $daycnts
*
* @param array $wdate
* @param array $recur
* @param int $wkst
* @return array
* @access private
* @static
*/
private static function initDayCnts( array $wdate, array $recur, $wkst ) {
$daycnts = [];
$yeardaycnt = [];
$yeardays = 0;
foreach( self::$DAYNAMES as $dn )
$yeardaycnt[$dn] = 0;
for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
$daycnts[$m] = [];
$weekdaycnt = [];
foreach( self::$DAYNAMES as $dn )
$weekdaycnt[$dn] = 0;
$mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate[util::$LCYEAR] ));
for( $d = 1; $d <= $mcnt; $d++ ) {
$daycnts[$m][$d] = [];
if( isset( $recur[util::$BYYEARDAY] )) {
$yeardays++;
$daycnts[$m][$d][self::$YEARCNT_UP] = $yeardays;
}
if( isset( $recur[util::$BYDAY] )) {
$day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate[util::$LCYEAR] ));
$day = self::$DAYNAMES[$day];
$daycnts[$m][$d][util::$DAY] = $day;
$weekdaycnt[$day]++;
$daycnts[$m][$d][self::$MONTHDAYNO_UP] = $weekdaycnt[$day];
$yeardaycnt[$day]++;
$daycnts[$m][$d][self::$YEARDAYNO_UP] = $yeardaycnt[$day];
}
if( isset( $recur[util::$BYWEEKNO] ) ||
( $recur[util::$FREQ] == self::$WEEKLY ))
$daycnts[$m][$d][self::$WEEKNO_UP] = (int) date( self::$W,
mktime( 0,
0,
$wkst,
$m,
$d,
$wdate[util::$LCYEAR] ));
} // end for( $d = 1; $d <= $mcnt; $d++ )
} // end for( $m = 1; $m <= 12; $m++ )
$daycnt = 0;
$yeardaycnt = [];
if( isset( $recur[util::$BYWEEKNO] ) ||
( $recur[util::$FREQ] == self::$WEEKLY )) {
$weekno = null;
for( $d=31; $d > 25; $d-- ) { // get last weekno for year
if( ! $weekno )
$weekno = $daycnts[12][$d][self::$WEEKNO_UP];
elseif( $weekno < $daycnts[12][$d][self::$WEEKNO_UP] ) {
$weekno = $daycnts[12][$d][self::$WEEKNO_UP];
break;
}
}
}
for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
$weekdaycnt = [];
foreach( self::$DAYNAMES as $dn )
$yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
$monthcnt = 0;
$mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate[util::$LCYEAR] ));
for( $d = $mcnt; $d > 0; $d-- ) {
if( isset( $recur[util::$BYYEARDAY] )) {
$daycnt -= 1;
$daycnts[$m][$d][self::$YEARCNT_DOWN] = $daycnt;
}
if( isset( $recur[util::$BYMONTHDAY] )) {
$monthcnt -= 1;
$daycnts[$m][$d][self::$MONTHCNT_DOWN] = $monthcnt;
}
if( isset( $recur[util::$BYDAY] )) {
$day = $daycnts[$m][$d][util::$DAY];
$weekdaycnt[$day] -= 1;
$daycnts[$m][$d][self::$MONTHDAYNO_DOWN] = $weekdaycnt[$day];
$yeardaycnt[$day] -= 1;
$daycnts[$m][$d][self::$YEARDAYNO_DOWN] = $yeardaycnt[$day];
}
if( isset( $recur[util::$BYWEEKNO] ) ||
( $recur[util::$FREQ] == self::$WEEKLY ))
$daycnts[$m][$d][self::$WEEKNO_DOWN] = ( $daycnts[$m][$d][self::$WEEKNO_UP] - $weekno - 1 );
}
} // end for( $m = 12; $m > 0; $m-- )
return $daycnts;
}
/**
* Return a reformatted input date
*
* @param mixed $aDate
* @access private
* @static
*/
private static function reFormatDate( & $aDate ) {
static $YMDHIS2 = 'Y-m-d H:i:s';
switch( true ) {
case ( is_string( $aDate )) :
util::strDate2arr( $aDate );
break;
case ( util::isDateTimeClass( $aDate )) :
$aDate = $aDate->format( $YMDHIS2 );
util::strDate2arr( $aDate );
break;
default :
break;
}
foreach( $aDate as $k => $v ) {
if( ctype_digit( $v ))
$aDate[$k] = (int) $v;
}
}
}

View File

@@ -0,0 +1,129 @@
<?php
/**
* iCalcreator, a PHP rfc2445/rfc5545 solution.
*
* This file is a part of iCalcreator.
*
* Copyright (c) 2007-2017 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
* Link http://kigkonsult.se/iCalcreator/index.php
* Package iCalcreator
* Version 2.24
* License Subject matter of licence is the software iCalcreator.
* The above copyright, link, package and version notices,
* this licence notice and the [rfc5545] PRODID as implemented and
* invoked in iCalcreator shall be included in all copies or
* substantial portions of the iCalcreator.
* iCalcreator can be used either under the terms of
* a proprietary license, available at <https://kigkonsult.se/>
* or the GNU Affero General Public License, version 3:
* iCalcreator is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* iCalcreator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
* License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
namespace kigkonsult\iCalcreator\util;
use kigkonsult\iCalcreator\vcalendar;
/**
* iCalcreator redirect support class
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.6 - 2017-04-13
*/
class utilRedirect {
/**
* HTTP headers
*
* @var array $headers
* @access private
* @static
*/
private static $headers = ['Content-Encoding: gzip',
'Vary: *',
'Content-Length: %s',
'Content-Type: text/calendar; charset=utf-8',
'Content-Disposition: attachment; filename="%s"',
'Content-Disposition: inline; filename="%s"',
'Cache-Control: max-age=10'];
/**
* Return created, updated and/or parsed calendar, sending a HTTP redirect header.
*
* @param vcalendar $calendar
* @param bool $utf8Encode
* @param bool $gzip
* @param bool $cdType true : Content-Disposition: attachment... (default), false : ...inline...
* @return bool true on success, false on error
* @static
*/
public static function returnCalendar( vcalendar $calendar,
$utf8Encode=false,
$gzip=false,
$cdType=true ) {
static $ICR = 'iCr';
$filename = $calendar->getConfig( util::$FILENAME );
$output = $calendar->createCalendar();
if( $utf8Encode )
$output = utf8_encode( $output );
$fsize = null;
if( $gzip ) {
$output = gzencode( $output, 9 );
$fsize = strlen( $output );
header( self::$headers[0] );
header( self::$headers[1] );
}
else {
if( false !== ( $temp = tempnam( sys_get_temp_dir(), $ICR ))) {
if( false !== file_put_contents( $temp, $output ))
$fsize = @filesize( $temp );
unlink( $temp );
}
}
if( ! empty( $fsize ))
header( sprintf( self::$headers[2], $fsize ));
header( self::$headers[3] );
$cdType = ( $cdType ) ? 4 : 5;
header( sprintf( self::$headers[$cdType], $filename ));
header( self::$headers[6] );
echo $output;
return true;
}
/**
* If recent version of calendar file exists (default one hour), an HTTP redirect header is sent
*
* @param vcalendar $calendar
* @param int $timeout default 3600 sec
* @param bool $cdType true : Content-Disposition: attachment... (default), false : ...inline...
* @return bool true on success, false on error
* @static
*/
public static function useCachedCalendar( vcalendar $calendar,
$timeout=3600,
$cdType=true ) {
static $R = 'r';
if( false === ( $dirfile = $calendar->getConfig( util::$URL )))
$dirfile = $calendar->getConfig( util::$DIRFILE );
if( ! is_file( $dirfile ) || ! is_readable( $dirfile ))
return false;
if( time() - filemtime( $dirfile ) > $timeout )
return false;
clearstatcache();
$fsize = @filesize( $dirfile );
$filename = $calendar->getConfig( util::$FILENAME );
header( self::$headers[3] );
if( ! empty( $fsize ))
header( sprintf( self::$headers[2], $fsize ));
$cdType = ( $cdType ) ? 4 : 5;
header( sprintf( self::$headers[$cdType], $filename ));
header( self::$headers[6] );
if( false === ( $fp = @fopen( $dirfile, $R )))
return false;
fpassthru( $fp );
fclose( $fp );
return true;
}
}

View File

@@ -0,0 +1,533 @@
<?php
/**
* iCalcreator, a PHP rfc2445/rfc5545 solution.
*
* This file is a part of iCalcreator.
*
* Copyright (c) 2007-2017 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
* Link http://kigkonsult.se/iCalcreator/index.php
* Package iCalcreator
* Version 2.24
* License Subject matter of licence is the software iCalcreator.
* The above copyright, link, package and version notices,
* this licence notice and the [rfc5545] PRODID as implemented and
* invoked in iCalcreator shall be included in all copies or
* substantial portions of the iCalcreator.
* iCalcreator can be used either under the terms of
* a proprietary license, available at <https://kigkonsult.se/>
* or the GNU Affero General Public License, version 3:
* iCalcreator is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* iCalcreator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
* License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
namespace kigkonsult\iCalcreator\util;
/**
* iCalcreator EXDATE/RDATE support class
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-04-04
*/
class utilRexdate {
/**
* Check (EXDATE/RDATE) date(-time) and params arrays for an opt. timezone
*
* If it is a DATE-TIME or DATE, updates $parno and (opt) $params)
* @param array $theDate date to check
* @param int $parno no of date parts (i.e. year, month.. .)
* @param array $params property parameters
* @access private
* @static
*/
private static function chkDateCfg( $theDate, & $parno, & $params ) {
$paramsValueIsDATE = util::isParamsValueSet( [util::$LCparams => $params],
util::$DATE );
switch( true ) {
case ( isset( $params[util::$TZID] )) :
$parno = 6;
break;
case ( $paramsValueIsDATE ) :
$params[util::$VALUE] = util::$DATE;
$parno = 3;
break;
default:
if( util::isParamsValueSet( [util::$LCparams => $params],
util::$PERIOD )) {
$params[util::$VALUE] = util::$PERIOD;
$parno = 7;
}
switch( true ) {
case ( is_array( $theDate )) :
if( isset( $theDate[util::$LCTIMESTAMP] ))
$tzid = ( isset( $theDate[util::$LCtz] ))
? $theDate[util::$LCtz] : null;
else
$tzid = ( isset( $theDate[util::$LCtz] ))
? $theDate[util::$LCtz] : ( 7 == count( $theDate )) ? end( $theDate ) : null;
if( ! empty( $tzid )) {
$parno = 7;
if( ! util::isOffset( $tzid ))
$params[util::$TZID] = $tzid; // save only timezone
}
elseif( ! $parno &&
( 3 == count( $theDate )) &&
$paramsValueIsDATE )
$parno = 3;
else
$parno = 6;
break;
default : // i.e. string
$date = trim((string) $theDate );
if( util::$Z == substr( $date, -1 ))
$parno = 7; // UTC DATE-TIME
elseif((( 8 == strlen( $date ) && ctype_digit( $date )) ||
( 11 >= strlen( $date )))
&& $paramsValueIsDATE )
$parno = 3;
$date = util::strDate2ArrayDate( $date, $parno );
unset( $date[util::$UNPARSEDTEXT] );
if( ! empty( $date[util::$LCtz] )) {
$parno = 7;
if( ! util::isOffset( $date[util::$LCtz] ))
$params[util::$TZID] = $date[util::$LCtz]; // save only timezone
}
elseif( empty( $parno ))
$parno = 6;
} // end switch( true )
if( isset( $params[util::$TZID] ))
$parno = 6;
break;
} // end switch( true )
return true;
}
/**
* Return formatted output for calendar component property data value type recur
*
* @param array $exdateData
* @param bool $allowEmpty
* @return string
* @static
*/
public static function formatExdate( $exdateData, $allowEmpty ) {
static $SORTER1 = ['kigkonsult\iCalcreator\vcalendarSortHandler',
'sortExdate1'];
static $SORTER2 = ['kigkonsult\iCalcreator\vcalendarSortHandler',
'sortExdate2'];
$output = null;
$exdates = [];
foreach(( array_keys( $exdateData )) as $ex ) {
$theExdate = $exdateData[$ex];
if( empty( $theExdate[util::$LCvalue] )) {
if( $allowEmpty )
$output .= util::createElement( util::$EXDATE );
continue;
}
if( 1 < count( $theExdate[util::$LCvalue] ))
usort( $theExdate[util::$LCvalue], $SORTER1 );
$exdates[] = $theExdate;
}
if( 1 < count( $exdates ))
usort( $exdates, $SORTER2 );
foreach(( array_keys( $exdates )) as $ex ) {
$theExdate = $exdates[$ex];
$content = $attributes = null;
foreach(( array_keys( $theExdate[util::$LCvalue] )) as $eix ) {
$exdatePart = $theExdate[util::$LCvalue][$eix];
$parno = count( $exdatePart );
$formatted = util::date2strdate( $exdatePart, $parno );
if( isset( $theExdate[util::$LCparams][util::$TZID] ))
$formatted = str_replace( util::$Z, null, $formatted);
if( 0 < $eix ) {
if( isset( $theExdate[util::$LCvalue][0][util::$LCtz] )) {
if( ctype_digit( substr( $theExdate[util::$LCvalue][0][util::$LCtz], -4 )) ||
( util::$Z == $theExdate[util::$LCvalue][0][util::$LCtz] )) {
if( util::$Z != substr( $formatted, -1 ))
$formatted .= util::$Z;
}
else
$formatted = str_replace( util::$Z, null, $formatted );
}
else
$formatted = str_replace( util::$Z, null, $formatted );
} // end if( 0 < $eix )
$content .= ( 0 < $eix ) ? util::$COMMA . $formatted : $formatted;
} // end foreach(( array_keys( $theExdate[util::$LCvalue]...
$output .= util::createElement( util::$EXDATE,
util::createParams( $theExdate[util::$LCparams] ),
$content );
} // end foreach(( array_keys( $exdates...
return $output;
}
/**
* Return prepared calendar component property exdate input
*
* @param array $exdates
* @param array $params
* @return array
* @static
*/
public static function prepInputExdate( $exdates, $params=null ) {
static $GMTUTCZARR = ['GMT', 'UTC', 'Z'];
$input = [util::$LCparams => util::setParams( $params,
util::$DEFAULTVALUEDATETIME )];
$toZ = ( isset( $input[util::$LCparams][util::$TZID] ) &&
in_array( strtoupper( $input[util::$LCparams][util::$TZID] ),
$GMTUTCZARR ))
? true : false;
/* ev. check 1:st date and save ev. timezone **/
self::chkDateCfg( reset( $exdates ), $parno, $input[util::$LCparams] );
util::existRem( $input[util::$LCparams],
util::$VALUE,
util::$DATE_TIME ); // remove default parameter
foreach(( array_keys( $exdates )) as $eix ) {
$theExdate = $exdates[$eix];
util::strDate2arr( $theExdate );
if( util::isArrayTimestampDate( $theExdate )) {
if( isset( $theExdate[util::$LCtz] ) &&
! util::isOffset( $theExdate[util::$LCtz] )) {
if( isset( $input[util::$LCparams][util::$TZID] ))
$theExdate[util::$LCtz] = $input[util::$LCparams][util::$TZID];
else
$input[util::$LCparams][util::$TZID] = $theExdate[util::$LCtz];
}
$exdatea = util::timestamp2date( $theExdate, $parno );
}
elseif( is_array( $theExdate )) {
$d = util::chkDateArr( $theExdate, $parno );
if( isset( $d[util::$LCtz] ) &&
( util::$Z != $d[util::$LCtz] ) &&
util::isOffset( $d[util::$LCtz] )) {
$strdate = sprintf( util::$YMDHISE, (int) $d[util::$LCYEAR],
(int) $d[util::$LCMONTH],
(int) $d[util::$LCDAY],
(int) $d[util::$LCHOUR],
(int) $d[util::$LCMIN],
(int) $d[util::$LCSEC],
$d[util::$LCtz] );
$exdatea = util::strDate2ArrayDate( $strdate, 7 );
unset( $exdatea[util::$UNPARSEDTEXT] );
}
else
$exdatea = $d;
}
elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18
$exdatea = util::strDate2ArrayDate( $theExdate, $parno );
unset( $exdatea[util::$UNPARSEDTEXT] );
}
if( 3 == $parno )
unset( $exdatea[util::$LCHOUR],
$exdatea[util::$LCMIN],
$exdatea[util::$LCSEC],
$exdatea[util::$LCtz] );
elseif( isset( $exdatea[util::$LCtz] ))
$exdatea[util::$LCtz] = (string) $exdatea[util::$LCtz];
if( isset( $input[util::$LCparams][util::$TZID] ) ||
( isset( $exdatea[util::$LCtz] ) &&
! util::isOffset( $exdatea[util::$LCtz] )) ||
( isset( $input[util::$LCvalue][0] ) &&
( ! isset( $input[util::$LCvalue][0][util::$LCtz] ))) ||
( isset( $input[util::$LCvalue][0][util::$LCtz] ) &&
! util::isOffset( $input[util::$LCvalue][0][util::$LCtz] )))
unset( $exdatea[util::$LCtz] );
if( $toZ ) // time zone Z
$exdatea[util::$LCtz] = util::$Z;
$input[util::$LCvalue][] = $exdatea;
} // end foreach(( array_keys( $exdates...
if( 0 >= count( $input[util::$LCvalue] ))
return false;
if( 3 == $parno ) {
$input[util::$LCparams][util::$VALUE] = util::$DATE;
unset( $input[util::$LCparams][util::$TZID] );
}
if( $toZ ) // time zone Z
unset( $input[util::$LCparams][util::$TZID] );
return $input;
}
/**
* Return formatted output for calendar component property rdate
*
* @param array $rdateData
* @param bool $allowEmpty
* @param string $objName
* @return string
* @static
*/
public static function formatRdate( $rdateData, $allowEmpty, $objName ) {
static $SORTER1 = ['kigkonsult\iCalcreator\vcalendarSortHandler',
'sortRdate1'];
static $SORTER2 = ['kigkonsult\iCalcreator\vcalendarSortHandler',
'sortRdate2'];
$utcTime = ( in_array( $objName, util::$TZCOMPS )) ? true : false;
$output = null;
$rdates = [];
foreach(( array_keys( $rdateData )) as $rpix ) {
$theRdate = $rdateData[$rpix];
if( empty( $theRdate[util::$LCvalue] )) {
if( $allowEmpty )
$output .= util::createElement( util::$RDATE );
continue;
}
if( $utcTime )
unset( $theRdate[util::$LCparams][util::$TZID] );
if( 1 < count( $theRdate[util::$LCvalue] ))
usort( $theRdate[util::$LCvalue], $SORTER1 );
$rdates[] = $theRdate;
}
if( 1 < count( $rdates ))
usort( $rdates, $SORTER2 );
foreach(( array_keys( $rdates )) as $rpix ) {
$theRdate = $rdates[$rpix];
$attributes = util::createParams( $theRdate[util::$LCparams] );
$cnt = count( $theRdate[util::$LCvalue] );
$content = null;
$rno = 1;
foreach(( array_keys( $theRdate[util::$LCvalue] )) as $rix ) {
$rdatePart = $theRdate[util::$LCvalue][$rix];
$contentPart = null;
if( is_array( $rdatePart ) &&
util::isParamsValueSet( $theRdate, util::$PERIOD )) { // PERIOD
if( $utcTime )
unset( $rdatePart[0][util::$LCtz] );
$formatted = util::date2strdate( $rdatePart[0] ); // PERIOD part 1
if( $utcTime || !empty( $theRdate[util::$LCparams][util::$TZID] ))
$formatted = str_replace( util::$Z, null, $formatted);
$contentPart .= $formatted;
$contentPart .= '/';
$cnt2 = count( $rdatePart[1]);
if( array_key_exists( util::$LCYEAR, $rdatePart[1] )) {
if( array_key_exists( util::$LCHOUR, $rdatePart[1] ))
$cnt2 = 7; // date-time
else
$cnt2 = 3; // date
}
elseif( array_key_exists( util::$LCWEEK, $rdatePart[1] )) // duration
$cnt2 = 5;
if(( 7 == $cnt2 ) && // period= -> date-time
isset( $rdatePart[1][util::$LCYEAR] ) &&
isset( $rdatePart[1][util::$LCMONTH] ) &&
isset( $rdatePart[1][util::$LCDAY] )) {
if( $utcTime )
unset( $rdatePart[1][util::$LCtz] );
$formatted = util::date2strdate( $rdatePart[1] ); // PERIOD part 2
if( $utcTime || !empty( $theRdate[util::$LCparams][util::$TZID] ))
$formatted = str_replace( util::$Z, null, $formatted );
$contentPart .= $formatted;
}
else { // period= -> dur-time
$contentPart .= util::duration2str( $rdatePart[1] );
}
} // PERIOD end
else { // SINGLE date start
if( $utcTime )
unset( $rdatePart[util::$LCtz] );
$parno = ( util::isParamsValueSet( $theRdate, util::$DATE )) ? 3 : null;
$formatted = util::date2strdate( $rdatePart, $parno );
if( $utcTime || !empty( $theRdate[util::$LCparams][util::$TZID] ))
$formatted = str_replace( util::$Z, null, $formatted);
$contentPart .= $formatted;
}
$content .= $contentPart;
if( $rno < $cnt )
$content .= util::$COMMA;
$rno++;
} // end foreach(( array_keys( $theRdate[util::$LCvalue]...
$output .= util::createElement( util::$RDATE,
$attributes,
$content );
} // foreach(( array_keys( $rdates...
return $output;
}
/**
* Return prepared calendar component property rdate input
*
* @param array $rdates
* @param array $params
* @param string $objName
* @return array
* @static
*/
public static function prepInputRdate( $rdates, $params, $objName ) {
static $PREFIXARR = ['P', '+', '-'];
static $GMTUTCZARR = ['GMT', 'UTC', 'Z'];
$input = [util::$LCparams => util::setParams( $params,
util::$DEFAULTVALUEDATETIME )];
if( in_array( $objName, util::$TZCOMPS )) {
unset( $input[util::$LCparams][util::$TZID] );
$input[util::$LCparams][util::$VALUE] = util::$DATE_TIME;
}
$toZ = ( isset( $params[util::$TZID] ) &&
in_array( strtoupper( $params[util::$TZID] ),
$GMTUTCZARR ))
? true : false;
/* check if PERIOD, if not set */
if(( ! isset( $input[util::$LCparams][util::$VALUE] ) ||
( ! util::isParamsValueSet( $input, util::$DATE ) &&
! util::isParamsValueSet( $input, util::$PERIOD ))) &&
isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) &&
isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && ! isset( $rdates[0][util::$LCTIMESTAMP] ) &&
(( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0][util::$LCTIMESTAMP] ) ||
util::isArrayDate( $rdates[0][0] ))) ||
( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) &&
( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] ))))))
$input[util::$LCparams][util::$VALUE] = util::$PERIOD;
/* check 1:st date, upd. $parno (opt) and save opt. timezone **/
$date = reset( $rdates );
if( isset( $input[util::$LCparams][util::$VALUE] ) &&
( util::$PERIOD == $input[util::$LCparams][util::$VALUE] )) // PERIOD
$date = reset( $date );
self::chkDateCfg( $date, $parno, $input[util::$LCparams] );
util::existRem( $input[util::$LCparams],
util::$VALUE,
util::$DATE_TIME ); // remove default
foreach( $rdates as $rpix => $theRdate ) {
$inputa = null;
util::strDate2arr( $theRdate );
if( is_array( $theRdate )) {
if( isset( $input[util::$LCparams][util::$VALUE] ) &&
( util::$PERIOD == $input[util::$LCparams][util::$VALUE] )) { // PERIOD
foreach( $theRdate as $rix => $rPeriod ) {
util::strDate2arr( $theRdate );
if( is_array( $rPeriod )) {
if( util::isArrayTimestampDate( $rPeriod )) { // timestamp
if( isset( $rPeriod[util::$LCtz] ) &&
! util::isOffset( $rPeriod[util::$LCtz] )) {
if( isset( $input[util::$LCparams][util::$TZID] ))
$rPeriod[util::$LCtz] = $input[util::$LCparams][util::$TZID];
else
$input[util::$LCparams][util::$TZID] = $rPeriod[util::$LCtz];
}
$inputab = util::timestamp2date( $rPeriod, $parno );
} // end if( util::isArrayTimestampDate( $rPeriod ))
elseif( util::isArrayDate( $rPeriod )) {
$d = ( 3 < count ( $rPeriod ))
? util::chkDateArr( $rPeriod, $parno )
: util::chkDateArr( $rPeriod, 6 );
if( isset( $d[util::$LCtz] ) &&
( util::$Z != $d[util::$LCtz] ) &&
util::isOffset( $d[util::$LCtz] )) {
$inputab = util::strDate2ArrayDate( sprintf( util::$YMDHISE,
(int) $d[util::$LCYEAR],
(int) $d[util::$LCMONTH],
(int) $d[util::$LCDAY],
(int) $d[util::$LCHOUR],
(int) $d[util::$LCMIN],
(int) $d[util::$LCSEC],
$d[util::$LCtz] ),
7 );
unset( $inputab[util::$UNPARSEDTEXT] );
}
else
$inputab = $d;
} // end elseif( util::isArrayDate( $rPeriod ))
elseif(( 1 == count( $rPeriod )) &&
( 8 <= strlen( reset( $rPeriod )))) { // text-date
$inputab = util::strDate2ArrayDate( reset( $rPeriod ), $parno );
unset( $inputab[util::$UNPARSEDTEXT] );
}
else // array format duration
$inputab = util::duration2arr( $rPeriod );
} // end if( is_array( $rPeriod ))
elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration
( in_array( $rPeriod[0], $PREFIXARR ))) {
if( 'P' != $rPeriod[0] )
$rPeriod = substr( $rPeriod, 1 );
$inputab = util::durationStr2arr( $rPeriod );
}
elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18
$inputab = util::strDate2ArrayDate( $rPeriod, $parno );
unset( $inputab[util::$UNPARSEDTEXT] );
}
if(( 0 == $rpix ) && ( 0 == $rix )) {
if( isset( $inputab[util::$LCtz] ) &&
in_array( strtoupper( $inputab[util::$LCtz] ),
$GMTUTCZARR )) {
$inputab[util::$LCtz] = util::$Z;
$toZ = true;
}
}
else {
if( isset( $inputa[0][util::$LCtz] ) &&
( util::$Z == $inputa[0][util::$LCtz] ) &&
isset( $inputab[util::$LCYEAR] ))
$inputab[util::$LCtz] = util::$Z;
else
unset( $inputab[util::$LCtz] );
}
if( $toZ && isset( $inputab[util::$LCYEAR] ) )
$inputab[util::$LCtz] = util::$Z;
$inputa[] = $inputab;
} // end foreach( $theRdate as $rix => $rPeriod )
} // PERIOD end
elseif ( util::isArrayTimestampDate( $theRdate )) { // timestamp
if( isset( $theRdate[util::$LCtz] ) &&
! util::isOffset( $theRdate[util::$LCtz] )) {
if( isset( $input[util::$LCparams][util::$TZID] ))
$theRdate[util::$LCtz] = $input[util::$LCparams][util::$TZID];
else
$input[util::$LCparams][util::$TZID] = $theRdate[util::$LCtz];
}
$inputa = util::timestamp2date( $theRdate, $parno );
}
else { // date[-time]
$inputa = util::chkDateArr( $theRdate, $parno );
if( isset( $inputa[util::$LCtz] ) &&
( util::$Z != $inputa[util::$LCtz] ) &&
util::isOffset( $inputa[util::$LCtz] )) {
$inputa = util::strDate2ArrayDate( sprintf( util::$YMDHISE,
(int) $inputa[util::$LCYEAR],
(int) $inputa[util::$LCMONTH],
(int) $inputa[util::$LCDAY],
(int) $inputa[util::$LCHOUR],
(int) $inputa[util::$LCMIN],
(int) $inputa[util::$LCSEC],
$inputa[util::$LCtz] ),
7 );
unset( $inputa[util::$UNPARSEDTEXT] );
}
}
} // end if( is_array( $theRdate ))
elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18
$inputa = util::strDate2ArrayDate( $theRdate, $parno );
unset( $inputa[util::$UNPARSEDTEXT] );
if( $toZ )
$inputa[util::$LCtz] = util::$Z;
}
if( ! isset( $input[util::$LCparams][util::$VALUE] ) ||
( util::$PERIOD != $input[util::$LCparams][util::$VALUE] )) { // no PERIOD
if(( 0 == $rpix ) && !$toZ )
$toZ = ( isset( $inputa[util::$LCtz] ) &&
in_array( strtoupper( $inputa[util::$LCtz] ), $GMTUTCZARR ))
? true : false;
if( $toZ )
$inputa[util::$LCtz] = util::$Z;
if( 3 == $parno )
unset( $inputa[util::$LCHOUR],
$inputa[util::$LCMIN],
$inputa[util::$LCSEC],
$inputa[util::$LCtz] );
elseif( isset( $inputa[util::$LCtz] ))
$inputa[util::$LCtz] = (string) $inputa[util::$LCtz];
if( isset( $input[util::$LCparams][util::$TZID] ) ||
( isset( $input[util::$LCvalue][0] ) &&
( ! isset( $input[util::$LCvalue][0][util::$LCtz] ))))
if( !$toZ )
unset( $inputa[util::$LCtz] );
}
$input[util::$LCvalue][] = $inputa;
}
if( 3 == $parno ) {
$input[util::$LCparams][util::$VALUE] = util::$DATE;
unset( $input[util::$LCparams][util::$TZID] );
}
if( $toZ )
unset( $input[util::$LCparams][util::$TZID] );
return $input;
}
}

View File

@@ -0,0 +1,822 @@
<?php
/**
* iCalcreator, a PHP rfc2445/rfc5545 solution.
*
* This file is a part of iCalcreator.
*
* Copyright (c) 2007-2017 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
* Link http://kigkonsult.se/iCalcreator/index.php
* Package iCalcreator
* Version 2.24
* License Subject matter of licence is the software iCalcreator.
* The above copyright, link, package and version notices,
* this licence notice and the [rfc5545] PRODID as implemented and
* invoked in iCalcreator shall be included in all copies or
* substantial portions of the iCalcreator.
* iCalcreator can be used either under the terms of
* a proprietary license, available at <https://kigkonsult.se/>
* or the GNU Affero General Public License, version 3:
* iCalcreator is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* iCalcreator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
* License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
namespace kigkonsult\iCalcreator\util;
use kigkonsult\iCalcreator\vcalendar;
use kigkonsult\iCalcreator\calendarComponent;
use kigkonsult\iCalcreator\vcalendarSortHandler;
use kigkonsult\iCalcreator\iCaldateTime;
/**
* iCalcreator geo support class
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.6 - 2017-04-14
*/
class utilSelect {
/**
* Return selected components from calendar on date or selectOption basis
*
* DTSTART MUST be set for every component.
* No check of date.
* @param object $calendar
* @param mixed $startY (int) start Year, default current Year
* ALT. (obj) start date (datetime)
* ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] )
* @param mixed $startM (int) start Month, default current Month
* ALT. (obj) end date (datetime)
* @param int $startD start Day, default current Day
* @param int $endY end Year, default $startY
* @param int $endM end Month, default $startM
* @param int $endD end Day, default $startD
* @param mixed $cType calendar component type(-s), default false=all else string/array type(-s)
* @param bool $flat false (default) => output : array[Year][Month][Day][]
* true => output : array[] (ignores split)
* @param bool $any true (default) - select component(-s) that occurs within period
* false - only component(-s) that starts within period
* @param bool $split true (default) - one component copy every DAY it occurs during the
* period (implies flat=false)
* false - one occurance of component only in output array
* @return mixed array on success, bool false on error
* @static
*/
public static function selectComponents( vcalendar $calendar,
$startY=null,
$startM=null,
$startD=null,
$endY=null,
$endM=null,
$endD=null,
$cType=null,
$flat=null,
$any=null,
$split=null ) {
static $Y = 'Y';
static $M = 'm';
static $D = 'd';
static $STRTOLOWER = 'strtolower';
static $P1D = 'P1D';
static $DTENDEXIST = 'dtendExist';
static $DUEEXIST = 'dueExist';
static $DURATIONEXIST = 'durationExist';
static $ENDALLDAYEVENT = 'endAllDayEvent';
static $MINUS1DAY = '-1 day';
static $RANGE = 'RANGE';
static $THISANDFUTURE = 'THISANDFUTURE';
static $YMDHIS2 = 'Y-m-d H:i:s';
static $PRA = '%a';
static $YMD2 = 'Y-m-d';
static $DAYOFDAYS = 'day %d of %d';
static $SORTER = ['kigkonsult\iCalcreator\vcalendarSortHandler',
'cmpfcn'];
/* check if empty calendar */
if( 1 > $calendar->countComponents())
return false;
if( is_array( $startY ))
return self::selectComponents2( $calendar, $startY );
/* check default dates */
if( util::isDateTimeClass( $startY ) &&
util::isDateTimeClass( $startM )) {
$endY = $startM->format( $Y );
$endM = $startM->format( $M );
$endD = $startM->format( $D );
$startD = $startY->format( $D );
$startM = $startY->format( $M );
$startY = $startY->format( $Y );
}
else {
if( empty( $startY )) $startY = date( $Y );
if( empty( $startM )) $startM = date( $M );
if( empty( $startD )) $startD = date( $D );
if( empty( $endY )) $endY = $startY;
if( empty( $endM )) $endM = $startM;
if( empty( $endD )) $endD = $startD;
}
/* check component types */
if( empty( $cType ))
$cType = util::$VCOMPS;
else {
if( ! is_array( $cType ))
$cType = [$cType];
$cType = array_map( $STRTOLOWER, $cType );
foreach( $cType as $cix => $theType ) {
if( ! in_array( $theType, util::$VCOMPS ))
$cType[$cix] = util::$LCVEVENT;
}
$cType = array_unique( $cType );
}
$flat = ( is_null( $flat )) ? false : (bool) $flat; // defaults
$any = ( is_null( $any )) ? true : (bool) $any;
$split = ( is_null( $split )) ? true : (bool) $split;
if(( false === $flat ) && ( false === $any )) // invalid combination
$split = false;
if(( true === $flat ) && ( true === $split )) // invalid combination
$split = false;
// echo " args={$startY}-{$startM}-{$startD} - {$endY}-{$endM}-{$endD}, flat={$flat}, any={$any}, split={$split}<br>\n"; $tcnt = 0;// test ###
/* iterate components */
$result = [];
$calendar->sort( util::$UID );
$compUIDcmp = null;
$exdatelist = $recurrIdList = [];
$INTERVAL_P1D = new \DateInterval( $P1D );
// echo ' comp ix : ' . implode( ',', ( array_keys( $calendar->components ))) . "<br>\n"; // test ###
$cix = -1;
while( $component = $calendar->getComponent()) {
$cix += 1;
if( empty( $component ))
continue;
/* deselect unvalid type components */
if( ! in_array( $component->objName, $cType ))
continue;
unset( $compStart, $compEnd );
/* select start from dtstart or due if dtstart is missing */
$prop = $component->getProperty( util::$DTSTART,
false,
true );
if( empty( $prop ) &&
( $component->objName == util::$LCVTODO ) &&
( false === ( $prop = $component->getProperty( util::$DUE,
false,
true ))))
continue;
if( empty( $prop ))
continue;
/* get UID */
$compUID = $component->getProperty( util::$UID );
// echo 'START comp(' . $cix . ') ' . $component->objName . ', UID:' . $compUID . "<br>\n"; // test ###
if( $compUIDcmp != $compUID ) {
$compUIDcmp = $compUID;
$exdatelist = $recurrIdList = [];
}
// echo "#$cix".PHP_EOL.var_export( $component, true ) . "\n"; // test ###
$compStart = iCaldateTime::factory( $prop[util::$LCvalue],
$prop[util::$LCparams],
$prop[util::$LCvalue] );
$dtstartTz = $compStart->getTimezoneName();
if( util::isParamsValueSet( $prop, util::$DATE ))
$compStartHis = null;
else {
$his = $compStart->getTime();
$compStartHis = sprintf( util::$HIS, (int) $his[0],
(int) $his[1],
(int) $his[2] );
}
/* get end date from dtend/due/duration properties */
if( false !== ( $prop = $component->getProperty( util::$DTEND,
false,
true ))) {
$compEnd = iCaldateTime::factory( $prop[util::$LCvalue],
$prop[util::$LCparams],
$prop[util::$LCvalue],
$dtstartTz );
$compEnd->SCbools[$DTENDEXIST] = true;
}
if( empty( $prop ) &&
( $component->objName == util::$LCVTODO ) &&
( false !== ( $prop = $component->getProperty( util::$DUE,
false,
true )))) {
$compEnd = iCaldateTime::factory( $prop[util::$LCvalue],
$prop[util::$LCparams],
$prop[util::$LCvalue],
$dtstartTz );
$compEnd->SCbools[$DUEEXIST] = true;
}
if( empty( $prop ) &&
( false !== ( $prop = $component->getProperty( util::$DURATION,
false,
true,
true )))) {
$compEnd = iCaldateTime::factory( $prop[util::$LCvalue], // in dtend (array) format
$prop[util::$LCparams],
$prop[util::$LCvalue],
$dtstartTz );
$compEnd->SCbools[$DURATIONEXIST] = true;
}
if( ! empty( $prop ) && ! isset( $prop[util::$LCvalue][util::$LCHOUR] )) {
/* a DTEND without time part denotes an end of an event that actually ends the day before,
for an all-day event DTSTART=20071201 DTEND=20071202, taking place 20071201!!! */
$compEnd->SCbools[$ENDALLDAYEVENT] = true;
$compEnd->modify( $MINUS1DAY );
$compEnd->setTime( 23, 59, 59 );
}
unset( $prop );
if( empty( $compEnd )) {
$compDuration = false;
$compEnd = clone $compStart;
$compEnd->setTime( 23, 59, 59 ); // 23:59:59 the same day as start
}
else {
if( $compEnd->format( $YMD2 ) < $compStart->format( $YMD2 )) { // MUST be after start date!!
$compEnd = clone $compStart;
$compEnd->setTime( 23, 59, 59 ); // 23:59:59 the same day as start or ???
}
$compDuration = $compStart->diff( $compEnd ); // DateInterval
}
/* check recurrence-id (note, a missing sequence is the same as sequence=0
so don't test for sequence), to alter when hit in dtstart/recurlist */
$recurrid = null;
if( false !== ( $prop = $component->getProperty( util::$RECURRENCE_ID,
false,
true ))) {
$recurrid = iCaldateTime::factory( $prop[util::$LCvalue],
$prop[util::$LCparams],
$prop[util::$LCvalue],
$dtstartTz );
$rangeSet = ( isset( $prop[util::$LCparams][$RANGE] ) &&
( $THISANDFUTURE == $prop[util::$LCparams][$RANGE] ))
? true : false;
$recurrIdList[$recurrid->key] = [clone $compStart,
clone $compEnd,
$compDuration,
$rangeSet]; // change recur this day to new YmdHis/duration/range
// echo "adding comp no:$cix with date=".$compStart->format('Y-m-d H:i:s e')." to recurrIdList id={$recurrid->key}, newDate={$compStart->key}<br>\n"; // test ###
unset( $prop );
continue; // ignore any other props in the component
} // end recurrence-id/sequence test
// else echo "comp no:$cix with date=".$compStart->format().", NO recurrence-id<br>\n"; // test ###
ksort( $recurrIdList, SORT_STRING );
// echo 'recurrIdList='.implode(', ', array_keys( $recurrIdList ))."<br>\n"; // test ###
$fcnStart = clone $compStart;
$fcnStart->setDate((int) $startY, (int) $startM, (int) $startD );
$fcnStart->setTime( 0, 0, 0 );
$fcnEnd = clone $compEnd;
$fcnEnd->setDate((int) $endY, (int) $endM, (int) $endD );
$fcnEnd->setTime( 23, 59, 59 );
/* make a list of optional exclude dates for component occurence
from exrule and exdate */
$workStart = clone $compStart;
$workStart->sub( $compDuration ? $compDuration : $INTERVAL_P1D );
$workEnd = clone $fcnEnd;
$workEnd->add( $compDuration ? $compDuration : $INTERVAL_P1D );
// echo 'compStart/end:'.$compStart->format( 'YmdHis e' ).' - ' . $compEnd->format( 'YmdHis e' )."<br>\n"; // test ###
// echo '.fcnStart/end:' .$fcnStart->format( 'YmdHis e' ).' - ' . $fcnEnd->format( 'YmdHis e' )."<br>\n"; // test ###
// echo 'workStart/end:'.$workStart->format( 'YmdHis e' ).' - ' . $workEnd->format( 'YmdHis e' )."<br>\n"; // test ###
self::getAllEXRULEdates( $component, $exdatelist,
$dtstartTz, $compStart, $workStart, $workEnd,
$compStartHis );
self::getAllEXDATEdates( $component, $exdatelist, $dtstartTz );
// echo 'exdatelist=' . implode(', ', array_keys( $exdatelist )) ."<br>\n"; // test ###
/* select only components within.. . */
$xRecurrence = 1;
if(( ! $any && self::inScope( $compStart, $fcnStart,
$compStart, $fcnEnd, $compStart->dateFormat )) || // (dt)start within the period
( $any && self::inScope( $fcnEnd, $compStart,
$fcnStart, $compEnd, $compStart->dateFormat ))) { // occurs within the period
/* add the selected component (WITHIN valid dates) to output array */
if( $flat ) { // any=true/false, ignores split
if( empty( $recurrid ))
$result[$compUID] = clone $component; // copy original to output (but not anyone with recurrence-id)
}
elseif( $split ) { // split the original component
// echo 'split comp:' . $compStart->format( 'Ymd His e' ) . ', fcn:'.$fcnStart->format( 'Ymd His e' )."<br>\n"; // test ###
if( $compStart->format( $YMDHIS2 ) < $fcnStart->format( $YMDHIS2 ))
$rstart = clone $fcnStart;
else
$rstart = clone $compStart;
if( $compEnd->format( $YMDHIS2 ) > $fcnEnd->format( $YMDHIS2 ))
$rend = clone $fcnEnd;
else
$rend = clone $compEnd;
// echo "going to test comp no:$cix, rstart=".$rstart->format( 'YmdHis e' )." (key={$rstart->key}), end=".$rend->format( 'YmdHis e' )."<br>\n"; // test ###
if( ! isset( $exdatelist[$rstart->key] )) { // not excluded in exrule/exdate
if( isset( $recurrIdList[$rstart->key] )) { // change start day to new YmdHis/duration
$k = $rstart->key;
// echo "recurrIdList HIT, key={$k}, recur Date=".$recurrIdList[$k][0]->key."<br>\n"; // test ###
$rstart = clone $recurrIdList[$k][0];
$startHis = $rstart->getTime();
$rend = clone $rstart;
if( false !== $recurrIdList[$k][2] )
$rend->add( $recurrIdList[$k][2] );
elseif( false !== $compDuration )
$rend->add( $compDuration );
$endHis = $rend->getTime();
unset( $recurrIdList[$k] );
}
else {
$startHis = $compStart->getTime();
$endHis = $compEnd->getTime();
}
//echo "_____testing comp no:$cix, rstart=".$rstart->format( 'YmdHis e' )." (key={$rstart->key}), end=".$rend->format( 'YmdHis e' )."<br>\n"; // test ###
$cnt = 0; // exclude any recurrence START date, found in exdatelist or recurrIdList but accept the reccurence-id comp itself
$occurenceDays = 1 + (int) $rstart->diff( $rend )->format( $PRA ); // count the days (incl start day)
while( $rstart->format( $YMD2 ) <= $rend->format( $YMD2 )) {
$cnt += 1;
if( 1 < $occurenceDays )
$component->setProperty( util::$X_OCCURENCE,
sprintf( $DAYOFDAYS, $cnt,
$occurenceDays ));
if( 1 < $cnt )
$rstart->setTime( 0, 0, 0 );
else {
$rstart->setTime( $startHis[0], $startHis[1], $startHis[2] );
$exdatelist[$rstart->key] = $compDuration; // make sure to exclude start day from the recurrence pattern
}
$component->setProperty( util::$X_CURRENT_DTSTART,
$rstart->format( $compStart->dateFormat ));
$xY = (int) $rstart->format( $Y );
$xM = (int) $rstart->format( $M );
$xD = (int) $rstart->format( $D );
if( false !== $compDuration ) {
$propName = ( isset( $compEnd->SCbools[$DUEEXIST] ))
? util::$X_CURRENT_DUE : util::$X_CURRENT_DTEND;
if( $cnt < $occurenceDays )
$rstart->setTime( 23, 59, 59 );
elseif(( $rstart->format( $YMD2 ) < $rend->format( $YMD2 )) &&
( '00' == $endHis[0] ) &&
( '00' == $endHis[1] ) &&
( '00' == $endHis[2] )) // end exactly at midnight
$rstart->setTime( 24, 0, 0 );
else
$rstart->setTime( $endHis[0], $endHis[1], $endHis[2] );
$component->setProperty( $propName,
$rstart->format( $compEnd->dateFormat ));
}
$result[$xY][$xM][$xD][$compUID] = clone $component; // copy to output
$rstart->add( $INTERVAL_P1D );
} // end while(( $rstart->format( 'Ymd' ) < $rend->format( 'Ymd' ))
unset( $cnt, $occurenceDays );
} // end if( ! isset( $exdatelist[$rstart->key] ))
// else echo "skip no:$cix with date=".$compStart->format()."<br>\n"; // test ###
unset( $rstart, $rend );
} // end elseif( $split ) - else use component date
else { // !$flat && !$split, i.e. no flat array and DTSTART within period
$tstart = ( isset( $recurrIdList[$compStart->key] ))
? clone $recurrIdList[$compStart->key][0] : clone $compStart;
// echo "going to test comp no:$cix with checkDate={$compStart->key} with recurrIdList=".implode(',',array_keys($recurrIdList)); // test ###
if( ! $any || ! isset( $exdatelist[$tstart->key] )) { // exclude any recurrence date, found in exdatelist
// echo " and copied to output<br>\n"; // test ###
$xY = (int) $tstart->format( $Y );
$xM = (int) $tstart->format( $M );
$xD = (int) $tstart->format( $D );
$result[$xY][$xM][$xD][$compUID] = clone $component; // copy to output
}
unset( $tstart );
}
} // end (dt)start within the period OR occurs within the period
/* *************************************************************
if 'any' components, check components with reccurrence rules, removing all excluding dates
*********************************************************** */
if( true === $any ) {
$recurlist = [];
/* make a list of optional repeating dates for component occurence, rrule, rdate */
self::getAllRRULEdates( $component, $recurlist,
$dtstartTz, $compStart, $workStart, $workEnd,
$compStartHis, $exdatelist, $compDuration );
// echo 'recurlist rrule=' .implode(', ', array_keys( $recurlist )) ."<br>\n"; // test ###
$workStart = clone $fcnStart;
$workStart->sub( $compDuration ? $compDuration : $INTERVAL_P1D );
self::getAllRDATEdates( $component, $recurlist,
$dtstartTz, $workStart, $fcnEnd, $compStart->dateFormat,
$exdatelist, $compStartHis, $compDuration );
unset( $workStart, $rend );
foreach( $recurrIdList as $rKey => $rVal ) { // check for recurrence-id, i.e. alter recur Ymd[His] and duration
if( isset( $recurlist[$rKey] )) {
unset( $recurlist[$rKey] );
$recurlist[$rVal[0]->key] = ( false !== $rVal[2] )
? $rVal[2] : $compDuration;
// echo "alter recurfrom {$rKey} to {$rVal[0]->key} ";if(false!==$dur)echo " ({$dur->format( '%a days, %h-%i-%s' )})";echo "<br>\n"; // test ###
}
}
ksort( $recurlist, SORT_STRING );
// echo 'recurlist rrule/rdate=' .implode(', ', array_keys( $recurlist )) ."<br>\n"; // test ###
// echo 'recurrIdList=' .implode(', ', array_keys( $recurrIdList )) ."<br>\n"; // test ###
/* output all remaining components in recurlist */
if( 0 < count( $recurlist )) {
$component2 = clone $component;
$compUID = $component2->getProperty( util::$UID );
$workStart = clone $fcnStart;
$workStart->sub( $compDuration ? $compDuration : $INTERVAL_P1D );
$YmdOld = null;
foreach( $recurlist as $recurkey => $durvalue ) {
if( $YmdOld == substr( $recurkey, 0, 8 )) // skip overlapping recur the same day, i.e. RDATE before RRULE
continue;
$YmdOld = substr( $recurkey, 0, 8 );
$rstart = clone $compStart;
$rstart->setDate((int) substr( $recurkey, 0, 4 ),
(int) substr( $recurkey, 4, 2 ),
(int) substr( $recurkey, 6, 2 ));
$rstart->setTime((int) substr( $recurkey, 8, 2 ),
(int) substr( $recurkey, 10, 2 ),
(int) substr( $recurkey, 12, 2 ));
// echo "recur start=".$rstart->format( 'Y-m-d H:i:s e' )."<br>\n"; // test ###;
/* add recurring components within valid dates to output array, only start date set */
if( $flat ) {
if( ! isset( $result[$compUID] )) // only one comp
$result[$compUID] = clone $component2; // copy to output
}
/* add recurring components within valid dates to output array, split for each day */
elseif( $split ) {
$rend = clone $rstart;
if( false !== $durvalue )
$rend->add( $durvalue );
if( $rend->format( $YMD2 ) > $fcnEnd->format( $YMD2 ))
$rend = clone $fcnEnd;
$endHis = $rend->getTime();
// echo "recur 1={$recurkey}, start=".$rstart->format( 'YmdHis e' ).", end=".$rend->format( util::$YMDHISE );if($durvalue) echo ", duration=".$durvalue->format( '%a days, %h hours, %i min, %s sec' );echo "<br>\n"; // test ###
$xRecurrence += 1;
$cnt = 0;
$occurenceDays = 1 + (int) $rstart->diff( $rend )->format( $PRA ); // count the days (incl start day)
while( $rstart->format( $YMD2 ) <= $rend->format( $YMD2 )) { // iterate.. .
$cnt += 1;
if( $rstart->format( $YMD2 ) < $fcnStart->format( $YMD2 )) { // date before dtstart
// echo "recur 3, start=".$rstart->format( 'YmdHis e' )." &gt;= fcnStart=".$fcnStart->format( 'YmdHis e' )."<br>\n"; // test ###
$rstart->add( $INTERVAL_P1D ); // cycle rstart to dtstart
$rstart->setTime( 0, 0, 0 );
continue;
}
elseif( 2 == $cnt )
$rstart->setTime( 0, 0, 0 );
$component2->setProperty( util::$X_RECURRENCE,
$xRecurrence );
if( 1 < $occurenceDays )
$component2->setProperty( util::$X_OCCURENCE,
sprintf( $DAYOFDAYS, $cnt, $occurenceDays ));
else
$component2->deleteProperty( util::$X_OCCURENCE );
$component2->setProperty( util::$X_CURRENT_DTSTART,
$rstart->format( $compStart->dateFormat ));
$xY = (int) $rstart->format( $Y );
$xM = (int) $rstart->format( $M );
$xD = (int) $rstart->format( $D );
$propName = ( isset( $compEnd->SCbools[$DUEEXIST] ))
? util::$X_CURRENT_DUE : util::$X_CURRENT_DTEND;
if( false !== $durvalue ) {
if( $cnt < $occurenceDays )
$rstart->setTime( 23, 59, 59 );
elseif(( $rstart->format( $YMD2 ) < $rend->format( $YMD2 )) &&
( '00' == $endHis[0] ) && ( '00' == $endHis[1] ) && ( '00' == $endHis[2] )) // end exactly at midnight
$rstart->setTime( 24, 0, 0 );
else
$rstart->setTime( $endHis[0], $endHis[1], $endHis[2] );
$component2->setProperty( $propName,
$rstart->format( $compEnd->dateFormat ));
// echo "checking date, (day {$cnt} of {$occurenceDays}), _end_=".$rstart->format( 'YmdHis e' )."<br>"; // test ###;
}
else
$component2->deleteProperty( $propName );
$result[$xY][$xM][$xD][$compUID] = clone $component2; // copy to output
$rstart->add( $INTERVAL_P1D );
} // end while( $rstart->format( 'Ymd' ) <= $rend->format( 'Ymd' ))
unset( $rstart, $rend );
} // end elseif( $split )
elseif( $rstart->format( $YMD2 ) >= $fcnStart->format( $YMD2 )) {
$xRecurrence += 1; // date within period, flat=false && split=false => one comp every recur startdate
$component2->setProperty( util::$X_RECURRENCE,
$xRecurrence );
$component2->setProperty( util::$X_CURRENT_DTSTART,
$rstart->format( $compStart->dateFormat ));
$propName = ( isset( $compEnd->SCbools[$DUEEXIST] ))
? util::$X_CURRENT_DUE : util::$X_CURRENT_DTEND;
if( false !== $durvalue ) {
$rstart->add( $durvalue );
$component2->setProperty( $propName,
$rstart->format( $compEnd->dateFormat ));
}
else
$component2->deleteProperty( $propName );
$xY = (int) $rstart->format( $Y );
$xM = (int) $rstart->format( $M );
$xD = (int) $rstart->format( $D );
$result[$xY][$xM][$xD][$compUID] = clone $component2; // copy to output
} // end elseif( $rstart >= $fcnStart )
unset( $rstart );
} // end foreach( $recurlist as $recurkey => $durvalue )
unset( $component2, $xRecurrence, $compUID, $workStart, $rstart );
} // end if( 0 < count( $recurlist ))
} // end if( true === $any )
unset( $component );
} // end while( $component = $calendar->getComponent())
if( 0 >= count( $result ))
return false;
elseif( ! $flat ) {
foreach( $result as $y => $yList ) {
foreach( $yList as $m => $mList ) {
foreach( $mList as $d => $dList ) {
if( empty( $dList ))
unset( $result[$y][$m][$d] );
else {
$result[$y][$m][$d] = array_values( $dList ); // skip tricky UID-index
if( 1 < count( $result[$y][$m][$d] )) {
foreach( $result[$y][$m][$d] as $cix => $d2List ) // sort
vcalendarSortHandler::setSortArgs( $result[$y][$m][$d][$cix] );
usort( $result[$y][$m][$d], $SORTER );
}
}
} // end foreach( $mList as $d => $dList )
if( empty( $result[$y][$m] ))
unset( $result[$y][$m] );
else
ksort( $result[$y][$m] );
} // end foreach( $yList as $m => $mList )
if( empty( $result[$y] ))
unset( $result[$y] );
else
ksort( $result[$y] );
} // end foreach( $result as $y => $yList )
if( empty( $result ))
unset( $result );
else
ksort( $result );
} // end elseif( !$flat )
if( 0 >= count( $result ))
return false;
return $result;
}
/**
* Return bool true if dates are in scope
*
* @param iCaldateTime $start
* @param iCaldateTime $scopeStart
* @param iCaldateTime $end
* @param iCaldateTime $scopeEnd
* @param string $format
* @return bool
* @access private
* @static
*/
private static function inScope( iCaldateTime $start,
iCaldateTime $scopeStart,
iCaldateTime $end,
iCaldateTime $scopeEnd,
$format ) {
return (( $start->format( $format ) >= $scopeStart->format( $format )) &&
( $end->format( $format ) <= $scopeEnd->format( $format )));
}
/**
* Get all EXRULE dates (multiple values allowed)
*
* @param calendarComponent $component
* @param array $exdatelist
* @param string $dtstartTz
* @param iCaldateTime $compStart
* @param iCaldateTime $workStart
* @param iCaldateTime $workEnd
* @param string $compStartHis
*/
private static function getAllEXRULEdates( calendarComponent $component,
array & $exdatelist,
$dtstartTz,
iCaldateTime $compStart,
iCaldateTime $workStart,
iCaldateTime $workEnd,
$compStartHis ) {
while( false !== ( $prop = $component->getProperty( util::$EXRULE ))) {
$exdatelist2 = [];
if( isset( $prop[util::$UNTIL][util::$LCHOUR] )) { // convert UNTIL date to DTSTART timezone
$until = iCaldateTime::factory( $prop[util::$UNTIL],
[util::$TZID => util::$UTC],
null,
$dtstartTz );
$until = $until->format();
util::strDate2arr( $until );
$prop[util::$UNTIL] = $until;
}
utilRecur::recur2date( $exdatelist2,
$prop,
$compStart,
$workStart,
$workEnd );
foreach( $exdatelist2 as $k => $v ) // point out exact every excluded ocurrence (incl. opt. His)
$exdatelist[$k.$compStartHis] = $v;
unset( $until, $exdatelist2 );
}
return true;
}
/**
* Get all EXDATE dates (multiple values allowed)
*
* @param calendarComponent $component
* @param array $exdatelist
* @param string $dtstartTz
*/
private static function getAllEXDATEdates( calendarComponent $component,
array & $exdatelist,
$dtstartTz ) {
while( false !== ( $prop = $component->getProperty( util::$EXDATE,
false,
true ))) {
foreach( $prop[util::$LCvalue] as $exdate ) {
$exdate = iCaldateTime::factory( $exdate,
$prop[util::$LCparams],
$exdate,
$dtstartTz );
$exdatelist[$exdate->key] = true;
} // end - foreach( $exdate as $exdate )
}
return true;
}
/**
* Update $recurlist all RRULE dates (multiple values allowed)
*
* @param calendarComponent $component
* @param array $recurlist
* @param string $dtstartTz
* @param iCaldateTime $compStart
* @param iCaldateTime $workStart
* @param iCaldateTime $workEnd
* @param string $compStartHis
* @param array $exdatelist
* @param object $compDuration
*/
private static function getAllRRULEdates( calendarComponent $component,
array & $recurlist,
$dtstartTz,
iCaldateTime $compStart,
iCaldateTime$workStart,
iCaldateTime $workEnd,
$compStartHis,
array $exdatelist,
$compDuration ) {
while( false !== ( $prop = $component->getProperty( util::$RRULE ))) {
$recurlist2 = [];
if( isset( $prop[util::$UNTIL][util::$LCHOUR] )) { // convert RRULE['UNTIL'] to the same timezone as DTSTART !!
$until = iCaldateTime::factory( $prop[util::$UNTIL],
[util::$TZID => util::$UTC],
null,
$dtstartTz );
$until = $until->format();
util::strDate2arr( $until );
$prop[util::$UNTIL] = $until;
}
utilRecur::recur2date( $recurlist2,
$prop,
$compStart,
$workStart,
$workEnd );
foreach( $recurlist2 as $recurkey => $recurvalue ) { // recurkey=Ymd
$recurkey .= $compStartHis; // add opt His
if( ! isset( $exdatelist[$recurkey] ))
$recurlist[$recurkey] = $compDuration; // DateInterval or false
}
unset( $prop, $until, $recurlist2 );
}
return true;
}
/**
* Update $recurlist with RDATE dates (multiple values allowed)
*
* @param calendarComponent $component
* @param array $recurlist
* @param string $dtstartTz
* @param iCaldateTime $workStart
* @param iCaldateTime $fcnEnd
* @param string $format
* @param array $exdatelist
* @param string $compStartHis
* @param object $compDuration
*/
private static function getAllRDATEdates( calendarComponent $component,
array & $recurlist,
$dtstartTz,
iCaldateTime $workStart,
iCaldateTime $fcnEnd,
$format,
array $exdatelist,
$compStartHis,
$compDuration ) {
while( false !== ( $prop = $component->getProperty( util::$RDATE,
false,
true ))) {
$rdateFmt = ( isset( $prop[util::$LCparams][util::$VALUE] ))
? $prop[util::$LCparams][util::$VALUE]
: util::$DATE_TIME;
$params = $prop[util::$LCparams];
$prop = $prop[util::$LCvalue];
foreach( $prop as $rix => $theRdate ) {
if( util::$PERIOD == $rdateFmt ) { // all days within PERIOD
$rdate = iCaldateTime::factory( $theRdate[0],
$params,
$theRdate[0],
$dtstartTz );
if( ! self::inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) ||
isset( $exdatelist[$rdate->key] ))
continue;
if( isset( $theRdate[1][util::$LCYEAR] )) // date-date period end
$recurlist[$rdate->key] = $rdate->diff( iCaldateTime::factory( $theRdate[1],
$params,
$theRdate[1],
$dtstartTz ));
else // period duration
$recurlist[$rdate->key] = new \DateInterval( util::duration2str( $theRdate[1] ));
} // end if( util::$PERIOD == $rdateFmt )
elseif( util::$DATE == $rdateFmt ) { // single recurrence, DATE
$rdate = iCaldateTime::factory( $theRdate,
array_merge( $params,
[util::$TZID => $dtstartTz] ),
null,
$dtstartTz );
if( self::inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) &&
! isset( $exdatelist[$rdate->key] )) // set start date for recurrence + DateInterval/false (+opt His)
$recurlist[$rdate->key.$compStartHis] = $compDuration;
} // end DATE
else { // start DATETIME
$rdate = iCaldateTime::factory( $theRdate,
$params,
$theRdate,
$dtstartTz );
if( self::inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) &&
! isset( $exdatelist[$rdate->key] ))
$recurlist[$rdate->key] = $compDuration; // set start datetime for recurrence DateInterval/false
} // end DATETIME
} // end foreach( $prop as $rix => $theRdate )
} // end while( false !== ( $prop = $component->getProperty( util::$RDATE, false, true )))
return true;
}
/**
* Return array with selected components values from calendar based on specific property value(-s)
*
* @param vcalendar $calendar
* @param array $selectOptions (string) key => (mixed) value, (key=propertyName)
* @return array
* @access private
* @static
*/
private static function selectComponents2( vcalendar $calendar,
array $selectOptions ) {
$output = [];
$selectOptions = array_change_key_case( $selectOptions, CASE_UPPER );
while( $component3 = $calendar->getComponent()) {
if( empty( $component3 ))
continue;
if( ! in_array( $component3->objName, util::$VCOMPS ))
continue;
$uid = $component3->getProperty( util::$UID );
foreach( $selectOptions as $propName => $pValue ) {
if( ! in_array( $propName, util::$OTHERPROPS ))
continue;
if( ! is_array( $pValue ))
$pValue = [$pValue];
if(( util::$UID == $propName ) && in_array( $uid, $pValue )) {
$output[$uid][] = $component3;
continue;
}
elseif( in_array( $propName, util::$MPROPS1 )) {
$propValues = [];
$component3->getProperties( $propName, $propValues );
$propValues = array_keys( $propValues );
foreach( $pValue as $theValue ) {
if( in_array( $theValue, $propValues )) { // && ! isset( $output[$uid] )) {
$output[$uid][] = $component3;
break;
}
}
continue;
} // end elseif( // multiple occurrence?
elseif( false === ( $d = $component3->getProperty( $propName ))) // single occurrence
continue;
if( is_array( $d )) {
foreach( $d as $part ) {
if( in_array( $part, $pValue ) && ! isset( $output[$uid] ))
$output[$uid][] = $component3;
}
}
elseif(( util::$SUMMARY == $propName ) && ! isset( $output[$uid] )) {
foreach( $pValue as $pval ) {
if( false !== stripos( $d, $pval )) {
$output[$uid][] = $component3;
break;
}
}
}
elseif( in_array( $d, $pValue ) && ! isset( $output[$uid] ))
$output[$uid][] = $component3;
} // end foreach( $selectOptions as $propName => $pValue )
} // end while( $component3 = $calendar->getComponent()) {
if( ! empty( $output )) {
ksort( $output ); // uid order
$output2 = [];
foreach( $output as $uid => $uList ) {
foreach( $uList as $cx => $uValue )
$output2[] = $uValue;
}
$output = $output2;
}
return $output;
}
}