init
This commit is contained in:
+891
@@ -0,0 +1,891 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WC_Product_CSV_Importer_Controller file.
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Internal\Utilities\FilesystemUtil;
|
||||
use Automattic\WooCommerce\Internal\Utilities\URL;
|
||||
use Automattic\WooCommerce\Utilities\I18nUtil;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_Importer' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Product importer controller - handles file upload and forms in admin.
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
* @version 3.1.0
|
||||
*/
|
||||
class WC_Product_CSV_Importer_Controller {
|
||||
|
||||
/**
|
||||
* The path to the current file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $file = '';
|
||||
|
||||
/**
|
||||
* The current import step.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $step = '';
|
||||
|
||||
/**
|
||||
* Progress steps.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $steps = array();
|
||||
|
||||
/**
|
||||
* Errors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $errors = array();
|
||||
|
||||
/**
|
||||
* The current delimiter for the file being read.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $delimiter = ',';
|
||||
|
||||
/**
|
||||
* Whether to use previous mapping selections.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $map_preferences = false;
|
||||
|
||||
/**
|
||||
* Whether to skip existing products.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $update_existing = false;
|
||||
|
||||
/**
|
||||
* The character encoding to use to interpret the input file, or empty string for autodetect.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $character_encoding = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Get importer instance.
|
||||
*
|
||||
* @param string $file File to import.
|
||||
* @param array $args Importer arguments.
|
||||
* @return WC_Product_CSV_Importer
|
||||
*/
|
||||
public static function get_importer( $file, $args = array() ) {
|
||||
$importer_class = apply_filters( 'woocommerce_product_csv_importer_class', 'WC_Product_CSV_Importer' );
|
||||
$args = apply_filters( 'woocommerce_product_csv_importer_args', $args, $importer_class );
|
||||
return new $importer_class( $file, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a file is a valid CSV file.
|
||||
*
|
||||
* @param string $file File path.
|
||||
* @param bool $check_path Whether to also check the file is located in a valid location (Default: true).
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_file_valid_csv( $file, $check_path = true ) {
|
||||
return wc_is_file_valid_csv( $file, $check_path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs before controller actions to check that the file used during the import is valid.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param string $path Path to test.
|
||||
*
|
||||
* @throws \Exception When file validation fails.
|
||||
*/
|
||||
protected static function check_file_path( string $path ): void {
|
||||
$wp_filesystem = FilesystemUtil::get_wp_filesystem();
|
||||
|
||||
// File must exist and be readable.
|
||||
$is_valid_file = $wp_filesystem->is_readable( $path );
|
||||
|
||||
// Check that file is within an allowed location.
|
||||
if ( $is_valid_file ) {
|
||||
$is_valid_file = self::file_is_in_directory( $path, $wp_filesystem->abspath() );
|
||||
if ( ! $is_valid_file ) {
|
||||
$upload_dir = wp_get_upload_dir();
|
||||
$is_valid_file = false === $upload_dir['error'] && self::file_is_in_directory( $path, $upload_dir['basedir'] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $is_valid_file ) {
|
||||
throw new \Exception( esc_html__( 'File path provided for import is invalid.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
if ( ! self::is_file_valid_csv( $path ) ) {
|
||||
throw new \Exception( esc_html__( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given file is inside a given directory.
|
||||
*
|
||||
* @param string $file_path The full path of the file to check.
|
||||
* @param string $directory The path of the directory to check.
|
||||
* @return bool True if the file is inside the directory.
|
||||
*/
|
||||
private static function file_is_in_directory( string $file_path, string $directory ): bool {
|
||||
$file_path = (string) new URL( $file_path ); // This resolves '/../' sequences.
|
||||
$file_path = preg_replace( '/^file:\\/\\//', '', $file_path );
|
||||
return 0 === stripos( wp_normalize_path( $file_path ), trailingslashit( wp_normalize_path( $directory ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the valid filetypes for a CSV file.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_valid_csv_filetypes() {
|
||||
return apply_filters(
|
||||
'woocommerce_csv_product_import_valid_filetypes',
|
||||
array(
|
||||
'csv' => 'text/csv',
|
||||
'txt' => 'text/plain',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$default_steps = array(
|
||||
'upload' => array(
|
||||
'name' => __( 'Upload CSV file', 'woocommerce' ),
|
||||
'view' => array( $this, 'upload_form' ),
|
||||
'handler' => array( $this, 'upload_form_handler' ),
|
||||
),
|
||||
'mapping' => array(
|
||||
'name' => __( 'Column mapping', 'woocommerce' ),
|
||||
'view' => array( $this, 'mapping_form' ),
|
||||
'handler' => '',
|
||||
),
|
||||
'import' => array(
|
||||
'name' => __( 'Import', 'woocommerce' ),
|
||||
'view' => array( $this, 'import' ),
|
||||
'handler' => '',
|
||||
),
|
||||
'done' => array(
|
||||
'name' => __( 'Done!', 'woocommerce' ),
|
||||
'view' => array( $this, 'done' ),
|
||||
'handler' => '',
|
||||
),
|
||||
);
|
||||
|
||||
$this->steps = apply_filters( 'woocommerce_product_csv_importer_steps', $default_steps );
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
$this->step = isset( $_REQUEST['step'] ) ? sanitize_key( $_REQUEST['step'] ) : current( array_keys( $this->steps ) );
|
||||
$this->file = isset( $_REQUEST['file'] ) ? wc_clean( wp_unslash( $_REQUEST['file'] ) ) : '';
|
||||
$this->update_existing = isset( $_REQUEST['update_existing'] ) ? (bool) $_REQUEST['update_existing'] : false;
|
||||
$this->delimiter = ! empty( $_REQUEST['delimiter'] ) ? wc_clean( wp_unslash( $_REQUEST['delimiter'] ) ) : ',';
|
||||
$this->map_preferences = isset( $_REQUEST['map_preferences'] ) ? (bool) $_REQUEST['map_preferences'] : false;
|
||||
$this->character_encoding = isset( $_REQUEST['character_encoding'] ) ? wc_clean( wp_unslash( $_REQUEST['character_encoding'] ) ) : 'UTF-8';
|
||||
// phpcs:enable
|
||||
|
||||
// Import mappings for CSV data.
|
||||
include_once dirname( __FILE__ ) . '/mappings/mappings.php';
|
||||
|
||||
if ( $this->map_preferences ) {
|
||||
add_filter( 'woocommerce_csv_product_import_mapped_columns', array( $this, 'auto_map_user_preferences' ), 9999 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL for the next step's screen.
|
||||
*
|
||||
* @param string $step slug (default: current step).
|
||||
* @return string URL for next step if a next step exists.
|
||||
* Admin URL if it's the last step.
|
||||
* Empty string on failure.
|
||||
*/
|
||||
public function get_next_step_link( $step = '' ) {
|
||||
if ( ! $step ) {
|
||||
$step = $this->step;
|
||||
}
|
||||
|
||||
$keys = array_keys( $this->steps );
|
||||
|
||||
if ( end( $keys ) === $step ) {
|
||||
return admin_url();
|
||||
}
|
||||
|
||||
$step_index = array_search( $step, $keys, true );
|
||||
|
||||
if ( false === $step_index ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'step' => $keys[ $step_index + 1 ],
|
||||
'file' => str_replace( DIRECTORY_SEPARATOR, '/', $this->file ),
|
||||
'delimiter' => $this->delimiter,
|
||||
'update_existing' => $this->update_existing,
|
||||
'map_preferences' => $this->map_preferences,
|
||||
'character_encoding' => $this->character_encoding,
|
||||
'_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ), // wp_nonce_url() escapes & to & breaking redirects.
|
||||
);
|
||||
|
||||
return add_query_arg( $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output header view.
|
||||
*/
|
||||
protected function output_header() {
|
||||
include dirname( __FILE__ ) . '/views/html-csv-import-header.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output steps view.
|
||||
*/
|
||||
protected function output_steps() {
|
||||
include dirname( __FILE__ ) . '/views/html-csv-import-steps.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output footer view.
|
||||
*/
|
||||
protected function output_footer() {
|
||||
include dirname( __FILE__ ) . '/views/html-csv-import-footer.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add error message.
|
||||
*
|
||||
* @param string $message Error message.
|
||||
* @param array $actions List of actions with 'url' and 'label'.
|
||||
*/
|
||||
protected function add_error( $message, $actions = array() ) {
|
||||
$this->errors[] = array(
|
||||
'message' => $message,
|
||||
'actions' => $actions,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add error message.
|
||||
*/
|
||||
protected function output_errors() {
|
||||
if ( ! $this->errors ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $this->errors as $error ) {
|
||||
echo '<div class="error inline">';
|
||||
echo '<p>' . esc_html( $error['message'] ) . '</p>';
|
||||
|
||||
if ( ! empty( $error['actions'] ) ) {
|
||||
echo '<p>';
|
||||
foreach ( $error['actions'] as $action ) {
|
||||
echo '<a class="button button-primary" href="' . esc_url( $action['url'] ) . '">' . esc_html( $action['label'] ) . '</a> ';
|
||||
}
|
||||
echo '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch current step and show correct view.
|
||||
*/
|
||||
public function dispatch() {
|
||||
$output = '';
|
||||
|
||||
try {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
if ( ! empty( $_POST['save_step'] ) && ! empty( $this->steps[ $this->step ]['handler'] ) ) {
|
||||
if ( is_callable( $this->steps[ $this->step ]['handler'] ) ) {
|
||||
call_user_func( $this->steps[ $this->step ]['handler'], $this );
|
||||
}
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
if ( is_callable( $this->steps[ $this->step ]['view'] ) ) {
|
||||
call_user_func( $this->steps[ $this->step ]['view'], $this );
|
||||
}
|
||||
|
||||
$output = ob_get_clean();
|
||||
} catch ( \Exception $e ) {
|
||||
$this->add_error( $e->getMessage() );
|
||||
}
|
||||
|
||||
$this->output_header();
|
||||
$this->output_steps();
|
||||
$this->output_errors();
|
||||
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output is HTML we've generated ourselves.
|
||||
$this->output_footer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes AJAX requests related to a product CSV import.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
public static function dispatch_ajax() {
|
||||
global $wpdb;
|
||||
|
||||
check_ajax_referer( 'wc-product-import', 'security' );
|
||||
|
||||
try {
|
||||
$file = wc_clean( wp_unslash( $_POST['file'] ?? '' ) ); // PHPCS: input var ok.
|
||||
self::check_file_path( $file );
|
||||
|
||||
$params = array(
|
||||
'delimiter' => ! empty( $_POST['delimiter'] ) ? wc_clean( wp_unslash( $_POST['delimiter'] ) ) : ',', // PHPCS: input var ok.
|
||||
'start_pos' => isset( $_POST['position'] ) ? absint( $_POST['position'] ) : 0, // PHPCS: input var ok.
|
||||
'mapping' => isset( $_POST['mapping'] ) ? (array) wc_clean( wp_unslash( $_POST['mapping'] ) ) : array(), // PHPCS: input var ok.
|
||||
'update_existing' => isset( $_POST['update_existing'] ) ? (bool) $_POST['update_existing'] : false, // PHPCS: input var ok.
|
||||
'character_encoding' => isset( $_POST['character_encoding'] ) ? wc_clean( wp_unslash( $_POST['character_encoding'] ) ) : '',
|
||||
|
||||
/**
|
||||
* Batch size for the product import process.
|
||||
*
|
||||
* @param int $size Batch size.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
'lines' => apply_filters( 'woocommerce_product_import_batch_size', 30 ),
|
||||
'parse' => true,
|
||||
);
|
||||
|
||||
// Log failures.
|
||||
if ( 0 !== $params['start_pos'] ) {
|
||||
$error_log = array_filter( (array) get_user_option( 'product_import_error_log' ) );
|
||||
} else {
|
||||
$error_log = array();
|
||||
}
|
||||
|
||||
include_once WC_ABSPATH . 'includes/import/class-wc-product-csv-importer.php';
|
||||
|
||||
$importer = self::get_importer( $file, $params );
|
||||
$results = $importer->import();
|
||||
$percent_complete = $importer->get_percent_complete();
|
||||
$error_log = array_merge( $error_log, $results['failed'], $results['skipped'] );
|
||||
|
||||
update_user_option( get_current_user_id(), 'product_import_error_log', $error_log );
|
||||
|
||||
if ( 100 === $percent_complete ) {
|
||||
// @codingStandardsIgnoreStart.
|
||||
$wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_original_id' ) );
|
||||
$wpdb->delete( $wpdb->posts, array(
|
||||
'post_type' => 'product',
|
||||
'post_status' => 'importing',
|
||||
) );
|
||||
$wpdb->delete( $wpdb->posts, array(
|
||||
'post_type' => 'product_variation',
|
||||
'post_status' => 'importing',
|
||||
) );
|
||||
// @codingStandardsIgnoreEnd.
|
||||
|
||||
// Clean up orphaned data.
|
||||
$wpdb->query(
|
||||
"
|
||||
DELETE {$wpdb->posts}.* FROM {$wpdb->posts}
|
||||
LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->posts}.post_parent
|
||||
WHERE wp.ID IS NULL AND {$wpdb->posts}.post_type = 'product_variation'
|
||||
"
|
||||
);
|
||||
$wpdb->query(
|
||||
"
|
||||
DELETE {$wpdb->postmeta}.* FROM {$wpdb->postmeta}
|
||||
LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->postmeta}.post_id
|
||||
WHERE wp.ID IS NULL
|
||||
"
|
||||
);
|
||||
// @codingStandardsIgnoreStart.
|
||||
$wpdb->query( "
|
||||
DELETE tr.* FROM {$wpdb->term_relationships} tr
|
||||
LEFT JOIN {$wpdb->posts} wp ON wp.ID = tr.object_id
|
||||
LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
|
||||
WHERE wp.ID IS NULL
|
||||
AND tt.taxonomy IN ( '" . implode( "','", array_map( 'esc_sql', get_object_taxonomies( 'product' ) ) ) . "' )
|
||||
" );
|
||||
// @codingStandardsIgnoreEnd.
|
||||
|
||||
// Send success.
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'position' => 'done',
|
||||
'percentage' => 100,
|
||||
'url' => add_query_arg( array( '_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ) ), admin_url( 'edit.php?post_type=product&page=product_importer&step=done' ) ),
|
||||
'imported' => is_countable( $results['imported'] ) ? count( $results['imported'] ) : 0,
|
||||
'imported_variations' => is_countable( $results['imported_variations'] ) ? count( $results['imported_variations'] ) : 0,
|
||||
'failed' => is_countable( $results['failed'] ) ? count( $results['failed'] ) : 0,
|
||||
'updated' => is_countable( $results['updated'] ) ? count( $results['updated'] ) : 0,
|
||||
'skipped' => is_countable( $results['skipped'] ) ? count( $results['skipped'] ) : 0,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'position' => $importer->get_file_position(),
|
||||
'percentage' => $percent_complete,
|
||||
'imported' => is_countable( $results['imported'] ) ? count( $results['imported'] ) : 0,
|
||||
'imported_variations' => is_countable( $results['imported_variations'] ) ? count( $results['imported_variations'] ) : 0,
|
||||
'failed' => is_countable( $results['failed'] ) ? count( $results['failed'] ) : 0,
|
||||
'updated' => is_countable( $results['updated'] ) ? count( $results['updated'] ) : 0,
|
||||
'skipped' => is_countable( $results['skipped'] ) ? count( $results['skipped'] ) : 0,
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
wp_send_json_error( array( 'message' => $e->getMessage() ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output information about the uploading process.
|
||||
*/
|
||||
protected function upload_form() {
|
||||
$bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
|
||||
$size = size_format( $bytes );
|
||||
$upload_dir = wp_upload_dir();
|
||||
|
||||
include dirname( __FILE__ ) . '/views/html-product-csv-import-form.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the upload form and store options.
|
||||
*/
|
||||
public function upload_form_handler() {
|
||||
check_admin_referer( 'woocommerce-csv-importer' );
|
||||
|
||||
$file = $this->handle_upload();
|
||||
|
||||
if ( is_wp_error( $file ) ) {
|
||||
$this->add_error( $file->get_error_message() );
|
||||
return;
|
||||
} else {
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
wp_redirect( esc_url_raw( $this->get_next_step_link() ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the CSV upload and initial parsing of the file to prepare for
|
||||
* displaying author import options.
|
||||
*
|
||||
* @return string|WP_Error
|
||||
*/
|
||||
public function handle_upload() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce already verified in WC_Product_CSV_Importer_Controller::upload_form_handler()
|
||||
$file_url = isset( $_POST['file_url'] ) ? wc_clean( wp_unslash( $_POST['file_url'] ) ) : '';
|
||||
|
||||
try {
|
||||
if ( ! empty( $file_url ) ) {
|
||||
$path = ABSPATH . $file_url;
|
||||
self::check_file_path( $path );
|
||||
} else {
|
||||
$csv_import_util = wc_get_container()->get( Automattic\WooCommerce\Internal\Admin\ImportExport\CSVUploadHelper::class );
|
||||
$upload = $csv_import_util->handle_csv_upload( 'product', 'import', self::get_valid_csv_filetypes() );
|
||||
$path = $upload['file'];
|
||||
}
|
||||
|
||||
return $path;
|
||||
} catch ( \Exception $e ) {
|
||||
return new \WP_Error( 'woocommerce_product_csv_importer_upload_invalid_file', $e->getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping step.
|
||||
*/
|
||||
protected function mapping_form() {
|
||||
check_admin_referer( 'woocommerce-csv-importer' );
|
||||
self::check_file_path( $this->file );
|
||||
|
||||
$args = array(
|
||||
'lines' => 1,
|
||||
'delimiter' => $this->delimiter,
|
||||
'character_encoding' => $this->character_encoding,
|
||||
);
|
||||
|
||||
$importer = self::get_importer( $this->file, $args );
|
||||
$headers = $importer->get_raw_keys();
|
||||
$mapped_items = $this->auto_map_columns( $headers );
|
||||
$sample = current( $importer->get_raw_data() );
|
||||
|
||||
if ( empty( $sample ) ) {
|
||||
$this->add_error(
|
||||
__( 'The file is empty or using a different encoding than UTF-8, please try again with a new file.', 'woocommerce' ),
|
||||
array(
|
||||
array(
|
||||
'url' => admin_url( 'edit.php?post_type=product&page=product_importer' ),
|
||||
'label' => __( 'Upload a new file', 'woocommerce' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Force output the errors in the same page.
|
||||
$this->output_errors();
|
||||
return;
|
||||
}
|
||||
|
||||
include_once dirname( __FILE__ ) . '/views/html-csv-import-mapping.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the file if it exists and is valid.
|
||||
*/
|
||||
public function import() {
|
||||
// Displaying this page triggers Ajax action to run the import with a valid nonce,
|
||||
// therefore this page needs to be nonce protected as well.
|
||||
check_admin_referer( 'woocommerce-csv-importer' );
|
||||
self::check_file_path( $this->file );
|
||||
|
||||
if ( ! empty( $_POST['map_from'] ) && ! empty( $_POST['map_to'] ) ) {
|
||||
$mapping_from = wc_clean( wp_unslash( $_POST['map_from'] ) );
|
||||
$mapping_to = wc_clean( wp_unslash( $_POST['map_to'] ) );
|
||||
|
||||
// Save mapping preferences for future imports.
|
||||
update_user_option( get_current_user_id(), 'woocommerce_product_import_mapping', $mapping_to );
|
||||
} else {
|
||||
wp_redirect( esc_url_raw( $this->get_next_step_link( 'upload' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'wc-product-import',
|
||||
'wc_product_import_params',
|
||||
array(
|
||||
'import_nonce' => wp_create_nonce( 'wc-product-import' ),
|
||||
'mapping' => array(
|
||||
'from' => $mapping_from,
|
||||
'to' => $mapping_to,
|
||||
),
|
||||
'file' => $this->file,
|
||||
'update_existing' => $this->update_existing,
|
||||
'delimiter' => $this->delimiter,
|
||||
'character_encoding' => $this->character_encoding,
|
||||
)
|
||||
);
|
||||
wp_enqueue_script( 'wc-product-import' );
|
||||
|
||||
include_once dirname( __FILE__ ) . '/views/html-csv-import-progress.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Done step.
|
||||
*/
|
||||
protected function done() {
|
||||
check_admin_referer( 'woocommerce-csv-importer' );
|
||||
$imported = isset( $_GET['products-imported'] ) ? absint( $_GET['products-imported'] ) : 0;
|
||||
$imported_variations = isset( $_GET['products-imported-variations'] ) ? absint( $_GET['products-imported-variations'] ) : 0;
|
||||
$updated = isset( $_GET['products-updated'] ) ? absint( $_GET['products-updated'] ) : 0;
|
||||
$failed = isset( $_GET['products-failed'] ) ? absint( $_GET['products-failed'] ) : 0;
|
||||
$skipped = isset( $_GET['products-skipped'] ) ? absint( $_GET['products-skipped'] ) : 0;
|
||||
$file_name = isset( $_GET['file-name'] ) ? sanitize_text_field( wp_unslash( $_GET['file-name'] ) ) : '';
|
||||
$errors = array_filter( (array) get_user_option( 'product_import_error_log' ) );
|
||||
|
||||
include_once dirname( __FILE__ ) . '/views/html-csv-import-done.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Columns to normalize.
|
||||
*
|
||||
* @param array $columns List of columns names and keys.
|
||||
* @return array
|
||||
*/
|
||||
protected function normalize_columns_names( $columns ) {
|
||||
$normalized = array();
|
||||
|
||||
foreach ( $columns as $key => $value ) {
|
||||
$normalized[ strtolower( $key ) ] = $value;
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto map column names.
|
||||
*
|
||||
* @param array $raw_headers Raw header columns.
|
||||
* @param bool $num_indexes If should use numbers or raw header columns as indexes.
|
||||
* @return array
|
||||
*/
|
||||
protected function auto_map_columns( $raw_headers, $num_indexes = true ) {
|
||||
$weight_unit_label = I18nUtil::get_weight_unit_label( get_option( 'woocommerce_weight_unit', 'kg' ) );
|
||||
$dimension_unit_label = I18nUtil::get_dimensions_unit_label( get_option( 'woocommerce_dimension_unit', 'cm' ) );
|
||||
|
||||
/*
|
||||
* @hooked wc_importer_generic_mappings - 10
|
||||
* @hooked wc_importer_wordpress_mappings - 10
|
||||
* @hooked wc_importer_default_english_mappings - 100
|
||||
*/
|
||||
$default_columns = $this->normalize_columns_names(
|
||||
apply_filters(
|
||||
'woocommerce_csv_product_import_mapping_default_columns',
|
||||
array(
|
||||
__( 'ID', 'woocommerce' ) => 'id',
|
||||
__( 'Type', 'woocommerce' ) => 'type',
|
||||
__( 'SKU', 'woocommerce' ) => 'sku',
|
||||
__( 'Name', 'woocommerce' ) => 'name',
|
||||
__( 'Published', 'woocommerce' ) => 'published',
|
||||
__( 'Is featured?', 'woocommerce' ) => 'featured',
|
||||
__( 'Visibility in catalog', 'woocommerce' ) => 'catalog_visibility',
|
||||
__( 'Short description', 'woocommerce' ) => 'short_description',
|
||||
__( 'Description', 'woocommerce' ) => 'description',
|
||||
__( 'Date sale price starts', 'woocommerce' ) => 'date_on_sale_from',
|
||||
__( 'Date sale price ends', 'woocommerce' ) => 'date_on_sale_to',
|
||||
__( 'Tax status', 'woocommerce' ) => 'tax_status',
|
||||
__( 'Tax class', 'woocommerce' ) => 'tax_class',
|
||||
__( 'In stock?', 'woocommerce' ) => 'stock_status',
|
||||
__( 'Stock', 'woocommerce' ) => 'stock_quantity',
|
||||
__( 'Backorders allowed?', 'woocommerce' ) => 'backorders',
|
||||
__( 'Low stock amount', 'woocommerce' ) => 'low_stock_amount',
|
||||
__( 'Sold individually?', 'woocommerce' ) => 'sold_individually',
|
||||
/* translators: %s: Weight unit */
|
||||
sprintf( __( 'Weight (%s)', 'woocommerce' ), $weight_unit_label ) => 'weight',
|
||||
/* translators: %s: Length unit */
|
||||
sprintf( __( 'Length (%s)', 'woocommerce' ), $dimension_unit_label ) => 'length',
|
||||
/* translators: %s: Width unit */
|
||||
sprintf( __( 'Width (%s)', 'woocommerce' ), $dimension_unit_label ) => 'width',
|
||||
/* translators: %s: Height unit */
|
||||
sprintf( __( 'Height (%s)', 'woocommerce' ), $dimension_unit_label ) => 'height',
|
||||
__( 'Allow customer reviews?', 'woocommerce' ) => 'reviews_allowed',
|
||||
__( 'Purchase note', 'woocommerce' ) => 'purchase_note',
|
||||
__( 'Sale price', 'woocommerce' ) => 'sale_price',
|
||||
__( 'Regular price', 'woocommerce' ) => 'regular_price',
|
||||
__( 'Categories', 'woocommerce' ) => 'category_ids',
|
||||
__( 'Tags', 'woocommerce' ) => 'tag_ids',
|
||||
__( 'Shipping class', 'woocommerce' ) => 'shipping_class_id',
|
||||
__( 'Images', 'woocommerce' ) => 'images',
|
||||
__( 'Download limit', 'woocommerce' ) => 'download_limit',
|
||||
__( 'Download expiry days', 'woocommerce' ) => 'download_expiry',
|
||||
__( 'Parent', 'woocommerce' ) => 'parent_id',
|
||||
__( 'Upsells', 'woocommerce' ) => 'upsell_ids',
|
||||
__( 'Cross-sells', 'woocommerce' ) => 'cross_sell_ids',
|
||||
__( 'Grouped products', 'woocommerce' ) => 'grouped_products',
|
||||
__( 'External URL', 'woocommerce' ) => 'product_url',
|
||||
__( 'Button text', 'woocommerce' ) => 'button_text',
|
||||
__( 'Position', 'woocommerce' ) => 'menu_order',
|
||||
),
|
||||
$raw_headers
|
||||
)
|
||||
);
|
||||
|
||||
$special_columns = $this->get_special_columns(
|
||||
$this->normalize_columns_names(
|
||||
apply_filters(
|
||||
'woocommerce_csv_product_import_mapping_special_columns',
|
||||
array(
|
||||
/* translators: %d: Attribute number */
|
||||
__( 'Attribute %d name', 'woocommerce' ) => 'attributes:name',
|
||||
/* translators: %d: Attribute number */
|
||||
__( 'Attribute %d value(s)', 'woocommerce' ) => 'attributes:value',
|
||||
/* translators: %d: Attribute number */
|
||||
__( 'Attribute %d visible', 'woocommerce' ) => 'attributes:visible',
|
||||
/* translators: %d: Attribute number */
|
||||
__( 'Attribute %d global', 'woocommerce' ) => 'attributes:taxonomy',
|
||||
/* translators: %d: Attribute number */
|
||||
__( 'Attribute %d default', 'woocommerce' ) => 'attributes:default',
|
||||
/* translators: %d: Download number */
|
||||
__( 'Download %d ID', 'woocommerce' ) => 'downloads:id',
|
||||
/* translators: %d: Download number */
|
||||
__( 'Download %d name', 'woocommerce' ) => 'downloads:name',
|
||||
/* translators: %d: Download number */
|
||||
__( 'Download %d URL', 'woocommerce' ) => 'downloads:url',
|
||||
/* translators: %d: Meta number */
|
||||
__( 'Meta: %s', 'woocommerce' ) => 'meta:',
|
||||
),
|
||||
$raw_headers
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$headers = array();
|
||||
foreach ( $raw_headers as $key => $field ) {
|
||||
$normalized_field = strtolower( $field );
|
||||
$index = $num_indexes ? $key : $field;
|
||||
$headers[ $index ] = $normalized_field;
|
||||
|
||||
if ( isset( $default_columns[ $normalized_field ] ) ) {
|
||||
$headers[ $index ] = $default_columns[ $normalized_field ];
|
||||
} else {
|
||||
foreach ( $special_columns as $regex => $special_key ) {
|
||||
// Don't use the normalized field in the regex since meta might be case-sensitive.
|
||||
if ( preg_match( $regex, $field, $matches ) ) {
|
||||
$headers[ $index ] = $special_key . $matches[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'woocommerce_csv_product_import_mapped_columns', $headers, $raw_headers );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map columns using the user's latest import mappings.
|
||||
*
|
||||
* @param array $headers Header columns.
|
||||
* @return array
|
||||
*/
|
||||
public function auto_map_user_preferences( $headers ) {
|
||||
$mapping_preferences = get_user_option( 'woocommerce_product_import_mapping' );
|
||||
|
||||
if ( ! empty( $mapping_preferences ) && is_array( $mapping_preferences ) ) {
|
||||
return $mapping_preferences;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize special column name regex.
|
||||
*
|
||||
* @param string $value Raw special column name.
|
||||
* @return string
|
||||
*/
|
||||
protected function sanitize_special_column_name_regex( $value ) {
|
||||
return '/' . str_replace( array( '%d', '%s' ), '(.*)', trim( quotemeta( $value ) ) ) . '/i';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get special columns.
|
||||
*
|
||||
* @param array $columns Raw special columns.
|
||||
* @return array
|
||||
*/
|
||||
protected function get_special_columns( $columns ) {
|
||||
$formatted = array();
|
||||
|
||||
foreach ( $columns as $key => $value ) {
|
||||
$regex = $this->sanitize_special_column_name_regex( $key );
|
||||
|
||||
$formatted[ $regex ] = $value;
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mapping options.
|
||||
*
|
||||
* @param string $item Item name.
|
||||
* @return array
|
||||
*/
|
||||
protected function get_mapping_options( $item = '' ) {
|
||||
// Get index for special column names.
|
||||
$index = $item;
|
||||
|
||||
if ( preg_match( '/\d+/', $item, $matches ) ) {
|
||||
$index = $matches[0];
|
||||
}
|
||||
|
||||
// Properly format for meta field.
|
||||
$meta = str_replace( 'meta:', '', $item );
|
||||
|
||||
// Available options.
|
||||
$weight_unit_label = I18nUtil::get_weight_unit_label( get_option( 'woocommerce_weight_unit', 'kg' ) );
|
||||
$dimension_unit_label = I18nUtil::get_dimensions_unit_label( get_option( 'woocommerce_dimension_unit', 'cm' ) );
|
||||
$options = array(
|
||||
'id' => __( 'ID', 'woocommerce' ),
|
||||
'type' => __( 'Type', 'woocommerce' ),
|
||||
'sku' => __( 'SKU', 'woocommerce' ),
|
||||
'global_unique_id' => __( 'GTIN, UPC, EAN, or ISBN', 'woocommerce' ),
|
||||
'name' => __( 'Name', 'woocommerce' ),
|
||||
'published' => __( 'Published', 'woocommerce' ),
|
||||
'featured' => __( 'Is featured?', 'woocommerce' ),
|
||||
'catalog_visibility' => __( 'Visibility in catalog', 'woocommerce' ),
|
||||
'short_description' => __( 'Short description', 'woocommerce' ),
|
||||
'description' => __( 'Description', 'woocommerce' ),
|
||||
'price' => array(
|
||||
'name' => __( 'Price', 'woocommerce' ),
|
||||
'options' => array(
|
||||
'regular_price' => __( 'Regular price', 'woocommerce' ),
|
||||
'sale_price' => __( 'Sale price', 'woocommerce' ),
|
||||
'date_on_sale_from' => __( 'Date sale price starts', 'woocommerce' ),
|
||||
'date_on_sale_to' => __( 'Date sale price ends', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'tax_status' => __( 'Tax status', 'woocommerce' ),
|
||||
'tax_class' => __( 'Tax class', 'woocommerce' ),
|
||||
'stock_status' => __( 'In stock?', 'woocommerce' ),
|
||||
'stock_quantity' => _x( 'Stock', 'Quantity in stock', 'woocommerce' ),
|
||||
'backorders' => __( 'Backorders allowed?', 'woocommerce' ),
|
||||
'low_stock_amount' => __( 'Low stock amount', 'woocommerce' ),
|
||||
'sold_individually' => __( 'Sold individually?', 'woocommerce' ),
|
||||
/* translators: %s: weight unit */
|
||||
'weight' => sprintf( __( 'Weight (%s)', 'woocommerce' ), $weight_unit_label ),
|
||||
'dimensions' => array(
|
||||
'name' => __( 'Dimensions', 'woocommerce' ),
|
||||
'options' => array(
|
||||
/* translators: %s: dimension unit */
|
||||
'length' => sprintf( __( 'Length (%s)', 'woocommerce' ), $dimension_unit_label ),
|
||||
/* translators: %s: dimension unit */
|
||||
'width' => sprintf( __( 'Width (%s)', 'woocommerce' ), $dimension_unit_label ),
|
||||
/* translators: %s: dimension unit */
|
||||
'height' => sprintf( __( 'Height (%s)', 'woocommerce' ), $dimension_unit_label ),
|
||||
),
|
||||
),
|
||||
'category_ids' => __( 'Categories', 'woocommerce' ),
|
||||
'tag_ids' => __( 'Tags (comma separated)', 'woocommerce' ),
|
||||
'tag_ids_spaces' => __( 'Tags (space separated)', 'woocommerce' ),
|
||||
'shipping_class_id' => __( 'Shipping class', 'woocommerce' ),
|
||||
'images' => __( 'Images', 'woocommerce' ),
|
||||
'parent_id' => __( 'Parent', 'woocommerce' ),
|
||||
'upsell_ids' => __( 'Upsells', 'woocommerce' ),
|
||||
'cross_sell_ids' => __( 'Cross-sells', 'woocommerce' ),
|
||||
'grouped_products' => __( 'Grouped products', 'woocommerce' ),
|
||||
'external' => array(
|
||||
'name' => __( 'External product', 'woocommerce' ),
|
||||
'options' => array(
|
||||
'product_url' => __( 'External URL', 'woocommerce' ),
|
||||
'button_text' => __( 'Button text', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'downloads' => array(
|
||||
'name' => __( 'Downloads', 'woocommerce' ),
|
||||
'options' => array(
|
||||
'downloads:id' . $index => __( 'Download ID', 'woocommerce' ),
|
||||
'downloads:name' . $index => __( 'Download name', 'woocommerce' ),
|
||||
'downloads:url' . $index => __( 'Download URL', 'woocommerce' ),
|
||||
'download_limit' => __( 'Download limit', 'woocommerce' ),
|
||||
'download_expiry' => __( 'Download expiry days', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'attributes' => array(
|
||||
'name' => __( 'Attributes', 'woocommerce' ),
|
||||
'options' => array(
|
||||
'attributes:name' . $index => __( 'Attribute name', 'woocommerce' ),
|
||||
'attributes:value' . $index => __( 'Attribute value(s)', 'woocommerce' ),
|
||||
'attributes:taxonomy' . $index => __( 'Is a global attribute?', 'woocommerce' ),
|
||||
'attributes:visible' . $index => __( 'Attribute visibility', 'woocommerce' ),
|
||||
'attributes:default' . $index => __( 'Default attribute', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'reviews_allowed' => __( 'Allow customer reviews?', 'woocommerce' ),
|
||||
'purchase_note' => __( 'Purchase note', 'woocommerce' ),
|
||||
'meta:' . $meta => __( 'Import as meta data', 'woocommerce' ),
|
||||
'menu_order' => __( 'Position', 'woocommerce' ),
|
||||
);
|
||||
|
||||
return apply_filters( 'woocommerce_csv_product_import_mapping_options', $options, $item );
|
||||
}
|
||||
}
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
<?php
|
||||
/**
|
||||
* Tax importer class file
|
||||
*
|
||||
* @version 2.3.0
|
||||
* @package WooCommerce\Admin
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_Importer' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tax Rates importer - import tax rates and local tax rates into WooCommerce.
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
* @version 2.3.0
|
||||
*/
|
||||
class WC_Tax_Rate_Importer extends WP_Importer {
|
||||
|
||||
/**
|
||||
* The current file id.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The current file url.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $file_url;
|
||||
|
||||
/**
|
||||
* The current import page.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $import_page;
|
||||
|
||||
/**
|
||||
* The current delimiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $delimiter;
|
||||
|
||||
/**
|
||||
* Error message for import.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $import_error_message;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->import_page = 'woocommerce_tax_rate_csv';
|
||||
$this->delimiter = empty( $_POST['delimiter'] ) ? ',' : (string) wc_clean( wp_unslash( $_POST['delimiter'] ) ); // WPCS: CSRF ok.
|
||||
}
|
||||
|
||||
/**
|
||||
* Registered callback function for the WordPress Importer.
|
||||
*
|
||||
* Manages the three separate stages of the CSV import process.
|
||||
*/
|
||||
public function dispatch() {
|
||||
|
||||
$this->header();
|
||||
|
||||
$step = empty( $_GET['step'] ) ? 0 : (int) $_GET['step'];
|
||||
|
||||
switch ( $step ) {
|
||||
|
||||
case 0:
|
||||
$this->greet();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
check_admin_referer( 'import-upload' );
|
||||
|
||||
if ( $this->handle_upload() ) {
|
||||
|
||||
if ( $this->id ) {
|
||||
$file = get_attached_file( $this->id );
|
||||
} else {
|
||||
$file = ABSPATH . $this->file_url;
|
||||
}
|
||||
|
||||
add_filter( 'http_request_timeout', array( $this, 'bump_request_timeout' ) );
|
||||
|
||||
$this->import( $file );
|
||||
} else {
|
||||
$this->import_error( $this->import_error_message );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$this->footer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Import is starting.
|
||||
*/
|
||||
private function import_start() {
|
||||
if ( function_exists( 'gc_enable' ) ) {
|
||||
gc_enable(); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.gc_enableFound
|
||||
}
|
||||
wc_set_time_limit( 0 );
|
||||
@ob_flush();
|
||||
@flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8 encode the data if `$enc` value isn't UTF-8.
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
* @param string $enc Encoding.
|
||||
* @return string
|
||||
*/
|
||||
public function format_data_from_csv( $data, $enc ) {
|
||||
return ( 'UTF-8' === $enc ) ? $data : utf8_encode( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the file if it exists and is valid.
|
||||
*
|
||||
* @param mixed $file File.
|
||||
*/
|
||||
public function import( $file ) {
|
||||
if ( ! is_file( $file ) ) {
|
||||
$this->import_error( __( 'The file does not exist, please try again.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$this->import_start();
|
||||
|
||||
$loop = 0;
|
||||
$handle = fopen( $file, 'r' );
|
||||
|
||||
if ( false !== $handle ) {
|
||||
|
||||
$header = fgetcsv( $handle, 0, $this->delimiter );
|
||||
$count = is_countable( $header ) ? count( $header ) : 0;
|
||||
if ( 10 === $count ) {
|
||||
|
||||
$row = fgetcsv( $handle, 0, $this->delimiter );
|
||||
|
||||
while ( false !== $row ) {
|
||||
|
||||
list( $country, $state, $postcode, $city, $rate, $name, $priority, $compound, $shipping, $class ) = $row;
|
||||
|
||||
$tax_rate = array(
|
||||
'tax_rate_country' => $country,
|
||||
'tax_rate_state' => $state,
|
||||
'tax_rate' => $rate,
|
||||
'tax_rate_name' => $name,
|
||||
'tax_rate_priority' => $priority,
|
||||
'tax_rate_compound' => $compound ? 1 : 0,
|
||||
'tax_rate_shipping' => $shipping ? 1 : 0,
|
||||
'tax_rate_order' => $loop ++,
|
||||
'tax_rate_class' => $class,
|
||||
);
|
||||
|
||||
$tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate );
|
||||
WC_Tax::_update_tax_rate_postcodes( $tax_rate_id, wc_clean( $postcode ) );
|
||||
WC_Tax::_update_tax_rate_cities( $tax_rate_id, wc_clean( $city ) );
|
||||
|
||||
$row = fgetcsv( $handle, 0, $this->delimiter );
|
||||
}
|
||||
} else {
|
||||
$this->import_error( __( 'The CSV is invalid.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
fclose( $handle );
|
||||
}
|
||||
|
||||
// Show Result.
|
||||
echo '<div class="updated settings-error"><p>';
|
||||
printf(
|
||||
/* translators: %s: tax rates count */
|
||||
esc_html__( 'Import complete - imported %s tax rates.', 'woocommerce' ),
|
||||
'<strong>' . absint( $loop ) . '</strong>'
|
||||
);
|
||||
echo '</p></div>';
|
||||
|
||||
$this->import_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs post-import cleanup of files and the cache.
|
||||
*/
|
||||
public function import_end() {
|
||||
echo '<p>' . esc_html__( 'All done!', 'woocommerce' ) . ' <a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=tax' ) ) . '">' . esc_html__( 'View tax rates', 'woocommerce' ) . '</a></p>';
|
||||
|
||||
do_action( 'import_end' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the import error message.
|
||||
*
|
||||
* @param string $message Error message.
|
||||
*/
|
||||
protected function set_import_error_message( $message ) {
|
||||
$this->import_error_message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the CSV upload and initial parsing of the file to prepare for.
|
||||
* displaying author import options.
|
||||
*
|
||||
* @return bool False if error uploading or invalid file, true otherwise
|
||||
*/
|
||||
public function handle_upload() {
|
||||
$file_url = isset( $_POST['file_url'] ) ? wc_clean( wp_unslash( $_POST['file_url'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce already verified in WC_Tax_Rate_Importer::dispatch()
|
||||
|
||||
if ( empty( $file_url ) ) {
|
||||
$file = wp_import_handle_upload();
|
||||
|
||||
if ( isset( $file['error'] ) ) {
|
||||
$this->set_import_error_message( $file['error'] );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! wc_is_file_valid_csv( $file['file'], false ) ) {
|
||||
// Remove file if not valid.
|
||||
wp_delete_attachment( $file['id'], true );
|
||||
|
||||
$this->set_import_error_message( __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->id = absint( $file['id'] );
|
||||
} elseif (
|
||||
( 0 === stripos( realpath( ABSPATH . $file_url ), ABSPATH ) ) &&
|
||||
file_exists( ABSPATH . $file_url )
|
||||
) {
|
||||
if ( ! wc_is_file_valid_csv( ABSPATH . $file_url ) ) {
|
||||
$this->set_import_error_message( __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->file_url = esc_attr( $file_url );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output header html.
|
||||
*/
|
||||
public function header() {
|
||||
echo '<div class="wrap">';
|
||||
echo '<h1>' . esc_html__( 'Import tax rates', 'woocommerce' ) . '</h1>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output footer html.
|
||||
*/
|
||||
public function footer() {
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output information about the uploading process.
|
||||
*/
|
||||
public function greet() {
|
||||
|
||||
echo '<div class="narrow">';
|
||||
echo '<p>' . esc_html__( 'Hi there! Upload a CSV file containing tax rates to import the contents into your shop. Choose a .csv file to upload, then click "Upload file and import".', 'woocommerce' ) . '</p>';
|
||||
|
||||
/* translators: 1: Link to tax rates sample file 2: Closing link. */
|
||||
echo '<p>' . sprintf( esc_html__( 'Your CSV needs to include columns in a specific order. %1$sClick here to download a sample%2$s.', 'woocommerce' ), '<a href="' . esc_url( WC()->plugin_url() ) . '/sample-data/sample_tax_rates.csv">', '</a>' ) . '</p>';
|
||||
|
||||
$action = 'admin.php?import=woocommerce_tax_rate_csv&step=1';
|
||||
|
||||
$bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
|
||||
$size = size_format( $bytes );
|
||||
$upload_dir = wp_upload_dir();
|
||||
if ( ! empty( $upload_dir['error'] ) ) :
|
||||
?>
|
||||
<div class="error">
|
||||
<p><?php esc_html_e( 'Before you can upload your import file, you will need to fix the following error:', 'woocommerce' ); ?></p>
|
||||
<p><strong><?php echo esc_html( $upload_dir['error'] ); ?></strong></p>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<form enctype="multipart/form-data" id="import-upload-form" method="post" action="<?php echo esc_attr( wp_nonce_url( $action, 'import-upload' ) ); ?>">
|
||||
<table class="form-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="upload"><?php esc_html_e( 'Choose a file from your computer:', 'woocommerce' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="file" id="upload" name="import" size="25" />
|
||||
<input type="hidden" name="action" value="save" />
|
||||
<input type="hidden" name="max_file_size" value="<?php echo absint( $bytes ); ?>" />
|
||||
<small>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s: maximum upload size */
|
||||
esc_html__( 'Maximum size: %s', 'woocommerce' ),
|
||||
esc_attr( $size )
|
||||
);
|
||||
?>
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="file_url"><?php esc_html_e( 'OR enter path to file:', 'woocommerce' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<?php echo ' ' . esc_html( ABSPATH ) . ' '; ?><input type="text" id="file_url" name="file_url" size="25" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><label><?php esc_html_e( 'Delimiter', 'woocommerce' ); ?></label><br/></th>
|
||||
<td><input type="text" name="delimiter" placeholder="," size="2" /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="submit">
|
||||
<button type="submit" class="button" value="<?php esc_attr_e( 'Upload file and import', 'woocommerce' ); ?>"><?php esc_html_e( 'Upload file and import', 'woocommerce' ); ?></button>
|
||||
</p>
|
||||
</form>
|
||||
<?php
|
||||
endif;
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Show import error and quit.
|
||||
*
|
||||
* @param string $message Error message.
|
||||
*/
|
||||
private function import_error( $message = '' ) {
|
||||
echo '<p><strong>' . esc_html__( 'Sorry, there has been an error.', 'woocommerce' ) . '</strong><br />';
|
||||
if ( $message ) {
|
||||
echo esc_html( $message );
|
||||
}
|
||||
echo '</p>';
|
||||
$this->footer();
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Added to http_request_timeout filter to force timeout at 60 seconds during import.
|
||||
*
|
||||
* @param int $val Value.
|
||||
* @return int 60
|
||||
*/
|
||||
public function bump_request_timeout( $val ) {
|
||||
return 60;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* Default mappings
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Importer current locale.
|
||||
*
|
||||
* @since 3.1.0
|
||||
* @return string
|
||||
*/
|
||||
function wc_importer_current_locale() {
|
||||
$locale = get_locale();
|
||||
if ( function_exists( 'get_user_locale' ) ) {
|
||||
$locale = get_user_locale();
|
||||
}
|
||||
|
||||
return $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add English mapping placeholders when not using English as current language.
|
||||
*
|
||||
* @since 3.1.0
|
||||
* @param array $mappings Importer columns mappings.
|
||||
* @return array
|
||||
*/
|
||||
function wc_importer_default_english_mappings( $mappings ) {
|
||||
if ( 'en_US' === wc_importer_current_locale() && is_array( $mappings ) && count( $mappings ) > 0 ) {
|
||||
return $mappings;
|
||||
}
|
||||
|
||||
$weight_unit = get_option( 'woocommerce_weight_unit' );
|
||||
$dimension_unit = get_option( 'woocommerce_dimension_unit' );
|
||||
$new_mappings = array(
|
||||
'ID' => 'id',
|
||||
'Type' => 'type',
|
||||
'SKU' => 'sku',
|
||||
'Name' => 'name',
|
||||
'Published' => 'published',
|
||||
'Is featured?' => 'featured',
|
||||
'Visibility in catalog' => 'catalog_visibility',
|
||||
'Short description' => 'short_description',
|
||||
'Description' => 'description',
|
||||
'Date sale price starts' => 'date_on_sale_from',
|
||||
'Date sale price ends' => 'date_on_sale_to',
|
||||
'Tax status' => 'tax_status',
|
||||
'Tax class' => 'tax_class',
|
||||
'In stock?' => 'stock_status',
|
||||
'Stock' => 'stock_quantity',
|
||||
'Backorders allowed?' => 'backorders',
|
||||
'Low stock amount' => 'low_stock_amount',
|
||||
'Sold individually?' => 'sold_individually',
|
||||
sprintf( 'Weight (%s)', $weight_unit ) => 'weight',
|
||||
sprintf( 'Length (%s)', $dimension_unit ) => 'length',
|
||||
sprintf( 'Width (%s)', $dimension_unit ) => 'width',
|
||||
sprintf( 'Height (%s)', $dimension_unit ) => 'height',
|
||||
'Allow customer reviews?' => 'reviews_allowed',
|
||||
'Purchase note' => 'purchase_note',
|
||||
'Sale price' => 'sale_price',
|
||||
'Regular price' => 'regular_price',
|
||||
'Categories' => 'category_ids',
|
||||
'Tags' => 'tag_ids',
|
||||
'Shipping class' => 'shipping_class_id',
|
||||
'Images' => 'images',
|
||||
'Download limit' => 'download_limit',
|
||||
'Download expiry days' => 'download_expiry',
|
||||
'Parent' => 'parent_id',
|
||||
'Upsells' => 'upsell_ids',
|
||||
'Cross-sells' => 'cross_sell_ids',
|
||||
'Grouped products' => 'grouped_products',
|
||||
'External URL' => 'product_url',
|
||||
'Button text' => 'button_text',
|
||||
'Position' => 'menu_order',
|
||||
);
|
||||
|
||||
return array_merge( $mappings, $new_mappings );
|
||||
}
|
||||
add_filter( 'woocommerce_csv_product_import_mapping_default_columns', 'wc_importer_default_english_mappings', 100 );
|
||||
|
||||
/**
|
||||
* Add English special mapping placeholders when not using English as current language.
|
||||
*
|
||||
* @since 3.1.0
|
||||
* @param array $mappings Importer columns mappings.
|
||||
* @return array
|
||||
*/
|
||||
function wc_importer_default_special_english_mappings( $mappings ) {
|
||||
if ( 'en_US' === wc_importer_current_locale() && is_array( $mappings ) && count( $mappings ) > 0 ) {
|
||||
return $mappings;
|
||||
}
|
||||
|
||||
$new_mappings = array(
|
||||
'Attribute %d name' => 'attributes:name',
|
||||
'Attribute %d value(s)' => 'attributes:value',
|
||||
'Attribute %d visible' => 'attributes:visible',
|
||||
'Attribute %d global' => 'attributes:taxonomy',
|
||||
'Attribute %d default' => 'attributes:default',
|
||||
'Download %d ID' => 'downloads:id',
|
||||
'Download %d name' => 'downloads:name',
|
||||
'Download %d URL' => 'downloads:url',
|
||||
'Meta: %s' => 'meta:',
|
||||
);
|
||||
|
||||
return array_merge( $mappings, $new_mappings );
|
||||
}
|
||||
add_filter( 'woocommerce_csv_product_import_mapping_special_columns', 'wc_importer_default_special_english_mappings', 100 );
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Generic mappings
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add generic mappings.
|
||||
*
|
||||
* @since 3.1.0
|
||||
* @param array $mappings Importer columns mappings.
|
||||
* @return array
|
||||
*/
|
||||
function wc_importer_generic_mappings( $mappings ) {
|
||||
$generic_mappings = array(
|
||||
__( 'Title', 'woocommerce' ) => 'name',
|
||||
__( 'Product Title', 'woocommerce' ) => 'name',
|
||||
__( 'Price', 'woocommerce' ) => 'regular_price',
|
||||
__( 'Parent SKU', 'woocommerce' ) => 'parent_id',
|
||||
__( 'Quantity', 'woocommerce' ) => 'stock_quantity',
|
||||
__( 'Menu order', 'woocommerce' ) => 'menu_order',
|
||||
);
|
||||
|
||||
return array_merge( $mappings, $generic_mappings );
|
||||
}
|
||||
add_filter( 'woocommerce_csv_product_import_mapping_default_columns', 'wc_importer_generic_mappings' );
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Load up extra automatic mappings for the CSV importer.
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require dirname( __FILE__ ) . '/default.php';
|
||||
require dirname( __FILE__ ) . '/generic.php';
|
||||
require dirname( __FILE__ ) . '/shopify.php';
|
||||
require dirname( __FILE__ ) . '/wordpress.php';
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* Shopify mappings
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Shopify mappings.
|
||||
*
|
||||
* @since 3.7.0
|
||||
* @param array $mappings Importer columns mappings.
|
||||
* @param array $raw_headers Raw headers from CSV being imported.
|
||||
* @return array
|
||||
*/
|
||||
function wc_importer_shopify_mappings( $mappings, $raw_headers ) {
|
||||
// Only map if this is looks like a Shopify export.
|
||||
if ( 0 !== count( array_diff( array( 'Title', 'Body (HTML)', 'Type', 'Variant SKU' ), $raw_headers ) ) ) {
|
||||
return $mappings;
|
||||
}
|
||||
$shopify_mappings = array(
|
||||
'Variant SKU' => 'sku',
|
||||
'Title' => 'name',
|
||||
'Body (HTML)' => 'description',
|
||||
'Quantity' => 'stock_quantity',
|
||||
'Variant Inventory Qty' => 'stock_quantity',
|
||||
'Image Src' => 'images',
|
||||
'Variant Image' => 'images',
|
||||
'Variant SKU' => 'sku',
|
||||
'Variant Price' => 'sale_price',
|
||||
'Variant Compare At Price' => 'regular_price',
|
||||
'Type' => 'category_ids',
|
||||
'Tags' => 'tag_ids_spaces',
|
||||
'Variant Grams' => 'weight',
|
||||
'Variant Requires Shipping' => 'meta:shopify_requires_shipping',
|
||||
'Variant Taxable' => 'tax_status',
|
||||
);
|
||||
return array_merge( $mappings, $shopify_mappings );
|
||||
}
|
||||
add_filter( 'woocommerce_csv_product_import_mapping_default_columns', 'wc_importer_shopify_mappings', 10, 2 );
|
||||
|
||||
/**
|
||||
* Add special wildcard Shopify mappings.
|
||||
*
|
||||
* @since 3.7.0
|
||||
* @param array $mappings Importer columns mappings.
|
||||
* @param array $raw_headers Raw headers from CSV being imported.
|
||||
* @return array
|
||||
*/
|
||||
function wc_importer_shopify_special_mappings( $mappings, $raw_headers ) {
|
||||
// Only map if this is looks like a Shopify export.
|
||||
if ( 0 !== count( array_diff( array( 'Title', 'Body (HTML)', 'Type', 'Variant SKU' ), $raw_headers ) ) ) {
|
||||
return $mappings;
|
||||
}
|
||||
$shopify_mappings = array(
|
||||
'Option%d Name' => 'attributes:name',
|
||||
'Option%d Value' => 'attributes:value',
|
||||
);
|
||||
return array_merge( $mappings, $shopify_mappings );
|
||||
}
|
||||
add_filter( 'woocommerce_csv_product_import_mapping_special_columns', 'wc_importer_shopify_special_mappings', 10, 2 );
|
||||
|
||||
/**
|
||||
* Expand special Shopify columns to WC format.
|
||||
*
|
||||
* @since 3.7.0
|
||||
* @param array $data Array of data.
|
||||
* @return array Expanded data.
|
||||
*/
|
||||
function wc_importer_shopify_expand_data( $data ) {
|
||||
if ( isset( $data['meta:shopify_requires_shipping'] ) ) {
|
||||
$requires_shipping = wc_string_to_bool( $data['meta:shopify_requires_shipping'] );
|
||||
|
||||
if ( ! $requires_shipping ) {
|
||||
if ( isset( $data['type'] ) ) {
|
||||
$data['type'][] = 'virtual';
|
||||
} else {
|
||||
$data['type'] = array( 'virtual' );
|
||||
}
|
||||
}
|
||||
|
||||
unset( $data['meta:shopify_requires_shipping'] );
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
add_filter( 'woocommerce_product_importer_pre_expand_data', 'wc_importer_shopify_expand_data' );
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* WordPress mappings
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add mappings for WordPress tables.
|
||||
*
|
||||
* @since 3.1.0
|
||||
* @param array $mappings Importer columns mappings.
|
||||
* @return array
|
||||
*/
|
||||
function wc_importer_wordpress_mappings( $mappings ) {
|
||||
|
||||
$wp_mappings = array(
|
||||
'post_id' => 'id',
|
||||
'post_title' => 'name',
|
||||
'post_content' => 'description',
|
||||
'post_excerpt' => 'short_description',
|
||||
'post_parent' => 'parent_id',
|
||||
);
|
||||
|
||||
return array_merge( $mappings, $wp_mappings );
|
||||
}
|
||||
add_filter( 'woocommerce_csv_product_import_mapping_default_columns', 'wc_importer_wordpress_mappings' );
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin View: Importer - Done!
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<div class="wc-progress-form-content woocommerce-importer">
|
||||
<section class="woocommerce-importer-done">
|
||||
<?php
|
||||
$results = array();
|
||||
|
||||
if ( 0 < $imported ) {
|
||||
$results[] = sprintf(
|
||||
/* translators: %d: products count */
|
||||
_n( '%s product imported', '%s products imported', $imported, 'woocommerce' ),
|
||||
'<strong>' . number_format_i18n( $imported ) . '</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
if ( 0 < $updated ) {
|
||||
$results[] = sprintf(
|
||||
/* translators: %d: products count */
|
||||
_n( '%s product updated', '%s products updated', $updated, 'woocommerce' ),
|
||||
'<strong>' . number_format_i18n( $updated ) . '</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
if ( 0 < $imported_variations ) {
|
||||
$results[] = sprintf(
|
||||
/* translators: %d: products count */
|
||||
_n( '%s variations imported', '%s variations imported', $imported_variations, 'woocommerce' ),
|
||||
'<strong>' . number_format_i18n( $imported_variations ) . '</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
if ( 0 < $skipped ) {
|
||||
$results[] = sprintf(
|
||||
/* translators: %d: products count */
|
||||
_n( '%s product was skipped', '%s products were skipped', $skipped, 'woocommerce' ),
|
||||
'<strong>' . number_format_i18n( $skipped ) . '</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
if ( 0 < $failed ) {
|
||||
$results [] = sprintf(
|
||||
/* translators: %d: products count */
|
||||
_n( 'Failed to import %s product', 'Failed to import %s products', $failed, 'woocommerce' ),
|
||||
'<strong>' . number_format_i18n( $failed ) . '</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
if ( 0 < $failed || 0 < $skipped ) {
|
||||
$results[] = '<a href="#" class="woocommerce-importer-done-view-errors">' . __( 'View import log', 'woocommerce' ) . '</a>';
|
||||
}
|
||||
|
||||
if ( ! empty( $file_name ) ) {
|
||||
$results[] = sprintf(
|
||||
/* translators: %s: File name */
|
||||
__( 'File uploaded: %s', 'woocommerce' ),
|
||||
'<strong>' . $file_name . '</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
/* translators: %d: import results */
|
||||
echo wp_kses_post( __( 'Import complete!', 'woocommerce' ) . ' ' . implode( '. ', $results ) );
|
||||
?>
|
||||
</section>
|
||||
<section class="wc-importer-error-log" style="display:none">
|
||||
<table class="widefat wc-importer-error-log-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Product', 'woocommerce' ); ?></th>
|
||||
<th><?php esc_html_e( 'Reason for failure', 'woocommerce' ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if ( is_array( $errors ) && count( $errors ) ) {
|
||||
foreach ( $errors as $error ) {
|
||||
if ( ! is_wp_error( $error ) ) {
|
||||
continue;
|
||||
}
|
||||
$error_data = $error->get_error_data();
|
||||
?>
|
||||
<tr>
|
||||
<th><code><?php echo esc_html( $error_data['row'] ); ?></code></th>
|
||||
<td><?php echo wp_kses_post( $error->get_error_message() ); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<script type="text/javascript">
|
||||
jQuery(function() {
|
||||
jQuery( '.woocommerce-importer-done-view-errors' ).on( 'click', function() {
|
||||
jQuery( '.wc-importer-error-log' ).slideToggle();
|
||||
return false;
|
||||
} );
|
||||
} );
|
||||
</script>
|
||||
<div class="wc-actions">
|
||||
<a class="button button-primary" href="<?php echo esc_url( admin_url( 'edit.php?post_type=product' ) ); ?>"><?php esc_html_e( 'View products', 'woocommerce' ); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin View: Header
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin View: Header
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<div class="wrap woocommerce">
|
||||
<h1><?php esc_html_e( 'Import Products', 'woocommerce' ); ?></h1>
|
||||
|
||||
<div class="woocommerce-progress-form-wrapper">
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin View: Importer - CSV mapping
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<form class="wc-progress-form-content woocommerce-importer" method="post" action="<?php echo esc_url( $this->get_next_step_link() ); ?>">
|
||||
<header>
|
||||
<h2><?php esc_html_e( 'Map CSV fields to products', 'woocommerce' ); ?></h2>
|
||||
<p><?php esc_html_e( 'Select fields from your CSV file to map against products fields, or to ignore during import.', 'woocommerce' ); ?></p>
|
||||
</header>
|
||||
<section class="wc-importer-mapping-table-wrapper">
|
||||
<table class="widefat wc-importer-mapping-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Column name', 'woocommerce' ); ?></th>
|
||||
<th><?php esc_html_e( 'Map to field', 'woocommerce' ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ( $headers as $index => $name ) : ?>
|
||||
<?php $mapped_value = $mapped_items[ $index ]; ?>
|
||||
<tr>
|
||||
<td class="wc-importer-mapping-table-name">
|
||||
<?php echo esc_html( $name ); ?>
|
||||
<?php if ( ! empty( $sample[ $index ] ) ) : ?>
|
||||
<span class="description"><?php esc_html_e( 'Sample:', 'woocommerce' ); ?> <code><?php echo esc_html( $sample[ $index ] ); ?></code></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="wc-importer-mapping-table-field">
|
||||
<input type="hidden" name="map_from[<?php echo esc_attr( $index ); ?>]" value="<?php echo esc_attr( $name ); ?>" />
|
||||
<select name="map_to[<?php echo esc_attr( $index ); ?>]">
|
||||
<option value=""><?php esc_html_e( 'Do not import', 'woocommerce' ); ?></option>
|
||||
<option value="">--------------</option>
|
||||
<?php foreach ( $this->get_mapping_options( $mapped_value ) as $key => $value ) : ?>
|
||||
<?php if ( is_array( $value ) ) : ?>
|
||||
<optgroup label="<?php echo esc_attr( $value['name'] ); ?>">
|
||||
<?php foreach ( $value['options'] as $sub_key => $sub_value ) : ?>
|
||||
<option value="<?php echo esc_attr( $sub_key ); ?>" <?php selected( $mapped_value, $sub_key ); ?>><?php echo esc_html( $sub_value ); ?></option>
|
||||
<?php endforeach ?>
|
||||
</optgroup>
|
||||
<?php else : ?>
|
||||
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $mapped_value, $key ); ?>><?php echo esc_html( $value ); ?></option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<div class="wc-actions">
|
||||
<button type="submit" class="button button-primary button-next" value="<?php esc_attr_e( 'Run the importer', 'woocommerce' ); ?>" name="save_step"><?php esc_html_e( 'Run the importer', 'woocommerce' ); ?></button>
|
||||
<input type="hidden" name="file" value="<?php echo esc_attr( $this->file ); ?>" />
|
||||
<input type="hidden" name="delimiter" value="<?php echo esc_attr( $this->delimiter ); ?>" />
|
||||
<input type="hidden" name="update_existing" value="<?php echo (int) $this->update_existing; ?>" />
|
||||
<?php if ( $args['character_encoding'] ) { ?>
|
||||
<input type="hidden" name="character_encoding" value="<?php echo esc_html( $args['character_encoding'] ); ?>" />
|
||||
<?php } ?>
|
||||
<?php wp_nonce_field( 'woocommerce-csv-importer' ); ?>
|
||||
</div>
|
||||
</form>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin View: Importer - CSV import progress
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<div class="wc-progress-form-content woocommerce-importer woocommerce-importer__importing">
|
||||
<header>
|
||||
<span class="spinner is-active"></span>
|
||||
<h2><?php esc_html_e( 'Importing', 'woocommerce' ); ?></h2>
|
||||
<p><?php esc_html_e( 'Your products are now being imported...', 'woocommerce' ); ?></p>
|
||||
</header>
|
||||
<section>
|
||||
<progress class="woocommerce-importer-progress" max="100" value="0"></progress>
|
||||
</section>
|
||||
</div>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin View: Steps
|
||||
*
|
||||
* @package WooCommerce\Admin\Importers
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<ol class="wc-progress-steps">
|
||||
<?php foreach ( $this->steps as $step_key => $step ) : ?>
|
||||
<?php
|
||||
$step_class = '';
|
||||
if ( $step_key === $this->step ) {
|
||||
$step_class = 'active';
|
||||
} elseif ( array_search( $this->step, array_keys( $this->steps ), true ) > array_search( $step_key, array_keys( $this->steps ), true ) ) {
|
||||
$step_class = 'done';
|
||||
}
|
||||
?>
|
||||
<li class="<?php echo esc_attr( $step_class ); ?>">
|
||||
<?php echo esc_html( $step['name'] ); ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin View: Product import form
|
||||
*
|
||||
* @package WooCommerce\Admin
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<form class="wc-progress-form-content woocommerce-importer" enctype="multipart/form-data" method="post">
|
||||
<header>
|
||||
<h2><?php esc_html_e( 'Import products from a CSV file', 'woocommerce' ); ?></h2>
|
||||
<p><?php esc_html_e( 'This tool allows you to import (or merge) product data to your store from a CSV or TXT file.', 'woocommerce' ); ?></p>
|
||||
</header>
|
||||
<section>
|
||||
<table class="form-table woocommerce-importer-options">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="upload">
|
||||
<?php esc_html_e( 'Choose a CSV file from your computer:', 'woocommerce' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<?php
|
||||
if ( ! empty( $upload_dir['error'] ) ) {
|
||||
?>
|
||||
<div class="inline error">
|
||||
<p><?php esc_html_e( 'Before you can upload your import file, you will need to fix the following error:', 'woocommerce' ); ?></p>
|
||||
<p><strong><?php echo esc_html( $upload_dir['error'] ); ?></strong></p>
|
||||
</div>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<input type="file" id="upload" name="import" size="25" />
|
||||
<input type="hidden" name="action" value="save" />
|
||||
<input type="hidden" name="max_file_size" value="<?php echo esc_attr( $bytes ); ?>" />
|
||||
<br>
|
||||
<small>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s: maximum upload size */
|
||||
esc_html__( 'Maximum size: %s', 'woocommerce' ),
|
||||
esc_html( $size )
|
||||
);
|
||||
?>
|
||||
</small>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><label for="woocommerce-importer-update-existing"><?php esc_html_e( 'Update existing products', 'woocommerce' ); ?></label><br/></th>
|
||||
<td>
|
||||
<input type="hidden" name="update_existing" value="0" />
|
||||
<input type="checkbox" id="woocommerce-importer-update-existing" name="update_existing" value="1" />
|
||||
<label for="woocommerce-importer-update-existing"><?php esc_html_e( 'Existing products that match by ID or SKU will be updated. Products that do not exist will be skipped.', 'woocommerce' ); ?></label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="woocommerce-importer-advanced hidden">
|
||||
<th>
|
||||
<label for="woocommerce-importer-file-url"><?php esc_html_e( 'Alternatively, enter the path to a CSV file on your server:', 'woocommerce' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label for="woocommerce-importer-file-url" class="woocommerce-importer-file-url-field-wrapper">
|
||||
<code><?php echo esc_html( ABSPATH ) . ' '; ?></code><input type="text" id="woocommerce-importer-file-url" name="file_url" />
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="woocommerce-importer-advanced hidden">
|
||||
<th><label><?php esc_html_e( 'CSV Delimiter', 'woocommerce' ); ?></label><br/></th>
|
||||
<td><input type="text" name="delimiter" placeholder="," size="2" /></td>
|
||||
</tr>
|
||||
<tr class="woocommerce-importer-advanced hidden">
|
||||
<th><label><?php esc_html_e( 'Use previous column mapping preferences?', 'woocommerce' ); ?></label><br/></th>
|
||||
<td><input type="checkbox" id="woocommerce-importer-map-preferences" name="map_preferences" value="1" /></td>
|
||||
</tr>
|
||||
<tr class="woocommerce-importer-advanced hidden">
|
||||
<th><label><?php esc_html_e( 'Character encoding of the file', 'woocommerce' ); ?></label><br/></th>
|
||||
<td><select id="woocommerce-importer-character-encoding" name="character_encoding">
|
||||
<option value="" selected><?php esc_html_e( 'Autodetect', 'woocommerce' ); ?></option>
|
||||
<?php
|
||||
$encodings = mb_list_encodings();
|
||||
sort( $encodings, SORT_NATURAL );
|
||||
foreach ( $encodings as $encoding ) {
|
||||
echo '<option>' . esc_html( $encoding ) . '</option>';
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<script type="text/javascript">
|
||||
jQuery(function() {
|
||||
jQuery( '.woocommerce-importer-toggle-advanced-options' ).on( 'click', function() {
|
||||
var elements = jQuery( '.woocommerce-importer-advanced' );
|
||||
if ( elements.is( '.hidden' ) ) {
|
||||
elements.removeClass( 'hidden' );
|
||||
jQuery( this ).text( jQuery( this ).data( 'hidetext' ) );
|
||||
} else {
|
||||
elements.addClass( 'hidden' );
|
||||
jQuery( this ).text( jQuery( this ).data( 'showtext' ) );
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
});
|
||||
</script>
|
||||
<div class="wc-actions">
|
||||
<a href="#" class="woocommerce-importer-toggle-advanced-options" data-hidetext="<?php esc_attr_e( 'Hide advanced options', 'woocommerce' ); ?>" data-showtext="<?php esc_attr_e( 'Show advanced options', 'woocommerce' ); ?>"><?php esc_html_e( 'Show advanced options', 'woocommerce' ); ?></a>
|
||||
<button type="submit" class="button button-primary button-next" value="<?php esc_attr_e( 'Continue', 'woocommerce' ); ?>" name="save_step"><?php esc_html_e( 'Continue', 'woocommerce' ); ?></button>
|
||||
<?php wp_nonce_field( 'woocommerce-csv-importer' ); ?>
|
||||
</div>
|
||||
</form>
|
||||
Reference in New Issue
Block a user