8889841cPK [hFeatured_Image_Uploader.phpnu[name = $file_array['name']; $this->type = $file_array['type']; $this->tmp_name = $file_array['tmp_name']; $this->error = $file_array['error']; $this->size = $file_array['size']; } public function save_file() { $this->validate_temporary_file(); $this->permanently_save_file(); } private function validate_temporary_file() { if ( ! file_exists( $this->tmp_name ) ) { throw new RuntimeException( sprintf( esc_html__( 'Temporary file not found. Could not save %s.', 'the-events-calendar' ), $this->name ) ); } } private function permanently_save_file() { self::clear_old_files(); $moved = move_uploaded_file( $this->tmp_name, self::get_file_path() ); if ( ! $moved ) { throw new RuntimeException( sprintf( esc_html__( 'Could not save %s.', 'the-events-calendar' ), $this->name ) ); } } public static function clear_old_files() { $path = self::get_file_path(); if ( file_exists( $path ) ) { unlink( $path ); } $dir = self::get_upload_directory(); rmdir( $dir ); } public static function get_file_path() { $path = trailingslashit( self::get_upload_directory() ); $path .= 'tribe-import.csv'; return $path; } /** * Indicates if the file returned by self::get_file_path() (still) exists * and is readable. * * @return bool */ public static function has_valid_csv_file() { $csv_file = self::get_file_path(); return file_exists( $csv_file ) && is_readable( $csv_file ); } private static function get_upload_directory() { $upload_dir_array = wp_upload_dir(); $path = $upload_dir_array['basedir']; $path = trailingslashit( $path ) . 'tribe-importer'; wp_mkdir_p( $path ); return $path; } } PK [pEEColumn_Mapper.phpnu[import_type = $import_type; switch ( $this->import_type ) { case 'events': $this->column_names = $this->get_event_column_names(); break; case 'venue': case 'venues': $this->column_names = $this->get_venue_column_names(); break; case 'organizer': case 'organizers': $this->column_names = $this->get_organizer_column_names(); break; default: /** * Filters the column names that will be available for a custom import type. * * @param array $column_names */ $this->column_names = apply_filters( "tribe_event_import_{$import_type}_column_names", [] ); break; } } public function set_defaults( $defaults ) { $this->defaults = $defaults; } public function make_select_box( $index ) { $selected = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : ''; $html = ''; return $html; } public function get_column_label( $key ) { if ( isset( $this->column_names[ $key ] ) ) { return $this->column_names[ $key ]; } return ''; } private function get_event_column_names() { $column_names = array( 'event_name' => esc_html__( 'Event Name', 'the-events-calendar' ), 'event_description' => esc_html__( 'Event Description', 'the-events-calendar' ), 'event_excerpt' => esc_html__( 'Event Excerpt', 'the-events-calendar' ), 'event_start_date' => esc_html__( 'Event Start Date', 'the-events-calendar' ), 'event_start_time' => esc_html__( 'Event Start Time', 'the-events-calendar' ), 'event_end_date' => esc_html__( 'Event End Date', 'the-events-calendar' ), 'event_end_time' => esc_html__( 'Event End Time', 'the-events-calendar' ), 'event_timezone' => esc_html__( 'Event Time Zone', 'the-events-calendar' ), 'event_all_day' => esc_html__( 'All Day Event', 'the-events-calendar' ), 'event_hide' => esc_html__( 'Hide Event From Event Listings', 'the-events-calendar' ), 'event_sticky' => esc_html__( 'Event Sticky in Month View', 'the-events-calendar' ), 'feature_event' => esc_html__( 'Feature Event', 'the-events-calendar' ), 'event_venue_name' => esc_html__( 'Event Venue Name', 'the-events-calendar' ), 'event_organizer_name' => esc_html__( 'Event Organizer Name(s) or ID(s)', 'the-events-calendar' ), 'event_show_map_link' => esc_html__( 'Event Show Map Link', 'the-events-calendar' ), 'event_show_map' => esc_html__( 'Event Show Map', 'the-events-calendar' ), 'event_cost' => esc_html__( 'Event Cost', 'the-events-calendar' ), 'event_currency_code' => esc_html__( 'Event ISO Currency Code', 'the-events-calendar' ), 'event_currency_symbol' => esc_html__( 'Event Currency Symbol', 'the-events-calendar' ), 'event_currency_position' => esc_html__( 'Event Currency Position', 'the-events-calendar' ), 'event_category' => esc_html__( 'Event Category', 'the-events-calendar' ), 'event_tags' => esc_html__( 'Event Tags', 'the-events-calendar' ), 'event_website' => esc_html__( 'Event Website', 'the-events-calendar' ), 'featured_image' => esc_html__( 'Event Featured Image', 'the-events-calendar' ), 'event_comment_status' => esc_html__( 'Event Allow Comments', 'the-events-calendar' ), 'event_ping_status' => esc_html__( 'Event Allow Trackbacks and Pingbacks', 'the-events-calendar' ), ); /** * Filters the Event column names that will be shown to the user. * * @param array $column_names An array of column names for event import. */ return apply_filters( 'tribe_events_importer_event_column_names', $column_names ); } private function get_venue_column_names() { $column_names = array( 'venue_name' => esc_html__( 'Venue Name', 'the-events-calendar' ), 'venue_description' => esc_html__( 'Venue Description', 'the-events-calendar' ), 'venue_country' => esc_html__( 'Venue Country', 'the-events-calendar' ), 'venue_address' => esc_html__( 'Venue Address', 'the-events-calendar' ), 'venue_address2' => esc_html__( 'Venue Address 2', 'the-events-calendar' ), 'venue_city' => esc_html__( 'Venue City', 'the-events-calendar' ), 'venue_state' => esc_html__( 'Venue State/Province', 'the-events-calendar' ), 'venue_zip' => esc_html__( 'Venue Zip', 'the-events-calendar' ), 'venue_phone' => esc_html__( 'Venue Phone', 'the-events-calendar' ), 'venue_url' => esc_html__( 'Venue Website', 'the-events-calendar' ), 'featured_image' => esc_html__( 'Venue Featured Image', 'the-events-calendar' ), ); /** * Filters the Venue column names that will be shown to the user. * * @param array $column_names An array of column names for venue import. */ return apply_filters( 'tribe_events_importer_venue_column_names', $column_names ); } private function get_organizer_column_names() { $column_names = array( 'organizer_name' => esc_html__( 'Organizer Name', 'the-events-calendar' ), 'organizer_description' => esc_html__( 'Organizer Description', 'the-events-calendar' ), 'organizer_email' => esc_html__( 'Organizer Email', 'the-events-calendar' ), 'organizer_website' => esc_html__( 'Organizer Website', 'the-events-calendar' ), 'organizer_phone' => esc_html__( 'Organizer Phone', 'the-events-calendar' ), 'featured_image' => esc_html__( 'Organizer Featured Image', 'the-events-calendar' ), ); /** * Filters the Organizer column names that will be shown to the user. * * @param array $column_names An array of column names for organizer import. */ return apply_filters( 'tribe_events_importer_organizer_column_names', $column_names ); } } PK [&Td$$File_Importer_Venues.phpnu[get_value_by_key( $record, 'venue_name' ); $id = $this->find_matching_post_id( $name, Tribe__Events__Main::VENUE_POST_TYPE ); return $id; } protected function update_post( $post_id, array $record ) { $venue = $this->build_venue_array( $post_id, $record ); Tribe__Events__API::updateVenue( $post_id, $venue ); if ( $this->is_aggregator && ! empty( $this->aggregator_record ) ) { $this->aggregator_record->meta['activity']->add( 'venue', 'updated', $post_id ); } } protected function create_post( array $record ) { $post_status_setting = tribe( 'events-aggregator.settings' )->default_post_status( 'csv' ); $venue = $this->build_venue_array( false, $record ); $id = Tribe__Events__API::createVenue( $venue, $post_status_setting ); if ( $this->is_aggregator && ! empty( $this->aggregator_record ) ) { $this->aggregator_record->meta['activity']->add( 'venue', 'created', $id ); } return $id; } /** * Build a venue array for creation/update of the current imported venue. * * @since 3.2 * @since 5.1.6 Adjust to prevent overwriting values that aren't mapped. * * @param int $venue_id The ID of the venue we're currently importing. * @param array $record The event record from the import file. Only contains mapped values. * Useful if value and key above don't appear to match what's expected. * In the format [ mapped_key => value ]. * * @return array $venue The array of venue data for creation/update. */ private function build_venue_array( $venue_id, array $record ) { $venue = []; $columns = [ 'Venue' => 'venue_name', 'Address' => 'venue_address', 'City' => 'venue_city', 'Country' => 'venue_country', 'Description' => 'venue_description', 'Phone' => 'venue_phone', 'Province' => 'venue_state', 'State' => 'venue_state', 'URL' => 'venue_url', 'Zip' => 'venue_zip', ]; foreach ( $columns as $name => $key ) { // Don't set/overwrite unmapped columns. if ( ! $this->has_value_by_key( $record, $key ) ) { continue; } // Reset. $value = ''; // Address is a concatenation of two possible values. if ( 'venue_address' === $key ) { $address_1 = trim( $this->get_value_by_key( $record, 'venue_address' ) ); $address_2 = trim( $this->get_value_by_key( $record, 'venue_address2' ) ); $value = ( empty( $address_2 ) ) ? $address_1 : $address_1 . ' ' . $address_2; } else { $value = $this->get_value_by_key( $record, $key ); } /** * Allows filtering of individual main values before setting. * Return boolean false to prevent importing that value. * @since 5.1.6 * * @param string $key The mapped key for the value we'll be importing. * From the $columns array above, this would be 'venue_name', for example. * @param string $value The mapped value we'll be importing. * @param array $venue The entire array of venue data we're modifying. * @param array $record The event record from the import file. Only contains mapped values. * Useful if value and key above don't appear to match what's expected. * In the format [ mapped_key => value ]. * @param object $this The current instance of Tribe__Events__Importer__File_Importer_Venues. */ $value = apply_filters( "tribe_events_importer_venue_{$key}_value", $value, $key, $venue, $record, $this ); if ( false === $value ) { continue; } $venue[ $name ] = $value; } // Handle the manual stuff. $venue['FeaturedImage'] = $this->get_featured_image( $venue_id, $record ); $show_map_setting = tribe( 'events-aggregator.settings' )->default_map( 'csv' ); $venue['ShowMap'] = $venue_id ? get_post_meta( $venue_id, '_VenueShowMap', true ) : $show_map_setting; $venue['ShowMapLink'] = $venue_id ? get_post_meta( $venue_id, '_VenueShowMapLink', true ) : $show_map_setting; /** * Allows triggering using the default values set in the admin for imported venues. * * @since 5.1.6 * @param int $venue_id The ID of the venue we're currently importing. * @param array $venue The array of venue data we're modifying. * @param array $record The event record from the import file. Only contains mapped values. * Useful if value and key above don't appear to match what's expected. * In the format [ mapped_key => value ]. */ $set_defaults = apply_filters( 'tribe_events_importer_set_default_venue_import_values', false, $venue_id, $venue, $record ); if ( $set_defaults ) { $venue = $this->set_defaults( $venue, $record ); } /** * Allows filtering of values before import. * * @since 4.2 * * @param array $venue The array of venue data we're modifying. * @param array $record The event record from the import file. Only contains mapped values. * Useful if value and key above don't appear to match what's expected. * In the format [ mapped_key => value ]. * @param int $venue_id The ID of the venue we're currently importing. * @param object $this The current instance of Tribe__Events__Importer__File_Importer_Venues. * $record, */ $venue = apply_filters( 'tribe_events_importer_venue_array', $venue, $record, $venue_id, $this ); return $venue; } /** * Set default venue values. * Note this will only set a value if it has been mapped, and it is empty. * If you are using the importer to erase values, you should NOT be triggering this! * * @since 5.1.6 * * @param array $venue The array of venue data we're modifying. * @param array $record The event record from the import file. Only contains mapped values. * Useful if value and key above don't appear to match what's expected. * In the format [ mapped_key => value ]. * * @return array The modified venue data. */ public function set_defaults( $venue, $record ) { $columns = [ 'Address' => 'address', 'City' => 'city', 'Country' => 'country', 'Phone' => 'phone', 'Province' => 'state', 'State' => 'state', 'URL' => 'url', 'Zip' => 'zip', ]; foreach ( $columns as $name => $key ) { // Only fill in empty columns that we're importing. if ( ! isset( $venue[ $name ] ) || ! empty( $venue[ $name ] ) ) { continue; } /** * Allows filtering of default value before setting. * Also allows setting a value (specifically for imports) by filter * that is not set in the admin for manually-created venues. * * @since 5.1.6 * * @param string $value The default value as set in the admin "defaults" settings. * @param string $key The mapped key for the value we'll be importing. * From the $columns array above, * this would be 'state' (the array "value"), for example. * @param string $name The name for the value. * From the $columns array above, * this would be 'Province' (the array "key"), for example. * @param array $venue The entire array of venue data we're modifying. * In the format [ $key => $value ]. * @param array $record The event record from the import file. Only contains mapped values. * Useful if value and key above don't appear to match what's expected. * In the format [ mapped_key => value ]. */ $default_value = apply_filters( "tribe_events_importer_venue_default_{$key}_value", tribe_get_default_value( $key ), $key, $name, $venue, $record ); /* * Country comes through as an array: [ 'US', 'United States' ] * We could handle this with a filter elsewhere, but let's deal with it here * so we don't break Geolocation functions. */ if ( 'country' === $key && is_array( $default_value ) ) { $default_value = array_pop( $default_value ); } // Let's not set values that haven't been set in the admin! if ( empty( $default_value ) ) { continue; } $venue[ $name ] = $default_value; } return $venue; } } PK [i88File_Importer.phpnu[ => ArrayIterator( [ , ... ] ) ]. * * @var array */ protected $created_terms = []; /** @var Tribe__Events__Importer__File_Reader */ private $reader = null; private $map = []; private $type = ''; private $limit = 100; private $offset = 0; private $errors = []; private $updated = 0; private $created = 0; private $encoding = []; protected $log = []; protected $skipped = []; protected $inverted_map = []; public $is_aggregator = false; public $aggregator_record; public $default_category; public $default_post_status; /** * @var Tribe__Events__Importer__Featured_Image_Uploader */ protected $featured_image_uploader; /** * @param string $type * @param Tribe__Events__Importer__File_Reader $file_reader * * @return Tribe__Events__Importer__File_Importer * @throws InvalidArgumentException */ public static function get_importer( $type, Tribe__Events__Importer__File_Reader $file_reader ) { switch ( $type ) { case 'event': case 'events': return new Tribe__Events__Importer__File_Importer_Events( $file_reader ); case 'venue': case 'venues': return new Tribe__Events__Importer__File_Importer_Venues( $file_reader ); case 'organizer': case 'organizers': return new Tribe__Events__Importer__File_Importer_Organizers( $file_reader ); default: /** * Allows developers to return an importer instance to use for unsupported import types. * * @param bool|mixed An importer instance or `false` if not found or not supported. * @param Tribe__Events__Importer__File_Reader $file_reader */ $importer = apply_filters( "tribe_events_import_{$type}_importer", false, $file_reader ); if ( false === $importer ) { throw new InvalidArgumentException( sprintf( esc_html__( 'No importer defined for %s', 'the-events-calendar' ), $type ) ); } return $importer; } } /** * @param Tribe__Events__Importer__File_Reader $file_reader */ public function __construct( Tribe__Events__Importer__File_Reader $file_reader, Tribe__Events__Importer__Featured_Image_Uploader $featured_image_uploader = null ) { $this->reader = $file_reader; $this->featured_image_uploader = $featured_image_uploader; $this->limit = apply_filters( 'tribe_aggregator_batch_size', Tribe__Events__Aggregator__Record__Queue_Processor::$batch_size ); } public function set_map( array $map_array ) { $this->map = $map_array; $this->inverted_map = array_flip( $this->map ); } public function set_type( $type ) { $this->type = $type; } public function set_limit( $limit ) { $this->limit = (int) $limit; } public function set_offset( $offset ) { $this->offset = (int) $offset; } public function do_import() { $this->reader->set_row( $this->offset ); for ( $i = 0; $i < $this->limit && ! $this->import_complete(); $i ++ ) { tribe_set_time_limit( 30 ); $this->import_next_row(); } } public function do_import_preview() { $rows = []; $this->reader->set_row( $this->offset ); for ( $i = 0; $i < $this->limit && ! $this->import_complete(); $i ++ ) { tribe_set_time_limit( 30 ); $rows[] = $this->import_next_row( false, true ); } return $rows; } public function get_last_completed_row() { return $this->reader->get_last_line_number_read() + 1; } public function import_complete() { return $this->reader->at_end_of_file(); } public function get_line_count() { return $this->reader->lines; } public function get_updated_post_count() { return $this->updated; } public function get_new_post_count() { return $this->created; } public function get_skipped_row_count() { return count( $this->skipped ); } public function get_skipped_row_numbers() { return $this->skipped; } public function get_encoding_changes_row_count() { return count( $this->encoding ); } public function get_encoding_changes_row_numbers() { return $this->encoding; } public function get_log_messages() { return $this->log; } public function get_required_fields() { return $this->required_fields; } public function get_type() { return $this->type; } public function import_next_row( $throw = false, $preview = false ) { $post_id = null; $record = $this->reader->read_next_row(); $row = $this->reader->get_last_line_number_read() + 1; //Check if option to encode is active $encoding_option = Tribe__Settings_Manager::get_option( 'imported_encoding_status', [ 'csv' => 'encode' ] ); /** * Filter Encoding Status Option for CSV Imports * * @since 4.6.18 * * @param $encoding_status array an array to encode * @param [ 'csv' => 'encode' ] array the default value to enable encoding to UTF8 */ $encoding_option = apply_filters( 'tribe_import_setting_imported_encoding_status', $encoding_option, [ 'csv' => 'encode' ] ); if ( isset( $encoding_option['csv'] ) && 'encode' == $encoding_option['csv'] ) { $encoded = ForceUTF8__Encoding::toUTF8( $record ); $encoding_diff = array_diff( $encoded, $record ); if ( ! empty( $encoding_diff ) ) { $this->encoding[] = $row; } $record = $encoded; } if ( $preview ) { return $record; } if ( ! $this->is_valid_record( $record ) ) { if ( ! $throw ) { $this->log[ $row ] = $this->get_skipped_row_message( $row ); $this->skipped[] = $row; return false; } else { throw new RuntimeException( sprintf( 'Missing required fields in row %d', $row ) ); } } try { $post_id = $this->update_or_create_post( $record ); } catch ( Exception $e ) { $this->log[ $row ] = sprintf( esc_html__( 'Failed to import record in row %d.', 'the-events-calendar' ), $row ); $this->skipped[] = $row; } return $post_id; } protected function update_or_create_post( array $record ) { if ( $id = $this->match_existing_post( $record ) ) { if ( false !== $this->update_post( $id, $record ) ) { $this->updated ++; $this->log[ $this->reader->get_last_line_number_read() + 1 ] = sprintf( esc_html__( '%s (post ID %d) updated.', 'the-events-calendar' ), get_the_title( $id ), $id ); } } else { $id = $this->create_post( $record ); $this->created ++; $this->log[ $this->reader->get_last_line_number_read() + 1 ] = sprintf( esc_html__( '%s (post ID %d) created.', 'the-events-calendar' ), get_the_title( $id ), $id ); } $featured_image = $this->get_value_by_key( $record, 'featured_image' ); do_action( 'tribe_log', 'debug', __CLASS__, [ 'post_id' => $id, 'featured_image' => $featured_image ] ); if ( ! empty( $featured_image ) ) { $post_thumbnail_process = new Tribe__Process__Post_Thumbnail_Setter(); $post_thumbnail_process->set_post_id( $id ); $post_thumbnail_process->set_post_thumbnail( $featured_image ); $post_thumbnail_process->dispatch(); } /** * Hook after an event is updated or created by the csv importer. * * @since 5.12.4 * * @param integer $id The event ID to update. * @param array $record An event record from the import. * @param Tribe__Events__Importer__File_Importer $this An instance of the Tribe__Events__Importer__File_Importer class. */ do_action( 'tec_events_csv_importer_post_update', $id, $record, $this ); return $id; } abstract protected function match_existing_post( array $record ); abstract protected function update_post( $post_id, array $record ); abstract protected function create_post( array $record ); protected function is_valid_record( array $record ) { foreach ( $this->get_required_fields() as $field ) { if ( $this->get_value_by_key( $record, $field ) == '' ) { return false; } } return true; } /** * Retrieves a value from the record. * * @since 5.1.6 - modify to use has_value_by_key(). * * @param array $record An event record from the import. * @param string $key The text of the key to find in the record array. * * @return mixed|string Either the value or an empty string if the value was not found. */ public function get_value_by_key( array $record, $key ) { if ( ! $this->has_value_by_key( $record, $key ) ) { return ''; } return $record[ $this->inverted_map[ $key ] ]; } /** * Check if a key is found. * * @since 5.1.6 * * @param array $record An event record from the import. * @param string $key The text of the key to find in the record array. * * @return bool Whether the key is found in the record. */ public function has_value_by_key( array $record, $key ) { if ( ! isset( $this->inverted_map[ $key ] ) ) { return false; } if ( ! isset( $record[ $this->inverted_map[ $key ] ] ) ) { return false; } return true; } protected function find_matching_post_id( $name, $post_type, $post_status = 'publish' ) { if ( empty( $name ) ) { return 0; } if ( is_numeric( $name ) && intval( $name ) == $name ) { $found = get_post( $name ); if ( $found && $found->post_type == $post_type ) { return $name; } } $query_args = [ 'post_type' => $post_type, 'post_status' => $post_status, 'post_title' => $name, 'fields' => 'ids', 'suppress_filters' => false, ]; add_filter( 'posts_search', [ $this, 'filter_query_for_title_search' ], 10, 2 ); $ids = get_posts( $query_args ); remove_filter( 'posts_search', [ $this, 'filter_query_for_title_search' ], 10 ); return empty( $ids ) ? 0 : reset( $ids ); } public function filter_query_for_title_search( $search, WP_Query $wp_query ) { $title = $wp_query->get( 'post_title' ); if ( ! empty( $title ) ) { global $wpdb; $search .= $wpdb->prepare( " AND {$wpdb->posts}.post_title=%s", $title ); } return $search; } /** * @param string|int $featured_image Either an absolute path to an image or an attachment ID. * * @return Tribe__Events__Importer__Featured_Image_Uploader */ protected function featured_image_uploader( $featured_image ) { // Remove any leading/trailing whitespace (if the string is a URL, extra whitespace // could result in URL validation fail) if ( is_string( $featured_image ) ) { $featured_image = trim( $featured_image ); } return empty( $this->featured_image_uploader ) ? new Tribe__Events__Importer__Featured_Image_Uploader( $featured_image ) : $this->featured_image_uploader; } /** * Returns a boolean value from the record. * * @param array $record * @param string $key * @param string $return_true_value The value to return if the value was found and is truthy. * @param string $return_false_value The value to return if the value was not found or is not truthy; * defaults to the original value. * @param array $accepted_true_values An array of values considered truthy. * * @return string */ public function get_boolean_value_by_key( $record, $key, $return_true_value = '1', $return_false_value = null, $accepted_true_values = [ 'yes', 'true', '1', ] ) { $value = strtolower( $this->get_value_by_key( $record, $key ) ); if ( in_array( $value, $accepted_true_values ) ) { return $return_true_value; } return is_null( $return_false_value ) ? $value : $return_false_value; } /** * @param $row * * @return string */ protected function get_skipped_row_message( $row ) { return sprintf( esc_html__( 'Missing required fields in row %d.', 'the-events-calendar' ), $row ); } /** * @param $event_id * @param array $record * * @return bool|int|mixed|null */ protected function get_featured_image( $event_id, array $record ) { $featured_image_content = $this->get_value_by_key( $record, 'featured_image' ); $featured_image = null; if ( ! empty( $event_id ) ) { $featured_image = get_post_meta( $event_id, '_wp_attached_file', true ); if ( empty( $featured_image ) ) { $featured_image = $this->featured_image_uploader( $featured_image_content )->upload_and_get_attachment_id(); return $featured_image; } return $featured_image; } else { $featured_image = $this->featured_image_uploader( $featured_image_content )->upload_and_get_attachment_id(); return $featured_image; } } /** * Hooks on term creation to log it. * * @since 4.6.24 * * @param int $term_id The newly created term ID. * @param int $tt_id The newly created term taxonomy ID. * @param string $taxonomy The current taxonomy. */ public function on_created_term( $term_id, $tt_id, $taxonomy ) { if ( ! isset( $this->created_terms[ $taxonomy ] ) ) { $this->created_terms[ $taxonomy ] = new ArrayIterator(); } $this->created_terms[ $taxonomy ]->append( $term_id ); } /** * Hooks on the term creation to watch for any newly created terms. * * @since 4.6.24 */ public function watch_term_creation() { if ( has_action( 'created_term', [ $this, 'on_created_term' ] ) ) { return; } add_action( 'created_term', [ $this, 'on_created_term' ], 10, 3 ); } /** * Stops watching for term creation and logging. * * @since 4.6.24 */ public function stop_watching_term_creation() { if ( ! has_action( 'created_term', [ $this, 'on_created_term' ] ) ) { return; } remove_action( 'created_term', [ $this, 'on_created_term' ] ); } /** * Returns an iterator to iterate over the last created terms. * * @since 4.6.24 * * By default a NoRewindIterator will be returned, this will allow successive calls from iterating code, * e.g. a `foreach`, to resume from the previously last position. * * @param string $taxonomy The taxonomy to fetch the created terms for. * @param bool $rewind Whether to return a rewinding iterator (`true`) or a NoRewind one (`false`); * defaults to `false`. * * @return ArrayIterator|NoRewindIterator An ArrayIterator built on the term IDs created for the taxonomy * or a NoRewindIterator built on top of it. */ public function created_terms( $taxonomy, $rewind = false ) { $iterator = Tribe__Utils__Array::get( $this->created_terms, $taxonomy, new ArrayIterator() ); return $rewind ? $iterator : new NoRewindIterator( $iterator ); } } PK [NFXXFile_Reader.phpnu[path = $file_path; $this->file = new SplFileObject( $this->path ); $this->file->setFlags( SplFileObject::SKIP_EMPTY | SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::DROP_NEW_LINE ); $this->set_csv_params( $this->get_csv_params() ); $this->file->seek( PHP_INT_MAX ); $total_lines = $this->file->key(); /* * In PHP 8.0.15 to 8.0.17 or 8.1.2 to 8.1.4 the use of seek() and then key() returns 0 when using the flag SplFileObject::READ_CSV. * This bug is fixed in PHP 8.0.18 and 8.1.5. * @see https://github.com/php/php-src/issues/8236 - outlines the issue with seek() * @see https://github.com/php/php-src/pull/8138 - PR to fix the issue */ if ( 0 === $total_lines ) { $total_lines = iterator_count( $this->file ); } $this->lines = $total_lines; $this->file->rewind(); add_filter( 'tribe_events_import_row', [ $this, 'sanitize_row' ] ); } public function __destruct() { $this->file = null; } public function get_header() { $this->file->rewind(); return $this->file->current(); } public function set_row( $row_number ) { $this->file->seek( $row_number ); } public function read_row( $row_number ) { $this->set_row( $row_number ); return $this->read_next_row(); } public function read_next_row() { $this->last_line_read = $this->file->key(); if ( ! $this->file->valid() ) { return []; } $row = $this->file->current(); /** * Allows for filtering the row for import * * @since 4.5.5 * * @param array $row */ $row = apply_filters( 'tribe_events_import_row', $row ); $this->file->next(); return empty( $row ) ? [] : $row; } public function get_last_line_number_read() { return $this->last_line_read; } public function at_end_of_file() { return ! $this->file->valid(); } /** * Sanitizes a row * * @since 4.5.5 * * @param array $row Import row */ public function sanitize_row( $row ) { return array_map( 'wp_kses_post', $row ); } /** * Get the field parameters used for reading CSV files. * * @since 4.6.1 * * @return array The CSV field parameters. */ public function get_csv_params() { $csv_params = [ 'delimter' => ',', 'enclosure' => '"', 'escape' => '\\', ]; /** * Set the parameters used for reading and importing CSV files. * * @see `SplFileObject::setCsvControl()` * * @since 4.6.1 * * @param array $csv_params ( * The parameters * * @param string $delimter The field delimiter (one character only). * @param string $enclosure The field enclosure character (one character only). * @param string $escape The field escape character (one character only). * } * @param string $file_path The path to the CSV file */ return apply_filters( 'tribe_events_csv_import_file_parameters', $csv_params, $this->path ); } /** * Set the import params for CSV fields * * @since 4.6.1 * * @param array $params ( * The parameters * * @param string $delimter The field delimiter (one character only). * @param string $enclosure The field enclosure character (one character only). * @param string $escape The field escape character (one character only). * } */ private function set_csv_params( $params ) { $this->file->setCsvControl( $params['delimter'], $params['enclosure'], $params['escape'] ); } } PK [I File_Importer_Organizers.phpnu[get_value_by_key( $record, 'organizer_name' ); $id = $this->find_matching_post_id( $name, Tribe__Events__Main::ORGANIZER_POST_TYPE ); return $id; } protected function update_post( $post_id, array $record ) { $organizer = $this->build_organizer_array( $post_id, $record ); Tribe__Events__API::updateOrganizer( $post_id, $organizer ); if ( $this->is_aggregator && ! empty( $this->aggregator_record ) ) { $this->aggregator_record->meta['activity']->add( 'organizer', 'updated', $post_id ); } } protected function create_post( array $record ) { $post_status_setting = tribe( 'events-aggregator.settings' )->default_post_status( 'csv' ); $organizer = $this->build_organizer_array( false, $record ); $id = Tribe__Events__API::createOrganizer( $organizer, $post_status_setting ); if ( $this->is_aggregator && ! empty( $this->aggregator_record ) ) { $this->aggregator_record->meta['activity']->add( 'organizer', 'created', $id ); } return $id; } /** * Build a organizer array for creation/update of the current imported organizer. * * @since 3.2 * @since 5.1.6 Adjust to prevent overwriting values that aren't mapped. * * @param int $organizer_id The ID of the organizer we're currently importing. * @param array $record An event record from the import. * * @return array $organizer The array of organizer data for creation/update. */ private function build_organizer_array( $organizer_id, array $record ) { $organizer = []; $columns = [ 'Organizer' => 'organizer_name', 'Description' => 'organizer_description', 'Email' => 'organizer_email', 'Phone' => 'organizer_phone', 'Website' => 'organizer_website', ]; foreach ( $columns as $name => $key ) { // Reset. $value = ''; // Don't set/overwrite unmapped columns. if ( ! $this->has_value_by_key( $record, $key ) ) { continue; } /** * Allows filtering of main values before setting. * Return boolean false to prevent importing that value. * * @since 5.1.6 * * @param string $key The key for the value we'll be importing. * @param string $value The value we'll be importing. * @param array $organizer The array of organizer data we're modifying. * @param array $record The event record from the import. */ $value = apply_filters( "tribe_events_importer_organizer_{$key}_value", $this->get_value_by_key( $record, $key ), $key, $organizer, $record, $this ); if ( false === $value ) { continue; } $organizer[ $name ] = $value; } // Handle the manual stuff. $organizer['FeaturedImage'] = $this->get_featured_image( $organizer, $record ); /** * Allows filtering of record values before import. * * @since 5.1.6 * * @param array $organizer The array of organizer data we're modifying. * @param array $record The event record from the import. * @param int $organizer_id The ID of the organizer we're currently importing. */ $organizer = apply_filters( 'tribe_events_importer_organizer_fields', $organizer, $record, $organizer_id, $this ); return $organizer; } } PK [o=IIFile_Importer_Events.phpnu[get_event_start_date( $record ); $end_date = $this->get_event_end_date( $record ); $all_day = $this->get_boolean_value_by_key( $record, 'event_all_day' ); // Base query - only the meta query will be different $query_args = [ 'post_type' => Tribe__Events__Main::POSTTYPE, 'post_title' => $this->get_value_by_key( $record, 'event_name' ), 'fields' => 'ids', 'posts_per_page' => 1, 'suppress_filters' => false, 'post_status' => 'any', ]; // When trying to find matches for all day events, the comparison should only be against the date // component only since a) the time is irrelevant and b) the time may have been adjusted to match // the eod cutoff setting if ( Tribe__Date_Utils::is_all_day( $all_day ) ) { $meta_query = [ [ 'key' => '_EventStartDate', 'value' => $this->get_event_start_date( $record, true ), 'compare' => 'LIKE', ], [ 'key' => '_EventAllDay', 'value' => 'yes', ], ]; // For regular, non-all day events, use the full date *and* time in the start date comparison } else { $meta_query = [ [ 'key' => '_EventStartDate', 'value' => $start_date, ], ]; } // Optionally use the end date/time for matching, where available if ( ! empty( $end_date ) && ! $all_day ) { $meta_query[] = [ 'key' => '_EventEndDate', 'value' => $end_date, ]; } $query_args['meta_query'] = $meta_query; $query_args['tribe_remove_date_filters'] = true; $query_args['tribe_suppress_query_filters'] = true; add_filter( 'posts_search', [ $this, 'filter_query_for_title_search' ], 10, 2 ); /** * Add an option to change the $matches that are duplicates. * * @since 4.6.15 * * @param array $matches Array with the duplicate matches * @param array $query_args Array with the arguments used to get the posts. */ $matches = (array) apply_filters( 'tribe_events_import_event_duplicate_matches', get_posts( $query_args ), $query_args ); remove_filter( 'posts_search', [ $this, 'filter_query_for_title_search' ], 10 ); if ( empty( $matches ) ) { return 0; } return reset( $matches ); } /** * Update an event with the imported information. * * @param integer $post_id The event ID to update. * @param array $record An event record from the import. * * @return false False if the update authority is set to retain or void if the update completes. */ protected function update_post( $post_id, array $record ) { $update_authority_setting = tribe( 'events-aggregator.settings' )->default_update_authority( 'csv' ); $this->watch_term_creation(); $event = $this->build_event_array( $post_id, $record ); if ( 'retain' === $update_authority_setting ) { $this->skipped[] = $event; if ( $this->is_aggregator && ! empty( $this->aggregator_record ) ) { $this->aggregator_record->meta['activity']->add( 'event', 'skipped', $post_id ); } $this->stop_watching_term_creation(); return false; } if ( 'preserve_changes' === $update_authority_setting ) { $event['ID'] = $post_id; $event = Tribe__Events__Aggregator__Event::preserve_changed_fields( $event ); } add_filter( 'tribe_tracker_enabled', '__return_false' ); Tribe__Events__API::updateEvent( $post_id, $event ); $this->stop_watching_term_creation(); if ( $this->is_aggregator && ! empty( $this->aggregator_record ) ) { $this->aggregator_record->meta['activity']->add( 'event', 'updated', $post_id ); foreach ( $this->created_terms( Tribe__Events__Main::TAXONOMY ) as $term_id ) { $this->aggregator_record->meta['activity']->add( 'category', 'created', $term_id ); } foreach ( $this->created_terms( 'post_tag' ) as $term_id ) { $this->aggregator_record->meta['activity']->add( 'tag', 'created', $term_id ); } } remove_filter( 'tribe_tracker_enabled', '__return_false' ); } /** * Create an event with the imported information. * * @param array $record An event record from the import. * * @return integer The new event's post id. */ protected function create_post( array $record ) { $this->watch_term_creation(); $event = $this->build_event_array( false, $record ); $id = Tribe__Events__API::createEvent( $event ); $this->stop_watching_term_creation(); if ( $this->is_aggregator && ! empty( $this->aggregator_record ) ) { Tribe__Events__Aggregator__Records::instance()->add_record_to_event( $id, $this->aggregator_record->id, 'csv' ); $this->aggregator_record->meta['activity']->add( 'event', 'created', $id ); foreach ( $this->created_terms( Tribe__Events__Main::TAXONOMY ) as $term_id ) { $this->aggregator_record->meta['activity']->add( 'category', 'created', $term_id ); } foreach ( $this->created_terms( 'post_tag' ) as $term_id ) { $this->aggregator_record->meta['activity']->add( 'tag', 'created', $term_id ); } } return $id; } /** * Get the event start date from the import record. * * @param array $record An event record from the import. * @param boolean $date_only An optional setting to incude the date only and no time. * * @return string $start_date The start date time string. */ private function get_event_start_date( array $record, $date_only = false ) { $start_date = $this->get_value_by_key( $record, 'event_start_date' ); $start_time = $this->get_value_by_key( $record, 'event_start_time' ); if ( ! $date_only && ! empty( $start_time ) ) { $start_date .= ' ' . $start_time; } $start_date = $date_only ? date( Tribe__Date_Utils::DBDATEFORMAT, strtotime( $start_date ) ) : date( Tribe__Date_Utils::DBDATETIMEFORMAT, strtotime( $start_date ) ); return $start_date; } /** * Get the event end date from the import record. * * @param array $record An event record from the import. * * @return string $end_date The end date time string. */ private function get_event_end_date( array $record ) { $start_date = $this->get_event_start_date( $record ); $end_date = $this->get_value_by_key( $record, 'event_end_date' ); $end_time = $this->get_value_by_key( $record, 'event_end_time' ); if ( empty( $end_date ) ) { $end_date = $start_date; } if ( ! empty( $end_time ) ) { $end_date .= ' ' . $end_time; } if ( ! empty( $end_date ) ) { $end_date = date( 'Y-m-d H:i:s', strtotime( $end_date ) ); } if ( $end_date < $start_date ) { $end_date = $start_date; } return $end_date; } /** * Build an event array from import record. * * @param integer $post_id The event ID to update. * @param array $record An event record from the import. * * @return array An array of information to save or update an event. */ private function build_event_array( $event_id, array $record ) { $start_date = strtotime( $this->get_event_start_date( $record ) ); $end_date = strtotime( $this->get_event_end_date( $record ) ); if ( $this->default_post_status ) { $post_status_setting = $this->default_post_status; } else { $post_status_setting = tribe( 'events-aggregator.settings' )->default_post_status( 'csv' ); } $event = [ 'post_type' => Tribe__Events__Main::POSTTYPE, 'post_title' => $this->get_value_by_key( $record, 'event_name' ), 'post_status' => $post_status_setting, 'post_content' => $this->get_post_text_field( $event_id, $record, 'event_description', 'post_content' ), 'comment_status' => $this->get_boolean_value_by_key( $record, 'event_comment_status', 'open', 'closed' ), 'ping_status' => $this->get_boolean_value_by_key( $record, 'event_ping_status', 'open', 'closed' ), 'post_excerpt' => $this->get_post_text_field( $event_id, $record, 'event_excerpt', 'post_excerpt' ), 'menu_order' => $this->get_boolean_value_by_key( $record, 'event_sticky', '-1', '0' ), 'EventStartDate' => date( 'Y-m-d', $start_date ), 'EventStartHour' => date( 'h', $start_date ), 'EventStartMinute' => date( 'i', $start_date ), 'EventStartMeridian' => date( 'a', $start_date ), 'EventEndDate' => date( 'Y-m-d', $end_date ), 'EventEndHour' => date( 'h', $end_date ), 'EventEndMinute' => date( 'i', $end_date ), 'EventEndMeridian' => date( 'a', $end_date ), 'EventShowMapLink' => $this->get_boolean_value_by_key( $record, 'event_show_map_link', '1', '' ), 'EventShowMap' => $this->get_boolean_value_by_key( $record, 'event_show_map', '1', '' ), 'EventCost' => $this->get_value_by_key( $record, 'event_cost' ), 'EventCurrencyCode' => $this->get_value_by_key( $record, 'event_currency_code' ), 'EventAllDay' => $this->get_boolean_value_by_key( $record, 'event_all_day', 'yes' ), 'EventHideFromUpcoming' => $this->get_boolean_value_by_key( $record, 'event_hide', 'yes', '' ), 'EventURL' => $this->get_value_by_key( $record, 'event_website' ), 'EventCurrencySymbol' => $this->get_value_by_key( $record, 'event_currency_symbol' ), 'EventCurrencyPosition' => $this->get_currency_position( $record ), 'EventTimezone' => $this->get_timezone( $this->get_value_by_key( $record, 'event_timezone' ) ), 'feature_event' => $this->get_boolean_value_by_key( $record, 'feature_event', '1', '' ), ]; if ( $organizer_id = $this->find_matching_organizer_id( $record ) ) { $event['organizer'] = is_array( $organizer_id ) ? $organizer_id : [ 'OrganizerID' => $organizer_id ]; } if ( $venue_id = $this->find_matching_venue_id( $record ) ) { $event['venue'] = [ 'VenueID' => $venue_id ]; } $cats = $this->get_value_by_key( $record, 'event_category' ); if ( $this->is_aggregator && ! empty( $this->default_category ) ) { $cats = $cats ? $cats . ',' . $this->default_category : $this->default_category; } elseif ( $category_setting = tribe( 'events-aggregator.settings' )->default_category( 'csv' ) ) { $cats = $cats ? $cats . ',' . $category_setting : $category_setting; } if ( $cats ) { $events_cat = Tribe__Events__Main::TAXONOMY; $event['tax_input'][ $events_cat ] = Tribe__Terms::translate_terms_to_ids( explode( ',', $cats ), $events_cat ); } if ( $tags = $this->get_value_by_key( $record, 'event_tags' ) ) { $event['tax_input']['post_tag'] = $tags; } // don't create the _EventHideFromUpcoming meta key/value pair if it doesn't need to be created if ( ! $event['EventHideFromUpcoming'] ) { unset( $event['EventHideFromUpcoming'] ); } if ( $event['menu_order'] == '-1' ) { $event['EventShowInCalendar'] = 'yes'; } $additional_fields = apply_filters( 'tribe_events_csv_import_event_additional_fields', [] ); if ( ! empty ( $additional_fields ) ) { foreach ( $additional_fields as $key => $csv_column ) { $value = $this->get_value_by_key( $record, $key ); if ( strpos( $value, '|' ) > -1 ) { $event[ $key ] = explode( '|', $value ); } else { $event[ $key ] = $value; } } } /** * Filter the csv event import event meta. * * @since 5.12.4 * * @param array $event An array event meta fields. * * @return array An array of the autodetect results. */ return apply_filters( 'tec_events_csv_import_event_meta', $event, $record, $this ); } /** * Filter allowing user to customize the separator used for organizers * Defaults to comma ',' * @since 4.6.19 * * @return mixed */ private function get_separator() { return apply_filters( 'tribe_get_event_import_organizer_separator', ',' ); } /** * Find organizer matches from separated string * Attempts to compensate for names with separators in them - Like "Woodhouse, Chelsea S." * @since 4.6.19 * @param $organizers * * @return array */ private function match_organizers( $organizers ) { $matches = []; $separator = $this->get_separator(); // We allow this to be filtered $skip = false; // For concatenation checks for ( $i = 0, $len = count( $organizers ); $i < $len; $i++ ) { if ( $skip ) { $skip = false; continue; } $potential_match = $this->find_matching_post_id( trim( $organizers[ $i ] ), Tribe__Events__Organizer::POSTTYPE, 'any' ); // We've got a match so we add it and move on if ( ! empty( $potential_match ) ) { $matches[] = $potential_match; $skip = false; continue; } // No match - test for separator in name by grabbing the next item and concatenating $test_organizer = trim( $organizers[ $i ] ) . $separator . ' ' . trim( $organizers[ $i + 1 ] ); $potential_match = $this->find_matching_post_id( $test_organizer, Tribe__Events__Organizer::POSTTYPE, 'any' ); // Still no match, skip this item and move on if ( empty( $potential_match ) ) { $skip = false; continue; } // we got a match when combined with the next, so we flag to skip the next item $skip = true; $matches[] = $potential_match; } $matches = array_filter( array_unique( $matches ) ); // If we get something outlandish - like no organizers or more organizers than expected, bail if ( empty( $matches ) || count( $matches ) > count( $organizers ) ) { return []; } $organizer_ids = [ 'OrganizerID' => [] ]; foreach ( $matches as $id ) { $organizer_ids[ 'OrganizerID' ][] = $id; } return $organizer_ids; } /** * Determine if organizer is a list of space-separated IDs * @param $organizer * * @return array[]|bool|false|string[] */ private function organizer_is_space_separated_ids( $organizer ) { $pattern = '/\s+/'; if ( preg_match( $pattern, $organizer ) && is_numeric( preg_replace( $pattern, '', $organizer ) ) ) { return preg_split( $pattern, $organizer ); } return false; } /** * * Determine if organizer is a list of $separator-separated IDs * @param $organizer * * @return array[]|bool|false|string[] */ private function maybe_organizer_is_separated_list( $organizer ) { $separator = $this->get_separator(); // When we require php > 5.5 we can combine these $cleared_separator = trim( $separator );// clear whitespace $pattern = ! empty( $cleared_separator ) ? '/' . $cleared_separator . '+/' : '/\s+/'; // event_organizer_name is a list of $separator-separated names and/or IDs if ( false !== stripos( $organizer, $separator ) ) { return preg_split( $pattern, $organizer ); } return false; } /** * Handle finding the matching organizer(s) for the event * @since 4.6.19 * @param $record - the event record from the import * * @return array */ private function find_matching_organizer_id( $record ) { $organizer = $this->get_value_by_key( $record, 'event_organizer_name' ); // Test for space-separated IDs separately if ( $maybe_spaced_organizers = $this->organizer_is_space_separated_ids( $organizer ) ) { return $this->match_organizers( $maybe_spaced_organizers ); } // Check for $separator list if ( $maybe_separated_organizers = $this->maybe_organizer_is_separated_list( $organizer ) ) { return $this->match_organizers( $maybe_separated_organizers ); } // Just in case something went wrong // We've likely got a single item - either a number or a name (with optional spaces) $matching_post_ids = $this->find_matching_post_id( $organizer, Tribe__Events__Organizer::POSTTYPE, 'any' ); if ( ! is_array( $matching_post_ids ) ) { $matching_post_ids = [ $matching_post_ids ]; } return [ 'OrganizerID' => $matching_post_ids ]; } private function find_matching_venue_id( $record ) { $name = $this->get_value_by_key( $record, 'event_venue_name' ); return $this->find_matching_post_id( $name, Tribe__Events__Venue::POSTTYPE, 'any' ); } /** * Parses a timezone string candidate and returns a TEC supported timezone string. * * @param string $timezone_candidate * * @return bool|string Either the timezone string or `false` if the timezone candidate is invalid. */ private function get_timezone( $timezone_candidate ) { if ( Tribe__Timezones::is_utc_offset( $timezone_candidate ) ) { return $timezone_candidate; } return Tribe__Timezones::get_timezone( $timezone_candidate, false ) ? $timezone_candidate : false; } /** * Get Post Text from Import or Existing Value using the provided field name and post field. * * @since 5.1.6 * * @param int $event_id The event id being updated by import. * @param array $record An event record from the import. * @param string $field The import field name. * @param string $post_field The post field name. * * @return string The description value to update the event with. */ protected function get_post_text_field( $event_id, $record, $field, $post_field ) { $import_exists = $this->has_value_by_key( $record, $field ); // If the import field is not being imported and there is no id, return an empty string. if ( ! $import_exists && empty( $event_id ) ) { return ''; } // If the import field is not being imported and there is an id, return current description. if ( ! $import_exists && $event_id ) { $post = get_post( $event_id ); if ( ! $post instanceof \WP_Post ) { return ''; } return $post->{$post_field}; } $import_description = $this->get_value_by_key( $record, $field ); // If there is no event id we return the imported description, even if empty. return $import_description; } /** * Allows the user to specify the currency position using alias terms. * * @param array $record * * @return string Either `prefix` or `suffix`; will fall back on the first if the specified position is not * a recognized alias. */ private function get_currency_position( array $record ) { $currency_position = $this->get_value_by_key( $record, 'event_currency_position' ); $after_aliases = [ 'suffix', 'after' ]; foreach ( $after_aliases as $after_alias ) { if ( preg_match( '/' . $after_alias . '/i', $currency_position ) ) { return 'suffix'; } } return 'prefix'; } } PK [hFeatured_Image_Uploader.phpnu[PK [6FFile_Uploader.phpnu[PK [pEEM Column_Mapper.phpnu[PK [&Td$$"File_Importer_Venues.phpnu[PK [i88GFile_Importer.phpnu[PK [NFXXFile_Reader.phpnu[PK [I File_Importer_Organizers.phpnu[PK [o=IIFile_Importer_Events.phpnu[PK