From 215a9fa0c1b6a6615f23ef96654b62245a35b9c3 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 17 May 2017 14:43:48 -0300 Subject: [PATCH 1/5] Fixed warning about empty callback name --- .../importers/class-wc-product-csv-importer-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/importers/class-wc-product-csv-importer-controller.php b/includes/admin/importers/class-wc-product-csv-importer-controller.php index 4b25571b48b..55285320478 100644 --- a/includes/admin/importers/class-wc-product-csv-importer-controller.php +++ b/includes/admin/importers/class-wc-product-csv-importer-controller.php @@ -171,7 +171,7 @@ class WC_Product_CSV_Importer_Controller { * Dispatch current step and show correct view. */ public function dispatch() { - if ( ! empty( $_POST['save_step'] ) && isset( $this->steps[ $this->step ]['handler'] ) ) { + if ( ! empty( $_POST['save_step'] ) && ! empty( $this->steps[ $this->step ]['handler'] ) ) { call_user_func( $this->steps[ $this->step ]['handler'], $this ); } $this->output_header(); From ce6741e11731457552fccb0213e52c622d07baa1 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 17 May 2017 14:57:03 -0300 Subject: [PATCH 2/5] Fixed error display and incorrect use of WP_Error --- .../class-wc-product-csv-importer-controller.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/admin/importers/class-wc-product-csv-importer-controller.php b/includes/admin/importers/class-wc-product-csv-importer-controller.php index 55285320478..8e54307fdc8 100644 --- a/includes/admin/importers/class-wc-product-csv-importer-controller.php +++ b/includes/admin/importers/class-wc-product-csv-importer-controller.php @@ -219,7 +219,7 @@ class WC_Product_CSV_Importer_Controller { $file = $this->handle_upload(); if ( is_wp_error( $file ) ) { - $this->add_error( $file->get_message() ); + $this->add_error( $file->get_error_message() ); return; } else { $this->file = $file; @@ -238,7 +238,7 @@ class WC_Product_CSV_Importer_Controller { public function handle_upload() { if ( empty( $_POST['file_url'] ) ) { if ( ! isset( $_FILES['import'] ) ) { - return new WP_Error( __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.', 'woocommerce' ) ); + return new WP_Error( 'woocommerce_product_csv_importer_upload_file_empty', __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.', 'woocommerce' ) ); } $overrides = array( 'test_form' => false, 'test_type' => false ); @@ -246,7 +246,7 @@ class WC_Product_CSV_Importer_Controller { $upload = wp_handle_upload( $_FILES['import'], $overrides ); if ( isset( $upload['error'] ) ) { - return new WP_Error( $upload['error'] ); + return new WP_Error( 'woocommerce_product_csv_importer_upload_error', $upload['error'] ); } // Construct the object array @@ -273,7 +273,7 @@ class WC_Product_CSV_Importer_Controller { return ABSPATH . $_POST['file_url']; } - return new WP_Error( __( 'Please upload or provide the link to a valid CSV file.', 'woocommerce' ) ); + return new WP_Error( 'woocommerce_product_csv_importer_upload_invalid_file', __( 'Please upload or provide the link to a valid CSV file.', 'woocommerce' ) ); } /** From d3ec48f3781e68aab1f0a23aab9ffae4078fe766 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 17 May 2017 17:55:28 -0300 Subject: [PATCH 3/5] Match regular fields between exporter and importer --- ...ass-wc-product-csv-importer-controller.php | 77 +++++++++++++++---- .../views/html-csv-import-mapping.php | 8 +- 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/includes/admin/importers/class-wc-product-csv-importer-controller.php b/includes/admin/importers/class-wc-product-csv-importer-controller.php index 8e54307fdc8..8f82b156bdc 100644 --- a/includes/admin/importers/class-wc-product-csv-importer-controller.php +++ b/includes/admin/importers/class-wc-product-csv-importer-controller.php @@ -280,9 +280,10 @@ class WC_Product_CSV_Importer_Controller { * Mapping step @todo */ protected function mapping_form() { - $importer = $this->get_importer( $this->file, array( 'lines' => 1 ) ); - $headers = $importer->get_raw_keys(); - $sample = current( $importer->get_raw_data() ); + $importer = $this->get_importer( $this->file, array( 'lines' => 1 ) ); + $headers = $importer->get_raw_keys(); + $mapped_items = $this->auto_mapping( $raw_headers ); + $sample = current( $importer->get_raw_data() ); if ( empty( $sample ) ) { $this->add_error( __( 'The file is empty, please try again with a new file.', 'woocommerce' ) ); @@ -290,7 +291,7 @@ class WC_Product_CSV_Importer_Controller { } // Check if all fields matches. - if ( 0 === count( array_diff( $headers, $this->get_default_fields() ) ) ) { + if ( 0 === count( array_diff( $mapped_items, $this->get_default_fields() ) ) ) { wp_redirect( esc_url_raw( $this->get_next_step_link() ) ); exit; } @@ -395,6 +396,53 @@ class WC_Product_CSV_Importer_Controller { return apply_filters( 'woocommerce_csv_product_default_fields', $fields ); } + protected function auto_mapping( $fields ) { + $weight_unit = get_option( 'woocommerce_weight_unit' ); + $dimension_unit = get_option( 'woocommerce_dimension_unit' ); + $default = array_flip( array( + 'id' => __( 'ID', 'woocommerce' ), + 'type' => __( 'Type', 'woocommerce' ), + 'sku' => __( 'SKU', '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' ), + 'date_on_sale_from' => __( 'Date sale price starts', 'woocommerce' ), + 'date_on_sale_to' => __( 'Date sale price ends', 'woocommerce' ), + 'tax_status' => __( 'Tax Class', 'woocommerce' ), + 'stock_status' => __( 'In stock?', 'woocommerce' ), + 'stock' => __( 'Stock', 'woocommerce' ), + 'backorders' => __( 'Backorders allowed?', 'woocommerce' ), + 'sold_individually' => __( 'Sold individually?', 'woocommerce' ), + 'weight' => sprintf( __( 'Weight (%s)', 'woocommerce' ), $weight_unit ), + 'length' => sprintf( __( 'Length (%s)', 'woocommerce' ), $dimension_unit ), + 'width' => sprintf( __( 'Width (%s)', 'woocommerce' ), $dimension_unit ), + 'height' => sprintf( __( 'Height (%s)', 'woocommerce' ), $dimension_unit ), + 'reviews_allowed' => __( 'Allow customer reviews?', 'woocommerce' ), + 'purchase_note' => __( 'Purchase Note', 'woocommerce' ), + 'sale_price' => __( 'Sale Price', 'woocommerce' ), + 'regular_price' => __( 'Regular Price', 'woocommerce' ), + 'category_ids' => __( 'Categories', 'woocommerce' ), + 'tag_ids' => __( 'Tags', 'woocommerce' ), + 'shipping_class_id' => __( 'Shipping Class', 'woocommerce' ), + 'image_id' => __( 'Images', 'woocommerce' ), + 'download_limit' => __( 'Download Limit', 'woocommerce' ), + 'download_expiry' => __( 'Download Expiry Days', 'woocommerce' ), + 'parent_id' => __( 'Parent', 'woocommerce' ), + 'upsell_ids' => __( 'Upsells', 'woocommerce' ), + 'cross_sell_ids' => __( 'Cross-sells', 'woocommerce' ), + ) ); + + $new_fields = array(); + foreach ( $fields as $field ) { + $new_fields[] = isset( $default[ $field ] ) ? $default[ $field ] : $field; + } + + return $new_fields; + } + /** * Get mapping options. * @@ -409,7 +457,7 @@ class WC_Product_CSV_Importer_Controller { 'type' => __( 'Type', 'woocommerce' ), 'sku' => __( 'SKU', 'woocommerce' ), 'name' => __( 'Name', 'woocommerce' ), - 'status' => __( 'Published', 'woocommerce' ), + 'published' => __( 'Published', 'woocommerce' ), 'featured' => __( 'Is featured?', 'woocommerce' ), 'catalog_visibility' => __( 'Visibility in catalog', 'woocommerce' ), 'short_description' => __( 'Short Description', 'woocommerce' ), @@ -431,13 +479,19 @@ class WC_Product_CSV_Importer_Controller { 'height' => sprintf( __( 'Height (%s)', 'woocommerce' ), $dimension_unit ), 'reviews_allowed' => __( 'Allow customer reviews?', 'woocommerce' ), 'purchase_note' => __( 'Purchase Note', 'woocommerce' ), - 'price' => __( 'Price', 'woocommerce' ), + 'sale_price' => __( 'Sale Price', 'woocommerce' ), 'regular_price' => __( 'Regular Price', 'woocommerce' ), - 'manage_stock' => __( 'Manage stock?', 'woocommerce' ), - 'stock_quantity' => __( 'Amount in stock', 'woocommerce' ), + 'stock' => __( 'Stock', 'woocommerce' ), 'category_ids' => __( 'Categories', 'woocommerce' ), 'tag_ids' => __( 'Tags', 'woocommerce' ), 'shipping_class_id' => __( 'Shipping Class', 'woocommerce' ), + 'image_id' => __( 'Images', 'woocommerce' ), + 'downloads' => __( 'Download Name:URL', 'woocommerce' ), + 'download_limit' => __( 'Download Limit', 'woocommerce' ), + 'download_expiry' => __( 'Download Expiry Days', 'woocommerce' ), + 'parent_id' => __( 'Parent', 'woocommerce' ), + 'upsell_ids' => __( 'Upsells', 'woocommerce' ), + 'cross_sell_ids' => __( 'Cross-sells', 'woocommerce' ), 'attributes' => array( 'name' => __( 'Attributes', 'woocommerce' ), 'options' => array( @@ -446,13 +500,6 @@ class WC_Product_CSV_Importer_Controller { 'default_attributes' => __( 'Default attribute', 'woocommerce' ), ), ), - 'images' => __( 'Image', 'woocommerce' ), - 'downloads' => __( 'Download Name:URL', 'woocommerce' ), - 'download_limit' => __( 'Download Limit', 'woocommerce' ), - 'download_expiry' => __( 'Download Expiry Days', 'woocommerce' ), - 'parent_id' => __( 'Parent', 'woocommerce' ), - 'upsell_ids' => __( 'Upsells', 'woocommerce' ), - 'cross_sell_ids' => __( 'Cross-sells', 'woocommerce' ), 'meta:' . $item => __( 'Import as meta', 'woocommerce' ), ); diff --git a/includes/admin/importers/views/html-csv-import-mapping.php b/includes/admin/importers/views/html-csv-import-mapping.php index cf3d1fc0301..8a4fa222048 100644 --- a/includes/admin/importers/views/html-csv-import-mapping.php +++ b/includes/admin/importers/views/html-csv-import-mapping.php @@ -22,22 +22,22 @@ if ( ! defined( 'ABSPATH' ) ) { $name ) : ?> + From df63ea9b5af4b155cc96a9fd7f7b5a7d131d1f84 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 17 May 2017 20:59:36 -0300 Subject: [PATCH 4/5] Handle special column names Handle columns names like "Attribute 1 Name" and "Meta: _foo_bar". --- ...ass-wc-product-csv-importer-controller.php | 95 +++++++++++++++---- 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/includes/admin/importers/class-wc-product-csv-importer-controller.php b/includes/admin/importers/class-wc-product-csv-importer-controller.php index 8f82b156bdc..c653bdf11f7 100644 --- a/includes/admin/importers/class-wc-product-csv-importer-controller.php +++ b/includes/admin/importers/class-wc-product-csv-importer-controller.php @@ -282,7 +282,7 @@ class WC_Product_CSV_Importer_Controller { protected function mapping_form() { $importer = $this->get_importer( $this->file, array( 'lines' => 1 ) ); $headers = $importer->get_raw_keys(); - $mapped_items = $this->auto_mapping( $raw_headers ); + $mapped_items = $this->auto_map_columns( $headers ); $sample = current( $importer->get_raw_data() ); if ( empty( $sample ) ) { @@ -396,10 +396,16 @@ class WC_Product_CSV_Importer_Controller { return apply_filters( 'woocommerce_csv_product_default_fields', $fields ); } - protected function auto_mapping( $fields ) { - $weight_unit = get_option( 'woocommerce_weight_unit' ); - $dimension_unit = get_option( 'woocommerce_dimension_unit' ); - $default = array_flip( array( + /** + * Auto map column names. + * + * @param array $fields Header columns. + * @return array + */ + protected function auto_map_columns( $fields ) { + $weight_unit = get_option( 'woocommerce_weight_unit' ); + $dimension_unit = get_option( 'woocommerce_dimension_unit' ); + $default_column_names = array_flip( array( 'id' => __( 'ID', 'woocommerce' ), 'type' => __( 'Type', 'woocommerce' ), 'sku' => __( 'SKU', 'woocommerce' ), @@ -434,13 +440,42 @@ class WC_Product_CSV_Importer_Controller { 'upsell_ids' => __( 'Upsells', 'woocommerce' ), 'cross_sell_ids' => __( 'Cross-sells', 'woocommerce' ), ) ); + $special_data = array_map( array( $this, 'sanitize_special_column_name_regex' ), array( + 'attributes:name' => __( 'Attribute %d Name', 'woocommerce' ), + 'attributes:value' => __( 'Attribute %d Value(s)', 'woocommerce' ), + 'attributes:default' => __( 'Attribute %d Default', 'woocommerce' ), + 'downloads:name' => __( 'Download %d Name', 'woocommerce' ), + 'downloads:url' => __( 'Download %d URL', 'woocommerce' ), + 'meta:' => __( 'Meta: %s', 'woocommerce' ), + ) ); $new_fields = array(); - foreach ( $fields as $field ) { - $new_fields[] = isset( $default[ $field ] ) ? $default[ $field ] : $field; + foreach ( $fields as $index => $field ) { + $new_fields[ $index ] = $field; + + if ( isset( $default_column_names[ $field ] ) ) { + $new_fields[ $index ] = $default_column_names[ $field ]; + } else { + foreach ( $special_data as $special_key => $regex ) { + if ( preg_match( $regex, $field, $matches ) ) { + $new_fields[ $index ] = $special_key . $matches[1]; + break; + } + } + } } - return $new_fields; + return apply_filters( 'woocommerce_csv_product_import_mapped_fields', $new_fields, $fields ); + } + + /** + * 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' ), '(.*)', quotemeta( $value ) ) . '/'; } /** @@ -450,6 +485,13 @@ class WC_Product_CSV_Importer_Controller { * @return array */ protected function get_mapping_options( $item = '' ) { + // Get number for special column names. + $special_index = $item; + if ( preg_match('/\d+$/', $item, $matches ) ) { + $special_index = $matches[0]; + } + $meta = str_replace( 'meta:', '', $item ); + $weight_unit = get_option( 'woocommerce_weight_unit' ); $dimension_unit = get_option( 'woocommerce_dimension_unit' ); $options = array( @@ -471,12 +513,17 @@ class WC_Product_CSV_Importer_Controller { 'sold_individually' => __( 'Sold individually?', 'woocommerce' ), /* translators: %s: weight unit */ 'weight' => sprintf( __( 'Weight (%s)', 'woocommerce' ), $weight_unit ), - /* translators: %s: dimension unit */ - 'length' => sprintf( __( 'Length (%s)', 'woocommerce' ), $dimension_unit ), - /* translators: %s: dimension unit */ - 'width' => sprintf( __( 'Width (%s)', 'woocommerce' ), $dimension_unit ), - /* translators: %s: dimension unit */ - 'height' => sprintf( __( 'Height (%s)', 'woocommerce' ), $dimension_unit ), + 'dimensions' => array( + 'name' => __( 'Dimensions', 'woocommerce' ), + 'options' => array( + /* translators: %s: dimension unit */ + 'length' => sprintf( __( 'Length (%s)', 'woocommerce' ), $dimension_unit ), + /* translators: %s: dimension unit */ + 'width' => sprintf( __( 'Width (%s)', 'woocommerce' ), $dimension_unit ), + /* translators: %s: dimension unit */ + 'height' => sprintf( __( 'Height (%s)', 'woocommerce' ), $dimension_unit ), + ), + ), 'reviews_allowed' => __( 'Allow customer reviews?', 'woocommerce' ), 'purchase_note' => __( 'Purchase Note', 'woocommerce' ), 'sale_price' => __( 'Sale Price', 'woocommerce' ), @@ -486,21 +533,27 @@ class WC_Product_CSV_Importer_Controller { 'tag_ids' => __( 'Tags', 'woocommerce' ), 'shipping_class_id' => __( 'Shipping Class', 'woocommerce' ), 'image_id' => __( 'Images', 'woocommerce' ), - 'downloads' => __( 'Download Name:URL', 'woocommerce' ), - 'download_limit' => __( 'Download Limit', 'woocommerce' ), - 'download_expiry' => __( 'Download Expiry Days', 'woocommerce' ), 'parent_id' => __( 'Parent', 'woocommerce' ), 'upsell_ids' => __( 'Upsells', 'woocommerce' ), 'cross_sell_ids' => __( 'Cross-sells', 'woocommerce' ), + 'downloads' => array( + 'name' => __( 'Downloads', 'woocommerce' ), + 'options' => array( + 'downloads:name' . $special_index => __( 'Download Name', 'woocommerce' ), + 'downloads:url' . $special_index => __( 'Download URL', 'woocommerce' ), + 'download_limit' => __( 'Download Limit', 'woocommerce' ), + 'download_expiry' => __( 'Download Expiry Days', 'woocommerce' ), + ), + ), 'attributes' => array( 'name' => __( 'Attributes', 'woocommerce' ), 'options' => array( - 'attributes_name' => __( 'Attributes name', 'woocommerce' ), - 'attributes_value' => __( 'Attributes value', 'woocommerce' ), - 'default_attributes' => __( 'Default attribute', 'woocommerce' ), + 'attributes:name' . $special_index => __( 'Attributes name', 'woocommerce' ), + 'attributes:value' . $special_index => __( 'Attributes value', 'woocommerce' ), + 'attributes:default' . $special_index => __( 'Default attribute', 'woocommerce' ), ), ), - 'meta:' . $item => __( 'Import as meta', 'woocommerce' ), + 'meta:' . $meta => __( 'Import as meta', 'woocommerce' ), ); return apply_filters( 'woocommerce_csv_product_import_mapping_options', $options, $item ); From 36fa8386785c630f592979ef4e5c526a23ca585e Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 17 May 2017 21:10:26 -0300 Subject: [PATCH 5/5] Allow extend auto mapping options --- ...ass-wc-product-csv-importer-controller.php | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/includes/admin/importers/class-wc-product-csv-importer-controller.php b/includes/admin/importers/class-wc-product-csv-importer-controller.php index c653bdf11f7..14767568a6e 100644 --- a/includes/admin/importers/class-wc-product-csv-importer-controller.php +++ b/includes/admin/importers/class-wc-product-csv-importer-controller.php @@ -403,9 +403,9 @@ class WC_Product_CSV_Importer_Controller { * @return array */ protected function auto_map_columns( $fields ) { - $weight_unit = get_option( 'woocommerce_weight_unit' ); - $dimension_unit = get_option( 'woocommerce_dimension_unit' ); - $default_column_names = array_flip( array( + $weight_unit = get_option( 'woocommerce_weight_unit' ); + $dimension_unit = get_option( 'woocommerce_dimension_unit' ); + $default_columns = array_flip( apply_filters( 'woocommerce_csv_product_import_mapping_default_columns', array( 'id' => __( 'ID', 'woocommerce' ), 'type' => __( 'Type', 'woocommerce' ), 'sku' => __( 'SKU', 'woocommerce' ), @@ -439,24 +439,29 @@ class WC_Product_CSV_Importer_Controller { 'parent_id' => __( 'Parent', 'woocommerce' ), 'upsell_ids' => __( 'Upsells', 'woocommerce' ), 'cross_sell_ids' => __( 'Cross-sells', 'woocommerce' ), - ) ); - $special_data = array_map( array( $this, 'sanitize_special_column_name_regex' ), array( - 'attributes:name' => __( 'Attribute %d Name', 'woocommerce' ), - 'attributes:value' => __( 'Attribute %d Value(s)', 'woocommerce' ), - 'attributes:default' => __( 'Attribute %d Default', 'woocommerce' ), - 'downloads:name' => __( 'Download %d Name', 'woocommerce' ), - 'downloads:url' => __( 'Download %d URL', 'woocommerce' ), - 'meta:' => __( 'Meta: %s', 'woocommerce' ), - ) ); + ) ) ); + $special_columns = array_map( + array( $this, 'sanitize_special_column_name_regex' ), + apply_filters( 'woocommerce_csv_product_import_mapping_special_columns', + array( + 'attributes:name' => __( 'Attribute %d Name', 'woocommerce' ), + 'attributes:value' => __( 'Attribute %d Value(s)', 'woocommerce' ), + 'attributes:default' => __( 'Attribute %d Default', 'woocommerce' ), + 'downloads:name' => __( 'Download %d Name', 'woocommerce' ), + 'downloads:url' => __( 'Download %d URL', 'woocommerce' ), + 'meta:' => __( 'Meta: %s', 'woocommerce' ), + ) + ) + ); $new_fields = array(); foreach ( $fields as $index => $field ) { $new_fields[ $index ] = $field; - if ( isset( $default_column_names[ $field ] ) ) { - $new_fields[ $index ] = $default_column_names[ $field ]; + if ( isset( $default_columns[ $field ] ) ) { + $new_fields[ $index ] = $default_columns[ $field ]; } else { - foreach ( $special_data as $special_key => $regex ) { + foreach ( $special_columns as $special_key => $regex ) { if ( preg_match( $regex, $field, $matches ) ) { $new_fields[ $index ] = $special_key . $matches[1]; break; @@ -465,7 +470,7 @@ class WC_Product_CSV_Importer_Controller { } } - return apply_filters( 'woocommerce_csv_product_import_mapped_fields', $new_fields, $fields ); + return apply_filters( 'woocommerce_csv_product_import_mapped_columns', $new_fields, $fields ); } /** @@ -485,13 +490,15 @@ class WC_Product_CSV_Importer_Controller { * @return array */ protected function get_mapping_options( $item = '' ) { - // Get number for special column names. - $special_index = $item; + // Get index for special column names. + $index = $item; if ( preg_match('/\d+$/', $item, $matches ) ) { - $special_index = $matches[0]; + $index = $matches[0]; } + // Properly format for meta field. $meta = str_replace( 'meta:', '', $item ); + // Available options. $weight_unit = get_option( 'woocommerce_weight_unit' ); $dimension_unit = get_option( 'woocommerce_dimension_unit' ); $options = array( @@ -539,18 +546,18 @@ class WC_Product_CSV_Importer_Controller { 'downloads' => array( 'name' => __( 'Downloads', 'woocommerce' ), 'options' => array( - 'downloads:name' . $special_index => __( 'Download Name', 'woocommerce' ), - 'downloads:url' . $special_index => __( 'Download URL', 'woocommerce' ), - 'download_limit' => __( 'Download Limit', 'woocommerce' ), - 'download_expiry' => __( 'Download Expiry Days', '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' . $special_index => __( 'Attributes name', 'woocommerce' ), - 'attributes:value' . $special_index => __( 'Attributes value', 'woocommerce' ), - 'attributes:default' . $special_index => __( 'Default attribute', 'woocommerce' ), + 'attributes:name' . $index => __( 'Attributes name', 'woocommerce' ), + 'attributes:value' . $index => __( 'Attributes value', 'woocommerce' ), + 'attributes:default' . $index => __( 'Default attribute', 'woocommerce' ), ), ), 'meta:' . $meta => __( 'Import as meta', 'woocommerce' ),