Add Custom Feature Tab module with database integration and AJAX support
- Created main module file `customfeaturetab.php` to manage product tabs based on feature values. - Implemented database installation and uninstallation methods to create necessary tables. - Added admin controller files for handling redirects and admin functionalities. - Introduced AJAX functionality in `admin.js` for dynamic feature value selection based on selected features. - Included temporary query script for testing feature values. - Added language support for the module with Polish translations. - Created necessary view files and JavaScript files for module functionality. - Added logo image for the module.
This commit is contained in:
10
.claude/settings.local.json
Normal file
10
.claude/settings.local.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(head:*)",
|
||||
"Bash(cp:*)",
|
||||
"Bash(cd:*)",
|
||||
"Bash(curl:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
3
.vscode/ftp-kr.json
vendored
3
.vscode/ftp-kr.json
vendored
@@ -12,6 +12,7 @@
|
||||
"ignoreRemoteModification": true,
|
||||
"ignore": [
|
||||
".git",
|
||||
"/.vscode"
|
||||
"/.vscode",
|
||||
"/.claude"
|
||||
]
|
||||
}
|
||||
99
modules/customfeaturetab/classes/CustomFeatureTabRule.php
Normal file
99
modules/customfeaturetab/classes/CustomFeatureTabRule.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Project-Pro <https://www.project-pro.pl>
|
||||
* @copyright Project-Pro
|
||||
* @license Proprietary - paid license
|
||||
*/
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class CustomFeatureTabRule extends ObjectModel
|
||||
{
|
||||
public $id_feature;
|
||||
public $id_feature_value;
|
||||
public $position;
|
||||
public $active;
|
||||
public $title;
|
||||
public $content;
|
||||
public $date_add;
|
||||
public $date_upd;
|
||||
|
||||
public static $definition = array(
|
||||
'table' => 'custom_feature_tab',
|
||||
'primary' => 'id_custom_feature_tab',
|
||||
'multilang' => true,
|
||||
'fields' => array(
|
||||
'id_feature' => array(
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedId',
|
||||
'required' => true,
|
||||
),
|
||||
'id_feature_value' => array(
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedId',
|
||||
'required' => true,
|
||||
),
|
||||
'position' => array(
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedInt',
|
||||
),
|
||||
'active' => array(
|
||||
'type' => self::TYPE_BOOL,
|
||||
'validate' => 'isBool',
|
||||
),
|
||||
'date_add' => array(
|
||||
'type' => self::TYPE_DATE,
|
||||
'validate' => 'isDate',
|
||||
),
|
||||
'date_upd' => array(
|
||||
'type' => self::TYPE_DATE,
|
||||
'validate' => 'isDate',
|
||||
),
|
||||
// Lang fields
|
||||
'title' => array(
|
||||
'type' => self::TYPE_STRING,
|
||||
'lang' => true,
|
||||
'validate' => 'isGenericName',
|
||||
'required' => true,
|
||||
'size' => 255,
|
||||
),
|
||||
'content' => array(
|
||||
'type' => self::TYPE_HTML,
|
||||
'lang' => true,
|
||||
'validate' => 'isCleanHtml',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Get active rules matching product features.
|
||||
*
|
||||
* @param array $productFeatures Array from Product::getFeaturesStatic
|
||||
* @param int $idLang Language ID
|
||||
* @return array
|
||||
*/
|
||||
public static function getMatchingRules(array $productFeatures, $idLang)
|
||||
{
|
||||
if (empty($productFeatures)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$conditions = array();
|
||||
foreach ($productFeatures as $feat) {
|
||||
$conditions[] = '(' . (int) $feat['id_feature'] . ', ' . (int) $feat['id_feature_value'] . ')';
|
||||
}
|
||||
|
||||
$sql = 'SELECT cft.*, cftl.`title`, cftl.`content`
|
||||
FROM `' . _DB_PREFIX_ . 'custom_feature_tab` cft
|
||||
LEFT JOIN `' . _DB_PREFIX_ . 'custom_feature_tab_lang` cftl
|
||||
ON cft.`id_custom_feature_tab` = cftl.`id_custom_feature_tab`
|
||||
AND cftl.`id_lang` = ' . (int) $idLang . '
|
||||
WHERE cft.`active` = 1
|
||||
AND (cft.`id_feature`, cft.`id_feature_value`) IN (' . implode(',', $conditions) . ')
|
||||
ORDER BY cft.`position` ASC, cft.`id_custom_feature_tab` ASC';
|
||||
|
||||
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
||||
}
|
||||
}
|
||||
8
modules/customfeaturetab/classes/index.php
Normal file
8
modules/customfeaturetab/classes/index.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ../');
|
||||
exit;
|
||||
12
modules/customfeaturetab/config.xml
Normal file
12
modules/customfeaturetab/config.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<module>
|
||||
<name>customfeaturetab</name>
|
||||
<displayName><![CDATA[Custom Feature Tab]]></displayName>
|
||||
<version><![CDATA[1.0.0]]></version>
|
||||
<description><![CDATA[Dodaje dodatkowe zakładki na karcie produktu w zależności od przypisanych cech produktu.]]></description>
|
||||
<author><![CDATA[Project-Pro]]></author>
|
||||
<tab>front_office_features</tab>
|
||||
<is_configurable>1</is_configurable>
|
||||
<need_instance>0</need_instance>
|
||||
<limited_countries></limited_countries>
|
||||
</module>
|
||||
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin controller for Custom Feature Tab rules.
|
||||
*
|
||||
* @author Project-Pro <https://www.project-pro.pl>
|
||||
* @copyright Project-Pro
|
||||
* @license Proprietary - paid license
|
||||
*/
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once _PS_MODULE_DIR_ . 'customfeaturetab/classes/CustomFeatureTabRule.php';
|
||||
|
||||
class AdminCustomFeatureTabController extends ModuleAdminController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->table = 'custom_feature_tab';
|
||||
$this->className = 'CustomFeatureTabRule';
|
||||
$this->identifier = 'id_custom_feature_tab';
|
||||
$this->lang = true;
|
||||
$this->bootstrap = true;
|
||||
$this->addRowAction('edit');
|
||||
$this->addRowAction('delete');
|
||||
$this->position_identifier = 'position';
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->bulk_actions = array(
|
||||
'delete' => array(
|
||||
'text' => $this->l('Delete selected'),
|
||||
'confirm' => $this->l('Delete selected items?'),
|
||||
'icon' => 'icon-trash',
|
||||
),
|
||||
);
|
||||
|
||||
$this->fields_list = array(
|
||||
'id_custom_feature_tab' => array(
|
||||
'title' => $this->l('ID'),
|
||||
'align' => 'center',
|
||||
'class' => 'fixed-width-xs',
|
||||
),
|
||||
'feature_name' => array(
|
||||
'title' => $this->l('Cecha'),
|
||||
'filter_key' => 'fl!name',
|
||||
),
|
||||
'feature_value_name' => array(
|
||||
'title' => $this->l('Wartość cechy'),
|
||||
'filter_key' => 'fvl!value',
|
||||
),
|
||||
'title' => array(
|
||||
'title' => $this->l('Tytuł zakładki'),
|
||||
'filter_key' => 'b!title',
|
||||
),
|
||||
'position' => array(
|
||||
'title' => $this->l('Pozycja'),
|
||||
'align' => 'center',
|
||||
'class' => 'fixed-width-xs',
|
||||
'position' => 'position',
|
||||
),
|
||||
'active' => array(
|
||||
'title' => $this->l('Aktywna'),
|
||||
'active' => 'status',
|
||||
'type' => 'bool',
|
||||
'align' => 'center',
|
||||
'class' => 'fixed-width-sm',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override getList to JOIN feature/feature_value names.
|
||||
*/
|
||||
public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false)
|
||||
{
|
||||
$this->_select = 'fl.`name` AS `feature_name`, fvl.`value` AS `feature_value_name`';
|
||||
$this->_join = '
|
||||
LEFT JOIN `' . _DB_PREFIX_ . 'feature_lang` fl
|
||||
ON (a.`id_feature` = fl.`id_feature` AND fl.`id_lang` = ' . (int) $id_lang . ')
|
||||
LEFT JOIN `' . _DB_PREFIX_ . 'feature_value_lang` fvl
|
||||
ON (a.`id_feature_value` = fvl.`id_feature_value` AND fvl.`id_lang` = ' . (int) $id_lang . ')';
|
||||
|
||||
parent::getList($id_lang, $order_by, $order_way, $start, $limit, $id_lang_shop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the add/edit form.
|
||||
*/
|
||||
public function renderForm()
|
||||
{
|
||||
$features = Feature::getFeatures($this->context->language->id);
|
||||
$featureOptions = array();
|
||||
foreach ($features as $feature) {
|
||||
$featureOptions[] = array(
|
||||
'id' => $feature['id_feature'],
|
||||
'name' => $feature['name'],
|
||||
);
|
||||
}
|
||||
|
||||
// For edit mode, get values of the currently selected feature
|
||||
$featureValueOptions = array();
|
||||
$selectedFeatureValue = 0;
|
||||
if ($this->object && $this->object->id) {
|
||||
$selectedFeatureValue = (int) $this->object->id_feature_value;
|
||||
$sql = 'SELECT fv.`id_feature_value`, fvl.`value`
|
||||
FROM `' . _DB_PREFIX_ . 'feature_value` fv
|
||||
LEFT JOIN `' . _DB_PREFIX_ . 'feature_value_lang` fvl
|
||||
ON fv.`id_feature_value` = fvl.`id_feature_value`
|
||||
AND fvl.`id_lang` = ' . (int) $this->context->language->id . '
|
||||
WHERE fv.`id_feature` = ' . (int) $this->object->id_feature . '
|
||||
ORDER BY fvl.`value` ASC';
|
||||
$values = Db::getInstance()->executeS($sql);
|
||||
if ($values) {
|
||||
foreach ($values as $val) {
|
||||
$featureValueOptions[] = array(
|
||||
'id' => $val['id_feature_value'],
|
||||
'name' => $val['value'],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->fields_form = array(
|
||||
'legend' => array(
|
||||
'title' => $this->l('Reguła karty cechy'),
|
||||
'icon' => 'icon-cogs',
|
||||
),
|
||||
'input' => array(
|
||||
array(
|
||||
'type' => 'select',
|
||||
'label' => $this->l('Cecha'),
|
||||
'name' => 'id_feature',
|
||||
'required' => true,
|
||||
'options' => array(
|
||||
'query' => $featureOptions,
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'type' => 'select',
|
||||
'label' => $this->l('Wartość cechy'),
|
||||
'name' => 'id_feature_value',
|
||||
'required' => true,
|
||||
'options' => array(
|
||||
'query' => $featureValueOptions,
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
),
|
||||
'desc' => $this->l('Wartości zostaną załadowane po wyborze cechy.'),
|
||||
),
|
||||
array(
|
||||
'type' => 'text',
|
||||
'label' => $this->l('Tytuł zakładki'),
|
||||
'name' => 'title',
|
||||
'lang' => true,
|
||||
'required' => true,
|
||||
'size' => 255,
|
||||
),
|
||||
array(
|
||||
'type' => 'textarea',
|
||||
'label' => $this->l('Treść'),
|
||||
'name' => 'content',
|
||||
'lang' => true,
|
||||
'autoload_rte' => true,
|
||||
'rows' => 10,
|
||||
'cols' => 100,
|
||||
),
|
||||
array(
|
||||
'type' => 'text',
|
||||
'label' => $this->l('Pozycja'),
|
||||
'name' => 'position',
|
||||
'class' => 'fixed-width-xs',
|
||||
),
|
||||
array(
|
||||
'type' => 'switch',
|
||||
'label' => $this->l('Aktywna'),
|
||||
'name' => 'active',
|
||||
'is_bool' => true,
|
||||
'values' => array(
|
||||
array('id' => 'active_on', 'value' => 1, 'label' => $this->l('Tak')),
|
||||
array('id' => 'active_off', 'value' => 0, 'label' => $this->l('Nie')),
|
||||
),
|
||||
),
|
||||
),
|
||||
'submit' => array(
|
||||
'title' => $this->l('Zapisz'),
|
||||
),
|
||||
);
|
||||
|
||||
return parent::renderForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add JS for AJAX dropdown and pass the selected value.
|
||||
*/
|
||||
public function setMedia($isNewTheme = false)
|
||||
{
|
||||
parent::setMedia($isNewTheme);
|
||||
|
||||
if (in_array($this->display, array('add', 'edit'))) {
|
||||
$this->addJS(_PS_MODULE_DIR_ . 'customfeaturetab/views/js/admin.js');
|
||||
|
||||
// Pass the preselected feature value to JS for edit mode
|
||||
if ($this->object && $this->object->id) {
|
||||
Media::addJsDef(array(
|
||||
'customfeaturetab_selected_value' => (int) $this->object->id_feature_value,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX endpoint: get feature values for a given feature.
|
||||
*/
|
||||
public function ajaxProcessGetFeatureValues()
|
||||
{
|
||||
$idFeature = (int) Tools::getValue('id_feature');
|
||||
$idLang = (int) $this->context->language->id;
|
||||
|
||||
// Include ALL values (predefined + custom) for the feature
|
||||
$sql = 'SELECT fv.`id_feature_value`, fvl.`value`
|
||||
FROM `' . _DB_PREFIX_ . 'feature_value` fv
|
||||
LEFT JOIN `' . _DB_PREFIX_ . 'feature_value_lang` fvl
|
||||
ON fv.`id_feature_value` = fvl.`id_feature_value`
|
||||
AND fvl.`id_lang` = ' . (int) $idLang . '
|
||||
WHERE fv.`id_feature` = ' . (int) $idFeature . '
|
||||
ORDER BY fvl.`value` ASC';
|
||||
|
||||
$values = Db::getInstance()->executeS($sql);
|
||||
$result = array();
|
||||
if ($values) {
|
||||
foreach ($values as $val) {
|
||||
$result[] = array(
|
||||
'id_feature_value' => $val['id_feature_value'],
|
||||
'value' => $val['value'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
die(json_encode($result));
|
||||
}
|
||||
}
|
||||
8
modules/customfeaturetab/controllers/admin/index.php
Normal file
8
modules/customfeaturetab/controllers/admin/index.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ../');
|
||||
exit;
|
||||
8
modules/customfeaturetab/controllers/index.php
Normal file
8
modules/customfeaturetab/controllers/index.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ../');
|
||||
exit;
|
||||
148
modules/customfeaturetab/customfeaturetab.php
Normal file
148
modules/customfeaturetab/customfeaturetab.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* Custom Feature Tab - Adds product tabs based on feature values.
|
||||
*
|
||||
* @author Project-Pro <https://www.project-pro.pl>
|
||||
* @copyright Project-Pro
|
||||
* @license Proprietary - paid license
|
||||
*/
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once dirname(__FILE__) . '/classes/CustomFeatureTabRule.php';
|
||||
|
||||
class CustomFeatureTab extends Module
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->name = 'customfeaturetab';
|
||||
$this->tab = 'front_office_features';
|
||||
$this->version = '1.0.0';
|
||||
$this->author = 'Project-Pro';
|
||||
$this->need_instance = 0;
|
||||
$this->bootstrap = true;
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->displayName = $this->l('Karty cech produktu');
|
||||
$this->description = $this->l('Dodaje dodatkowe zakładki na karcie produktu w zależności od przypisanych cech.');
|
||||
$this->ps_versions_compliancy = array('min' => '1.7.0.0', 'max' => _PS_VERSION_);
|
||||
}
|
||||
|
||||
public function install()
|
||||
{
|
||||
return parent::install()
|
||||
&& $this->installDb()
|
||||
&& $this->installTab()
|
||||
&& $this->registerHook('displayProductExtraContent');
|
||||
}
|
||||
|
||||
public function uninstall()
|
||||
{
|
||||
return $this->uninstallDb()
|
||||
&& $this->uninstallTab()
|
||||
&& parent::uninstall();
|
||||
}
|
||||
|
||||
private function installDb()
|
||||
{
|
||||
$sql = array();
|
||||
|
||||
$sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'custom_feature_tab` (
|
||||
`id_custom_feature_tab` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`id_feature` INT(11) UNSIGNED NOT NULL,
|
||||
`id_feature_value` INT(11) UNSIGNED NOT NULL,
|
||||
`position` INT(11) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`active` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
|
||||
`date_add` DATETIME NOT NULL,
|
||||
`date_upd` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`id_custom_feature_tab`),
|
||||
INDEX `idx_feature_value` (`id_feature`, `id_feature_value`)
|
||||
) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;';
|
||||
|
||||
$sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'custom_feature_tab_lang` (
|
||||
`id_custom_feature_tab` INT(11) UNSIGNED NOT NULL,
|
||||
`id_lang` INT(11) UNSIGNED NOT NULL,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`content` TEXT,
|
||||
PRIMARY KEY (`id_custom_feature_tab`, `id_lang`)
|
||||
) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;';
|
||||
|
||||
foreach ($sql as $query) {
|
||||
if (!Db::getInstance()->execute($query)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function uninstallDb()
|
||||
{
|
||||
return Db::getInstance()->execute('DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'custom_feature_tab_lang`')
|
||||
&& Db::getInstance()->execute('DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'custom_feature_tab`');
|
||||
}
|
||||
|
||||
private function installTab()
|
||||
{
|
||||
$tab = new Tab();
|
||||
$tab->class_name = 'AdminCustomFeatureTab';
|
||||
$tab->module = $this->name;
|
||||
$tab->id_parent = (int) Tab::getIdFromClassName('AdminCatalog');
|
||||
$tab->icon = 'description';
|
||||
$languages = Language::getLanguages(false);
|
||||
foreach ($languages as $lang) {
|
||||
$tab->name[$lang['id_lang']] = 'Karty cech produktu';
|
||||
}
|
||||
|
||||
return $tab->add();
|
||||
}
|
||||
|
||||
private function uninstallTab()
|
||||
{
|
||||
$idTab = (int) Tab::getIdFromClassName('AdminCustomFeatureTab');
|
||||
if ($idTab) {
|
||||
$tab = new Tab($idTab);
|
||||
return $tab->delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
Tools::redirectAdmin($this->context->link->getAdminLink('AdminCustomFeatureTab'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook: displayProductExtraContent
|
||||
* Returns extra tabs for products that match feature rules.
|
||||
*/
|
||||
public function hookDisplayProductExtraContent($params)
|
||||
{
|
||||
$product = $params['product'];
|
||||
$idLang = (int) $this->context->language->id;
|
||||
|
||||
$productFeatures = Product::getFeaturesStatic((int) $product->id);
|
||||
if (empty($productFeatures)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$rules = CustomFeatureTabRule::getMatchingRules($productFeatures, $idLang);
|
||||
if (empty($rules)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$tabs = array();
|
||||
foreach ($rules as $rule) {
|
||||
$extraContent = new PrestaShop\PrestaShop\Core\Product\ProductExtraContent();
|
||||
$extraContent->setTitle($rule['title']);
|
||||
$extraContent->setContent($rule['content']);
|
||||
$tabs[] = $extraContent;
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
}
|
||||
23
modules/customfeaturetab/dbquery_temp.php
Normal file
23
modules/customfeaturetab/dbquery_temp.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
// Temporary query script - DELETE AFTER USE
|
||||
include_once dirname(__FILE__) . '/../../config/config.inc.php';
|
||||
|
||||
$sql = "SELECT p.id_product, pl.name AS product_name, fl.name AS feature_name, fvl.value AS feature_value
|
||||
FROM ps_feature_product fp
|
||||
JOIN ps_feature_lang fl ON fp.id_feature = fl.id_feature AND fl.id_lang = 1
|
||||
JOIN ps_feature_value_lang fvl ON fp.id_feature_value = fvl.id_feature_value AND fvl.id_lang = 1
|
||||
JOIN ps_product p ON fp.id_product = p.id_product
|
||||
JOIN ps_product_lang pl ON p.id_product = pl.id_product AND pl.id_lang = 1
|
||||
WHERE fl.name = 'Seria' AND fvl.value = 'Simon 10'
|
||||
ORDER BY p.id_product
|
||||
LIMIT 20";
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
$results = Db::getInstance()->executeS($sql);
|
||||
if ($results) {
|
||||
foreach ($results as $row) {
|
||||
echo $row['id_product'] . ' | ' . $row['product_name'] . ' | ' . $row['feature_name'] . ' -> ' . $row['feature_value'] . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "Brak wynikow\n";
|
||||
}
|
||||
14
modules/customfeaturetab/index.php
Normal file
14
modules/customfeaturetab/index.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* @author InterBlue
|
||||
* @copyright InterBlue
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
*/
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ../');
|
||||
exit;
|
||||
BIN
modules/customfeaturetab/logo.png
Normal file
BIN
modules/customfeaturetab/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
8
modules/customfeaturetab/views/index.php
Normal file
8
modules/customfeaturetab/views/index.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ../');
|
||||
exit;
|
||||
56
modules/customfeaturetab/views/js/admin.js
Normal file
56
modules/customfeaturetab/views/js/admin.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* AJAX dependent dropdown for feature values in admin form.
|
||||
*/
|
||||
$(document).ready(function () {
|
||||
var $featureSelect = $('select[name="id_feature"]');
|
||||
var $valueSelect = $('select[name="id_feature_value"]');
|
||||
|
||||
if (!$featureSelect.length || !$valueSelect.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build AJAX base URL from current page URL
|
||||
var baseUrl = window.location.href.split('#')[0];
|
||||
// Strip existing query noise and keep controller + token
|
||||
var preselectedValue = $valueSelect.data('selected') || $valueSelect.val();
|
||||
|
||||
$featureSelect.on('change', function () {
|
||||
var idFeature = $(this).val();
|
||||
if (!idFeature) {
|
||||
$valueSelect.empty().append('<option value="">--</option>');
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the current page URL, append ajax params
|
||||
var ajaxUrl = baseUrl + '&ajax=1&action=getFeatureValues&id_feature=' + idFeature;
|
||||
|
||||
$.ajax({
|
||||
url: ajaxUrl,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
$valueSelect.empty();
|
||||
if (data && data.length) {
|
||||
$.each(data, function (i, item) {
|
||||
var selected = (item.id_feature_value == preselectedValue) ? ' selected' : '';
|
||||
$valueSelect.append(
|
||||
'<option value="' + item.id_feature_value + '"' + selected + '>' +
|
||||
item.value +
|
||||
'</option>'
|
||||
);
|
||||
});
|
||||
preselectedValue = null;
|
||||
} else {
|
||||
$valueSelect.append('<option value="">--</option>');
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error('[customfeaturetab] AJAX error:', status, error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if ($featureSelect.val()) {
|
||||
$featureSelect.trigger('change');
|
||||
}
|
||||
});
|
||||
8
modules/customfeaturetab/views/js/index.php
Normal file
8
modules/customfeaturetab/views/js/index.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ../');
|
||||
exit;
|
||||
@@ -231,14 +231,6 @@
|
||||
aria-controls="product-details"
|
||||
{if !$product.description} aria-selected="true"{/if}>{l s='Product Details' d='Shop.Theme.Catalog'}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link"
|
||||
data-toggle="tab"
|
||||
href="#attachments"
|
||||
role="tab"
|
||||
aria-controls="attachments">{l s='Attachments' d='Shop.Theme.Catalog'}</a>
|
||||
</li>
|
||||
{foreach from=$product.extraContent item=extra key=extraKey}
|
||||
<li class="nav-item">
|
||||
<a
|
||||
@@ -249,6 +241,14 @@
|
||||
aria-controls="extra-{$extraKey}">{$extra.title}</a>
|
||||
</li>
|
||||
{/foreach}
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link"
|
||||
data-toggle="tab"
|
||||
href="#attachments"
|
||||
role="tab"
|
||||
aria-controls="attachments">{l s='Attachments' d='Shop.Theme.Catalog'}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#ekomiprc">
|
||||
Opinie
|
||||
@@ -268,6 +268,12 @@
|
||||
{include file='catalog/_partials/product-details.tpl'}
|
||||
{/block}
|
||||
|
||||
{foreach from=$product.extraContent item=extra key=extraKey}
|
||||
<div class="tab-pane fade in {$extra.attr.class}" id="extra-{$extraKey}" role="tabpanel" {foreach $extra.attr as $key => $val} {$key}="{$val}"{/foreach}>
|
||||
{$extra.content nofilter}
|
||||
</div>
|
||||
{/foreach}
|
||||
|
||||
{block name='product_attachments'}
|
||||
<div class="tab-pane fade in" id="attachments" role="tabpanel">
|
||||
<section class="product-attachments">
|
||||
@@ -298,12 +304,6 @@
|
||||
</div>
|
||||
{/block}
|
||||
{/block}
|
||||
|
||||
{foreach from=$product.extraContent item=extra key=extraKey}
|
||||
<div class="tab-pane fade in {$extra.attr.class}" id="extra-{$extraKey}" role="tabpanel" {foreach $extra.attr as $key => $val} {$key}="{$val}"{/foreach}>
|
||||
{$extra.content nofilter}
|
||||
</div>
|
||||
{/foreach}
|
||||
{hook h='displayProductTabContent'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user