/**
  * Takes a heading and normalizes it based on the current importer type
  *
  * @since 1.0.0
  * @param string $heading
  * @return string
  */
 public static function normalize_heading($heading)
 {
     $s_heading = trim($heading);
     // lowercase and replace space with underscores if not a custom meta value
     if (!SV_WC_Helper::str_starts_with($s_heading, 'meta:')) {
         $s_heading = strtolower($heading);
         $s_heading = str_replace(' ', '_', $s_heading);
     }
     return $s_heading;
 }
Пример #2
0
 /**
  * Maybe force TLS v1.2 requests.
  *
  * @since 4.4.0
  */
 public function set_tls_1_2_request($handle, $r, $url)
 {
     if (!SV_WC_Helper::str_starts_with($url, 'https://')) {
         return;
     }
     $versions = curl_version();
     $curl_version = $versions['version'];
     // Get the SSL details
     list($ssl_type, $ssl_version) = explode('/', $versions['ssl_version']);
     $ssl_version = substr($ssl_version, 0, -1);
     // If cURL and/or OpenSSL aren't up to the challenge, bail
     if (!version_compare($curl_version, '7.34.0', '>=') || 'OpenSSL' === $ssl_type && !version_compare($ssl_version, '1.0.1', '>=')) {
         return;
     }
     curl_setopt($handle, CURLOPT_SSLVERSION, 6);
 }
 /**
  * Locates the WooCommerce template files from our templates directory
  *
  * @since 1.0.0
  * @param string $template Already found template
  * @param string $template_name Searchable template name
  * @param string $template_path Template path
  * @return string Search result for the template
  */
 public function locate_template($template, $template_name, $template_path)
 {
     // Only keep looking if no custom theme template was found or if
     // a default WooCommerce template was found.
     if (!$template || SV_WC_Helper::str_starts_with($template, WC()->plugin_path())) {
         // Set the path to our templates directory
         $plugin_path = $this->get_plugin_path() . '/templates/';
         // If a template is found, make it so
         if (is_readable($plugin_path . $template_name)) {
             $template = $plugin_path . $template_name;
         }
     }
     return $template;
 }
