Project

General

Profile

eo-recurrences.patch

Boone Gorges, 2016-03-07 02:11 PM

View differences:

includes/event-organiser-archives.php
79 79
				$query->set('ondate',false);
80 80
			}
81 81
		}
82
	
82

  
83 83
		$query->set( 'post_type', 'event' );
84 84
		$query->set( 'event_start_before', $ondate_end );
85 85
		$query->set( 'event_end_after', $ondate_start );
......
93 93
	//@see https://github.com/stephenharris/Event-Organiser/issues/30
94 94
	if( $query->is_main_query() ){
95 95
		if( eo_is_event_archive( 'day' ) || eo_is_event_archive( 'month' ) || eo_is_event_archive( 'year' ) ){
96
			$query->set( 'showpastevents', true );  
96
			$query->set( 'showpastevents', true );
97 97
		}
98
	}   
99
		
98
	}
99

  
100 100
	$blog_now = new DateTime(null, eo_get_blog_timezone());
101 101

  
102 102
	//Determine whether or not to show past events and each occurrence. //If not set, use options
......
277 277
 */
278 278
function eventorganiser_event_fields( $select, $query ){
279 279
	global $wpdb;
280
	
280

  
281 281
	$q_fields = $query->get( 'fields' );
282
	
282

  
283 283
	if( eventorganiser_is_event_query( $query, true ) && 'ids' != $q_fields && 'id=>parent' != $q_fields ){
284 284
		$et =$wpdb->eo_events;
285 285
		$select .= ", {$et}.event_id, {$et}.event_id AS occurrence_id, {$et}.StartDate, {$et}.StartTime, {$et}.EndDate, {$et}.FinishTime, {$et}.event_occurrence ";
......
336 336

  
337 337
	if( eventorganiser_is_event_query( $query, true ) ){
338 338
		if( 'series'== $query->get('group_events_by') ) {
339
			$join .= " LEFT JOIN 
340
						( SELECT * FROM {$wpdb->eo_events} ORDER BY {$wpdb->eo_events}.StartDate ASC, {$wpdb->eo_events}.StartTime ASC ) 
339
			$join .= " LEFT JOIN
340
						( SELECT * FROM {$wpdb->eo_events} ORDER BY {$wpdb->eo_events}.StartDate ASC, {$wpdb->eo_events}.StartTime ASC )
341 341
						AS {$wpdb->eo_events} ON $wpdb->posts.id = {$wpdb->eo_events}.post_id ";
342 342

  
343 343
		}else{
......
355 355
 * @return bool True if the query is an event query. False otherwise.
356 356
 */
357 357
function eventorganiser_is_event_query( $query, $exclusive = false ){
358
		
358

  
359 359
	$post_types = $query->get( 'post_type' );
360 360

  
361 361
	if( 'any' == $post_types ){
362 362
		$post_types = get_post_types( array('exclude_from_search' => false) );
363
	}	
364
	
363
	}
364

  
365 365
	if( 'event' == $post_types || array( 'event' ) == $post_types ){
366 366
		$bool = true;
367
	
367

  
368 368
	}elseif( ( $query && $query->is_feed( 'eo-events' ) ) || is_feed( 'eo-events' ) ){
369 369
		$bool = true;
370
		
370

  
371 371
	}elseif( empty( $post_types ) && eo_is_event_taxonomy( $query ) ){
372
		
372

  
373 373
		//Querying by taxonomy - check if 'event' is the only post type
374 374
		$post_types = array();
375 375
		$taxonomies = wp_list_pluck( $query->tax_query->queries, 'taxonomy' );
376
		
376

  
377 377
		foreach ( get_post_types() as $pt ) {
378
			
378

  
379 379
			$object_taxonomies = ( $pt === 'attachment' ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt ) );
380
			
380

  
381 381
			if ( array_intersect( $taxonomies, $object_taxonomies ) ) {
382 382
				$post_types[] = $pt;
383 383
			}
......
397 397
		}
398 398
	}elseif( $exclusive ){
399 399
		$bool = false;
400
		
400

  
401 401
	}elseif( ( is_array( $post_types ) && in_array( 'event', $post_types ) ) ){
402
		
402

  
403 403
		$bool = true;
404
		
404

  
405 405
	}else{
406 406
		$bool = false;
407
		
407

  
408 408
	}
409 409

  
410 410
	/**
411 411
	 * Filters whether the query is an event query.
412
	 * 
413
	 * This should be `true` if the query is for events, `false` otherwise. The 
414
	 * third parameter, `$exclusive` qualifies if this means 'query exclusively 
415
	 * for events' or not. If `true` then this filter should return `true` only 
412
	 *
413
	 * This should be `true` if the query is for events, `false` otherwise. The
414
	 * third parameter, `$exclusive` qualifies if this means 'query exclusively
415
	 * for events' or not. If `true` then this filter should return `true` only
416 416
	 * if the query is exclusively for events.
417
	 * 
417
	 *
418 418
	 * @param bool     $bool      Whether the query is an event query.
419 419
	 * @param WP_Query $query     The WP_Query instance to check.
420
	 * @param bool     $exclusive Whether the check if for queries exclusively for events. 
420
	 * @param bool     $exclusive Whether the check if for queries exclusively for events.
421 421
	 */
422 422
	return apply_filters( 'eventorganiser_is_event_query', $bool, $query, $exclusive );
423 423
}
......
452 452
			$occurrence_id =$query->query_vars['event_occurrence_id'];
453 453
			$where .= $wpdb->prepare(" AND {$wpdb->eo_events}.event_id=%d ",$occurrence_id);
454 454
		endif;
455
		
455

  
456 456
		//https://core.trac.wordpress.org/ticket/16471
457 457
		if( isset( $query->query_vars['event_occurrence__not_in'] ) ):
458 458
			$occurrence__not_in = implode(', ', array_map( 'intval', $query->query_vars['event_occurrence__not_in'] ) );
459 459
			$where .= " AND {$wpdb->eo_events}.event_id NOT IN({$occurrence__not_in}) ";
460 460
		endif;
461
		
461

  
462 462
		//https://core.trac.wordpress.org/ticket/16471
463 463
		if( isset( $query->query_vars['event_occurrence__in'] ) ):
464 464
			$occurrence__in = implode(', ', array_map( 'intval', $query->query_vars['event_occurrence__in'] ) );
465 465
			$where .= " AND {$wpdb->eo_events}.event_id IN({$occurrence__in}) ";
466 466
		endif;
467 467

  
468
		//Check date ranges were are interested in. 
468
		//Check date ranges were are interested in.
469 469
		$date_queries = array(
470 470
			'event_start_after'=>array(
471 471
				'notstrict' =>" AND {$wpdb->eo_events}.StartDate >= %s ",
......
493 493
				$time = eo_format_date($datetime,'H:i:s');
494 494
				if( $time == '00:00:00' ){
495 495
					$sql = $_sql['notstrict'];
496
					$where .= $wpdb->prepare($sql, $date);				
496
					$where .= $wpdb->prepare($sql, $date);
497 497
				}else{
498 498
					$sql = $_sql['strict'];
499
					$where .= $wpdb->prepare($sql, $date, $date, $time);				
499
					$where .= $wpdb->prepare($sql, $date, $date, $time);
500 500
				}
501 501
			}
502 502
		}
......
563 563

  
564 564

  
565 565
function _eventorganiser_update_event_dates_cache( $events, $query ){
566
	
566

  
567 567
	// No point in doing all this work if we didn't match any posts.
568 568
	if ( ! $events ) {
569 569
		return $events;
......
573 573
	if ( version_compare( PHP_VERSION, '5.3.0' ) < 0 ) {
574 574
		return $events;
575 575
	}
576
	
576

  
577 577
	//TODO do we need to check if $events is an array of WP_Post objects?
578 578
	//TODO allow this to be skipped?
579 579

  
......
582 582
	}
583 583

  
584 584
	$tz = eo_get_blog_timezone();
585
	
585

  
586 586
	foreach ( $events as $event ) {
587
		
587

  
588 588
		$id = $event->ID;
589 589
		$cached_event = wp_cache_get( 'eventorganiser_occurrences_'.$id );
590
		
590

  
591 591
		if ( isset( $cached_event[$event->occurrence_id] ) ) {
592 592
			continue;
593 593
		}
594
		
594

  
595 595
		$cached_event[$event->occurrence_id] = 	array(
596 596
			'start' => new DateTime($event->StartDate.' '.$event->StartTime, $tz ),
597 597
			'end'   => new DateTime($event->EndDate.' '.$event->FinishTime, $tz ),
598 598
		);
599
		
599

  
600 600
		wp_cache_set( 'eventorganiser_occurrences_'.$id, $cached_event );
601
		
601

  
602 602
	}
603
	
603

  
604 604
	return $events;
605 605
}
606 606

  
includes/event.php
5 5
 */
6 6
/**
7 7
* This functions updates a post of event type, with data given in the $post_data
8
* and event data given in $event_data. Returns the post_id. 
8
* and event data given in $event_data. Returns the post_id.
9 9
*
10 10
* Triggers {@see `eventorganiser_save_event`} passing event (post) ID
11 11
*
......
17 17
*      * (string) BYMONTHDAY=XX to repeat on XXth day of month, e.g. BYMONTHDAY=01 to repeat on the first of every month.
18 18
*      * (string) BYDAY=ND. N= 1|2|3|4|-1 (first, second, third, fourth, last). D is day of week SU|MO|TU|WE|TH|FR|SA. E.g. BYDAY=2TU (repeat on second tuesday)
19 19
*   * For weekly schedules,
20
*      * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays. 
20
*      * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays.
21 21
*      * Can be left blank to repeat weekly from the start date.
22 22
* * `frequency` => (int) positive integer, sets frequency of recurrence (every 2 days, or every 3 days etc)
23 23
* * `all_day` => 1 if its an all day event, 0 if not
......
25 25
* * `end` => end date (of first occurrence)  as a datetime object
26 26
* * `until` =>  **START** date of last occurrence (or upper-bound thereof) as a datetime object
27 27
* * `schedule_last` =>  Alias of until. Deprecated 2.13.0, use until.
28
* * `number_occurrences` => Instead of specifying `until` you can specify the number of occurrence a recurring event should have. 
28
* * `number_occurrences` => Instead of specifying `until` you can specify the number of occurrence a recurring event should have.
29 29
* This is only used if `until` is not, and for daily, weekly, monthly or yearly recurring events.
30 30
* * `include` => array of datetime objects to include in the schedule
31 31
* * `exclude` => array of datetime objects to exclude in the schedule
......
41 41
function eo_update_event( $post_id, $event_data = array(), $post_data = array() ){
42 42

  
43 43
	$post_id = (int) $post_id;
44
	
44

  
45 45
	$input = array_merge( $post_data, $event_data );
46
	
46

  
47 47
	//Backwards compat:
48 48
	if( !empty( $input['venue'] ) ){
49 49
		$input['tax_input']['event-venue'] = $input['venue'];
......
51 51
	if( !empty( $input['category'] ) ){
52 52
		$input['tax_input']['event-category'] = $input['category'];
53 53
	}
54
	
55
	$event_keys = array_flip( array( 
56
		'start', 'end', 'schedule', 'schedule_meta', 'frequency', 
54

  
55
	$event_keys = array_flip( array(
56
		'start', 'end', 'schedule', 'schedule_meta', 'frequency',
57 57
		'all_day', 'until', 'schedule_last', 'include', 'exclude', 'occurs_by', 'number_occurrences',
58 58
	) );
59
	
59

  
60 60
	$post_keys = array_flip( array(
61
		'post_title','post_content','post_status', 'post_type','post_author','ping_status','post_parent','menu_order', 
61
		'post_title','post_content','post_status', 'post_type','post_author','ping_status','post_parent','menu_order',
62 62
		'to_ping', 'pinged', 'post_password', 'guid', 'post_content_filtered', 'post_excerpt', 'import_id', 'tax_input',
63 63
		'comment_status', 'context', 'post_date', 'post_date_gmt',
64 64
	) );
65
	
65

  
66 66
	$event_data = array_intersect_key( $input, $event_keys );
67 67
	$post_data = array_intersect_key( $input, $post_keys ) + $post_data;
68
	 
68

  
69 69
	if( empty( $post_id ) ){
70 70
		return new WP_Error( 'eo_error', 'Empty post ID.' );
71 71
	}
72
		
72

  
73 73
	/**
74 74
	 *@ignore
75 75
	 */
......
80 80
	$post_data = apply_filters( 'eventorganiser_update_event_post_data', $post_data, $post_id, $post_data, $event_data );
81 81

  
82 82
	if( !empty($post_data) ){
83
		$post_data['ID'] = $post_id;		
83
		$post_data['ID'] = $post_id;
84 84
		wp_update_post( $post_data );
85 85
	}
86 86

  
......
94 94
		}
95 95
		unset( $event_data['schedule_last'] );
96 96
	}
