/**
  * get_matching_events function
  *
  * Get events that match with the arguments provided.
  *
  * @param int | bool          $start      Events start before this (GMT) time
  * @param int | bool          $end        Events end before this (GMT) time
  * @param array $filter       Array of filters for the events returned.
  *                            ['cat_ids']   => non-associatative array of category IDs
  *                            ['tag_ids']   => non-associatative array of tag IDs
  *                            ['post_ids']  => non-associatative array of post IDs
  *
  * @return array Matching events #'
  **/
 function get_matching_events($start = false, $end = false, $filter = array())
 {
     global $wpdb, $ai1ec_calendar_helper, $ai1ec_localization_helper;
     // holds event_categories sql
     $c_sql = '';
     $c_where_sql = '';
     // holds event_tags sql
     $t_sql = '';
     $t_where_sql = '';
     // holds posts sql
     $p_where_sql = '';
     // holds start sql
     $start_where_sql = '';
     // holds end sql
     $end_where_sql = '';
     // hold escape values
     $args = array();
     // =============================
     // = Generating start date sql =
     // =============================
     if ($start !== false) {
         $start_where_sql = "AND (e.start >= %s OR e.recurrence_rules != '')";
         $args[] = Ai1ec_Time_Utility::to_mysql_date($start);
     }
     // ===========================
     // = Generating end date sql =
     // ===========================
     if ($end !== false) {
         $end_where_sql = "AND (e.end <= %s OR e.recurrence_rules != '')";
         $args[] = Ai1ec_Time_Utility::to_mysql_date($end);
     }
     $wpml_join_particle = $ai1ec_localization_helper->get_wpml_table_join();
     $wpml_where_particle = $ai1ec_localization_helper->get_wpml_table_where();
     // Get the Join (filter_join) and Where (filter_where) statements based on $filter elements specified
     $ai1ec_calendar_helper->_get_filter_sql($filter);
     $query = $wpdb->prepare("SELECT *, e.post_id, e.start as start, e.end as end, e.allday, e.recurrence_rules, e.exception_rules,\n\t\t\t\te.recurrence_dates, e.exception_dates, e.venue, e.country, e.address, e.city, e.province, e.postal_code,\n\t\t\t\te.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, e.ical_feed_url, e.ical_source_url,\n\t\t\t\te.ical_organizer, e.ical_contact, e.ical_uid " . "FROM {$wpdb->posts} " . "INNER JOIN {$wpdb->prefix}ai1ec_events AS e ON e.post_id = ID " . $wpml_join_particle . $filter['filter_join'] . "WHERE post_type = '" . AI1EC_POST_TYPE . "' " . "AND post_status = 'publish' " . $wpml_where_particle . $filter['filter_where'] . $start_where_sql . $end_where_sql, $args);
     $events = $wpdb->get_results($query, ARRAY_A);
     foreach ($events as &$event) {
         $event['start'] = Ai1ec_Time_Utility::from_mysql_date($event['start']);
         $event['end'] = Ai1ec_Time_Utility::from_mysql_date($event['end']);
         try {
             $event = new Ai1ec_Event($event);
         } catch (Ai1ec_Event_Not_Found $n) {
             unset($event);
             // The event is not found, continue to the next event
             continue;
         }
         // if there are recurrence rules, include the event, else...
         if (empty($event->recurrence_rules)) {
             // if start time is set, and event start time is before the range
             // it, continue to the next event
             if ($start !== false && $event->start < $start) {
                 unset($event);
                 continue;
             }
             // if end time is set, and event end time is after
             // it, continue to the next event
             if ($end !== false && $ev->end < $end) {
                 unset($event);
                 continue;
             }
         }
     }
     return $events;
 }
 /**
  * save function
  *
  * Saves the current event data to the database. If $this->post_id exists,
  * but $update is false, creates a new record in the ai1ec_events table of
  * this event data, but does not try to create a new post. Else if $update
  * is true, updates existing event record. If $this->post_id is empty,
  * creates a new post AND record in the ai1ec_events table for this event.
  *
  * @param  bool  $update  Whether to update an existing event or create a
  *                        new one
  * @return int            The post_id of the new or existing event.
  **/
 function save($update = false)
 {
     global $wpdb, $ai1ec_events_helper, $ai1ec_exporter_controller;
     // ===========================
     // = Insert events meta data =
     // ===========================
     // Set facebook user and eid to 0 if they are not set, otherwise they will be set to '' since we use %s for big ints
     $facebook_eid = isset($this->facebook_eid) ? $this->facebook_eid : 0;
     $facebook_user = isset($this->facebook_user) ? $this->facebook_user : 0;
     $columns = array('post_id' => $this->post_id, 'start' => Ai1ec_Time_Utility::to_mysql_date($this->start), 'end' => Ai1ec_Time_Utility::to_mysql_date($this->end), 'allday' => $this->allday, 'instant_event' => $this->instant_event, 'recurrence_rules' => $this->recurrence_rules, 'exception_rules' => $this->exception_rules, 'recurrence_dates' => $this->recurrence_dates, 'exception_dates' => $this->exception_dates, 'venue' => $this->venue, 'country' => $this->country, 'address' => $this->address, 'city' => $this->city, 'province' => $this->province, 'postal_code' => $this->postal_code, 'show_map' => $this->show_map, 'contact_name' => $this->contact_name, 'contact_phone' => $this->contact_phone, 'contact_email' => $this->contact_email, 'contact_url' => $this->contact_url, 'cost' => $this->cost, 'ticket_url' => $this->ticket_url, 'ical_feed_url' => $this->ical_feed_url, 'ical_source_url' => $this->ical_source_url, 'ical_uid' => $this->ical_uid, 'show_coordinates' => $this->show_coordinates, 'latitude' => $this->latitude, 'longitude' => $this->longitude, 'facebook_eid' => $facebook_eid, 'facebook_user' => $facebook_user, 'facebook_status' => $this->facebook_status);
     $format = array('%d', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%f', '%f', '%s', '%s', '%s');
     $table_name = $wpdb->prefix . 'ai1ec_events';
     if ($this->post_id) {
         if (!$update) {
             // =========================
             // = Insert new event data =
             // =========================
             $wpdb->query($wpdb->prepare("INSERT INTO {$table_name} ( " . join(', ', array_keys($columns)) . " ) VALUES ( " . join(', ', $format) . " )", $columns));
             $ai1ec_exporter_controller->export_location($columns, false);
         } else {
             // ==============================
             // = Update existing event data =
             // ==============================
             $where = array('post_id' => $this->post_id);
             $where_escape = array('%d');
             $wpdb->update($table_name, $columns, $where, $format, $where_escape);
             $ai1ec_exporter_controller->export_location($columns, true);
         }
     } else {
         // ===================
         // = Insert new post =
         // ===================
         $this->post_id = wp_insert_post($this->post);
         $columns['post_id'] = $this->post_id;
         wp_set_post_terms($this->post_id, $this->categories, 'events_categories');
         wp_set_post_terms($this->post_id, $this->tags, 'events_tags');
         if (isset($this->feed) && isset($this->feed->feed_id)) {
             $feed_name = $this->feed->feed_url;
             // If the feed is not from an imported file, parse the url.
             if (!isset($this->feed->feed_imported_file)) {
                 $url_components = parse_url($this->feed->feed_url);
                 $feed_name = $url_components["host"];
             }
             $term = term_exists($feed_name, 'events_feeds');
             if (!$term) {
                 // term doesn't exist, create it
                 $term = wp_insert_term($feed_name, 'events_feeds', array('description' => $this->feed->feed_url));
             }
             // term_exists returns object, wp_insert_term returns array
             $term = (object) $term;
             if (isset($term->term_id)) {
                 // associate the event with the feed only if we have term id set
                 $a = wp_set_object_terms($this->post_id, (int) $term->term_id, 'events_feeds', false);
             }
         }
         // =========================
         // = Insert new event data =
         // =========================
         $wpdb->query($wpdb->prepare("INSERT INTO {$table_name} ( " . join(', ', array_keys($columns)) . " ) VALUES ( " . join(', ', $format) . " )", $columns));
         $ai1ec_exporter_controller->export_location($columns, false);
     }
     return $this->post_id;
 }
 /**
  * get_events_relative_to function
  *
  * Return all events starting after the given reference time, limiting the
  * result set to a maximum of $limit items, offset by $page_offset. A
  * negative $page_offset can be provided, which will return events *before*
  * the reference time, as expected.
  *
  * @param int $time           limit to events starting after this (local) UNIX time
  * @param int $limit          return a maximum of this number of items
  * @param int $page_offset    offset the result set by $limit times this number
  * @param array $filter       Array of filters for the events returned.
  *		                        ['cat_ids']   => non-associatative array of category IDs
  *                            ['tag_ids']   => non-associatative array of tag IDs
  *                            ['post_ids']  => non-associatative array of post IDs
  * @param int $last_day       Last day (time), that was displayed.
  *                            NOTE FROM NICOLA: be careful, if you want a query with events
  *                            that have a start date which is greater than today, pass 0 as
  *                            this parameter. If you pass false ( or pass nothing ) you end up with a query
  *                            with events that finish before today. I don't know the rationale
  *                            behind this but that's how it works
  *
  * @return array              five-element array:
  *                              ['events'] an array of matching event objects
  *                              ['prev'] true if more previous events
  *                              ['next'] true if more next events
  *                              ['date_first'] UNIX timestamp (date part) of first event
  *                              ['date_last'] UNIX timestamp (date part) of last event
  */
 function get_events_relative_to($time, $limit = 0, $page_offset = 0, $filter = array(), $last_day = false)
 {
     global $wpdb, $ai1ec_events_helper, $ai1ec_localization_helper, $ai1ec_settings;
     // Figure out what the beginning of the day is to properly query all-day
     // events; then convert to GMT time
     $bits = $ai1ec_events_helper->gmgetdate($time);
     // Even if there ARE more than 5 times the limit results - we shall not
     // try to fetch and display these, as it would crash system
     $upper_boundary = $limit;
     if ($ai1ec_settings->agenda_include_entire_last_day && false !== $last_day) {
         $upper_boundary *= 5;
     }
     // Convert timestamp to GMT time
     $time = $ai1ec_events_helper->local_to_gmt($time);
     // Query arguments
     $args = array(Ai1ec_Time_Utility::to_mysql_date($time));
     if ($page_offset >= 0) {
         $first_record = $page_offset * $limit;
     } else {
         $first_record = (-$page_offset - 1) * $limit;
     }
     // Get post status Where snippet and associated SQL arguments
     $this->_get_post_status_sql($post_status_where, $args);
     // Get the Join (filter_join) and Where (filter_where) statements based on
     // $filter elements specified
     $this->_get_filter_sql($filter);
     $wpml_join_particle = $ai1ec_localization_helper->get_wpml_table_join('p.ID');
     $wpml_where_particle = $ai1ec_localization_helper->get_wpml_table_where();
     $filter_date_clause = $page_offset >= 0 ? 'i.end >= %s ' : 'i.start < %s ';
     $order_direction = $page_offset >= 0 ? 'ASC' : 'DESC';
     if (false !== $last_day) {
         if (0 == $last_day) {
             $last_day = (int) $_SERVER['REQUEST_TIME'];
         }
         $filter_date_clause = ' i.start ';
         if ($page_offset < 0) {
             $filter_date_clause .= '<';
             $order_direction = 'DESC';
         } else {
             $filter_date_clause .= '>';
             $order_direction = 'ASC';
         }
         $filter_date_clause .= ' %s ';
         $args[0] = Ai1ec_Time_Utility::to_mysql_date($last_day);
         $first_record = 0;
     }
     $query = $wpdb->prepare('SELECT DISTINCT SQL_CALC_FOUND_ROWS p.*, e.post_id, i.id AS instance_id, ' . 'i.start AS start, ' . 'i.end AS end, ' . 'IF( e.allday, e.allday, i.end = DATE_ADD( i.start, INTERVAL 1 DAY ) ) AS allday, ' . 'e.recurrence_rules, e.exception_rules, e.instant_event, e.recurrence_dates, e.exception_dates, ' . 'e.venue, e.country, e.address, e.city, e.province, e.postal_code, ' . 'e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, ' . 'e.ical_feed_url, e.ical_source_url, e.ical_organizer, e.ical_contact, e.ical_uid ' . 'FROM ' . $wpdb->prefix . 'ai1ec_events e ' . 'INNER JOIN ' . $wpdb->posts . ' p ON e.post_id = p.ID ' . $wpml_join_particle . 'INNER JOIN ' . $wpdb->prefix . 'ai1ec_event_instances i ON e.post_id = i.post_id ' . $filter['filter_join'] . "WHERE post_type = '" . AI1EC_POST_TYPE . "' " . 'AND ' . $filter_date_clause . $wpml_where_particle . $filter['filter_where'] . $post_status_where . 'ORDER BY i.start ' . $order_direction . ', post_title ' . $order_direction . ' LIMIT ' . $first_record . ', ' . $upper_boundary, $args);
     $events = $wpdb->get_results($query, ARRAY_A);
     // Limit the number of records to convert to data-object
     $events = $this->_limit_result_set($events, $limit, false !== $last_day);
     // Reorder records if in negative page offset
     if ($page_offset < 0) {
         $events = array_reverse($events);
     }
     $date_first = $date_last = NULL;
     foreach ($events as &$event) {
         $event['start'] = Ai1ec_Time_Utility::from_mysql_date($event['start']);
         $event['end'] = Ai1ec_Time_Utility::from_mysql_date($event['end']);
         if (NULL === $date_first) {
             $date_first = $event['start'];
         }
         $date_last = $event['start'];
         $event = new Ai1ec_Event($event);
     }
     // Find out if there are more records in the current nav direction
     $more = $wpdb->get_var('SELECT FOUND_ROWS()') > $first_record + $limit;
     // Navigating in the future
     if ($page_offset > 0) {
         $prev = true;
         $next = $more;
     } elseif ($page_offset < 0) {
         $prev = $more;
         $next = true;
     } else {
         $query = $wpdb->prepare("SELECT COUNT(*) " . "FROM {$wpdb->prefix}ai1ec_events e " . "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " . "INNER JOIN {$wpdb->posts} p ON e.post_id = p.ID " . $wpml_join_particle . $filter['filter_join'] . "WHERE post_type = '" . AI1EC_POST_TYPE . "' " . "AND i.start < %s " . $wpml_where_particle . $filter['filter_where'] . $post_status_where, $args);
         $prev = $wpdb->get_var($query);
         $next = $more;
     }
     return array('events' => $events, 'prev' => $prev, 'next' => $next, 'date_first' => $date_first, 'date_last' => $date_last);
 }