* @copyright 2007-2019 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ use PhpOffice\PhpSpreadsheet\IOFactory; @ini_set('max_execution_time', 0); /* No max line limit since the lines can be more than 4096. Performance impact is not significant. */ define('MAX_LINE_SIZE', 0); /* Used for validatefields diying without user friendly error or not */ define('UNFRIENDLY_ERROR', false); /* this value set the number of columns visible on each page */ define('MAX_COLUMNS', 6); /* correct Mac error on eof */ @ini_set('auto_detect_line_endings', '1'); class AdminImportControllerCore extends AdminController { public static $column_mask; public $entities = array(); public $available_fields = array(); public $required_fields = array(); public static $default_values = array(); public static $validators = array( 'active' => array('AdminImportController', 'getBoolean'), 'tax_rate' => array('AdminImportController', 'getPrice'), /* Tax excluded */ 'price_tex' => array('AdminImportController', 'getPrice'), /* Tax included */ 'price_tin' => array('AdminImportController', 'getPrice'), 'reduction_price' => array('AdminImportController', 'getPrice'), 'reduction_percent' => array('AdminImportController', 'getPrice'), 'wholesale_price' => array('AdminImportController', 'getPrice'), 'ecotax' => array('AdminImportController', 'getPrice'), 'name' => array('AdminImportController', 'createMultiLangField'), 'description' => array('AdminImportController', 'createMultiLangField'), 'description_short' => array('AdminImportController', 'createMultiLangField'), 'meta_title' => array('AdminImportController', 'createMultiLangField'), 'meta_keywords' => array('AdminImportController', 'createMultiLangField'), 'meta_description' => array('AdminImportController', 'createMultiLangField'), 'link_rewrite' => array('AdminImportController', 'createMultiLangField'), 'available_now' => array('AdminImportController', 'createMultiLangField'), 'available_later' => array('AdminImportController', 'createMultiLangField'), 'category' => array('AdminImportController', 'split'), 'online_only' => array('AdminImportController', 'getBoolean'), 'accessories' => array('AdminImportController', 'split'), 'image_alt' => array('AdminImportController', 'split'), 'delivery_in_stock' => array('AdminImportController', 'createMultiLangField'), 'delivery_out_stock' => array('AdminImportController', 'createMultiLangField'), ); public $separator; public $convert; public $multiple_value_separator; /** * This flag shows if import was executed in current request. * Used for symfony migration purposes. * * @var bool */ private $importExecuted = false; public function __construct() { $this->bootstrap = true; parent::__construct(); $this->entities = array( $this->trans('Categories', array(), 'Admin.Global'), $this->trans('Products', array(), 'Admin.Global'), $this->trans('Combinations', array(), 'Admin.Global'), $this->trans('Customers', array(), 'Admin.Global'), $this->trans('Addresses', array(), 'Admin.Global'), $this->trans('Brands', array(), 'Admin.Global'), $this->trans('Suppliers', array(), 'Admin.Global'), $this->trans('Alias', array(), 'Admin.Shopparameters.Feature'), $this->trans('Store contacts', array(), 'Admin.Advparameters.Feature'), ); // @since 1.5.0 if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT')) { $this->entities = array_merge( $this->entities, array( $this->trans('Supply Orders', array(), 'Admin.Advparameters.Feature'), $this->trans('Supply Order Details', array(), 'Admin.Advparameters.Feature'), ) ); } $this->entities = array_flip($this->entities); switch ((int) Tools::getValue('entity')) { case $this->entities[$this->trans('Combinations', array(), 'Admin.Global')]: $this->required_fields = array( 'group', 'attribute', ); $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id_product' => array('label' => $this->trans('Product ID', array(), 'Admin.Advparameters.Feature')), 'product_reference' => array('label' => $this->trans('Product Reference', array(), 'Admin.Advparameters.Feature')), 'group' => array( 'label' => $this->trans('Attribute (Name:Type:Position)', array(), 'Admin.Advparameters.Feature') . '*', ), 'attribute' => array( 'label' => $this->trans('Value (Value:Position)', array(), 'Admin.Advparameters.Feature') . '*', ), 'supplier_reference' => array('label' => $this->trans('Supplier reference', array(), 'Admin.Advparameters.Feature')), 'reference' => array('label' => $this->trans('Reference', array(), 'Admin.Global')), 'ean13' => array('label' => $this->trans('EAN13', array(), 'Admin.Advparameters.Feature')), 'upc' => array('label' => $this->trans('UPC', array(), 'Admin.Advparameters.Feature')), 'wholesale_price' => array('label' => $this->trans('Cost price', array(), 'Admin.Catalog.Feature')), 'price' => array('label' => $this->trans('Impact on price', array(), 'Admin.Catalog.Feature')), 'ecotax' => array('label' => $this->trans('Ecotax', array(), 'Admin.Catalog.Feature')), 'quantity' => array('label' => $this->trans('Quantity', array(), 'Admin.Global')), 'minimal_quantity' => array('label' => $this->trans('Minimal quantity', array(), 'Admin.Advparameters.Feature')), 'low_stock_threshold' => array('label' => $this->trans('Low stock level', array(), 'Admin.Catalog.Feature')), 'low_stock_alert' => array('label' => $this->trans('Send me an email when the quantity is under this level', array(), 'Admin.Catalog.Feature')), 'weight' => array('label' => $this->trans('Impact on weight', array(), 'Admin.Catalog.Feature')), 'default_on' => array('label' => $this->trans('Default (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature')), 'available_date' => array('label' => $this->trans('Combination availability date', array(), 'Admin.Advparameters.Feature')), 'image_position' => array( 'label' => $this->trans('Choose among product images by position (1,2,3...)', array(), 'Admin.Advparameters.Feature'), ), 'image_url' => array('label' => $this->trans('Image URLs (x,y,z...)', array(), 'Admin.Advparameters.Feature')), 'image_alt' => array('label' => $this->trans('Image alt texts (x,y,z...)', array(), 'Admin.Advparameters.Feature')), 'shop' => array( 'label' => $this->trans('ID / Name of shop', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Ignore this field if you don\'t use the Multistore tool. If you leave this field empty, the default shop will be used.', array(), 'Admin.Advparameters.Help'), ), 'advanced_stock_management' => array( 'label' => $this->trans('Advanced Stock Management', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Enable Advanced Stock Management on product (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Help'), ), 'depends_on_stock' => array( 'label' => $this->trans('Depends on stock', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('0 = Use quantity set in product, 1 = Use quantity from warehouse.', array(), 'Admin.Advparameters.Help'), ), 'warehouse' => array( 'label' => $this->trans('Warehouse', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('ID of the warehouse to set as storage.', array(), 'Admin.Advparameters.Help'), ), ); self::$default_values = array( 'reference' => '', 'supplier_reference' => '', 'ean13' => '', 'upc' => '', 'wholesale_price' => 0, 'price' => 0, 'ecotax' => 0, 'quantity' => 0, 'minimal_quantity' => 1, 'low_stock_threshold' => null, 'low_stock_alert' => false, 'weight' => 0, 'default_on' => null, 'advanced_stock_management' => 0, 'depends_on_stock' => 0, 'available_date' => date('Y-m-d'), ); break; case $this->entities[$this->trans('Categories', array(), 'Admin.Global')]: $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id' => array('label' => $this->trans('ID', array(), 'Admin.Global')), 'active' => array('label' => $this->trans('Active (0/1)', array(), 'Admin.Advparameters.Feature')), 'name' => array('label' => $this->trans('Name', array(), 'Admin.Global')), 'parent' => array('label' => $this->trans('Parent category', array(), 'Admin.Catalog.Feature')), 'is_root_category' => array( 'label' => $this->trans('Root category (0/1)', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('A category root is where a category tree can begin. This is used with multistore.', array(), 'Admin.Advparameters.Help'), ), 'description' => array('label' => $this->trans('Description', array(), 'Admin.Global')), 'meta_title' => array('label' => $this->trans('Meta title', array(), 'Admin.Global')), 'meta_keywords' => array('label' => $this->trans('Meta keywords', array(), 'Admin.Global')), 'meta_description' => array('label' => $this->trans('Meta description', array(), 'Admin.Global')), 'link_rewrite' => array('label' => $this->trans('Rewritten URL', array(), 'Admin.Shopparameters.Feature')), 'image' => array('label' => $this->trans('Image URL', array(), 'Admin.Advparameters.Feature')), 'shop' => array( 'label' => $this->trans('ID / Name of shop', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Ignore this field if you don\'t use the Multistore tool. If you leave this field empty, the default shop will be used.', array(), 'Admin.Advparameters.Help'), ), ); self::$default_values = array( 'active' => '1', 'parent' => Configuration::get('PS_HOME_CATEGORY'), 'link_rewrite' => '', ); break; case $this->entities[$this->trans('Products', array(), 'Admin.Global')]: self::$validators['image'] = array( 'AdminImportController', 'split', ); $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id' => array('label' => $this->trans('ID', array(), 'Admin.Global')), 'active' => array('label' => $this->trans('Active (0/1)', array(), 'Admin.Advparameters.Feature')), 'name' => array('label' => $this->trans('Name', array(), 'Admin.Global')), 'category' => array('label' => $this->trans('Categories (x,y,z...)', array(), 'Admin.Advparameters.Feature')), 'price_tex' => array('label' => $this->trans('Price tax excluded', array(), 'Admin.Advparameters.Feature')), 'price_tin' => array('label' => $this->trans('Price tax included', array(), 'Admin.Advparameters.Feature')), 'id_tax_rules_group' => array('label' => $this->trans('Tax rule ID', array(), 'Admin.Advparameters.Feature')), 'wholesale_price' => array('label' => $this->trans('Cost price', array(), 'Admin.Catalog.Feature')), 'on_sale' => array('label' => $this->trans('On sale (0/1)', array(), 'Admin.Advparameters.Feature')), 'reduction_price' => array('label' => $this->trans('Discount amount', array(), 'Admin.Advparameters.Feature')), 'reduction_percent' => array('label' => $this->trans('Discount percent', array(), 'Admin.Advparameters.Feature')), 'reduction_from' => array('label' => $this->trans('Discount from (yyyy-mm-dd)', array(), 'Admin.Advparameters.Feature')), 'reduction_to' => array('label' => $this->trans('Discount to (yyyy-mm-dd)', array(), 'Admin.Advparameters.Feature')), 'reference' => array('label' => $this->trans('Reference #', array(), 'Admin.Advparameters.Feature')), 'supplier_reference' => array('label' => $this->trans('Supplier reference #', array(), 'Admin.Advparameters.Feature')), 'supplier' => array('label' => $this->trans('Supplier', array(), 'Admin.Global')), 'manufacturer' => array('label' => $this->trans('Brand', array(), 'Admin.Global')), 'ean13' => array('label' => $this->trans('EAN13', array(), 'Admin.Advparameters.Feature')), 'upc' => array('label' => $this->trans('UPC', array(), 'Admin.Advparameters.Feature')), 'ecotax' => array('label' => $this->trans('Ecotax', array(), 'Admin.Catalog.Feature')), 'width' => array('label' => $this->trans('Width', array(), 'Admin.Global')), 'height' => array('label' => $this->trans('Height', array(), 'Admin.Global')), 'depth' => array('label' => $this->trans('Depth', array(), 'Admin.Global')), 'weight' => array('label' => $this->trans('Weight', array(), 'Admin.Global')), 'delivery_in_stock' => array( 'label' => $this->trans( 'Delivery time of in-stock products:', array(), 'Admin.Catalog.Feature' ), ), 'delivery_out_stock' => array( 'label' => $this->trans( 'Delivery time of out-of-stock products with allowed orders:', array(), 'Admin.Advparameters.Feature' ), ), 'quantity' => array('label' => $this->trans('Quantity', array(), 'Admin.Global')), 'minimal_quantity' => array('label' => $this->trans('Minimal quantity', array(), 'Admin.Advparameters.Feature')), 'low_stock_threshold' => array('label' => $this->trans('Low stock level', array(), 'Admin.Catalog.Feature')), 'low_stock_alert' => array('label' => $this->trans('Send me an email when the quantity is under this level', array(), 'Admin.Catalog.Feature')), 'visibility' => array('label' => $this->trans('Visibility', array(), 'Admin.Catalog.Feature')), 'additional_shipping_cost' => array('label' => $this->trans('Additional shipping cost', array(), 'Admin.Advparameters.Feature')), 'unity' => array('label' => $this->trans('Unit for the price per unit', array(), 'Admin.Advparameters.Feature')), 'unit_price' => array('label' => $this->trans('Price per unit', array(), 'Admin.Advparameters.Feature')), 'description_short' => array('label' => $this->trans('Summary', array(), 'Admin.Catalog.Feature')), 'description' => array('label' => $this->trans('Description', array(), 'Admin.Global')), 'tags' => array('label' => $this->trans('Tags (x,y,z...)', array(), 'Admin.Advparameters.Feature')), 'meta_title' => array('label' => $this->trans('Meta title', array(), 'Admin.Global')), 'meta_keywords' => array('label' => $this->trans('Meta keywords', array(), 'Admin.Global')), 'meta_description' => array('label' => $this->trans('Meta description', array(), 'Admin.Global')), 'link_rewrite' => array('label' => $this->trans('Rewritten URL', array(), 'Admin.Advparameters.Feature')), 'available_now' => array('label' => $this->trans('Label when in stock', array(), 'Admin.Catalog.Feature')), 'available_later' => array('label' => $this->trans('Label when backorder allowed', array(), 'Admin.Advparameters.Feature')), 'available_for_order' => array('label' => $this->trans('Available for order (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature')), 'available_date' => array('label' => $this->trans('Product availability date', array(), 'Admin.Advparameters.Feature')), 'date_add' => array('label' => $this->trans('Product creation date', array(), 'Admin.Advparameters.Feature')), 'show_price' => array('label' => $this->trans('Show price (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature')), 'image' => array('label' => $this->trans('Image URLs (x,y,z...)', array(), 'Admin.Advparameters.Feature')), 'image_alt' => array('label' => $this->trans('Image alt texts (x,y,z...)', array(), 'Admin.Advparameters.Feature')), 'delete_existing_images' => array( 'label' => $this->trans('Delete existing images (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature'), ), 'features' => array('label' => $this->trans('Feature (Name:Value:Position:Customized)', array(), 'Admin.Advparameters.Feature')), 'online_only' => array('label' => $this->trans('Available online only (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature')), 'condition' => array('label' => $this->trans('Condition', array(), 'Admin.Catalog.Feature')), 'customizable' => array('label' => $this->trans('Customizable (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature')), 'uploadable_files' => array('label' => $this->trans('Uploadable files (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature')), 'text_fields' => array('label' => $this->trans('Text fields (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature')), 'out_of_stock' => array('label' => $this->trans('Action when out of stock', array(), 'Admin.Advparameters.Feature')), 'is_virtual' => array('label' => $this->trans('Virtual product (0 = No, 1 = Yes)', array(), 'Admin.Advparameters.Feature')), 'file_url' => array('label' => $this->trans('File URL', array(), 'Admin.Advparameters.Feature')), 'nb_downloadable' => array( 'label' => $this->trans('Number of allowed downloads', array(), 'Admin.Catalog.Feature'), 'help' => $this->trans('Number of days this file can be accessed by customers. Set to zero for unlimited access.', array(), 'Admin.Catalog.Help'), ), 'date_expiration' => array('label' => $this->trans('Expiration date (yyyy-mm-dd)', array(), 'Admin.Advparameters.Feature')), 'nb_days_accessible' => array( 'label' => $this->trans('Number of days', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Number of days this file can be accessed by customers. Set to zero for unlimited access.', array(), 'Admin.Catalog.Help'), ), 'shop' => array( 'label' => $this->trans('ID / Name of shop', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Ignore this field if you don\'t use the Multistore tool. If you leave this field empty, the default shop will be used.', array(), 'Admin.Advparameters.Help'), ), 'advanced_stock_management' => array( 'label' => $this->trans('Advanced Stock Management', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Enable Advanced Stock Management on product (0 = No, 1 = Yes).', array(), 'Admin.Advparameters.Help'), ), 'depends_on_stock' => array( 'label' => $this->trans('Depends on stock', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('0 = Use quantity set in product, 1 = Use quantity from warehouse.', array(), 'Admin.Advparameters.Help'), ), 'warehouse' => array( 'label' => $this->trans('Warehouse', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('ID of the warehouse to set as storage.', array(), 'Admin.Advparameters.Help'), ), 'accessories' => array('label' => $this->trans('Accessories (x,y,z...)', array(), 'Admin.Advparameters.Feature')), ); self::$default_values = array( 'id_category' => array((int) Configuration::get('PS_HOME_CATEGORY')), 'id_category_default' => null, 'active' => '1', 'width' => 0.000000, 'height' => 0.000000, 'depth' => 0.000000, 'weight' => 0.000000, 'visibility' => 'both', 'additional_shipping_cost' => 0.00, 'unit_price' => 0, 'quantity' => 0, 'minimal_quantity' => 1, 'low_stock_threshold' => null, 'low_stock_alert' => false, 'price' => 0, 'id_tax_rules_group' => 0, 'description_short' => array((int) Configuration::get('PS_LANG_DEFAULT') => ''), 'link_rewrite' => array((int) Configuration::get('PS_LANG_DEFAULT') => ''), 'online_only' => 0, 'condition' => 'new', 'available_date' => date('Y-m-d'), 'date_add' => date('Y-m-d H:i:s'), 'date_upd' => date('Y-m-d H:i:s'), 'customizable' => 0, 'uploadable_files' => 0, 'text_fields' => 0, 'advanced_stock_management' => 0, 'depends_on_stock' => 0, 'is_virtual' => 0, ); break; case $this->entities[$this->trans('Customers', array(), 'Admin.Global')]: //Overwrite required_fields AS only email is required whereas other entities $this->required_fields = array('email', 'passwd', 'lastname', 'firstname'); $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id' => array('label' => $this->trans('ID', array(), 'Admin.Global')), 'active' => array('label' => $this->trans('Active (0/1)', array(), 'Admin.Advparameters.Feature')), 'id_gender' => array('label' => $this->trans('Titles ID (Mr = 1, Ms = 2, else 0)', array(), 'Admin.Advparameters.Feature')), 'email' => array('label' => $this->trans('Email', array(), 'Admin.Global') . '*'), 'passwd' => array('label' => $this->trans('Password', array(), 'Admin.Global') . '*'), 'birthday' => array('label' => $this->trans('Birth date (yyyy-mm-dd)', array(), 'Admin.Advparameters.Feature')), 'lastname' => array('label' => $this->trans('Last name', array(), 'Admin.Global') . '*'), 'firstname' => array('label' => $this->trans('First name', array(), 'Admin.Global') . '*'), 'newsletter' => array('label' => $this->trans('Newsletter (0/1)', array(), 'Admin.Advparameters.Feature')), 'optin' => array('label' => $this->trans('Partner offers (0/1)', array(), 'Admin.Advparameters.Feature')), 'date_add' => array('label' => $this->trans('Registration date (yyyy-mm-dd)', array(), 'Admin.Advparameters.Feature')), 'group' => array('label' => $this->trans('Groups (x,y,z...)', array(), 'Admin.Advparameters.Feature')), 'id_default_group' => array('label' => $this->trans('Default group ID', array(), 'Admin.Advparameters.Feature')), 'id_shop' => array( 'label' => $this->trans('ID / Name of shop', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Ignore this field if you don\'t use the Multistore tool. If you leave this field empty, the default shop will be used.', array(), 'Admin.Advparameters.Help'), ), ); self::$default_values = array( 'active' => '1', 'id_shop' => Configuration::get('PS_SHOP_DEFAULT'), ); break; case $this->entities[$this->trans('Addresses', array(), 'Admin.Global')]: //Overwrite required_fields $this->required_fields = array( 'alias', 'lastname', 'firstname', 'address1', 'postcode', 'country', 'customer_email', 'city', ); $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id' => array('label' => $this->trans('ID', array(), 'Admin.Global')), 'alias' => array('label' => $this->trans('Alias', array(), 'Admin.Shopparameters.Feature') . '*'), 'active' => array('label' => $this->trans('Active (0/1)', array(), 'Admin.Advparameters.Feature')), 'customer_email' => array('label' => $this->trans('Customer email', array(), 'Admin.Advparameters.Feature') . '*'), 'id_customer' => array('label' => $this->trans('Customer ID', array(), 'Admin.Advparameters.Feature')), 'manufacturer' => array('label' => $this->trans('Brand', array(), 'Admin.Global')), 'supplier' => array('label' => $this->trans('Supplier', array(), 'Admin.Global')), 'company' => array('label' => $this->trans('Company', array(), 'Admin.Global')), 'lastname' => array('label' => $this->trans('Last name', array(), 'Admin.Global') . '*'), 'firstname' => array('label' => $this->trans('First name ', array(), 'Admin.Global') . '*'), 'address1' => array('label' => $this->trans('Address', array(), 'Admin.Global') . '*'), 'address2' => array('label' => $this->trans('Address (2)', array(), 'Admin.Global')), 'postcode' => array('label' => $this->trans('Zip/postal code', array(), 'Admin.Global') . '*'), 'city' => array('label' => $this->trans('City', array(), 'Admin.Global') . '*'), 'country' => array('label' => $this->trans('Country', array(), 'Admin.Global') . '*'), 'state' => array('label' => $this->trans('State', array(), 'Admin.Global')), 'other' => array('label' => $this->trans('Other', array(), 'Admin.Global')), 'phone' => array('label' => $this->trans('Phone', array(), 'Admin.Global')), 'phone_mobile' => array('label' => $this->trans('Mobile Phone', array(), 'Admin.Global')), 'vat_number' => array('label' => $this->trans('VAT number', array(), 'Admin.Orderscustomers.Feature')), 'dni' => array('label' => $this->trans('Identification number', array(), 'Admin.Orderscustomers.Feature')), ); self::$default_values = array( 'alias' => 'Alias', 'postcode' => 'X', ); break; case $this->entities[$this->trans('Brands', array(), 'Admin.Global')]: case $this->entities[$this->trans('Suppliers', array(), 'Admin.Global')]: //Overwrite validators AS name is not MultiLangField self::$validators = array( 'description' => array('AdminImportController', 'createMultiLangField'), 'short_description' => array('AdminImportController', 'createMultiLangField'), 'meta_title' => array('AdminImportController', 'createMultiLangField'), 'meta_keywords' => array('AdminImportController', 'createMultiLangField'), 'meta_description' => array('AdminImportController', 'createMultiLangField'), ); $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id' => array('label' => $this->trans('ID', array(), 'Admin.Global')), 'active' => array('label' => $this->trans('Active (0/1)', array(), 'Admin.Advparameters.Feature')), 'name' => array('label' => $this->trans('Name', array(), 'Admin.Global')), 'description' => array('label' => $this->trans('Description', array(), 'Admin.Global')), 'short_description' => array('label' => $this->trans('Short description', array(), 'Admin.Catalog.Feature')), 'meta_title' => array('label' => $this->trans('Meta title', array(), 'Admin.Global')), 'meta_keywords' => array('label' => $this->trans('Meta keywords', array(), 'Admin.Global')), 'meta_description' => array('label' => $this->trans('Meta description', array(), 'Admin.Global')), 'image' => array('label' => $this->trans('Image URL', array(), 'Admin.Advparameters.Feature')), 'shop' => array( 'label' => $this->trans('ID / Name of group shop', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Ignore this field if you don\'t use the Multistore tool. If you leave this field empty, the default shop will be used.', array(), 'Admin.Advparameters.Help'), ), ); self::$default_values = array( 'shop' => Shop::getGroupFromShop(Configuration::get('PS_SHOP_DEFAULT')), ); break; case $this->entities[$this->trans('Alias', array(), 'Admin.Shopparameters.Feature')]: //Overwrite required_fields $this->required_fields = array( 'alias', 'search', ); $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id' => array('label' => $this->trans('ID', array(), 'Admin.Global')), 'alias' => array('label' => $this->trans('Alias', array(), 'Admin.Shopparameters.Feature') . '*'), 'search' => array('label' => $this->trans('Search', array(), 'Admin.Shopparameters.Feature') . '*'), 'active' => array('label' => $this->trans('Active', array(), 'Admin.Global')), ); self::$default_values = array( 'active' => '1', ); break; case $this->entities[$this->trans('Store contacts', array(), 'Admin.Advparameters.Feature')]: self::$validators['hours'] = array('AdminImportController', 'split'); self::$validators['address1'] = array('AdminImportController', 'createMultiLangField'); self::$validators['address2'] = array('AdminImportController', 'createMultiLangField'); $this->required_fields = array( 'address1', 'city', 'country', 'latitude', 'longitude', ); $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id' => array('label' => $this->trans('ID', array(), 'Admin.Global')), 'active' => array('label' => $this->trans('Active (0/1)', array(), 'Admin.Advparameters.Feature')), 'name' => array('label' => $this->trans('Name', array(), 'Admin.Global')), 'address1' => array('label' => $this->trans('Address', array(), 'Admin.Global') . '*'), 'address2' => array('label' => $this->trans('Address (2)', array(), 'Admin.Advparameters.Feature')), 'postcode' => array('label' => $this->trans('Zip/postal code', array(), 'Admin.Global')), 'state' => array('label' => $this->trans('State', array(), 'Admin.Global')), 'city' => array('label' => $this->trans('City', array(), 'Admin.Global') . '*'), 'country' => array('label' => $this->trans('Country', array(), 'Admin.Global') . '*'), 'latitude' => array('label' => $this->trans('Latitude', array(), 'Admin.Advparameters.Feature') . '*'), 'longitude' => array('label' => $this->trans('Longitude', array(), 'Admin.Advparameters.Feature') . '*'), 'phone' => array('label' => $this->trans('Phone', array(), 'Admin.Global')), 'fax' => array('label' => $this->trans('Fax', array(), 'Admin.Global')), 'email' => array('label' => $this->trans('Email address', array(), 'Admin.Global')), 'note' => array('label' => $this->trans('Note', array(), 'Admin.Advparameters.Feature')), 'hours' => array('label' => $this->trans('Hours (x,y,z...)', array(), 'Admin.Advparameters.Feature')), 'image' => array('label' => $this->trans('Image URL', array(), 'Admin.Advparameters.Feature')), 'shop' => array( 'label' => $this->trans('ID / Name of shop', array(), 'Admin.Advparameters.Feature'), 'help' => $this->trans('Ignore this field if you don\'t use the Multistore tool. If you leave this field empty, the default shop will be used.', array(), 'Admin.Advparameters.Help'), ), ); self::$default_values = array( 'active' => '1', ); break; } // @since 1.5.0 if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT')) { switch ((int) Tools::getValue('entity')) { case $this->entities[$this->trans('Supply Orders', array(), 'Admin.Advparameters.Feature')]: // required fields $this->required_fields = array( 'id_supplier', 'id_warehouse', 'reference', 'date_delivery_expected', ); // available fields $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'id' => array('label' => $this->trans('ID', array(), 'Admin.Global')), 'id_supplier' => array('label' => $this->trans('Supplier ID *', array(), 'Admin.Advparameters.Feature')), 'id_lang' => array('label' => $this->trans('Lang ID', array(), 'Admin.Advparameters.Feature')), 'id_warehouse' => array('label' => $this->trans('Warehouse ID *', array(), 'Admin.Advparameters.Feature')), 'id_currency' => array('label' => $this->trans('Currency ID *', array(), 'Admin.Advparameters.Feature')), 'reference' => array('label' => $this->trans('Supply Order Reference *', array(), 'Admin.Advparameters.Feature')), 'date_delivery_expected' => array('label' => $this->trans('Delivery Date (Y-M-D)*', array(), 'Admin.Advparameters.Feature')), 'discount_rate' => array('label' => $this->trans('Discount rate', array(), 'Admin.Advparameters.Feature')), 'is_template' => array('label' => $this->trans('Template', array(), 'Admin.Advparameters.Feature')), ); // default values self::$default_values = array( 'id_lang' => (int) Configuration::get('PS_LANG_DEFAULT'), 'id_currency' => Currency::getDefaultCurrency()->id, 'discount_rate' => '0', 'is_template' => '0', ); break; case $this->entities[$this->trans('Supply Order Details', array(), 'Admin.Advparameters.Feature')]: // required fields $this->required_fields = array( 'supply_order_reference', 'id_product', 'unit_price_te', 'quantity_expected', ); // available fields $this->available_fields = array( 'no' => array('label' => $this->trans('Ignore this column', array(), 'Admin.Advparameters.Feature')), 'supply_order_reference' => array('label' => $this->trans('Supply Order Reference *', array(), 'Admin.Advparameters.Feature')), 'id_product' => array('label' => $this->trans('Product ID *', array(), 'Admin.Advparameters.Feature')), 'id_product_attribute' => array('label' => $this->trans('Product Attribute ID', array(), 'Admin.Advparameters.Feature')), 'unit_price_te' => array('label' => $this->trans('Unit Price (tax excl.)*', array(), 'Admin.Advparameters.Feature')), 'quantity_expected' => array('label' => $this->trans('Quantity Expected *', array(), 'Admin.Advparameters.Feature')), 'discount_rate' => array('label' => $this->trans('Discount Rate', array(), 'Admin.Advparameters.Feature')), 'tax_rate' => array('label' => $this->trans('Tax Rate', array(), 'Admin.Advparameters.Feature')), ); // default values self::$default_values = array( 'discount_rate' => '0', 'tax_rate' => '0', ); break; } } $this->separator = ($separator = Tools::substr((string) (trim(Tools::getValue('separator'))), 0, 1)) ? $separator : ';'; $this->convert = false; $this->multiple_value_separator = ($separator = Tools::substr((string) (trim(Tools::getValue('multiple_value_separator'))), 0, 1)) ? $separator : ','; } public function setMedia($isNewTheme = false) { $bo_theme = ((Validate::isLoadedObject($this->context->employee) && $this->context->employee->bo_theme) ? $this->context->employee->bo_theme : 'default'); if (!file_exists(_PS_BO_ALL_THEMES_DIR_ . $bo_theme . DIRECTORY_SEPARATOR . 'template')) { $bo_theme = 'default'; } // We need to set parent media first, so that jQuery is loaded before the dependant plugins parent::setMedia($isNewTheme); $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.iframe-transport.js'); $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload.js'); $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload-process.js'); $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload-validate.js'); $this->addJs(__PS_BASE_URI__ . 'js/vendor/spin.js'); $this->addJs(__PS_BASE_URI__ . 'js/vendor/ladda.js'); } public function renderForm() { // If import was executed - collect errors or success message // and send them to the migrated controller. if ($this->importExecuted) { $session = $this->getSession(); if ($this->errors) { foreach ($this->errors as $error) { $session->getFlashBag()->add('error', $error); } } else { foreach ($this->warnings as $warning) { $session->getFlashBag()->add('warning', $warning); } $session->getFlashBag()->add( 'success', $this->trans( 'Your file has been successfully imported into your shop. Don\'t forget to re-build the products\' search index.', [], 'Admin.Advparameters.Notification' ) ); } } $request = $this->getSymfonyRequest(); if ($request && $request->isMethod(\Symfony\Component\HttpFoundation\Request::METHOD_GET)) { // Import form is reworked in Symfony. // If user tries to access legacy form directly, // we redirect him to new form. $symfonyImportForm = $this->context->link->getAdminLink('AdminImport'); Tools::redirectAdmin($symfonyImportForm); } if (!is_dir(AdminImportController::getPath())) { return !($this->errors[] = $this->trans('The import directory doesn\'t exist. Please check your file path.', array(), 'Admin.Advparameters.Notification')); } if (!is_writable(AdminImportController::getPath())) { $this->displayWarning($this->trans('The import directory must be writable (CHMOD 755 / 777).', array(), 'Admin.Advparameters.Notification')); } $files_to_import = scandir(AdminImportController::getPath(), SCANDIR_SORT_NONE); uasort($files_to_import, array('AdminImportController', 'usortFiles')); foreach ($files_to_import as $k => &$filename) { //exclude . .. .svn and index.php and all hidden files if (preg_match('/^\..*|index\.php/i', $filename) || is_dir(AdminImportController::getPath() . $filename)) { unset($files_to_import[$k]); } } unset($filename); $this->fields_form = array(''); $this->toolbar_scroll = false; $this->toolbar_btn = array(); // adds fancybox $this->addJqueryPlugin(array('fancybox')); $entity_selected = 0; if (isset($this->entities[$this->l(Tools::ucfirst(Tools::getValue('import_type')))])) { $entity_selected = $this->entities[$this->l(Tools::ucfirst(Tools::getValue('import_type')))]; $this->context->cookie->entity_selected = (int) $entity_selected; } elseif (isset($this->context->cookie->entity_selected)) { $entity_selected = (int) $this->context->cookie->entity_selected; } $csv_selected = ''; if (isset($this->context->cookie->csv_selected) && @filemtime(AdminImportController::getPath( urldecode($this->context->cookie->csv_selected) ))) { $csv_selected = urldecode($this->context->cookie->csv_selected); } else { $this->context->cookie->csv_selected = $csv_selected; } $id_lang_selected = ''; if (isset($this->context->cookie->iso_lang_selected) && $this->context->cookie->iso_lang_selected) { $id_lang_selected = (int) Language::getIdByIso(urldecode($this->context->cookie->iso_lang_selected)); } $separator_selected = $this->separator; if (isset($this->context->cookie->separator_selected) && $this->context->cookie->separator_selected) { $separator_selected = urldecode($this->context->cookie->separator_selected); } $multiple_value_separator_selected = $this->multiple_value_separator; if (isset($this->context->cookie->multiple_value_separator_selected) && $this->context->cookie->multiple_value_separator_selected) { $multiple_value_separator_selected = urldecode($this->context->cookie->multiple_value_separator_selected); } //get post max size $post_max_size = ini_get('post_max_size'); $bytes = (int) trim($post_max_size); $last = strtolower($post_max_size[strlen($post_max_size) - 1]); switch ($last) { case 'g': $bytes *= 1024; // no break to fall-through case 'm': $bytes *= 1024; // no break to fall-through case 'k': $bytes *= 1024; } if (!isset($bytes) || $bytes == '') { $bytes = 20971520; } // 20Mb $this->tpl_form_vars = array( 'post_max_size' => (int) $bytes, 'module_confirmation' => Tools::isSubmit('import') && (isset($this->warnings) && !count($this->warnings)), 'path_import' => AdminImportController::getPath(), 'entities' => $this->entities, 'entity_selected' => $entity_selected, 'csv_selected' => $csv_selected, 'separator_selected' => $separator_selected, 'multiple_value_separator_selected' => $multiple_value_separator_selected, 'files_to_import' => $files_to_import, 'languages' => Language::getLanguages(false), 'id_language' => ($id_lang_selected) ? $id_lang_selected : $this->context->language->id, 'available_fields' => $this->getAvailableFields(), 'truncateAuthorized' => (Shop::isFeatureActive() && $this->context->employee->isSuperAdmin()) || !Shop::isFeatureActive(), 'PS_ADVANCED_STOCK_MANAGEMENT' => Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT'), ); return parent::renderForm(); } public function ajaxProcessuploadCsv() { $filename_prefix = date('YmdHis') . '-'; if (isset($_FILES['file']) && !empty($_FILES['file']['error'])) { switch ($_FILES['file']['error']) { case UPLOAD_ERR_INI_SIZE: $_FILES['file']['error'] = $this->trans('The uploaded file exceeds the upload_max_filesize directive in php.ini. If your server configuration allows it, you may add a directive in your .htaccess.', array(), 'Admin.Advparameters.Notification'); break; case UPLOAD_ERR_FORM_SIZE: $_FILES['file']['error'] = $this->trans('The uploaded file exceeds the post_max_size directive in php.ini. If your server configuration allows it, you may add a directive in your .htaccess, for example:', array(), 'Admin.Advparameters.Notification') . '
php_value post_max_size 20M ' . $this->trans('(click to open "Generators" page)', array(), 'Admin.Advparameters.Notification') . ''; break; break; case UPLOAD_ERR_PARTIAL: $_FILES['file']['error'] = $this->trans('The uploaded file was only partially uploaded.', array(), 'Admin.Advparameters.Notification'); break; break; case UPLOAD_ERR_NO_FILE: $_FILES['file']['error'] = $this->trans('No file was uploaded.', array(), 'Admin.Advparameters.Notification'); break; break; } } elseif (!preg_match('#([^\.]*?)\.(csv|xls[xt]?|o[dt]s)$#is', $_FILES['file']['name'])) { $_FILES['file']['error'] = $this->trans('The extension of your file should be .csv.', array(), 'Admin.Advparameters.Notification'); } elseif (!@filemtime($_FILES['file']['tmp_name']) || !@move_uploaded_file($_FILES['file']['tmp_name'], AdminImportController::getPath() . $filename_prefix . str_replace("\0", '', $_FILES['file']['name']))) { $_FILES['file']['error'] = $this->trans('An error occurred while uploading / copying the file.', array(), 'Admin.Advparameters.Notification'); } else { @chmod(AdminImportController::getPath() . $filename_prefix . $_FILES['file']['name'], 0664); $_FILES['file']['filename'] = $filename_prefix . str_replace('\0', '', $_FILES['file']['name']); } die(json_encode($_FILES)); } public function renderView() { $this->addJS(_PS_JS_DIR_ . 'admin/import.js'); $handle = $this->openCsvFile(); $nb_column = $this->getNbrColumn($handle, $this->separator); $nb_table = ceil($nb_column / MAX_COLUMNS); $res = array(); foreach ($this->required_fields as $elem) { $res[] = '\'' . $elem . '\''; } $data = array(); for ($i = 0; $i < $nb_table; ++$i) { $data[$i] = $this->generateContentTable($i, $nb_column, $handle, $this->separator); } $this->context->cookie->entity_selected = (int) Tools::getValue('entity'); $this->context->cookie->iso_lang_selected = urlencode(Tools::getValue('iso_lang')); $this->context->cookie->separator_selected = urlencode($this->separator); $this->context->cookie->multiple_value_separator_selected = urlencode($this->multiple_value_separator); $this->context->cookie->csv_selected = urlencode(Tools::getValue('csv')); $this->tpl_view_vars = array( 'import_matchs' => Db::getInstance()->executeS('SELECT * FROM ' . _DB_PREFIX_ . 'import_match', true, false), 'fields_value' => array( 'csv' => Tools::getValue('csv'), 'entity' => (int) Tools::getValue('entity'), 'iso_lang' => Tools::getValue('iso_lang'), 'truncate' => Tools::getValue('truncate'), 'forceIDs' => Tools::getValue('forceIDs'), 'regenerate' => Tools::getValue('regenerate'), 'sendemail' => Tools::getValue('sendemail'), 'match_ref' => Tools::getValue('match_ref'), 'separator' => $this->separator, 'multiple_value_separator' => $this->multiple_value_separator, ), 'nb_table' => $nb_table, 'nb_column' => $nb_column, 'res' => implode(',', $res), 'max_columns' => MAX_COLUMNS, 'no_pre_select' => array('price_tin', 'feature'), 'available_fields' => $this->available_fields, 'data' => $data, ); return parent::renderView(); } public function initToolbar() { switch ($this->display) { case 'import': // Default cancel button - like old back link $back = Tools::safeOutput(Tools::getValue('back', '')); if (empty($back)) { $back = self::$currentIndex . '&token=' . $this->token; } $this->toolbar_btn['cancel'] = array( 'href' => $back, 'desc' => $this->trans('Cancel', array(), 'Admin.Actions'), ); // Default save button - action dynamically handled in javascript $this->toolbar_btn['save-import'] = array( 'href' => '#', 'desc' => $this->trans('Import .CSV data', array(), 'Admin.Advparameters.Feature'), ); break; } } protected function generateContentTable($current_table, $nb_column, $handle, $glue) { $html = ''; // Header for ($i = 0; $i < $nb_column; ++$i) { if (MAX_COLUMNS * (int) $current_table <= $i && (int) $i < MAX_COLUMNS * ((int) $current_table + 1)) { $html .= ''; } } $html .= ''; AdminImportController::setLocale(); for ($current_line = 0; $current_line < 10 && $line = fgetcsv($handle, MAX_LINE_SIZE, $glue); ++$current_line) { /* UTF-8 conversion */ if ($this->convert) { $line = $this->utf8EncodeArray($line); } $html .= ''; foreach ($line as $nb_c => $column) { if ((MAX_COLUMNS * (int) $current_table <= $nb_c) && ((int) $nb_c < MAX_COLUMNS * ((int) $current_table + 1))) { $html .= ''; } } $html .= ''; } $html .= ''; AdminImportController::rewindBomAware($handle); return $html; } public function init() { parent::init(); if (Tools::isSubmit('submitImportFile')) { $this->display = 'import'; } } public function initContent() { if ($this->display == 'import') { if (Tools::getValue('csv')) { $this->content .= $this->renderView(); } else { $this->errors[] = $this->trans('To proceed, please upload a file first.', array(), 'Admin.Advparameters.Notification'); $this->content .= $this->renderForm(); } } else { $this->content .= $this->renderForm(); } $this->context->smarty->assign(array( 'content' => $this->content, )); } protected static function rewindBomAware($handle) { // A rewind wrapper that skips BOM signature wrongly if (!is_resource($handle)) { return false; } rewind($handle); if (($bom = fread($handle, 3)) != "\xEF\xBB\xBF") { rewind($handle); } } protected static function getBoolean($field) { return (bool) $field; } protected static function getPrice($field) { $field = ((float) str_replace(',', '.', $field)); $field = ((float) str_replace('%', '', $field)); return $field; } protected static function split($field) { if (empty($field)) { return array(); } $separator = Tools::getValue('multiple_value_separator'); if (null === $separator || trim($separator) == '') { $separator = ','; } $tab = ''; $uniqid_path = false; // try data:// protocole. If failed, old school file on filesystem. if (($fd = @fopen('data://text/plain;base64,' . base64_encode($field), 'rb')) === false) { do { $uniqid_path = _PS_UPLOAD_DIR_ . uniqid(); } while (file_exists($uniqid_path)); file_put_contents($uniqid_path, $field); $fd = fopen($uniqid_path, 'rb'); } if ($fd === false) { return array(); } $tab = fgetcsv($fd, MAX_LINE_SIZE, $separator); fclose($fd); if ($uniqid_path !== false && file_exists($uniqid_path)) { @unlink($uniqid_path); } if (empty($tab) || (!is_array($tab))) { return array(); } return $tab; } protected static function createMultiLangField($field) { $res = array(); foreach (Language::getIDs(false) as $id_lang) { $res[$id_lang] = $field; } return $res; } protected function getTypeValuesOptions($nb_c) { $i = 0; $no_pre_select = array('price_tin', 'feature'); $options = ''; foreach ($this->available_fields as $k => $field) { $options .= '