/**
  * add_vcalendar_events_to_db method
  *
  * Process vcalendar instance - add events to database
  *
  * @param vcalendar $v              Calendar to retrieve data from
  * @param stdClass  $feed           Instance of feed (see Ai1ecIcs plugin)
  * @param string    $comment_status WP comment status: 'open' or 'closed'
  * @param int       $do_show_map    Map display status (DB boolean: 0 or 1)
  *
  * @return int Count of events added to database
  */
 public function add_vcalendar_events_to_db(vcalendar $v, $feed, $comment_status, $do_show_map = 0)
 {
     global $ai1ec_events_helper;
     $count = 0;
     $do_show_map = Ai1ec_Number_Utility::db_bool($do_show_map);
     $v->sort();
     // Reverse the sort order, so that RECURRENCE-IDs are listed before the
     // defining recurrence events, and therefore take precedence during
     // caching.
     $v->components = array_reverse($v->components);
     // TODO: select only VEVENT components that occur after, say, 1 month ago.
     // Maybe use $v->selectComponents(), which takes into account recurrence
     // Fetch default timezone in case individual properties don't define it
     $timezone = $v->getProperty('X-WR-TIMEZONE');
     $timezone = (string) $timezone[1];
     // go over each event
     while ($e = $v->getComponent('vevent')) {
         // Event data array.
         $data = array();
         // =====================
         // = Start & end times =
         // =====================
         $start = $e->getProperty('dtstart', 1, true);
         $end = $e->getProperty('dtend', 1, true);
         // For cases where a "VEVENT" calendar component
         // specifies a "DTSTART" property with a DATE value type but none
         // of "DTEND" nor "DURATION" property, the event duration is taken to
         // be one day.  For cases where a "VEVENT" calendar component
         // specifies a "DTSTART" property with a DATE-TIME value type but no
         // "DTEND" property, the event ends on the same calendar date and
         // time of day specified by the "DTSTART" property.
         if (empty($end)) {
             // #1 if duration is present, assign it to end time
             $end = $e->getProperty('duration', 1, true, true);
             if (empty($end)) {
                 // #2 if only DATE value is set for start, set duration to 1 day
                 if (!isset($start['value']['hour'])) {
                     $end = array('value' => array('year' => $start['value']['year'], 'month' => $start['value']['month'], 'day' => $start['value']['day'] + 1, 'hour' => 0, 'min' => 0, 'sec' => 0, 'tz' => $start['value']['tz']));
                 } else {
                     // #3 set end date to start time
                     $end = $start;
                 }
             }
         }
         $categories = $e->getProperty("CATEGORIES", false, true);
         $imported_cat = array();
         // If the user chose to preserve taxonomies during import, add categories.
         if ($categories && $feed->keep_tags_categories) {
             $imported_cat = $this->add_categories_and_tags($categories['value'], $imported_cat, false, true);
         }
         $feed_categories = $feed->feed_category;
         if (!empty($feed_categories)) {
             $imported_cat = $this->add_categories_and_tags($feed_categories, $imported_cat, false, false);
         }
         $tags = $e->getProperty("X-TAGS", false, true);
         $imported_tags = array();
         // If the user chose to preserve taxonomies during import, add tags.
         if ($tags && $feed->keep_tags_categories) {
             $imported_tags = $this->add_categories_and_tags($tags[1]['value'], $imported_tags, true, true);
         }
         $feed_tags = $feed->feed_tags;
         if (!empty($feed_tags)) {
             $imported_tags = $this->add_categories_and_tags($feed_tags, $imported_tags, true, true);
         }
         // Event is all-day if no time components are defined
         $allday = $this->_is_timeless($start['value']) && $this->_is_timeless($end['value']);
         // Also check the proprietary MS all-day field.
         $ms_allday = $e->getProperty('X-MICROSOFT-CDO-ALLDAYEVENT');
         if (!empty($ms_allday) && $ms_allday[1] == 'TRUE') {
             $allday = true;
         }
         $start = $this->time_array_to_timestamp($start, $timezone);
         $end = $this->time_array_to_timestamp($end, $timezone);
         if (false === $start || false === $end) {
             trigger_error('Failed to parse one or more dates given timezone "' . var_export($timezone, true) . '".', E_USER_WARNING);
             continue;
         }
         // If all-day, and start and end times are equal, then this event has
         // invalid end time (happens sometimes with poorly implemented iCalendar
         // exports, such as in The Event Calendar), so set end time to 1 day
         // after start time.
         if ($allday && $start === $end) {
             $end += 24 * 60 * 60;
         }
         $data += compact('start', 'end', 'allday');
         // =======================================
         // = Recurrence rules & recurrence dates =
         // =======================================
         if ($rrule = $e->createRrule()) {
             $rrule = trim(end(explode(':', $rrule)));
         }
         if ($exrule = $e->createExrule()) {
             $exrule = trim(end(explode(':', $exrule)));
         }
         if ($rdate = $e->createRdate()) {
             $rdate = trim(end(explode(':', $rdate)));
         }
         // ===================
         // = Exception dates =
         // ===================
         $exdate_array = array();
         if ($exdates = $e->createExdate()) {
             // We may have two formats:
             // one exdate with many dates ot more EXDATE rules
             $exdates = explode("EXDATE", $exdates);
             foreach ($exdates as $exd) {
                 if (empty($exd)) {
                     continue;
                 }
                 $exdate_array[] = trim(end(explode(':', $exd)));
             }
         }
         // This is the local string.
         $exdate_loc = implode(',', $exdate_array);
         $gmt_exdates = array();
         // Now we convert the string to gmt. I must do it here
         // because EXDATE:date1,date2,date3 must be parsed
         if (!empty($exdate_loc)) {
             foreach (explode(',', $exdate_loc) as $date) {
                 // If the date is > 8 char that's a datetime, we just want the
                 // date part for the exclusion rules
                 if (strlen($date) > 8) {
                     $date = substr($date, 0, 8);
                 }
                 $gmt_exdates[] = $ai1ec_events_helper->exception_dates_to_gmt($date);
             }
         }
         $exdate = implode(',', $gmt_exdates);
         // ========================
         // = Latitude & longitude =
         // ========================
         $latitude = $longitude = NULL;
         $geo_tag = $e->getProperty('geo');
         if (is_array($geo_tag)) {
             if (isset($geo_tag['latitude']) && isset($geo_tag['longitude'])) {
                 $latitude = (double) $geo_tag['latitude'];
                 $longitude = (double) $geo_tag['longitude'];
             }
         } else {
             if (!empty($geo_tag) && false !== strpos($geo_tag, ';')) {
                 list($latitude, $longitude) = explode(';', $geo_tag, 2);
                 $latitude = (double) $latitude;
                 $longitude = (double) $longitude;
             }
         }
         unset($geo_tag);
         if (NULL !== $latitude) {
             $data += compact('latitude', 'longitude');
             // Check the input coordinates checkbox, otherwise lat/long data
             // is not present on the edit event page
             $data['show_coordinates'] = 1;
         }
         // ===================
         // = Venue & address =
         // ===================
         $address = $venue = '';
         $location = $e->getProperty('location');
         $matches = array();
         // This regexp matches a venue / address in the format
         // "venue @ address" or "venue - address".
         preg_match('/\\s*(.*\\S)\\s+[\\-@]\\s+(.*)\\s*/', $location, $matches);
         // if there is no match, it's not a combined venue + address
         if (empty($matches)) {
             // if there is a comma, probably it's an address
             if (false === strpos($location, ',')) {
                 $venue = $location;
             } else {
                 $address = $location;
             }
         } else {
             $venue = isset($matches[1]) ? $matches[1] : '';
             $address = isset($matches[2]) ? $matches[2] : '';
         }
         // =====================================================
         // = Set show map status based on presence of location =
         // =====================================================
         if (1 === $do_show_map && NULL === $latitude && empty($address)) {
             $do_show_map = 0;
         }
         // ==================
         // = Cost & tickets =
         // ==================
         $cost = $e->getProperty('X-COST');
         $cost = $cost ? $cost[1] : '';
         $ticket_url = $e->getProperty('X-TICKETS-URL');
         $ticket_url = $ticket_url ? $ticket_url[1] : '';
         // ===============================
         // = Contact name, phone, e-mail =
         // ===============================
         $organizer = $e->getProperty('organizer');
         if ('MAILTO:' === substr($organizer, 0, 7) && false === strpos($organizer, '@')) {
             $organizer = substr($organizer, 7);
         }
         $contact = $e->getProperty('contact');
         $elements = explode(';', $contact, 4);
         foreach ($elements as $el) {
             $el = trim($el);
             // Detect e-mail address.
             if (false !== strpos($el, '@')) {
                 $data['contact_email'] = $el;
             } elseif (false !== strpos($el, '://')) {
                 $data['contact_url'] = $el;
             } elseif (preg_match('/\\d/', $el)) {
                 $data['contact_phone'] = $el;
             } else {
                 $data['contact_name'] = $el;
             }
         }
         if (!isset($data['contact_name']) || !$data['contact_name']) {
             // If no contact name, default to organizer property.
             $data['contact_name'] = $organizer;
         }
         // Store yet-unsaved values to the $data array.
         $data += array('recurrence_rules' => $rrule, 'exception_rules' => $exrule, 'recurrence_dates' => $rdate, 'exception_dates' => $exdate, 'venue' => $venue, 'address' => $address, 'cost' => $cost, 'ticket_url' => $ticket_url, 'show_map' => $do_show_map, 'ical_feed_url' => $feed->feed_url, 'ical_source_url' => $e->getProperty('url'), 'ical_organizer' => $organizer, 'ical_contact' => $contact, 'ical_uid' => $e->getProperty('uid'), 'categories' => array_keys($imported_cat), 'tags' => array_keys($imported_tags), 'feed' => $feed, 'post' => array('post_status' => 'publish', 'comment_status' => $comment_status, 'post_type' => AI1EC_POST_TYPE, 'post_author' => 1, 'post_title' => $e->getProperty('summary'), 'post_content' => stripslashes(str_replace('\\n', "\n", $e->getProperty('description')))));
         // Create event object.
         $event = new Ai1ec_Event($data);
         // TODO: when singular events change their times in an ICS feed from one
         // import to another, the matching_event_id is null, which is wrong. We
         // want to match that event that previously had a different time.
         // However, we also want the function to NOT return a matching event in
         // the case of recurring events, and different events with different
         // RECURRENCE-IDs... ponder how to solve this.. may require saving the
         // RECURRENCE-ID as another field in the database.
         $matching_event_id = $ai1ec_events_helper->get_matching_event_id($event->ical_uid, $event->ical_feed_url, $event->start, !empty($event->recurrence_rules));
         if (NULL === $matching_event_id) {
             // =================================================
             // = Event was not found, so store it and the post =
             // =================================================
             $event->save();
         } else {
             // ======================================================
             // = Event was found, let's store the new event details =
             // ======================================================
             // Update the post
             $post = get_post($matching_event_id);
             $post->post_title = $event->post->post_title;
             $post->post_content = $event->post->post_content;
             wp_update_post($post);
             // Update the event
             $event->post_id = $matching_event_id;
             $event->post = $post;
             $event->save(true);
             // Delete event's cache
             $ai1ec_events_helper->delete_event_cache($matching_event_id);
         }
         // Regenerate event's cache
         $ai1ec_events_helper->cache_event($event);
         $count++;
     }
     return $count;
 }
 /**
  * add_ics_feed function
  *
  * Adds submitted ics feed to the database
  *
  * @return string JSON output
  **/
 public function add_ics_feed()
 {
     global $ai1ec_view_helper, $wpdb;
     $table_name = $wpdb->prefix . 'ai1ec_event_feeds';
     $entry = array('feed_url' => $_REQUEST['feed_url'], 'feed_category' => $_REQUEST['feed_category'], 'feed_tags' => $_REQUEST['feed_tags'], 'comments_enabled' => Ai1ec_Number_Utility::db_bool($_REQUEST['comments_enabled']), 'map_display_enabled' => Ai1ec_Number_Utility::db_bool($_REQUEST['map_display_enabled']));
     $format = array('%s', '%d', '%s', '%d', '%d');
     $wpdb->insert($table_name, $entry, $format);
     $feed_id = $wpdb->insert_id;
     ob_start();
     $feed_category = get_term($_REQUEST['feed_category'], 'events_categories');
     $args = array('feed_url' => $_REQUEST['feed_url'], 'event_category' => $feed_category->name, 'tags' => $_REQUEST['feed_tags'], 'feed_id' => $feed_id, 'comments_enabled' => (bool) intval($_REQUEST['comments_enabled']), 'map_display_enabled' => (bool) intval($_REQUEST['map_display_enabled']), 'events' => 0);
     // display added feed row
     $ai1ec_view_helper->display_admin('feed_row.php', $args);
     $output = ob_get_contents();
     ob_end_clean();
     $output = array("error" => 0, "message" => stripslashes($output));
     echo json_encode($output);
     exit;
 }
 /**
  * toggle_regenerate_status method
  *
  * Change event regeneration requirement status.
  *
  * @param int $post_id ID of event, to change regeneration status for
  * @param int $status  Target status, to change to [optional=1]
  *
  * @return bool Success
  */
 public function toggle_regenerate_status($post_id, $status = 1)
 {
     global $wpdb;
     $post_id = absint($post_id);
     $status = Ai1ec_Number_Utility::db_bool($status);
     $sql_query = 'UPDATE ' . $wpdb->prefix . 'ai1ec_events ' . 'SET force_regenerate = %d WHERE post_id = %d';
     $sql_query = $wpdb->prepare($sql_query, $status, $post_id);
     if (is_wp_error($wpdb->query($sql_query))) {
         return false;
     }
     return true;
 }