/** * import_the_events_calendar function * * Import events from The Events Calendar into Ai1ec. * * @return void **/ function import_the_events_calendar() { global $ai1ec_view_helper, $ai1ec_events_helper; $args = array('post_type' => 'post', 'numberposts' => -1, 'meta_key' => '_isEvent', 'meta_value' => 'yes'); $posts = get_posts($args); $imported_events = 0; foreach ($posts as $post) { $event = new Ai1ec_Event(null); $postmeta = get_post_custom($post->ID); // Need this to offset dates coming from The Events Calendar $gm_diff = mktime(0) - gmmktime(0); $event->allday = $postmeta['_EventAllDay'][0] == 'yes' || $postmeta['_EventAllDay'][0] == 1; $event->start = strtotime($postmeta['_EventStartDate'][0]) - $gm_diff; $event->end = strtotime($postmeta['_EventEndDate'][0]) - $gm_diff; // If all-day event, align start/end to start/end of day if ($event->allday) { $event->start = $ai1ec_events_helper->gmgetdate($event->start); $event->start = gmmktime(0, 0, 0, $event->start['mon'], $event->start['mday'], $event->start['year']); $event->end = $ai1ec_events_helper->gmgetdate($event->end); $event->end = gmmktime(0, 0, 0, $event->end['mon'], $event->end['mday'], $event->end['year']); } // Finally, convert to GMT storage format $event->start = $ai1ec_events_helper->local_to_gmt($event->start); $event->end = $ai1ec_events_helper->local_to_gmt($event->end); // Bug in The Events Calendar where some all-day events start and end at the same time if ($event->allday && $event->end - $event->start < 24 * 60 * 60) { $event->end = $event->start + 24 * 60 * 60; } $event->venue = $postmeta['_EventVenue'][0]; $event->country = $postmeta['_EventCountry'][0]; $event->city = $postmeta['_EventCity'][0]; $event->province = $postmeta['_EventState'][0]; $event->postal_code = $postmeta['_EventZip'][0]; $event->address = array(); if ($postmeta['$_EventAddress']) { $event->address[] = $postmeta['$_EventAddress']; } if ($event->city) { $event->address[] = $event->city; } if ($event->province) { $event->address[] = $event->province; } if ($event->postal_code) { $event->address[] = $event->postal_code; } if ($event->country) { $event->address[] = $event->country; } $event->address = join(', ', $event->address); $event->show_map = $postmeta['_EventShowMapLink'][0] == 'true' || $postmeta['_EventShowMap'][0] == 'true'; $event->cost = $postmeta['_EventCost'][0]; $event->contact_phone = $postmeta['_EventPhone'][0]; $event->post = get_object_vars($post); $event->post["post_type"] = AI1EC_POST_TYPE; unset($event->post["ID"]); // Transfer post categories => event categories, post tags => event tags $terms = wp_get_post_terms($post->ID, array('category', 'post_tag')); $event->categories = array(); $event->tags = array(); foreach ($terms as $term) { switch ($term->taxonomy) { case 'category': // Ignore special "Events" category by The Events Calendar if ($term->name == 'Events') { break; } // Need to find out the category ID, if it exists. $event_term = get_term_by('name', $term->name, 'events_categories'); // If no category exists, create it. if ($event_term === false) { $event_term = (object) wp_insert_term($term->name, 'events_categories', array('description' => $term->description, 'slug' => $term->slug)); } $event->categories[] = $event_term->term_id; break; case 'post_tag': // For some reason tag-like taxonomies are treated differently; term // IDs cannot be used; instead the actual term name must be appended $event->tags[] = $term->name; break; } } $post_id = $event->save(); $ai1ec_events_helper->cache_event($event, $post_id); $imported_events++; } $ai1ec_view_helper->display_admin("import.php", array('imported_events' => $imported_events)); }
/** * Convert an event from a feed into a new Ai1ec_Event object and add it to * the calendar. * * @param Ai1ec_Event $event Event object. * @param vcalendar $calendar Calendar object. * @param bool $export States whether events are created for export. * @param array $params Additional parameters for export. * * @return void */ protected function _insert_event_in_calendar(Ai1ec_Event $event, vcalendar $calendar, $export = false, array $params = array()) { $tz = $this->_registry->get('date.timezone')->get_default_timezone(); $e =& $calendar->newComponent('vevent'); $uid = ''; if ($event->get('ical_uid')) { $uid = addcslashes($event->get('ical_uid'), "\\;,\n"); } else { $uid = $event->get_uid(); $event->set('ical_uid', $uid); $event->save(true); } $e->setProperty('uid', $this->_sanitize_value($uid)); $e->setProperty('url', get_permalink($event->get('post_id'))); // ========================= // = Summary & description = // ========================= $e->setProperty('summary', $this->_sanitize_value(html_entity_decode(apply_filters('the_title', $event->get('post')->post_title), ENT_QUOTES, 'UTF-8'))); $content = apply_filters('the_content', $event->get('post')->post_content); $content = str_replace(']]>', ']]>', $content); $content = html_entity_decode($content, ENT_QUOTES, 'UTF-8'); // Prepend featured image if available. $size = null; $avatar = $this->_registry->get('view.event.avatar'); if ($img_url = $avatar->get_post_thumbnail_url($event, $size)) { $content = '<div class="ai1ec-event-avatar alignleft timely"><img src="' . esc_attr($img_url) . '" width="' . $size[0] . '" height="' . $size[1] . '" /></div>' . $content; } if (isset($params['no_html']) && $params['no_html']) { $e->setProperty('description', $this->_sanitize_value(strip_tags(strip_shortcodes($content)))); if (!empty($content)) { $html_content = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">\\n' . '<HTML>\\n<HEAD>\\n<TITLE></TITLE>\\n</HEAD>\\n<BODY>' . $content . '</BODY></HTML>'; $e->setProperty('X-ALT-DESC', $this->_sanitize_value($html_content), array('FMTTYPE' => 'text/html')); unset($html_content); } } else { $e->setProperty('description', $this->_sanitize_value($content)); } $revision = (int) current(array_keys(wp_get_post_revisions($event->get('post_id')))); $e->setProperty('sequence', $revision); // ===================== // = Start & end times = // ===================== $dtstartstring = ''; $dtstart = $dtend = array(); if ($event->is_allday()) { $dtstart['VALUE'] = $dtend['VALUE'] = 'DATE'; // For exporting all day events, don't set a timezone if ($tz && !$export) { $dtstart['TZID'] = $dtend['TZID'] = $tz; } // For exportin' all day events, only set the date not the time if ($export) { $e->setProperty('dtstart', $this->_sanitize_value($event->get('start')->format('Ymd')), $dtstart); $e->setProperty('dtend', $this->_sanitize_value($event->get('end')->format('Ymd')), $dtend); } else { $e->setProperty('dtstart', $this->_sanitize_value($event->get('start')->format("Ymd\\T")), $dtstart); $e->setProperty('dtend', $this->_sanitize_value($event->get('end')->format("Ymd\\T")), $dtend); } } else { if ($tz) { $dtstart['TZID'] = $dtend['TZID'] = $tz; } // This is used later. $dtstartstring = $event->get('start')->format("Ymd\\THis"); $e->setProperty('dtstart', $this->_sanitize_value($dtstartstring), $dtstart); $e->setProperty('dtend', $this->_sanitize_value($event->get('end')->format("Ymd\\THis")), $dtend); } // ======================== // = Latitude & longitude = // ======================== if (floatval($event->get('latitude')) || floatval($event->get('longitude'))) { $e->setProperty('geo', $event->get('latitude'), $event->get('longitude')); } // =================== // = Venue & address = // =================== if ($event->get('venue') || $event->get('address')) { $location = array($event->get('venue'), $event->get('address')); $location = array_filter($location); $location = implode(' @ ', $location); $e->setProperty('location', $this->_sanitize_value($location)); } $categories = array(); $language = get_bloginfo('language'); foreach ($this->_taxonomy_model->get_post_categories($event->get('post_id')) as $cat) { $categories[] = $cat->name; } $e->setProperty('categories', implode(',', $categories), array("LANGUAGE" => $language)); $tags = array(); foreach ($this->_taxonomy_model->get_post_tags($event->get('post_id')) as $tag) { $tags[] = $tag->name; } if (!empty($tags)) { $e->setProperty('X-TAGS', implode(',', $tags), array("LANGUAGE" => $language)); } // ================== // = Cost & tickets = // ================== if ($event->get('cost')) { $e->setProperty('X-COST', $this->_sanitize_value($event->get('cost'))); } if ($event->get('ticket_url')) { $e->setProperty('X-TICKETS-URL', $this->_sanitize_value($event->get_nonloggable_url($event->get('ticket_url')))); } // ==================================== // = Contact name, phone, e-mail, URL = // ==================================== $contact = array($event->get('contact_name'), $event->get('contact_phone'), $event->get('contact_email'), $event->get_nonloggable_url($event->get('contact_url'))); $contact = array_filter($contact); $contact = implode('; ', $contact); $e->setProperty('contact', $this->_sanitize_value($contact)); // ==================== // = Recurrence rules = // ==================== $rrule = array(); $recurrence = $event->get('recurrence_rules'); if (!empty($recurrence)) { $rules = array(); foreach (explode(';', $event->get('recurrence_rules')) as $v) { if (strpos($v, '=') === false) { continue; } list($k, $v) = explode('=', $v); $k = strtoupper($k); // If $v is a comma-separated list, turn it into array for iCalcreator switch ($k) { case 'BYSECOND': case 'BYMINUTE': case 'BYHOUR': case 'BYDAY': case 'BYMONTHDAY': case 'BYYEARDAY': case 'BYWEEKNO': case 'BYMONTH': case 'BYSETPOS': $exploded = explode(',', $v); break; default: $exploded = $v; break; } // iCalcreator requires a more complex array structure for BYDAY... if ($k == 'BYDAY') { $v = array(); foreach ($exploded as $day) { $v[] = array('DAY' => $day); } } else { $v = $exploded; } $rrule[$k] = $v; } } // =================== // = Exception rules = // =================== $exceptions = $event->get('exception_rules'); $exrule = array(); if (!empty($exceptions)) { $rules = array(); foreach (explode(';', $exceptions) as $v) { if (strpos($v, '=') === false) { continue; } list($k, $v) = explode('=', $v); $k = strtoupper($k); // If $v is a comma-separated list, turn it into array for iCalcreator switch ($k) { case 'BYSECOND': case 'BYMINUTE': case 'BYHOUR': case 'BYDAY': case 'BYMONTHDAY': case 'BYYEARDAY': case 'BYWEEKNO': case 'BYMONTH': case 'BYSETPOS': $exploded = explode(',', $v); break; default: $exploded = $v; break; } // iCalcreator requires a more complex array structure for BYDAY... if ($k == 'BYDAY') { $v = array(); foreach ($exploded as $day) { $v[] = array('DAY' => $day); } } else { $v = $exploded; } $exrule[$k] = $v; } } // add rrule to exported calendar if (!empty($rrule)) { $e->setProperty('rrule', $this->_sanitize_value($rrule)); } // add exrule to exported calendar if (!empty($exrule)) { $e->setProperty('exrule', $this->_sanitize_value($exrule)); } // =================== // = Exception dates = // =================== // For all day events that use a date as DTSTART, date must be supplied // For other other events which use DATETIME, we must use that as well // We must also match the exact starting time $exception_dates = $event->get('exception_dates'); if (!empty($exception_dates)) { $params = array('VALUE' => 'DATE-TIME', 'TZID' => $tz); $dt_suffix = $event->get('start')->format('\\THis'); foreach (explode(',', $exception_dates) as $exdate) { $exdate = $this->_registry->get('date.time', $exdate)->format('Ymd'); $e->setProperty('exdate', array($exdate . $dt_suffix), $params); } } return $calendar; }
/** * Handle AJAX request for submission of front-end create event form. * * @return null */ public function submit_front_end_create_event_form() { global $ai1ec_view_helper, $ai1ec_calendar_helper, $ai1ec_settings, $ai1ec_events_helper; $error = false; $html = ''; $default_error_msg = __('There was an error creating your event.', AI1EC_PLUGIN_NAME) . ' ' . __('Please try again or contact the site administrator for help.', AI1EC_PLUGIN_NAME); $valid = $this->validate_front_end_create_event_form($message); // If valid submission, proceed with event creation. if ($valid) { // Determine post publish status. if (current_user_can('publish_ai1ec_events')) { $post_status = 'publish'; } else { if (current_user_can('edit_ai1ec_events')) { $post_status = 'pending'; } else { if ($ai1ec_settings->allow_anonymous_submissions) { $post_status = 'pending'; } } } // Strip slashes if ridiculous PHP setting magic_quotes_gpc is enabled. if (get_magic_quotes_gpc()) { foreach ($_POST as &$param) { $param = stripslashes($param); } } // Build post array from submitted data. $post = array('post_type' => AI1EC_POST_TYPE, 'post_author' => get_current_user_id(), 'post_title' => $_POST['post_title'], 'post_content' => $_POST['post_content'], 'post_status' => $post_status); // Copy posted event data to new empty event object. $event = new Ai1ec_Event(); $event->post = $post; $event->categories = isset($_POST['ai1ec_categories']) ? implode(',', $_POST['ai1ec_categories']) : ''; $event->tags = isset($_POST['ai1ec_tags']) ? $_POST['ai1ec_tags'] : ''; $event->allday = isset($_POST['ai1ec_all_day_event']) ? (bool) $_POST['ai1ec_all_day_event'] : 0; $event->instant_event = isset($_POST['ai1ec_instant_event']) ? (bool) $_POST['ai1ec_instant_event'] : 0; $event->start = isset($_POST['ai1ec_start_time']) ? $_POST['ai1ec_start_time'] : ''; if ($event->instant_event) { $event->end = $event->start + 1800; } else { $event->end = isset($_POST['ai1ec_end_time']) ? $_POST['ai1ec_end_time'] : ''; } $event->address = isset($_POST['ai1ec_address']) ? $_POST['ai1ec_address'] : ''; $event->show_map = isset($_POST['ai1ec_google_map']) ? (bool) $_POST['ai1ec_google_map'] : 0; // Save the event to the database. try { $event->save(); $ai1ec_events_helper->cache_event($event); // Check if uploads are enabled and there is an uploaded file. if ((is_user_logged_in() || $ai1ec_settings->allow_anonymous_submissions && $ai1ec_settings->allow_anonymous_uploads) && !empty($_FILES['ai1ec_image']['name'])) { require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; $attach_id = media_handle_upload('ai1ec_image', $event->post_id); if (is_int($attach_id)) { update_post_meta($event->post_id, '_thumbnail_id', $attach_id); } } if (current_user_can('publish_ai1ec_events')) { $message = sprintf(__('Thank you for your submission. Your event <em>%s</em> was published successfully.', AI1EC_PLUGIN_NAME), $post['post_title']); $link_text = __('View Your Event', AI1EC_PLUGIN_NAME); $link_url = get_permalink($event->post_id); } else { $message = sprintf(__('Thank you for your submission. Your event <em>%s</em> will be reviewed and published once approved.', AI1EC_PLUGIN_NAME), $post['post_title']); $link_text = __('Back to Calendar', AI1EC_PLUGIN_NAME); $link_url = $ai1ec_calendar_helper->get_calendar_url(); } } catch (Exception $e) { trigger_error(sprintf(__('There was an error during event creation: %s', AI1EC_PLUGIN_NAME), $e->getMessage()), E_USER_WARNING); $error = true; $message = $default_error_msg; } $args = array('message_type' => $error ? 'error' : 'success', 'message' => $message, 'link_text' => $link_text, 'link_url' => $link_url); $html = $ai1ec_view_helper->get_theme_view('create-event-message.php', $args); } else { $error = true; } $response = array('error' => $error, 'message' => $message, 'html' => $html); $ai1ec_view_helper->xml_response($response); }
/** * 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; }
/** * Convert an event from a feed into a new Ai1ec_Event object and add it to * the calendar. * * @param Ai1ec_Event $event Event object. * @param vcalendar $calendar Calendar object. * @param bool $export States whether events are created for export. * @param array $params Additional parameters for export. * * @return void */ protected function _insert_event_in_calendar(Ai1ec_Event $event, vcalendar $calendar, $export = false, array $params = array()) { $tz = $this->_registry->get('date.timezone')->get_default_timezone(); $e =& $calendar->newComponent('vevent'); $uid = ''; if ($event->get('ical_uid')) { $uid = addcslashes($event->get('ical_uid'), "\\;,\n"); } else { $uid = $event->get_uid(); $event->set('ical_uid', $uid); $event->save(true); } $e->setProperty('uid', $this->_sanitize_value($uid)); $e->setProperty('url', get_permalink($event->get('post_id'))); // ========================= // = Summary & description = // ========================= $e->setProperty('summary', $this->_sanitize_value(html_entity_decode(apply_filters('the_title', $event->get('post')->post_title), ENT_QUOTES, 'UTF-8'))); $content = apply_filters('ai1ec_the_content', apply_filters('the_content', $event->get('post')->post_content)); $post_meta_values = get_post_meta($event->get('post_id'), '', false); $cost_type = null; if ($post_meta_values) { foreach ($post_meta_values as $key => $value) { if ('_ai1ec_cost_type' === $key) { $cost_type = $value[0]; } if (isset($params['xml']) && $params['xml'] && false !== preg_match('/^x\\-meta\\-/i', $key)) { $e->setProperty($key, $this->_sanitize_value($value)); } } } if (false === ai1ec_is_blank($cost_type)) { $e->setProperty('X-COST-TYPE', $this->_sanitize_value($cost_type)); } $url = ''; $api = $this->_registry->get('model.api.api-ticketing'); $api_event_id = $api->get_api_event_id($event->get('post_id')); if ($api_event_id) { //getting all necessary informations that will be necessary on imported ticket events $e->setProperty('X-API-EVENT-ID', $api_event_id); $e->setProperty('X-API-URL', $api->get_api_event_url($event->get('post_id'))); $e->setProperty('X-CHECKOUT-URL', $api->get_api_event_checkout_url($event->get('post_id'))); $e->setProperty('X-API-EVENT-CURRENCY', $api->get_api_event_currency($event->get('post_id'))); } else { if ($event->get('ticket_url')) { $url = $event->get('ticket_url'); } } //Adding Ticket URL to the Description field if (false === ai1ec_is_blank($url)) { $content = $this->_remove_ticket_url($content); $content = $content . '<p>' . __('Tickets: ', AI1EC_PLUGIN_NAME) . '<a class="ai1ec-ticket-url-exported" href="' . $url . '">' . $url . '</a>.</p>'; } $content = str_replace(']]>', ']]>', $content); $content = html_entity_decode($content, ENT_QUOTES, 'UTF-8'); // Prepend featured image if available. $size = null; $avatar = $this->_registry->get('view.event.avatar'); $matches = $avatar->get_image_from_content($content); // if no img is already present - add thumbnail if (empty($matches)) { $post_id = get_post_thumbnail_id($event->get('post_id')); $images = null; $added = null; foreach (array('thumbnail', 'medium', 'large', 'full') as $_size) { $attributes = wp_get_attachment_image_src($post_id, $_size); if (false !== $attributes) { $key_str = sprintf('%d_%d', $attributes[1], $attributes[2]); if (null === $added || false === isset($added[$key_str])) { $added[$key_str] = true; array_unshift($attributes, $_size); $images[] = implode(';', $attributes); } } } if (null !== $images) { $e->setProperty('X-WP-IMAGES-URL', $this->_sanitize_value(implode(',', $images))); } if ($img_url = $avatar->get_post_thumbnail_url($event, $size)) { $content = '<div class="ai1ec-event-avatar alignleft timely"><img src="' . esc_attr($img_url) . '" width="' . $size[0] . '" height="' . $size[1] . '" /></div>' . $content; } } if (isset($params['no_html']) && $params['no_html']) { $e->setProperty('description', $this->_sanitize_value(strip_tags(strip_shortcodes($content)))); if (!empty($content)) { $html_content = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">\\n' . '<HTML>\\n<HEAD>\\n<TITLE></TITLE>\\n</HEAD>\\n<BODY>' . $content . '</BODY></HTML>'; $e->setProperty('X-ALT-DESC', $this->_sanitize_value($html_content), array('FMTTYPE' => 'text/html')); unset($html_content); } } else { $e->setProperty('description', $this->_sanitize_value($content)); } $revision = (int) current(array_keys(wp_get_post_revisions($event->get('post_id')))); $e->setProperty('sequence', $revision); // ===================== // = Start & end times = // ===================== $dtstartstring = ''; $dtstart = $dtend = array(); if ($event->is_allday()) { $dtstart['VALUE'] = $dtend['VALUE'] = 'DATE'; // For exporting all day events, don't set a timezone if ($tz && !$export) { $dtstart['TZID'] = $dtend['TZID'] = $tz; } // For exportin' all day events, only set the date not the time if ($export) { $e->setProperty('dtstart', $this->_sanitize_value($event->get('start')->format('Ymd')), $dtstart); $e->setProperty('dtend', $this->_sanitize_value($event->get('end')->format('Ymd')), $dtend); } else { $e->setProperty('dtstart', $this->_sanitize_value($event->get('start')->format("Ymd\\T")), $dtstart); $e->setProperty('dtend', $this->_sanitize_value($event->get('end')->format("Ymd\\T")), $dtend); } } else { if ($tz) { $dtstart['TZID'] = $dtend['TZID'] = $tz; } // This is used later. $dtstartstring = $event->get('start')->format("Ymd\\THis"); $e->setProperty('dtstart', $this->_sanitize_value($dtstartstring), $dtstart); if (false === (bool) $event->get('instant_event')) { $e->setProperty('dtend', $this->_sanitize_value($event->get('end')->format("Ymd\\THis")), $dtend); } } // ======================== // = Latitude & longitude = // ======================== if (floatval($event->get('latitude')) || floatval($event->get('longitude'))) { $e->setProperty('geo', $event->get('latitude'), $event->get('longitude')); } // =================== // = Venue & address = // =================== if ($event->get('venue') || $event->get('address')) { $location = array($event->get('venue'), $event->get('address')); $location = array_filter($location); $location = implode(' @ ', $location); $e->setProperty('location', $this->_sanitize_value($location)); } $categories = array(); $language = get_bloginfo('language'); foreach ($this->_taxonomy_model->get_post_categories($event->get('post_id')) as $cat) { if ('events_categories' === $cat->taxonomy) { $categories[] = $cat->name; } } $e->setProperty('categories', implode(',', $categories), array("LANGUAGE" => $language)); $tags = array(); foreach ($this->_taxonomy_model->get_post_tags($event->get('post_id')) as $tag) { $tags[] = $tag->name; } if (!empty($tags)) { $e->setProperty('X-TAGS', implode(',', $tags), array("LANGUAGE" => $language)); } // ================== // = Cost & tickets = // ================== if ($event->get('cost')) { $e->setProperty('X-COST', $this->_sanitize_value($event->get('cost'))); } if ($event->get('ticket_url')) { $e->setProperty('X-TICKETS-URL', $this->_sanitize_value($event->get('ticket_url'))); } // ================= // = Instant Event = // ================= if ($event->is_instant()) { $e->setProperty('X-INSTANT-EVENT', $this->_sanitize_value($event->is_instant())); } // ==================================== // = Contact name, phone, e-mail, URL = // ==================================== $contact = array($event->get('contact_name'), $event->get('contact_phone'), $event->get('contact_email'), $event->get('contact_url')); $contact = array_filter($contact); $contact = implode('; ', $contact); $e->setProperty('contact', $this->_sanitize_value($contact)); // ==================== // = Recurrence rules = // ==================== $rrule = array(); $recurrence = $event->get('recurrence_rules'); $recurrence = $this->_filter_rule($recurrence); if (!empty($recurrence)) { $rules = array(); foreach (explode(';', $recurrence) as $v) { if (strpos($v, '=') === false) { continue; } list($k, $v) = explode('=', $v); $k = strtoupper($k); // If $v is a comma-separated list, turn it into array for iCalcreator switch ($k) { case 'BYSECOND': case 'BYMINUTE': case 'BYHOUR': case 'BYDAY': case 'BYMONTHDAY': case 'BYYEARDAY': case 'BYWEEKNO': case 'BYMONTH': case 'BYSETPOS': $exploded = explode(',', $v); break; default: $exploded = $v; break; } // iCalcreator requires a more complex array structure for BYDAY... if ($k == 'BYDAY') { $v = array(); foreach ($exploded as $day) { $v[] = array('DAY' => $day); } } else { $v = $exploded; } $rrule[$k] = $v; } } // =================== // = Exception rules = // =================== $exceptions = $event->get('exception_rules'); $exceptions = $this->_filter_rule($exceptions); $exrule = array(); if (!empty($exceptions)) { $rules = array(); foreach (explode(';', $exceptions) as $v) { if (strpos($v, '=') === false) { continue; } list($k, $v) = explode('=', $v); $k = strtoupper($k); // If $v is a comma-separated list, turn it into array for iCalcreator switch ($k) { case 'BYSECOND': case 'BYMINUTE': case 'BYHOUR': case 'BYDAY': case 'BYMONTHDAY': case 'BYYEARDAY': case 'BYWEEKNO': case 'BYMONTH': case 'BYSETPOS': $exploded = explode(',', $v); break; default: $exploded = $v; break; } // iCalcreator requires a more complex array structure for BYDAY... if ($k == 'BYDAY') { $v = array(); foreach ($exploded as $day) { $v[] = array('DAY' => $day); } } else { $v = $exploded; } $exrule[$k] = $v; } } // add rrule to exported calendar if (!empty($rrule) && !isset($rrule['RDATE'])) { $e->setProperty('rrule', $this->_sanitize_value($rrule)); } // add exrule to exported calendar if (!empty($exrule) && !isset($exrule['EXDATE'])) { $e->setProperty('exrule', $this->_sanitize_value($exrule)); } // =================== // = Exception dates = // =================== // For all day events that use a date as DTSTART, date must be supplied // For other other events which use DATETIME, we must use that as well // We must also match the exact starting time $recurrence_dates = $event->get('recurrence_dates'); $recurrence_dates = $this->_filter_rule($recurrence_dates); if (!empty($recurrence_dates)) { $params = array('VALUE' => 'DATE-TIME', 'TZID' => $tz); $dt_suffix = $event->get('start')->format('\\THis'); foreach (explode(',', $recurrence_dates) as $exdate) { // date-time string in EXDATES is formatted as 'Ymd\THis\Z', that // means - in UTC timezone, thus we use `format_to_gmt` here. $exdate = $this->_registry->get('date.time', $exdate)->format_to_gmt('Ymd'); $e->setProperty('rdate', array($exdate . $dt_suffix), $params); } } $exception_dates = $event->get('exception_dates'); $exception_dates = $this->_filter_rule($exception_dates); if (!empty($exception_dates)) { $params = array('VALUE' => 'DATE-TIME', 'TZID' => $tz); $dt_suffix = $event->get('start')->format('\\THis'); foreach (explode(',', $exception_dates) as $exdate) { // date-time string in EXDATES is formatted as 'Ymd\THis\Z', that // means - in UTC timezone, thus we use `format_to_gmt` here. $exdate = $this->_registry->get('date.time', $exdate)->format_to_gmt('Ymd'); $e->setProperty('exdate', array($exdate . $dt_suffix), $params); } } return $calendar; }
/** * Handle AJAX request for submission of front-end create event form. * * @return null */ public function submit_front_end_create_event_form() { global $ai1ec_view_helper, $ai1ec_calendar_helper, $ai1ec_events_helper; $ai1ec_settings = Ai1ec_Settings::get_instance(); $error = false; $html = ''; $default_error_msg = __('There was an error creating your event.', AI1EC_PLUGIN_NAME) . ' ' . __('Please try again or contact the site administrator for help.', AI1EC_PLUGIN_NAME); $valid = $this->validate_front_end_create_event_form($message); // If valid submission, proceed with event creation. if ($valid) { // Determine post publish status. if (current_user_can('publish_ai1ec_events')) { $post_status = 'publish'; } else { if (current_user_can('edit_ai1ec_events')) { $post_status = 'pending'; } else { if ($ai1ec_settings->allow_anonymous_submissions) { $post_status = 'pending'; } } } // Strip slashes if ridiculous PHP setting magic_quotes_gpc is enabled. foreach ($_POST as $param_name => $param) { if ('ai1ec' === substr($param_name, 0, 5) && is_scalar($param)) { $_POST[$param_name] = stripslashes($param); } } // Build post array from submitted data. $post = array('post_type' => AI1EC_POST_TYPE, 'post_author' => get_current_user_id(), 'post_title' => $_POST['post_title'], 'post_content' => $_POST['post_content'], 'post_status' => $post_status); // Copy posted event data to new empty event object. $event = new Ai1ec_Event(); $event->post = $post; $event->categories = isset($_POST['ai1ec_categories']) ? implode(',', $_POST['ai1ec_categories']) : ''; $event->tags = isset($_POST['ai1ec_tags']) ? $_POST['ai1ec_tags'] : ''; $event->allday = isset($_POST['ai1ec_all_day_event']) ? (bool) $_POST['ai1ec_all_day_event'] : 0; $event->instant_event = isset($_POST['ai1ec_instant_event']) ? (bool) $_POST['ai1ec_instant_event'] : 0; $event->start = isset($_POST['ai1ec_start_time']) ? $_POST['ai1ec_start_time'] : ''; if ($event->instant_event) { $event->end = $event->start + 1800; } else { $event->end = isset($_POST['ai1ec_end_time']) ? $_POST['ai1ec_end_time'] : ''; } $event->address = isset($_POST['ai1ec_address']) ? $_POST['ai1ec_address'] : ''; $event->show_map = isset($_POST['ai1ec_google_map']) ? (bool) $_POST['ai1ec_google_map'] : 0; $scalar_field_list = array('ai1ec_venue' => FILTER_SANITIZE_STRING, 'ai1ec_cost' => FILTER_SANITIZE_STRING, 'ai1ec_is_free' => FILTER_SANITIZE_NUMBER_INT, 'ai1ec_ticket_url' => FILTER_VALIDATE_URL, 'ai1ec_contact_name' => FILTER_SANITIZE_STRING, 'ai1ec_contact_phone' => FILTER_SANITIZE_STRING, 'ai1ec_contact_email' => FILTER_VALIDATE_EMAIL, 'ai1ec_contact_url' => FILTER_VALIDATE_URL); foreach ($scalar_field_list as $scalar_field => $field_filter) { $scalar_value = filter_input(INPUT_POST, $scalar_field, $field_filter); if (!empty($scalar_value)) { $use_name = substr($scalar_field, 6); $event->{$use_name} = $scalar_value; } } // Save the event to the database. try { $event->save(); $ai1ec_events_helper->cache_event($event); // Check if uploads are enabled and there is an uploaded file. if ((is_user_logged_in() || $ai1ec_settings->allow_anonymous_submissions && $ai1ec_settings->allow_anonymous_uploads) && !empty($_FILES['ai1ec_image']['name'])) { require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; $attach_id = media_handle_upload('ai1ec_image', $event->post_id); if (is_int($attach_id)) { update_post_meta($event->post_id, '_thumbnail_id', $attach_id); } } // Send the mail $admin_notification = Ai1ec_Notification_Factory::create_notification_instance(array(get_option('admin_email')), $ai1ec_settings->admin_add_new_event_mail_body, Ai1ec_Notification_Factory::EMAIL_NOTIFICATION, $ai1ec_settings->admin_add_new_event_mail_subject); $edit_url = 'post.php?post=' . $event->post_id . '&action=edit'; $translations = array('[event_title]' => $_POST['post_title'], '[site_title]' => get_bloginfo('name'), '[site_url]' => site_url(), '[event_admin_url]' => admin_url($edit_url)); $admin_notification->set_translations($translations); $sent = $admin_notification->send(); if (current_user_can('publish_ai1ec_events')) { $message = sprintf(__('Thank you for your submission. Your event <em>%s</em> was published successfully.', AI1EC_PLUGIN_NAME), $post['post_title']); $link_text = __('View Your Event', AI1EC_PLUGIN_NAME); $link_url = get_permalink($event->post_id); } else { $message = sprintf(__('Thank you for your submission. Your event <em>%s</em> will be reviewed and published once approved.', AI1EC_PLUGIN_NAME), $post['post_title']); $link_text = __('Back to Calendar', AI1EC_PLUGIN_NAME); $link_url = $ai1ec_calendar_helper->get_calendar_url(); } } catch (Exception $e) { trigger_error(sprintf(__('There was an error during event creation: %s', AI1EC_PLUGIN_NAME), $e->getMessage()), E_USER_WARNING); $error = true; $message = $default_error_msg; } $args = array('message_type' => $error ? 'error' : 'success', 'message' => $message, 'link_text' => $link_text, 'link_url' => $link_url); $html = $ai1ec_view_helper->get_theme_view('create-event-message.php', $args); } else { $error = true; } $response = array('error' => $error, 'message' => $message, 'html' => $html); $ai1ec_view_helper->xml_response($response); }
/** * Handles instances saving and switching if needed. If original event * and created event have different start dates proceed in old style * otherwise find next instance, switch original start date to next * instance start date. If there are no next instances mark event as * non recurring. Filter also exception dates if are past. * * @param Ai1ec_Event $created_event Created event object. * @param Ai1ec_Event $original_event Original event object. * * @return void Method does not return. */ protected function _handle_instances(Ai1ec_Event $created_event, Ai1ec_Event $original_event, $instance_id) { $ce_start = $created_event->get('start'); $oe_start = $original_event->get('start'); if ($ce_start->format() !== $oe_start->format()) { $this->add_exception_date($original_event->get('post_id'), $ce_start); return; } $next_instance = $this->_find_next_instance($original_event->get('post_id'), $instance_id); if (!$next_instance) { $original_event->set('recurrence_rules', null); $original_event->save(true); return; } $original_event->set('start', $this->_registry->get('date.time', $next_instance->get('start'))); $original_event->set('end', $this->_registry->get('date.time', $next_instance->get('end'))); $edates = $this->_filter_exception_dates($original_event); $original_event->set('exception_dates', implode(',', $edates)); $recurrence_rules = $original_event->get('recurrence_rules'); $rules_info = $this->_registry->get('recurrence.rule')->build_recurrence_rules_array($recurrence_rules); if (isset($rules_info['COUNT'])) { $next_instances_count = $this->_count_next_instances($original_event->get('post_id'), $instance_id); $rules_info['COUNT'] = (int) $next_instances_count + count($edates); $rules = ''; if ($rules_info['COUNT'] <= 1) { $rules_info = array(); } foreach ($rules_info as $key => $value) { $rules .= $key . '=' . $value . ';'; } $original_event->set('recurrence_rules', $rules); } $original_event->save(true); }
/** * Create a duplicate from a posts' instance */ function duplicate_post_create_duplicate($post, $status = '') { $new_post_author = $this->duplicate_post_get_current_user(); $new_post_status = $status; if (empty($new_post_status)) { $new_post_status = $post->post_status; } $new_post_status = $this->get_new_post_status($new_post_status); $new_post = array('menu_order' => $post->menu_order, 'comment_status' => $post->comment_status, 'ping_status' => $post->ping_status, 'pinged' => $post->pinged, 'post_author' => $new_post_author->ID, 'post_content' => $post->post_content, 'post_date' => $post->post_date, 'post_date_gmt' => get_gmt_from_date($post->post_date), 'post_excerpt' => $post->post_excerpt, 'post_parent' => $post->post_parent, 'post_password' => $post->post_password, 'post_status' => $new_post_status, 'post_title' => $post->post_title, 'post_type' => $post->post_type, 'to_ping' => $post->to_ping); $new_post_id = wp_insert_post($new_post); $edit_event_url = esc_attr(admin_url("post.php?post={$new_post_id}&action=edit")); $message = Ai1ec_Helper_Factory::create_admin_message_instance(sprintf(__('<p>The event <strong>%s</strong> was cloned succesfully. <a href="%s">Edit cloned event</a></p>', AI1EC_PLUGIN_NAME), $post->post_title, $edit_event_url)); $message->set_message_type('updated'); $this->admin_notice_helper->add_renderable_children($message); // If you have written a plugin which uses non-WP database tables to save // information about a post you can hook this action to dupe that data. if ($post->post_type == 'page' || function_exists('is_post_type_hierarchical') && is_post_type_hierarchical($post->post_type)) { do_action('dp_duplicate_page', $new_post_id, $post); } else { do_action('dp_duplicate_post', $new_post_id, $post); } if ('ai1ec_event' === $post->post_type) { try { $old_event = new Ai1ec_Event($post->ID); $old_event->post_id = $new_post_id; unset($old_event->post); $old_event->save(); } catch (Ai1ec_Event_Not_Found $exception) { /* ignore */ } } delete_post_meta($new_post_id, '_dp_original'); add_post_meta($new_post_id, '_dp_original', $post->ID); // If the copy gets immediately published, we have to set a proper slug. if ($new_post_status == 'publish' || $new_post_status == 'future') { $post_name = wp_unique_post_slug($post->post_name, $new_post_id, $new_post_status, $post->post_type, $post->post_parent); $new_post = array(); $new_post['ID'] = $new_post_id; $new_post['post_name'] = $post_name; // Update the post into the database wp_update_post($new_post); } return $new_post_id; }
/** * add_exception_date method * * Add exception (date) to event. * * @param int $post_id Event edited post ID * @param mixed $date Parseable date representation to exclude * * @return bool Success */ public function add_exception_date($post_id, $date) { if (!is_int($date) && !ctype_digit($date)) { $date = strtotime($date); } $event = new Ai1ec_Event($post_id); $dates_list = explode(',', $event->exception_dates); if (empty($dates_list[0])) { unset($dates_list[0]); } $dates_list[] = gmdate('Ymd\\THis\\Z', $this->local_to_gmt($date)); $event->exception_dates = implode(',', $dates_list); return $event->save(true); }