/** * This is a private function - handles the generation of occurrence dates from the schedule data * @access private * @ignore * * @param array $event_data - Array containing the event's schedule data * @return array $event_data - Array containing the event's schedule data including 'occurrences', an array of DateTimes */ function _eventorganiser_generate_occurrences($event_data = array()) { $event_defaults = array('start' => '', 'end' => '', 'all_day' => 0, 'schedule' => 'once', 'schedule_meta' => '', 'frequency' => 1, 'schedule_last' => '', 'number_occurrences' => 0, 'exclude' => array(), 'include' => array()); extract(wp_parse_args($event_data, $event_defaults)); $occurrences = array(); //occurrences array $exclude = array_filter((array) $exclude); $include = array_filter((array) $include); $exclude = array_udiff($exclude, $include, '_eventorganiser_compare_datetime'); $include = array_udiff($include, $exclude, '_eventorganiser_compare_datetime'); //White list schedule if (!in_array($schedule, array('once', 'daily', 'weekly', 'monthly', 'yearly', 'custom'))) { return new WP_Error('eo_error', __('Schedule not recognised.', 'eventorganiser')); } //Ensure event frequency is a positive integer. Else set to 1. $frequency = max(absint($frequency), 1); $all_day = (int) $all_day; $number_occurrences = absint($number_occurrences); //Check dates are supplied and are valid if (!$start instanceof DateTime) { return new WP_Error('eo_error', __('Start date not provided.', 'eventorganiser')); } if (!$end instanceof DateTime) { $end = clone $start; } //If use 'number_occurrences' to limit recurring event, set dummy 'schedule_last' date. if (!$schedule_last instanceof DateTime && $number_occurrences && in_array($schedule, array('daily', 'weekly', 'monthly', 'yearly'))) { //Set dummy "last occurrance" date. $schedule_last = clone $start; } else { $number_occurrences = 0; } if ('once' == $schedule || !$schedule_last instanceof DateTime) { $schedule_last = clone $start; } //Check dates are in chronological order if ($end < $start) { return new WP_Error('eo_error', __('Start date occurs after end date.', 'eventorganiser')); } if ($schedule_last < $start) { return new WP_Error('eo_error', __('Schedule end date is before is before the start date.', 'eventorganiser')); } //Now set timezones $timezone = eo_get_blog_timezone(); $start->setTimezone($timezone); $end->setTimezone($timezone); $schedule_last->setTimezone($timezone); $H = intval($start->format('H')); $i = intval($start->format('i')); $start_days = array(); $workaround = ''; $icaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); $weekdays = array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); $ical2day = array('SU' => 'Sunday', 'MO' => 'Monday', 'TU' => 'Tuesday', 'WE' => 'Wednesday', 'TH' => 'Thursday', 'FR' => 'Friday', 'SA' => 'Saturday'); //Set up schedule switch ($schedule) { case 'once': case 'custom': $frequency = 1; $schedule_meta = ''; $schedule_start = clone $start; $schedule_last = clone $start; $start_days[] = clone $start; $workaround = 'once'; //Not strictly a workaround. break; case 'daily': $interval = "+" . $frequency . "day"; $start_days[] = clone $start; break; case 'weekly': $schedule_meta = $schedule_meta ? array_filter($schedule_meta) : array(); if (!empty($schedule_meta) && is_array($schedule_meta)) { foreach ($schedule_meta as $day) { $start_day = clone $start; $start_day->modify($ical2day[$day]); $start_days[] = $start_day; } } else { $schedule_meta = array($icaldays[$start->format('w')]); $start_days[] = clone $start; } $interval = "+" . $frequency . "week"; break; case 'monthly': $start_days[] = clone $start; $rule_value = explode('=', $schedule_meta, 2); $rule = $rule_value[0]; $values = !empty($rule_value[1]) ? explode(',', $rule_value[1]) : array(); //Should only be one value, but may support more in future $values = array_filter($values); if ($rule == 'BYMONTHDAY') { $date = (int) $start_days[0]->format('d'); $interval = "+" . $frequency . "month"; if ($date >= 29) { $workaround = 'short months'; } //This case deals with 29/30/31 of month $schedule_meta = 'BYMONTHDAY=' . $date; } else { if (empty($values)) { $date = (int) $start_days[0]->format('d'); $n = ceil($date / 7); // nth weekday of month. $day_num = intval($start_days[0]->format('w')); //0 (Sun) - 6(Sat) } else { //expect e.g. array( 2MO ) preg_match('/^(-?\\d{1,2})([a-zA-Z]{2})/', $values[0], $matches); $n = (int) $matches[1]; $day_num = array_search($matches[2], $icaldays); //(Sun) - 6(Sat) } if ($n == 5) { $n = -1; } //If 5th, interpret it as last. $ordinal = array('1' => "first", '2' => "second", '3' => "third", '4' => "fourth", '-1' => "last"); if (!isset($ordinal[$n])) { return new WP_Error('eo_error', __('Invalid monthly schedule (invalid ordinal)', 'eventorganiser')); } $ical_day = $icaldays[$day_num]; //ical day from day_num (SU - SA) $day = $weekdays[$day_num]; //Full day name from day_num (Sunday -Monday) $schedule_meta = 'BYDAY=' . $n . $ical_day; //E.g. BYDAY=2MO $interval = $ordinal[$n] . ' ' . $day . ' of +' . $frequency . ' month'; //E.g. second monday of +1 month //Work around for PHP <5.3 if (!function_exists('date_diff')) { $workaround = 'php5.2'; } } break; case 'yearly': $start_days[] = clone $start; if ('29-02' == $start_days[0]->format('d-m')) { $workaround = 'leap year'; } $interval = "+" . $frequency . "year"; break; } //End $schedule switch //Now we have setup and validated the schedules - loop through and generate occurrences foreach ($start_days as $index => $start_day) { $current = clone $start_day; $occurrence_n = 0; switch ($workaround) { //Not really a workaround. Just add the occurrence and finish. case 'once': $current->setTime($H, $i); $occurrences[] = clone $current; break; //Loops for monthly events that require php5.3 functionality //Loops for monthly events that require php5.3 functionality case 'php5.2': while ($current <= $schedule_last || $occurrence_n < $number_occurrences) { $current->setTime($H, $i); $occurrences[] = clone $current; $current = _eventorganiser_php52_modify($current, $interval); $occurrence_n++; } break; //Loops for monthly events on the 29th/30th/31st //Loops for monthly events on the 29th/30th/31st case 'short months': $day_int = intval($start_day->format('d')); //Set the first month $current_month = clone $start_day; $current_month = date_create($current_month->format('Y-m-1')); while ($current_month <= $schedule_last || $occurrence_n < $number_occurrences) { $month_int = intval($current_month->format('m')); $year_int = intval($current_month->format('Y')); if (checkdate($month_int, $day_int, $year_int)) { $current = new DateTime($day_int . '-' . $month_int . '-' . $year_int, $timezone); $current->setTime($H, $i); $occurrences[] = clone $current; $occurrence_n++; } $current_month->modify($interval); } break; //To be used for yearly events occuring on Feb 29 //To be used for yearly events occuring on Feb 29 case 'leap year': $current_year = clone $current; $current_year->modify('-1 day'); while ($current_year <= $schedule_last || $occurrence_n < $number_occurrences) { $is_leap_year = (int) $current_year->format('L'); if ($is_leap_year) { $current = clone $current_year; $current->modify('+1 day'); $current->setTime($H, $i); $occurrences[] = clone $current; $occurrence_n++; } $current_year->modify($interval); } break; default: while ($current <= $schedule_last || $occurrence_n < $number_occurrences) { $current->setTime($H, $i); $occurrences[] = clone $current; $current->modify($interval); $occurrence_n++; } break; } //End 'workaround' switch; } //Now schedule meta is set up and occurrences are generated. if ($number_occurrences > 0) { //If recurrence is limited by #occurrences. Do that here. sort($occurrences); $occurrences = array_slice($occurrences, 0, $number_occurrences); } //Add inclusions, removes exceptions and duplicates if (defined('WP_DEBUG') && WP_DEBUG) { //Make sure 'included' dates doesn't appear in generate date $include = array_udiff($include, $occurrences, '_eventorganiser_compare_datetime'); } $occurrences = array_merge($occurrences, $include); $occurrences = array_udiff($occurrences, $exclude, '_eventorganiser_compare_datetime'); $occurrences = _eventorganiser_remove_duplicates($occurrences); //Sort occurrences sort($occurrences); if (empty($occurrences) || !$occurrences[0] || !$occurrences[0] instanceof DateTime) { return new WP_Error('eo_error', __('Event does not contain any dates.', 'eventorganiser')); } $schedule_start = clone $occurrences[0]; $schedule_last = clone end($occurrences); $_event_data = array('start' => $start, 'end' => $end, 'all_day' => $all_day, 'schedule' => $schedule, 'schedule_meta' => $schedule_meta, 'frequency' => $frequency, 'schedule_start' => $schedule_start, 'schedule_last' => $schedule_last, 'exclude' => $exclude, 'include' => $include, 'occurrences' => $occurrences); /** * Filters the event schedule after its dates has been generated by a given schedule. * * The filtered array is an array of occurrences generated from a * schedule which may include: * * * **start** (DateTime) - when the event starts * * **end** (DateTime) - when the event ends * * **all_day** (Bool) - If the event is all day or no * * **all_day** (Bool) - If the event is all day or not * * **schedule** (String) - One of once|weekl|daily|monthly|yearly|custom * * **schedule_meta** (Array|String) - See documentation for `eo_insert_event()` * * **frequency** (int) - The frequency of which the event repeats * * **schedule_last** (DateTime) - date of last occurrence of event * * **number_occurrences** (int) - number of times the event should repeat (if `schedule_last` is not specified). * * **exclude** (array) - Array of DateTime objects to exclude from the schedule * * **include** (array) - Array of DateTime objects to include in the schedule * * **occurrences** (array) - Array of DateTime objects generated from the above schedule. * * @param array The event schedule with generated occurrences. * @param array The original event schedule (without occurrences). */ $_event_data = apply_filters('eventorganiser_generate_occurrences', $_event_data, $event_data); return $_event_data; }
public function testRemoveDuplicatesAssociative() { $array = array('hello' => new DateTime('2013-01-01 15:00'), 'foo' => new DateTime('2013-01-02 15:00'), 'bar' => new DateTime('2013-01-01 15:00')); $array_without_duplicates = array('hello' => new DateTime('2013-01-01 15:00'), 'foo' => new DateTime('2013-01-02 15:00')); $this->assertEquals($array_without_duplicates, _eventorganiser_remove_duplicates($array)); }
/** * Saves the event data posted from the event metabox. * Hooked to the 'save_post' action * * @since 1.0.0 * * @param int $post_id the event post ID * @return int $post_id the event post ID */ function eventorganiser_details_save($post_id) { //make sure data came from our meta box if (!isset($_POST['_eononce']) || !wp_verify_nonce($_POST['_eononce'], 'eventorganiser_event_update_' . $post_id . '_' . get_current_blog_id())) { return; } //verify this is not an auto save routine. if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } //authentication checks if (!current_user_can('edit_event', $post_id)) { return; } //Collect raw data $raw_data = isset($_POST['eo_input']) ? $_POST['eo_input'] : array(); $raw_data = wp_parse_args($raw_data, array('StartDate' => '', 'EndDate' => '', 'StartTime' => '00:00', 'FinishTime' => '23:59', 'schedule' => 'once', 'event_frequency' => 1, 'schedule_end' => '', 'allday' => 0, 'schedule_meta' => '', 'days' => array(), 'include' => '', 'exclude' => '')); //Update venue $venue_id = !empty($raw_data['event-venue']) ? intval($raw_data['event-venue']) : null; //Maybe create a new venue if (empty($venue_id) && !empty($_POST['eo_venue']) && current_user_can('manage_venues')) { $venue = $_POST['eo_venue']; if (!empty($venue['name'])) { $new_venue = eo_insert_venue($venue['name'], $venue); if (!is_wp_error($new_venue)) { $venue_id = $new_venue['term_id']; } else { if ($new_venue->get_error_code() == 'term_exists') { $venue_id = eo_get_venue($event_id); } } } } //Set venue $r = wp_set_post_terms($post_id, array($venue_id), 'event-venue', false); //If reocurring, but not editing occurrences, can abort here, but trigger hook. if (eo_reoccurs($post_id) && (!isset($raw_data['AlterRe']) || 'yes' != $raw_data['AlterRe'])) { /** * Triggered after an event has been updated. * * @param int $post_id The ID of the event */ do_action('eventorganiser_save_event', $post_id); //Need this to update cache return; } //Check dates $date_format = eventorganiser_get_option('dateformat'); $is24 = eventorganiser_blog_is_24(); $time_format = $is24 ? 'H:i' : 'g:ia'; $datetime_format = $date_format . ' ' . $time_format; //Set times for all day events $all_day = intval($raw_data['allday']); if ($all_day) { $raw_data['StartTime'] = $is24 ? '00:00' : '12:00am'; $raw_data['FinishTime'] = $is24 ? '23:59' : '11:59pm'; } $start = eo_check_datetime($datetime_format, trim($raw_data['StartDate']) . ' ' . trim($raw_data['StartTime'])); $end = eo_check_datetime($datetime_format, trim($raw_data['EndDate']) . ' ' . trim($raw_data['FinishTime'])); $schedule_last = eo_check_datetime($datetime_format, trim($raw_data['schedule_end']) . ' ' . trim($raw_data['StartTime'])); //Collect schedule meta $schedule = $raw_data['schedule']; if ('weekly' == $schedule) { $schedule_meta = $raw_data['days']; $occurs_by = ''; } elseif ('monthly' == $schedule) { $schedule_meta = $raw_data['schedule_meta']; $occurs_by = trim($schedule_meta, '='); } else { $schedule_meta = ''; $occurs_by = ''; } //Collect include/exclude $in_ex = array(); $orig_schedule = eo_get_event_schedule($post_id); foreach (array('include', 'exclude') as $key) { $in_ex[$key] = array(); $arr = explode(',', sanitize_text_field($raw_data[$key])); if (!empty($arr)) { foreach ($arr as $date) { if ($date_obj = eo_check_datetime('Y-m-d', trim($date))) { $date_obj->setTime($start->format('H'), $start->format('i')); $in_ex[$key][] = $date_obj; } } if ($orig = array_uintersect($orig_schedule[$key], $in_ex[$key], '_eventorganiser_compare_dates')) { $in_ex[$key] = array_merge($orig, $in_ex[$key]); $in_ex[$key] = _eventorganiser_remove_duplicates($in_ex[$key]); } } } $event_data = array('start' => $start, 'end' => $end, 'all_day' => $all_day, 'schedule' => $schedule, 'frequency' => (int) $raw_data['event_frequency'], 'schedule_last' => $schedule_last, 'schedule_meta' => $schedule_meta, 'occurs_by' => $occurs_by, 'include' => $in_ex['include'], 'exclude' => $in_ex['exclude']); $response = eo_update_event($post_id, $event_data); if (is_wp_error($response)) { global $EO_Errors; $code = $response->get_error_code(); $message = $response->get_error_message($code); $errors[$post_id][] = __('Event dates were not saved.', 'eventorganiser'); $errors[$post_id][] = $message; $EO_Errors->add('eo_error', $message); update_option('eo_notice', $errors); } return; }