97
	
97

  
98 98
	//Get previous data, parse with data to be updated
99 99
	$prev = eo_get_event_schedule( $post_id );
100 100
	$event_data = wp_parse_args( $event_data, $prev );
......
103 103
	if( ( empty($event_data['schedule']) || 'once' == $event_data['schedule'] ) && !empty($event_data['include']) ){
104 104
		$event_data['schedule'] = 'custom';
105 105
	}
106
	
106

  
107 107
	$event_data = _eventorganiser_generate_occurrences( $event_data );
108 108

  
109 109
	if( is_wp_error( $event_data ) ){
......
145 145
*      * (string) BYMONTHDAY=XX to repeat on XXth day of month, e.g. BYMONTHDAY=01 to repeat on the first of every month.
146 146
*      * (string) BYDAY=ND. N= 1|2|3|4|-1 (first, second, third, fourth, last). D is day of week SU|MO|TU|WE|TH|FR|SA. E.g. BYDAY=2TU (repeat on second tuesday)
147 147
*   * For weekly schedules,
148
*      * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays. 
148
*      * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays.
149 149
*      * Can be left blank to repeat weekly from the start date.
150 150
* * `frequency` => (int) positive integer, sets frequency of recurrence (every 2 days, or every 3 days etc)
151 151
* * `all_day` => 1 if its an all day event, 0 if not
......
153 153
* * `end` => end date (of first occurrence)  as a datetime object
154 154
* * `until` =>  **START** date of last occurrence (or upper-bound thereof) as a datetime object
155 155
* * `schedule_last` =>  Alias of until. Deprecated 2.13.0, use until.
156
* * `number_occurrences` => Instead of specifying `until` you can specify the number of occurrence a recurring event should have. 
156
* * `number_occurrences` => Instead of specifying `until` you can specify the number of occurrence a recurring event should have.
157 157
* This is only used if `until` is not, and for daily, weekly, monthly or yearly recurring events.
158 158
* * `include` => array of datetime objects to include in the schedule
159 159
* * `exclude` => array of datetime objects to exclude in the schedule
......
176 176
*
177 177
*    $e = eo_insert_event($post_data,$event_data);
178 178
* </code>
179
* 
179
*
180 180
* ### Tutorial
181 181
* See this <a href="http://www.stephenharris.info/2012/front-end-event-posting/">tutorial</a> or <a href="https://gist.github.com/3867194">this Gist</a> on front-end event posting.
182 182
*
183 183
* @since 1.5
184 184
* @link http://www.stephenharris.info/2012/front-end-event-posting/ Tutorial on front-end event posting
185
* @uses wp_insert_post() 
185
* @uses wp_insert_post()
186 186
*
187 187
* @param array $post_data array of data to be used by wp_insert_post.
188 188
* @param array $event_data array of event data
......
192 192
	global $wpdb;
193 193

  
194 194
	$input = array_merge( $post_data, $event_data );
195
	
195

  
196 196
	//Backwards compat:
197 197
	if( !empty( $input['venue'] ) ){
198 198
		$input['tax_input']['event-venue'] = $input['venue'];
......
200 200
	if( !empty( $input['category'] ) ){
201 201
		$input['tax_input']['event-category'] = $input['category'];
202 202
	}
203
	
203

  
204 204
	$event_keys = array_flip( array(
205
		'start', 'end', 'schedule', 'schedule_meta', 'frequency', 'all_day', 
206
		'until', 'schedule_last', 'include', 'exclude', 'occurs_by', 'number_occurrences', 
205
		'start', 'end', 'schedule', 'schedule_meta', 'frequency', 'all_day',
206
		'until', 'schedule_last', 'include', 'exclude', 'occurs_by', 'number_occurrences',
207 207
	) );
208
	
208

  
209 209
	$post_keys = array_flip( array(
210
		'post_title','post_content','post_status', 'post_type','post_author','ping_status','post_parent','menu_order', 
210
		'post_title','post_content','post_status', 'post_type','post_author','ping_status','post_parent','menu_order',
211 211
		'to_ping', 'pinged', 'post_password', 'guid', 'post_content_filtered', 'post_excerpt', 'import_id', 'tax_input',
212 212
		'comment_status', 'context',  'post_date', 'post_date_gmt',
213 213
	) );
214
	
214

  
215 215
	$event_data = array_intersect_key( $input, $event_keys ) + $event_data;
216 216
	$post_data = array_intersect_key( $input, $post_keys );
217
		
217

  
218 218
	//If schedule is 'once' and dates are included - set to 'custom':
219 219
	if( ( empty($event_data['schedule']) || 'once' == $event_data['schedule'] ) && !empty($event_data['include']) ){
220 220
		$event_data['schedule'] = 'custom';
221 221
	}
222
	
222

  
223 223
	if( !empty( $event_data['schedule_last'] ) ){
224 224
		if( !isset( $event_data['until'] ) ){
225 225
			$event_data['until'] = clone $event_data['schedule_last'];
......
228 228
	}
229 229

  
230 230
	$event_data = _eventorganiser_generate_occurrences( $event_data );
231
		
231

  
232 232
	if( is_wp_error( $event_data ) ){
233 233
		return $event_data;
234 234
	}
......
237 237
	 *@ignore
238 238
	 */
239 239
	$event_data = apply_filters( 'eventorganiser_insert_event_event_data', $event_data, $post_data, $event_data );
240
	
240

  
241 241
	/**
242 242
	 *@ignore
243 243
	 */
244 244
	$post_data = apply_filters( 'eventorganiser_insert_event_post_data', $post_data, $post_data, $event_data );
245
		
245

  
246 246
	//Finally we create event (first create the post in WP)
247
	$post_input = array_merge(array('post_title'=>'untitled event'), $post_data, array('post_type'=>'event'));			
247
	$post_input = array_merge(array('post_title'=>'untitled event'), $post_data, array('post_type'=>'event'));
248 248
	$post_id = wp_insert_post($post_input, true);
249 249

  
250
	//Did the event insert correctly? 
251
	if ( is_wp_error( $post_id) ) 
250
	//Did the event insert correctly?
251
	if ( is_wp_error( $post_id) )
252 252
			return $post_id;
253 253

  
254 254
	_eventorganiser_insert_occurrences($post_id, $event_data);
255
			
255

  
256 256
	//Action used to break cache & trigger Pro actions (& by other plug-ins?)
257 257
	/**
258 258
	 * Triggered after an event has been updated.
259
	 * 
260
	 * @param int $post_id The ID of the event 
259
	 *
260
	 * @param int $post_id The ID of the event
261 261
	 */
262 262
	do_action( 'eventorganiser_save_event', $post_id );
263 263

  
......
287 287
 *
288 288
 * This function does not update any of the event schedule details.
289 289
 * **Don't call this unless you know what you're doing**.
290
 * 
290
 *
291 291
 * @since 1.5
292 292
 * @access private
293 293
 * @param int $post_id the event's (post) ID to be deleted
294 294
 * @param int|array $occurrence_ids Occurrence ID (or array of IDs) for specificaly occurrences to delete. If empty/false, deletes all.
295
 * 
295
 *
296 296
 */
297 297
function eo_delete_event_occurrences( $event_id, $occurrence_ids = false ){
298 298
	global $wpdb;
299 299
	//TODO use this in break/remove occurrence
300
	
300

  
301 301
	//Let's just ensure empty is cast as false
302 302
	$occurrence_ids = ( empty( $occurrence_ids ) ? false : $occurrence_ids );
303
	
303

  
304 304
	if( $occurrence_ids !== false ){
305 305
		$occurrence_ids = (array) $occurrence_ids;
306 306
		$occurrence_ids = array_map( 'absint', $occurrence_ids );
307 307
		$occurrence_ids_in = implode( ', ', $occurrence_ids );
308
		
308

  
309 309
		$raw_sql = "DELETE FROM $wpdb->eo_events WHERE post_id=%d AND event_id IN( $occurrence_ids_in )";
310 310

  
311 311
	}else{
312 312
		$raw_sql = "DELETE FROM $wpdb->eo_events WHERE post_id=%d";
313 313
	}
314
	
314

  
315 315
	/**
316 316
	 * @ignore
317 317
	 */
318 318
	do_action( 'eventorganiser_delete_event', $event_id, $occurrence_ids ); //Deprecated - do not use!
319
	
319

  
320 320
	/**
321 321
	 * Triggers just before the specified occurrences for the event are deleted.
322
	 * 
322
	 *
323 323
	 * @param int $event_id The (post) ID of the event of which we're deleting occurrences.
324 324
	 * @param array|false $occurrence_ids An array of occurrences to be delete. If `false`, all occurrences are to be removed.
325 325
	 */
326 326
	do_action( 'eventorganiser_delete_event_occurrences', $event_id, $occurrence_ids );
327
	
327

  
328 328
	$del = $wpdb->get_results( $wpdb->prepare(  $raw_sql, $event_id ) );
329
	
329

  
330 330
}
331 331
add_action( 'delete_post', 'eo_delete_event_occurrences', 10 );
332 332

  
......
340 340
* @return int $post_id
341 341
*/
342 342
function _eventorganiser_insert_occurrences( $post_id, $event_data ){
343
	
343

  
344 344
	global $wpdb;
345 345
	extract( $event_data );
346 346
	$tz = eo_get_blog_timezone();
......
349 349
	//Also see https://github.com/stephenharris/Event-Organiser/issues/205
350 350
	//And https://github.com/stephenharris/Event-Organiser/issues/224
351 351
	$duration_str = eo_date_interval( $start, $end, '+%y year +%m month +%d days +%h hours +%i minutes +%s seconds' );
352
	
352

  
353 353
	$event_data['duration_str'] = $duration_str;
354 354

  
355 355
	$schedule_last_end = clone $schedule_last;
......
358 358
	//Get dates to be deleted / added
359 359
	$current_occurrences = eo_get_the_occurrences( $post_id );
360 360
	$current_occurrences = $current_occurrences ? $current_occurrences : array();
361
	
361

  
362 362
	$delete   = array_udiff( $current_occurrences, $occurrences, '_eventorganiser_compare_dates' );
363 363
	$insert   = array_udiff( $occurrences, $current_occurrences, '_eventorganiser_compare_dates' );
364 364
	$update   = array_uintersect( $occurrences, $current_occurrences, '_eventorganiser_compare_dates' );
365 365
	$update_2 = array_uintersect( $current_occurrences, $update, '_eventorganiser_compare_dates' );
366 366
	$keys     = array_keys( $update_2 );
367
	
367

  
368 368
	if( $delete ){
369 369
		$delete_occurrence_ids = array_keys( $delete );
370 370
		eo_delete_event_occurrences( $post_id, $delete_occurrence_ids );
371 371
	}
372
	
372

  
373 373
	$occurrence_cache = array();
374 374
	$occurrence_array = array();
375
	
375

  
376 376
	if( $update ){
377 377
		$update   = array_combine( $keys, $update );
378
		
378

  
379 379
		foreach( $update as $occurrence_id => $occurrence ){
380 380

  
381 381
			$occurrence_end = clone $occurrence;
382 382
			$occurrence_end->modify($duration_str);
383
			
383

  
384 384
			$occurrence_input = array(
385 385
				'StartDate'        => $occurrence->format('Y-m-d'),
386 386
				'StartTime'        => $occurrence->format('H:i:s'),
......
389 389
			);
390 390

  
391 391
			$wpdb->update(
392
				$wpdb->eo_events, 
393
				$occurrence_input,				
392
				$wpdb->eo_events,
393
				$occurrence_input,
394 394
				array( 'event_id' => $occurrence_id )
395 395
			);
396 396

  
......
402 402
			);
403 403
		}
404 404
	}
405
	
405

  
406 406
	if( $insert ){
407 407
		foreach( $insert as $counter => $occurrence ):
408 408
			$occurrence_end = clone $occurrence;
......
418 418
			);
419 419

  
420 420
			$wpdb->insert( $wpdb->eo_events, $occurrence_input );
421
			
421

  
422 422
			$occurrence_array[$wpdb->insert_id] = $occurrence->format('Y-m-d H:i:s');
423 423

  
424 424
			//Add to occurrence cache: TODO use post meta
......
429 429

  
430 430
		endforeach;
431 431
	}
432
		
432

  
433 433
	//Set occurrence cache
434 434
	wp_cache_set( 'eventorganiser_occurrences_'.$post_id, $occurrence_cache );
435 435
	wp_cache_set( 'eventorganiser_all_occurrences_'.$post_id, $occurrence_cache );
436 436

  
437 437
	unset( $event_data['occurrences'] );
438 438
	//$event_data['_occurrences'] = $occurrence_array;
439
		
439

  
440 440
	if( !empty($include) ){
441 441
		$event_data['include'] = array_map('eo_format_datetime', $include, array_fill(0, count($include), 'Y-m-d H:i:s') );
442 442
	}
443
	
443

  
444 444
	if( !empty($exclude) ){
445 445
		$event_data['exclude'] = array_map('eo_format_datetime', $exclude, array_fill(0, count($exclude), 'Y-m-d H:i:s') );
446 446
	}
......
450 450
	unset( $event_data['schedule_start'] );
451 451
	unset( $event_data['schedule_last'] );
452 452
	unset( $event_data['until'] );
453
		
453

  
454 454
	update_post_meta( $post_id, '_eventorganiser_event_schedule', $event_data );
455 455
	update_post_meta( $post_id, '_eventorganiser_schedule_start_start', $start->format('Y-m-d H:i:s') );
456 456
	update_post_meta( $post_id, '_eventorganiser_schedule_start_finish', $end->format('Y-m-d H:i:s') );
457 457
	update_post_meta( $post_id, '_eventorganiser_schedule_until', $until->format('Y-m-d H:i:s') );
458 458
	update_post_meta( $post_id, '_eventorganiser_schedule_last_start', $schedule_last->format('Y-m-d H:i:s') );
459 459
	update_post_meta( $post_id, '_eventorganiser_schedule_last_finish', $schedule_last_end->format('Y-m-d H:i:s') );
460
		
460

  
461 461
	return $post_id;
462 462
}
463 463

  
......
474 474
*      * (string) BYMONTHDAY=XX to repeat on XXth day of month, e.g. BYMONTHDAY=01 to repeat on the first of every month.
475 475
*      * (string) BYDAY=ND. N= 1|2|3|4|-1 (first, second, third, fourth, last). D is day of week SU|MO|TU|WE|TH|FR|SA. E.g. BYDAY=2TU (repeat on second tuesday)
476 476
*   * For weekly schedules,
477
*      * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays. 
477
*      * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays.
478 478
* * `occurs_by` - For use with monthly schedules: how the event recurs: BYDAY or BYMONTHDAY
479 479
* * `frequency` => (int) positive integer, sets frequency of recurrence (every 2 days, or every 3 days etc)
480 480
* * `all_day` => 1 if its an all day event, 0 if not
481 481
* * `start` =>  start date (of first occurrence)  as a datetime object
482 482
* * `end` => end date (of first occurrence)  as a datetime object
483 483
* * `until` => For recurring events, the date they repeat until. Note that this may not be equal to `schedule_last` if
484
*              dates are included/excluded. 
484
*              dates are included/excluded.
485 485
* * `schedule_last` =>  **START** date of last occurrence as a datetime object
486 486
* * `include` => array of datetime objects to include in the schedule
487 487
* * `exclude` => array of datetime objects to exclude in the schedule
......
493 493

  
494 494
	$post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
495 495

  
496
	if( empty( $post_id ) ){ 
496
	if( empty( $post_id ) ){
497 497
		return false;
498 498
	}
499 499

  
......
519 519
	} else {
520 520
		// No start time, so set a default start time to next half-hour
521 521
		$now = new DateTime( 'now', $tz );
522
		
522

  
523 523
		$minute = $now->format( 'i' ) > 30 ? 0 : 30;
524
		
524

  
525 525
		$now->setTime( $now->format( 'G' ), $minute );
526
		
526

  
527 527
		if( 0 === $minute ){
528 528
			$now->modify( '+1 hour' );
529 529
		}
530
		
531
		$event_details['start'] = $now; 
530

  
531
		$event_details['start'] = $now;
532 532
	}
533 533

  
534 534
	// Get end time
......
551 551
		$event_details['until'] = clone $event_details['schedule_last'];
552 552
		update_post_meta( $post_id, '_eventorganiser_schedule_until', $event_details['until']->format( 'Y-m-d H:i:s' ) );
553 553
	}
