Files
Roman Pyrih d6241cfa7a first commit
2024-12-19 15:27:13 +01:00

408 lines
17 KiB
PHP

<?php
/**
* @version 1.0
* @package ICS functions
* @subpackage Import / Export ICS
* @category ICS
*
* @author wpdevelop
* @link https://oplugins.com/
* @email info@oplugins.com
*
* @modified 2017-03-01
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
require_once( WPBM_PLUGIN_DIR . '/assets/libs/icalendar/zapcallib.php' );
/** Download Feed .ICS & Parse to Array
*
* @param string $ics_url - URL to ics file
* @return array | WP_Error on error - Array return array( 'events' => $ics_events, 'data' => $ics_data );
$ics_events = array (
[DTSTART] => 20140716
[DTEND] => 20140717
[DTSTAMP] => 20170627T112020Z
[UID] => d3ihiuv8nqbti9djmpetrgn5ok@google.com
[CREATED] => 20140628T081753Z
[DESCRIPTION] =>
[LAST-MODIFIED] => 20140628T081753Z
[LOCATION] =>
[SEQUENCE] => 0
[STATUS] => CONFIRMED
[SUMMARY] => Test booking here
[TRANSP] => TRANSPARENT
[RRULE] => FREQ=DAILY;UNTIL=20140717
[_BOOKING_DATES] => Array
(
[0] => 2014-07-16 00:00:00
[1] => 2014-07-17 00:00:00
[2] => 2014-07-17 00:00:00
)
[_BOOKING_SUMMARY] => Test booking here
[_BOOKING_DESCRIPTION] =>
[_BOOKING_LOCATION] =>
[_BOOKING_GUID] => d3ihiuv8nqbti9djmpetrgn5ok@google.com
[_BOOKING_MODIFIED] => 2014-06-28 08:17:53
)
*/
function wpbm_ics_file_to_array( $ics_url ) {
$ics_content = wpbm_get_ssl_page_content( $ics_url );
if ( false === $ics_content )
return new WP_Error( 'wpbm_ics_url_error', '<strong>[WPBM Error]</strong> ' . __( 'Could not download URL' ) . ' ' . $ics_url , 'wrong_url' );
$ics_events = wpbm_ics_content_to_array( $ics_content );
return $ics_events;
}
/** Parse Content .ICS to array
*
* @param string $ics_content - ICS content
* @return array | false on error - ICS Events Array
* @return array | false on error - ICS Events Array return array( 'events' => $ics_events, 'data' => $ics_data );
$ics_events = array (
[DTSTART] => 20140716
[DTEND] => 20140717
[DTSTAMP] => 20170627T112020Z
[UID] => d3ihiuv8nqbti9djmpetrgn5ok@google.com
[CREATED] => 20140628T081753Z
[DESCRIPTION] =>
[LAST-MODIFIED] => 20140628T081753Z
[LOCATION] =>
[SEQUENCE] => 0
[STATUS] => CONFIRMED
[SUMMARY] => Test booking here
[TRANSP] => TRANSPARENT
[RRULE] => FREQ=DAILY;UNTIL=20140717
[_BOOKING_DATES] => Array
(
[0] => 2014-07-16 00:00:00
[1] => 2014-07-17 00:00:00
[2] => 2014-07-17 00:00:00
)
[_BOOKING_SUMMARY] => Test booking here
[_BOOKING_DESCRIPTION] =>
[_BOOKING_LOCATION] =>
[_BOOKING_GUID] => d3ihiuv8nqbti9djmpetrgn5ok@google.com
[_BOOKING_MODIFIED] => 2014-06-28 08:17:53
)
*/
function wpbm_ics_content_to_array( $ics_content ) {
if ( empty( $ics_content ) )
return false;
if ( false === strpos( $ics_content, 'VEVENT' ) ) {
return new WP_Error( 'wpbm_ics_error', '<strong>[WPBM Error]</strong> ' . __( 'File does not contain events' ) . ' ' , 'wrong_ics_content' );
}
$ics_content = trim($ics_content); //FixIn: 2.1.9.2
$icalobj = new ZCiCal( $ics_content ); // create the ical object
$events_count = 0;
$ics_events = array();
if ( isset( $icalobj->tree->child ) ) {
foreach ( $icalobj->tree->child as $node ) {
if ( $node->getName() == "VEVENT" ) {
$ics_events[ $events_count ] = array();
foreach ( $node->data as $key => $value ) {
$ics_events[ $events_count ][ $key ] = $value->getValues();
}
$events_count++;
}
}
}
/** Data, like: array ( [PRODID] => -//Calendar Labs//Calendar 1.0//EN
[VERSION] => 2.0
[CALSCALE] => GREGORIAN
[METHOD] => PUBLISH
[X-WR-CALNAME] => Us Holidays
[X-WR-TIMEZONE] => America/New_York
*/
$ics_data = array();
if ( isset( $icalobj->tree->data ) ) {
foreach ( $icalobj->tree->data as $node ) {
$ics_data[ $node->getName() ] = $node->getValues();
}
}
// Add Booking fields, like "_BOOKING_DATES, _BOOKING_SUMMARY, ..."
foreach ( $ics_events as $i_key => $i_event) {
//FixIn: 2.0.23.1
/**
* The spec says that if DTSTART has a DATE data type, and there is no DTEND then the event finishes at the end of the day that it starts.
* But if DTSTART has a full DATE-TIME data type, and there is no DTEND then it finishes at the same time that it starts.
*
* It's in section 3.6.1 of RFC 5545 (https://www.rfc-editor.org/rfc/rfc5545#page-54):
*
* For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE value type but no "DTEND" nor "DURATION" property,
* the event's duration is taken to be one day. For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE-TIME value
* type but no "DTEND" property, the event ends on the same calendar date and time of day specified by the "DTSTART" property.
*/
if ( ( empty( $ics_events[ $i_key ]['DTEND'] ) )
&& ( ! empty( $ics_events[ $i_key ]['DTSTART'] ) ) ) {
$ics_events[ $i_key ]['DTEND'] = 'null';
}
$dates_arr = wpbm_get_dates_arr_from_ics_dates( $ics_events[ $i_key ][ 'DTSTART' ]
, $ics_events[ $i_key ][ 'DTEND' ]
, ( empty( $ics_events[ $i_key ]['RRULE'] ) ? '' : $ics_events[ $i_key ]['RRULE'] )
, ( empty( $ics_events[ $i_key ]['RDATE'] ) ? '' : $ics_events[ $i_key ]['RDATE'] )
);
// Bookings Fields
$ics_events[ $i_key ]['_BOOKING_DATES'] = $dates_arr;
$ics_events[ $i_key ]['_BOOKING_SUMMARY'] = '';
$ics_events[ $i_key ]['_BOOKING_DESCRIPTION'] = '';
$ics_events[ $i_key ]['_BOOKING_LOCATION'] = '';
$ics_events[ $i_key ]['_BOOKING_UID'] = '';
$ics_events[ $i_key ]['_BOOKING_MODIFIED'] = '';
$ics_events[ $i_key ]['_BOOKING_ATTENDEE'] = '';
if ( ! empty( $ics_events[ $i_key ]['SUMMARY'] ) ) {
$ics_events[ $i_key ]['_BOOKING_SUMMARY'] = $ics_events[ $i_key ]['SUMMARY'];
}
if ( ! empty( $ics_events[ $i_key ]['DESCRIPTION'] ) ) {
$ics_events[ $i_key ]['_BOOKING_DESCRIPTION'] = $ics_events[ $i_key ]['DESCRIPTION'];
}
if ( ! empty( $ics_events[ $i_key ]['LOCATION'] ) ) {
$ics_events[ $i_key ]['_BOOKING_LOCATION'] = $ics_events[ $i_key ]['LOCATION'];
}
if ( ! empty( $ics_events[ $i_key ]['ATTENDEE'] ) ) {
$ics_events[ $i_key ]['_BOOKING_ATTENDEE'] = $ics_events[ $i_key ]['ATTENDEE'];
}
// 15ig8t0i739kajgjc8ekc386dt@google.com - ID of event from ICS
// 15ig8t0i739kajgjc8ekc386dt_20170815 - ID of event during import from Google Calendar (probably created by ID before @ + first date)
if ( ! empty( $ics_events[ $i_key ]['UID'] ) ) {
$ics_events[ $i_key ]['_BOOKING_UID'] = $ics_events[ $i_key ]['UID'];
}
if ( ! empty( $ics_events[ $i_key ]['LAST-MODIFIED'] ) ) {
$ics_events[ $i_key ]['_BOOKING_MODIFIED'] = date_i18n( 'Y-m-d H:i:s', ZDateHelper::fromiCaltoUnixDateTime( $ics_events[ $i_key ]['LAST-MODIFIED'] ) );
}
}
return array( 'data' => $ics_data, 'events' => $ics_events );
}
/** Get Dates array from START & END times & RULES
* Examples: 2017-07-10 00:00:00 -- 2017-07-13 00:00:00 on FREQ=MONTHLY;COUNT=4;BYDAY=2MO
* OR 2017-06-14 12:00:00 -- 2017-06-14 15:30:00 on FREQ=WEEKLY;COUNT=3;BYDAY=WE,SA
*
* @param string $start_date_ics // 20170614T120000
* @param string $end_date_ics // 20170614T153000
* @param string $ics_rules // FREQ=WEEKLY;COUNT=3;BYDAY=WE,SA
* @param string $ics_rdates // 20170826T120001,20170830T120001
* @return array // Array (
[0] => 2017-06-14 12:00:01
[1] => 2017-06-14 15:30:02
[2] => 2017-06-17 12:00:01
[3] => 2017-06-17 15:30:02
[4] => 2017-06-21 12:00:01
[5] => 2017-06-21 15:30:02
)
*/
function wpbm_get_dates_arr_from_ics_dates( $start_date_ics, $end_date_ics, $ics_rules , $ics_rdates ) {
//FixIn: 2.0.23.1
/**
* The spec says that if DTSTART has a DATE data type, and there is no DTEND then the event finishes at the end of the day that it starts.
* But if DTSTART has a full DATE-TIME data type, and there is no DTEND then it finishes at the same time that it starts.
*
* It's in section 3.6.1 of RFC 5545 (https://www.rfc-editor.org/rfc/rfc5545#page-54):
*
* For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE value type but no "DTEND" nor "DURATION" property,
* the event's duration is taken to be one day. For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE-TIME value
* type but no "DTEND" property, the event ends on the same calendar date and time of day specified by the "DTSTART" property.
*/
if ('null' === $end_date_ics) {
$end_date_ics = wpbm_ics_date_reset_seconds( $start_date_ics ); // 20170614T120000
$start_date_unix_calc = ZDateHelper::fromiCaltoUnixDateTime(
wpbm_ics_date_reset_seconds( $start_date_ics )
); // 1499644800 - 20170710
$end_date_ics = strtotime( '+1 minute', $start_date_unix_calc );
$end_date_ics = ZDateHelper::fromUnixDateTimetoiCal( $end_date_ics );
}
$start_date_ics = wpbm_ics_date_reset_seconds( $start_date_ics ); // 20170614T120000
$end_date_ics = wpbm_ics_date_reset_seconds( $end_date_ics ); // 20170614T120000
// FREQ=MONTHLY;COUNT=4;BYDAY=2MO - Rules
$start_date_unix = ZDateHelper::fromiCaltoUnixDateTime( $start_date_ics ); // 1499644800 - 20170710
$end_date_unix = ZDateHelper::fromiCaltoUnixDateTime( $end_date_ics ); // 1499904000 - 20170713
$time_difference = $end_date_unix - $start_date_unix; // 259200 - Difference in seconds
$start_date = date_i18n( 'Y-m-d H:i:s', $start_date_unix ); // 2017-07-10 00:00:00
$end_date = date_i18n( 'Y-m-d H:i:s', $end_date_unix ); // 2017-07-13 00:00:00
// Get Unix Time for Strat Date at midnight -- 2017-07-10
$start_date_midnight_unix = strtotime( date_i18n( 'Y-m-d 00:00:00', $start_date_unix ) );
$start_date_midnight = date_i18n( 'Y-m-d H:i:s', $start_date_midnight_unix ); // Same in MySQL format: 2017-07-10 00:00:00
////////////////////////////////////////////////////////////////////////////////////////////////////////////
$internal_dates =array();
// Get Unix Time for Strat Date at midnight -- 2017-07-10
$end_date_midnight_unix = strtotime( date_i18n( 'Y-m-d 00:00:00', $end_date_unix ) );
$end_date_midnight = date_i18n( 'Y-m-d H:i:s', $end_date_midnight_unix ); // Same in MySQL format: 2018-01-14 00:00:00
// Get internal days between START and END dates: // array( [0] => 1499731200 [1] => 1499817600 )
// Need to check relative to the END MIDNIGHT, becase if
// we have END DATE for specific time, like 2018-01-14 02:30:02 , we do NOT have this date as 2018-01-14 00:00:00
while( ( $start_date_midnight_unix + ( 60*60*24 ) ) < $end_date_midnight_unix ) {
$start_date_midnight_unix += 60*60*24; // Get next day
$internal_dates[] = $start_date_midnight_unix;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Get Initial Dates interval
$initial_time_interval_unix = array();
// In case if we have start time, like 15:00:00 we will trasnform it to 15:00:01 - for internal usage of Booking Calendar
if ( substr( $start_date, 11 ) != '00:00:00' ) $initial_time_interval_unix[] = $start_date_unix + 1; // Start time for specific time slot
else $initial_time_interval_unix[] = $start_date_unix ; // Full date
foreach ( $internal_dates as $internal_date_unix ) {
$initial_time_interval_unix[] = $internal_date_unix;
}
// Same for end date/time +2 seconds for having time like 10:00:02
if ( substr( $end_date, 11 ) != '00:00:00' ) $initial_time_interval_unix[] = $end_date_unix - 8; // End time for specific time slot //FixIn: 2.0.22.2 //FixIn: 9.0.1.3 //FixIn: 2.0.21.1
// We do not need to add END date, if the TIME == '00:00:00', because EVENT ended at previous date
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Here we are getting SHIFT in seonds starting from INITIAL start DATE_TIME
$initial_time_interval_shift_unix = array();
foreach ( $initial_time_interval_unix as $internal_date_unix ) {
$initial_time_interval_shift_unix[] = $internal_date_unix - $start_date_unix;
}
// $initial_time_interval_shift_unix >>> FULL DAYS: array( [0] => 0 [1] => 86400 [2] => 172800 )
// $initial_time_interval_shift_unix >>> TIMES: array( [0] => 1 [1] => 12602 )
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Get DATES depsnding from RULES imported from ICS
$ics_start_dates_unix = array();
if ( ! empty( $ics_rules ) ) {
// Parse RULE and Get Dates array
$rDates = new ZCRecurringDate(
$ics_rules
, strtotime( $start_date_ics )
);
$ics_start_dates_unix = $rDates->getDates(); // MAX DATE: strtotime( $ics_events[ $i_key ][ 'DTEND' ] ) );
sort( $ics_start_dates_unix ); // Sort
$ics_start_dates_unix = array_unique( $ics_start_dates_unix ); // Remove Duplicates
}
// Example: Array ( [0] => 1499644800 [1] => 1502668800 [2] => 1505088000 [3] => 1507507200 ) or array()
// If we are having rDates
if ( ! empty( $ics_rdates ) ) {
$ics_rdates = explode( ',', $ics_rdates );
foreach ( $ics_rdates as $ics_rdates_key => $ics_rdates_value ) {
$ics_rdates_value = wpbm_ics_date_reset_seconds( $ics_rdates_value ); // 20170614T120000
$ics_rdates_value = ZDateHelper::fromiCaltoUnixDateTime( $ics_rdates_value ); // // 1497441600
$ics_rdates[ $ics_rdates_key ] = $ics_rdates_value;
}
// We need to add also to these array start day
$ics_rdates[] = strtotime( $start_date_ics );
sort( $ics_rdates ); // Sort
$ics_start_dates_unix = array_merge( $ics_start_dates_unix, $ics_rdates );
$ics_start_dates_unix = array_unique( $ics_start_dates_unix ); // Remove Duplicates
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
$dates_arr_unix = array();
// We are having some RULES
if ( ! empty( $ics_start_dates_unix ) ) {
foreach ( $ics_start_dates_unix as $ics_start_date_unix ) { // Depend from rules, we are having starting interval of our EVENT
foreach ( $initial_time_interval_shift_unix as $shift_unix ) { // Because EVENT can have several dates or times, we add our SHIFT interval from INITIAL
$dates_arr_unix[] = $ics_start_date_unix + $shift_unix;
}
}
} else { // No Rules
foreach ( $initial_time_interval_unix as $initial_time_unix ) { // So we are getting only INITIAL interval
$dates_arr_unix[] = $initial_time_unix;
}
}
//ex: Array ( [0] => 1497441601 [1] => 1497454202 [2] => 1497700801 [3] => 1497713402 [4] => 1498046401 [5] => 1498059002 )
$dates_arr_mysql = array();
foreach ( $dates_arr_unix as $date_unix ) {
$dates_arr_mysql[] = date_i18n( 'Y-m-d H:i:s', $date_unix );
}
//ex: Array ( [0] => 2017-06-14 12:00:01 [1] => 2017-06-14 15:30:02 [2] => 2017-06-17 12:00:01 [3] => 2017-06-17 15:30:02 [4] => 2017-06-21 12:00:01 [5] => 2017-06-21 15:30:02 )
return $dates_arr_mysql;
}
/** Reset any seconds in ics date and return it,
* Its useful, if such info was exported by Booking Calendar and now is importing again.
*
* @param string $ics_date_time - 20170614T120001
* @return string - 20170614T120000
*/
function wpbm_ics_date_reset_seconds( $ics_date_time ) {
// 1497441601
$date_time_unix = ZDateHelper::fromiCaltoUnixDateTime( $ics_date_time );
// 2017-07-10 00:00:01
$date_time_sql = date_i18n( 'Y-m-d H:i:s', $date_time_unix );
if( strlen( $date_time_sql ) > 10 ) {
$date_time_sql = substr($date_time_sql, 0, -2 ) . '00'; // Reset seconds to 00, if previously we was have 01 or 02
}
// 2017-07-10 00:00:00
$date_time_unix = strtotime( $date_time_sql );
$ics_date_time = ZDateHelper::fromUnixDateTimetoiCal( $date_time_unix );
return $ics_date_time;
}