* @copyright 2013 - 2017 Patryk Marek * @link http://prestadev.pl * @package Product attributes list for PrestaShop 1.5.x and 1.6.x and 1.7.x * @version 1.0.3 * @license Do not edit, modify or copy this file, if you wish to customize it, contact us at info@prestadev.pl * @date 28-10-2017 * */ if (!defined('_PS_VERSION_')) { exit; } class PdProductAttributesList extends Module { private $html = ''; private $postErrors = array(); public function __construct() { $this->name = 'pdproductattributeslist'; $this->tab = 'front_office_features'; $this->version = '2.0.0'; $this->author = 'PrestaDev.pl'; $this->need_instance = 0; $this->module_key = '5f46066c31c75e2acfdf9483396e7ar6'; $this->secure_key = Tools::encrypt($this->name); $this->bootstrap = true; parent::__construct(); $this->displayName = $this->l('Product attributes list on product page'); $this->description = $this->l('Allow to display product attributes list on product page as a tab or in product footer hook'); $this->ps_version_17 = (version_compare(substr(_PS_VERSION_, 0, 3), '1.7', '=')) ? true : false; $this->ps_version_8 = (version_compare(substr(_PS_VERSION_, 0, 3), '8.0', '>=')) ? true : false; } public function install() { if (!parent::install() || !$this->registerHook('displayProductTab') || !$this->registerHook('displayHeader') || !$this->registerHook('displayFooterProduct') || !$this->registerHook('displayProductTabContent') || !$this->registerHook('displayProductExtraContent') || !$this->registerHook('displayCustomAttributesListTable') || !Configuration::updateValue('PD_PAL_ONLY_IN_STOCK', 0) || !Configuration::updateValue('PD_PAL_DISPLAY_T_HEADING', 1) || !Configuration::updateValue('PD_PAL_DISPLAY_B_HEADING', 1) || !Configuration::updateValue('PD_PAL_SHOW_ATTRIBUT_TETEXTURE_IMG', 1) || !Configuration::updateValue('PD_PAL_DISPLAY_LAYOUT', 0) || !Configuration::updateValue('PD_PAL_PLACEMENT', 1)) { return false; } return true; } public function uninstall() { if (!parent::uninstall()) { return false; } return true; } /* ** Form Config Methods ** */ public function getContent() { if (Tools::isSubmit('btnSubmit')) { $this->postValidation(); if (!count($this->postErrors)) { $this->postProcess(); } else { foreach ($this->postErrors as $err) { $this->html .= $this->displayError($err); } } } else { $this->html .= '
'; } $this->html .= '

'.$this->displayName.' (v'.$this->version.')

'.$this->description.'

