From 34fb95a699dd54f1fbdc7980969b499a79b596a2 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Thu, 13 Aug 2026 15:31:23 +0200 Subject: [PATCH] feat(main): Add alert type; add template override support --- src/Config/Config.php | 8 +++++ .../CustomHtmlBlockDataHandler.php | 4 +++ .../CustomHtmlBlockDataProvider.php | 4 +++ src/Form/Type/CustomHtmlBlockType.php | 16 +++++++++- src/Model/CustomHtmlBlock.php | 10 +++++- src/Module/DisplayHooks.php | 5 +++ upgrade/upgrade-1.10.0.php | 31 +++++++++++++++++++ upgrade/upgrade-1.8.0.php | 20 ++++++++++++ upgrade/upgrade-1.9.0.php | 20 ++++++++++++ views/css/admin/grid.css | 14 ++++++--- views/css/front/customhtml.css | 22 +++++++++++++ views/js/front/customhtml.js | 26 ++++++++++++++++ .../admin/form/blocks/block_form.html.twig | 2 ++ .../form/customhtmlblock_create.html.twig | 1 + .../admin/form/customhtmlblock_edit.html.twig | 1 + views/templates/hook/_alert.tpl | 16 ++++++++++ views/templates/hook/customhtml_block.tpl | 8 ++++- ws_customhtml.php | 2 +- 18 files changed, 202 insertions(+), 8 deletions(-) create mode 100644 upgrade/upgrade-1.10.0.php create mode 100644 upgrade/upgrade-1.8.0.php create mode 100644 upgrade/upgrade-1.9.0.php create mode 100644 views/js/front/customhtml.js create mode 100644 views/templates/hook/_alert.tpl diff --git a/src/Config/Config.php b/src/Config/Config.php index c3264a8..26e886d 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -14,6 +14,14 @@ class Config 'html' => 'HTML', 'faq' => 'FAQ Accordion', 'banner' => 'Banner', + 'alert' => 'Alert', + ]; + + const ALERT_STYLES = [ + 'success' => 'Success', + 'info' => 'Info', + 'warning' => 'Warning', + 'danger' => 'Danger', ]; const BANNER_IMAGE_ALLOWED_EXTENSIONS = [ diff --git a/src/Form/DataHandler/CustomHtmlBlockDataHandler.php b/src/Form/DataHandler/CustomHtmlBlockDataHandler.php index 5737af0..2642335 100644 --- a/src/Form/DataHandler/CustomHtmlBlockDataHandler.php +++ b/src/Form/DataHandler/CustomHtmlBlockDataHandler.php @@ -27,6 +27,8 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface $block->description = (string) ($formData['description'] ?? ''); $block->extra_description = (string) ($formData['extra_description'] ?? ''); $block->link = (string) ($formData['link'] ?? ''); + $block->alert_type = (string) ($formData['alert_type'] ?? 'info'); + $block->timeout = (int) ($formData['timeout'] ?? 0); $block->image = $this->saveUploadedImage($formData['image'] ?? null, ''); $block->active = (int) $formData['active']; $block->ordering = $this->getNextOrdering($idGroup); @@ -57,6 +59,8 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface $block->description = (string) ($formData['description'] ?? ''); $block->extra_description = (string) ($formData['extra_description'] ?? ''); $block->link = (string) ($formData['link'] ?? ''); + $block->alert_type = (string) ($formData['alert_type'] ?? 'info'); + $block->timeout = (int) ($formData['timeout'] ?? 0); $block->image = $this->saveUploadedImage($formData['image'] ?? null, $block->image); $block->active = (int) $formData['active']; diff --git a/src/Form/DataProvider/CustomHtmlBlockDataProvider.php b/src/Form/DataProvider/CustomHtmlBlockDataProvider.php index 14b99ee..2337365 100644 --- a/src/Form/DataProvider/CustomHtmlBlockDataProvider.php +++ b/src/Form/DataProvider/CustomHtmlBlockDataProvider.php @@ -28,6 +28,8 @@ final class CustomHtmlBlockDataProvider implements FormDataProviderInterface 'description' => $block->description, 'extra_description' => $block->extra_description, 'link' => $block->link, + 'alert_type' => $block->alert_type, + 'timeout' => (int) $block->timeout, 'faqs' => $this->loadFaqs((int) $id), 'active' => (bool) $block->active, ]; @@ -46,6 +48,8 @@ final class CustomHtmlBlockDataProvider implements FormDataProviderInterface 'description' => '', 'extra_description' => '', 'link' => '', + 'alert_type' => 'info', + 'timeout' => 0, 'faqs' => [], 'active' => true, ]; diff --git a/src/Form/Type/CustomHtmlBlockType.php b/src/Form/Type/CustomHtmlBlockType.php index b8b4298..e7d3453 100644 --- a/src/Form/Type/CustomHtmlBlockType.php +++ b/src/Form/Type/CustomHtmlBlockType.php @@ -7,6 +7,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\FileType; +use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use PrestaShop\PrestaShop\Core\Form\ConfigurableFormChoiceProviderInterface; @@ -70,10 +71,23 @@ class CustomHtmlBlockType extends AbstractType 'prototype' => true, 'required' => false, ]) + ->add('alert_type', ChoiceType::class, [ + 'label' => $this->translator->trans('Alert style', [], 'Modules.Wscustomhtml.Admin'), + 'choices' => array_flip(Config::ALERT_STYLES), + 'required' => false, + 'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-alert'], + ]) + ->add('timeout', IntegerType::class, [ + 'label' => $this->translator->trans('Auto-dismiss after (seconds)', [], 'Modules.Wscustomhtml.Admin'), + 'required' => false, + 'attr' => ['min' => 0, 'placeholder' => '0'], + 'help' => $this->translator->trans('0 means the alert never auto-dismisses; visitors can still close it manually.', [], 'Modules.Wscustomhtml.Admin'), + 'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-alert'], + ]) ->add('description', TextareaType::class, [ 'label' => $this->translator->trans('Description', [], 'Modules.Wscustomhtml.Admin'), 'required' => false, - 'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-banner'], + 'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-banner customhtmlblock-type-alert'], ]) ->add('extra_description', TextareaType::class, [ 'label' => $this->translator->trans('Extra description', [], 'Modules.Wscustomhtml.Admin'), diff --git a/src/Model/CustomHtmlBlock.php b/src/Model/CustomHtmlBlock.php index e549999..efa6d10 100644 --- a/src/Model/CustomHtmlBlock.php +++ b/src/Model/CustomHtmlBlock.php @@ -37,6 +37,12 @@ class CustomHtmlBlock extends ObjectModel /** @var string */ public $image = ''; + /** @var string */ + public $alert_type = 'info'; + + /** @var int */ + public $timeout = 0; + /** @var int */ public $active = 1; @@ -65,6 +71,8 @@ class CustomHtmlBlock extends ObjectModel 'extra_description' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => false, 'size' => 1024], 'link' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => false, 'size' => 512], 'image' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => false, 'size' => 255], + 'alert_type' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => false, 'size' => 16], + 'timeout' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'required' => false], 'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'show_title' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'ordering' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], @@ -97,7 +105,7 @@ class CustomHtmlBlock extends ObjectModel $result = Db::getInstance()->executeS( 'SELECT `id_ws_customhtml`, `id_ws_customhtml_group`, `title`, `type`, `content`, `custom_class`, - `description`, `extra_description`, `link`, `image`, `active`, `show_title`, `ordering` + `description`, `extra_description`, `link`, `image`, `alert_type`, `timeout`, `active`, `show_title`, `ordering` FROM `' . _DB_PREFIX_ . 'ws_customhtml` WHERE `id_ws_customhtml_group` IN (' . implode(',', $groupIds) . ') AND `active` = 1 diff --git a/src/Module/DisplayHooks.php b/src/Module/DisplayHooks.php index f1eb00e..35b0eb2 100644 --- a/src/Module/DisplayHooks.php +++ b/src/Module/DisplayHooks.php @@ -15,6 +15,11 @@ trait DisplayHooks 'ws_customhtml_front', '/modules/' . $this->name . '/views/css/front/customhtml.css' ); + + $this->context->controller->registerJavascript( + 'ws_customhtml_front', + '/modules/' . $this->name . '/views/js/front/customhtml.js' + ); } public function renderWidget($hookName = null, array $configuration = []) diff --git a/upgrade/upgrade-1.10.0.php b/upgrade/upgrade-1.10.0.php new file mode 100644 index 0000000..f2b3626 --- /dev/null +++ b/upgrade/upgrade-1.10.0.php @@ -0,0 +1,31 @@ + 'VARCHAR(16) NOT NULL DEFAULT \'info\'', + 'timeout' => 'INT(11) NOT NULL DEFAULT 0', + ]; + + foreach ($columnsToAdd as $column => $definition) { + $existing = Db::getInstance()->executeS( + 'SHOW COLUMNS FROM `' . _DB_PREFIX_ . 'ws_customhtml` LIKE \'' . $column . '\'' + ); + + if (!empty($existing)) { + continue; + } + + if (!Db::getInstance()->execute( + 'ALTER TABLE `' . _DB_PREFIX_ . 'ws_customhtml` ADD COLUMN `' . $column . '` ' . $definition + )) { + return false; + } + } + + return true; +} diff --git a/upgrade/upgrade-1.8.0.php b/upgrade/upgrade-1.8.0.php new file mode 100644 index 0000000..27ce763 --- /dev/null +++ b/upgrade/upgrade-1.8.0.php @@ -0,0 +1,20 @@ +executeS( + 'SHOW COLUMNS FROM `' . _DB_PREFIX_ . 'ws_customhtml` LIKE \'template_key\'' + ); + + if (!empty($existing)) { + return true; + } + + return Db::getInstance()->execute( + 'ALTER TABLE `' . _DB_PREFIX_ . 'ws_customhtml` ADD COLUMN `template_key` VARCHAR(64) NOT NULL DEFAULT \'\'' + ); +} diff --git a/upgrade/upgrade-1.9.0.php b/upgrade/upgrade-1.9.0.php new file mode 100644 index 0000000..f4410aa --- /dev/null +++ b/upgrade/upgrade-1.9.0.php @@ -0,0 +1,20 @@ +executeS( + 'SHOW COLUMNS FROM `' . _DB_PREFIX_ . 'ws_customhtml` LIKE \'template_key\'' + ); + + if (empty($existing)) { + return true; + } + + return Db::getInstance()->execute( + 'ALTER TABLE `' . _DB_PREFIX_ . 'ws_customhtml` DROP COLUMN `template_key`' + ); +} diff --git a/views/css/admin/grid.css b/views/css/admin/grid.css index e802213..68498fe 100644 --- a/views/css/admin/grid.css +++ b/views/css/admin/grid.css @@ -18,21 +18,27 @@ td.action-type .btn-group-action { max-width: unset !important; text-align: left !important; } - form[name="custom_html_block"] .card-body { + form[name="custom_html_block"] .card-body, + form[name="custom_html_group"] .card-body { padding: 25px 50px !important; } form[name="custom_html_block"] .card-header, - form[name="custom_html_block"] .card-footer { + form[name="custom_html_block"] .card-footer, + form[name="custom_html_group"] .card-header, + form[name="custom_html_group"] .card-footer { padding: 20px 50px !important; } } @media (min-width: 1500px) { - form[name="custom_html_block"] .card-body { + form[name="custom_html_block"] .card-body, + form[name="custom_html_group"] .card-body { padding: 25px 300px !important; } form[name="custom_html_block"] .card-header, - form[name="custom_html_block"] .card-footer { + form[name="custom_html_block"] .card-footer, + form[name="custom_html_group"] .card-header, + form[name="custom_html_group"] .card-footer { padding: 20px 300px !important; } } diff --git a/views/css/front/customhtml.css b/views/css/front/customhtml.css index 94bd04c..98cfeef 100644 --- a/views/css/front/customhtml.css +++ b/views/css/front/customhtml.css @@ -15,3 +15,25 @@ .customhtml-banner-extra-description { margin-bottom: 1rem; } + +.customhtml-alert { + max-height: 500px; + overflow: hidden; + transition: + max-height 0.4s ease, + padding 0.4s ease, + margin 0.4s ease, + opacity 0.4s ease; +} + +.customhtml-alert.customhtml-alert-collapsing { + max-height: 0; + padding-top: 0; + padding-bottom: 0; + margin-bottom: 0; + opacity: 0; +} + +.customhtml-alert-title { + margin-right: 0.5rem; +} diff --git a/views/js/front/customhtml.js b/views/js/front/customhtml.js new file mode 100644 index 0000000..978e581 --- /dev/null +++ b/views/js/front/customhtml.js @@ -0,0 +1,26 @@ +document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll('.customhtml-alert').forEach(function (alertEl) { + var timeout = parseInt(alertEl.getAttribute('data-timeout'), 10) || 0; + var timer = null; + + var dismiss = function () { + alertEl.classList.add('customhtml-alert-collapsing'); + }; + + if (timeout > 0) { + timer = setTimeout(dismiss, timeout * 1000); + } + + var closeButton = alertEl.querySelector('.customhtml-alert-close'); + + if (closeButton) { + closeButton.addEventListener('click', function () { + if (timer) { + clearTimeout(timer); + } + + dismiss(); + }); + } + }); +}); diff --git a/views/templates/admin/form/blocks/block_form.html.twig b/views/templates/admin/form/blocks/block_form.html.twig index fb8e4ef..66a6c7f 100644 --- a/views/templates/admin/form/blocks/block_form.html.twig +++ b/views/templates/admin/form/blocks/block_form.html.twig @@ -59,6 +59,8 @@ + {{ form_row(customHtmlBlockForm.alert_type) }} + {{ form_row(customHtmlBlockForm.timeout) }} {{ form_row(customHtmlBlockForm.description) }} {{ form_row(customHtmlBlockForm.extra_description) }} {{ form_row(customHtmlBlockForm.link) }} diff --git a/views/templates/admin/form/customhtmlblock_create.html.twig b/views/templates/admin/form/customhtmlblock_create.html.twig index d053cbc..608fe3e 100644 --- a/views/templates/admin/form/customhtmlblock_create.html.twig +++ b/views/templates/admin/form/customhtmlblock_create.html.twig @@ -20,6 +20,7 @@ $('.customhtmlblock-type-html').hide(); $('.customhtmlblock-type-faq').hide(); $('.customhtmlblock-type-banner').hide(); + $('.customhtmlblock-type-alert').hide(); $('.customhtmlblock-type-' + type).show(); } diff --git a/views/templates/admin/form/customhtmlblock_edit.html.twig b/views/templates/admin/form/customhtmlblock_edit.html.twig index 1b263a9..9a184f2 100644 --- a/views/templates/admin/form/customhtmlblock_edit.html.twig +++ b/views/templates/admin/form/customhtmlblock_edit.html.twig @@ -20,6 +20,7 @@ $('.customhtmlblock-type-html').hide(); $('.customhtmlblock-type-faq').hide(); $('.customhtmlblock-type-banner').hide(); + $('.customhtmlblock-type-alert').hide(); $('.customhtmlblock-type-' + type).show(); } diff --git a/views/templates/hook/_alert.tpl b/views/templates/hook/_alert.tpl new file mode 100644 index 0000000..6527057 --- /dev/null +++ b/views/templates/hook/_alert.tpl @@ -0,0 +1,16 @@ +{if !empty($block.description)} + +{/if} diff --git a/views/templates/hook/customhtml_block.tpl b/views/templates/hook/customhtml_block.tpl index 3e2b00d..899201a 100644 --- a/views/templates/hook/customhtml_block.tpl +++ b/views/templates/hook/customhtml_block.tpl @@ -4,13 +4,19 @@
{foreach from=$group.blocks item="block"}
- {if $block.type == 'faq'} + {assign var="customhtmlOverridePath" value="{$smarty.const._PS_THEME_DIR_}modules/ws_customhtml/views/templates/hook/{$hookName}/{$block.type}.tpl"} + + {if file_exists($customhtmlOverridePath)} + {include file="module:ws_customhtml/views/templates/hook/`$hookName`/`$block.type`.tpl" block=$block group=$group hookName=$hookName} + {elseif $block.type == 'faq'} {if $block.show_title && !empty($block.title)}
{$block.title|escape:'html':'UTF-8'}
{/if} {include file="module:ws_customhtml/views/templates/hook/_faq_accordion.tpl" block=$block} {elseif $block.type == 'banner'} {include file="module:ws_customhtml/views/templates/hook/_banner.tpl" block=$block} + {elseif $block.type == 'alert'} + {include file="module:ws_customhtml/views/templates/hook/_alert.tpl" block=$block} {else} {if $block.show_title && !empty($block.title)}
{$block.title|escape:'html':'UTF-8'}
diff --git a/ws_customhtml.php b/ws_customhtml.php index 9dd5487..f2a9677 100644 --- a/ws_customhtml.php +++ b/ws_customhtml.php @@ -29,7 +29,7 @@ class Ws_CustomHtml extends Module implements WidgetInterface public function __construct() { $this->name = 'ws_customhtml'; - $this->version = '1.7.0'; + $this->version = '1.10.0'; $this->author = 'Isabelle Oving-Anno | De Websmid BV'; $this->need_instance = 0; $this->bootstrap = true;