554
	
554

  
555 555
	if ( ! empty( $event_details['include'] ) ) {
556 556
		$event_details['include'] = array_map( 'eventorganiser_date_create', $event_details['include'] );
557 557
	}
......
570 570

  
571 571
	/**
572 572
	 * Filters the schedule metadata for an event (as returned by `eo_get_event_schedule()`.
573
	 * 
573
	 *
574 574
	 * See documentation on `eo_get_event_schedule()` for more details.
575 575
	 *
576 576
	 * @param array $event_details Details of the event's dates and recurrence pattern
......
593 593

  
594 594
	$event_defaults = array(
595 595
		'start' => '', 'end' => '', 'all_day' => 0,
596
		'schedule' => 'once', 'schedule_meta' => '', 'frequency' => 1, 'schedule_last' => '', 
596
		'schedule' => 'once', 'schedule_meta' => '', 'frequency' => 1, 'schedule_last' => '',
597 597
		'until' => '', 'number_occurrences' => 0, 'exclude' => array(), 'include' => array(),
598 598
	);
599 599

  
600 600
	extract( wp_parse_args( $event_data, $event_defaults ) );
601
		
602
		$occurrences =array(); //occurrences array	
601

  
602
		$occurrences =array(); //occurrences array
603 603

  
604 604
		$exclude = array_filter( (array) $exclude );
605 605
		$include = array_filter( (array) $include );
606
		
606

  
607 607
		$exclude = array_udiff($exclude, $include, '_eventorganiser_compare_datetime');
608 608
		$include = array_udiff($include, $exclude, '_eventorganiser_compare_datetime');
609
		
609

  
610 610
		//White list schedule
611 611
		if( !in_array($schedule, array('once','daily','weekly','monthly','yearly','custom')) )
612 612
			return new WP_Error('eo_error',__('Schedule not recognised.','eventorganiser'));
613
		
613

  
614 614
		//Ensure event frequency is a positive integer. Else set to 1.
615 615
		$frequency = max(absint($frequency),1);
616 616
		$all_day = (int) $all_day;
617 617
		$number_occurrences = absint( $number_occurrences );
618
		
618

  
619 619
		//Check dates are supplied and are valid
620 620
		if( !($start instanceof DateTime) )
621 621
			return new WP_Error('eo_error',__('Start date not provided.','eventorganiser'));
622 622

  
623 623
		if( !($end instanceof DateTime) )
624 624
			$end = clone $start;
625
		
625

  
626 626
		//If use 'number_occurrences' to limit recurring event, set dummy 'schedule_last' date.
627 627
		if( !($until instanceof DateTime) && $number_occurrences && in_array( $schedule, array( 'daily','weekly','monthly','yearly' ) ) ){
628 628
			//Set dummy "last occurrance" date.
......
637 637
		//Check dates are in chronological order
638 638
		if($end < $start)
639 639
			return new WP_Error('eo_error',__('Start date occurs after end date.','eventorganiser'));
640
		
640

  
641 641
		if($until < $start)
642 642
			return new WP_Error('eo_error',__('Schedule end date is before is before the start date.','eventorganiser'));
643 643

  
......
652 652
		$start_days =array();
653 653
		$workaround='';
654 654
		$icaldays = array('SU','MO','TU','WE','TH','FR','SA');
655
		$weekdays = array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'); 
655
		$weekdays = array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
656 656
		$ical2day = array('SU'=>'Sunday','MO'=>'Monday','TU'=>'Tuesday','WE'=>'Wednesday','TH'=>'Thursday','FR'=>'Friday','SA'=>'Saturday');
657 657

  
658 658
		//Set up schedule
......
670 670
			case 'daily':
671 671
				$interval = "+".$frequency."day";
672 672
				$start_days[] = clone $start;
673
				break;	
673
				break;
674 674

  
675 675
			case 'weekly':
676 676
				$schedule_meta = ( $schedule_meta ? array_filter($schedule_meta) : array() );
......
694 694
				$rule =$rule_value[0];
695 695
				$values = !empty( $rule_value[1] ) ? explode(',',$rule_value[1]) : array();//Should only be one value, but may support more in future
696 696
				$values =  array_filter( $values );
697
				
697

  
698 698
				if( $rule=='BYMONTHDAY' ):
699 699
					$date = (int) $start_days[0]->format('d');
700 700
					$interval = "+".$frequency."month";
701
					
701

  
702 702
					if($date >= 29)
703 703
						$workaround = 'short months';	//This case deals with 29/30/31 of month
704 704

  
......
727 727
					$day = $weekdays[$day_num];//Full day name from day_num (Sunday -Monday)
728 728
					$schedule_meta = 'BYDAY='.$n.$ical_day; //E.g. BYDAY=2MO
729 729
					$interval = $ordinal[$n].' '.$day.' of +'.$frequency.' month'; //E.g. second monday of +1 month
730
					
730

  
731 731
					//Work around for PHP <5.3
732 732
					if(!function_exists('date_diff')){
733 733
						$workaround = 'php5.2';
734 734
					}
735 735
				endif;
736 736
				break;
737
	
737

  
738 738
			case 'yearly':
739 739
				$start_days[] = clone $start;
740 740
				if( '29-02' == $start_days[0]->format('d-m') )
741 741
					$workaround = 'leap year';
742
				
742

  
743 743
				$interval = "+".$frequency."year";
744 744
				break;
745 745
		endswitch; //End $schedule switch
......
749 749
		foreach($start_days as $index => $start_day):
750 750
			$current = clone $start_day;
751 751
			$occurrence_n = 0;
752
			
752

  
753 753
			switch($workaround):
754 754
				//Not really a workaround. Just add the occurrence and finish.
755 755
				case 'once':
756 756
					$current->setTime($H,$i );
757 757
					$occurrences[] = clone $current;
758 758
					break;
759
				
759

  
760 760
				//Loops for monthly events that require php5.3 functionality
761 761
				case 'php5.2':
762 762
					while( $current <= $until || $occurrence_n < $number_occurrences ):
763 763
						$current->setTime($H,$i );
764
						$occurrences[] = clone $current;	
764
						$occurrences[] = clone $current;
765 765
						$current = _eventorganiser_php52_modify($current,$interval);
766 766
						$occurrence_n++;
767
					endwhile; 
767
					endwhile;
768 768
					break;
769 769

  
770 770
				//Loops for monthly events on the 29th/30th/31st
771 771
				case 'short months':
772 772
					 $day_int =intval($start_day->format('d'));
773
	
773

  
774 774
					//Set the first month
775 775
					$current_month= clone $start_day;
776 776
					$current_month = date_create($current_month->format('Y-m-1'));
777
				
777

  
778 778
					while( $current_month <= $until || $occurrence_n < $number_occurrences ):
779
						$month_int = intval($current_month->format('m'));		
780
						$year_int = intval($current_month->format('Y'));		
779
						$month_int = intval($current_month->format('m'));
780
						$year_int = intval($current_month->format('Y'));
781 781

  
782 782
						if( checkdate($month_int , $day_int , $year_int) ){
783 783
							$current = new DateTime($day_int.'-'.$month_int.'-'.$year_int, $timezone);
......
786 786
							$occurrence_n++;
787 787
						}
788 788
						$current_month->modify($interval);
789
					endwhile;	
789
					endwhile;
790 790
					break;
791 791

  
792 792
				//To be used for yearly events occuring on Feb 29
......
794 794
					$current_year = clone $current;
795 795
					$current_year->modify('-1 day');
796 796

  
797
					while( $current_year <= $until || $occurrence_n < $number_occurrences  ):	
797
					while( $current_year <= $until || $occurrence_n < $number_occurrences  ):
798 798
						$is_leap_year = (int) $current_year->format('L');
799 799

  
800 800
						if( $is_leap_year ){
......
808 808
						$current_year->modify( $interval );
809 809
					endwhile;
810 810
					break;
811
			
811

  
812 812
				default:
813 813
					while( $current <= $until || $occurrence_n < $number_occurrences  ):
814 814
						$current->setTime($H,$i );
815
						$occurrences[] = clone $current;	
815
						$occurrences[] = clone $current;
816 816
						$current->modify( $interval );
817 817
						$occurrence_n++;
818 818
					endwhile;
......
828 828
			$occurrences =  array_slice( $occurrences, 0, $number_occurrences );
829 829
			$until = end( $occurrences );
830 830
		}
831
		
831

  
832 832
		//Cast includes/exclude to timezone
833 833
		$tz = eo_get_blog_timezone();
834 834
		if( $include ){
......
841 841
				$excluded_date->setTimezone( $tz );
842 842
			}
843 843
		}
844
		
844

  
845 845
		//Add inclusions, removes exceptions and duplicates
846 846
		if( defined( 'WP_DEBUG' ) && WP_DEBUG ){
847 847
			//Make sure 'included' dates doesn't appear in generate date
848 848
			$include = array_udiff( $include, $occurrences, '_eventorganiser_compare_datetime' );
849 849
		}
850
		$occurrences = array_merge($occurrences, $include); 
850
		$occurrences = array_merge($occurrences, $include);
851 851
		$occurrences = array_udiff( $occurrences, $exclude, '_eventorganiser_compare_datetime') ;
852 852
		$occurrences = _eventorganiser_remove_duplicates($occurrences);
853 853

  
854 854
		//Sort occurrences
855 855
		sort($occurrences);
856
		
856

  
857 857
		if( empty( $occurrences ) || !$occurrences[0] || !( $occurrences[0] instanceof DateTime ) ){
858 858
			return new WP_Error('eo_error',__('Event does not contain any dates.','eventorganiser'));
859 859
		}
......
874 874
			'include'        => $include,
875 875
			'occurrences'    => $occurrences,
876 876
		);
877
		
877

  
878 878
		/**
879 879
		 * Filters the event schedule after its dates has been generated by a given schedule.
880
		 * 
881
		 * The filtered array is an array of occurrences generated from a 
880
		 *
881
		 * The filtered array is an array of occurrences generated from a
882 882
		 * schedule which may include:
883
		 * 
883
		 *
884 884
		 * * **start** (DateTime) -  when the event starts
885 885
		 * * **end** (DateTime) - when the event ends
886 886
		 * * **all_day** (Bool) - If the event is all day or no
......
894 894
		 * * **exclude** (array) - Array of DateTime objects  to exclude from the schedule
895 895
		 * * **include** (array) - Array of DateTime objects to include in the schedule
896 896
		 * * **occurrences** (array) - Array of DateTime objects generated from the above schedule.
897
		 * 
897
		 *
898 898
		 * @param array $_event_data The event schedule with generated occurrences.
899
		 * @param array $event_data The original event schedule (without occurrences).  
899
		 * @param array $event_data The original event schedule (without occurrences).
900 900
		 */
