PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/controllers/front/ProductController.php

https://github.com/netplayer/PrestaShop
PHP | 725 lines | 584 code | 67 blank | 74 comment | 155 complexity | 81eef1d3fffe9ef4a1cf82ab870fd3a7 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, LGPL-3.0
  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 ProductControllerCore extends FrontController
  27. {
  28. public $php_self = 'product';
  29. protected $product;
  30. protected $category;
  31. public function setMedia()
  32. {
  33. parent::setMedia();
  34. if (count($this->errors))
  35. return ;
  36. if (!$this->useMobileTheme())
  37. {
  38. $this->addCSS(_THEME_CSS_DIR_.'product.css');
  39. $this->addCSS(_THEME_CSS_DIR_.'print.css', 'print');
  40. $this->addJqueryPlugin(array('fancybox', 'idTabs', 'scrollTo', 'serialScroll', 'bxslider'));
  41. $this->addJS(array(
  42. _THEME_JS_DIR_.'tools.js', // retro compat themes 1.5
  43. _THEME_JS_DIR_.'product.js'
  44. ));
  45. }
  46. else
  47. {
  48. $this->addJqueryPlugin(array('scrollTo', 'serialScroll'));
  49. $this->addJS(array(
  50. _THEME_JS_DIR_.'tools.js', // retro compat themes 1.5
  51. _THEME_MOBILE_JS_DIR_.'product.js',
  52. _THEME_MOBILE_JS_DIR_.'jquery.touch-gallery.js'
  53. ));
  54. }
  55. if (Configuration::get('PS_DISPLAY_JQZOOM') == 1)
  56. $this->addJqueryPlugin('jqzoom');
  57. }
  58. public function canonicalRedirection($canonical_url = '')
  59. {
  60. if (Tools::getValue('live_edit'))
  61. return ;
  62. if (Validate::isLoadedObject($this->product))
  63. parent::canonicalRedirection($this->context->link->getProductLink($this->product));
  64. }
  65. /**
  66. * Initialize product controller
  67. * @see FrontController::init()
  68. */
  69. public function init()
  70. {
  71. parent::init();
  72. if ($id_product = (int)Tools::getValue('id_product'))
  73. $this->product = new Product($id_product, true, $this->context->language->id, $this->context->shop->id);
  74. if (!Validate::isLoadedObject($this->product))
  75. {
  76. header('HTTP/1.1 404 Not Found');
  77. header('Status: 404 Not Found');
  78. $this->errors[] = Tools::displayError('Product not found');
  79. }
  80. else
  81. {
  82. $this->canonicalRedirection();
  83. /*
  84. * If the product is associated to the shop
  85. * and is active or not active but preview mode (need token + file_exists)
  86. * allow showing the product
  87. * In all the others cases => 404 "Product is no longer available"
  88. */
  89. if (!$this->product->isAssociatedToShop() || !$this->product->active)
  90. {
  91. if (Tools::getValue('adtoken') == Tools::getAdminToken('AdminProducts'.(int)Tab::getIdFromClassName('AdminProducts').(int)Tools::getValue('id_employee')) && $this->product->isAssociatedToShop())
  92. {
  93. // If the product is not active, it's the admin preview mode
  94. $this->context->smarty->assign('adminActionDisplay', true);
  95. }
  96. else
  97. {
  98. $this->context->smarty->assign('adminActionDisplay', false);
  99. if ($this->product->id_product_redirected == $this->product->id)
  100. $this->product->redirect_type = '404';
  101. switch ($this->product->redirect_type)
  102. {
  103. case '301':
  104. header('HTTP/1.1 301 Moved Permanently');
  105. header('Location: '.$this->context->link->getProductLink($this->product->id_product_redirected));
  106. break;
  107. case '302':
  108. header('HTTP/1.1 302 Moved Temporarily');
  109. header('Cache-Control: no-cache');
  110. header('Location: '.$this->context->link->getProductLink($this->product->id_product_redirected));
  111. break;
  112. case '404':
  113. default:
  114. header('HTTP/1.1 404 Not Found');
  115. header('Status: 404 Not Found');
  116. $this->errors[] = Tools::displayError('This product is no longer available.');
  117. break;
  118. }
  119. }
  120. }
  121. elseif (!$this->product->checkAccess(isset($this->context->customer) ? $this->context->customer->id : 0))
  122. {
  123. header('HTTP/1.1 403 Forbidden');
  124. header('Status: 403 Forbidden');
  125. $this->errors[] = Tools::displayError('You do not have access to this product.');
  126. }
  127. else
  128. {
  129. // Load category
  130. $id_category = false;
  131. if (isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'] == Tools::secureReferrer($_SERVER['HTTP_REFERER']) // Assure us the previous page was one of the shop
  132. && preg_match('~^.*(?<!\/content)\/([0-9]+)\-(.*[^\.])|(.*)id_(category|product)=([0-9]+)(.*)$~', $_SERVER['HTTP_REFERER'], $regs))
  133. {
  134. // If the previous page was a category and is a parent category of the product use this category as parent category
  135. $id_object = false;
  136. if (isset($regs[1]) && is_numeric($regs[1]))
  137. $id_object = (int)$regs[1];
  138. elseif (isset($regs[5]) && is_numeric($regs[5]))
  139. $id_object = (int)$regs[5];
  140. if ($id_object)
  141. {
  142. $referers = array($_SERVER['HTTP_REFERER'],urldecode($_SERVER['HTTP_REFERER']));
  143. if (in_array($this->context->link->getCategoryLink($id_object), $referers))
  144. $id_category = (int)$id_object;
  145. elseif (isset($this->context->cookie->last_visited_category) && (int)$this->context->cookie->last_visited_category && in_array($this->context->link->getProductLink($id_object), $referers))
  146. $id_category = (int)$this->context->cookie->last_visited_category;
  147. }
  148. }
  149. if (!$id_category || !Category::inShopStatic($id_category, $this->context->shop) || !Product::idIsOnCategoryId((int)$this->product->id, array('0' => array('id_category' => $id_category))))
  150. $id_category = (int)$this->product->id_category_default;
  151. $this->category = new Category((int)$id_category, (int)$this->context->cookie->id_lang);
  152. if (isset($this->context->cookie) && isset($this->category->id_category) && !(Module::isInstalled('blockcategories') && Module::isEnabled('blockcategories')))
  153. $this->context->cookie->last_visited_category = (int)$this->category->id_category;
  154. }
  155. }
  156. }
  157. /**
  158. * Assign template vars related to page content
  159. * @see FrontController::initContent()
  160. */
  161. public function initContent()
  162. {
  163. parent::initContent();
  164. if (!$this->errors)
  165. {
  166. if (Pack::isPack((int)$this->product->id) && !Pack::isInStock((int)$this->product->id))
  167. $this->product->quantity = 0;
  168. $this->product->description = $this->transformDescriptionWithImg($this->product->description);
  169. // Assign to the template the id of the virtual product. "0" if the product is not downloadable.
  170. $this->context->smarty->assign('virtual', ProductDownload::getIdFromIdProduct((int)$this->product->id));
  171. $this->context->smarty->assign('customizationFormTarget', Tools::safeOutput(urldecode($_SERVER['REQUEST_URI'])));
  172. if (Tools::isSubmit('submitCustomizedDatas'))
  173. {
  174. // If cart has not been saved, we need to do it so that customization fields can have an id_cart
  175. // We check that the cookie exists first to avoid ghost carts
  176. if (!$this->context->cart->id && isset($_COOKIE[$this->context->cookie->getName()]))
  177. {
  178. $this->context->cart->add();
  179. $this->context->cookie->id_cart = (int)$this->context->cart->id;
  180. }
  181. $this->pictureUpload();
  182. $this->textRecord();
  183. $this->formTargetFormat();
  184. }
  185. else if (Tools::getIsset('deletePicture') && !$this->context->cart->deleteCustomizationToProduct($this->product->id, Tools::getValue('deletePicture')))
  186. $this->errors[] = Tools::displayError('An error occurred while deleting the selected picture.');
  187. $pictures = array();
  188. $text_fields = array();
  189. if ($this->product->customizable)
  190. {
  191. $files = $this->context->cart->getProductCustomization($this->product->id, Product::CUSTOMIZE_FILE, true);
  192. foreach ($files as $file)
  193. $pictures['pictures_'.$this->product->id.'_'.$file['index']] = $file['value'];
  194. $texts = $this->context->cart->getProductCustomization($this->product->id, Product::CUSTOMIZE_TEXTFIELD, true);
  195. foreach ($texts as $text_field)
  196. $text_fields['textFields_'.$this->product->id.'_'.$text_field['index']] = str_replace('<br />', "\n", $text_field['value']);
  197. }
  198. $this->context->smarty->assign(array(
  199. 'pictures' => $pictures,
  200. 'textFields' => $text_fields));
  201. $this->product->customization_required = false;
  202. $customizationFields = $this->product->customizable ? $this->product->getCustomizationFields($this->context->language->id) : false;
  203. if (is_array($customizationFields))
  204. foreach($customizationFields as $customizationField)
  205. if ($this->product->customization_required = $customizationField['required'])
  206. break;
  207. // Assign template vars related to the category + execute hooks related to the category
  208. $this->assignCategory();
  209. // Assign template vars related to the price and tax
  210. $this->assignPriceAndTax();
  211. // Assign template vars related to the images
  212. $this->assignImages();
  213. // Assign attribute groups to the template
  214. $this->assignAttributesGroups();
  215. // Assign attributes combinations to the template
  216. $this->assignAttributesCombinations();
  217. // Pack management
  218. $pack_items = $this->product->cache_is_pack ? Pack::getItemTable($this->product->id, $this->context->language->id, true) : array();
  219. $this->context->smarty->assign('packItems', $pack_items);
  220. $this->context->smarty->assign('packs', Pack::getPacksTable($this->product->id, $this->context->language->id, true, 1));
  221. if (isset($this->category->id) && $this->category->id)
  222. $return_link = Tools::safeOutput($this->context->link->getCategoryLink($this->category));
  223. else
  224. $return_link = 'javascript: history.back();';
  225. $this->context->smarty->assign(array(
  226. 'stock_management' => Configuration::get('PS_STOCK_MANAGEMENT'),
  227. 'customizationFields' => $customizationFields,
  228. 'accessories' => $this->product->getAccessories($this->context->language->id),
  229. 'return_link' => $return_link,
  230. 'product' => $this->product,
  231. 'product_manufacturer' => new Manufacturer((int)$this->product->id_manufacturer, $this->context->language->id),
  232. 'token' => Tools::getToken(false),
  233. 'features' => $this->product->getFrontFeatures($this->context->language->id),
  234. 'attachments' => (($this->product->cache_has_attachments) ? $this->product->getAttachments($this->context->language->id) : array()),
  235. 'allow_oosp' => $this->product->isAvailableWhenOutOfStock((int)$this->product->out_of_stock),
  236. 'last_qties' => (int)Configuration::get('PS_LAST_QTIES'),
  237. 'HOOK_EXTRA_LEFT' => Hook::exec('displayLeftColumnProduct'),
  238. 'HOOK_EXTRA_RIGHT' => Hook::exec('displayRightColumnProduct'),
  239. 'HOOK_PRODUCT_OOS' => Hook::exec('actionProductOutOfStock', array('product' => $this->product)),
  240. 'HOOK_PRODUCT_ACTIONS' => Hook::exec('displayProductButtons', array('product' => $this->product)),
  241. 'HOOK_PRODUCT_TAB' => Hook::exec('displayProductTab', array('product' => $this->product)),
  242. 'HOOK_PRODUCT_TAB_CONTENT' => Hook::exec('displayProductTabContent', array('product' => $this->product)),
  243. 'HOOK_PRODUCT_CONTENT' => Hook::exec('displayProductContent', array('product' => $this->product)),
  244. 'display_qties' => (int)Configuration::get('PS_DISPLAY_QTIES'),
  245. 'display_ht' => !Tax::excludeTaxeOption(),
  246. 'currencySign' => $this->context->currency->sign,
  247. 'currencyRate' => $this->context->currency->conversion_rate,
  248. 'currencyFormat' => $this->context->currency->format,
  249. 'currencyBlank' => $this->context->currency->blank,
  250. 'jqZoomEnabled' => Configuration::get('PS_DISPLAY_JQZOOM'),
  251. 'ENT_NOQUOTES' => ENT_NOQUOTES,
  252. 'outOfStockAllowed' => (int)Configuration::get('PS_ORDER_OUT_OF_STOCK'),
  253. 'errors' => $this->errors,
  254. 'body_classes' => array(
  255. $this->php_self.'-'.$this->product->id,
  256. $this->php_self.'-'.$this->product->link_rewrite,
  257. 'category-'.(isset($this->category) ? $this->category->id : ''),
  258. 'category-'.(isset($this->category) ? $this->category->getFieldByLang('link_rewrite') : '')
  259. ),
  260. 'display_discount_price' => Configuration::get('PS_DISPLAY_DISCOUNT_PRICE'),
  261. ));
  262. }
  263. $this->setTemplate(_PS_THEME_DIR_.'product.tpl');
  264. }
  265. /**
  266. * Assign price and tax to the template
  267. */
  268. protected function assignPriceAndTax()
  269. {
  270. $id_customer = (isset($this->context->customer) ? (int)$this->context->customer->id : 0);
  271. $id_group = (int)Group::getCurrent()->id;
  272. $id_country = (int)$id_customer ? Customer::getCurrentCountry($id_customer) : Configuration::get('PS_COUNTRY_DEFAULT');
  273. $group_reduction = GroupReduction::getValueForProduct($this->product->id, $id_group);
  274. if ($group_reduction === false)
  275. $group_reduction = Group::getReduction((int)$this->context->cookie->id_customer) / 100;
  276. // Tax
  277. $tax = (float)$this->product->getTaxesRate(new Address((int)$this->context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}));
  278. $this->context->smarty->assign('tax_rate', $tax);
  279. $product_price_with_tax = Product::getPriceStatic($this->product->id, true, null, 6);
  280. if (Product::$_taxCalculationMethod == PS_TAX_INC)
  281. $product_price_with_tax = Tools::ps_round($product_price_with_tax, 2);
  282. $product_price_without_eco_tax = (float)$product_price_with_tax - $this->product->ecotax;
  283. $ecotax_rate = (float)Tax::getProductEcotaxRate($this->context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
  284. $ecotax_tax_amount = Tools::ps_round($this->product->ecotax, 2);
  285. if (Product::$_taxCalculationMethod == PS_TAX_INC && (int)Configuration::get('PS_TAX'))
  286. $ecotax_tax_amount = Tools::ps_round($ecotax_tax_amount * (1 + $ecotax_rate / 100), 2);
  287. $id_currency = (int)$this->context->cookie->id_currency;
  288. $id_product = (int)$this->product->id;
  289. $id_shop = $this->context->shop->id;
  290. $quantity_discounts = SpecificPrice::getQuantityDiscounts($id_product, $id_shop, $id_currency, $id_country, $id_group, null, true, (int)$this->context->customer->id);
  291. foreach ($quantity_discounts as &$quantity_discount)
  292. {
  293. if ($quantity_discount['id_product_attribute'])
  294. {
  295. $combination = new Combination((int)$quantity_discount['id_product_attribute']);
  296. $attributes = $combination->getAttributesName((int)$this->context->language->id);
  297. foreach ($attributes as $attribute)
  298. $quantity_discount['attributes'] = $attribute['name'].' - ';
  299. $quantity_discount['attributes'] = rtrim($quantity_discount['attributes'], ' - ');
  300. }
  301. if ((int)$quantity_discount['id_currency'] == 0 && $quantity_discount['reduction_type'] == 'amount')
  302. $quantity_discount['reduction'] = Tools::convertPriceFull($quantity_discount['reduction'], null, Context::getContext()->currency);
  303. }
  304. $product_price = $this->product->getPrice(Product::$_taxCalculationMethod == PS_TAX_INC, false);
  305. $address = new Address($this->context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
  306. $this->context->smarty->assign(array(
  307. 'quantity_discounts' => $this->formatQuantityDiscounts($quantity_discounts, $product_price, (float)$tax, $ecotax_tax_amount),
  308. 'ecotax_tax_inc' => $ecotax_tax_amount,
  309. 'ecotax_tax_exc' => Tools::ps_round($this->product->ecotax, 2),
  310. 'ecotaxTax_rate' => $ecotax_rate,
  311. 'productPriceWithoutEcoTax' => (float)$product_price_without_eco_tax,
  312. 'group_reduction' => $group_reduction,
  313. 'no_tax' => Tax::excludeTaxeOption() || !$this->product->getTaxesRate($address),
  314. 'ecotax' => (!count($this->errors) && $this->product->ecotax > 0 ? Tools::convertPrice((float)$this->product->ecotax) : 0),
  315. 'tax_enabled' => Configuration::get('PS_TAX'),
  316. 'customer_group_without_tax' => Group::getPriceDisplayMethod($this->context->customer->id_default_group),
  317. ));
  318. }
  319. /**
  320. * Assign template vars related to images
  321. */
  322. protected function assignImages()
  323. {
  324. $images = $this->product->getImages((int)$this->context->cookie->id_lang);
  325. $product_images = array();
  326. if(isset($images[0]))
  327. $this->context->smarty->assign('mainImage', $images[0]);
  328. foreach ($images as $k => $image)
  329. {
  330. if ($image['cover'])
  331. {
  332. $this->context->smarty->assign('mainImage', $image);
  333. $cover = $image;
  334. $cover['id_image'] = (Configuration::get('PS_LEGACY_IMAGES') ? ($this->product->id.'-'.$image['id_image']) : $image['id_image']);
  335. $cover['id_image_only'] = (int)$image['id_image'];
  336. }
  337. $product_images[(int)$image['id_image']] = $image;
  338. }
  339. if (!isset($cover))
  340. {
  341. if(isset($images[0]))
  342. {
  343. $cover = $images[0];
  344. $cover['id_image'] = (Configuration::get('PS_LEGACY_IMAGES') ? ($this->product->id.'-'.$images[0]['id_image']) : $images[0]['id_image']);
  345. $cover['id_image_only'] = (int)$images[0]['id_image'];
  346. }
  347. else
  348. $cover = array(
  349. 'id_image' => $this->context->language->iso_code.'-default',
  350. 'legend' => 'No picture',
  351. 'title' => 'No picture'
  352. );
  353. }
  354. $size = Image::getSize(ImageType::getFormatedName('large'));
  355. $this->context->smarty->assign(array(
  356. 'have_image' => (isset($cover['id_image']) && (int)$cover['id_image'])? array((int)$cover['id_image']) : Product::getCover((int)Tools::getValue('id_product')),
  357. 'cover' => $cover,
  358. 'imgWidth' => (int)$size['width'],
  359. 'mediumSize' => Image::getSize(ImageType::getFormatedName('medium')),
  360. 'largeSize' => Image::getSize(ImageType::getFormatedName('large')),
  361. 'homeSize' => Image::getSize(ImageType::getFormatedName('home')),
  362. 'cartSize' => Image::getSize(ImageType::getFormatedName('cart')),
  363. 'col_img_dir' => _PS_COL_IMG_DIR_));
  364. if (count($product_images))
  365. $this->context->smarty->assign('images', $product_images);
  366. }
  367. /**
  368. * Assign template vars related to attribute groups and colors
  369. */
  370. protected function assignAttributesGroups()
  371. {
  372. $colors = array();
  373. $groups = array();
  374. // @todo (RM) should only get groups and not all declination ?
  375. $attributes_groups = $this->product->getAttributesGroups($this->context->language->id);
  376. if (is_array($attributes_groups) && $attributes_groups)
  377. {
  378. $combination_images = $this->product->getCombinationImages($this->context->language->id);
  379. $combination_prices_set = array();
  380. foreach ($attributes_groups as $k => $row)
  381. {
  382. // Color management
  383. if (isset($row['is_color_group']) && $row['is_color_group'] && (isset($row['attribute_color']) && $row['attribute_color']) || (file_exists(_PS_COL_IMG_DIR_.$row['id_attribute'].'.jpg')))
  384. {
  385. $colors[$row['id_attribute']]['value'] = $row['attribute_color'];
  386. $colors[$row['id_attribute']]['name'] = $row['attribute_name'];
  387. if (!isset($colors[$row['id_attribute']]['attributes_quantity']))
  388. $colors[$row['id_attribute']]['attributes_quantity'] = 0;
  389. $colors[$row['id_attribute']]['attributes_quantity'] += (int)$row['quantity'];
  390. }
  391. if (!isset($groups[$row['id_attribute_group']]))
  392. $groups[$row['id_attribute_group']] = array(
  393. 'group_name' => $row['group_name'],
  394. 'name' => $row['public_group_name'],
  395. 'group_type' => $row['group_type'],
  396. 'default' => -1,
  397. );
  398. $groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']] = $row['attribute_name'];
  399. if ($row['default_on'] && $groups[$row['id_attribute_group']]['default'] == -1)
  400. $groups[$row['id_attribute_group']]['default'] = (int)$row['id_attribute'];
  401. if (!isset($groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']]))
  402. $groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']] = 0;
  403. $groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']] += (int)$row['quantity'];
  404. $combinations[$row['id_product_attribute']]['attributes_values'][$row['id_attribute_group']] = $row['attribute_name'];
  405. $combinations[$row['id_product_attribute']]['attributes'][] = (int)$row['id_attribute'];
  406. $combinations[$row['id_product_attribute']]['price'] = (float)$row['price'];
  407. // Call getPriceStatic in order to set $combination_specific_price
  408. if (!isset($combination_prices_set[(int)$row['id_product_attribute']]))
  409. {
  410. Product::getPriceStatic((int)$this->product->id, false, $row['id_product_attribute'], 6, null, false, true, 1, false, null, null, null, $combination_specific_price);
  411. $combination_prices_set[(int)$row['id_product_attribute']] = true;
  412. $combinations[$row['id_product_attribute']]['specific_price'] = $combination_specific_price;
  413. }
  414. $combinations[$row['id_product_attribute']]['ecotax'] = (float)$row['ecotax'];
  415. $combinations[$row['id_product_attribute']]['weight'] = (float)$row['weight'];
  416. $combinations[$row['id_product_attribute']]['quantity'] = (int)$row['quantity'];
  417. $combinations[$row['id_product_attribute']]['reference'] = $row['reference'];
  418. $combinations[$row['id_product_attribute']]['unit_impact'] = $row['unit_price_impact'];
  419. $combinations[$row['id_product_attribute']]['minimal_quantity'] = $row['minimal_quantity'];
  420. if ($row['available_date'] != '0000-00-00')
  421. {
  422. $combinations[$row['id_product_attribute']]['available_date'] = $row['available_date'];
  423. $combinations[$row['id_product_attribute']]['date_formatted'] = Tools::displayDate($row['available_date']);
  424. }
  425. else
  426. $combinations[$row['id_product_attribute']]['available_date'] = '';
  427. if (!isset($combination_images[$row['id_product_attribute']][0]['id_image']))
  428. $combinations[$row['id_product_attribute']]['id_image'] = -1;
  429. else
  430. {
  431. $combinations[$row['id_product_attribute']]['id_image'] = $id_image = (int)$combination_images[$row['id_product_attribute']][0]['id_image'];
  432. if ($row['default_on'])
  433. {
  434. if (isset($this->context->smarty->tpl_vars['cover']->value))
  435. $current_cover = $this->context->smarty->tpl_vars['cover']->value;
  436. if (is_array($combination_images[$row['id_product_attribute']]))
  437. {
  438. foreach ($combination_images[$row['id_product_attribute']] as $tmp)
  439. if ($tmp['id_image'] == $current_cover['id_image'])
  440. {
  441. $combinations[$row['id_product_attribute']]['id_image'] = $id_image = (int)$tmp['id_image'];
  442. break;
  443. }
  444. }
  445. if ($id_image > 0)
  446. {
  447. if (isset($this->context->smarty->tpl_vars['images']->value))
  448. $product_images = $this->context->smarty->tpl_vars['images']->value;
  449. if (isset($product_images) && is_array($product_images) && isset($product_images[$id_image]))
  450. {
  451. $product_images[$id_image]['cover'] = 1;
  452. $this->context->smarty->assign('mainImage', $product_images[$id_image]);
  453. if (count($product_images))
  454. $this->context->smarty->assign('images', $product_images);
  455. }
  456. if (isset($this->context->smarty->tpl_vars['cover']->value))
  457. $cover = $this->context->smarty->tpl_vars['cover']->value;
  458. if (isset($cover) && is_array($cover) && isset($product_images) && is_array($product_images))
  459. {
  460. $product_images[$cover['id_image']]['cover'] = 0;
  461. if (isset($product_images[$id_image]))
  462. $cover = $product_images[$id_image];
  463. $cover['id_image'] = (Configuration::get('PS_LEGACY_IMAGES') ? ($this->product->id.'-'.$id_image) : (int)$id_image);
  464. $cover['id_image_only'] = (int)$id_image;
  465. $this->context->smarty->assign('cover', $cover);
  466. }
  467. }
  468. }
  469. }
  470. }
  471. // wash attributes list (if some attributes are unavailables and if allowed to wash it)
  472. if (!Product::isAvailableWhenOutOfStock($this->product->out_of_stock) && Configuration::get('PS_DISP_UNAVAILABLE_ATTR') == 0)
  473. {
  474. foreach ($groups as &$group)
  475. foreach ($group['attributes_quantity'] as $key => &$quantity)
  476. if ($quantity <= 0)
  477. unset($group['attributes'][$key]);
  478. foreach ($colors as $key => $color)
  479. if ($color['attributes_quantity'] <= 0)
  480. unset($colors[$key]);
  481. }
  482. foreach ($combinations as $id_product_attribute => $comb)
  483. {
  484. $attribute_list = '';
  485. foreach ($comb['attributes'] as $id_attribute)
  486. $attribute_list .= '\''.(int)$id_attribute.'\',';
  487. $attribute_list = rtrim($attribute_list, ',');
  488. $combinations[$id_product_attribute]['list'] = $attribute_list;
  489. }
  490. $this->context->smarty->assign(array(
  491. 'groups' => $groups,
  492. 'colors' => (count($colors)) ? $colors : false,
  493. 'combinations' => $combinations,
  494. 'combinationImages' => $combination_images
  495. ));
  496. }
  497. }
  498. /**
  499. * Get and assign attributes combinations informations
  500. */
  501. protected function assignAttributesCombinations()
  502. {
  503. $attributes_combinations = Product::getAttributesInformationsByProduct($this->product->id);
  504. if (is_array($attributes_combinations) && count($attributes_combinations))
  505. foreach ($attributes_combinations as &$ac)
  506. foreach ($ac as &$val)
  507. $val = str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite(str_replace(array(',', '.'), '-', $val)));
  508. else
  509. $attributes_combinations = array();
  510. $this->context->smarty->assign(array(
  511. 'attributesCombinations' => $attributes_combinations,
  512. 'attribute_anchor_separator' => Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR')
  513. )
  514. );
  515. }
  516. /**
  517. * Assign template vars related to category
  518. */
  519. protected function assignCategory()
  520. {
  521. // Assign category to the template
  522. if ($this->category !== false && Validate::isLoadedObject($this->category) && $this->category->inShop() && $this->category->isAssociatedToShop())
  523. $path = Tools::getPath($this->category->id, $this->product->name, true);
  524. elseif (Category::inShopStatic($this->product->id_category_default, $this->context->shop))
  525. {
  526. $this->category = new Category((int)$this->product->id_category_default, (int)$this->context->language->id);
  527. if (Validate::isLoadedObject( $this->category) && $this->category->active && $this->category->isAssociatedToShop())
  528. $path = Tools::getPath((int)$this->product->id_category_default, $this->product->name);
  529. }
  530. if (!isset($path) || !$path)
  531. $path = Tools::getPath((int)$this->context->shop->id_category, $this->product->name);
  532. $subCategories = array();
  533. if (Validate::isLoadedObject($this->category))
  534. {
  535. $subCategories = $this->category->getSubCategories($this->context->language->id, true);
  536. // various assignements before Hook::exec
  537. $this->context->smarty->assign(array(
  538. 'path' => $path,
  539. 'category' => $this->category,
  540. 'subCategories' => $subCategories,
  541. 'id_category_current' => (int)$this->category->id,
  542. 'id_category_parent' => (int)$this->category->id_parent,
  543. 'return_category_name' => Tools::safeOutput($this->category->getFieldByLang('name')),
  544. 'categories' => Category::getHomeCategories($this->context->language->id, true, (int)$this->context->shop->id)
  545. ));
  546. }
  547. $this->context->smarty->assign(array('HOOK_PRODUCT_FOOTER' => Hook::exec('displayFooterProduct', array('product' => $this->product, 'category' => $this->category))));
  548. }
  549. protected function transformDescriptionWithImg($desc)
  550. {
  551. $reg = '/\[img\-([0-9]+)\-(left|right)\-([a-zA-Z0-9-_]+)\]/';
  552. while (preg_match($reg, $desc, $matches))
  553. {
  554. $link_lmg = $this->context->link->getImageLink($this->product->link_rewrite, $this->product->id.'-'.$matches[1], $matches[3]);
  555. $class = $matches[2] == 'left' ? 'class="imageFloatLeft"' : 'class="imageFloatRight"';
  556. $html_img = '<img src="'.$link_lmg.'" alt="" '.$class.'/>';
  557. $desc = str_replace($matches[0], $html_img, $desc);
  558. }
  559. return $desc;
  560. }
  561. protected function pictureUpload()
  562. {
  563. if (!$field_ids = $this->product->getCustomizationFieldIds())
  564. return false;
  565. $authorized_file_fields = array();
  566. foreach ($field_ids as $field_id)
  567. if ($field_id['type'] == Product::CUSTOMIZE_FILE)
  568. $authorized_file_fields[(int)$field_id['id_customization_field']] = 'file'.(int)$field_id['id_customization_field'];
  569. $indexes = array_flip($authorized_file_fields);
  570. foreach ($_FILES as $field_name => $file)
  571. if (in_array($field_name, $authorized_file_fields) && isset($file['tmp_name']) && !empty($file['tmp_name']))
  572. {
  573. $file_name = md5(uniqid(rand(), true));
  574. if ($error = ImageManager::validateUpload($file, (int)Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE')))
  575. $this->errors[] = $error;
  576. $product_picture_width = (int)Configuration::get('PS_PRODUCT_PICTURE_WIDTH');
  577. $product_picture_height = (int)Configuration::get('PS_PRODUCT_PICTURE_HEIGHT');
  578. $tmp_name = tempnam(_PS_TMP_IMG_DIR_, 'PS');
  579. if ($error || (!$tmp_name || !move_uploaded_file($file['tmp_name'], $tmp_name)))
  580. return false;
  581. /* Original file */
  582. if (!ImageManager::resize($tmp_name, _PS_UPLOAD_DIR_.$file_name))
  583. $this->errors[] = Tools::displayError('An error occurred during the image upload process.');
  584. /* A smaller one */
  585. elseif (!ImageManager::resize($tmp_name, _PS_UPLOAD_DIR_.$file_name.'_small', $product_picture_width, $product_picture_height))
  586. $this->errors[] = Tools::displayError('An error occurred during the image upload process.');
  587. elseif (!chmod(_PS_UPLOAD_DIR_.$file_name, 0777) || !chmod(_PS_UPLOAD_DIR_.$file_name.'_small', 0777))
  588. $this->errors[] = Tools::displayError('An error occurred during the image upload process.');
  589. else
  590. $this->context->cart->addPictureToProduct($this->product->id, $indexes[$field_name], Product::CUSTOMIZE_FILE, $file_name);
  591. unlink($tmp_name);
  592. }
  593. return true;
  594. }
  595. protected function textRecord()
  596. {
  597. if (!$field_ids = $this->product->getCustomizationFieldIds())
  598. return false;
  599. $authorized_text_fields = array();
  600. foreach ($field_ids as $field_id)
  601. if ($field_id['type'] == Product::CUSTOMIZE_TEXTFIELD)
  602. $authorized_text_fields[(int)$field_id['id_customization_field']] = 'textField'.(int)$field_id['id_customization_field'];
  603. $indexes = array_flip($authorized_text_fields);
  604. foreach ($_POST as $field_name => $value)
  605. if (in_array($field_name, $authorized_text_fields) && $value != '')
  606. {
  607. if (!Validate::isMessage($value))
  608. $this->errors[] = Tools::displayError('Invalid message');
  609. else
  610. $this->context->cart->addTextFieldToProduct($this->product->id, $indexes[$field_name], Product::CUSTOMIZE_TEXTFIELD, $value);
  611. }
  612. else if (in_array($field_name, $authorized_text_fields) && $value == '')
  613. $this->context->cart->deleteCustomizationToProduct((int)$this->product->id, $indexes[$field_name]);
  614. }
  615. protected function formTargetFormat()
  616. {
  617. $customization_form_target = Tools::safeOutput(urldecode($_SERVER['REQUEST_URI']));
  618. foreach ($_GET as $field => $value)
  619. if (strncmp($field, 'group_', 6) == 0)
  620. $customization_form_target = preg_replace('/&group_([[:digit:]]+)=([[:digit:]]+)/', '', $customization_form_target);
  621. if (isset($_POST['quantityBackup']))
  622. $this->context->smarty->assign('quantityBackup', (int)$_POST['quantityBackup']);
  623. $this->context->smarty->assign('customizationFormTarget', $customization_form_target);
  624. }
  625. protected function formatQuantityDiscounts($specific_prices, $price, $tax_rate, $ecotax_amount)
  626. {
  627. foreach ($specific_prices as $key => &$row)
  628. {
  629. $row['quantity'] = &$row['from_quantity'];
  630. if ($row['price'] >= 0) // The price may be directly set
  631. {
  632. $cur_price = (Product::$_taxCalculationMethod == PS_TAX_EXC ? $row['price'] : $row['price'] * (1 + $tax_rate / 100)) + (float)$ecotax_amount;
  633. if ($row['reduction_type'] == 'amount')
  634. $cur_price -= (Product::$_taxCalculationMethod == PS_TAX_INC ? $row['reduction'] : $row['reduction'] / (1 + $tax_rate / 100));
  635. else
  636. $cur_price *= 1 - $row['reduction'];
  637. $row['real_value'] = $price - $cur_price;
  638. }
  639. else
  640. {
  641. if ($row['reduction_type'] == 'amount')
  642. $row['real_value'] = Product::$_taxCalculationMethod == PS_TAX_INC ? $row['reduction'] : $row['reduction'] / (1 + $tax_rate / 100);
  643. else
  644. $row['real_value'] = $row['reduction'] * 100;
  645. }
  646. $row['nextQuantity'] = (isset($specific_prices[$key + 1]) ? (int)$specific_prices[$key + 1]['from_quantity'] : -1);
  647. }
  648. return $specific_prices;
  649. }
  650. public function getProduct()
  651. {
  652. return $this->product;
  653. }
  654. public function getCategory()
  655. {
  656. return $this->category;
  657. }
  658. }