Пример #4
0
 /**
  *
  * @dataProvider provider_test_str_starts_with_false
  */
 public function test_str_starts_with_false($haystack, $needle)
 {
     $this->assertFalse(\SV_WC_Helper::str_starts_with($haystack, $needle));
 }
 /**
  * Write the given row to the CSV
  *
  * This is abstracted so the provided data can be matched to the CSV headers set and the CSV delimiter and
  * enclosure can be controlled from a single method
  *
  * @since 3.0
  * @param array $row
  */
 private function write($row)
 {
     $data = array();
     foreach ($this->headers as $header_key => $_) {
         if (!isset($row[$header_key])) {
             $row[$header_key] = '';
         }
         // strict string comparison, as values like '0' are valid
         $value = '' !== $row[$header_key] ? $row[$header_key] : '';
         // escape leading equals sign character with a single quote to prevent CSV injections, see http://www.contextis.com/resources/blog/comma-separated-vulnerabilities/
         if (SV_WC_Helper::str_starts_with($value, '=')) {
             $value = "'" . $value;
         }
         $data[] = $value;
     }
     fputcsv($this->stream, $data, $this->delimiter, $this->enclosure);
 }
 /**
  * Parse raw coupon data
  *
  * @since 3.0.0
  * @param array $item Raw coupon data from CSV
  * @param array $options Optional. Options
  * @param array $raw_headers Optional. Raw headers
  * @throws \WC_CSV_Import_Suite_Import_Exception validation, parsing errors
  * @return array|bool Parsed coupon data or false on failure
  */
 protected function parse_item($item, $options = array(), $raw_headers = array())
 {
     $coupon_code = isset($item['code']) ? $item['code'] : null;
     $merging = $options['merge'];
     $insert_non_matching = isset($options['insert_non_matching']) && $options['insert_non_matching'];
     /* translators: Placeholders: %s - row number */
     $preparing = $merging ? __('> Row %s - preparing for merge.', 'woocommerce-csv-import-suite') : __('> Row %s - preparing for import.', 'woocommerce-csv-import-suite');
     wc_csv_import_suite()->log('---');
     wc_csv_import_suite()->log(sprintf($preparing, $this->line_num));
     // prepare coupon & postmeta for import
     $coupon = $postmeta = $terms = array();
     // cannot merge or insert without coupon code
     if (!$coupon_code) {
         throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_missing_coupon_code', __('Missing coupon code.', 'woocommerce-csv-import-suite'));
     }
     global $wpdb;
     // check for existing coupons
     $found_coupon = $wpdb->get_var($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE post_type = 'shop_coupon' AND post_status = 'publish' AND post_title = %s", $coupon_code));
     // prepare for merging
     if ($merging) {
         // no coupon found
         if (!$found_coupon) {
             if ($insert_non_matching) {
                 wc_csv_import_suite()->log(sprintf(__('> > Skipped. Cannot find coupon with code %s. Importing instead.', 'woocommerce-csv-import-suite'), esc_html($coupon_code)));
                 $merging = false;
             } else {
                 throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_cannot_find_coupon', sprintf(__('Cannot find coupon with code %s.', 'woocommerce-csv-import-suite'), esc_html($coupon_code)));
             }
         } else {
             /* translators: Placeholders: %s - coupon code */
             wc_csv_import_suite()->log(sprintf(__("> > Found coupon with code '%s'.", 'woocommerce-csv-import-suite'), esc_html($coupon_code)));
             // record the coupon ID
             $coupon['id'] = $found_coupon;
         }
     }
     // prepare for importing
     if (!$merging) {
         // coupon already exists
         if ($found_coupon) {
             /* translators: Placeholders: %s - coupon code */
             throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_coupon_code_already_exists', sprintf(__("Coupon code '%s' already exists.", 'woocommerce-csv-import-suite'), esc_html($coupon_code)));
         }
         // check required fields
         if (!isset($item['type']) || !$item['type']) {
             throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_missing_coupon_type', __("Missing coupon discount type.", 'woocommerce-csv-import-suite'));
         }
     }
     // get the set of possible coupon discount types
     $discount_types = wc_get_coupon_types();
     // check for the discount type validity both by key and value (ie either 'fixed_cart' or 'Cart Discount'
     if (isset($item['type']) && $item['type']) {
         $discount_type_is_valid = false;
         foreach ($discount_types as $key => $value) {
             if (0 === strcasecmp($key, $item['type']) || 0 === strcasecmp($value, __($item['type'], 'woocommerce-csv-import-suite'))) {
                 $discount_type_is_valid = true;
                 $coupon['type'] = $key;
                 break;
             }
         }
         if (!$discount_type_is_valid) {
             /* translators: Placeholders: %s - discount type name */
             throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_unknown_coupon_type', sprintf(__("Unknown discount type '%s'.", 'woocommerce-csv-import-suite'), esc_html($item['type'])));
         }
     }
     // build the coupon data object
     $coupon['code'] = $item['code'];
     $coupon['description'] = isset($item['description']) ? $item['description'] : '';
     // get any known coupon data fields
     foreach ($this->coupon_data_fields as $column) {
         switch ($column) {
             case 'products':
                 // handle products: look up by sku
             // handle products: look up by sku
             case 'exclude_products':
                 $val = isset($item[$column]) ? $item[$column] : '';
                 $skus = array_filter(array_map('trim', explode(',', $val)));
                 $val = array();
                 foreach ($skus as $sku) {
                     // find by sku
                     $product_id = $wpdb->get_var($wpdb->prepare("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $sku));
                     // no product found
                     if (!$product_id) {
                         /* translators: Placeholders: %s - product SKU */
                         throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_unknown_product_sku', sprintf(__('Unknown product sku: %s.', 'woocommerce-csv-import-suite'), esc_html($sku)));
                     }
                     $val[] = $product_id;
                 }
                 // map to standard column name
                 $column = 'products' == $column ? 'product_ids' : 'exclude_product_ids';
                 break;
             case 'product_categories':
             case 'exclude_product_categories':
                 $val = isset($item[$column]) ? $item[$column] : '';
                 $product_cats = array_filter(array_map('trim', explode(',', $val)));
                 $val = array();
                 foreach ($product_cats as $product_cat) {
                     // validate product category
                     $term = term_exists($product_cat, 'product_cat');
                     // unknown category
                     if (!$term) {
                         /* translators: Placeholders: %s - product category name */
                         throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_unknown_product_category', sprintf(__('Unknown product category: %s.', 'woocommerce-csv-import-suite'), esc_html($product_cat)));
                     }
                     $val[] = $term['term_id'];
                 }
                 // map to standard column name
                 $column = 'product_categories' == $column ? 'product_category_ids' : 'exclude_product_category_ids';
                 break;
             case 'customer_emails':
                 $val = isset($item[$column]) ? $item[$column] : '';
                 $emails = array_filter(array_map('trim', explode(',', $val)));
                 $val = array();
                 foreach ($emails as $email) {
                     // invalid email
                     if (!is_email($email)) {
                         /* translators: Placeholders: %s - email address */
                         throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_invalid_email', sprintf(__('Invalid email: %s.', 'woocommerce-csv-import-suite'), esc_html($email)));
                     }
                     $val[] = $email;
                 }
                 break;
             case 'enable_free_shipping':
                 // handle booleans, defaulting to 'no' on import (not merge)
             // handle booleans, defaulting to 'no' on import (not merge)
             case 'individual_use':
             case 'exclude_sale_items':
                 $val = isset($item[$column]) && $item[$column] ? strtolower($item[$column]) : ($merging ? '' : 'no');
                 if ($val && 'yes' != $val && 'no' != $val) {
                     /* translators: Placeholders: %s - column name */
                     throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_invalid_value', sprintf(__("Column '%s' must be 'yes' or 'no'.", 'woocommerce-csv-import-suite'), esc_html($column)));
                 }
                 // transform into true boolean, so that the format matches with
                 // WC_API_Coupons
                 $val = 'yes' === $val;
                 break;
             case 'expiry_date':
                 $val = isset($item[$column]) ? $item[$column] : '';
                 // invalid date format
                 if ($val && false === strtotime($val)) {
                     /* translators: Placeholders: %s - a date in invalid format */
                     throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_invalid_date_format', sprintf(__("Invalid date format '%s'", 'woocommerce-csv-import-suite'), esc_html($item[$column])));
                 }
                 break;
             case 'usage_limit':
                 // handle integers
             // handle integers
             case 'usage_count':
             case 'usage_limit_per_user':
             case 'limit_usage_to_x_items':
                 $val = isset($item[$column]) ? $item[$column] : '';
                 // invalid integer value
                 if (!empty($val) && !is_numeric($val)) {
                     /* translators: Placeholders: %1$s - column title, %2$s - column value */
                     throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_invalid_value', sprintf(__('Invalid %1$s \'%2$s\'.', 'woocommerce-csv-import-suite'), esc_html($column), esc_html($val)));
                 }
                 break;
             default:
                 $val = isset($item[$column]) ? $item[$column] : '';
         }
         // only use non-empty values (zeroes are fine, but empty strings are not)
         if (is_numeric($val) || !empty($val)) {
             $coupon[$column] = $val;
         }
     }
     // get any custom meta fields
     foreach ($item as $key => $value) {
         if (!$value) {
             continue;
         }
         // handle meta: columns - import as custom fields
         if (SV_WC_Helper::str_starts_with($key, 'meta:')) {
             // get meta key name
             $meta_key = trim(str_replace('meta:', '', $key));
             // skip known meta fields
             if (in_array($meta_key, $this->coupon_meta_fields)) {
                 continue;
             }
             // add to postmeta array
             $postmeta[$meta_key] = $value;
         } elseif (SV_WC_Helper::str_starts_with($key, 'tax:')) {
             $results = $this->parse_taxonomy_terms($key, $value);
             if (!$results) {
                 continue;
             }
             // add to array
             $terms[] = array('taxonomy' => $results[0], 'terms' => $results[1]);
         }
     }
     $coupon['coupon_meta'] = $postmeta;
     $coupon['terms'] = $terms;
     /**
      * Filter parsed coupon data
      *
      * Gives a chance for 3rd parties to parse data from custom columns
      *
      * @since 3.0.0
      * @param array $coupon Parsed coupon data
      * @param array $data Raw coupon data from CSV
      * @param array $options Import options
      * @param array $raw_headers Raw CSV headers
      */
     return apply_filters('wc_csv_import_suite_parsed_coupon_data', $coupon, $item, $options, $raw_headers);
 }
 /**
  * Parse raw customer data
  *
  * @since 3.0.0
  * @param array $item Raw customer data from CSV
  * @param array $options Optional. Options
  * @param array $raw_headers Optional. Raw headers
  * @throws \WC_CSV_Import_Suite_Import_Exception validation, parsing errors
  * @return array|bool Parsed customer data or false on failure
  */
 protected function parse_item($item, $options = array(), $raw_headers = array())
 {
     $customer_id = !empty($item['id']) ? $item['id'] : 0;
     $username = isset($item['username']) && $item['username'] ? sanitize_user($item['username']) : null;
     $email = isset($item['email']) && $item['email'] ? $item['email'] : null;
     $merging = $options['merge'];
     $insert_non_matching = isset($options['insert_non_matching']) && $options['insert_non_matching'];
     /* translators: Placeholders: %s - row number */
     $preparing = $merging ? __('> Row %s - preparing for merge.', 'woocommerce-csv-import-suite') : __('> Row %s - preparing for import.', 'woocommerce-csv-import-suite');
     wc_csv_import_suite()->log('---');
     wc_csv_import_suite()->log(sprintf($preparing, $this->line_num));
     // prepare for merging
     if ($merging) {
         // check if user exists
         $found_customer = false;
         // check that at least one required field for merging is provided
         if (!$customer_id && !$username && !$email) {
             wc_csv_import_suite()->log(__('> > Cannot merge without id, email or username. Importing instead.', 'woocommerce-csv-import-suite'));
             $merging = false;
         }
         // 1. try matching on user ID
         if ($customer_id) {
             $found_customer = get_user_by('id', $customer_id);
             if (!$found_customer) {
                 // no other fields to match on
                 if (!$username && !$email) {
                     if ($insert_non_matching) {
                         wc_csv_import_suite()->log(sprintf(__('> > Skipped. Cannot find customer with id %s. Importing instead.', 'woocommerce-csv-import-suite'), $customer_id));
                         $merging = false;
                     } else {
                         throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_cannot_find_customer', sprintf(__('Cannot find customer with id %s.', 'woocommerce-csv-import-suite'), $customer_id));
                     }
                 } else {
                     // we can keep trying with username and/or email
                     wc_csv_import_suite()->log(sprintf(__('> > Cannot find customer with id %s.', 'woocommerce-csv-import-suite'), $customer_id));
                 }
             } else {
                 wc_csv_import_suite()->log(sprintf(__('> > Found user with id %s.', 'woocommerce-csv-import-suite'), $customer_id));
             }
         }
         // 2. try matching on username
         if (!$found_customer && $username) {
             // check by username
             $found_customer = username_exists($username);
             if (!$found_customer) {
                 if (!$email) {
                     if ($insert_non_matching) {
                         wc_csv_import_suite()->log(sprintf(__('> > Skipped. Cannot find customer with username %s. Importing instead.', 'woocommerce-csv-import-suite'), $username));
                         $merging = false;
                     } else {
                         throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_cannot_find_customer', sprintf(__('Cannot find customer with username %s.', 'woocommerce-csv-import-suite'), $username));
                     }
                 } else {
                     // We can keep trying with email
                     wc_csv_import_suite()->log(sprintf(__('> > Cannot find customer with username %s.', 'woocommerce-csv-import-suite'), $username));
                 }
             } else {
                 wc_csv_import_suite()->log(sprintf(__('> > Found user with username %s.', 'woocommerce-csv-import-suite'), $username));
                 $customer_id = $found_customer;
             }
         }
         // 3. try matching on email
         if (!$found_customer && $email) {
             // check by email
             $found_customer = email_exists($email);
             if (!$found_customer) {
                 if ($insert_non_matching) {
                     wc_csv_import_suite()->log(sprintf(__('> > Skipped. Cannot find customer with email %s. Importing instead.', 'woocommerce-csv-import-suite'), $email));
                     $merging = false;
                 } else {
                     throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_cannot_find_customer', sprintf(__('Cannot find customer with email %s.', 'woocommerce-csv-import-suite'), $email));
                 }
             } else {
                 wc_csv_import_suite()->log(sprintf(__('> > Found user with email %s.', 'woocommerce-csv-import-suite'), $email));
                 $customer_id = $found_customer;
             }
         }
     }
     // prepare for importing
     if (!$merging) {
         // Required fields. although login (user_login) is technically also required, we can use email for that
         if (!$email) {
             throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_missing_customer_email', __('No email set for new customer.', 'woocommerce-csv-import-suite'));
         }
         // Check if user already exists
         $user_exists = $username && username_exists($username) || $email && email_exists($email);
         if ($user_exists) {
             $identifier = esc_html($username ? $username : $email);
             throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_customer_already_exists', sprintf(__('Customer %s already exists.', 'woocommerce-csv-import-suite'), $identifier));
         }
     }
     // validate username
     if ($username && !validate_username($username)) {
         throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_invalid_username', sprintf(__('Invalid username: %s', 'woocommerce-csv-import-suite'), esc_html($username)));
     }
     // validate email
     if ($email && !is_email($email)) {
         throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_invalid_email', sprintf(__('Invalid email: %s', 'woocommerce-csv-import-suite'), esc_html($email)));
     }
     // verify the role: allow by slug or by name. skip if not found (TODO is this too harsh? {IT 2016-04-09})
     if (isset($item['role']) && $item['role']) {
         global $wp_roles;
         if (!isset($wp_roles->role_names[$item['role']])) {
             $found_role_by_name = false;
             // fallback to first role by name
             foreach ($wp_roles->role_names as $slug => $name) {
                 if ($name == $item['role']) {
                     $item['role'] = $slug;
                     $found_role_by_name = true;
                     break;
                 }
             }
             if (!$found_role_by_name) {
                 throw new WC_CSV_Import_Suite_Import_Exception('wc_csv_import_suite_invalid_role', sprintf(__('Role "%s" not found.', 'woocommerce-csv-import-suite'), $item['role']));
             }
         }
     }
     // prepare user & usermeta for import
     $user = $usermeta = array();
     // if merging, set user ID
     if ($merging && $customer_id) {
         $user['id'] = $customer_id;
     }
     // email
     $user['email'] = $email;
     // ensure username is set (required)
     $user['username'] = $username ? $username : sanitize_user($email);
     // password
     if (isset($item['password'])) {
         $user['password'] = $item['password'];
     }
     // user role, defaults to customer if not merging
     if (isset($item['role']) && $item['role']) {
         $user['role'] = $item['role'];
     }
     if (isset($item['first_name']) && $item['first_name']) {
         $user['first_name'] = $item['first_name'];
     }
     if (isset($item['last_name']) && $item['last_name']) {
         $user['last_name'] = $item['last_name'];
     }
     if (isset($item['date_registered']) && $item['date_registered']) {
         $user['date_registered'] = $item['date_registered'];
     }
     $user['billing_address'] = $user['shipping_address'] = array();
     // get any known customer data (meta) fields
     foreach ($this->customer_data_fields as $column) {
         switch ($column) {
             // normalize customer addresses, to match the WC_API/CLI formats
             case 'billing_address':
             case 'shipping_address':
                 $type = substr($column, 0, strpos($column, '_'));
                 $address_fields = $this->address_fields;
                 if ('shipping' == $type) {
                     unset($address_fields['phone']);
                     unset($address_fields['email']);
                 }
                 foreach ($address_fields as $key) {
                     $meta_key = $type . '_' . $key;
                     // on insert use all columns, on merge only use if there is a value.
                     if (isset($item[$meta_key]) && (!$merging || $item[$meta_key])) {
                         $user[$column][$key] = $item[$meta_key];
                     }
                     // on create default wp user first/last name to billing first/last
                     if (!$merging) {
                         if ('billing_first_name' == $meta_key && !empty($item[$meta_key]) && empty($user['first_name'])) {
                             $user['first_name'] = $item[$meta_key];
                         } elseif ('billing_last_name' == $meta_key && !empty($item[$meta_key]) && empty($user['last_name'])) {
                             $user['last_name'] = $item[$meta_key];
                         }
                     }
                 }
                 break;
                 // normalize the paying customer field
             // normalize the paying customer field
             case 'paying_customer':
                 if (isset($item[$column]) && (!$merging || $item[$column])) {
                     $usermeta[$column] = $item[$column];
                 }
                 break;
         }
     }
     // handle the billing/shipping address defaults as needed
     $copy_billing_to_shipping = isset($options['billing_address_for_shipping_address']) && $options['billing_address_for_shipping_address'];
     if ($copy_billing_to_shipping && !empty($user['billing_address'])) {
         foreach ($user['billing_address'] as $key => $value) {
             // if the shipping address field is set, use that. Otherwise, copy the
             // value from billing address
             if (!isset($user['shipping_address'][$key]) || !$user['shipping_address'][$key]) {
                 $user['shipping_address'][$key] = $value;
             }
         }
     }
     // get any custom meta fields
     foreach ($item as $key => $value) {
         if (!$value) {
             continue;
         }
         // handle meta: columns - import as custom fields
         if (SV_WC_Helper::str_starts_with($key, 'meta:')) {
             // get meta key name
             $meta_key = trim(str_replace('meta:', '', $key));
             // skip known meta fields
             if (in_array($meta_key, $this->customer_data_fields)) {
                 continue;
             }
             // add to usermeta array
             $usermeta[$meta_key] = $value;
         }
     }
     $user['user_meta'] = $usermeta;
     /**
      * Filter parsed customer data
      *
      * Gives a chance for 3rd parties to parse data from custom columns
      *
      * @since 3.0.0
      * @param array $customer Parsed customer data
      * @param array $data Raw customer data from CSV
      * @param array $options Import options
      * @param array $raw_headers Raw CSV headers
      */
     return apply_filters('wc_csv_import_suite_parsed_customer_data', $user, $item, $options, $raw_headers);
 }
 /**
  * Render column mapper for CSV import
  *
  * @since 3.0.0
  * @param array $input
  * @param array $options
  * @param array $raw_headers
  * @return string HTML for column mapper
  */
 protected function render_column_mapper($data, $options, $raw_headers)
 {
     $headers = array_keys($data[2]);
     // data always starts from 2nd line
     $columns = array();
     $sample_size = count($data);
     foreach ($headers as $heading) {
         $importer = sanitize_key($_GET['import']);
         // determine default mapping for heading
         $mapping = WC_CSV_Import_Suite_Parser::normalize_heading($heading);
         if (SV_WC_Helper::str_starts_with($heading, 'meta:')) {
             $mapping = 'import_as_meta';
         }
         if (SV_WC_Helper::str_starts_with($heading, 'tax:')) {
             $mapping = 'import_as_taxonomy';
         }
         /**
          * Filter default CSV column <-> field mapping
          *
          * @since 3.0.0
          * @param string $map_to Field to map the column to. Defaults to column name
          * @param string $column Column name from CSV file
          */
         $default_mapping = apply_filters("wc_csv_import_suite_{$importer}_column_default_mapping", $mapping, $heading);
         $columns[$heading] = array('default_mapping' => $default_mapping, 'sample_values' => array());
         foreach ($data as $row) {
             $columns[$heading]['sample_values'][] = isset($row[$heading]) ? $row[$heading] : '';
         }
     }
     /**
      * Filter column mapping options
      *
      * @since 3.0.0
      * @param array $mapping_options Associative array of column mapping options
      * @param string $importer Importer type
      * @param array $headers Normalized headers
      * @param array $raw_headers Raw headers from CSV file
      * @param array $columns Associative array as 'column' => 'default mapping'
      */
     $mapping_options = apply_filters('wc_csv_import_suite_column_mapping_options', $this->get_column_mapping_options(), $importer, $headers, $raw_headers, $columns);
     $mapping_options['import_as_meta'] = __('Custom Field with column name', 'woocommerce-csv-import-suite');
     $mapping_options['import_as_taxonomy'] = __('Taxonomy with column name', 'woocommerce-csv-import-suite');
     // give this instance a descriptive name to be used in the view template
     $csv_importer = $this;
     include 'admin/views/html-import-column-mapper.php';
 }
 /**
  * Check if input string is possibly a JSON array
  *
  * @since 3.0.0
  * @param string $string
  * @return bool True if string is possible JSON, false otherwise
  */
 private function is_possibly_json_array($string)
 {
     return '[]' == $string || SV_WC_Helper::str_starts_with($string, '[{') && SV_WC_Helper::str_ends_with($string, '}]');
 }