901 901
		$_event_data = apply_filters( 'eventorganiser_generate_occurrences', $_event_data, $event_data );
902 902
		return $_event_data;
903 903
	}
904 904

  
905 905
/**
906
 * Generates the ICS RRULE fromthe event schedule data. 
906
 * Generates the ICS RRULE fromthe event schedule data.
907 907
 * @access private
908 908
 * @ignore
909 909
 * @since 1.0.0
......
921 921
			return false;
922 922

  
923 923
		extract($rrule);
924
		
924

  
925 925
		$schedule_last->setTimezone( new DateTimeZone('UTC') );
926 926
		$schedule_last = $schedule_last->format( 'Ymd\THis\Z' );
927 927

  
......
938 938
				$recurrence_rule .=$schedule_meta.";";
939 939
				$recurrence_rule .= "UNTIL=".$schedule_last;
940 940
				return $recurrence_rule;
941
	
941

  
942 942
			case 'weekly':
943
				
943

  
944 944
				if( !eo_is_all_day( $post_id ) ){
945 945
					//None all day event, setting event timezone to UTC may cause it to shift days.
946 946
					//E.g. a 9pm Monday event in New York will a Tuesday event in UTC.
947 947
					//We may need to correct the BYDAY attribute to be valid for UTC.
948
					
948

  
949 949
					$days_of_week = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
950 950
					$UTC = new DateTimeZone('UTC');
951
					
951

  
952 952
					//Get day shift upon timezone set to UTC
953 953
					$start = eo_get_schedule_start( DATETIMEOBJ, $post_id );
954 954
					$local_day = (int) $start->format( 'w' );
955 955
					$start->setTimezone( $UTC );
956 956
					$utc_day = (int) $start->format( 'w' );
957 957
					$diff = $utc_day - $local_day + 7; //ensure difference is positive (should be 0, +1 or +6).
958
					
958

  
959 959
					//If there is a shift correct BYDAY
960 960
					if( $diff ){
961 961
						$utc_days = array();
962
					
962

  
963 963
						foreach( $schedule_meta as $day ){
964 964
							$utc_day_index = ( array_search( $day, $days_of_week ) + $diff ) %7;
965 965
							$utc_days[] = $days_of_week[$utc_day_index];
966 966
						}
967 967
						$schedule_meta = $utc_days;
968 968
					}
969
					
969

  
970 970
				}
971
				
971

  
972 972
				return "FREQ=WEEKLY;INTERVAL=".$frequency.";BYDAY=".implode(',',$schedule_meta).";UNTIL=".$schedule_last;
973 973

  
974 974
			case 'daily':
......
980 980
}
981 981

  
982 982
function eventorganiser_ical_vtimezone( $timezone, $from, $to ) {
983
	
983

  
984 984
	$vtimezone = "BEGIN:VTIMEZONE\r\n";
985 985
	$vtimezone .= sprintf( "TZID:%s\r\n", $timezone->getName() );
986
	
986

  
987 987
	//$timezone->getTransitions() doesn't accept any arguments in php 5.2, and would be ineffecient
988 988
	if ( version_compare( PHP_VERSION, '5.3.0' ) < 0 ) {
989 989
		return '';
990 990
	}
991
	
991

  
992 992
	// get all transitions, and (as an estimate) an early one which we skip
993 993
	$transitions = $timezone->getTransitions( $from - YEAR_IN_SECONDS / 2, $to );
994
	
994

  
995 995
	if ( ! $transitions ) {
996 996
		return '';
997 997
	}
998 998

  
999 999
	foreach ( $transitions as $i => $trans ) {
1000
		
1000

  
1001 1001
		$pm      = $trans['offset'] >= 0 ? '+' : '-';
1002 1002
 		$hours   = floor( absint( $trans['offset'] ) / HOUR_IN_SECONDS ) % 24;
1003 1003
		$minutes = ( absint( $trans['offset'] ) - $hours * HOUR_IN_SECONDS ) / MINUTE_IN_SECONDS;
1004
		
1004

  
1005 1005
		$tzto = $pm . str_pad( $hours, 2, '0', STR_PAD_LEFT ) . str_pad( $minutes, 2, '0', STR_PAD_LEFT );
1006
	
1006

  
1007 1007
		// skip the first entry, we just want it for the TZOFFSETFROM value of the next one
1008 1008
		if ( $i == 0 ) {
1009 1009
			$tzfrom = $tzto;
......
1011 1011
				continue;
1012 1012
			}
1013 1013
		}
1014
		
1015
		$type = $trans['isdst'] ? 'DAYLIGHT' : 'STANDARD';		
1014

  
1015
		$type = $trans['isdst'] ? 'DAYLIGHT' : 'STANDARD';
1016 1016
		$dt   = new DateTime( $trans['time'], $timezone );
1017
			
1017

  
1018 1018
		$vtimezone .= sprintf( "BEGIN:%s\r\n", $type );
1019 1019
		$vtimezone .= sprintf( "TZOFFSETFROM:%s\r\n", $tzfrom ); //needs formatting
1020 1020
		$vtimezone .= sprintf( "TZOFFSETTO:%s\r\n", $tzto ); //needs formatting
1021 1021
		$vtimezone .= sprintf( "DTSTART:%s\r\n",  $dt->format('Ymd\THis') );
1022
		$vtimezone .= sprintf( "TZNAME:%s\r\n",  $trans['abbr'] ); 
1022
		$vtimezone .= sprintf( "TZNAME:%s\r\n",  $trans['abbr'] );
1023 1023
		$vtimezone .= sprintf( "END:%s\r\n", $type );
1024
		
1025
		$tzfrom = $tzto;	
1024

  
1025
		$tzfrom = $tzto;
1026 1026
	}
1027
	
1027

  
1028 1028
	$vtimezone .= 'END:VTIMEZONE';
1029
	
1029

  
1030 1030
	return $vtimezone;
1031 1031
}
1032 1032

  
......
1044 1044
		global $wpdb;
1045 1045

  
1046 1046
		$remove = $wpdb->get_row($wpdb->prepare(
1047
			"SELECT {$wpdb->eo_events}.StartDate, {$wpdb->eo_events}.StartTime  
1048
			FROM {$wpdb->eo_events} 
1047
			"SELECT {$wpdb->eo_events}.StartDate, {$wpdb->eo_events}.StartTime
1048
			FROM {$wpdb->eo_events}
1049 1049
			WHERE post_id=%d AND event_id=%d",$post_id,$event_id));
1050 1050

  
1051 1051
		if( !$remove )
......
1064 1064
		}
1065 1065

  
1066 1066
		//Update post meta and delete date from events table
1067
		update_post_meta( $post_id,'_eventorganiser_event_schedule',$event_details);		
1067
		update_post_meta( $post_id,'_eventorganiser_event_schedule',$event_details);
1068 1068
		eo_delete_event_occurrences( $post_id, $event_id );
1069 1069

  
1070 1070
		//Clear cache
......
1073 1073
		return true;
1074 1074
	}
1075 1075

  
1076
	
1076

  
1077 1077
/**
1078
 * Updates a specific occurrence, and preserves the occurrence ID. 
1079
 * 
1078
 * Updates a specific occurrence, and preserves the occurrence ID.
1079
 *
1080 1080
 * Currently two occurrences cannot occupy the same date.
1081
 * 
1081
 *
1082 1082
 * @ignore
1083 1083
 * @access private
1084 1084
 * @since 2.12.0
1085
 * 
1085
 *
1086 1086
 * @param int $event_id      ID of the event whose occurrence we're moving
1087 1087
 * @param int $occurrence_id ID of the occurrence we're moving
1088 1088
 * @param DateTime $start    New start DateTime of the occurrence
......
1092 1092
function eventorganiser_move_occurrence( $event_id, $occurrence_id, $start, $end ){
1093 1093

  
1094 1094
	global $wpdb;
1095
		
1095

  
1096 1096
	$old_start = eo_get_the_start( DATETIMEOBJ, $event_id, null, $occurrence_id );
1097 1097
	$schedule  = eo_get_event_schedule( $event_id );
1098
	
1098

  
1099 1099
	if( $start == $old_start ){
1100 1100
		return true;
1101 1101
	}
1102
	
1102

  
1103 1103
	$current_occurrences = eo_get_the_occurrences( $event_id );
1104 1104
	unset( $current_occurrences[$occurrence_id] );
1105 1105
	$current_occurrences = array_map( 'eo_format_datetime', $current_occurrences );
1106
	
1106

  
1107 1107
	if( in_array( $start->format( 'd-m-Y' ), $current_occurrences ) ){
1108
		return new WP_Error( 'events-cannot-share-date', __( 'There is already an occurrence on this date', 'eventorganiser' ) );		
1108
		return new WP_Error( 'events-cannot-share-date', __( 'There is already an occurrence on this date', 'eventorganiser' ) );
1109 1109
	}
1110
	
1110

  
1111 1111
	//We update the date directly in the DB first so the occurrence is not deleted and recreated,
1112
	//but simply updated. 
1113
	
1112
	//but simply updated.
1113

  
1114 1114
	$wpdb->update(
1115
		$wpdb->eo_events, 
1115
		$wpdb->eo_events,
1116 1116
		array(
1117 1117
			'StartDate'  => $start->format( 'Y-m-d' ),
1118 1118
			'StartTime'  => $start->format( 'H:i:s' ),
1119 1119
			'EndDate'    => $end->format( 'Y-m-d' ),
1120 1120
			'FinishTime' => $end->format( 'H:i:s' ),
1121
		),				
1121
		),
1122 1122
		array( 'event_id' => $occurrence_id )
1123 1123
	);
1124
	
1124

  
1125 1125
	wp_cache_delete( 'eventorganiser_occurrences_'.$event_id );//Important: update DB clear cache
1126 1126
	wp_cache_delete( 'eventorganiser_all_occurrences_'.$event_id );//Important: update DB clear cache
1127 1127

  
1128 1128
	//Now update event schedule...
1129
	
1130
	//If date being removed was manually included remove it, 
1129

  
1130
	//If date being removed was manually included remove it,
1131 1131
	//otherwise add it to exclude. Then add new date as include.
1132 1132
	if( false === ( $index = array_search( $old_start, $schedule['include'] ) ) ){
1133 1133
		$schedule['exclude'][] = $old_start;
......
1137 1137
	$schedule['include'][] = $start;
1138 1138

  
1139 1139
	$re = eo_update_event( $event_id, $schedule );
1140
	
1140

  
1141 1141
	if( $re && !is_wp_error( $re ) ){
1142 1142
		return true;
1143 1143
	}
1144
	
1144

  
1145 1145
	return $re;
1146 1146
}
1147 1147
?>
tests/unit-tests/recurrenceTest.php
2 2

  
3 3
class recurrenceTest extends EO_UnitTestCase
4 4
{
5
	
5

  
6 6
    public function testOneOff()
7 7
    {
8 8
    	$_event_data = array(
......
13 13
    	$expected = array(
14 14
    			new DateTime( '2013-10-22 19:19:00', eo_get_blog_timezone() ),
15 15
    	);
16
    	
16

  
17 17
    	$this->assertEquals( $expected, $event_data['occurrences'] );
18
		
18

  
19 19
    }
20 20

  
21
    
21

  
22 22
    public function testDaily()
23 23
    {
24 24
    	$_event_data = array(
......
27 27
    			'schedule' => 'daily',
28 28
    			'frequency' => 2,
29 29
    			'until' => new DateTime( '2013-11-02 20:19:00', eo_get_blog_timezone() ),
30
    			
30

  
31 31
    	);
32
    	
32

  
33 33
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
34 34
    	$expected = array(
35 35
    			new DateTime( '2013-10-22 19:19:00', eo_get_blog_timezone() ),
......
39 39
    			new DateTime( '2013-10-30 19:19:00', eo_get_blog_timezone() ),
40 40
    			new DateTime( '2013-11-01 19:19:00', eo_get_blog_timezone() )
41 41
    	);
42
    	 
42

  
43 43
    	$this->assertEquals( $expected, $event_data['occurrences'] );
44
    
44

  
45 45
    }
46
    
46

  
47 47
    public function testWeekly()
48 48
    {
49 49
    	$_event_data = array(
......
52 52
    			'schedule' => 'weekly',
53 53
    			'schedule_meta' => array( 'WE', 'FR' ),
54 54
    			'until' => new DateTime( '2013-11-02 20:19:00', eo_get_blog_timezone() ),
55
    			 
55

  
56 56
    	);
57
    	 
57

  
58 58
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
59 59
    	$expected = array(
60 60
    			new DateTime( '2013-10-23 19:19:00', eo_get_blog_timezone() ),
......
62 62
    			new DateTime( '2013-10-30 19:19:00', eo_get_blog_timezone() ),
63 63
    			new DateTime( '2013-11-01 19:19:00', eo_get_blog_timezone() )
64 64
    	);
65
    
65

  
66 66
    	$this->assertEquals( $expected, $event_data['occurrences'] );
67
    
67

  
68 68
    }
69
    
69

  
70 70
    public function testWeeklyWithoutMeta()
71 71
    {
72 72
    	$_event_data = array(
......
74 74
    			'end' => new DateTime( '2013-10-22 20:19:00', eo_get_blog_timezone() ),
75 75
    			'schedule' => 'weekly',
76 76
    			'until' => new DateTime( '2013-11-02 20:19:00', eo_get_blog_timezone() ),
77
    
77

  
78 78
    	);
79
    
79

  
80 80
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
81 81
    	$expected = array(
82 82
    			new DateTime( '2013-10-22 19:19:00', eo_get_blog_timezone() ),
83 83
    			new DateTime( '2013-10-29 19:19:00', eo_get_blog_timezone() )
84 84
    	);
85
    
85

  
86 86
    	$this->assertEquals( $expected, $event_data['occurrences'] );
87
    	
87

  
88 88
    	$this->assertEquals( array( 'TU' ), $event_data['schedule_meta'] );
89
    
89

  
90 90
    }
91
    
91

  
92 92
    public function testWeeklyLimitedData()
93 93
    {
94 94
    	$_event_data = array(
......
96 96
    			'end' => new DateTime( '2013-10-22 20:19:00', eo_get_blog_timezone() ),
97 97
    			'schedule' => 'weekly',
98 98
    			'until' => new DateTime( '2013-11-02 20:19:00', eo_get_blog_timezone() ),
99
    
99

  
100 100
    	);
101
    
101

  
102 102
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
103 103
    	$expected = array(
104 104
    			new DateTime( '2013-10-22 19:19:00', eo_get_blog_timezone() ),
105 105
    			new DateTime( '2013-10-29 19:19:00', eo_get_blog_timezone() )
106 106
    	);
107
    
107

  
108 108
    	$this->assertEquals( $expected, $event_data['occurrences'] );
109
    
109

  
110 110
    }
111 111

  
112
    
112

  
113 113
    public function testMonthlyByMonthDay()
114 114
    {
115 115
    	$_event_data = array(
......
118 118
    			'schedule' => 'monthly',
119 119
    			'schedule_meta' => 'BYMONTHDAY',
120 120
    			'until' => new DateTime( '2014-01-22 19:19:00', eo_get_blog_timezone() ),
121
    
121

  
122 122
    	);
123
    
123

  
124 124
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
125 125
    	$expected = array(
126 126
    			new DateTime( '2013-10-22 19:19:00', eo_get_blog_timezone() ),
......
128 128
    			new DateTime( '2013-12-22 19:19:00', eo_get_blog_timezone() ),
129 129
    			new DateTime( '2014-01-22 19:19:00', eo_get_blog_timezone() ),
130 130
    	);
131
    
131

  
132 132
    	$this->assertEquals( $expected, $event_data['occurrences'] );
133 133
    }
134
    
134

  
135 135
    public function testMonthlyByDay()
136 136
    {
137 137
    	$_event_data = array(
......
140 140
    			'schedule' => 'monthly',
141 141
    			'schedule_meta' => 'BYDAY=4WE',
142 142
    			'until' => new DateTime( '2014-01-22 19:19:00', eo_get_blog_timezone() ),
143
    
143

  
144 144
    	);
145
    
145

  
146 146
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
147 147
    	$expected = array(
148 148
    			new DateTime( '2013-10-22 19:19:00', eo_get_blog_timezone() ), //Tuesday
......
150 150
    			new DateTime( '2013-12-25 19:19:00', eo_get_blog_timezone() ), //4th Wednesday
151 151
    			new DateTime( '2014-01-22 19:19:00', eo_get_blog_timezone() ), //4th Wednesday
152 152
    	);
153
    
153

  
154 154
    	$this->assertEquals( $expected, $event_data['occurrences'] );
155 155
    }
156
    
156

  
157 157
    public function testYearly()
158 158
    {
159 159
    	$_event_data = array(
......
162 162
    			'schedule' => 'yearly',
163 163
    			'frequency' => 2,
164 164
    			'until' => new DateTime( '2017-10-22 19:19:00', eo_get_blog_timezone() ),
165
    
165

  
166 166
    	);
167
    
167

  
168 168
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
169 169
    	$expected = array(
170 170
    			new DateTime( '2013-10-22 19:19:00', eo_get_blog_timezone() ),
171 171
    			new DateTime( '2015-10-22 19:19:00', eo_get_blog_timezone() ),
172 172
    			new DateTime( '2017-10-22 19:19:00', eo_get_blog_timezone() )
173 173
    	);
174
    
174

  
175 175
    	$this->assertEquals( $expected, $event_data['occurrences'] );
176 176
    }
177
    
177

  
178 178
    public function testCustom()
179 179
    {
180 180
    	$_event_data = array(
......
187 187
    				new DateTime( '2013-11-17 19:19:00', eo_get_blog_timezone() ),
188 188
    				new DateTime( '2013-12-06 19:19:00', eo_get_blog_timezone() ),
189 189
    			)
190
    
190

  
191 191
    	);
192
    
192

  
193 193
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
194 194
    	$expected = array(
195 195
    		new DateTime( '2013-10-22 19:19:00', eo_get_blog_timezone() ),
......
197 197
    		new DateTime( '2013-11-17 19:19:00', eo_get_blog_timezone() ),
198 198
    		new DateTime( '2013-12-06 19:19:00', eo_get_blog_timezone() ),
199 199
    	);
200
    
200

  
201 201
    	$this->assertEquals( $expected, $event_data['occurrences'] );
202 202
    }
203
    
203

  
204 204
    public function testLeapYear()
205 205
    {
206 206
    	$_event_data = array(
......
209 209
    			'schedule' => 'yearly',
210 210
    			'frequency' => 1,
211 211
    			'until' => new DateTime( '2020-02-29 00:00:00', eo_get_blog_timezone() ),
212
    
212

  
213 213
    	);
214
    
214

  
215 215
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
216 216
    	$expected = array(
217 217
    			new DateTime( '2012-02-29 00:00:00', eo_get_blog_timezone() ),
218 218
    			new DateTime( '2016-02-29 00:00:00', eo_get_blog_timezone() ),
219 219
    			new DateTime( '2020-02-29 00:00:00', eo_get_blog_timezone() )
220 220
    	);
221
    
221

  
222 222
    	$this->assertEquals( $expected, $event_data['occurrences'] );
223 223
    }
224
    
224

  
225 225
    public function testShortMonth()
226 226
    {
227 227
    	$_event_data = array(
......
231 231
    			'frequency' => 1,
232 232
    			'until' => new DateTime( '2013-03-31 00:00:00', eo_get_blog_timezone() ),
233 233
    			'schedule_meta' => 'BYMONTHDAY',
234
    
234

  
235 235
    	);
236
    
236

  
237 237
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
238 238
    	$expected = array(
239 239
    			new DateTime( '2012-10-31 00:00:00', eo_get_blog_timezone() ),
......
241 241
    			new DateTime( '2013-01-31 00:00:00', eo_get_blog_timezone() ),
242 242
    			new DateTime( '2013-03-31 00:00:00', eo_get_blog_timezone() ),
243 243
    	);
244
    
244

  
245 245
    	$this->assertEquals( $expected, $event_data['occurrences'] );
246 246
    }
247 247

  
248
    
248

  
249 249
    public function testNoOccurrences()
250 250
    {
251 251
    	$_event_data = array(
......
256 256
    			)
257 257
    	);
258 258
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
259
    	 
259

  
260 260
    	$this->assertInstanceOf( 'WP_Error', $event_data );
261 261
    }
262
    
263
    
262

  
263

  
264 264
    public function testNumberOccurrences()
265 265
    {
266 266
    	$_event_data = array(
......
270 270
    			'frequency' => 3,
271 271
    			'number_occurrences' => 3,
272 272
    	);
273
    
273

  
274 274
    	$event_data = _eventorganiser_generate_occurrences( $_event_data );
275 275
    	$expected = array(
276 276
    			new DateTime( '2013-11-01 00:00:00', eo_get_blog_timezone() ),
277 277
    			new DateTime( '2013-11-04 00:00:00', eo_get_blog_timezone() ),
278 278
    			new DateTime( '2013-11-07 00:00:00', eo_get_blog_timezone() )
279 279
    	);
280
    
280

  
281 281
    	$this->assertEquals( $expected, $event_data['occurrences'] );
282 282
    }
283
    
283

  
284 284
    public function testNumberOccurrencesWeekly(){
285
    	
285

  
286 286
    	$_event_data = array(
... This diff was truncated because it exceeds the maximum size that can be displayed.