/** * update function * * Called when a user submits the widget configuration form. The data should * be validated and returned. * * @param array $new_instance The new data that was submitted. * @param array $old_instance The widget's old data. * @return array The new data to save for this widget instance. */ function update($new_instance, $old_instance) { // Save existing data as a base to modify with new data $instance = $old_instance; $instance['title'] = strip_tags($new_instance['title']); $instance['events_per_page'] = Ai1ec_Number_Utility::index($new_instance['events_per_page'], 1, 1); $instance['days_per_page'] = Ai1ec_Number_Utility::index($new_instance['days_per_page'], 1, 1); $instance['events_seek_type'] = $this->_valid_seek_type($new_instance['events_seek_type']); $instance['show_subscribe_buttons'] = $new_instance['show_subscribe_buttons'] ? true : false; $instance['show_calendar_button'] = $new_instance['show_calendar_button'] ? true : false; $instance['hide_on_calendar_page'] = $new_instance['hide_on_calendar_page'] ? true : false; // For limits, set the limit to False if no IDs were selected, or set the respective IDs to empty if "limit by" was unchecked $instance['limit_by_cat'] = false; $instance['event_cat_ids'] = array(); if (isset($new_instance['event_cat_ids']) && $new_instance['event_cat_ids'] != false) { $instance['limit_by_cat'] = true; } if (isset($new_instance['limit_by_cat']) && $new_instance['limit_by_cat'] != false) { $instance['limit_by_cat'] = true; } if (isset($new_instance['event_cat_ids']) && $instance['limit_by_cat'] === true) { $instance['event_cat_ids'] = $new_instance['event_cat_ids']; } $instance['limit_by_tag'] = false; $instance['event_tag_ids'] = array(); if (isset($new_instance['event_tag_ids']) && $new_instance['event_tag_ids'] != false) { $instance['limit_by_tag'] = true; } if (isset($new_instance['limit_by_tag']) && $new_instance['limit_by_tag'] != false) { $instance['limit_by_tag'] = true; } if (isset($new_instance['event_tag_ids']) && $instance['limit_by_tag'] === true) { $instance['event_tag_ids'] = $new_instance['event_tag_ids']; } $instance['limit_by_post'] = false; $instance['event_post_ids'] = array(); if (isset($new_instance['event_post_ids']) && $new_instance['event_post_ids'] != false) { $instance['limit_by_post'] = true; } if (isset($new_instance['limit_by_post']) && $new_instance['limit_by_post'] != false) { $instance['limit_by_post'] = true; } if (isset($new_instance['event_post_ids']) && $instance['limit_by_post'] === true) { $instance['event_post_ids'] = $new_instance['event_post_ids']; } return $instance; }
/** * 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; }
/** * 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; }
/** * filter_by_terms function * * Returns a subset of post IDs from the given set of post IDs that have any * of the given taxonomy term IDs. This is actually useful for all posts and * taxonomies in general, not just event posts and event-specific taxonomies. * * @param array|string $post_ids Post IDs as an array of ints or * comma-separated string * @param array|string $term_ids Term IDs as an array of ints or * comma-separated string * * @return array Filtered post IDs as an array of ints */ public function filter_by_terms($post_ids, $term_ids) { global $wpdb; // =============================================== // = Sanitize provided IDs against SQL injection = // =============================================== $post_ids = implode(',', Ai1ec_Number_Utility::convert_to_int_list(',', $post_ids)); $term_ids = implode(',', Ai1ec_Number_Utility::convert_to_int_list(',', $term_ids)); $query = 'SELECT DISTINCT p.ID ' . 'FROM ' . $wpdb->posts . ' p ' . 'INNER JOIN ' . $wpdb->term_relationships . ' tr ON p.ID = tr.object_id ' . 'INNER JOIN ' . $wpdb->term_taxonomy . ' tt ON tr.term_taxonomy_id = tt.term_taxonomy_id ' . 'WHERE p.ID IN ( ' . $post_ids . ' ) ' . 'AND tt.term_id IN ( ' . $term_ids . ' )'; return $wpdb->get_col($query); }
/** * export_events function * * Export events * * @return void **/ function export_events() { global $ai1ec_events_helper, $ai1ec_exporter_helper, $ai1ec_localization_helper; $ai1ec_cat_ids = !empty($_REQUEST['ai1ec_cat_ids']) ? $_REQUEST['ai1ec_cat_ids'] : false; $ai1ec_tag_ids = !empty($_REQUEST['ai1ec_tag_ids']) ? $_REQUEST['ai1ec_tag_ids'] : false; $ai1ec_post_ids = !empty($_REQUEST['ai1ec_post_ids']) ? $_REQUEST['ai1ec_post_ids'] : false; if (!empty($_REQUEST['lang'])) { $ai1ec_localization_helper->set_language($_REQUEST['lang']); } $filter = array(); if ($ai1ec_cat_ids) { $filter['cat_ids'] = Ai1ec_Number_Utility::convert_to_int_list(',', $ai1ec_cat_ids); } if ($ai1ec_tag_ids) { $filter['tag_ids'] = Ai1ec_Number_Utility::convert_to_int_list(',', $ai1ec_tag_ids); } if ($ai1ec_post_ids) { $filter['post_ids'] = Ai1ec_Number_Utility::convert_to_int_list(',', $ai1ec_post_ids); } // when exporting events by post_id, do not look up the event's start/end date/time $start = $ai1ec_post_ids !== false ? false : Ai1ec_Time_Utility::current_time(true) - 24 * 60 * 60; // Include any events ending today $end = false; $c = new vcalendar(); $c->setProperty('calscale', 'GREGORIAN'); $c->setProperty('method', 'PUBLISH'); // if no post id are specified do not export those properties // as they would create a new calendar in outlook. // a user reported this in AIOEC-982 and said this would fix it if (false === $ai1ec_post_ids) { $c->setProperty('X-WR-CALNAME', get_bloginfo('name')); $c->setProperty('X-WR-CALDESC', get_bloginfo('description')); } $c->setProperty('X-FROM-URL', home_url()); // Timezone setup $tz = Ai1ec_Meta::get_option('timezone_string'); if ($tz) { $c->setProperty('X-WR-TIMEZONE', $tz); $tz_xprops = array('X-LIC-LOCATION' => $tz); iCalUtilityFunctions::createTimezone($c, $tz, $tz_xprops); } $events = $ai1ec_events_helper->get_matching_events($start, $end, $filter); foreach ($events as $event) { $ai1ec_exporter_helper->insert_event_in_calendar($event, $c, $export = true); } $str = ltrim($c->createCalendar()); header('Content-type: text/calendar; charset=utf-8'); echo $str; exit; }