empty( $this->meta['file'] ) ? null : $this->meta['file'], ); $meta = wp_parse_args( $meta, $defaults ); return parent::create( $type, $args, $meta ); } public function queue_import( $args = array() ) { $is_previewing = ( ! empty( $_GET['action'] ) && ( 'tribe_aggregator_create_import' === $_GET['action'] || 'tribe_aggregator_preview_import' === $_GET['action'] ) ); $data = $this->get_csv_data(); $result = array( 'status' => 'success', 'message_code' => 'success', 'data' => array( 'import_id' => $this->id, 'items' => $data, ), ); $first_row = reset( $data ); $columns = array_keys( $first_row ); $result['data']['columns'] = $columns; // store the import id update_post_meta( $this->id, self::$meta_key_prefix . 'import_id', $this->id ); // only set as pending if we aren't previewing the record if ( ! $is_previewing ) { // if we get here, we're good! Set the status to pending $this->set_status_as_pending(); } return $result; } /** * Public facing Label for this Origin * * @return string */ public function get_label() { return __( 'CSV', 'the-events-calendar' ); } public function get_csv_data() { if ( empty( $this->meta['file'] ) || ! $file_path = $this->get_file_path() ) { return $this->set_status_as_failed( tribe_error( 'core:aggregator:invalid-csv-file' ) ); } $content_type = str_replace( 'tribe_', '', $this->meta['content_type'] ); $file_reader = new Tribe__Events__Importer__File_Reader( $file_path ); $importer = Tribe__Events__Importer__File_Importer::get_importer( $content_type, $file_reader ); $this->update_meta( 'source_name', basename( $file_path ) ); $rows = $importer->do_import_preview(); /* * Strip whitespace from the beginning and end of row values */ $formatted_rows = array(); foreach ( $rows as $row ) { $formatted_rows[] = array_map( 'trim', $row ); } $rows = $formatted_rows; $headers = array_shift( $rows ); /* * To avoid empty columns from collapsing onto each other we provide * each column without an header a generated one. */ $empty_counter = 1; $formatted_headers = array(); foreach ( $headers as $header ) { if ( empty( $header ) ) { $header = __( 'Unknown Column ', 'the-events-calendar' ) . $empty_counter ++; } $formatted_headers[] = $header; } $headers = $formatted_headers; $data = array(); foreach ( $rows as $row ) { $item = array(); foreach ( $headers as $key => $header ) { $item[ $header ] = $row[ $key ]; } $data[] = $item; } return $data; } /** * Queues events, venues, and organizers for insertion * * @param array $data Import data * @param bool $ignored This parameter is, de facto, ignored when processing CSV files: all * imports are immediately started. * * @return array|WP_Error */ public function process_posts( $data = array(), $ignored = false ) { if ( 'csv' !== $data['origin'] || empty( $data['csv']['content_type'] ) ) { return tribe_error( 'core:aggregator:invalid-csv-parameters' ); } if ( $this->has_queue() ) { $queue = Tribe__Events__Aggregator__Record__Queue_Processor::build_queue( $this->post->ID ); return $queue->process(); } $importer = $this->prep_import_data( $data ); if ( tribe_is_error( $importer ) ) { return $importer; } $queue = Tribe__Events__Aggregator__Record__Queue_Processor::build_queue( $this->post->ID, $importer ); return $queue->process(); } /** * Handles import data before queuing * * Ensures the import record source name is accurate, checks for errors, and limits import items * based on selection * * @param array $data Import data * * @return array|WP_Error */ public function prep_import_data( $data = array() ) { if ( empty( $this->meta['finalized'] ) ) { return tribe_error( 'core:aggregator:record-not-finalized' ); } // if $data is an object already, don't attempt to manipulate it into an importer object if ( is_object( $data ) ) { return $data; } // if $data is empty, grab the data from meta if ( empty( $data ) ) { $data = $this->meta; } if ( empty( $data['column_map'] ) ) { return tribe_error( 'core:aggregator:missing-csv-column-map' ); } $content_type = $this->get_csv_content_type(); update_option( 'tribe_events_import_column_mapping_' . $content_type, $data['column_map'] ); try { $importer = $this->get_importer(); } catch ( RuntimeException $e ) { return tribe_error( 'core:aggregator:missing-csv-file' ); } if ( ! empty( $data['category'] ) ) { $importer = $this->maybe_set_default_category( $importer ); } if ( ! empty( $data['post_status'] ) ) { $importer = $this->maybe_set_default_post_status( $importer ); } $required_fields = $importer->get_required_fields(); $missing = array_diff( $required_fields, $data['column_map'] ); if ( ! empty( $missing ) ) { $mapper = new Tribe__Events__Importer__Column_Mapper( $content_type ); /** * @todo allow to overwrite the default message */ $message = '

' . esc_html__( 'The following fields are required for a successful import:', 'the-events-calendar' ) . '

'; $message .= ''; return new WP_Error( 'csv-invalid-column-mapping', $message ); } update_option( 'tribe_events_import_column_mapping_' . $content_type, $data['column_map'] ); return $importer; } public function get_importer() { if ( ! $this->importer ) { $content_type = $this->get_csv_content_type(); $file_path = $this->get_file_path(); $file_reader = new Tribe__Events__Importer__File_Reader( $file_path ); $this->importer = Tribe__Events__Importer__File_Importer::get_importer( $content_type, $file_reader ); $this->importer->set_map( get_option( 'tribe_events_import_column_mapping_' . $content_type, array() ) ); $this->importer->set_type( $content_type ); $this->importer->set_limit( absint( apply_filters( 'tribe_aggregator_batch_size', Tribe__Events__Aggregator__Record__Queue_Processor::$batch_size ) ) ); $this->importer->set_offset( 1 ); } return $this->importer; } public function get_content_type() { return str_replace( 'tribe_', '', $this->meta['content_type'] ); } /** * Translates the posttype-driven content types to content types that the CSV importer knows * * @param string $content_type Content Type * * @return string CSV Importer compatible content type */ public function get_csv_content_type( $content_type = null ) { if ( ! $content_type ) { $content_type = $this->get_content_type(); } $lowercase_content_type = strtolower( $content_type ); $map = array( 'event' => 'events', 'events' => 'events', 'organizer' => 'organizers', 'organizers' => 'organizers', 'venue' => 'venues', 'venues' => 'venues', ); if ( isset( $map[ $lowercase_content_type ] ) ) { return $map[ $lowercase_content_type ]; } return $content_type; } /** * Gets the available post types for importing * * @return array Array of Post Type Objects */ public function get_import_post_types() { $post_types = array( get_post_type_object( Tribe__Events__Main::POSTTYPE ), get_post_type_object( Tribe__Events__Organizer::POSTTYPE ), get_post_type_object( Tribe__Events__Venue::POSTTYPE ), ); /** * Filters the available CSV post types for the event aggregator form * * @param array $post_types Array of post type objects */ return apply_filters( 'tribe_aggregator_csv_post_types', $post_types ); } /** * Returns the path to the CSV file. * * @since 4.6.15 * * @return bool|false|string Either the absolute path to the CSV file or `false` on failure. */ protected function get_file_path() { if ( is_numeric( $this->meta['file'] ) ) { $file_path = get_attached_file( absint( $this->meta['file'] ) ); } else { $file_path = realpath( $this->meta['file'] ); } return $file_path && file_exists( $file_path ) ? $file_path : false; } private function begin_import() { $this->reset_tracking_options(); return $this->continue_import(); } public function reset_tracking_options() { update_option( 'tribe_events_importer_offset', 1 ); update_option( 'tribe_events_import_log', array( 'updated' => 0, 'created' => 0, 'skipped' => 0, 'encoding' => 0 ) ); update_option( 'tribe_events_import_failed_rows', array() ); update_option( 'tribe_events_import_encoded_rows', array() ); } public function continue_import() { $lock_key = 'tribe_ea_csv_import_' . $this->id; if ( ! $this->acquire_db_lock( $lock_key ) ) { return $this->meta['activity']; } $importer = $this->get_importer(); $importer->is_aggregator = true; $importer->aggregator_record = $this; $importer = $this->maybe_set_default_category( $importer ); $importer = $this->maybe_set_default_post_status( $importer ); $offset = (int) get_option( 'tribe_events_importer_offset', 1 ); if ( -1 === $offset ) { $this->state = 'complete'; $this->clean_up_after_import(); } else { $this->state = 'importing'; $importer->set_offset( $offset ); $this->do_import( $importer ); $this->log_import_results( $importer ); } return $this->meta['activity']; } /** * If a custom category has been specified, set it in the importer * * @param Tribe__Events__Importer__File_Importer $importer Importer object * * @return Tribe__Events__Importer__File_Importer */ public function maybe_set_default_category( $importer ) { if ( ! empty( $this->meta['category'] ) ) { $importer->default_category = (int) $this->meta['category']; } return $importer; } /** * If a custom post_status has been specified, set it in the importer * * @param Tribe__Events__Importer__File_Importer $importer Importer object * * @return Tribe__Events__Importer__File_Importer */ public function maybe_set_default_post_status( $importer ) { if ( ! empty( $this->meta['post_status'] ) ) { $importer->default_post_status = $this->meta['post_status']; } return $importer; } protected function do_import( Tribe__Events__Importer__File_Importer $importer ) { $importer->do_import(); $this->messages = $importer->get_log_messages(); $new_offset = $importer->import_complete() ? -1 : $importer->get_last_completed_row(); update_option( 'tribe_events_importer_offset', $new_offset ); $lock_key = 'tribe_ea_csv_import_' . $this->id; $this->release_db_lock( $lock_key ); if ( -1 === $new_offset ) { do_action( 'tribe_events_csv_import_complete' ); } } protected function log_import_results( Tribe__Events__Importer__File_Importer $importer ) { $log = get_option( 'tribe_events_import_log' ); if ( empty( $log['encoding'] ) ) { $log['encoding'] = 0; } $updated = $importer->get_updated_post_count(); $created = $importer->get_new_post_count(); $skipped = $importer->get_skipped_row_count(); if ( empty( $log['updated'] ) ) { $log['updated'] = 0; } if ( empty( $log['created'] ) ) { $log['created'] = 0; } if ( empty( $log['skipped'] ) ) { $log['skipped'] = 0; } if ( $updated ) { $this->meta['activity']->add( 'updated', $this->meta['content_type'], array_fill( 0, $updated, 1 ) ); } if ( $created ) { $this->meta['activity']->add( 'created', $this->meta['content_type'], array_fill( 0, $created, 1 ) ); } if ( $skipped ) { $this->meta['activity']->add( 'skipped', $this->meta['content_type'], array_fill( 0, $skipped, 1 ) ); } $log['updated'] += $updated; $log['created'] += $created; $log['skipped'] += $skipped; $log['encoding'] += $importer->get_encoding_changes_row_count(); update_option( 'tribe_events_import_log', $log ); $skipped_rows = $importer->get_skipped_row_numbers(); $previously_skipped_rows = get_option( 'tribe_events_import_failed_rows', array() ); $skipped_rows = $previously_skipped_rows + $skipped_rows; update_option( 'tribe_events_import_failed_rows', $skipped_rows ); $encoded_rows = $importer->get_encoding_changes_row_numbers(); $previously_encoded_rows = get_option( 'tribe_events_import_encoded_rows', array() ); $encoded_rows = $previously_encoded_rows + $encoded_rows; update_option( 'tribe_events_import_encoded_rows', $encoded_rows ); } private function clean_up_after_import() { Tribe__Events__Importer__File_Uploader::clear_old_files(); } }