Fruitcake_Gary Posted September 29 Share Posted September 29 Hi I have some problem with transferring new variables to a template. I wrote myself a small module that passes custom text to products in a category. I wanted these variables to appear in product-add-to-cart.tpl. <?php if (!defined('_PS_VERSION_')) { exit; } class CustomTextCategories extends Module { public function __construct() { $this->name = 'customtextcategories'; $this->tab = 'front_office_features'; $this->version = '1.0.0'; $this->author = 'X X'; $this->need_instance = 0; parent::__construct(); $this->displayName = $this->l('Custom Text for Categories'); $this->description = $this->l('Displays custom text for selected product categories.'); } public function install() { return parent::install() && $this->registerHook('displayProductButtons') && $this->installTab() && Configuration::updateValue('CUSTOM_TEXT', '') && Configuration::updateValue('CATEGORY_IDS', ''); } public function installTab() { $tab = new Tab(); $tab->active = 1; $tab->class_name = 'AdminCustomTextCategories'; $tab->name = []; foreach (Language::getLanguages(true) as $lang) { $tab->name[$lang['id_lang']] = 'Custom Text Categories'; } $tab->id_parent = (int) Tab::getIdFromClassName('AdminOrders'); $tab->module = $this->name; $tab->add(); return true; } public function uninstall() { $this->uninstallTab(); return parent::uninstall() && Configuration::deleteByName('CUSTOM_TEXT') && Configuration::deleteByName('CATEGORY_IDS'); } public function uninstallTab() { $tabId = (int) Tab::getIdFromClassName('AdminCustomTextCategories'); if ($tabId) { $tab = new Tab($tabId); $tab->delete(); } } public function getContent() { $output = ''; if (Tools::isSubmit('submitCustomTextCategories')) { $customText = Tools::getValue('CUSTOM_TEXT'); $categoryIds = Tools::getValue('CATEGORY_IDS'); Configuration::updateValue('CUSTOM_TEXT', $customText); Configuration::updateValue('CATEGORY_IDS', $categoryIds); $output .= $this->displayConfirmation($this->l('Settings updated.')); } return $output . $this->renderForm(); } protected function renderForm() { $fieldsForm = [ 'form' => [ 'legend' => [ 'title' => $this->l('Settings'), ], 'input' => [ [ 'type' => 'textarea', 'label' => $this->l('Custom Text'), 'name' => 'CUSTOM_TEXT', 'rows' => 5, 'cols' => 40, 'desc' => $this->l('Enter the custom text to display on products in selected categories.'), ], [ 'type' => 'text', 'label' => $this->l('Category IDs'), 'name' => 'CATEGORY_IDS', 'desc' => $this->l('Enter category IDs, separated by commas. Example: 1,2,3'), ], ], 'submit' => [ 'title' => $this->l('Save'), ], ], ]; $helper = new HelperForm(); $helper->submit_action = 'submitCustomTextCategories'; $helper->fields_value['CUSTOM_TEXT'] = Configuration::get('CUSTOM_TEXT'); $helper->fields_value['CATEGORY_IDS'] = Configuration::get('CATEGORY_IDS'); return $helper->generateForm([$fieldsForm]); } public function hookDisplayProductButtons($params) { error_log("hookDisplayProductButtons called"); $customText = Configuration::get('CUSTOM_TEXT'); $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); error_log("Custom Text: " . $customText); error_log("Category IDs: " . implode(',', $categoryIdsArray)); // Przypisanie zmiennych do Smarty $this->context->smarty->assign([ 'custom_text' => $customText, 'custom_categories' => $categoryIdsArray, ]); } public function hookHeader() { $this->context->controller->registerStylesheet( 'customtextcategories-style', 'modules/' . $this->name . '/views/css/customtextcategories.css', ['media' => 'all', 'priority' => 150] ); } } product-add-to-cart.tpl <div class="product-add-to-cart js-product-add-to-cart text-uppercase din-pro-font"> {if !$configuration.is_catalog} {if $product.add_to_cart_url} {block name='product_quantity'} <span class="control-label">{l s='Quantity' d='Shop.Theme.Catalog'}</span> <div class="product-quantity clearfix"> <div class="qty"> <input type="number" name="qty" id="quantity_wanted" inputmode="numeric" pattern="[0-9]*" {if $product.quantity_wanted} value="{$product.quantity_wanted}" min="{$product.minimal_quantity}" {else} value="1" min="1" {/if} class="input-group" aria-label="{l s='Quantity' d='Shop.Theme.Actions'}" > </div> <h1>{$custom_text}</h1> <div class="add"> {* <button*} {* class="btn btn-primary add-to-cart buy-button"*} {* data-button-action="add-to-cart"*} {* type="submit"*} {* {if !$product.add_to_cart_url}*} {* disabled*} {* {/if}*} {* >*} {* <i class="material-icons shopping-cart"></i>*} {* {l s='Add to cart' d='Shop.Theme.Actions'}*} {* </button>*} {if $product.id_category_default|in_array:[$custom_categories]} <div class="custom-text"> {$custom_text} </div> {else} <button class="btn btn-primary add-to-cart buy-button" data-button-action="add-to-cart" type="submit" {if !$product.add_to_cart_url} disabled {/if} > <i class="material-icons shopping-cart"></i> {l s='Add to cart' d='Shop.Theme.Actions'} </button> {/if} </div> {hook h='displayProductActions' product=$product} </div> {/block} {else} <div class="main-container-styled p-1 mb-2"> Oferta ma charakter informacyjny i prezentuje dostępność broni i amunicji w naszym sklepie stacjonarny pod adresem ul. Jag 2A, 55-095 Mirków. Zakup broni, amunicji, prochów strzelniczych oraz spłonek możliwy jest wyłącznie w siedzibie naszej firmy, za okazaniem stosownych dokumentów uprawniających do zakupu. Kontakt w sprawie broni i amunicji: </div> {/if} {if Configuration::get('RESERVATION_ENABLED')} <button id="reserve-button" class="btn btn-primary buy-button">ZAREZERWUJ</button> {/if} {block name='product_availability'} <span id="product-availability" class="js-product-availability"> {if $product.show_availability && $product.availability_message} {if $product.availability == 'available'} <i class="material-icons rtl-no-flip product-available"></i> {elseif $product.availability == 'last_remaining_items'} <i class="material-icons product-last-items"></i> {else} <i class="material-icons product-unavailable"></i> {/if} {$product.availability_message} {/if} </span> {/block} {block name='product_minimal_quantity'} <p class="product-minimal-quantity js-product-minimal-quantity"> {if $product.minimal_quantity > 1} {l s='The minimum purchase order quantity for the product is %quantity%.' d='Shop.Theme.Checkout' sprintf=['%quantity%' => $product.minimal_quantity] } {/if} </p> {/block} {/if} </div> The problem is that these variables do not pass, I want them to appear directly in this template and not any hook. The only thing that appears in <pre> is '1'. In the hookup {hook h='displayProductActions' product=$product} the variables do appear, but the point is not there. Link to comment Share on other sites More sharing options...
ps8modules Posted October 4 Share Posted October 4 Hi. And you've already tried: {$custom_text nofilter} Or add your own hook to the TPL template, for example: {hook h='displayMyCustomText' productId=$product.id categoryDefault=$product.id_category_default} Link to comment Share on other sites More sharing options...
WisQQ Posted October 4 Share Posted October 4 Dnia 30.09.2024 o 12:45 AM, Fruitcake_Gary napisał: Hi I have some problem with transferring new variables to a template. I wrote myself a small module that passes custom text to products in a category. I wanted these variables to appear in product-add-to-cart.tpl. <?php if (!defined('_PS_VERSION_')) { exit; } class CustomTextCategories extends Module { public function __construct() { $this->name = 'customtextcategories'; $this->tab = 'front_office_features'; $this->version = '1.0.0'; $this->author = 'X X'; $this->need_instance = 0; parent::__construct(); $this->displayName = $this->l('Custom Text for Categories'); $this->description = $this->l('Displays custom text for selected product categories.'); } public function install() { return parent::install() && $this->registerHook('displayProductButtons') && $this->installTab() && Configuration::updateValue('CUSTOM_TEXT', '') && Configuration::updateValue('CATEGORY_IDS', ''); } public function installTab() { $tab = new Tab(); $tab->active = 1; $tab->class_name = 'AdminCustomTextCategories'; $tab->name = []; foreach (Language::getLanguages(true) as $lang) { $tab->name[$lang['id_lang']] = 'Custom Text Categories'; } $tab->id_parent = (int) Tab::getIdFromClassName('AdminOrders'); $tab->module = $this->name; $tab->add(); return true; } public function uninstall() { $this->uninstallTab(); return parent::uninstall() && Configuration::deleteByName('CUSTOM_TEXT') && Configuration::deleteByName('CATEGORY_IDS'); } public function uninstallTab() { $tabId = (int) Tab::getIdFromClassName('AdminCustomTextCategories'); if ($tabId) { $tab = new Tab($tabId); $tab->delete(); } } public function getContent() { $output = ''; if (Tools::isSubmit('submitCustomTextCategories')) { $customText = Tools::getValue('CUSTOM_TEXT'); $categoryIds = Tools::getValue('CATEGORY_IDS'); Configuration::updateValue('CUSTOM_TEXT', $customText); Configuration::updateValue('CATEGORY_IDS', $categoryIds); $output .= $this->displayConfirmation($this->l('Settings updated.')); } return $output . $this->renderForm(); } protected function renderForm() { $fieldsForm = [ 'form' => [ 'legend' => [ 'title' => $this->l('Settings'), ], 'input' => [ [ 'type' => 'textarea', 'label' => $this->l('Custom Text'), 'name' => 'CUSTOM_TEXT', 'rows' => 5, 'cols' => 40, 'desc' => $this->l('Enter the custom text to display on products in selected categories.'), ], [ 'type' => 'text', 'label' => $this->l('Category IDs'), 'name' => 'CATEGORY_IDS', 'desc' => $this->l('Enter category IDs, separated by commas. Example: 1,2,3'), ], ], 'submit' => [ 'title' => $this->l('Save'), ], ], ]; $helper = new HelperForm(); $helper->submit_action = 'submitCustomTextCategories'; $helper->fields_value['CUSTOM_TEXT'] = Configuration::get('CUSTOM_TEXT'); $helper->fields_value['CATEGORY_IDS'] = Configuration::get('CATEGORY_IDS'); return $helper->generateForm([$fieldsForm]); } public function hookDisplayProductButtons($params) { error_log("hookDisplayProductButtons called"); $customText = Configuration::get('CUSTOM_TEXT'); $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); error_log("Custom Text: " . $customText); error_log("Category IDs: " . implode(',', $categoryIdsArray)); // Przypisanie zmiennych do Smarty $this->context->smarty->assign([ 'custom_text' => $customText, 'custom_categories' => $categoryIdsArray, ]); } public function hookHeader() { $this->context->controller->registerStylesheet( 'customtextcategories-style', 'modules/' . $this->name . '/views/css/customtextcategories.css', ['media' => 'all', 'priority' => 150] ); } } product-add-to-cart.tpl <div class="product-add-to-cart js-product-add-to-cart text-uppercase din-pro-font"> {if !$configuration.is_catalog} {if $product.add_to_cart_url} {block name='product_quantity'} <span class="control-label">{l s='Quantity' d='Shop.Theme.Catalog'}</span> <div class="product-quantity clearfix"> <div class="qty"> <input type="number" name="qty" id="quantity_wanted" inputmode="numeric" pattern="[0-9]*" {if $product.quantity_wanted} value="{$product.quantity_wanted}" min="{$product.minimal_quantity}" {else} value="1" min="1" {/if} class="input-group" aria-label="{l s='Quantity' d='Shop.Theme.Actions'}" > </div> <h1>{$custom_text}</h1> <div class="add"> {* <button*} {* class="btn btn-primary add-to-cart buy-button"*} {* data-button-action="add-to-cart"*} {* type="submit"*} {* {if !$product.add_to_cart_url}*} {* disabled*} {* {/if}*} {* >*} {* <i class="material-icons shopping-cart"></i>*} {* {l s='Add to cart' d='Shop.Theme.Actions'}*} {* </button>*} {if $product.id_category_default|in_array:[$custom_categories]} <div class="custom-text"> {$custom_text} </div> {else} <button class="btn btn-primary add-to-cart buy-button" data-button-action="add-to-cart" type="submit" {if !$product.add_to_cart_url} disabled {/if} > <i class="material-icons shopping-cart"></i> {l s='Add to cart' d='Shop.Theme.Actions'} </button> {/if} </div> {hook h='displayProductActions' product=$product} </div> {/block} {else} <div class="main-container-styled p-1 mb-2"> Oferta ma charakter informacyjny i prezentuje dostępność broni i amunicji w naszym sklepie stacjonarny pod adresem ul. Jag 2A, 55-095 Mirków. Zakup broni, amunicji, prochów strzelniczych oraz spłonek możliwy jest wyłącznie w siedzibie naszej firmy, za okazaniem stosownych dokumentów uprawniających do zakupu. Kontakt w sprawie broni i amunicji: </div> {/if} {if Configuration::get('RESERVATION_ENABLED')} <button id="reserve-button" class="btn btn-primary buy-button">ZAREZERWUJ</button> {/if} {block name='product_availability'} <span id="product-availability" class="js-product-availability"> {if $product.show_availability && $product.availability_message} {if $product.availability == 'available'} <i class="material-icons rtl-no-flip product-available"></i> {elseif $product.availability == 'last_remaining_items'} <i class="material-icons product-last-items"></i> {else} <i class="material-icons product-unavailable"></i> {/if} {$product.availability_message} {/if} </span> {/block} {block name='product_minimal_quantity'} <p class="product-minimal-quantity js-product-minimal-quantity"> {if $product.minimal_quantity > 1} {l s='The minimum purchase order quantity for the product is %quantity%.' d='Shop.Theme.Checkout' sprintf=['%quantity%' => $product.minimal_quantity] } {/if} </p> {/block} {/if} </div> The problem is that these variables do not pass, I want them to appear directly in this template and not any hook. The only thing that appears in <pre> is '1'. In the hookup {hook h='displayProductActions' product=$product} the variables do appear, but the point is not there. I dont see your hook being called inside tpl. Also you should return template to your variables since they passed only to your template. public function hookDisplayProductButtons($params) { error_log("hookDisplayProductButtons called"); $customText = Configuration::get('CUSTOM_TEXT'); $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); error_log("Custom Text: " . $customText); error_log("Category IDs: " . implode(',', $categoryIdsArray)); // Przypisanie zmiennych do Smarty $this->context->smarty->assign([ 'custom_text' => $customText, 'custom_categories' => $categoryIdsArray, ]); return $this->fetch('module:'.$this->name.'/views/templates/front/yourtemplatename.tpl'); } Then you simply add your hook inside theme template so it will be executed. product-add-to-cart.tpl {hook='DisplayProductButtons'} Link to comment Share on other sites More sharing options...
Fruitcake_Gary Posted October 24 Author Share Posted October 24 Hi @WisQQ Your solution works correctly. However, there was another problem. While plugging this hookup works popawfully it plugs in addition to displayProductAdditionalInfo which makes my button appear twice. TEST HOOK comes from my module. It adds itself to product-additional-info which causes JS to malfunction, the chevrons responsible for increasing the number of products disappear, and that heart icon disappears. The same problem occurs in quick-view. By the way, is there any possibility that after installing the module, it will replace the {block name='product_add_to_cart'} {include file='catalog/_partials/product-add-to-cart.tpl'} {/block} For my solution, or at least overwrite this product-add-to-cart.tpl {hook h='displayProductButtons' product=$product} Link to comment Share on other sites More sharing options...
Fruitcake_Gary Posted October 24 Author Share Posted October 24 @WisQQ good, I got to the fact that this button comes from core.js and is injected into the template. Is there any way to force Prestashop to use my template by default as the primary one? It's as if the store forgets that such a file in _partials-product-add-to-cart.tpl even exists, and takes the same file from my module, but already properly crafted <div class="product-add-to-cart js-product-add-to-cart text-uppercase din-pro-font"> <span class="control-label">Ilość</span> <div class="product-quantity clearfix"> <div class="qty"> <input type="number" name="qty" id="quantity_wanted" inputmode="numeric" pattern="[0-9]*" value="1" min="1" class="input-group" aria-label="Ilość" > </div> <div class="add"> <button class="btn btn-primary add-to-cart buy-button" data-button-action="add-to-cart" type="submit" > <i class="material-icons shopping-cart"></i> Dodaj do koszyka ZZ </button> </div> <h1>test strona</h1> <div class="wishlist-button Q" data-url="http://localhost:8080/pl/module/blockwishlist/action?action=deleteProductFromWishlist" data-product-id="1" data-product-attribute-id="1" data-is-logged="1" data-list-id="1" data-checked="true" data-is-product="true" ></div> </div> <button id="reserve-button" class="btn btn-primary buy-button">ZAREZERWUJ</button> <span id="product-availability" class="js-product-availability"> </span> <p class="product-minimal-quantity js-product-minimal-quantity"> </p> </div> The idea is to make the module fully autonomous. I upload it and no longer have to code anything Link to comment Share on other sites More sharing options...
WisQQ Posted October 25 Share Posted October 25 9 godzin temu, Fruitcake_Gary napisał: @WisQQ good, I got to the fact that this button comes from core.js and is injected into the template. Is there any way to force Prestashop to use my template by default as the primary one? It's as if the store forgets that such a file in _partials-product-add-to-cart.tpl even exists, and takes the same file from my module, but already properly crafted <div class="product-add-to-cart js-product-add-to-cart text-uppercase din-pro-font"> <span class="control-label">Ilość</span> <div class="product-quantity clearfix"> <div class="qty"> <input type="number" name="qty" id="quantity_wanted" inputmode="numeric" pattern="[0-9]*" value="1" min="1" class="input-group" aria-label="Ilość" > </div> <div class="add"> <button class="btn btn-primary add-to-cart buy-button" data-button-action="add-to-cart" type="submit" > <i class="material-icons shopping-cart"></i> Dodaj do koszyka ZZ </button> </div> <h1>test strona</h1> <div class="wishlist-button Q" data-url="http://localhost:8080/pl/module/blockwishlist/action?action=deleteProductFromWishlist" data-product-id="1" data-product-attribute-id="1" data-is-logged="1" data-list-id="1" data-checked="true" data-is-product="true" ></div> </div> <button id="reserve-button" class="btn btn-primary buy-button">ZAREZERWUJ</button> <span id="product-availability" class="js-product-availability"> </span> <p class="product-minimal-quantity js-product-minimal-quantity"> </p> </div> The idea is to make the module fully autonomous. I upload it and no longer have to code anything Please paste code you are using in your module, not sure where is problem with calling additional info twice, because this hook isnt inside classic theme product-add-to-cart.tpl You could try to ovrride add to cart button using hookfilterProductContent. But I'm not 100% sure that it isnt protected property. Link to comment Share on other sites More sharing options...
Fruitcake_Gary Posted October 25 Author Share Posted October 25 @WisQQ This is the full code for the module <?php define('_PS_MODE_DEV_', true); class CustomTextCategories extends Module { public function __construct() { $this->name = 'customtextcategories'; $this->tab = 'administration'; $this->version = '1.0.0'; $this->author = 'test.test'; $this->need_instance = 0; $this->bootstrap = true; parent::__construct(); $this->displayName = $this->l('Custom Text Categories'); $this->description = $this->l('Module to display custom text for specific product categories.'); $this->ps_versions_compliancy = ['min' => '1.7', 'max' => _PS_VERSION_]; } public function install() { return parent::install() && $this->registerHook('displayOverrideTemplate') && $this->registerHook('hookActionFrontControllerAfterInit') && $this->registerHook('displayProductButtons') && $this->registerHook('displayCategoryProductList') && // $this->registerHook('displayProductListFunctionalButtons') && $this->registerHook('actionFrontControllerSetMedia') && $this->installTab('AdminParentOrders', 'AdminCustomTextCategories', 'Custom Text for Categories') && Configuration::updateValue('CUSTOM_TEXT', '') && Configuration::updateValue('CATEGORY_IDS', ''); } public function installTab($parent, $className, $name) { $tab = new Tab(); $tab->id_parent = (int) Tab::getIdFromClassName($parent); $tab->class_name = $className; $tab->module = $this->name; $tab->active = 1; foreach (Language::getLanguages(true) as $lang) { $tab->name[$lang['id_lang']] = $name; } return $tab->add(); } public function initContent() { $this->context->smarty->assign([ 'custom_text' => Configuration::get('CUSTOM_TEXT'), 'category_ids' => Configuration::get('CATEGORY_IDS'), ]); $this->content .= $this->context->smarty->fetch( _PS_MODULE_DIR_ . $this->module->name . '/views/templates/admin/configure.tpl' ); parent::initContent(); } public function uninstall() { return parent::uninstall() && Configuration::deleteByName('CUSTOM_TEXT') && Configuration::deleteByName('CATEGORY_IDS') && $this->uninstallTab('AdminCustomTextCategories'); } private function uninstallTab($class_name) { $id_tab = (int)Tab::getIdFromClassName($class_name); if ($id_tab) { $tab = new Tab($id_tab); return $tab->delete(); } return false; } public function getContent() { $output = null; if (Tools::isSubmit('submitCustomTextCategories')) { $this->processForm(); $output .= $this->displayConfirmation($this->l('Settings updated')); } return $output . $this->renderForm(); } public function renderForm() { $helper = new HelperForm(); $helper->show_toolbar = false; $helper->table = $this->table; $helper->module = $this; $helper->default_form_language = $this->context->language->id; $helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG', 0); $helper->identifier = $this->identifier; $helper->submit_action = 'submitCustomTextCategories'; $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 = [ 'fields_value' => $this->getConfigFormValues(), 'languages' => $this->context->controller->getLanguages(), 'id_language' => $this->context->language->id, ]; return $helper->generateForm([$this->getConfigForm()]); } public function hookDisplayProductButtons($params) { static $hook_called = false; if ($hook_called) { return ''; // Hook był już wywołany, więc nic nie robimy } $hook_called = true; $customText = Configuration::get('CUSTOM_TEXT'); $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); $blockBuyButton = Configuration::get('BLOCK_BUY_BUTTON'); $this->context->smarty->assign([ 'custom_text' => $customText, 'custom_categories' => $categoryIdsArray, 'block_buy_button' => $blockBuyButton, ]); return $this->fetch('module:customtextcategories/views/templates/front/product-add-to-cart.tpl'); } public function hookDisplayOverrideTemplate($params) { if ($params['controller'] == 'product') { if ($params['template_file'] == 'catalog/_partials/product-add-to-cart.tpl') { $params['template_file'] = 'module:customtextcategories/views/templates/front/product-add-to-cart.tpl'; } } } public function hookActionFrontControllerAfterInit($params) { if ($this->context->controller->php_self === 'product') { $this->context->smarty->assign([ 'custom_text' => Configuration::get('CUSTOM_TEXT'), 'category_ids' => explode(',', Configuration::get('CATEGORY_IDS')), 'block_buy_button' => Configuration::get('BLOCK_BUY_BUTTON'), ]); $this->context->controller->getTemplateVarPage()['body_classes'][] = 'custom-add-to-cart'; } } public function hookDisplayCategoryProductList($params) { $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); $productCategories = Product::getProductCategories($params['product']['id_product']); $matchingCategories = array_intersect($productCategories, $categoryIdsArray); $this->context->smarty->assign([ 'custom_categories' => $matchingCategories, ]); return $this->fetch('module:customtextcategories/views/templates/hook/category-add-to-cart.tpl'); } public function hookActionFrontControllerSetMedia($params) { if ('product' === $this->context->controller->php_self) { $this->context->controller->registerJavascript( 'modules-customtextcategories', 'modules/' . $this->name . '/views/js/add_to_cart.js', ['position' => 'bottom', 'priority' => 150] ); } } public function getAllCategories() { $categories = Category::getCategories($this->context->language->id, true, false); return $categories; } public function getConfigForm() { $categories = $this->getAllCategories(); $categoryCheckboxes = []; foreach ($categories as $category) { $categoryCheckboxes[] = [ 'id' => 'category_' . $category['id_category'], 'value' => $category['id_category'], 'label' => $category['name'] ]; } return [ 'form' => [ 'legend' => [ 'title' => $this->l('Custom Text Categories Configuration'), 'icon' => 'icon-cogs', ], 'input' => [ [ 'type' => 'textarea', 'label' => $this->l('Custom Text'), 'name' => 'CUSTOM_TEXT', 'cols' => 60, 'rows' => 5, 'required' => true, ], [ 'type' => 'switch', 'label' => $this->l('Włącz okno modalne'), 'name' => 'BLOCK_BUY_BUTTON', 'required' => false, 'is_bool' => true, 'values' => [ [ 'id' => 'block_buy_button_on', 'value' => 1, 'label' => $this->l('Yes') ], [ 'id' => 'block_buy_button_off', 'value' => 0, 'label' => $this->l('No') ] ] ] ], 'submit' => [ 'title' => $this->l('Save'), ], ], ]; } public function getConfigFormValues() { $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); $values = [ 'CUSTOM_TEXT' => Configuration::get('CUSTOM_TEXT'), 'CATEGORY_IDS' => $categoryIdsArray, 'BLOCK_BUY_BUTTON' => Configuration::get('BLOCK_BUY_BUTTON') ]; return $values; } public function processForm() { $customText = Tools::getValue('CUSTOM_TEXT'); Configuration::updateValue('CUSTOM_TEXT', $customText); $categories = Tools::getValue('CATEGORY_IDS', []); PrestaShopLogger::addLog('CATEGORY_IDS: ' . var_export($categories, true), 1); if (is_array($categories) && !empty($categories)) { $categoriesString = implode(',', $categories); Configuration::updateValue('CATEGORY_IDS', $categoriesString); } else { Configuration::updateValue('CATEGORY_IDS', ''); } $blockBuyButton = Tools::getValue('BLOCK_BUY_BUTTON', 0); Configuration::updateValue('BLOCK_BUY_BUTTON', (int)$blockBuyButton); PrestaShopLogger::addLog('BLOCK_BUY_BUTTON value: ' . Tools::getValue('BLOCK_BUY_BUTTON'), 1); } public function postProcess() { if (Tools::isSubmit('submitCustomTextCategories')) { $customText = Tools::getValue('CUSTOM_TEXT'); $categoryIds = Tools::getValue('CATEGORY_IDS', []); Configuration::updateValue('CUSTOM_TEXT', $customText); if (is_array($categoryIds) && !empty($categoryIds)) { $categoryIdsString = implode(',', $categoryIds); Configuration::updateValue('CATEGORY_IDS', $categoryIdsString); } else { Configuration::updateValue('CATEGORY_IDS', ''); } $blockBuyButton = Tools::getValue('BLOCK_BUY_BUTTON', 0); Configuration::updateValue('BLOCK_BUY_BUTTON', (int)$blockBuyButton); PrestaShopLogger::addLog('BLOCK_BUY_BUTTON value: ' . Tools::getValue('BLOCK_BUY_BUTTON'), 1); } } } Module structure: I wish I didn't have to paste this: {hook h='displayProductButtons' product=$product} But it automatically turned into this: {block name='product_add_to_cart'} {include file='catalog/_partials/product-add-to-cart.tpl'} {/block} .tpl {if (in_array($product->id_category_default, $custom_categories) && $block_buy_button == 0) || $configuration.is_catalog} <div class="main-container-styled p-1 mb-2"> {$custom_text} </div> {else} <div class="product-add-to-cart js-product-add-to-cart text-uppercase din-pro-font"> {if !$configuration.is_catalog} {block name='product_quantity'} <span class="control-label">{l s='Quantity' d='Shop.Theme.Catalog'}</span> <div class="product-quantity clearfix"> <div class="qty"> <input type="number" name="qty" id="quantity_wanted" inputmode="numeric" pattern="[0-9]*" {if $product.quantity_wanted} value="{$product.quantity_wanted}" min="{$product.minimal_quantity}" {else} value="1" min="1" {/if} class="input-group" aria-label="{l s='Quantity' d='Shop.Theme.Actions'}" > </div> <div class="add"> <button class="btn btn-primary add-to-cart buy-button test_Custom_modules" {if (in_array($product->id_category_default, $custom_categories) && $block_buy_button == 1)} data-button-action="test-cart" type="button" {else} data-button-action="add-to-cart" type="submit" {/if} {if !$product.add_to_cart_url} disabled {/if} > <i class="material-icons shopping-cart"></i> {l s='Add to cart' d='Shop.Theme.Actions'} </button> </div> <h1>test hook</h1> {hook h='displayProductActions' product=$product} </div> {/block} {if Configuration::get('RESERVATION_ENABLED') && $product.add_to_cart_url} <button id="reserve-button" class="btn btn-primary buy-button">ZAREZERWUJ</button> {/if} {block name='product_availability'} <span id="product-availability" class="js-product-availability"> {if $product.show_availability && $product.availability_message} {if $product.availability == 'available'} <i class="material-icons rtl-no-flip product-available"></i> {elseif $product.availability == 'last_remaining_items'} <i class="material-icons product-last-items"></i> {else} <i class="material-icons product-unavailable"></i> {/if} {$product.availability_message} {/if} </span> {/block} {block name='product_minimal_quantity'} <p class="product-minimal-quantity js-product-minimal-quantity"> {if $product.minimal_quantity > 1} {l s='The minimum purchase order quantity for the product is %quantity%.' d='Shop.Theme.Checkout' sprintf=['%quantity%' => $product.minimal_quantity] } {/if} </p> {/block} {/if} </div> {/if} What I am trying to do is that it substitutes its template instead of _partials-product-add-to-cart.tpl. The thing is that my product-add-to-cart.tpl has some modified features that I really care about. I could make it so that I substitute the original _partials-product-add-to-cart.tpl but I would like to make it so that the person who will use this module will not have to interfere with anything in the store Link to comment Share on other sites More sharing options...
WisQQ Posted October 28 Share Posted October 28 Dnia 25.10.2024 o 10:14 PM, Fruitcake_Gary napisał: @WisQQ This is the full code for the module <?php define('_PS_MODE_DEV_', true); class CustomTextCategories extends Module { public function __construct() { $this->name = 'customtextcategories'; $this->tab = 'administration'; $this->version = '1.0.0'; $this->author = 'test.test'; $this->need_instance = 0; $this->bootstrap = true; parent::__construct(); $this->displayName = $this->l('Custom Text Categories'); $this->description = $this->l('Module to display custom text for specific product categories.'); $this->ps_versions_compliancy = ['min' => '1.7', 'max' => _PS_VERSION_]; } public function install() { return parent::install() && $this->registerHook('displayOverrideTemplate') && $this->registerHook('hookActionFrontControllerAfterInit') && $this->registerHook('displayProductButtons') && $this->registerHook('displayCategoryProductList') && // $this->registerHook('displayProductListFunctionalButtons') && $this->registerHook('actionFrontControllerSetMedia') && $this->installTab('AdminParentOrders', 'AdminCustomTextCategories', 'Custom Text for Categories') && Configuration::updateValue('CUSTOM_TEXT', '') && Configuration::updateValue('CATEGORY_IDS', ''); } public function installTab($parent, $className, $name) { $tab = new Tab(); $tab->id_parent = (int) Tab::getIdFromClassName($parent); $tab->class_name = $className; $tab->module = $this->name; $tab->active = 1; foreach (Language::getLanguages(true) as $lang) { $tab->name[$lang['id_lang']] = $name; } return $tab->add(); } public function initContent() { $this->context->smarty->assign([ 'custom_text' => Configuration::get('CUSTOM_TEXT'), 'category_ids' => Configuration::get('CATEGORY_IDS'), ]); $this->content .= $this->context->smarty->fetch( _PS_MODULE_DIR_ . $this->module->name . '/views/templates/admin/configure.tpl' ); parent::initContent(); } public function uninstall() { return parent::uninstall() && Configuration::deleteByName('CUSTOM_TEXT') && Configuration::deleteByName('CATEGORY_IDS') && $this->uninstallTab('AdminCustomTextCategories'); } private function uninstallTab($class_name) { $id_tab = (int)Tab::getIdFromClassName($class_name); if ($id_tab) { $tab = new Tab($id_tab); return $tab->delete(); } return false; } public function getContent() { $output = null; if (Tools::isSubmit('submitCustomTextCategories')) { $this->processForm(); $output .= $this->displayConfirmation($this->l('Settings updated')); } return $output . $this->renderForm(); } public function renderForm() { $helper = new HelperForm(); $helper->show_toolbar = false; $helper->table = $this->table; $helper->module = $this; $helper->default_form_language = $this->context->language->id; $helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG', 0); $helper->identifier = $this->identifier; $helper->submit_action = 'submitCustomTextCategories'; $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 = [ 'fields_value' => $this->getConfigFormValues(), 'languages' => $this->context->controller->getLanguages(), 'id_language' => $this->context->language->id, ]; return $helper->generateForm([$this->getConfigForm()]); } public function hookDisplayProductButtons($params) { static $hook_called = false; if ($hook_called) { return ''; // Hook był już wywołany, więc nic nie robimy } $hook_called = true; $customText = Configuration::get('CUSTOM_TEXT'); $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); $blockBuyButton = Configuration::get('BLOCK_BUY_BUTTON'); $this->context->smarty->assign([ 'custom_text' => $customText, 'custom_categories' => $categoryIdsArray, 'block_buy_button' => $blockBuyButton, ]); return $this->fetch('module:customtextcategories/views/templates/front/product-add-to-cart.tpl'); } public function hookDisplayOverrideTemplate($params) { if ($params['controller'] == 'product') { if ($params['template_file'] == 'catalog/_partials/product-add-to-cart.tpl') { $params['template_file'] = 'module:customtextcategories/views/templates/front/product-add-to-cart.tpl'; } } } public function hookActionFrontControllerAfterInit($params) { if ($this->context->controller->php_self === 'product') { $this->context->smarty->assign([ 'custom_text' => Configuration::get('CUSTOM_TEXT'), 'category_ids' => explode(',', Configuration::get('CATEGORY_IDS')), 'block_buy_button' => Configuration::get('BLOCK_BUY_BUTTON'), ]); $this->context->controller->getTemplateVarPage()['body_classes'][] = 'custom-add-to-cart'; } } public function hookDisplayCategoryProductList($params) { $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); $productCategories = Product::getProductCategories($params['product']['id_product']); $matchingCategories = array_intersect($productCategories, $categoryIdsArray); $this->context->smarty->assign([ 'custom_categories' => $matchingCategories, ]); return $this->fetch('module:customtextcategories/views/templates/hook/category-add-to-cart.tpl'); } public function hookActionFrontControllerSetMedia($params) { if ('product' === $this->context->controller->php_self) { $this->context->controller->registerJavascript( 'modules-customtextcategories', 'modules/' . $this->name . '/views/js/add_to_cart.js', ['position' => 'bottom', 'priority' => 150] ); } } public function getAllCategories() { $categories = Category::getCategories($this->context->language->id, true, false); return $categories; } public function getConfigForm() { $categories = $this->getAllCategories(); $categoryCheckboxes = []; foreach ($categories as $category) { $categoryCheckboxes[] = [ 'id' => 'category_' . $category['id_category'], 'value' => $category['id_category'], 'label' => $category['name'] ]; } return [ 'form' => [ 'legend' => [ 'title' => $this->l('Custom Text Categories Configuration'), 'icon' => 'icon-cogs', ], 'input' => [ [ 'type' => 'textarea', 'label' => $this->l('Custom Text'), 'name' => 'CUSTOM_TEXT', 'cols' => 60, 'rows' => 5, 'required' => true, ], [ 'type' => 'switch', 'label' => $this->l('Włącz okno modalne'), 'name' => 'BLOCK_BUY_BUTTON', 'required' => false, 'is_bool' => true, 'values' => [ [ 'id' => 'block_buy_button_on', 'value' => 1, 'label' => $this->l('Yes') ], [ 'id' => 'block_buy_button_off', 'value' => 0, 'label' => $this->l('No') ] ] ] ], 'submit' => [ 'title' => $this->l('Save'), ], ], ]; } public function getConfigFormValues() { $categoryIds = Configuration::get('CATEGORY_IDS'); $categoryIdsArray = explode(',', $categoryIds); $values = [ 'CUSTOM_TEXT' => Configuration::get('CUSTOM_TEXT'), 'CATEGORY_IDS' => $categoryIdsArray, 'BLOCK_BUY_BUTTON' => Configuration::get('BLOCK_BUY_BUTTON') ]; return $values; } public function processForm() { $customText = Tools::getValue('CUSTOM_TEXT'); Configuration::updateValue('CUSTOM_TEXT', $customText); $categories = Tools::getValue('CATEGORY_IDS', []); PrestaShopLogger::addLog('CATEGORY_IDS: ' . var_export($categories, true), 1); if (is_array($categories) && !empty($categories)) { $categoriesString = implode(',', $categories); Configuration::updateValue('CATEGORY_IDS', $categoriesString); } else { Configuration::updateValue('CATEGORY_IDS', ''); } $blockBuyButton = Tools::getValue('BLOCK_BUY_BUTTON', 0); Configuration::updateValue('BLOCK_BUY_BUTTON', (int)$blockBuyButton); PrestaShopLogger::addLog('BLOCK_BUY_BUTTON value: ' . Tools::getValue('BLOCK_BUY_BUTTON'), 1); } public function postProcess() { if (Tools::isSubmit('submitCustomTextCategories')) { $customText = Tools::getValue('CUSTOM_TEXT'); $categoryIds = Tools::getValue('CATEGORY_IDS', []); Configuration::updateValue('CUSTOM_TEXT', $customText); if (is_array($categoryIds) && !empty($categoryIds)) { $categoryIdsString = implode(',', $categoryIds); Configuration::updateValue('CATEGORY_IDS', $categoryIdsString); } else { Configuration::updateValue('CATEGORY_IDS', ''); } $blockBuyButton = Tools::getValue('BLOCK_BUY_BUTTON', 0); Configuration::updateValue('BLOCK_BUY_BUTTON', (int)$blockBuyButton); PrestaShopLogger::addLog('BLOCK_BUY_BUTTON value: ' . Tools::getValue('BLOCK_BUY_BUTTON'), 1); } } } Module structure: I wish I didn't have to paste this: {hook h='displayProductButtons' product=$product} But it automatically turned into this: {block name='product_add_to_cart'} {include file='catalog/_partials/product-add-to-cart.tpl'} {/block} .tpl {if (in_array($product->id_category_default, $custom_categories) && $block_buy_button == 0) || $configuration.is_catalog} <div class="main-container-styled p-1 mb-2"> {$custom_text} </div> {else} <div class="product-add-to-cart js-product-add-to-cart text-uppercase din-pro-font"> {if !$configuration.is_catalog} {block name='product_quantity'} <span class="control-label">{l s='Quantity' d='Shop.Theme.Catalog'}</span> <div class="product-quantity clearfix"> <div class="qty"> <input type="number" name="qty" id="quantity_wanted" inputmode="numeric" pattern="[0-9]*" {if $product.quantity_wanted} value="{$product.quantity_wanted}" min="{$product.minimal_quantity}" {else} value="1" min="1" {/if} class="input-group" aria-label="{l s='Quantity' d='Shop.Theme.Actions'}" > </div> <div class="add"> <button class="btn btn-primary add-to-cart buy-button test_Custom_modules" {if (in_array($product->id_category_default, $custom_categories) && $block_buy_button == 1)} data-button-action="test-cart" type="button" {else} data-button-action="add-to-cart" type="submit" {/if} {if !$product.add_to_cart_url} disabled {/if} > <i class="material-icons shopping-cart"></i> {l s='Add to cart' d='Shop.Theme.Actions'} </button> </div> <h1>test hook</h1> {hook h='displayProductActions' product=$product} </div> {/block} {if Configuration::get('RESERVATION_ENABLED') && $product.add_to_cart_url} <button id="reserve-button" class="btn btn-primary buy-button">ZAREZERWUJ</button> {/if} {block name='product_availability'} <span id="product-availability" class="js-product-availability"> {if $product.show_availability && $product.availability_message} {if $product.availability == 'available'} <i class="material-icons rtl-no-flip product-available"></i> {elseif $product.availability == 'last_remaining_items'} <i class="material-icons product-last-items"></i> {else} <i class="material-icons product-unavailable"></i> {/if} {$product.availability_message} {/if} </span> {/block} {block name='product_minimal_quantity'} <p class="product-minimal-quantity js-product-minimal-quantity"> {if $product.minimal_quantity > 1} {l s='The minimum purchase order quantity for the product is %quantity%.' d='Shop.Theme.Checkout' sprintf=['%quantity%' => $product.minimal_quantity] } {/if} </p> {/block} {/if} </div> {/if} What I am trying to do is that it substitutes its template instead of _partials-product-add-to-cart.tpl. The thing is that my product-add-to-cart.tpl has some modified features that I really care about. I could make it so that I substitute the original _partials-product-add-to-cart.tpl but I would like to make it so that the person who will use this module will not have to interfere with anything in the store You can use any other hook but using {hook h='displayProductButtons' product=$product} allows you to place it anywhere you want, so i would still keep it as an option for the user. public function hookDisplayOverrideTemplate($params) { if ($params['controller'] == 'product') { if ($params['template_file'] == 'catalog/_partials/product-add-to-cart.tpl') { $params['template_file'] = 'module:customtextcategories/views/templates/front/product-add-to-cart.tpl'; } } } With this hook you can override entire product.tpl, but you cant change partials. When you dump the template_file variable you will see that it refers to 'catalog/product'. You can override the addtocart on variant change, using prestashop js events. on updatedProducthttps://devdocs.prestashop-project.org/8/themes/reference/javascript-events/ Or you can try to override ProductController displayAjaxRefresh() function. However you cant override add to cart template as its part of the theme without modifying theme file. You can only do that by using javascript after page was loaded. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now