PageRenderTime 36ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/controllers/admin/AdminProductsController.php

https://github.com/netplayer/PrestaShop
PHP | 4756 lines | 3910 code | 545 blank | 301 comment | 831 complexity | 89cdea2af3e401ff2a074e05f8a33493 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * 2007-2014 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2014 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. class AdminProductsControllerCore extends AdminController
  27. {
  28. /** @var integer Max image size for upload
  29. * As of 1.5 it is recommended to not set a limit to max image size
  30. */
  31. protected $max_file_size = null;
  32. protected $max_image_size = null;
  33. protected $_category;
  34. /**
  35. * @var string name of the tab to display
  36. */
  37. protected $tab_display;
  38. protected $tab_display_module;
  39. /**
  40. * The order in the array decides the order in the list of tab. If an element's value is a number, it will be preloaded.
  41. * The tabs are preloaded from the smallest to the highest number.
  42. * @var array Product tabs.
  43. */
  44. protected $available_tabs = array();
  45. protected $default_tab = 'Informations';
  46. protected $available_tabs_lang = array();
  47. protected $position_identifier = 'id_product';
  48. protected $submitted_tabs;
  49. protected $id_current_category;
  50. public function __construct()
  51. {
  52. $this->bootstrap = true;
  53. $this->table = 'product';
  54. $this->className = 'Product';
  55. $this->lang = true;
  56. $this->explicitSelect = true;
  57. $this->bulk_actions = array(
  58. 'delete' => array(
  59. 'text' => $this->l('Delete selected'),
  60. 'icon' => 'icon-trash',
  61. 'confirm' => $this->l('Delete selected items?')
  62. )
  63. );
  64. if (!Tools::getValue('id_product'))
  65. $this->multishop_context_group = false;
  66. parent::__construct();
  67. $this->imageType = 'jpg';
  68. $this->_defaultOrderBy = 'position';
  69. $this->max_file_size = (int)(Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE') * 1000000);
  70. $this->max_image_size = (int)Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE');
  71. $this->allow_export = true;
  72. // @since 1.5 : translations for tabs
  73. $this->available_tabs_lang = array(
  74. 'Informations' => $this->l('Information'),
  75. 'Pack' => $this->l('Pack'),
  76. 'VirtualProduct' => $this->l('Virtual Product'),
  77. 'Prices' => $this->l('Prices'),
  78. 'Seo' => $this->l('SEO'),
  79. 'Images' => $this->l('Images'),
  80. 'Associations' => $this->l('Associations'),
  81. 'Shipping' => $this->l('Shipping'),
  82. 'Combinations' => $this->l('Combinations'),
  83. 'Features' => $this->l('Features'),
  84. 'Customization' => $this->l('Customization'),
  85. 'Attachments' => $this->l('Attachments'),
  86. 'Quantities' => $this->l('Quantities'),
  87. 'Suppliers' => $this->l('Suppliers'),
  88. 'Warehouses' => $this->l('Warehouses'),
  89. );
  90. $this->available_tabs = array('Quantities' => 6, 'Warehouses' => 14);
  91. if ($this->context->shop->getContext() != Shop::CONTEXT_GROUP)
  92. $this->available_tabs = array_merge($this->available_tabs, array(
  93. 'Informations' => 0,
  94. 'Pack' => 7,
  95. 'VirtualProduct' => 8,
  96. 'Prices' => 1,
  97. 'Seo' => 2,
  98. 'Associations' => 3,
  99. 'Images' => 9,
  100. 'Shipping' => 4,
  101. 'Combinations' => 5,
  102. 'Features' => 10,
  103. 'Customization' => 11,
  104. 'Attachments' => 12,
  105. 'Suppliers' => 13,
  106. ));
  107. // Sort the tabs that need to be preloaded by their priority number
  108. asort($this->available_tabs, SORT_NUMERIC);
  109. /* Adding tab if modules are hooked */
  110. $modules_list = Hook::getHookModuleExecList('displayAdminProductsExtra');
  111. if (is_array($modules_list) && count($modules_list) > 0)
  112. foreach ($modules_list as $m)
  113. {
  114. $this->available_tabs['Module'.ucfirst($m['module'])] = 23;
  115. $this->available_tabs_lang['Module'.ucfirst($m['module'])] = Module::getModuleName($m['module']);
  116. }
  117. if (Tools::getValue('reset_filter_category'))
  118. $this->context->cookie->id_category_products_filter = false;
  119. if (Shop::isFeatureActive() && $this->context->cookie->id_category_products_filter)
  120. {
  121. $category = new Category((int)$this->context->cookie->id_category_products_filter);
  122. if (!$category->inShop())
  123. {
  124. $this->context->cookie->id_category_products_filter = false;
  125. Tools::redirectAdmin($this->context->link->getAdminLink('AdminProducts'));
  126. }
  127. }
  128. /* Join categories table */
  129. if ($id_category = (int)Tools::getValue('productFilter_cl!name'))
  130. {
  131. $this->_category = new Category((int)$id_category);
  132. $_POST['productFilter_cl!name'] = $this->_category->name[$this->context->language->id];
  133. }
  134. else
  135. {
  136. if ($id_category = (int)Tools::getValue('id_category'))
  137. {
  138. $this->id_current_category = $id_category;
  139. $this->context->cookie->id_category_products_filter = $id_category;
  140. }
  141. elseif ($id_category = $this->context->cookie->id_category_products_filter)
  142. $this->id_current_category = $id_category;
  143. if ($this->id_current_category)
  144. $this->_category = new Category((int)$this->id_current_category);
  145. else
  146. $this->_category = new Category();
  147. }
  148. $join_category = false;
  149. if (Validate::isLoadedObject($this->_category) && empty($this->_filter))
  150. $join_category = true;
  151. $this->_join .= '
  152. LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = a.`id_product`)
  153. LEFT JOIN `'._DB_PREFIX_.'stock_available` sav ON (sav.`id_product` = a.`id_product` AND sav.`id_product_attribute` = 0
  154. '.StockAvailable::addSqlShopRestriction(null, null, 'sav').') ';
  155. $alias = 'sa';
  156. $alias_image = 'image_shop';
  157. $id_shop = Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP? (int)$this->context->shop->id : 'a.id_shop_default';
  158. $this->_join .= ' JOIN `'._DB_PREFIX_.'product_shop` sa ON (a.`id_product` = sa.`id_product` AND sa.id_shop = '.$id_shop.')
  159. LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON ('.$alias.'.`id_category_default` = cl.`id_category` AND b.`id_lang` = cl.`id_lang` AND cl.id_shop = '.$id_shop.')
  160. LEFT JOIN `'._DB_PREFIX_.'shop` shop ON (shop.id_shop = '.$id_shop.')
  161. LEFT JOIN `'._DB_PREFIX_.'image_shop` image_shop ON (image_shop.`id_image` = i.`id_image` AND image_shop.`cover` = 1 AND image_shop.id_shop = '.$id_shop.')
  162. LEFT JOIN `'._DB_PREFIX_.'product_download` pd ON (pd.`id_product` = a.`id_product`)';
  163. $this->_select .= 'shop.name as shopname, a.id_shop_default, ';
  164. $this->_select .= 'MAX('.$alias_image.'.id_image) id_image, cl.name `name_category`, '.$alias.'.`price`, 0 AS price_final, a.`is_virtual`, pd.`nb_downloadable`, sav.`quantity` as sav_quantity, '.$alias.'.`active`, IF(sav.`quantity`<=0, 1, 0) badge_danger';
  165. if ($join_category)
  166. {
  167. $this->_join .= ' INNER JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_product` = a.`id_product` AND cp.`id_category` = '.(int)$this->_category->id.') ';
  168. $this->_select .= ' , cp.`position`, ';
  169. }
  170. $this->_group = 'GROUP BY '.$alias.'.id_product';
  171. $this->fields_list = array();
  172. $this->fields_list['id_product'] = array(
  173. 'title' => $this->l('ID'),
  174. 'align' => 'center',
  175. 'class' => 'fixed-width-xs',
  176. 'type' => 'int'
  177. );
  178. $this->fields_list['image'] = array(
  179. 'title' => $this->l('Photo'),
  180. 'align' => 'center',
  181. 'image' => 'p',
  182. 'orderby' => false,
  183. 'filter' => false,
  184. 'search' => false
  185. );
  186. $this->fields_list['name'] = array(
  187. 'title' => $this->l('Name'),
  188. 'filter_key' => 'b!name'
  189. );
  190. $this->fields_list['reference'] = array(
  191. 'title' => $this->l('Reference'),
  192. 'align' => 'left',
  193. );
  194. if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP)
  195. $this->fields_list['shopname'] = array(
  196. 'title' => $this->l('Default shop'),
  197. 'filter_key' => 'shop!name',
  198. );
  199. else
  200. $this->fields_list['name_category'] = array(
  201. 'title' => $this->l('Category'),
  202. 'filter_key' => 'cl!name',
  203. );
  204. $this->fields_list['price'] = array(
  205. 'title' => $this->l('Base price'),
  206. 'type' => 'price',
  207. 'align' => 'text-right',
  208. 'filter_key' => 'a!price'
  209. );
  210. $this->fields_list['price_final'] = array(
  211. 'title' => $this->l('Final price'),
  212. 'type' => 'price',
  213. 'align' => 'text-right',
  214. 'havingFilter' => true,
  215. 'orderby' => false,
  216. 'search' => false
  217. );
  218. if (Configuration::get('PS_STOCK_MANAGEMENT'))
  219. $this->fields_list['sav_quantity'] = array(
  220. 'title' => $this->l('Quantity'),
  221. 'type' => 'int',
  222. 'align' => 'text-right',
  223. 'filter_key' => 'sav!quantity',
  224. 'orderby' => true,
  225. 'badge_danger' => true,
  226. //'hint' => $this->l('This is the quantity available in the current shop/group.'),
  227. );
  228. $this->fields_list['active'] = array(
  229. 'title' => $this->l('Status'),
  230. 'active' => 'status',
  231. 'filter_key' => $alias.'!active',
  232. 'align' => 'text-center',
  233. 'type' => 'bool',
  234. 'class' => 'fixed-width-sm',
  235. 'orderby' => false
  236. );
  237. if ($join_category && (int)$this->id_current_category)
  238. $this->fields_list['position'] = array(
  239. 'title' => $this->l('Position'),
  240. 'filter_key' => 'cp!position',
  241. 'align' => 'center',
  242. 'position' => 'position'
  243. );
  244. }
  245. public static function getQuantities($echo, $tr)
  246. {
  247. if ((int)$tr['is_virtual'] == 1 && $tr['nb_downloadable'] == 0)
  248. return '&infin;';
  249. else
  250. return $echo;
  251. }
  252. public function setMedia()
  253. {
  254. parent::setMedia();
  255. $bo_theme = ((Validate::isLoadedObject($this->context->employee)
  256. && $this->context->employee->bo_theme) ? $this->context->employee->bo_theme : 'default');
  257. if (!file_exists(_PS_BO_ALL_THEMES_DIR_.$bo_theme.DIRECTORY_SEPARATOR
  258. .'template'))
  259. $bo_theme = 'default';
  260. $this->addJs(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$bo_theme.'/js/jquery.iframe-transport.js');
  261. $this->addJs(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$bo_theme.'/js/jquery.fileupload.js');
  262. $this->addJs(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$bo_theme.'/js/jquery.fileupload-process.js');
  263. $this->addJs(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$bo_theme.'/js/jquery.fileupload-validate.js');
  264. $this->addJs(__PS_BASE_URI__.'js/vendor/spin.js');
  265. $this->addJs(__PS_BASE_URI__.'js/vendor/ladda.js');
  266. }
  267. protected function _cleanMetaKeywords($keywords)
  268. {
  269. if (!empty($keywords) && $keywords != '')
  270. {
  271. $out = array();
  272. $words = explode(',', $keywords);
  273. foreach ($words as $word_item)
  274. {
  275. $word_item = trim($word_item);
  276. if (!empty($word_item) && $word_item != '')
  277. $out[] = $word_item;
  278. }
  279. return ((count($out) > 0) ? implode(',', $out) : '');
  280. }
  281. else
  282. return '';
  283. }
  284. protected function copyFromPost(&$object, $table)
  285. {
  286. parent::copyFromPost($object, $table);
  287. if (get_class($object) != 'Product')
  288. return;
  289. /* Additional fields */
  290. $languages = Language::getLanguages(false);
  291. foreach ($languages as $language)
  292. if (isset($_POST['meta_keywords_'.$language['id_lang']]))
  293. {
  294. $_POST['meta_keywords_'.$language['id_lang']] = $this->_cleanMetaKeywords(Tools::strtolower($_POST['meta_keywords_'.$language['id_lang']]));
  295. // preg_replace('/ *,? +,* /', ',', strtolower($_POST['meta_keywords_'.$language['id_lang']]));
  296. $object->meta_keywords[$language['id_lang']] = $_POST['meta_keywords_'.$language['id_lang']];
  297. }
  298. $_POST['width'] = empty($_POST['width']) ? '0' : str_replace(',', '.', $_POST['width']);
  299. $_POST['height'] = empty($_POST['height']) ? '0' : str_replace(',', '.', $_POST['height']);
  300. $_POST['depth'] = empty($_POST['depth']) ? '0' : str_replace(',', '.', $_POST['depth']);
  301. $_POST['weight'] = empty($_POST['weight']) ? '0' : str_replace(',', '.', $_POST['weight']);
  302. if (Tools::getIsset('unit_price') != null)
  303. $object->unit_price = str_replace(',', '.', Tools::getValue('unit_price'));
  304. if (Tools::getIsset('ecotax') != null)
  305. $object->ecotax = str_replace(',', '.', Tools::getValue('ecotax'));
  306. $object->available_for_order = (int)Tools::getValue('available_for_order');
  307. $object->show_price = $object->available_for_order ? 1 : (int)Tools::getValue('show_price');
  308. $object->on_sale = (int)Tools::getValue('on_sale');
  309. $object->online_only = (int)Tools::getValue('online_only');
  310. }
  311. public function getList($id_lang, $orderBy = null, $orderWay = null, $start = 0, $limit = null, $id_lang_shop = null)
  312. {
  313. $orderByPriceFinal = (empty($orderBy) ? ($this->context->cookie->__get($this->table.'Orderby') ? $this->context->cookie->__get($this->table.'Orderby') : 'id_'.$this->table) : $orderBy);
  314. $orderWayPriceFinal = (empty($orderWay) ? ($this->context->cookie->__get($this->table.'Orderway') ? $this->context->cookie->__get($this->table.'Orderby') : 'ASC') : $orderWay);
  315. if ($orderByPriceFinal == 'price_final')
  316. {
  317. $orderBy = 'id_'.$this->table;
  318. $orderWay = 'ASC';
  319. }
  320. parent::getList($id_lang, $orderBy, $orderWay, $start, $limit, $this->context->shop->id);
  321. /* update product quantity with attributes ...*/
  322. $nb = count($this->_list);
  323. if ($this->_list)
  324. {
  325. $context = $this->context->cloneContext();
  326. $context->shop = clone($context->shop);
  327. /* update product final price */
  328. for ($i = 0; $i < $nb; $i++)
  329. {
  330. if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP)
  331. $context->shop = new Shop((int)$this->_list[$i]['id_shop_default']);
  332. // convert price with the currency from context
  333. $this->_list[$i]['price'] = Tools::convertPrice($this->_list[$i]['price'], $this->context->currency, true, $this->context);
  334. $this->_list[$i]['price_tmp'] = Product::getPriceStatic($this->_list[$i]['id_product'], true, null, 2, null, false, true, 1, true, null, null, null, $nothing, true, true, $context);
  335. }
  336. }
  337. if ($orderByPriceFinal == 'price_final')
  338. {
  339. if (strtolower($orderWayPriceFinal) == 'desc')
  340. uasort($this->_list, 'cmpPriceDesc');
  341. else
  342. uasort($this->_list, 'cmpPriceAsc');
  343. }
  344. for ($i = 0; $this->_list && $i < $nb; $i++)
  345. {
  346. $this->_list[$i]['price_final'] = $this->_list[$i]['price_tmp'];
  347. unset($this->_list[$i]['price_tmp']);
  348. }
  349. }
  350. protected function loadObject($opt = false)
  351. {
  352. $result = parent::loadObject($opt);
  353. if ($result && Validate::isLoadedObject($this->object))
  354. {
  355. if (Shop::getContext() == Shop::CONTEXT_SHOP && Shop::isFeatureActive() && !$this->object->isAssociatedToShop())
  356. {
  357. $default_product = new Product((int)$this->object->id, false, null, (int)$this->object->id_shop_default);
  358. $def = ObjectModel::getDefinition($this->object);
  359. foreach ($def['fields'] as $field_name => $row)
  360. {
  361. if (is_array($default_product->$field_name))
  362. foreach ($default_product->$field_name as $key => $value)
  363. $this->object->{$field_name}[$key] = $value;
  364. else
  365. $this->object->$field_name = $default_product->$field_name;
  366. }
  367. }
  368. $this->object->loadStockData();
  369. }
  370. return $result;
  371. }
  372. public function ajaxProcessGetCountriesOptions()
  373. {
  374. if (!$res = Country::getCountriesByIdShop((int)Tools::getValue('id_shop'), (int)$this->context->language->id))
  375. return ;
  376. $tpl = $this->createTemplate('specific_prices_shop_update.tpl');
  377. $tpl->assign(array(
  378. 'option_list' => $res,
  379. 'key_id' => 'id_country',
  380. 'key_value' => 'name'
  381. )
  382. );
  383. $this->content = $tpl->fetch();
  384. }
  385. public function ajaxProcessGetCurrenciesOptions()
  386. {
  387. if (!$res = Currency::getCurrenciesByIdShop((int)Tools::getValue('id_shop')))
  388. return ;
  389. $tpl = $this->createTemplate('specific_prices_shop_update.tpl');
  390. $tpl->assign(array(
  391. 'option_list' => $res,
  392. 'key_id' => 'id_currency',
  393. 'key_value' => 'name'
  394. )
  395. );
  396. $this->content = $tpl->fetch();
  397. }
  398. public function ajaxProcessGetGroupsOptions()
  399. {
  400. if (!$res = Group::getGroups((int)$this->context->language->id, (int)Tools::getValue('id_shop')))
  401. return ;
  402. $tpl = $this->createTemplate('specific_prices_shop_update.tpl');
  403. $tpl->assign(array(
  404. 'option_list' => $res,
  405. 'key_id' => 'id_group',
  406. 'key_value' => 'name'
  407. )
  408. );
  409. $this->content = $tpl->fetch();
  410. }
  411. public function processDeleteVirtualProduct()
  412. {
  413. if (!($id_product_download = ProductDownload::getIdFromIdProduct((int)Tools::getValue('id_product'))))
  414. $this->errors[] = Tools::displayError('Cannot retrieve file');
  415. else
  416. {
  417. $product_download = new ProductDownload((int)$id_product_download);
  418. if (!$product_download->deleteFile((int)$id_product_download))
  419. $this->errors[] = Tools::displayError('Cannot delete file');
  420. else
  421. $this->redirect_after = self::$currentIndex.'&id_product='.(int)Tools::getValue('id_product').'&updateproduct&key_tab=VirtualProduct&conf=1&token='.$this->token;
  422. }
  423. $this->display = 'edit';
  424. $this->tab_display = 'VirtualProduct';
  425. }
  426. public function ajaxProcessAddAttachment()
  427. {
  428. if (isset($_FILES['attachment_file']))
  429. {
  430. if ((int)$_FILES['attachment_file']['error'] === 1)
  431. {
  432. $_FILES['attachment_file']['error'] = array();
  433. $max_upload = (int)ini_get('upload_max_filesize');
  434. $max_post = (int)ini_get('post_max_size');
  435. $upload_mb = min($max_upload, $max_post);
  436. $_FILES['attachment_file']['error'][] = sprintf(
  437. $this->l('File %1$s exceeds the size allowed by the server. The limit is set to %2$d MB.'),
  438. '<b>'.$_FILES['attachment_file']['name'].'</b> ',
  439. '<b>'.$upload_mb.'</b>'
  440. );
  441. }
  442. $_FILES['attachment_file']['error'] = array();
  443. $is_attachment_name_valid = false;
  444. $attachment_names = Tools::getValue('attachment_name');
  445. $attachment_descriptions = Tools::getValue('attachment_description');
  446. if (!isset($attachment_names) || !$attachment_names)
  447. $attachment_names = array();
  448. if (!isset($attachment_descriptions) || !$attachment_descriptions)
  449. $attachment_descriptions = array();
  450. foreach ($attachment_names as $lang => $name)
  451. {
  452. $language = Language::getLanguage((int)$lang);
  453. if (Tools::strlen($name) > 0)
  454. $is_attachment_name_valid = true;
  455. if (!Validate::isGenericName($name))
  456. $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('Invalid name for %s language'), $language['name']);
  457. elseif (Tools::strlen($name) > 32)
  458. $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('The name for %1s language is too long (%2d chars max).'), $language['name'], 32);
  459. }
  460. foreach ($attachment_descriptions as $lang => $description)
  461. {
  462. $language = Language::getLanguage((int)$lang);
  463. if (!Validate::isCleanHtml($description))
  464. $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('Invalid description for %s language'), $language['name']);
  465. }
  466. if (!$is_attachment_name_valid)
  467. $_FILES['attachment_file']['error'][] = Tools::displayError('An attachment name is required.');
  468. if (empty($_FILES['attachment_file']['error']))
  469. {
  470. if (is_uploaded_file($_FILES['attachment_file']['tmp_name']))
  471. {
  472. if ($_FILES['attachment_file']['size'] > (Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024 * 1024))
  473. $_FILES['attachment_file']['error'][] = sprintf(
  474. $this->l('The file is too large. Maximum size allowed is: %1$d kB. The file you\'re trying to upload is: %2$d kB.'),
  475. (Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024),
  476. number_format(($_FILES['attachment_file']['size'] / 1024), 2, '.', '')
  477. );
  478. else
  479. {
  480. do $uniqid = sha1(microtime());
  481. while (file_exists(_PS_DOWNLOAD_DIR_.$uniqid));
  482. if (!copy($_FILES['attachment_file']['tmp_name'], _PS_DOWNLOAD_DIR_.$uniqid))
  483. $_FILES['attachment_file']['error'][] = $this->l('File copy failed');
  484. @unlink($_FILES['attachment_file']['tmp_name']);
  485. }
  486. }
  487. else
  488. $_FILES['attachment_file']['error'][] = Tools::displayError('The file is missing.');
  489. if (empty($_FILES['attachment_file']['error']) && isset($uniqid))
  490. {
  491. $attachment = new Attachment();
  492. foreach ($attachment_names as $lang => $name)
  493. $attachment->name[(int)$lang] = $name;
  494. foreach ($attachment_descriptions as $lang => $description)
  495. $attachment->description[(int)$lang] = $description;
  496. $attachment->file = $uniqid;
  497. $attachment->mime = $_FILES['attachment_file']['type'];
  498. $attachment->file_name = $_FILES['attachment_file']['name'];
  499. if (empty($attachment->mime) || Tools::strlen($attachment->mime) > 128)
  500. $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file extension');
  501. if (!Validate::isGenericName($attachment->file_name))
  502. $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file name');
  503. if (Tools::strlen($attachment->file_name) > 128)
  504. $_FILES['attachment_file']['error'][] = Tools::displayError('The file name is too long.');
  505. if (empty($this->errors))
  506. {
  507. $res = $attachment->add();
  508. if (!$res)
  509. $_FILES['attachment_file']['error'][] = Tools::displayError('This attachment was unable to be loaded into the database.');
  510. else
  511. {
  512. $_FILES['attachment_file']['id_attachment'] = $attachment->id;
  513. $_FILES['attachment_file']['filename'] = $attachment->name[$this->context->employee->id_lang];
  514. $id_product = (int)Tools::getValue($this->identifier);
  515. $res = $attachment->attachProduct($id_product);
  516. if (!$res)
  517. $_FILES['attachment_file']['error'][] = Tools::displayError('We were unable to associate this attachment to a product.');
  518. }
  519. }
  520. else
  521. $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file');
  522. }
  523. }
  524. die(Tools::jsonEncode($_FILES));
  525. }
  526. }
  527. /**
  528. * Attach an existing attachment to the product
  529. *
  530. * @return void
  531. */
  532. public function processAttachments()
  533. {
  534. if ($id = (int)Tools::getValue($this->identifier))
  535. {
  536. $attachments = trim(Tools::getValue('arrayAttachments'), ',');
  537. $attachments = explode(',', $attachments);
  538. if (!Attachment::attachToProduct($id, $attachments))
  539. $this->errors[] = Tools::displayError('An error occurred while saving product attachments.');
  540. }
  541. }
  542. public function processDuplicate()
  543. {
  544. if (Validate::isLoadedObject($product = new Product((int)Tools::getValue('id_product'))))
  545. {
  546. $id_product_old = $product->id;
  547. if (empty($product->price) && Shop::getContext() == Shop::CONTEXT_GROUP)
  548. {
  549. $shops = ShopGroup::getShopsFromGroup(Shop::getContextShopGroupID());
  550. foreach ($shops as $shop)
  551. if ($product->isAssociatedToShop($shop['id_shop']))
  552. {
  553. $product_price = new Product($id_product_old, false, null, $shop['id_shop']);
  554. $product->price = $product_price->price;
  555. }
  556. }
  557. unset($product->id);
  558. unset($product->id_product);
  559. $product->indexed = 0;
  560. $product->active = 0;
  561. if ($product->add()
  562. && Category::duplicateProductCategories($id_product_old, $product->id)
  563. && ($combination_images = Product::duplicateAttributes($id_product_old, $product->id)) !== false
  564. && GroupReduction::duplicateReduction($id_product_old, $product->id)
  565. && Product::duplicateAccessories($id_product_old, $product->id)
  566. && Product::duplicateFeatures($id_product_old, $product->id)
  567. && Product::duplicateSpecificPrices($id_product_old, $product->id)
  568. && Pack::duplicate($id_product_old, $product->id)
  569. && Product::duplicateCustomizationFields($id_product_old, $product->id)
  570. && Product::duplicateTags($id_product_old, $product->id)
  571. && Product::duplicateDownload($id_product_old, $product->id))
  572. {
  573. if ($product->hasAttributes())
  574. Product::updateDefaultAttribute($product->id);
  575. if (!Tools::getValue('noimage') && !Image::duplicateProductImages($id_product_old, $product->id, $combination_images))
  576. $this->errors[] = Tools::displayError('An error occurred while copying images.');
  577. else
  578. {
  579. Hook::exec('actionProductAdd', array('product' => $product));
  580. if (in_array($product->visibility, array('both', 'search')) && Configuration::get('PS_SEARCH_INDEXATION'))
  581. Search::indexation(false, $product->id);
  582. $this->redirect_after = self::$currentIndex.(Tools::getIsset('id_category') ? '&id_category='.(int)Tools::getValue('id_category') : '').'&conf=19&token='.$this->token;
  583. }
  584. }
  585. else
  586. $this->errors[] = Tools::displayError('An error occurred while creating an object.');
  587. }
  588. }
  589. public function processDelete()
  590. {
  591. if (Validate::isLoadedObject($object = $this->loadObject()) && isset($this->fieldImageSettings))
  592. {
  593. // check if request at least one object with noZeroObject
  594. if (isset($object->noZeroObject) && count($taxes = call_user_func(array($this->className, $object->noZeroObject))) <= 1)
  595. $this->errors[] = Tools::displayError('You need at least one object.').' <b>'.$this->table.'</b><br />'.Tools::displayError('You cannot delete all of the items.');
  596. else
  597. {
  598. /*
  599. * @since 1.5.0
  600. * It is NOT possible to delete a product if there are currently:
  601. * - physical stock for this product
  602. * - supply order(s) for this product
  603. */
  604. if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && $object->advanced_stock_management)
  605. {
  606. $stock_manager = StockManagerFactory::getManager();
  607. $physical_quantity = $stock_manager->getProductPhysicalQuantities($object->id, 0);
  608. $real_quantity = $stock_manager->getProductRealQuantities($object->id, 0);
  609. if ($physical_quantity > 0 || $real_quantity > $physical_quantity)
  610. $this->errors[] = Tools::displayError('You cannot delete this product because there\'s physical stock left.');
  611. }
  612. if (!count($this->errors))
  613. {
  614. if ($object->delete())
  615. {
  616. $id_category = (int)Tools::getValue('id_category');
  617. $category_url = empty($id_category) ? '' : '&id_category='.(int)$id_category;
  618. PrestaShopLogger::addLog(sprintf($this->l('%s deletion', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int)$object->id, true, (int)$this->context->employee->id);
  619. $this->redirect_after = self::$currentIndex.'&conf=1&token='.$this->token.$category_url;
  620. }
  621. else
  622. $this->errors[] = Tools::displayError('An error occurred during deletion.');
  623. }
  624. }
  625. }
  626. else
  627. $this->errors[] = Tools::displayError('An error occurred while deleting the object.').' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
  628. }
  629. public function processImage()
  630. {
  631. $id_image = (int)Tools::getValue('id_image');
  632. $image = new Image((int)$id_image);
  633. if (Validate::isLoadedObject($image))
  634. {
  635. /* Update product image/legend */
  636. // @todo : move in processEditProductImage
  637. if (Tools::getIsset('editImage'))
  638. {
  639. if ($image->cover)
  640. $_POST['cover'] = 1;
  641. $_POST['id_image'] = $image->id;
  642. }
  643. /* Choose product cover image */
  644. elseif (Tools::getIsset('coverImage'))
  645. {
  646. Image::deleteCover($image->id_product);
  647. $image->cover = 1;
  648. if (!$image->update())
  649. $this->errors[] = Tools::displayError('You cannot change the product\'s cover image.');
  650. else
  651. {
  652. $productId = (int)Tools::getValue('id_product');
  653. @unlink(_PS_TMP_IMG_DIR_.'product_'.$productId.'.jpg');
  654. @unlink(_PS_TMP_IMG_DIR_.'product_mini_'.$productId.'_'.$this->context->shop->id.'.jpg');
  655. $this->redirect_after = self::$currentIndex.'&id_product='.$image->id_product.'&id_category='.(Tools::getIsset('id_category') ? '&id_category='.(int)Tools::getValue('id_category') : '').'&action=Images&addproduct'.'&token='.$this->token;
  656. }
  657. }
  658. /* Choose product image position */
  659. elseif (Tools::getIsset('imgPosition') && Tools::getIsset('imgDirection'))
  660. {
  661. $image->updatePosition(Tools::getValue('imgDirection'), Tools::getValue('imgPosition'));
  662. $this->redirect_after = self::$currentIndex.'&id_product='.$image->id_product.'&id_category='.(Tools::getIsset('id_category') ? '&id_category='.(int)Tools::getValue('id_category') : '').'&add'.$this->table.'&action=Images&token='.$this->token;
  663. }
  664. }
  665. else
  666. $this->errors[] = Tools::displayError('The image could not be found. ');
  667. }
  668. protected function processBulkDelete()
  669. {
  670. if ($this->tabAccess['delete'] === '1')
  671. {
  672. if (is_array($this->boxes) && !empty($this->boxes))
  673. {
  674. $object = new $this->className();
  675. if (isset($object->noZeroObject) &&
  676. // Check if all object will be deleted
  677. (count(call_user_func(array($this->className, $object->noZeroObject))) <= 1 || count($_POST[$this->table.'Box']) == count(call_user_func(array($this->className, $object->noZeroObject)))))
  678. $this->errors[] = Tools::displayError('You need at least one object.').' <b>'.$this->table.'</b><br />'.Tools::displayError('You cannot delete all of the items.');
  679. else
  680. {
  681. $success = 1;
  682. $products = Tools::getValue($this->table.'Box');
  683. if (is_array($products) && ($count = count($products)))
  684. {
  685. // Deleting products can be quite long on a cheap server. Let's say 1.5 seconds by product (I've seen it!).
  686. if (intval(ini_get('max_execution_time')) < round($count * 1.5))
  687. ini_set('max_execution_time', round($count * 1.5));
  688. if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT'))
  689. $stock_manager = StockManagerFactory::getManager();
  690. foreach ($products as $id_product)
  691. {
  692. $product = new Product((int)$id_product);
  693. /*
  694. * @since 1.5.0
  695. * It is NOT possible to delete a product if there are currently:
  696. * - physical stock for this product
  697. * - supply order(s) for this product
  698. */
  699. if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && $product->advanced_stock_management)
  700. {
  701. $physical_quantity = $stock_manager->getProductPhysicalQuantities($product->id, 0);
  702. $real_quantity = $stock_manager->getProductRealQuantities($product->id, 0);
  703. if ($physical_quantity > 0 || $real_quantity > $physical_quantity)
  704. $this->errors[] = sprintf(Tools::displayError('You cannot delete the product #%d because there is physical stock left.'), $product->id);
  705. }
  706. if (!count($this->errors))
  707. {
  708. if ($product->delete())
  709. PrestaShopLogger::addLog(sprintf($this->l('%s deletion', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int)$product->id, true, (int)$this->context->employee->id);
  710. else
  711. $success = false;
  712. }
  713. else
  714. $success = 0;
  715. }
  716. }
  717. if ($success)
  718. {
  719. $id_category = (int)Tools::getValue('id_category');
  720. $category_url = empty($id_category) ? '' : '&id_category='.(int)$id_category;
  721. $this->redirect_after = self::$currentIndex.'&conf=2&token='.$this->token.$category_url;
  722. }
  723. else
  724. $this->errors[] = Tools::displayError('An error occurred while deleting this selection.');
  725. }
  726. }
  727. else
  728. $this->errors[] = Tools::displayError('You must select at least one element to delete.');
  729. }
  730. else
  731. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  732. }
  733. public function processProductAttribute()
  734. {
  735. // Don't process if the combination fields have not been submitted
  736. if (!Combination::isFeatureActive() || !Tools::getValue('attribute_combination_list'))
  737. return;
  738. if (Validate::isLoadedObject($product = $this->object))
  739. {
  740. if ($this->isProductFieldUpdated('attribute_price') && (!Tools::getIsset('attribute_price') || Tools::getIsset('attribute_price') == null))
  741. $this->errors[] = Tools::displayError('The price attribute is required.');
  742. if (!Tools::getIsset('attribute_combination_list') || Tools::isEmpty(Tools::getValue('attribute_combination_list')))
  743. $this->errors[] = Tools::displayError('You must add at least one attribute.');
  744. $array_checks = array(
  745. 'reference' => 'isReference',
  746. 'supplier_reference' => 'isReference',
  747. 'location' => 'isReference',
  748. 'ean13' => 'isEan13',
  749. 'upc' => 'isUpc',
  750. 'wholesale_price' => 'isPrice',
  751. 'price' => 'isPrice',
  752. 'ecotax' => 'isPrice',
  753. 'quantity' => 'isInt',
  754. 'weight' => 'isUnsignedFloat',
  755. 'unit_price_impact' => 'isPrice',
  756. 'default_on' => 'isBool',
  757. 'minimal_quantity' => 'isUnsignedInt',
  758. 'available_date' => 'isDateFormat'
  759. );
  760. foreach ($array_checks as $property => $check)
  761. if (Tools::getValue('attribute_'.$property) !== false && !call_user_func(array('Validate', $check), Tools::getValue('attribute_'.$property)))
  762. $this->errors[] = sprintf(Tools::displayError('Field %s is not valid'), $property);
  763. if (!count($this->errors))
  764. {
  765. if (!isset($_POST['attribute_wholesale_price'])) $_POST['attribute_wholesale_price'] = 0;
  766. if (!isset($_POST['attribute_price_impact'])) $_POST['attribute_price_impact'] = 0;
  767. if (!isset($_POST['attribute_weight_impact'])) $_POST['attribute_weight_impact'] = 0;
  768. if (!isset($_POST['attribute_ecotax'])) $_POST['attribute_ecotax'] = 0;
  769. if (Tools::getValue('attribute_default'))
  770. $product->deleteDefaultAttributes();
  771. // Change existing one
  772. if (($id_product_attribute = (int)Tools::getValue('id_product_attribute')) || ($id_product_attribute = $product->productAttributeExists(Tools::getValue('attribute_combination_list'), false, null, true, true)))
  773. {
  774. if ($this->tabAccess['edit'] === '1')
  775. {
  776. if ($this->isProductFieldUpdated('available_date_attribute') && (Tools::getValue('available_date_attribute') != '' &&!Validate::isDateFormat(Tools::getValue('available_date_attribute'))))
  777. $this->errors[] = Tools::displayError('Invalid date format.');
  778. else
  779. {
  780. $product->updateAttribute((int)$id_product_attribute,
  781. $this->isProductFieldUpdated('attribute_wholesale_price') ? Tools::getValue('attribute_wholesale_price') : null,
  782. $this->isProductFieldUpdated('attribute_price_impact') ? Tools::getValue('attribute_price') * Tools::getValue('attribute_price_impact') : null,
  783. $this->isProductFieldUpdated('attribute_weight_impact') ? Tools::getValue('attribute_weight') * Tools::getValue('attribute_weight_impact') : null,
  784. $this->isProductFieldUpdated('attribute_unit_impact') ? Tools::getValue('attribute_unity') * Tools::getValue('attribute_unit_impact') : null,
  785. $this->isProductFieldUpdated('attribute_ecotax') ? Tools::getValue('attribute_ecotax') : null,
  786. Tools::getValue('id_image_attr'),
  787. Tools::getValue('attribute_reference'),
  788. Tools::getValue('attribute_ean13'),
  789. $this->isProductFieldUpdated('attribute_default') ? Tools::getValue('attribute_default') : null,
  790. Tools::getValue('attribute_location'),
  791. Tools::getValue('attribute_upc'),
  792. $this->isProductFieldUpdated('attribute_minimal_quantity') ? Tools::getValue('attribute_minimal_quantity') : null,
  793. $this->isProductFieldUpdated('available_date_attribute') ? Tools::getValue('available_date_attribute') : null, false);
  794. StockAvailable::setProductDependsOnStock((int)$product->id, $product->depends_on_stock, null, (int)$id_product_attribute);
  795. StockAvailable::setProductOutOfStock((int)$product->id, $product->out_of_stock, null, (int)$id_product_attribute);
  796. }
  797. }
  798. else
  799. $this->errors[] = Tools::displayError('You do not have permission to add this.');
  800. }
  801. // Add new
  802. else
  803. {
  804. if ($this->tabAccess['add'] === '1')
  805. {
  806. if ($product->productAttributeExists(Tools::getValue('attribute_combination_list')))
  807. $this->errors[] = Tools::displayError('This combination already exists.');
  808. else
  809. {
  810. $id_product_attribute = $product->addCombinationEntity(
  811. Tools::getValue('attribute_wholesale_price'),
  812. Tools::getValue('attribute_price') * Tools::getValue('attribute_price_impact'),
  813. Tools::getValue('attribute_weight') * Tools::getValue('attribute_weight_impact'),
  814. Tools::getValue('attribute_unity') * Tools::getValue('attribute_unit_impact'),
  815. Tools::getValue('attribute_ecotax'),
  816. 0,
  817. Tools::getValue('id_image_attr'),
  818. Tools::getValue('attribute_reference'),
  819. null,
  820. Tools::getValue('attribute_ean13'),
  821. Tools::getValue('attribute_default'),
  822. Tools::getValue('attribute_location'),
  823. Tools::getValue('attribute_upc'),
  824. Tools::getValue('attribute_minimal_quantity'),
  825. Array(),
  826. Tools::getValue('available_date_attribute')
  827. );
  828. StockAvailable::setProductDependsOnStock((int)$product->id, $product->depends_on_stock, null, (int)$id_product_attribute);
  829. StockAvailable::setProductOutOfStock((int)$product->id, $product->out_of_stock, null, (int)$id_product_attribute);
  830. }
  831. }
  832. else
  833. $this->errors[] = Tools::displayError('You do not have permission to').'<hr>'.Tools::displayError('edit here.');
  834. }
  835. if (!count($this->errors))
  836. {
  837. $combination = new Combination((int)$id_product_attribute);
  838. $combination->setAttributes(Tools::getValue('attribute_combination_list'));
  839. // images could be deleted before
  840. $id_images = Tools::getValue('id_image_attr');
  841. if (!empty($id_images))
  842. $combination->setImages($id_images);
  843. $product->checkDefaultAttributes();
  844. if (Tools::getValue('attribute_default'))
  845. {
  846. Product::updateDefaultAttribute((int)$product->id);
  847. if(isset($id_product_attribute))
  848. $product->cache_default_attribute = (int)$id_product_attribute;
  849. if ($available_date = Tools::getValue('available_date_attribute'))
  850. $product->setAvailableDate($available_date);
  851. }
  852. }
  853. }
  854. }
  855. }
  856. public function processFeatures()
  857. {
  858. if (!Feature::isFeatureActive())
  859. return;
  860. if (Validate::isLoadedObject($product = new Product((int)Tools::getValue('id_product'))))
  861. {
  862. // delete all objects
  863. $product->deleteFeatures();
  864. // add new objects
  865. $languages = Language::getLanguages(false);
  866. foreach ($_POST as $key => $val)
  867. {
  868. if (preg_match('/^feature_([0-9]+)_value/i', $key, $match))
  869. {
  870. if ($val)
  871. $product->addFeaturesToDB($match[1], $val);
  872. else
  873. {
  874. if ($default_value = $this->checkFeatures($languages, $match[1]))
  875. {
  876. $id_value = $product->addFeaturesToDB($match[1], 0, 1);
  877. foreach ($languages as $language)
  878. {
  879. if ($cust = Tools::getValue('custom_'.$match[1].'_'.(int)$language['id_lang']))
  880. $product->addFeaturesCustomToDB($id_value, (int)$language['id_lang'], $cust);
  881. else
  882. $product->addFeaturesCustomToDB($id_value, (int)$language['id_lang'], $default_value);
  883. }
  884. }
  885. }
  886. }
  887. }
  888. }
  889. else
  890. $this->errors[] = Tools::displayError('A product must be created before adding features.');
  891. }
  892. /**
  893. * This function is never called at the moment (specific prices cannot be edited)
  894. */
  895. public function processPricesModification()
  896. {
  897. $id_specific_prices = Tools::getValue('spm_id_specific_price');
  898. $id_combinations = Tools::getValue('spm_id_product_attribute');
  899. $id_shops = Tools::getValue('spm_id_shop');
  900. $id_currencies = Tools::getValue('spm_id_currency');
  901. $id_countries = Tools::getValue('spm_id_country');
  902. $id_groups = Tools::getValue('spm_id_group');
  903. $id_customers = Tools::getValue('spm_id_customer');
  904. $prices = Tools::getValue('spm_price');
  905. $from_quantities = Tools::getValue('spm_from_quantity');
  906. $reductions = Tools::getValue('spm_reduction');
  907. $reduction_types = Tools::getValue('spm_reduction_type');
  908. $froms = Tools::getValue('spm_from');
  909. $tos = Tools::getValue('spm_to');
  910. foreach ($id_specific_prices as $key => $id_specific_price)
  911. if ($reduction_types[$key] == 'percentage' && ((float)$reductions[$key] <= 0 || (float)$reductions[$key] > 100))
  912. $this->errors[] = Tools::displayError('Submitted reduction value (0-100) is out-of-range');
  913. elseif ($this->_validateSpecificPrice($id_shops[$key], $id_currencies[$key], $id_countries[$key], $id_groups[$key], $id_customers[$key], $prices[$key], $from_quantities[$key], $reductions[$key], $reduction_types[$key], $froms[$key], $tos[$key], $id_combinations[$key]))
  914. {
  915. $specific_price = new SpecificPrice((int)($id_specific_price));
  916. $specific_price->id_shop = (int)$id_shops[$key];
  917. $specific_price->id_product_attribute = (int)$id_combinations[$key];
  918. $specific_price->id_currency = (int)($id_currencies[$key]);
  919. $specific_price->id_country = (int)($id_countries[$key]);
  920. $specific_price->id_group = (int)($id_groups[$key]);
  921. $specific_price->id_customer = (int)$id_customers[$key];
  922. $specific_price->price = (float)($prices[$key]);
  923. $specific_price->from_quantity = (int)($from_quantities[$key]);
  924. $specific_price->reduction = (float)($reduction_types[$key] == 'percentage' ? ($reductions[$key] / 100) : $reductions[$key]);
  925. $specific_price->reduction_type = !$reductions[$key] ? 'amount' : $reduction_types[$key];
  926. $specific_price->from = !$froms[$key] ? '0000-00-00 00:00:00' : $froms[$key];
  927. $specific_price->to = !$tos[$key] ? '0000-00-00 00:00:00' : $tos[$key];
  928. if (!$specific_price->update())
  929. $this->errors[] = Tools::displayError('An error occurred while updating the specific price.');
  930. }
  931. if (!count($this->errors))
  932. $this->redirect_after = self::$currentIndex.'&id_product='.(int)(Tools::getValue('id_product')).(Tools::getIsset('id_category') ? '&id_category='.(int)Tools::getValue('id_category') : '').'&update'.$this->table.'&action=Prices&token='.$this->token;
  933. }
  934. public function processPriceAddition()
  935. {
  936. // Check if a specific price has been submitted
  937. if (!Tools::getIsset('submitPriceAddition'))
  938. return;
  939. $id_product = Tools::getValue('id_product');
  940. $id_product_attribute = Tools::getValue('sp_id_product_attribute');
  941. $id_shop = Tools::getValue('sp_id_shop');
  942. $id_currency = Tools::getValue('sp_id_currency');
  943. $id_country = Tools::getValue('sp_id_country');
  944. $id_group = Tools::getValue('sp_id_group');
  945. $id_customer = Tools::getValue('sp_id_customer');
  946. $price = Tools::getValue('leave_bprice') ? '-1' : Tools::getValue('sp_price');
  947. $from_quantity = Tools::getValue('sp_from_quantity');
  948. $reduction = (float)(Tools::getValue('sp_reduction'));
  949. $reduction_type = !$reduction ? 'amount' : Tools::getValue('sp_reduction_type');
  950. $from = Tools::getValue('sp_from');
  951. if (!$from)
  952. $from = '0000-00-00 00:00:00';
  953. $to = Tools::getValue('sp_to');
  954. if (!$to)
  955. $to = '0000-00-00 00:00:00';
  956. if ($reduction_type == 'percentage' && ((float)$reduction <= 0 || (float)$reduction > 100))
  957. $this->errors[] = Tools::displayError('Submitted reduction value (0-100) is out-of-range');
  958. elseif ($this->_validateSpecificPrice($id_shop, $id_currency, $id_country, $id_group, $id_customer, $price, $from_quantity, $reduction, $reduction_type, $from, $to, $id_product_attribute))
  959. {
  960. $specificPrice = new SpecificPrice();
  961. $specificPrice->id_product = (int)$id_product;
  962. $specificPrice->id_product_attribute = (int)$id_product_attribute;
  963. $specificPrice->id_shop = (int)$id_shop;
  964. $specificPrice->id_currency = (int)($id_currency);
  965. $specificPrice->id_country = (int)($id_country);
  966. $specificPrice->id_group = (int)($id_group);
  967. $specificPrice->id_customer = (int)$id_customer;
  968. $specificPrice->price = (float)($price);
  969. $specificPrice->from_quantity = (int)($from_quantity);
  970. $specificPrice->reduction = (float)($reduction_type == 'percentage' ? $reduction / 100 : $reduction);
  971. $specificPrice->reduction_type = $reduction_type;
  972. $specificPrice->from = $from;
  973. $specificPrice->to = $to;
  974. if (!$specificPrice->add())
  975. $this->errors[] = Tools::displayError('An error occurred while updating the specific price.');
  976. }
  977. }
  978. public function ajaxProcessDeleteSpecificPrice()
  979. {
  980. if ($this->tabAccess['delete'] === '1')
  981. {
  982. $id_specific_price = (int)Tools::getValue('id_specific_price');
  983. if (!$id_specific_price || !Validate::isUnsignedId($id_specific_price))
  984. $error = Tools::displayError('The specific price ID is invalid.');
  985. else
  986. {
  987. $specificPrice = new SpecificPrice((int)$id_specific_price);
  988. if (!$specificPrice->delete())
  989. $error = Tools::displayError('An error occurred while attempting to delete the specific price.');
  990. }
  991. }
  992. else
  993. $error = Tools::displayError('You do not have permission to delete this.');
  994. if (isset($error))
  995. $json = array(
  996. 'status' => 'error',
  997. 'message'=> $error
  998. );
  999. else
  1000. $json = array(
  1001. 'status' => 'ok',
  1002. 'message'=> $this->_conf[1]
  1003. );
  1004. die(Tools::jsonEncode($json));
  1005. }
  1006. public function processSpecificPricePriorities()
  1007. {
  1008. if (!($obj = $this->loadObject()))
  1009. return;
  1010. if (!$priorities = Tools::getValue('specificPricePriority'))
  1011. $this->errors[] = Tools::displayError('Please specify priorities.');
  1012. elseif (Tools::isSubmit('specificPricePriorityToAll'))
  1013. {
  1014. if (!SpecificPrice::setPriorities($priorities))
  1015. $this->errors[] = Tools::displayError('An error occurred while updating priorities.');
  1016. else
  1017. $this->confirmations[] = $this->l('The price rule has successfully updated');
  1018. }
  1019. elseif (!SpecificPrice::setSpecificPriority((int)$obj->id, $priorities))
  1020. $this->errors[] = Tools::displayError('An error occurred while setting priorities.');
  1021. }
  1022. public function processCustomizationConfiguration()
  1023. {
  1024. $product = $this->object;
  1025. // Get the number of existing customization fields ($product->text_fields is the updated value, not the existing value)
  1026. $current_customization = $product->getCustomizationFieldIds();
  1027. $files_count = 0;
  1028. $text_count = 0;
  1029. if (is_array($current_customization))
  1030. {
  1031. foreach ($current_customization as $field)
  1032. {
  1033. if ($field['type'] == 1)
  1034. $text_count++;
  1035. else
  1036. $files_count++;
  1037. }
  1038. }
  1039. if (!$product->createLabels((int)$product->uploadable_files - $files_count, (int)$product->text_fields - $text_count))
  1040. $this->errors[] = Tools::displayError('An error occurred while creating customization fields.');
  1041. if (!count($this->errors) && !$product->updateLabels())
  1042. $this->errors[] = Tools::displayError('An error occurred while updating customization fields.');
  1043. $product->customizable = ($product->uploadable_files > 0 || $product->text_fields > 0) ? 1 : 0;
  1044. if (!count($this->errors) && !$product->update())
  1045. $this->errors[] = Tools::displayError('An error occurred while updating the custom configuration.');
  1046. }
  1047. public function processProductCustomization()
  1048. {
  1049. if (Validate::isLoadedObject($product = new Product((int)Tools::getValue('id_product'))))
  1050. {
  1051. foreach ($_POST as $field => $value)
  1052. if (strncmp($field, 'label_', 6) == 0 && !Validate::isLabel($value))
  1053. $this->errors[] = Tools::displayError('The label fields defined are invalid.');
  1054. if (empty($this->errors) && !$product->updateLabels())
  1055. $this->errors[] = Tools::displayError('An error occurred while updating customization fields.');
  1056. if (empty($this->errors))
  1057. $this->confirmations[] = $this->l('Update successful');
  1058. }
  1059. else
  1060. $this->errors[] = Tools::displayError('A product must be created before adding customization.');
  1061. }
  1062. /**
  1063. * Overrides parent for custom redirect link
  1064. */
  1065. public function processPosition()
  1066. {
  1067. if (!Validate::isLoadedObject($object = $this->loadObject()))
  1068. {
  1069. $this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').
  1070. ' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
  1071. }
  1072. elseif (!$object->updatePosition((int)Tools::getValue('way'), (int)Tools::getValue('position')))
  1073. $this->errors[] = Tools::displayError('Failed to update the position.');
  1074. else
  1075. {
  1076. $category = new Category((int)Tools::getValue('id_category'));
  1077. if (Validate::isLoadedObject($category))
  1078. Hook::exec('actionCategoryUpdate', array('category' => $category));
  1079. $this->redirect_after = self::$currentIndex.'&'.$this->table.'Orderby=position&'.$this->table.'Orderway=asc&action=Customization&conf=5'.(($id_category = (Tools::getIsset('id_category') ? (int)Tools::getValue('id_category') : '')) ? ('&id_category='.$id_category) : '').'&token='.Tools::getAdminTokenLite('AdminProducts');
  1080. }
  1081. }
  1082. public function initProcess()
  1083. {
  1084. if (Tools::isSubmit('submitAddproductAndStay') || Tools::isSubmit('submitAddproduct'))
  1085. {
  1086. $this->id_object = (int)Tools::getValue('id_product');
  1087. $this->object = new Product($this->id_object);
  1088. if ((bool)$this->object->is_virtual && (int)Tools::getValue('type_product') != 2)
  1089. {
  1090. if (!($id_product_download = ProductDownload::getIdFromIdProduct($this->id_object)))
  1091. $this->errors[] = Tools::displayError('Cannot retrieve file');
  1092. else
  1093. {
  1094. $product_download = new ProductDownload((int)$id_product_download);
  1095. if (!$product_download->deleteFile((int)$id_product_download))
  1096. $this->errors[] = Tools::displayError('Cannot delete file');
  1097. }
  1098. }
  1099. }
  1100. // Delete a product in the download folder
  1101. if (Tools::getValue('deleteVirtualProduct'))
  1102. {
  1103. if ($this->tabAccess['delete'] === '1')
  1104. $this->action = 'deleteVirtualProduct';
  1105. else
  1106. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  1107. }
  1108. // Product preview
  1109. elseif (Tools::isSubmit('submitAddProductAndPreview'))
  1110. {
  1111. $this->display = 'edit';
  1112. $this->action = 'save';
  1113. if (Tools::getValue('id_product'))
  1114. {
  1115. $this->id_object = Tools::getValue('id_product');
  1116. $this->object = new Product((int)Tools::getValue('id_product'));
  1117. }
  1118. }
  1119. elseif (Tools::isSubmit('submitAttachments'))
  1120. {
  1121. if ($this->tabAccess['edit'] === '1')
  1122. {
  1123. $this->action = 'attachments';
  1124. $this->tab_display = 'attachments';
  1125. }
  1126. else
  1127. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  1128. }
  1129. // Product duplication
  1130. elseif (Tools::getIsset('duplicate'.$this->table))
  1131. {
  1132. if ($this->tabAccess['add'] === '1')
  1133. $this->action = 'duplicate';
  1134. else
  1135. $this->errors[] = Tools::displayError('You do not have permission to add this.');
  1136. }
  1137. // Pro…

Large files files are truncated, but you can click here to view the full file