* @copyright 2019 Dalibor Stojcevski * @license Dalibor Stojcevski */ /** Used for validatefields diying without user friendly error or not */ if (!defined('UNFRIENDLY_ERROR')) { define('UNFRIENDLY_ERROR', false); } class Creator { private $home_category = 0; private $id_lang = 1; public function __construct($file_id) { $this->id_lang = (int)Configuration::get('PS_LANG_DEFAULT'); $this->home_category = Configuration::get('PS_HOME_CATEGORY'); //$query_file_settings = Db::getInstance()->executeS("SELECT f.*, fs.mapping FROM ". _DB_PREFIX_ . "ia_files f LEFT JOIN ". _DB_PREFIX_ . "ia_file_settings fs ON(fs.file_id = f.file_id) WHERE f.file_id = '" . $file_id . "' LIMIT 1"); $query_file_settings = Db::getInstance()->executeS("SELECT * FROM " . _DB_PREFIX_ . "ia_file_settings WHERE file_id = '" . $file_id . "' LIMIT 1"); $this->settings = !empty($query_file_settings[0]['settings']) ? json_decode($query_file_settings[0]['settings'], true) : array(); $this->setAttributes(); } public function getManufacturerId($name) { if (is_numeric($name) && Manufacturer::manufacturerExists((int)$name)) { return (int)$name; } elseif (is_string($name)) { if ($manufacturer_id = Manufacturer::getIdByName($name)) { return (int)$manufacturer_id; } else { $manufacturer = new Manufacturer(); $manufacturer->name = $name; $manufacturer->active = true; if (($field_error = $manufacturer->validateFields(UNFRIENDLY_ERROR, true)) === true && ($lang_field_error = $manufacturer->validateFieldsLang(UNFRIENDLY_ERROR, true)) === true && $manufacturer->add() ) { return (int)$manufacturer->id; //$manufacturer->associateTo($product->id_shop_list); } } } return 0; } public function getCategoryId($value, $parent_id = null) { if (!$parent_id) { $parent_id = $this->home_category; } if (is_numeric($value) && Category::categoryExists((int)$value)) { return (int)$value; } elseif (is_string($value)) { $existing_category = Category::searchByName($this->id_lang, $value, true, true); // try and false at end to work with Cache, view Category class function if ($existing_category) { return (int)$existing_category['id_category']; } else { $category_to_create = new Category(); $category_to_create->id = (int)$value; $category_to_create->name = Creator::createMultiLangField($value); $category_to_create->active = 1; $category_to_create->id_parent = $parent_id; // Default parent is home for unknown category to create $category_link_rewrite = Tools::link_rewrite($category_to_create->name[$this->id_lang]); $category_to_create->link_rewrite = Creator::createMultiLangField($category_link_rewrite); if (($field_error = $category_to_create->validateFields(UNFRIENDLY_ERROR, true)) === true && ($lang_field_error = $category_to_create->validateFieldsLang(UNFRIENDLY_ERROR, true)) === true && $category_to_create->add() ) { return (int)$category_to_create->id; } } } return 0; } protected static function createMultiLangField($field) { $res = array(); foreach (Language::getIDs(false) as $id_lang) { $res[$id_lang] = $field; } return $res; } public function addImageToProduct($url, $id, $is_cover = false) { if (!empty($url)) { $url = str_replace(' ', '%20', $url); $image = new Image(); $image->id_product = (int)$id; $image->position = Image::getHighestPosition($id) + 1; $image->cover = $is_cover; // file_exists doesn't work with HTTP protocol if (($field_error = $image->validateFields(UNFRIENDLY_ERROR, true)) === true && ($lang_field_error = $image->validateFieldsLang(UNFRIENDLY_ERROR, true)) === true && $image->add() ) { // associate image to selected shops $shops = Shop::getContextListShopID(); $image->associateTo($shops); if (!Creator::copyImg($id, $image->id, $url, 'products', true)) { $image->delete(); } } } } public function addFeaturesToProduct($features, $id) { foreach ($features as $single_feature) { if (empty($single_feature)) { continue; } $feature_name = $single_feature['feature']; $feature_value = $single_feature['feature_value']; $position = false; $custom = false; $id_lang = $this->id_lang; $id_product = (int)$id; if (!empty($feature_name) && !empty($feature_value)) { $id_feature = (int)Feature::addFeatureImport($feature_name, $position); $id_feature_value = (int)FeatureValue::addFeatureValueImport($id_feature, $feature_value, $id_product, $id_lang, $custom); Product::addFeatureProductImport($id, $id_feature, $id_feature_value); } } } public function addAttributesToProduct($attributes_array, $id, $attribute_details = array()) { $product = new Product($id); $this->settings['combination_price_multiplier']; $attributes_to_add = array(); $attributes_ids = array(); foreach ($attributes_array as $key => $attributes_full) { foreach ($attributes_full as $attribute_pair) { if (empty($attribute_pair['attribute_value'])) { continue; } if ($id_attribute = $this->getAttributeId($attribute_pair)) { $attributes_to_add[$key][] = $id_attribute; if (isset($attribute_details)) { $attributes_ids[$id_attribute] = $attribute_pair['attribute_value']; } } } } $combinations = $this->generateCombinations($attributes_to_add); $attribute_details_par = array('price', 'ean', 'quantity', 'weight'); if (isset($attribute_details)) { foreach ($attribute_details_par as $parameter) { foreach ($combinations as $key => $combination) { foreach ($combination as $c) { if (isset($attribute_details[$parameter][$attributes_ids[$c]])) { $combination_detais[$key][$parameter] = $attribute_details[$parameter][$attributes_ids[$c]]; } } } } } foreach ($combinations as $key => $combination) { $implode_sql = array(); if (!$combination) continue; $combination_price = 0; if (isset($combination_detais[$key]['price'])) { $combination_price = (float)$combination_detais[$key]['price']; if (!empty($this->settings['combination_price_multiplier'])) { $combination_price *= (float)$this->settings['combination_price_multiplier']; } if (empty($this->settings['add_combination_price'])) { $combination_price -= $product->price; } if ($combination_price < 0) { $combination_price = 0; } } $combination_ean = (!empty($combination_detais[$key]['ean']) && Validate::isEan13($combination_detais[$key]['ean'])) ? $combination_detais[$key]['ean'] : ''; $id_product_attribute = $product->productAttributeExists($combination, false, null, false, true); // Last 'true' is to return id if (!$id_product_attribute) { $combination_weight = isset($combination_detais[$key]['weight']) ? (float)$combination_detais[$key]['weight'] : 0; $combination_quantity = isset($combination_detais[$key]['quantity']) ? (int)$combination_detais[$key]['quantity'] : (int)$product->quantity; $id_product_attribute = $product->addCombinationEntity( (float) 0, $combination_price, $combination_weight, 0, (Configuration::get('PS_USE_ECOTAX') ? (float) $product->ecotax : 0), $combination_quantity, null, '', 0, $combination_ean, null ); foreach ($combination as $id_attribute) { Db::getInstance()->execute(' INSERT IGNORE INTO ' . _DB_PREFIX_ . 'product_attribute_combination (id_attribute, id_product_attribute) VALUES (' . (int) $id_attribute . ',' . (int) $id_product_attribute . ')', false); } } else { if (isset($attribute_details)) { if (isset($combination_detais[$key]['price'])) { $implode_sql['price'] = 'price = ' . (float)$combination_price; } if (isset($combination_detais[$key]['weight'])) { $implode_sql['weight'] = 'weight = ' . (float)$combination_detais[$key]['weight']; } if (isset($combination_detais[$key]['ean'])) { $implode_sql['ean13'] = 'ean13 = ' . pSQL($combination_ean); } } } if (isset($combination_detais[$key]['quantity'])) { $implode_sql['quantity'] = 'quantity = ' . (int)$combination_detais[$key]['quantity']; StockAvailable::setQuantity($id, $id_product_attribute, (int)$combination_detais[$key]['quantity']); } if ($implode_sql) { Db::getInstance()->execute(' UPDATE ' . _DB_PREFIX_ . 'product_attribute SET ' . implode(', ', $implode_sql) . ' WHERE id_product_attribute = ' . (int)$id_product_attribute); unset($implode_sql['ean13']); unset($implode_sql['quantity']); Db::getInstance()->execute(' UPDATE ' . _DB_PREFIX_ . 'product_attribute_shop SET ' . implode(', ', $implode_sql) . ' WHERE id_product_attribute = ' . (int)$id_product_attribute); } } } /** * copyImg copy an image located in $url and save it in a path * according to $entity->$id_entity . * $id_image is used if we need to add a watermark * * @param int $id_entity id of product or category (set in entity) * @param int $id_image (default null) id of the image if watermark enabled. * @param string $url path or url to use * @param string $entity 'products' or 'categories' * @param bool $regenerate * @return bool */ protected static function copyImg($id_entity, $id_image = null, $url, $entity = 'products', $regenerate = true) { $tmpfile = tempnam(_PS_TMP_IMG_DIR_, 'ps_import'); $watermark_types = explode(',', Configuration::get('WATERMARK_TYPES')); switch ($entity) { default: case 'products': $image_obj = new Image($id_image); $path = $image_obj->getPathForCreation(); break; case 'categories': $path = _PS_CAT_IMG_DIR_ . (int)$id_entity; break; case 'manufacturers': $path = _PS_MANU_IMG_DIR_ . (int)$id_entity; break; case 'suppliers': $path = _PS_SUPP_IMG_DIR_ . (int)$id_entity; break; } $url = urldecode(trim($url)); $parced_url = parse_url($url); if (isset($parced_url['path'])) { $uri = ltrim($parced_url['path'], '/'); $parts = explode('/', $uri); foreach ($parts as &$part) { $part = rawurlencode($part); } unset($part); $parced_url['path'] = '/' . implode('/', $parts); } if (isset($parced_url['query'])) { $query_parts = array(); parse_str($parced_url['query'], $query_parts); $parced_url['query'] = http_build_query($query_parts); } if (!function_exists('http_build_url')) { require_once(_PS_TOOL_DIR_ . 'http_build_url/http_build_url.php'); } $url = http_build_url('', $parced_url); $orig_tmpfile = $tmpfile; // download image by curl to avoid ssl problem $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_TIMEOUT, 50); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); $image = curl_exec($curl); curl_close($curl); // copy image to $tmpfile file_put_contents($tmpfile, $image); // if $tmpfile is not readable, return false if (!filesize($tmpfile)) { @unlink($tmpfile); return false; } // Evaluate the memory required to resize the image: if it's too much, you can't resize it. if (!ImageManager::checkImageMemoryLimit($tmpfile)) { @unlink($tmpfile); return false; } // Sprawdź typ MIME pliku $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime_type = finfo_file($finfo, $tmpfile); finfo_close($finfo); if ($mime_type == 'image/webp') { // Wczytaj obraz WebP $image = imagecreatefromwebp($tmpfile); if ($image === false) { @unlink($tmpfile); return false; } // Zapisz obraz jako JPG, nadpisując oryginalny plik imagejpeg($image, $tmpfile, 100); // 100% jakości imagedestroy($image); } if (!ImageManager::isRealImage($tmpfile)) { @unlink($tmpfile); return false; } $tgt_width = $tgt_height = 0; $src_width = $src_height = 0; $error = 0; ImageManager::resize( $tmpfile, $path . '.jpg', null, null, 'jpg', false, $error, $tgt_width, $tgt_height, 5, $src_width, $src_height ); $images_types = ImageType::getImagesTypes($entity, true); if ($regenerate) { $previous_path = null; $path_infos = array(); $path_infos[] = array($tgt_width, $tgt_height, $path . '.jpg'); foreach ($images_types as $image_type) { $tmpfile = self::get_best_path($image_type['width'], $image_type['height'], $path_infos); if (ImageManager::resize( $tmpfile, $path . '-' . stripslashes($image_type['name']) . '.jpg', $image_type['width'], $image_type['height'], 'jpg', false, $error, $tgt_width, $tgt_height, 5, $src_width, $src_height )) { // the last image should not be added in the candidate list if it's bigger than the original image if ($tgt_width <= $src_width && $tgt_height <= $src_height) { $path_infos[] = array($tgt_width, $tgt_height, $path . '-' . stripslashes($image_type['name']) . '.jpg'); } if ($entity == 'products') { if (is_file(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int)$id_entity . '.jpg')) { unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int)$id_entity . '.jpg'); } if (is_file(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int)$id_entity . '_' . (int)Context::getContext()->shop->id . '.jpg')) { unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int)$id_entity . '_' . (int)Context::getContext()->shop->id . '.jpg'); } } } if (in_array($image_type['id_image_type'], $watermark_types)) { Hook::exec('actionWatermark', array('id_image' => $id_image, 'id_product' => $id_entity)); } } } unlink($orig_tmpfile); return true; } private static function get_best_path($tgt_width, $tgt_height, $path_infos) { $path_infos = array_reverse($path_infos); $path = ''; foreach ($path_infos as $path_info) { list($width, $height, $path) = $path_info; if ($width >= $tgt_width && $height >= $tgt_height) { return $path; } } return $path; } public function getProductId($query, $table, $identifier = 'name') { $sql = new DbQuery(); $sql->select('p.`id_product`, p.`ean13`, p.`upc`, p.`active`, p.`reference`'); $sql->from('product', 'p'); if ($identifier == 'name') { $sql->join(Shop::addSqlAssociation('product', 'p')); $sql->leftJoin( 'product_lang', 'pl', ' p.`id_product` = pl.`id_product` AND pl.`id_lang` = ' . (int)$this->id_lang . Shop::addSqlRestrictionOnLang('pl') ); $where = 'pl.`name` = \'' . pSQL($query) . '\''; } else { $where = 'p.`' . $identifier . '` = \'' . pSQL($query) . '\''; } $sql->where($where); $sql->limit(1); $result = Db::getInstance()->executeS($sql); if (!empty($result[0]['id_product'])) { return $result[0]['id_product']; } return 0; } public function editProduct($product_id, $original_product) { $simple_fields = array('price', 'wholesale_price', 'additional_shipping_cost', 'ean13', 'ups', 'condition', 'width', 'height', 'depth', 'weight', 'active', 'minimal_quantity'); $product = new Product($product_id); if ($product->name == null) { return $product; } if (!empty($this->settings['update_manufacturer'])) { if (!empty($original_product['brand'])) { $product->id_manufacturer = $this->getManufacturerId($original_product['brand']); } else { $product->id_manufacturer = $this->settings['default_manufacturer_id']; } } foreach ($simple_fields as $field) { if (!empty($this->settings['update_' . $field]) && isset($original_product[$field])) { $product->$field = $original_product[$field]; } } if (isset($this->settings['update_id_tax_rules_group']) && $this->settings['id_tax_rules_group'] != -1) { $product->id_tax_rules_group = $this->settings['id_tax_rules_group']; } foreach (Language::getLanguages(false) as $lang) { if (!empty($this->settings['update_name'])) { $original_product['name'] = htmlspecialchars_decode($original_product['name']); $original_product['name'] = str_replace(['>', '<', '=', ';', '{', '}', '#'], ' ', $original_product['name']); $product->name[$lang['id_lang']] = $original_product['name']; $product->link_rewrite[$lang['id_lang']] = Tools::link_rewrite($original_product['name']); $product->meta_keywords[$lang['id_lang']] = str_replace(' ', ',', $original_product['name']); } if (!empty($this->settings['update_description'])) { $original_product['description'] = nl2br($original_product['description']); $original_product['description'] = htmlspecialchars_decode(htmlspecialchars_decode($original_product['description'], ENT_COMPAT)); $product->description_long[$lang['id_lang']] = $original_product['description']; $product->description[$lang['id_lang']] = $original_product['description']; } if (!empty($original_product['short_description']) && !empty($this->settings['update_short_description'])) { $product->description_short[$lang['id_lang']] = $original_product['short_description']; } } $product->additional_delivery_times = 2; $product->delivery_in_stock[7] = '1-5 dni roboczych'; //$product->active = isset($original_product['active']) ? (int)$original_product['active'] : 1;; $product->save(); if (!empty($this->settings['update_quantity'])) { StockAvailable::setQuantity((int)$product_id, 0, $original_product['quantity']); } // check if product has no images $images_count = "SELECT COUNT(0) FROM " . _DB_PREFIX_ . "image WHERE id_product = " . $product->id; $result_images_count = Db::getInstance()->getValue($images_count); if (!empty($this->settings['update_images']) and $result_images_count == 0) { foreach ($original_product['images'] as $image_url) { $this->addImageToProduct($image_url, $product->id); } if ($original_product['cover']) { $this->addImageToProduct($original_product['cover'], $product->id, true); } } if (!empty($this->settings['update_cover']) && !empty($original_product['cover'])) { $this->addImageToProduct($original_product['cover'], $product->id, true); } if ($original_product['feature'] and $original_product['feature_value']) { $original_product['features'] = array( array( 'feature' => $original_product['feature'], 'feature_value' => $original_product['feature_value'] ) ); } if (!empty($this->settings['update_features']) && !empty($original_product['features'])) { $this->addFeaturesToProduct($original_product['features'], $product->id); } if (!empty($this->settings['update_attributes']) && !empty($original_product['attributes'])) { $attribute_details = !empty($original_product['attribute_details']) ? $original_product['attribute_details'] : array(); $this->addAttributesToProduct($original_product['attributes'], $product->id, $attribute_details); } if ( $this -> settings['update_active'] ) { $sql = 'UPDATE ' . _DB_PREFIX_ . 'product SET active = ' . (int)$original_product['active'] . ' WHERE id_product = ' . (int)$product->id; Db::getInstance()->execute($sql); // materac_product_shop $sql_shop = 'UPDATE ' . _DB_PREFIX_ . 'product_shop SET active = ' . (int)$original_product['active'] . ' WHERE id_product = ' . (int)$product->id; Db::getInstance()->execute($sql_shop); } if ( $this -> settings['update_quantity'] ) { $sql = 'UPDATE ' . _DB_PREFIX_ . 'stock_available SET quantity = ' . (int)$original_product['quantity'] . ' WHERE id_product = ' . (int)$product->id; Db::getInstance()->execute($sql); } return $product; } public function processMissing($file_id, $queue_id) { $sql = "SELECT * FROM " . _DB_PREFIX_ . "ia_products WHERE shop = 'default' AND file_id = " . (int)$file_id . " AND queue_id != " . (int)$queue_id; //$sql .= " LIMIT 20"; if (!empty($this->settings['not_existing']) && $products = Db::getInstance()->executeS($sql)) { if ($this->settings['not_existing'] == 1) { foreach ($products as $product) { StockAvailable::setQuantity((int)$product['product_id'], 0, 0); } } elseif ($this->settings['not_existing'] == 2) { foreach ($products as $product) { $productObject = new Product($product['product_id']); if ($productObject->name) { $productObject->active = 0; $productObject->save(); } } } elseif ($this->settings['not_existing'] == 3) { foreach ($products as $product) { $productObject = new Product($product['product_id']); if ($productObject->name) { $productObject->delete(); } Db::getInstance()->execute("DELETE FROM " . _DB_PREFIX_ . "ia_products WHERE product_id = '" . (int)$product['product_id'] . "'"); } } } Db::getInstance()->execute("UPDATE " . _DB_PREFIX_ . "ia_queues SET source = 'admin', status = '4', date_processed = '" . time() . "' WHERE queue_id = '" . (int)$queue_id . "'"); } public function setAttributes() { $default_language = $this->id_lang; $this->groups = array(); foreach (AttributeGroup::getAttributesGroups($default_language) as $group) { $this->groups[$group['name']] = (int) $group['id_attribute_group']; } $this->attributes = array(); foreach (Attribute::getAttributes($default_language) as $attribute) { $this->attributes[$attribute['attribute_group'] . '_' . $attribute['name']] = (int) $attribute['id_attribute']; } } function getAttributeGroupId($name) { $tab_group = explode(':', $name); $group = trim($tab_group[0]); if (isset($this->groups[$group])) { return $this->groups[$group]; } if (!isset($tab_group[1])) { $type = 'select'; } else { $type = trim($tab_group[1]); } $obj = new AttributeGroup(); $obj->is_color_group = false; $obj->group_type = pSQL($type); $obj->name[$this->id_lang] = $group; $obj->public_name[$this->id_lang] = $group; //$obj->position = (!$position) ? AttributeGroup::getHigherPosition() + 1 : $position; $obj->add(); $this->groups[$group] = $obj->id; return $obj->id; } function getAttributeId($pair) { $name = $pair['attribute_value']; $group = $pair['attribute']; if (isset($this->attributes[$group . '_' . $name])) { return $this->attributes[$group . '_' . $name]; } $id_attribute_group = $this->getAttributeGroupId($group); $obj = new Attribute(); $obj->id_attribute_group = $id_attribute_group; $obj->name[$this->id_lang] = str_replace('\n', '', str_replace('\r', '', $name)); $obj->add(); $this->attributes[$group . '_' . $name] = $obj->id; return $obj->id; } function generateCombinations($attributes_to_add) { $i = 0; $combinations = array(); // 3 options if (count($attributes_to_add) == 3) { foreach ($attributes_to_add[1] as $attr_1) { $temp_1 = isset($combinations[$i]) ? $combinations[$i] : array(); $combinations[$i][] = $attr_1; foreach ($attributes_to_add[2] as $attr_2) { $temp_2 = $combinations[$i]; $combinations[$i][] = $attr_2; foreach ($attributes_to_add[3] as $attr_3) { $temp_3 = $combinations[$i]; $combinations[$i][] = $attr_3; $i++; $combinations[$i] = $temp_3; } $combinations[$i] = $temp_2; } $combinations[$i] = $temp_1; } } elseif (count($attributes_to_add) == 2 && isset($attributes_to_add[1])) { foreach ($attributes_to_add[1] as $attr_1) { $temp_1 = isset($combinations[$i]) ? $combinations[$i] : array(); $combinations[$i][] = $attr_1; foreach ($attributes_to_add[2] as $attr_2) { $temp_2 = $combinations[$i]; $combinations[$i][] = $attr_2; $i++; $combinations[$i] = $temp_2; } $combinations[$i] = $temp_1; } } elseif (count($attributes_to_add) == 1 && isset($attributes_to_add[1])) { foreach ($attributes_to_add[1] as $attr_1) { //$temp_0 = isset($combinations[$i]) ? $combinations[$i] : array(); $combinations[$i][] = $attr_1; $i++; //$combinations[$i] = $temp_0; } } return $combinations; } }