2022-06-16 14:01:47 +02:00
< ? php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2019-11-15 23:26:29 +01:00
/**
2022-06-16 14:01:47 +02:00
* Get and render iCal feeds .
* Used by the Upcoming Events widget and the [ upcomingevents ] shortcode .
*
* @ package automattic / jetpack
2019-11-15 23:26:29 +01:00
*/
2023-04-26 17:39:43 +02:00
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
2022-06-16 14:01:47 +02:00
/**
* Calendar utilities class .
*
* phpcs : disable PEAR . NamingConventions . ValidClassName . StartWithCapital
*/
2019-11-15 23:26:29 +01:00
class iCalendarReader {
2022-06-16 14:01:47 +02:00
// phpcs:enable PEAR.NamingConventions.ValidClassName.StartWithCapital
// phpcs:disable WordPress.DateTime.RestrictedFunctions.date_date -- we manually handle timezones all over the file.
// @todo Verify that we're manually handling timezones *correctly*. We probably need more `DateTime` with `$this->timezone` and maybe `wp_date()` and less `strtotime()` and `date()` and `date_i18n()`.
/**
* Count To Do events in calendar .
*
* @ var int
*/
2019-11-15 23:26:29 +01:00
public $todo_count = 0 ;
2022-06-16 14:01:47 +02:00
/**
* How many events can be found in calendar .
*
* @ var int
*/
2019-11-15 23:26:29 +01:00
public $event_count = 0 ;
2022-06-16 14:01:47 +02:00
/**
* Details about our calendar .
*
* @ var array
*/
2019-11-15 23:26:29 +01:00
public $cal = array ();
2022-06-16 14:01:47 +02:00
/**
* Timezone parsed from the iCalendar feed , if any .
*
* @ var null | DateTimeZone
*/
2019-11-15 23:26:29 +01:00
public $timezone = null ;
2025-04-25 12:30:07 +02:00
/**
* Last iCalendar keyword parsed .
*
* @ var string
*/
public $last_keyword ;
2019-11-15 23:26:29 +01:00
/**
* Class constructor
*
* @ return void
*/
public function __construct () {}
/**
* Return an array of events
*
2022-06-16 14:01:47 +02:00
* @ param string $url ( default : '' ) URL of the iCal feed .
* @ param int $count Count the number of events .
*
2019-11-15 23:26:29 +01:00
* @ return array | false on failure
*/
public function get_events ( $url = '' , $count = 5 ) {
2022-06-16 14:01:47 +02:00
$count = ( int ) $count ;
2019-11-15 23:26:29 +01:00
$transient_id = 'icalendar_vcal_' . md5 ( $url ) . '_' . $count ;
$vcal = get_transient ( $transient_id );
if ( ! empty ( $vcal ) ) {
2022-06-16 14:01:47 +02:00
if ( isset ( $vcal [ 'TIMEZONE' ] ) ) {
2019-11-15 23:26:29 +01:00
$this -> timezone = $this -> timezone_from_string ( $vcal [ 'TIMEZONE' ] );
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
if ( isset ( $vcal [ 'VEVENT' ] ) ) {
$vevent = $vcal [ 'VEVENT' ];
2022-06-16 14:01:47 +02:00
if ( $count > 0 ) {
2019-11-15 23:26:29 +01:00
$vevent = array_slice ( $vevent , 0 , $count );
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
$this -> cal [ 'VEVENT' ] = $vevent ;
return $this -> cal [ 'VEVENT' ];
}
}
2022-06-16 14:01:47 +02:00
if ( ! $this -> parse ( $url ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
$vcal = array ();
if ( $this -> timezone ) {
$vcal [ 'TIMEZONE' ] = $this -> timezone -> getName ();
} else {
$this -> timezone = $this -> timezone_from_string ( '' );
}
if ( ! empty ( $this -> cal [ 'VEVENT' ] ) ) {
$vevent = $this -> cal [ 'VEVENT' ];
2022-06-16 14:01:47 +02:00
// check for recurring events.
// $vevent = $this->add_recurring_events( $vevent );.
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// remove before caching - no sense in hanging onto the past.
2019-11-15 23:26:29 +01:00
$vevent = $this -> filter_past_and_recurring_events ( $vevent );
2022-06-16 14:01:47 +02:00
// order by soonest start date.
2019-11-15 23:26:29 +01:00
$vevent = $this -> sort_by_recent ( $vevent );
$vcal [ 'VEVENT' ] = $vevent ;
}
set_transient ( $transient_id , $vcal , HOUR_IN_SECONDS );
2022-06-16 14:01:47 +02:00
if ( ! isset ( $vcal [ 'VEVENT' ] ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
if ( $count > 0 ) {
2019-11-15 23:26:29 +01:00
return array_slice ( $vcal [ 'VEVENT' ], 0 , $count );
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
return $vcal [ 'VEVENT' ];
}
2022-06-16 14:01:47 +02:00
/**
* Adjust event 's time based on site' s timezone .
*
* @ param array $events Array of events .
*
* @ return array
*/
public function apply_timezone_offset ( $events ) {
2019-11-15 23:26:29 +01:00
if ( ! $events ) {
return $events ;
}
// get timezone offset from the timezone name.
$timezone = wp_timezone ();
$offsetted_events = array ();
foreach ( $events as $event ) {
2022-06-16 14:01:47 +02:00
// Don't handle all-day events.
2019-11-15 23:26:29 +01:00
if ( 8 < strlen ( $event [ 'DTSTART' ] ) ) {
$start_time = preg_replace ( '/Z$/' , '' , $event [ 'DTSTART' ] );
$start_time = new DateTime ( $start_time , $this -> timezone );
$start_time -> setTimeZone ( $timezone );
$event [ 'DTSTART' ] = $start_time -> format ( 'YmdHis\Z' );
2025-02-28 08:42:11 +01:00
if ( isset ( $event [ 'DTEND' ] ) ) {
$end_time = preg_replace ( '/Z$/' , '' , $event [ 'DTEND' ] );
$end_time = new DateTime ( $end_time , $this -> timezone );
$end_time -> setTimeZone ( $timezone );
$event [ 'DTEND' ] = $end_time -> format ( 'YmdHis\Z' );
}
2019-11-15 23:26:29 +01:00
}
$offsetted_events [] = $event ;
}
return $offsetted_events ;
}
2022-06-16 14:01:47 +02:00
/**
* Reorganize events into an array of events with standardized data .
*
* @ param array $events Array of events .
*
* @ return array
*/
2019-11-15 23:26:29 +01:00
protected function filter_past_and_recurring_events ( $events ) {
2022-06-16 14:01:47 +02:00
$upcoming = array ();
2019-11-15 23:26:29 +01:00
$set_recurring_events = array ();
/**
* This filter allows any time to be passed in for testing or changing timezones , etc ...
*
* @ module widgets
*
* @ since 3.4 . 0
*
* @ param object time () A time object .
*/
$current = apply_filters ( 'ical_get_current_time' , time () );
foreach ( $events as $event ) {
$date_from_ics = strtotime ( $event [ 'DTSTART' ] );
if ( isset ( $event [ 'DTEND' ] ) ) {
$duration = strtotime ( $event [ 'DTEND' ] ) - strtotime ( $event [ 'DTSTART' ] );
} else {
$duration = 0 ;
}
2022-06-16 14:01:47 +02:00
if ( isset ( $event [ 'RRULE' ] ) && $this -> timezone -> getName () && 8 !== strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
try {
2022-06-16 14:01:47 +02:00
$adjusted_time = new DateTime ( $event [ 'DTSTART' ], new DateTimeZone ( 'UTC' ) );
2019-11-15 23:26:29 +01:00
$adjusted_time -> setTimeZone ( new DateTimeZone ( $this -> timezone -> getName () ) );
2022-06-16 14:01:47 +02:00
$event [ 'DTSTART' ] = $adjusted_time -> format ( 'Ymd\THis' );
$date_from_ics = strtotime ( $event [ 'DTSTART' ] );
2019-11-15 23:26:29 +01:00
$event [ 'DTEND' ] = date ( 'Ymd\THis' , strtotime ( $event [ 'DTSTART' ] ) + $duration );
2022-06-16 14:01:47 +02:00
} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// Invalid argument to DateTime.
2019-11-15 23:26:29 +01:00
}
if ( isset ( $event [ 'EXDATE' ] ) ) {
$exdates = array ();
foreach ( ( array ) $event [ 'EXDATE' ] as $exdate ) {
try {
2022-06-16 14:01:47 +02:00
$adjusted_time = new DateTime ( $exdate , new DateTimeZone ( 'UTC' ) );
2019-11-15 23:26:29 +01:00
$adjusted_time -> setTimeZone ( new DateTimeZone ( $this -> timezone -> getName () ) );
2022-06-16 14:01:47 +02:00
if ( 8 === strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
$exdates [] = $adjusted_time -> format ( 'Ymd' );
} else {
$exdates [] = $adjusted_time -> format ( 'Ymd\THis' );
}
2022-06-16 14:01:47 +02:00
} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// Invalid argument to DateTime.
2019-11-15 23:26:29 +01:00
}
}
$event [ 'EXDATE' ] = $exdates ;
} else {
$event [ 'EXDATE' ] = array ();
}
}
if ( ! isset ( $event [ 'DTSTART' ] ) ) {
continue ;
}
2022-06-16 14:01:47 +02:00
// Process events with RRULE before other events.
$rrule = isset ( $event [ 'RRULE' ] ) ? $event [ 'RRULE' ] : false ;
2025-02-28 08:42:11 +01:00
$uid = isset ( $event [ 'UID' ] ) ? $event [ 'UID' ] : false ;
2019-11-15 23:26:29 +01:00
2025-02-28 08:42:11 +01:00
if ( $rrule && $uid && ! in_array ( $uid , $set_recurring_events , true ) ) {
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// Break down the RRULE into digestible chunks.
2019-11-15 23:26:29 +01:00
$rrule_array = array ();
2022-06-16 14:01:47 +02:00
foreach ( explode ( ';' , $event [ 'RRULE' ] ) as $rline ) {
list ( $rkey , $rvalue ) = explode ( '=' , $rline , 2 );
$rrule_array [ $rkey ] = $rvalue ;
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
$interval = ( isset ( $rrule_array [ 'INTERVAL' ] ) ) ? $rrule_array [ 'INTERVAL' ] : 1 ;
2019-11-15 23:26:29 +01:00
$rrule_count = ( isset ( $rrule_array [ 'COUNT' ] ) ) ? $rrule_array [ 'COUNT' ] : 0 ;
2022-06-16 14:01:47 +02:00
$until = ( isset ( $rrule_array [ 'UNTIL' ] ) ) ? strtotime ( $rrule_array [ 'UNTIL' ] ) : strtotime ( '+1 year' , $current );
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// Used to bound event checks.
2019-11-15 23:26:29 +01:00
$echo_limit = 10 ;
2022-06-16 14:01:47 +02:00
$noop = false ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// Set bydays for the event.
2019-11-15 23:26:29 +01:00
$weekdays = array ( 'SU' , 'MO' , 'TU' , 'WE' , 'TH' , 'FR' , 'SA' );
2022-06-16 14:01:47 +02:00
$bydays = $weekdays ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// Calculate a recent start date for incrementing depending on the frequency and interval.
2019-11-15 23:26:29 +01:00
switch ( $rrule_array [ 'FREQ' ] ) {
case 'DAILY' :
2022-06-16 14:01:47 +02:00
$frequency = 'day' ;
2019-11-15 23:26:29 +01:00
$echo_limit = 10 ;
if ( $date_from_ics >= $current ) {
$recurring_event_date_start = date ( 'Ymd\THis' , strtotime ( $event [ 'DTSTART' ] ) );
} else {
2022-06-16 14:01:47 +02:00
// Interval and count.
2019-11-15 23:26:29 +01:00
$catchup = floor ( ( $current - strtotime ( $event [ 'DTSTART' ] ) ) / ( $interval * DAY_IN_SECONDS ) );
if ( $rrule_count && $catchup > 0 ) {
if ( $catchup < $rrule_count ) {
2022-06-16 14:01:47 +02:00
$rrule_count = $rrule_count - $catchup ;
$recurring_event_date_start = date (
'Ymd' ,
strtotime (
'+ ' . ( $interval * $catchup ) . ' days' ,
strtotime ( $event [ 'DTSTART' ] )
)
) . date (
'\THis' ,
strtotime ( $event [ 'DTSTART' ] )
);
2019-11-15 23:26:29 +01:00
} else {
$noop = true ;
}
} else {
2022-06-16 14:01:47 +02:00
$recurring_event_date_start = date (
'Ymd' ,
strtotime (
'+ ' . ( $interval * $catchup ) . ' days' ,
strtotime ( $event [ 'DTSTART' ] )
)
) . date (
'\THis' ,
strtotime ( $event [ 'DTSTART' ] )
);
2019-11-15 23:26:29 +01:00
}
}
break ;
case 'WEEKLY' :
2022-06-16 14:01:47 +02:00
$frequency = 'week' ;
2019-11-15 23:26:29 +01:00
$echo_limit = 4 ;
2022-06-16 14:01:47 +02:00
// BYDAY exception to current date.
2019-11-15 23:26:29 +01:00
$day = false ;
if ( ! isset ( $rrule_array [ 'BYDAY' ] ) ) {
2022-06-16 14:01:47 +02:00
$rrule_array [ 'BYDAY' ] = strtoupper ( substr ( date ( 'D' , strtotime ( $event [ 'DTSTART' ] ) ), 0 , 2 ) );
$day = $rrule_array [ 'BYDAY' ];
2019-11-15 23:26:29 +01:00
}
$bydays = explode ( ',' , $rrule_array [ 'BYDAY' ] );
if ( $date_from_ics >= $current ) {
$recurring_event_date_start = date ( 'Ymd\THis' , strtotime ( $event [ 'DTSTART' ] ) );
} else {
2022-06-16 14:01:47 +02:00
// Interval and count.
2019-11-15 23:26:29 +01:00
$catchup = floor ( ( $current - strtotime ( $event [ 'DTSTART' ] ) ) / ( $interval * WEEK_IN_SECONDS ) );
if ( $rrule_count && $catchup > 0 ) {
if ( ( $catchup * count ( $bydays ) ) < $rrule_count ) {
2022-06-16 14:01:47 +02:00
$rrule_count = $rrule_count - ( $catchup * count ( $bydays ) ); // Estimate current event count.
2019-11-15 23:26:29 +01:00
$recurring_event_date_start = date ( 'Ymd' , strtotime ( '+ ' . ( $interval * $catchup ) . ' weeks' , strtotime ( $event [ 'DTSTART' ] ) ) ) . date ( '\THis' , strtotime ( $event [ 'DTSTART' ] ) );
} else {
$noop = true ;
}
} else {
$recurring_event_date_start = date ( 'Ymd' , strtotime ( '+ ' . ( $interval * $catchup ) . ' weeks' , strtotime ( $event [ 'DTSTART' ] ) ) ) . date ( '\THis' , strtotime ( $event [ 'DTSTART' ] ) );
}
}
2022-06-16 14:01:47 +02:00
// Set to Sunday start.
2019-11-15 23:26:29 +01:00
if ( ! $noop && 'SU' !== strtoupper ( substr ( date ( 'D' , strtotime ( $recurring_event_date_start ) ), 0 , 2 ) ) ) {
2022-06-16 14:01:47 +02:00
$recurring_event_date_start = date ( 'Ymd' , strtotime ( 'last Sunday' , strtotime ( $recurring_event_date_start ) ) ) . date ( '\THis' , strtotime ( $event [ 'DTSTART' ] ) );
2019-11-15 23:26:29 +01:00
}
break ;
case 'MONTHLY' :
2022-06-16 14:01:47 +02:00
$frequency = 'month' ;
2019-11-15 23:26:29 +01:00
$echo_limit = 1 ;
if ( $date_from_ics >= $current ) {
$recurring_event_date_start = date ( 'Ymd\THis' , strtotime ( $event [ 'DTSTART' ] ) );
} else {
2022-06-16 14:01:47 +02:00
// Describe the date in the month.
2024-02-16 11:03:01 +01:00
if ( isset ( $rrule_array [ 'BYDAY' ] )
&& preg_match ( '/^(-?\d)([A-Z]{2})/' , $rrule_array [ 'BYDAY' ], $matches )
) {
$day_number = $matches [ 1 ];
$week_day = $matches [ 2 ];
$day_cardinals = array (
- 3 => 'third to last' ,
- 2 => 'second to last' ,
- 1 => 'last' ,
1 => 'first' ,
2 => 'second' ,
3 => 'third' ,
4 => 'fourth' ,
5 => 'fifth' ,
2022-06-16 14:01:47 +02:00
);
2024-02-16 11:03:01 +01:00
$weekdays = array (
2022-06-16 14:01:47 +02:00
'SU' => 'Sunday' ,
'MO' => 'Monday' ,
'TU' => 'Tuesday' ,
'WE' => 'Wednesday' ,
'TH' => 'Thursday' ,
'FR' => 'Friday' ,
'SA' => 'Saturday' ,
);
2024-02-16 11:03:01 +01:00
$day_cardinal = $day_cardinals [ $day_number ] ? ? '' ;
$weekday = $weekdays [ $week_day ] ? ? '' ;
$event_date_desc = " $day_cardinal $weekday of " ;
2019-11-15 23:26:29 +01:00
} else {
$event_date_desc = date ( 'd ' , strtotime ( $event [ 'DTSTART' ] ) );
}
2022-06-16 14:01:47 +02:00
// Interval only.
2019-11-15 23:26:29 +01:00
if ( $interval > 1 ) {
$catchup = 0 ;
2022-06-16 14:01:47 +02:00
$maybe = strtotime ( $event [ 'DTSTART' ] );
2019-11-15 23:26:29 +01:00
while ( $maybe < $current ) {
$maybe = strtotime ( '+ ' . ( $interval * $catchup ) . ' months' , strtotime ( $event [ 'DTSTART' ] ) );
2023-04-26 17:39:43 +02:00
++ $catchup ;
2019-11-15 23:26:29 +01:00
}
$recurring_event_date_start = date ( 'Ymd' , strtotime ( $event_date_desc . date ( 'F Y' , strtotime ( '+ ' . ( $interval * ( $catchup - 1 ) ) . ' months' , strtotime ( $event [ 'DTSTART' ] ) ) ) ) ) . date ( '\THis' , strtotime ( $event [ 'DTSTART' ] ) );
} else {
$recurring_event_date_start = date ( 'Ymd' , strtotime ( $event_date_desc . date ( 'F Y' , $current ) ) ) . date ( '\THis' , strtotime ( $event [ 'DTSTART' ] ) );
}
2022-06-16 14:01:47 +02:00
// Add one interval if necessary.
2019-11-15 23:26:29 +01:00
if ( strtotime ( $recurring_event_date_start ) < $current ) {
if ( $interval > 1 ) {
$recurring_event_date_start = date ( 'Ymd' , strtotime ( $event_date_desc . date ( 'F Y' , strtotime ( '+ ' . ( $interval * $catchup ) . ' months' , strtotime ( $event [ 'DTSTART' ] ) ) ) ) ) . date ( '\THis' , strtotime ( $event [ 'DTSTART' ] ) );
} else {
try {
$adjustment = new DateTime ( date ( 'Y-m-d' , $current ) );
$adjustment -> modify ( 'first day of next month' );
$recurring_event_date_start = date ( 'Ymd' , strtotime ( $event_date_desc . $adjustment -> format ( 'F Y' ) ) ) . date ( '\THis' , strtotime ( $event [ 'DTSTART' ] ) );
2022-06-16 14:01:47 +02:00
} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// Invalid argument to DateTime.
2019-11-15 23:26:29 +01:00
}
}
}
}
break ;
case 'YEARLY' :
2022-06-16 14:01:47 +02:00
$frequency = 'year' ;
2019-11-15 23:26:29 +01:00
$echo_limit = 1 ;
if ( $date_from_ics >= $current ) {
2022-06-16 14:01:47 +02:00
$recurring_event_date_start = date ( 'Ymd\THis' , strtotime ( $event [ 'DTSTART' ] ) );
2019-11-15 23:26:29 +01:00
} else {
2022-06-16 14:01:47 +02:00
$recurring_event_date_start = date ( 'Y' , $current ) . date ( 'md\THis' , strtotime ( $event [ 'DTSTART' ] ) );
2019-11-15 23:26:29 +01:00
if ( strtotime ( $recurring_event_date_start ) < $current ) {
try {
$next = new DateTime ( date ( 'Y-m-d' , $current ) );
$next -> modify ( 'first day of next year' );
2022-06-16 14:01:47 +02:00
$recurring_event_date_start = $next -> format ( 'Y' ) . date ( 'md\THis' , strtotime ( $event [ 'DTSTART' ] ) );
} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// Invalid argument to DateTime.
2019-11-15 23:26:29 +01:00
}
}
}
break ;
default :
$frequency = false ;
}
2022-06-16 14:01:47 +02:00
if ( false !== $frequency && ! $noop ) {
2019-11-15 23:26:29 +01:00
$count_counter = 1 ;
2022-06-16 14:01:47 +02:00
// If no COUNT limit, go to 10.
2019-11-15 23:26:29 +01:00
if ( empty ( $rrule_count ) ) {
$rrule_count = 10 ;
}
2022-06-16 14:01:47 +02:00
// Set up EXDATE handling for the event.
2019-11-15 23:26:29 +01:00
$exdates = ( isset ( $event [ 'EXDATE' ] ) ) ? $event [ 'EXDATE' ] : array ();
for ( $i = 1 ; $i <= $echo_limit ; $i ++ ) {
2022-06-16 14:01:47 +02:00
// Weeks need a daily loop and must check for inclusion in BYDAYS.
if ( 'week' === $frequency ) {
2019-11-15 23:26:29 +01:00
$byday_event_date_start = strtotime ( $recurring_event_date_start );
foreach ( $weekdays as $day ) {
$event_start_timestamp = $byday_event_date_start ;
2022-06-16 14:01:47 +02:00
$start_time = date ( 'His' , $event_start_timestamp );
$event_end_timestamp = $event_start_timestamp + $duration ;
$end_time = date ( 'His' , $event_end_timestamp );
if ( 8 === strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
$exdate_compare = date ( 'Ymd' , $event_start_timestamp );
} else {
$exdate_compare = date ( 'Ymd\THis' , $event_start_timestamp );
}
2022-06-16 14:01:47 +02:00
if (
in_array ( $day , $bydays , true )
&& $event_end_timestamp > $current
&& $event_start_timestamp < $until
&& $count_counter <= $rrule_count
&& $event_start_timestamp >= $date_from_ics
&& ! in_array ( $exdate_compare , $exdates , true )
) {
if ( 8 === strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
$event [ 'DTSTART' ] = date ( 'Ymd' , $event_start_timestamp );
2022-06-16 14:01:47 +02:00
$event [ 'DTEND' ] = date ( 'Ymd' , $event_end_timestamp );
2019-11-15 23:26:29 +01:00
} else {
$event [ 'DTSTART' ] = date ( 'Ymd\THis' , $event_start_timestamp );
2022-06-16 14:01:47 +02:00
$event [ 'DTEND' ] = date ( 'Ymd\THis' , $event_end_timestamp );
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
if ( $this -> timezone -> getName () && 8 !== strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
try {
$adjusted_time = new DateTime ( $event [ 'DTSTART' ], new DateTimeZone ( $this -> timezone -> getName () ) );
$adjusted_time -> setTimeZone ( new DateTimeZone ( 'UTC' ) );
2022-06-16 14:01:47 +02:00
$event [ 'DTSTART' ] = $adjusted_time -> format ( 'Ymd\THis' );
2019-11-15 23:26:29 +01:00
$event [ 'DTEND' ] = date ( 'Ymd\THis' , strtotime ( $event [ 'DTSTART' ] ) + $duration );
2022-06-16 14:01:47 +02:00
} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// Invalid argument to DateTime.
2019-11-15 23:26:29 +01:00
}
}
$upcoming [] = $event ;
2023-04-26 17:39:43 +02:00
++ $count_counter ;
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
// Move forward one day.
2019-11-15 23:26:29 +01:00
$byday_event_date_start = strtotime ( date ( 'Ymd\T' , strtotime ( '+ 1 day' , $event_start_timestamp ) ) . $start_time );
}
2022-06-16 14:01:47 +02:00
// Restore first event timestamp.
2019-11-15 23:26:29 +01:00
$event_start_timestamp = strtotime ( $recurring_event_date_start );
} else {
$event_start_timestamp = strtotime ( $recurring_event_date_start );
2022-06-16 14:01:47 +02:00
$start_time = date ( 'His' , $event_start_timestamp );
$event_end_timestamp = $event_start_timestamp + $duration ;
$end_time = date ( 'His' , $event_end_timestamp );
if ( 8 === strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
$exdate_compare = date ( 'Ymd' , $event_start_timestamp );
} else {
$exdate_compare = date ( 'Ymd\THis' , $event_start_timestamp );
}
2022-06-16 14:01:47 +02:00
if (
$event_end_timestamp > $current
&& $event_start_timestamp < $until
&& $count_counter <= $rrule_count
&& $event_start_timestamp >= $date_from_ics
&& ! in_array ( $exdate_compare , $exdates , true )
) {
if ( 8 === strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
$event [ 'DTSTART' ] = date ( 'Ymd' , $event_start_timestamp );
2022-06-16 14:01:47 +02:00
$event [ 'DTEND' ] = date ( 'Ymd' , $event_end_timestamp );
2019-11-15 23:26:29 +01:00
} else {
$event [ 'DTSTART' ] = date ( 'Ymd\T' , $event_start_timestamp ) . $start_time ;
2022-06-16 14:01:47 +02:00
$event [ 'DTEND' ] = date ( 'Ymd\T' , $event_end_timestamp ) . $end_time ;
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
if ( $this -> timezone -> getName () && 8 !== strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
try {
$adjusted_time = new DateTime ( $event [ 'DTSTART' ], new DateTimeZone ( $this -> timezone -> getName () ) );
$adjusted_time -> setTimeZone ( new DateTimeZone ( 'UTC' ) );
2022-06-16 14:01:47 +02:00
$event [ 'DTSTART' ] = $adjusted_time -> format ( 'Ymd\THis' );
2019-11-15 23:26:29 +01:00
$event [ 'DTEND' ] = date ( 'Ymd\THis' , strtotime ( $event [ 'DTSTART' ] ) + $duration );
2022-06-16 14:01:47 +02:00
} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// Invalid argument to DateTime.
2019-11-15 23:26:29 +01:00
}
}
$upcoming [] = $event ;
2023-04-26 17:39:43 +02:00
++ $count_counter ;
2019-11-15 23:26:29 +01:00
}
}
2022-06-16 14:01:47 +02:00
// Set up next interval and reset $event['DTSTART'] and $event['DTEND'], keeping timestamps intact.
2019-11-15 23:26:29 +01:00
$next_start_timestamp = strtotime ( " + { $interval } { $frequency } s " , $event_start_timestamp );
2022-06-16 14:01:47 +02:00
if ( 8 === strlen ( $event [ 'DTSTART' ] ) ) {
2019-11-15 23:26:29 +01:00
$event [ 'DTSTART' ] = date ( 'Ymd' , $next_start_timestamp );
2022-06-16 14:01:47 +02:00
$event [ 'DTEND' ] = date ( 'Ymd' , strtotime ( $event [ 'DTSTART' ] ) + $duration );
2019-11-15 23:26:29 +01:00
} else {
$event [ 'DTSTART' ] = date ( 'Ymd\THis' , $next_start_timestamp );
2022-06-16 14:01:47 +02:00
$event [ 'DTEND' ] = date ( 'Ymd\THis' , strtotime ( $event [ 'DTSTART' ] ) + $duration );
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
// Move recurring event date forward.
2019-11-15 23:26:29 +01:00
$recurring_event_date_start = $event [ 'DTSTART' ];
}
$set_recurring_events [] = $uid ;
}
2023-04-26 17:39:43 +02:00
} elseif ( strtotime ( isset ( $event [ 'DTEND' ] ) ? $event [ 'DTEND' ] : $event [ 'DTSTART' ] ) >= $current ) { // Process normal events.
$upcoming [] = $event ;
2019-11-15 23:26:29 +01:00
}
}
return $upcoming ;
}
/**
* Parse events from an iCalendar feed
*
2022-06-16 14:01:47 +02:00
* @ param string $url ( default : '' ) .
2019-11-15 23:26:29 +01:00
* @ return array | false on failure
*/
public function parse ( $url = '' ) {
2022-06-16 14:01:47 +02:00
$cache_group = 'icalendar_reader_parse' ;
2019-11-15 23:26:29 +01:00
$disable_get_key = 'disable:' . md5 ( $url );
2022-06-16 14:01:47 +02:00
// Check to see if previous attempts have failed.
if ( false !== wp_cache_get ( $disable_get_key , $cache_group ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
2023-12-07 09:44:11 +01:00
// rewrite webcal: URI scheme to HTTP.
2022-06-16 14:01:47 +02:00
$url = preg_replace ( '/^webcal/' , 'http' , $url );
// try to fetch.
2023-01-25 20:43:46 +01:00
$r = wp_safe_remote_get (
2022-06-16 14:01:47 +02:00
$url ,
array (
'timeout' => 3 ,
'sslverify' => false ,
)
);
2019-11-15 23:26:29 +01:00
if ( 200 !== wp_remote_retrieve_response_code ( $r ) ) {
2022-06-16 14:01:47 +02:00
// We were unable to fetch any content, so don't try again for another 60 seconds.
2019-11-15 23:26:29 +01:00
wp_cache_set ( $disable_get_key , 1 , $cache_group , 60 );
return false ;
}
$body = wp_remote_retrieve_body ( $r );
2022-06-16 14:01:47 +02:00
if ( empty ( $body ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
$body = str_replace ( " \r \n " , " \n " , $body );
2019-11-15 23:26:29 +01:00
$lines = preg_split ( " / \n (?=[A-Z])/ " , $body );
2022-06-16 14:01:47 +02:00
if ( empty ( $lines ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
if ( false === stristr ( $lines [ 0 ], 'BEGIN:VCALENDAR' ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
$type = '' ;
2019-11-15 23:26:29 +01:00
foreach ( $lines as $line ) {
2022-06-16 14:01:47 +02:00
$add = $this -> key_value_from_string ( $line );
2019-11-15 23:26:29 +01:00
if ( ! $add ) {
$this -> add_component ( $type , false , $line );
continue ;
}
list ( $keyword , $value ) = $add ;
switch ( $keyword ) {
case 'BEGIN' :
case 'END' :
switch ( $line ) {
case 'BEGIN:VTODO' :
2023-04-26 17:39:43 +02:00
++ $this -> todo_count ;
2019-11-15 23:26:29 +01:00
$type = 'VTODO' ;
break ;
case 'BEGIN:VEVENT' :
2023-04-26 17:39:43 +02:00
++ $this -> event_count ;
2019-11-15 23:26:29 +01:00
$type = 'VEVENT' ;
break ;
case 'BEGIN:VCALENDAR' :
case 'BEGIN:DAYLIGHT' :
case 'BEGIN:VTIMEZONE' :
case 'BEGIN:STANDARD' :
$type = $value ;
break ;
case 'END:VTODO' :
case 'END:VEVENT' :
case 'END:VCALENDAR' :
case 'END:DAYLIGHT' :
case 'END:VTIMEZONE' :
case 'END:STANDARD' :
$type = 'VCALENDAR' ;
break ;
}
break ;
case 'TZID' :
2022-06-16 14:01:47 +02:00
if (
'VTIMEZONE' === $type
&& ! $this -> timezone
) {
2019-11-15 23:26:29 +01:00
$this -> timezone = $this -> timezone_from_string ( $value );
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
break ;
case 'X-WR-TIMEZONE' :
2022-06-16 14:01:47 +02:00
if ( ! $this -> timezone ) {
2019-11-15 23:26:29 +01:00
$this -> timezone = $this -> timezone_from_string ( $value );
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
break ;
default :
$this -> add_component ( $type , $keyword , $value );
break ;
}
}
2022-06-16 14:01:47 +02:00
// Filter for RECURRENCE-IDs.
2019-11-15 23:26:29 +01:00
$recurrences = array ();
if ( array_key_exists ( 'VEVENT' , $this -> cal ) ) {
foreach ( $this -> cal [ 'VEVENT' ] as $event ) {
if ( isset ( $event [ 'RECURRENCE-ID' ] ) ) {
$recurrences [] = $event ;
}
}
foreach ( $recurrences as $recurrence ) {
2022-06-16 14:01:47 +02:00
$count_vevent = count ( $this -> cal [ 'VEVENT' ] );
for ( $i = 0 ; $i < $count_vevent ; $i ++ ) {
if (
$this -> cal [ 'VEVENT' ][ $i ][ 'UID' ] === $recurrence [ 'UID' ]
&& ! isset ( $this -> cal [ 'VEVENT' ][ $i ][ 'RECURRENCE-ID' ] )
) {
2019-11-15 23:26:29 +01:00
$this -> cal [ 'VEVENT' ][ $i ][ 'EXDATE' ][] = $recurrence [ 'RECURRENCE-ID' ];
break ;
}
}
}
}
return $this -> cal ;
}
/**
* Parse key : value from a string
*
2022-06-16 14:01:47 +02:00
* @ param string $text ( default : '' ) .
2019-11-15 23:26:29 +01:00
* @ return array
*/
public function key_value_from_string ( $text = '' ) {
preg_match ( '/([^:]+)(;[^:]+)?[:]([\w\W]*)/' , $text , $matches );
2022-06-16 14:01:47 +02:00
if ( 0 === count ( $matches ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
return array ( $matches [ 1 ], $matches [ 3 ] );
}
/**
* Convert a timezone name into a timezone object .
*
2022-06-16 14:01:47 +02:00
* @ param string $text Timezone name . Example : America / Chicago .
2019-11-15 23:26:29 +01:00
* @ return object | null A DateTimeZone object if the conversion was successful .
*/
private function timezone_from_string ( $text ) {
try {
$timezone = new DateTimeZone ( $text );
} catch ( Exception $e ) {
$blog_timezone = get_option ( 'timezone_string' );
if ( ! $blog_timezone ) {
$blog_timezone = 'Etc/UTC' ;
}
$timezone = new DateTimeZone ( $blog_timezone );
}
return $timezone ;
}
/**
* Add a component to the calendar array
*
2022-06-16 14:01:47 +02:00
* @ param string $component ( default : '' ) .
* @ param bool | string $keyword ( default : '' ) .
* @ param string $value ( default : '' ) .
2019-11-15 23:26:29 +01:00
* @ return void
*/
public function add_component ( $component = '' , $keyword = '' , $value = '' ) {
2022-06-16 14:01:47 +02:00
if ( ! $keyword ) {
2019-11-15 23:26:29 +01:00
$keyword = $this -> last_keyword ;
switch ( $component ) {
2022-06-16 14:01:47 +02:00
case 'VEVENT' :
$value = $this -> cal [ $component ][ $this -> event_count - 1 ][ $keyword ] . $value ;
break ;
case 'VTODO' :
$value = $this -> cal [ $component ][ $this -> todo_count - 1 ][ $keyword ] . $value ;
break ;
2019-11-15 23:26:29 +01:00
}
}
/*
* Some events have a specific timezone set in their start / end date ,
* and it may or may not be different than the calendar timzeone .
* Valid formats include :
* DTSTART ; TZID = Pacific Standard Time : 20141219 T180000
* DTEND ; TZID = Pacific Standard Time : 20141219 T200000
* EXDATE : 19960402 T010000Z , 19960403 T010000Z , 19960404 T010000Z
* EXDATE ; VALUE = DATE : 2015050
* EXDATE ; TZID = America / New_York : 20150424 T170000
* EXDATE ; TZID = Pacific Standard Time : 20120615 T140000 , 20120629 T140000 , 20120706 T140000
*/
2022-06-16 14:01:47 +02:00
// Always store EXDATE as an array.
2019-11-15 23:26:29 +01:00
if ( stristr ( $keyword , 'EXDATE' ) ) {
$value = explode ( ',' , $value );
}
2022-06-16 14:01:47 +02:00
// Adjust DTSTART, DTEND, and EXDATE according to their TZID if set.
2019-11-15 23:26:29 +01:00
if ( strpos ( $keyword , ';' ) && ( stristr ( $keyword , 'DTSTART' ) || stristr ( $keyword , 'DTEND' ) || stristr ( $keyword , 'EXDATE' ) || stristr ( $keyword , 'RECURRENCE-ID' ) ) ) {
$keyword = explode ( ';' , $keyword );
$tzid = false ;
2022-06-16 14:01:47 +02:00
if ( 2 === count ( $keyword ) ) {
2019-11-15 23:26:29 +01:00
$tparam = $keyword [ 1 ];
2023-12-07 09:44:11 +01:00
if ( str_contains ( $tparam , 'TZID' ) ) {
2019-11-15 23:26:29 +01:00
$tzid = $this -> timezone_from_string ( str_replace ( 'TZID=' , '' , $tparam ) );
}
}
2022-06-16 14:01:47 +02:00
// Normalize all times to default UTC.
2019-11-15 23:26:29 +01:00
if ( $tzid ) {
$adjusted_times = array ();
foreach ( ( array ) $value as $v ) {
try {
$adjusted_time = new DateTime ( $v , $tzid );
$adjusted_time -> setTimeZone ( new DateTimeZone ( 'UTC' ) );
2022-06-16 14:01:47 +02:00
$adjusted_times [] = $adjusted_time -> format ( 'Ymd\THis' );
2019-11-15 23:26:29 +01:00
} catch ( Exception $e ) {
2022-06-16 14:01:47 +02:00
// Invalid argument to DateTime.
2019-11-15 23:26:29 +01:00
return ;
}
}
$value = $adjusted_times ;
}
2022-06-16 14:01:47 +02:00
// Format for adding to event.
2019-11-15 23:26:29 +01:00
$keyword = $keyword [ 0 ];
2022-06-16 14:01:47 +02:00
if ( 'EXDATE' !== $keyword ) {
2019-11-15 23:26:29 +01:00
$value = implode ( ( array ) $value );
}
}
foreach ( ( array ) $value as $v ) {
2022-06-16 14:01:47 +02:00
switch ( $component ) {
2019-11-15 23:26:29 +01:00
case 'VTODO' :
2022-06-16 14:01:47 +02:00
if ( 'EXDATE' === $keyword ) {
2019-11-15 23:26:29 +01:00
$this -> cal [ $component ][ $this -> todo_count - 1 ][ $keyword ][] = $v ;
} else {
$this -> cal [ $component ][ $this -> todo_count - 1 ][ $keyword ] = $v ;
}
break ;
case 'VEVENT' :
2022-06-16 14:01:47 +02:00
if ( 'EXDATE' === $keyword ) {
2019-11-15 23:26:29 +01:00
$this -> cal [ $component ][ $this -> event_count - 1 ][ $keyword ][] = $v ;
} else {
$this -> cal [ $component ][ $this -> event_count - 1 ][ $keyword ] = $v ;
}
break ;
default :
$this -> cal [ $component ][ $keyword ] = $v ;
break ;
}
}
$this -> last_keyword = $keyword ;
}
/**
* Escape strings with wp_kses , allow links
*
2022-06-16 14:01:47 +02:00
* @ param string $string ( default : '' ) The string to escape .
2019-11-15 23:26:29 +01:00
* @ return string
*/
public function escape ( $string = '' ) {
2022-06-16 14:01:47 +02:00
// Unfold content lines per RFC 5545.
2019-11-15 23:26:29 +01:00
$string = str_replace ( " \n \t " , '' , $string );
$string = str_replace ( " \n " , '' , $string );
$allowed_html = array (
'a' => array (
'href' => array (),
2022-06-16 14:01:47 +02:00
'title' => array (),
),
2019-11-15 23:26:29 +01:00
);
$allowed_tags = '' ;
foreach ( array_keys ( $allowed_html ) as $tag ) {
$allowed_tags .= " < { $tag } > " ;
}
// Running strip_tags() first with allowed tags to get rid of remaining gallery markup, etc
// because wp_kses() would only htmlentity'fy that. Then still running wp_kses(), for extra
// safety and good measure.
return wp_kses ( strip_tags ( $string , $allowed_tags ), $allowed_html );
}
/**
* Render the events
*
2022-06-16 14:01:47 +02:00
* @ param string $url ( default : '' ) URL of the iCal feed .
* @ param array $args Event options .
*
2019-11-15 23:26:29 +01:00
* @ return mixed bool | string false on failure , rendered HTML string on success .
*/
public function render ( $url = '' , $args = array () ) {
2022-06-16 14:01:47 +02:00
$args = wp_parse_args (
$args ,
array (
'context' => 'widget' ,
'number' => 5 ,
)
);
2019-11-15 23:26:29 +01:00
$events = $this -> get_events ( $url , $args [ 'number' ] );
$events = $this -> apply_timezone_offset ( $events );
2022-06-16 14:01:47 +02:00
if ( empty ( $events ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
ob_start ();
2022-06-16 14:01:47 +02:00
if ( 'widget' === $args [ 'context' ] ) : ?>
2019-11-15 23:26:29 +01:00
< ul class = " upcoming-events " >
< ? php foreach ( $events as $event ) : ?>
< li >
2022-06-16 14:01:47 +02:00
< strong class = " event-summary " >
< ? php
echo $this -> escape ( stripslashes ( $event [ 'SUMMARY' ] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
?>
</ strong >
< span class = " event-when " >< ? php echo esc_html ( $this -> formatted_date ( $event ) ); ?> </span>
2019-11-15 23:26:29 +01:00
< ? php if ( ! empty ( $event [ 'LOCATION' ] ) ) : ?>
2022-06-16 14:01:47 +02:00
< span class = " event-location " >
< ? php
echo $this -> escape ( stripslashes ( $event [ 'LOCATION' ] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
?>
</ span >
2019-11-15 23:26:29 +01:00
< ? php endif ; ?>
< ? php if ( ! empty ( $event [ 'DESCRIPTION' ] ) ) : ?>
2022-06-16 14:01:47 +02:00
< span class = " event-description " >
< ? php
echo wp_trim_words ( $this -> escape ( stripcslashes ( $event [ 'DESCRIPTION' ] ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
?>
</ span >
2019-11-15 23:26:29 +01:00
< ? php endif ; ?>
</ li >
< ? php endforeach ; ?>
</ ul >
2022-06-16 14:01:47 +02:00
< ? php
endif ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
if ( 'shortcode' === $args [ 'context' ] ) :
?>
2019-11-15 23:26:29 +01:00
< table class = " upcoming-events " >
< thead >
< tr >
< th >< ? php esc_html_e ( 'Location' , 'jetpack' ); ?> </th>
< th >< ? php esc_html_e ( 'When' , 'jetpack' ); ?> </th>
< th >< ? php esc_html_e ( 'Summary' , 'jetpack' ); ?> </th>
< th >< ? php esc_html_e ( 'Description' , 'jetpack' ); ?> </th>
</ tr >
</ thead >
< tbody >
< ? php foreach ( $events as $event ) : ?>
< tr >
2022-06-16 14:01:47 +02:00
< td >
< ? php
echo empty ( $event [ 'LOCATION' ] )
? ' '
: $this -> escape ( stripslashes ( $event [ 'LOCATION' ] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
?>
</ td >
< td >< ? php echo esc_html ( $this -> formatted_date ( $event ) ); ?> </td>
< td >
< ? php
echo empty ( $event [ 'SUMMARY' ] )
? ' '
: $this -> escape ( stripslashes ( $event [ 'SUMMARY' ] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
?>
</ td >
< td >
< ? php
echo empty ( $event [ 'DESCRIPTION' ] )
? ' '
: wp_trim_words ( $this -> escape ( stripcslashes ( $event [ 'DESCRIPTION' ] ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
?>
</ td >
2019-11-15 23:26:29 +01:00
</ tr >
< ? php endforeach ; ?>
</ tbody >
</ table >
2022-06-16 14:01:47 +02:00
< ? php
endif ;
2019-11-15 23:26:29 +01:00
$rendered = ob_get_clean ();
2022-06-16 14:01:47 +02:00
if ( empty ( $rendered ) ) {
2019-11-15 23:26:29 +01:00
return false ;
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
return $rendered ;
}
2022-06-16 14:01:47 +02:00
/**
* Return a localized string with information about the event ' s date and time ,
* or starting date and end date .
*
* @ param array $event Info about the event .
*
* @ return string
*/
2019-11-15 23:26:29 +01:00
public function formatted_date ( $event ) {
$date_format = get_option ( 'date_format' );
$time_format = get_option ( 'time_format' );
2022-06-16 14:01:47 +02:00
$start = strtotime ( $event [ 'DTSTART' ] );
$end = isset ( $event [ 'DTEND' ] ) ? strtotime ( $event [ 'DTEND' ] ) : false ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
$all_day = ( 8 === strlen ( $event [ 'DTSTART' ] ) );
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
if ( ! $all_day && $this -> timezone ) {
2019-11-15 23:26:29 +01:00
try {
2023-01-25 20:43:46 +01:00
$timezone_offset = $this -> timezone -> getOffset ( new DateTime ( $event [ 'DTSTART' ] ) );
2022-06-16 14:01:47 +02:00
$start += $timezone_offset ;
2019-11-15 23:26:29 +01:00
if ( $end ) {
$end += $timezone_offset ;
}
} catch ( Exception $e ) {
2022-06-16 14:01:47 +02:00
// Invalid argument to DateTime.
return '' ;
2019-11-15 23:26:29 +01:00
}
}
$single_day = $end ? ( $end - $start ) <= DAY_IN_SECONDS : true ;
/* translators: Date and time */
2022-06-16 14:01:47 +02:00
$date_with_time = __ ( '%1$s at %2$s' , 'jetpack' );
2019-11-15 23:26:29 +01:00
/* translators: Two dates with a separator */
2022-06-16 14:01:47 +02:00
$two_dates = __ ( '%1$s – %2$s' , 'jetpack' );
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// we'll always have the start date. Maybe with time.
if ( $all_day ) {
2019-11-15 23:26:29 +01:00
$date = date_i18n ( $date_format , $start );
2022-06-16 14:01:47 +02:00
} else {
$date = sprintf (
$date_with_time ,
date_i18n ( $date_format , $start ),
date_i18n ( $time_format , $start )
);
}
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// single day, timed.
if ( $single_day && ! $all_day && false !== $end ) {
2019-11-15 23:26:29 +01:00
$date = sprintf ( $two_dates , $date , date_i18n ( $time_format , $end ) );
2022-06-16 14:01:47 +02:00
}
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// multi-day.
2019-11-15 23:26:29 +01:00
if ( ! $single_day ) {
if ( $all_day ) {
2022-06-16 14:01:47 +02:00
// DTEND for multi-day events represents "until", not "including", so subtract one minute.
2019-11-15 23:26:29 +01:00
$end_date = date_i18n ( $date_format , $end - 60 );
} else {
$end_date = sprintf ( $date_with_time , date_i18n ( $date_format , $end ), date_i18n ( $time_format , $end ) );
}
$date = sprintf ( $two_dates , $date , $end_date );
}
return $date ;
}
2022-06-16 14:01:47 +02:00
/**
* Sort list of events by event date .
*
* @ param array $list List of events .
*
* @ return array
*/
2019-11-15 23:26:29 +01:00
protected function sort_by_recent ( $list ) {
2022-06-16 14:01:47 +02:00
$dates = array ();
$sorted_list = array ();
2019-11-15 23:26:29 +01:00
foreach ( $list as $key => $row ) {
$date = $row [ 'DTSTART' ];
2022-06-16 14:01:47 +02:00
// pad some time onto an all day date.
if ( 8 === strlen ( $date ) ) {
2019-11-15 23:26:29 +01:00
$date .= 'T000000Z' ;
2022-06-16 14:01:47 +02:00
}
$dates [ $key ] = $date ;
2019-11-15 23:26:29 +01:00
}
asort ( $dates );
2022-06-16 14:01:47 +02:00
foreach ( $dates as $key => $value ) {
$sorted_list [ $key ] = $list [ $key ];
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
unset ( $list );
2019-11-15 23:26:29 +01:00
return $sorted_list ;
}
2022-06-16 14:01:47 +02:00
// phpcs:enable WordPress.DateTime.RestrictedFunctions.date_date
2019-11-15 23:26:29 +01:00
}
/**
* Wrapper function for iCalendarReader -> get_events ()
*
2022-06-16 14:01:47 +02:00
* @ param string $url ( default : '' ) .
* @ param int $count Number of events to fetch .
2019-11-15 23:26:29 +01:00
* @ return array
*/
function icalendar_get_events ( $url = '' , $count = 5 ) {
2022-06-16 14:01:47 +02:00
/*
* Find your calendar ' s address
* https :// support . google . com / calendar / bin / answer . py ? hl = en & answer = 37103
*/
2023-01-25 20:43:46 +01:00
return ( new iCalendarReader () ) -> get_events ( $url , $count );
2019-11-15 23:26:29 +01:00
}
/**
* Wrapper function for iCalendarReader -> render ()
*
2022-06-16 14:01:47 +02:00
* @ param string $url ( default : '' ) .
* @ param array $args Options when rendering events .
*
2019-11-15 23:26:29 +01:00
* @ return mixed bool | string false on failure , rendered HTML string on success .
*/
function icalendar_render_events ( $url = '' , $args = array () ) {
2023-01-25 20:43:46 +01:00
return ( new iCalendarReader () ) -> render ( $url , $args );
2019-11-15 23:26:29 +01:00
}