PluginProbe
Booking Manager – Sync WP Booking Calendar – Import Events, Export Bookings to ICS Calendar / trunk
Booking Manager – Sync WP Booking Calendar – Import Events, Export Bookings to ICS Calendar vtrunk
2.1.20 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 trunk 1.1 2.0 2.0.1 2.0.10.2 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.17 2.0.18 2.0.2 2.0.20 2.0.21 2.0.22 2.0.23 All 54 releases
booking-manager / core / wpbm-ics.php

wpbm-ics.php in Booking Manager – Sync WP Booking Calendar – Import Events, Export Bookings to ICS Calendar trunk, at core/wpbm-ics.php

408 lines 16.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @version 1.0
4 * @package ICS functions
5 * @subpackage Import / Export ICS
6 * @category ICS
7 *
8 * @author wpdevelop
9 * @link https://oplugins.com/
10 * @email info@oplugins.com
11 *
12 * @modified 2017-03-01
13 */
14
15 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
16
17
18 require_once( WPBM_PLUGIN_DIR . '/assets/libs/icalendar/zapcallib.php' );
19
20
21 /** Download Feed .ICS & Parse to Array
22 *
23 * @param string $ics_url - URL to ics file
24 * @return array | WP_Error on error - Array return array( 'events' => $ics_events, 'data' => $ics_data );
25 $ics_events = array (
26 [DTSTART] => 20140716
27 [DTEND] => 20140717
28 [DTSTAMP] => 20170627T112020Z
29 [UID] => d3ihiuv8nqbti9djmpetrgn5ok@google.com
30 [CREATED] => 20140628T081753Z
31 [DESCRIPTION] =>
32 [LAST-MODIFIED] => 20140628T081753Z
33 [LOCATION] =>
34 [SEQUENCE] => 0
35 [STATUS] => CONFIRMED
36 [SUMMARY] => Test booking here
37 [TRANSP] => TRANSPARENT
38 [RRULE] => FREQ=DAILY;UNTIL=20140717
39 [_BOOKING_DATES] => Array
40 (
41 [0] => 2014-07-16 00:00:00
42 [1] => 2014-07-17 00:00:00
43 [2] => 2014-07-17 00:00:00
44 )
45
46 [_BOOKING_SUMMARY] => Test booking here
47 [_BOOKING_DESCRIPTION] =>
48 [_BOOKING_LOCATION] =>
49 [_BOOKING_GUID] => d3ihiuv8nqbti9djmpetrgn5ok@google.com
50 [_BOOKING_MODIFIED] => 2014-06-28 08:17:53
51 )
52 */
53 function wpbm_ics_file_to_array( $ics_url ) {
54
55 $ics_content = wpbm_get_ssl_page_content( $ics_url );
56
57 if ( false === $ics_content )
58 return new WP_Error( 'wpbm_ics_url_error', '<strong>[WPBM Error]</strong> ' . __( 'Could not download URL' ) . ' ' . $ics_url , 'wrong_url' );
59
60
61 $ics_events = wpbm_ics_content_to_array( $ics_content );
62
63 return $ics_events;
64 }
65
66
67 /** Parse Content .ICS to array
68 *
69 * @param string $ics_content - ICS content
70 * @return array | false on error - ICS Events Array
71 * @return array | false on error - ICS Events Array return array( 'events' => $ics_events, 'data' => $ics_data );
72 $ics_events = array (
73 [DTSTART] => 20140716
74 [DTEND] => 20140717
75 [DTSTAMP] => 20170627T112020Z
76 [UID] => d3ihiuv8nqbti9djmpetrgn5ok@google.com
77 [CREATED] => 20140628T081753Z
78 [DESCRIPTION] =>
79 [LAST-MODIFIED] => 20140628T081753Z
80 [LOCATION] =>
81 [SEQUENCE] => 0
82 [STATUS] => CONFIRMED
83 [SUMMARY] => Test booking here
84 [TRANSP] => TRANSPARENT
85 [RRULE] => FREQ=DAILY;UNTIL=20140717
86 [_BOOKING_DATES] => Array
87 (
88 [0] => 2014-07-16 00:00:00
89 [1] => 2014-07-17 00:00:00
90 [2] => 2014-07-17 00:00:00
91 )
92
93 [_BOOKING_SUMMARY] => Test booking here
94 [_BOOKING_DESCRIPTION] =>
95 [_BOOKING_LOCATION] =>
96 [_BOOKING_GUID] => d3ihiuv8nqbti9djmpetrgn5ok@google.com
97 [_BOOKING_MODIFIED] => 2014-06-28 08:17:53
98 )
99 */
100 function wpbm_ics_content_to_array( $ics_content ) {
101
102 if ( empty( $ics_content ) )
103 return false;
104
105 if ( false === strpos( $ics_content, 'VEVENT' ) ) {
106 return new WP_Error( 'wpbm_ics_error', '<strong>[WPBM Error]</strong> ' . __( 'File does not contain events' ) . ' ' , 'wrong_ics_content' );
107 }
108
109 $ics_content = trim($ics_content); //FixIn: 2.1.9.2
110 $icalobj = new ZCiCal( $ics_content ); // create the ical object
111
112
113 $events_count = 0;
114 $ics_events = array();
115
116
117 if ( isset( $icalobj->tree->child ) ) {
118 foreach ( $icalobj->tree->child as $node ) {
119
120 if ( $node->getName() == "VEVENT" ) {
121
122 $ics_events[ $events_count ] = array();
123
124 foreach ( $node->data as $key => $value ) {
125
126 $ics_events[ $events_count ][ $key ] = $value->getValues();
127 }
128 $events_count++;
129 }
130 }
131 }
132
133
134 /** Data, like: array ( [PRODID] => -//Calendar Labs//Calendar 1.0//EN
135 [VERSION] => 2.0
136 [CALSCALE] => GREGORIAN
137 [METHOD] => PUBLISH
138 [X-WR-CALNAME] => Us Holidays
139 [X-WR-TIMEZONE] => America/New_York
140 */
141 $ics_data = array();
142 if ( isset( $icalobj->tree->data ) ) {
143 foreach ( $icalobj->tree->data as $node ) {
144 $ics_data[ $node->getName() ] = $node->getValues();
145 }
146 }
147
148
149 // Add Booking fields, like "_BOOKING_DATES, _BOOKING_SUMMARY, ..."
150 foreach ( $ics_events as $i_key => $i_event) {
151
152 //FixIn: 2.0.23.1
153 /**
154 * 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.
155 * 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.
156 *
157 * It's in section 3.6.1 of RFC 5545 (https://www.rfc-editor.org/rfc/rfc5545#page-54):
158 *
159 * For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE value type but no "DTEND" nor "DURATION" property,
160 * 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
161 * type but no "DTEND" property, the event ends on the same calendar date and time of day specified by the "DTSTART" property.
162 */
163 if ( ( empty( $ics_events[ $i_key ]['DTEND'] ) )
164 && ( ! empty( $ics_events[ $i_key ]['DTSTART'] ) ) ) {
165
166 $ics_events[ $i_key ]['DTEND'] = 'null';
167 }
168
169
170 $dates_arr = wpbm_get_dates_arr_from_ics_dates( $ics_events[ $i_key ][ 'DTSTART' ]
171 , $ics_events[ $i_key ][ 'DTEND' ]
172 , ( empty( $ics_events[ $i_key ]['RRULE'] ) ? '' : $ics_events[ $i_key ]['RRULE'] )
173 , ( empty( $ics_events[ $i_key ]['RDATE'] ) ? '' : $ics_events[ $i_key ]['RDATE'] )
174 );
175
176
177 // Bookings Fields
178 $ics_events[ $i_key ]['_BOOKING_DATES'] = $dates_arr;
179
180 $ics_events[ $i_key ]['_BOOKING_SUMMARY'] = '';
181 $ics_events[ $i_key ]['_BOOKING_DESCRIPTION'] = '';
182 $ics_events[ $i_key ]['_BOOKING_LOCATION'] = '';
183 $ics_events[ $i_key ]['_BOOKING_UID'] = '';
184 $ics_events[ $i_key ]['_BOOKING_MODIFIED'] = '';
185 $ics_events[ $i_key ]['_BOOKING_ATTENDEE'] = '';
186
187 if ( ! empty( $ics_events[ $i_key ]['SUMMARY'] ) ) {
188 $ics_events[ $i_key ]['_BOOKING_SUMMARY'] = $ics_events[ $i_key ]['SUMMARY'];
189 }
190 if ( ! empty( $ics_events[ $i_key ]['DESCRIPTION'] ) ) {
191 $ics_events[ $i_key ]['_BOOKING_DESCRIPTION'] = $ics_events[ $i_key ]['DESCRIPTION'];
192 }
193 if ( ! empty( $ics_events[ $i_key ]['LOCATION'] ) ) {
194 $ics_events[ $i_key ]['_BOOKING_LOCATION'] = $ics_events[ $i_key ]['LOCATION'];
195 }
196
197 if ( ! empty( $ics_events[ $i_key ]['ATTENDEE'] ) ) {
198 $ics_events[ $i_key ]['_BOOKING_ATTENDEE'] = $ics_events[ $i_key ]['ATTENDEE'];
199 }
200 // 15ig8t0i739kajgjc8ekc386dt@google.com - ID of event from ICS
201 // 15ig8t0i739kajgjc8ekc386dt_20170815 - ID of event during import from Google Calendar (probably created by ID before @ + first date)
202 if ( ! empty( $ics_events[ $i_key ]['UID'] ) ) {
203 $ics_events[ $i_key ]['_BOOKING_UID'] = $ics_events[ $i_key ]['UID'];
204 }
205 if ( ! empty( $ics_events[ $i_key ]['LAST-MODIFIED'] ) ) {
206 $ics_events[ $i_key ]['_BOOKING_MODIFIED'] = date_i18n( 'Y-m-d H:i:s', ZDateHelper::fromiCaltoUnixDateTime( $ics_events[ $i_key ]['LAST-MODIFIED'] ) );
207 }
208
209 }
210
211 return array( 'data' => $ics_data, 'events' => $ics_events );
212 }
213
214
215 /** Get Dates array from START & END times & RULES
216 * Examples: 2017-07-10 00:00:00 -- 2017-07-13 00:00:00 on FREQ=MONTHLY;COUNT=4;BYDAY=2MO
217 * OR 2017-06-14 12:00:00 -- 2017-06-14 15:30:00 on FREQ=WEEKLY;COUNT=3;BYDAY=WE,SA
218 *
219 * @param string $start_date_ics // 20170614T120000
220 * @param string $end_date_ics // 20170614T153000
221 * @param string $ics_rules // FREQ=WEEKLY;COUNT=3;BYDAY=WE,SA
222 * @param string $ics_rdates // 20170826T120001,20170830T120001
223 * @return array // Array (
224 [0] => 2017-06-14 12:00:01
225 [1] => 2017-06-14 15:30:02
226 [2] => 2017-06-17 12:00:01
227 [3] => 2017-06-17 15:30:02
228 [4] => 2017-06-21 12:00:01
229 [5] => 2017-06-21 15:30:02
230 )
231 */
232 function wpbm_get_dates_arr_from_ics_dates( $start_date_ics, $end_date_ics, $ics_rules , $ics_rdates ) {
233
234 //FixIn: 2.0.23.1
235 /**
236 * 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.
237 * 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.
238 *
239 * It's in section 3.6.1 of RFC 5545 (https://www.rfc-editor.org/rfc/rfc5545#page-54):
240 *
241 * For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE value type but no "DTEND" nor "DURATION" property,
242 * 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
243 * type but no "DTEND" property, the event ends on the same calendar date and time of day specified by the "DTSTART" property.
244 */
245 if ('null' === $end_date_ics) {
246 $end_date_ics = wpbm_ics_date_reset_seconds( $start_date_ics ); // 20170614T120000
247
248 $start_date_unix_calc = ZDateHelper::fromiCaltoUnixDateTime(
249 wpbm_ics_date_reset_seconds( $start_date_ics )
250 ); // 1499644800 - 20170710
251
252 $end_date_ics = strtotime( '+1 minute', $start_date_unix_calc );
253 $end_date_ics = ZDateHelper::fromUnixDateTimetoiCal( $end_date_ics );
254 }
255
256
257 $start_date_ics = wpbm_ics_date_reset_seconds( $start_date_ics ); // 20170614T120000
258 $end_date_ics = wpbm_ics_date_reset_seconds( $end_date_ics ); // 20170614T120000
259
260 // FREQ=MONTHLY;COUNT=4;BYDAY=2MO - Rules
261 $start_date_unix = ZDateHelper::fromiCaltoUnixDateTime( $start_date_ics ); // 1499644800 - 20170710
262 $end_date_unix = ZDateHelper::fromiCaltoUnixDateTime( $end_date_ics ); // 1499904000 - 20170713
263
264 $time_difference = $end_date_unix - $start_date_unix; // 259200 - Difference in seconds
265
266 $start_date = date_i18n( 'Y-m-d H:i:s', $start_date_unix ); // 2017-07-10 00:00:00
267 $end_date = date_i18n( 'Y-m-d H:i:s', $end_date_unix ); // 2017-07-13 00:00:00
268
269 // Get Unix Time for Strat Date at midnight -- 2017-07-10
270 $start_date_midnight_unix = strtotime( date_i18n( 'Y-m-d 00:00:00', $start_date_unix ) );
271 $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
272
273 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
274
275 $internal_dates =array();
276
277 // Get Unix Time for Strat Date at midnight -- 2017-07-10
278 $end_date_midnight_unix = strtotime( date_i18n( 'Y-m-d 00:00:00', $end_date_unix ) );
279 $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
280
281 // Get internal days between START and END dates: // array( [0] => 1499731200 [1] => 1499817600 )
282 // Need to check relative to the END MIDNIGHT, becase if
283 // 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
284 while( ( $start_date_midnight_unix + ( 60*60*24 ) ) < $end_date_midnight_unix ) {
285 $start_date_midnight_unix += 60*60*24; // Get next day
286 $internal_dates[] = $start_date_midnight_unix;
287 }
288
289 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
290 // Get Initial Dates interval
291 $initial_time_interval_unix = array();
292
293 // 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
294 if ( substr( $start_date, 11 ) != '00:00:00' ) $initial_time_interval_unix[] = $start_date_unix + 1; // Start time for specific time slot
295 else $initial_time_interval_unix[] = $start_date_unix ; // Full date
296
297 foreach ( $internal_dates as $internal_date_unix ) {
298 $initial_time_interval_unix[] = $internal_date_unix;
299 }
300
301 // Same for end date/time +2 seconds for having time like 10:00:02
302 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
303 // We do not need to add END date, if the TIME == '00:00:00', because EVENT ended at previous date
304
305 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
306
307 // Here we are getting SHIFT in seonds starting from INITIAL start DATE_TIME
308 $initial_time_interval_shift_unix = array();
309 foreach ( $initial_time_interval_unix as $internal_date_unix ) {
310 $initial_time_interval_shift_unix[] = $internal_date_unix - $start_date_unix;
311 }
312
313 // $initial_time_interval_shift_unix >>> FULL DAYS: array( [0] => 0 [1] => 86400 [2] => 172800 )
314 // $initial_time_interval_shift_unix >>> TIMES: array( [0] => 1 [1] => 12602 )
315
316
317 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
318
319 // Get DATES depsnding from RULES imported from ICS
320 $ics_start_dates_unix = array();
321 if ( ! empty( $ics_rules ) ) {
322
323 // Parse RULE and Get Dates array
324 $rDates = new ZCRecurringDate(
325 $ics_rules
326 , strtotime( $start_date_ics )
327 );
328 $ics_start_dates_unix = $rDates->getDates(); // MAX DATE: strtotime( $ics_events[ $i_key ][ 'DTEND' ] ) );
329 sort( $ics_start_dates_unix ); // Sort
330 $ics_start_dates_unix = array_unique( $ics_start_dates_unix ); // Remove Duplicates
331 }
332 // Example: Array ( [0] => 1499644800 [1] => 1502668800 [2] => 1505088000 [3] => 1507507200 ) or array()
333
334 // If we are having rDates
335 if ( ! empty( $ics_rdates ) ) {
336 $ics_rdates = explode( ',', $ics_rdates );
337
338 foreach ( $ics_rdates as $ics_rdates_key => $ics_rdates_value ) {
339
340 $ics_rdates_value = wpbm_ics_date_reset_seconds( $ics_rdates_value ); // 20170614T120000
341 $ics_rdates_value = ZDateHelper::fromiCaltoUnixDateTime( $ics_rdates_value ); // // 1497441600
342 $ics_rdates[ $ics_rdates_key ] = $ics_rdates_value;
343 }
344 // We need to add also to these array start day
345 $ics_rdates[] = strtotime( $start_date_ics );
346
347 sort( $ics_rdates ); // Sort
348
349 $ics_start_dates_unix = array_merge( $ics_start_dates_unix, $ics_rdates );
350 $ics_start_dates_unix = array_unique( $ics_start_dates_unix ); // Remove Duplicates
351 }
352
353 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
354
355 $dates_arr_unix = array();
356
357 // We are having some RULES
358 if ( ! empty( $ics_start_dates_unix ) ) {
359 foreach ( $ics_start_dates_unix as $ics_start_date_unix ) { // Depend from rules, we are having starting interval of our EVENT
360
361 foreach ( $initial_time_interval_shift_unix as $shift_unix ) { // Because EVENT can have several dates or times, we add our SHIFT interval from INITIAL
362
363 $dates_arr_unix[] = $ics_start_date_unix + $shift_unix;
364 }
365 }
366 } else { // No Rules
367
368 foreach ( $initial_time_interval_unix as $initial_time_unix ) { // So we are getting only INITIAL interval
369 $dates_arr_unix[] = $initial_time_unix;
370 }
371 }
372 //ex: Array ( [0] => 1497441601 [1] => 1497454202 [2] => 1497700801 [3] => 1497713402 [4] => 1498046401 [5] => 1498059002 )
373
374 $dates_arr_mysql = array();
375 foreach ( $dates_arr_unix as $date_unix ) {
376 $dates_arr_mysql[] = date_i18n( 'Y-m-d H:i:s', $date_unix );
377 }
378 //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 )
379
380 return $dates_arr_mysql;
381 }
382
383
384 /** Reset any seconds in ics date and return it,
385 * Its useful, if such info was exported by Booking Calendar and now is importing again.
386 *
387 * @param string $ics_date_time - 20170614T120001
388 * @return string - 20170614T120000
389 */
390 function wpbm_ics_date_reset_seconds( $ics_date_time ) {
391
392 // 1497441601
393 $date_time_unix = ZDateHelper::fromiCaltoUnixDateTime( $ics_date_time );
394
395 // 2017-07-10 00:00:01
396 $date_time_sql = date_i18n( 'Y-m-d H:i:s', $date_time_unix );
397
398 if( strlen( $date_time_sql ) > 10 ) {
399 $date_time_sql = substr($date_time_sql, 0, -2 ) . '00'; // Reset seconds to 00, if previously we was have 01 or 02
400 }
401 // 2017-07-10 00:00:00
402
403 $date_time_unix = strtotime( $date_time_sql );
404
405 $ics_date_time = ZDateHelper::fromUnixDateTimetoiCal( $date_time_unix );
406
407 return $ics_date_time;
408 }