'; $this->html .= $this->renderForm(); $this->html .= '
'; $this->html .= $this->displayExtraForm(); return $this->html; } public function renderForm() { $switch = version_compare(_PS_VERSION_, '1.6.0', '>=') ? 'switch' : 'radio'; $fields_form_1 = array( 'form' => array( 'legend' => array( 'title' => $this->l('Module Configuration'), 'icon' => 'icon-cogs' ), 'input' => array( array( 'type' => $switch, 'label' => $this->l('Display block heading'), 'desc' => $this->l('Displays block heading'), 'class' => 't', 'name' => 'PD_PAL_DISPLAY_B_HEADING', 'values' => array( array( 'id' => 'yes', 'value' => 1, 'label' => $this->l('Yes') ), array( 'id' => 'no', 'value' => 0, 'label' => $this->l('No') ), ) ), array( 'type' => 'radio', 'label' => $this->l('Display layout'), 'desc' => $this->l('Display layout of product combinations'), 'class' => 't', 'name' => 'PD_PAL_DISPLAY_LAYOUT', 'values' => array( array( 'id' => '0', 'value' => 0, 'label' => $this->l('Table') ), array( 'id' => '1', 'value' => 1, 'label' => $this->l('Grid') ) ) ), array( 'type' => 'radio', 'label' => $this->l('Table placement'), 'desc' => $this->l('Displays table in product page tab or custom hook in product page (see instructions bellow)'), 'class' => 't', 'name' => 'PD_PAL_PLACEMENT', 'values' => array( array( 'id' => '0', 'value' => 0, 'label' => $this->l('Custom hook (displayCustomAttributesListTable)') ), array( 'id' => '1', 'value' => 1, 'label' => $this->l('Product tab') ), array( 'id' => '2', 'value' => 2, 'label' => $this->l('Product footer') ), ) ), ), 'submit' => array( 'title' => $this->l('Save settings'), ) ), ); $helper = new HelperForm(); $helper->module = $this; $helper->show_toolbar = false; $helper->table = $this->table; $lang = new Language((int)Configuration::get('PS_LANG_DEFAULT')); $helper->default_form_language = $lang->id; $helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') ? Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') : 0; $this->fields_form = array(); $helper->identifier = $this->identifier; $helper->submit_action = 'btnSubmit'; $helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false).'&configure='.$this->name.'&tab_module='.$this->tab.'&module_name='.$this->name; $helper->token = Tools::getAdminTokenLite('AdminModules'); $helper->tpl_vars = array( 'fields_value' => $this->getConfigFieldsValues(), 'languages' => $this->context->controller->getLanguages(), 'id_language' => $this->context->language->id ); return $helper->generateForm(array($fields_form_1)); } private function displayExtraForm() { $this->html .= '
'.$this->l('Instalation instructions for custom hook table').'
'.$this->l('Please open file on which You want to add module').' '.$this->l('for editing and add in place where you want to display table code:').'
'; } public function getConfigFieldsValues() { $return = array(); $return['PD_PAL_DISPLAY_T_HEADING'] = Tools::getValue('PD_PAL_DISPLAY_T_HEADING', Configuration::get('PD_PAL_DISPLAY_T_HEADING')); $return['PD_PAL_DISPLAY_B_HEADING'] = Tools::getValue('PD_PAL_DISPLAY_B_HEADING', Configuration::get('PD_PAL_DISPLAY_B_HEADING')); $return['PD_PAL_PLACEMENT'] = Configuration::get('PD_PAL_PLACEMENT'); $return['PD_PAL_ONLY_IN_STOCK'] = Configuration::get('PD_PAL_ONLY_IN_STOCK'); $return['PD_PAL_DISPLAY_LAYOUT'] = Configuration::get('PD_PAL_DISPLAY_LAYOUT'); $return['PD_PAL_SHOW_ATTRIBUT_TETEXTURE_IMG'] = Configuration::get('PD_PAL_SHOW_ATTRIBUT_TETEXTURE_IMG'); return $return; } private function postValidation() { } private function postProcess() { Configuration::updateValue('PD_PAL_DISPLAY_B_HEADING', Tools::getValue('PD_PAL_DISPLAY_B_HEADING')); Configuration::updateValue('PD_PAL_DISPLAY_T_HEADING', Tools::getValue('PD_PAL_DISPLAY_T_HEADING')); Configuration::updateValue('PD_PAL_PLACEMENT', Tools::getValue('PD_PAL_PLACEMENT')); Configuration::updateValue('PD_PAL_ONLY_IN_STOCK', Tools::getValue('PD_PAL_ONLY_IN_STOCK')); Configuration::updateValue('PD_PAL_DISPLAY_LAYOUT', Tools::getValue('PD_PAL_DISPLAY_LAYOUT')); Configuration::updateValue('PD_PAL_SHOW_ATTRIBUT_TETEXTURE_IMG', Tools::getValue('PD_PAL_SHOW_ATTRIBUT_TETEXTURE_IMG')); $this->html .= $this->displayConfirmation($this->l('Settings updated')); } public function hookDisplayHeader($params) { if (!empty(Context::getContext()->controller->php_self)) { $controller = Context::getContext()->controller->php_self; } else { $controller = Tools::getValue('controller'); } if ($controller == 'product' || $controller == 'category') { $this->context->controller->addJquery(); $this->context->controller->addJqueryPlugin('fancybox'); $this->context->controller->addJqueryPlugin('growl', null, false); $this->context->controller->registerStylesheet('growl-css', 'js/jquery/plugins/growl/jquery.growl.css'); Media::addJsDef(array( 'pdproductattributeslist_add_ok' => $this->l(' was added!'), 'pdproductattributeslist_title_error' => $this->l('Error'), 'pdproductattributeslist_title_ok' => $this->l('Success'), 'pdproductattributeslist_pcs' => $this->l('pcs.'), 'pdproductattributeslist_product' => $this->l('Product: '), 'pdproductattributeslist_variant' => $this->l('in variant:'), 'pdproductattributeslist_max_qty' => $this->l(' max quantity is: '), 'pdproductattributeslist_add_error' => $this->l('There no products quantities selected!'), 'pdproductattributeslist_secure_key' => $this->secure_key, 'pdproductattributeslist_ajax_link' => $this->context->link->getModuleLink('pdproductattributeslist', 'ajax', array()), )); } $this->context->controller->registerStylesheet('modules-pdproductattributeslist-front', 'modules/'.$this->name.'/views/css/pdproductattributeslist.css', array('media' => 'all', 'priority' => 150)); $this->context->controller->registerStylesheet('modules-pdproductattributeslist-front-tablesorter', 'modules/'.$this->name.'/views/css/theme.default.min.css', array('media' => 'all', 'priority' => 150)); $this->context->controller->registerStylesheet('modules-pdproductattributeslist-front-tablesorter-bootstrap-3', 'modules/'.$this->name.'/views/css/theme.bootstrap_3.min.css', array('media' => 'all', 'priority' => 150)); $this->context->controller->registerStylesheet('modules-pdproductattributeslist-front-tablesorter-bootstrap-4', 'modules/'.$this->name.'/views/css/theme.bootstrap_4.min.css', array('media' => 'all', 'priority' => 150)); $this->context->controller->registerStylesheet('modules-pdproductattributeslist-front-tablesorter-bootstrap', 'modules/'.$this->name.'/views/css/theme.bootstrap.min.css', array('media' => 'all', 'priority' => 150)); $this->context->controller->registerJavascript('modules-pdproductattributeslist-tablesorter', 'modules/'.$this->name.'/views/js/jquery.tablesorter.min.js', array('position' => 'bottom', 'priority' => 150)); $this->context->controller->registerJavascript('modules-pdproductattributeslist-tablesorter-widgets', 'modules/'.$this->name.'/views/js/jquery.tablesorter.widgets.min.js', array('position' => 'bottom', 'priority' => 150)); $this->context->controller->registerJavascript('modules-pdproductattributeslist', 'modules/'.$this->name.'/views/js/pdproductattributeslist.js', array('position' => 'bottom', 'priority' => 150)); } public function hookDisplayProductExtraContent($params) { $placement = Configuration::get('PD_PAL_PLACEMENT'); $product = $this->context->controller->getProduct(); if ($product->hasAttributes() == 0) { return array(); } if (($this->ps_version_17 || $this->ps_version_8) && $placement == 1 && Tools::getValue('ajax', 'false') == 'false') { $tabz[] = (new PrestaShop\PrestaShop\Core\Product\ProductExtraContent())->setTitle($this->l('Select product combination'))->setContent($this->hookDisplayProductTabContent($params)); return $tabz; } else { return array(); } } public function hookDisplayProductTabContent($params) { $placement = Configuration::get('PD_PAL_PLACEMENT'); $display_layout = Configuration::get('PD_PAL_DISPLAY_LAYOUT'); if ($placement == 1) { $id_lang = (int)$this->context->language->id; $id_shop = (int)$this->context->shop->id; $product = $this->context->controller->getProduct(); $product_combinations = $this->getAttributeCombinations($product->id, $id_lang, $id_shop); //dump($product_combinations); $this->context->smarty->assign(array( 'product_combinations' => $product_combinations, 'block_heading'=> Configuration::get('PD_PAL_DISPLAY_B_HEADING'), 'cartSize' => Image::getSize(ImageType::getFormattedName('cart')), 'homeSize' => Image::getSize(ImageType::getFormattedName('home')), 'is_catalog' => (bool) Configuration::isCatalogMode(), 'show_prices' => (bool) Configuration::showPrices(), 'ps_version_17' => $this->ps_version_17, 'ps_version_8' => $this->ps_version_8 )); if ($display_layout == 0) { return $this->display(__FILE__, 'hookDisplayProductTabContent.tpl'); } else { return $this->display(__FILE__, 'hookDisplayProductTabContentGrid.tpl'); } } } public function hookDisplayProductTab($params) { $placement = Configuration::get('PD_PAL_PLACEMENT'); if ($placement == 1) { $context = Context::getContext(); $cart = new Cart((int)$context->cart->id); $product = $this->context->controller->getProduct(); $this->context->smarty->assign(array( 'ps_version_15' => $this->ps_version_15, 'ps_version_16' => $this->ps_version_16, 'ps_version_17' => $this->ps_version_17 )); return $this->display(__FILE__, 'hookDisplayProductTab.tpl'); } } public function hookDisplayFooterProduct($params) { $placement = Configuration::get('PD_PAL_PLACEMENT'); $display_layout = Configuration::get('PD_PAL_DISPLAY_LAYOUT'); if ($placement == 2) { $id_lang = (int)$this->context->language->id; $id_shop = (int)$this->context->shop->id; $product = $this->context->controller->getProduct(); $product_combinations = $this->getAttributeCombinations($product->id, $id_lang, $id_shop); //dump($product_combinations); $this->context->smarty->assign(array( 'product_combinations' => $product_combinations, 'block_heading'=> Configuration::get('PD_PAL_DISPLAY_B_HEADING'), 'cartSize' => Image::getSize(ImageType::getFormattedName('cart')), 'homeSize' => Image::getSize(ImageType::getFormattedName('home')), 'is_catalog' => (bool) Configuration::isCatalogMode(), 'show_prices' => (bool) Configuration::showPrices(), 'ps_version_17' => $this->ps_version_17, 'ps_version_8' => $this->ps_version_8 )); if ($display_layout == 0) { return $this->display(__FILE__, 'hookDisplayProductTabContent.tpl'); } else { return $this->display(__FILE__, 'hookDisplayProductTabContentGrid.tpl'); } } } public function hookDisplayCustomAttributesListTable($params) { $id_product = (int)$params['product']->id; $product = new Product($id_product); $id_lang = (int)$this->context->language->id; $id_shop = (int)$this->context->shop->id; $product_combinations = $this->getAttributeCombinations($id_product, $id_lang, $id_shop); $this->context->smarty->assign(array( 'product_combinations' => $product_combinations, 'block_heading'=> Configuration::get('PD_PAL_DISPLAY_B_HEADING'), 'cartSize' => Image::getSize(ImageType::getFormattedName('cart')), 'homeSize' => Image::getSize(ImageType::getFormattedName('home')), 'is_catalog' => (bool) Configuration::isCatalogMode(), 'show_prices' => (bool) Configuration::showPrices(), 'ps_version_17' => $this->ps_version_17, 'ps_version_8' => $this->ps_version_8 )); return $this->display(__FILE__, 'hookDisplayCustomAttributesListTable.tpl'); } private function getAttributeCombinations($id_product, $id_lang, $id_shop) { $sql = 'SELECT pa.`id_product_attribute`, pa.`id_product`, pa.`reference`, pa.`ean13`, pa.`mpn`, pas.`weight` as weight_impact, p.`weight` as weight_base, pa.`minimal_quantity`, ag.`id_attribute_group`, ag.`is_color_group`, agl.`public_name` AS group_name, al.`name` AS attribute_name, al.`name` AS attribute_name_html, a.`id_attribute`, a.`color` FROM `'._DB_PREFIX_.'product_attribute` pa INNER JOIN `'._DB_PREFIX_.'product_attribute_shop` pas ON (pas.id_product_attribute = pa.id_product_attribute AND pas.id_shop = '.(int)$id_shop.') LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute` a ON a.`id_attribute` = pac.`id_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group` LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'product` p ON (p.`id_product` = '.(int)$id_product.') WHERE pa.`id_product` = '.(int)$id_product.' ORDER BY pa.`id_product_attribute`'; $results = Db::getInstance()->ExecuteS($sql); $return = array(); $product = new Product($id_product, false, $id_lang, $id_shop); $context = Context::getContext(); $specific_price_output = null; $only_in_stock = Configuration::get('PD_PAL_ONLY_IN_STOCK'); if (is_array($results) && count($results) > 1) { foreach ($results as $k => $r) { if (!isset($return[$r['id_product_attribute']]['attribute_name'])) { $return[$r['id_product_attribute']]['attribute_name'] = ''; } if (!isset($return[$r['id_product_attribute']]['attribute_name_html'])) { $return[$r['id_product_attribute']]['attribute_name_html'] = ''; } $return[$r['id_product_attribute']]['id_attribute'] = $r['id_attribute']; $return[$r['id_product_attribute']]['color'] = $r['color']; $return[$r['id_product_attribute']]['id_product'] = $id_product; $return[$r['id_product_attribute']]['id_product_attribute'] = $r['id_product_attribute']; $return[$r['id_product_attribute']]['attribute_name'] .= rtrim(Tools::ucfirst($r['group_name']).': '.Tools::ucfirst($r['attribute_name']).', ', ','); $return[$r['id_product_attribute']]['attribute_name_html'] .= ''.$r['attribute_name'].'
'; $return[$r['id_product_attribute']]['reference'] = $r['reference'] ? $r['reference'] : ''; $return[$r['id_product_attribute']]['ean13'] = $r['ean13'] ? $r['ean13'] : ''; $return[$r['id_product_attribute']]['mpn'] = $r['mpn'] ? $r['mpn'] : ''; $return[$r['id_product_attribute']]['minimal_quantity'] = $r['minimal_quantity']; $return[$r['id_product_attribute']]['price'] = Product::getPriceStatic( (int)$id_product, true, (int)$r['id_product_attribute'], 2, null, false, true, 1, false, null, null, null, $specific_price_output, true, true, $context, true ); $return[$r['id_product_attribute']]['price_tax_excl'] = Product::getPriceStatic( (int)$id_product, false, (int)$r['id_product_attribute'], 2, null, false, true, 1, false, null, null, null, $specific_price_output, true, true, $context, true ); $return[$r['id_product_attribute']]['price_old'] = Product::getPriceStatic( (int)$id_product, true, (int)$r['id_product_attribute'], 2, null, false, false, 1, false, null, null, null, $specific_price_output, true, true, $context, true ); $return[$r['id_product_attribute']]['price_old_tax_excl'] = Product::getPriceStatic( (int)$id_product, false, (int)$r['id_product_attribute'], 2, null, false, false, 1, false, null, null, null, $specific_price_output, true, true, $context, true ); $return[$r['id_product_attribute']]['quantity'] = StockAvailable::getQuantityAvailableByProduct($id_product, (int)$r['id_product_attribute'], $id_shop); $return[$r['id_product_attribute']]['images'] = Image::getBestImageAttribute($id_shop, $id_lang, $id_product, (int)$r['id_product_attribute']); if ((!isset($return[$r['id_product_attribute']]['color']) && Tools::isEmpty($return[$r['id_product_attribute']]['color'])) && (file_exists(_PS_COL_IMG_DIR_.$r['id_attribute'].'.jpg'))) { $return[$r['id_product_attribute']]['color_texture_link'] = _THEME_COL_DIR_.$r['id_attribute'].'.jpg'; } else { $return[$r['id_product_attribute']]['color_texture_link'] = ''; } $return[$r['id_product_attribute']]['combination_link'] = $context->link->getProductLink( $product, $product->link_rewrite, null, null, $context->language->id, $context->shop->id, $r['id_product_attribute'] ); } foreach ($return as &$r) { $r['product_name'] = $product->name; $r['attribute_name'] = rtrim($r['attribute_name'], ', '); $r['name'] = rtrim((string)$r['attribute_name'], ', '); // if ($only_in_stock && $return[$r['id_product_attribute']]['quantity'] <= 0) { // unset($r['id_product_attribute']); // } $images = Image::getImages($id_lang, $id_product, $r['id_product_attribute'], $id_shop); if (!$images) { $images = Image::getImages($id_lang, $id_product, null, $id_shop); } foreach ($images as &$i) { $i['large_default'] = $context->link->getImageLink($product->link_rewrite, $i['id_image'], 'large_default'); $i['home_default'] = $context->link->getImageLink($product->link_rewrite, $i['id_image'], 'home_default'); } $r['images'] = $images; } } else { $images = Image::getImages($id_lang, $id_product, null, $id_shop); foreach ($images as &$i) { $i['large_default'] = $context->link->getImageLink($product->link_rewrite, $i['id_image'], 'large_default'); $i['home_default'] = $context->link->getImageLink($product->link_rewrite, $i['id_image'], 'home_default'); } $return[0] = [ 'product_name' => $product->name, 'name' => $product->name, 'attribute_name' => $this->l('--'), 'attribute_name_html' => $this->l('--'), 'images' => $images, 'reference' => $product->reference, 'ean13' => $product->ean13, 'mpn' => $product->mpn, 'id_product' => $product->id, 'id_product_attribute' => 0, 'minimal_quantity' => '', 'price' => Product::getPriceStatic( (int)$id_product, true, 0, 2, null, false, true, 1, false, null, null, null, $specific_price_output, true, true, $context, true ), 'price_tax_excl' => Product::getPriceStatic( (int)$id_product, false, 0, 2, null, false, true, 1, false, null, null, null, $specific_price_output, true, true, $context, true ), 'price_old' => Product::getPriceStatic( (int)$id_product, true, 0, 2, null, false, false, 1, false, null, null, null, $specific_price_output, true, true, $context, true ), 'price_old_tax_excl' => Product::getPriceStatic( (int)$id_product, false, 0, 2, null, false, false, 1, false, null, null, null, $specific_price_output, true, true, $context, true ), 'quantity' => StockAvailable::getQuantityAvailableByProduct($id_product, 0, $id_shop), ]; } return $return; } }