/** * Parse and validate the coupon input file, building and returning an array of coupon data * to import into the database. * * The coupon data is broken into two portions: the couple of defined fields * that make up the wp_posts table, and then the name-value meta data that is * inserted into the wp_postmeta table. Within the meta data category, there * are known meta fields, such as 'discount_type' for instance, and then * arbitrary meta fields are allowed and identified by a CSV column title with * the prefix 'meta:'. * * @param array $parsed_data the raw data parsed from the CSV file * @param array $raw_headers the headers parsed from the CSV file * @param boolean $merging whether this is a straight import, or merge. For * the coupon import this will always be false. * @param int $record_offset number of records to skip before processing * * @return array associative array containing the key 'coupon' mapped to the parsed * data, and key 'skipped' with a count of the skipped rows */ private function parse_coupons($parsed_data, $raw_headers, $merging, $record_offset) { global $WC_CSV_Import, $wpdb; $results = array(); // Count row $row = 0; // Track skipped records $skipped = 0; // first validate headers (non custom meta) and issue warnings for any unexpected ones foreach ($raw_headers as $key => $value) { if ('meta:' != substr($key, 0, 5)) { if (!in_array($key, $this->coupon_data_fields) && !in_array($key, $this->coupon_meta_fields)) { $WC_CSV_Import->log->add(sprintf(__("> Warning: Unknown column named '%s' will be ignored.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($value))); } } } // Format coupon data foreach ($parsed_data as $item) { $row++; // skip record? if ($row <= $record_offset) { $WC_CSV_Import->log->add(sprintf(__('> Row %s - skipped due to record offset.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), $row)); continue; } $postmeta = $coupon = array(); // give the line number and coupon code $WC_CSV_Import->log->add(sprintf(__('> Row %s%s - preparing for import.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), $row, isset($item['coupon_code']) && $item['coupon_code'] ? ' - "' . $item['coupon_code'] . '"' : '')); // coupon code is required for merging or updating if (!isset($item['coupon_code']) || !$item['coupon_code']) { $WC_CSV_Import->log->add(__('> > Skipped. Missing coupon code.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN)); $skipped++; continue; } // Check for existing coupons $coupon_found = $wpdb->get_var($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE post_type = 'shop_coupon' AND post_status = 'publish' AND post_title = %s", $item['coupon_code'])); if ($merging) { // when merging we need that coupon to exist if (!$coupon_found) { $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Coupon code '%s' not found, unable to merge.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($item['coupon_code']))); $skipped++; continue; } // record the coupon id $coupon['id'] = $coupon_found; } else { // when inserting we need it to not exist if ($coupon_found) { $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Coupon code '%s' already exists.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($item['coupon_code']))); $skipped++; continue; } } // Get the set of possible coupon discount types $discount_types = SV_WC_Plugin_Compatibility::wc_get_coupon_types(); // discount type is required if (!$merging && (!isset($item['discount_type']) || !$item['discount_type'])) { $WC_CSV_Import->log->add(__('> > Skipped. Missing discount type.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN)); $skipped++; continue; } // Check for the discount type validity both by key and value (ie either 'fixed_cart' or 'Cart Discount' if (isset($item['discount_type']) && $item['discount_type']) { $discount_type_is_valid = false; foreach ($discount_types as $key => $value) { if (0 === strcasecmp($key, $item['discount_type']) || 0 === strcasecmp($value, __($item['discount_type'], WC_Customer_CSV_Import_Suite::TEXT_DOMAIN))) { $discount_type_is_valid = true; $postmeta[] = array('key' => 'discount_type', 'value' => $key); break; } } if (!$discount_type_is_valid) { $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Unknown discount type '%s'.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($item['discount_type']))); $skipped++; continue; } } // build the coupon data object $coupon['post_title'] = apply_filters('woocommerce_coupon_code', $item['coupon_code']); $coupon['post_excerpt'] = isset($item['description']) ? $item['description'] : ''; // Get any known coupon meta fields, and default any missing ones foreach ($this->coupon_meta_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)); if (!$product_id) { // unknown product $WC_CSV_Import->log->add(sprintf(__('> > Skipped. Unknown product sku: %s.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($sku))); $skipped++; continue 4; // break outer loop } $val[] = $product_id; } $postmeta[] = array('key' => 'products' == $column ? 'product_ids' : 'exclude_product_ids', 'value' => implode(',', $val)); 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'); if (!$term) { // unknown category $WC_CSV_Import->log->add(sprintf(__('> > Skipped. Unknown product category: %s.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($product_cat))); $skipped++; continue 4; // continue main loop } $val[] = $term['term_id']; } $postmeta[] = array('key' => $column, 'value' => $val); break; case 'customer_emails': $val = isset($item[$column]) ? $item[$column] : ''; $emails = array_filter(array_map('trim', explode(',', $val))); $val = array(); foreach ($emails as $email) { if (!is_email($email)) { // invalid email $WC_CSV_Import->log->add(sprintf(__('> > Skipped. Invalid email: %s.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($email))); $skipped++; continue 4; // continue main loop } $val[] = $email; } $postmeta[] = array('key' => 'customer_email', 'value' => $val); break; case 'free_shipping': // handle booleans, defaulting to 'no' on import (not merge) // handle booleans, defaulting to 'no' on import (not merge) case 'individual_use': case 'apply_before_tax': case 'exclude_sale_items': $val = isset($item[$column]) && $item[$column] ? strtolower($item[$column]) : ($merging ? '' : 'no'); if ($val && 'yes' != $val && 'no' != $val) { $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Column '%s' must be 'yes' or 'no'.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($raw_headers[$column]))); $skipped++; continue 3; // continue main loop } $postmeta[] = array('key' => $column, 'value' => $val); break; case 'expiry_date': $val = isset($item[$column]) ? $item[$column] : ''; if ($val && false === ($val = strtotime($val))) { // invalid date format $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Invalid date format '%s'.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($item[$column]))); $skipped++; continue 3; // continue main loop } $postmeta[] = array('key' => $column, 'value' => $val ? date('Y-m-d', $val) : ''); 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] : ''; if ('' !== $val && !is_numeric($val)) { // invalid usage count value $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Invalid %s '%s'.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($raw_headers[$column]), esc_html($val))); $skipped++; continue 3; // continue main loop } $postmeta[] = array('key' => $column, 'value' => $val); break; default: $postmeta[] = array('key' => $column, 'value' => isset($item[$column]) ? $item[$column] : ""); } } // Get any custom meta fields, as long as they have values foreach ($item as $key => $value) { if ('' !== $value) { continue; } // Handle meta: columns - import as custom fields if ('meta:' == substr($key, 0, 5)) { // Get raw meta key name $meta_key = $raw_headers[$key]; $meta_key = trim(str_replace('meta:', '', $meta_key)); // Add to postmeta array $postmeta[] = array('key' => esc_attr($meta_key), 'value' => $value); } } $coupon['postmeta'] = $postmeta; // the coupon array will now contain the necessary name-value pairs for the wp_posts table, and also any meta data in the 'postmeta' array $results[] = $coupon; } // Result return array($this->type => $results, 'skipped' => $skipped); }