From 1493081602fe57a40395aa39b04c7ade0e6bff57 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Tue, 11 Aug 2026 13:25:06 +0200 Subject: [PATCH] feat(main): Add FAQ accordion --- Screenshot 2026-08-11 095051.png | Bin 0 -> 18593 bytes config/form.yml | 8 +++ src/Config/Config.php | 6 ++ src/Controller/CustomHtmlBlockController.php | 2 + .../CustomHtmlBlockDataHandler.php | 31 ++++++++- .../CustomHtmlBlockDataProvider.php | 15 +++++ src/Form/Type/CustomHtmlBlockType.php | 16 +++++ src/Form/Type/CustomHtmlFaqType.php | 32 ++++++++++ .../CustomHtmlBlockGridDefinitionFactory.php | 16 +++++ .../Query/CustomHtmlBlockQueryBuilder.php | 7 ++ src/Model/CustomHtmlBlock.php | 6 +- src/Model/CustomHtmlFaq.php | 60 ++++++++++++++++++ src/Module/AdminHooks.php | 15 +++++ src/Module/DisplayHooks.php | 15 +++++ src/Module/Install.php | 44 +++++++++++++ upgrade/upgrade-1.2.0.php | 38 +++++++++++ .../admin/form/blocks/block_form.html.twig | 38 +++++++++++ .../form/customhtmlblock_create.html.twig | 38 +++++++++++ .../admin/form/customhtmlblock_edit.html.twig | 38 +++++++++++ views/templates/hook/_faq_accordion.tpl | 30 +++++++++ views/templates/hook/customhtml_block.tpl | 6 +- ws_customhtml.php | 2 +- 22 files changed, 459 insertions(+), 4 deletions(-) create mode 100644 Screenshot 2026-08-11 095051.png create mode 100644 src/Form/Type/CustomHtmlFaqType.php create mode 100644 src/Model/CustomHtmlFaq.php create mode 100644 upgrade/upgrade-1.2.0.php create mode 100644 views/templates/hook/_faq_accordion.tpl diff --git a/Screenshot 2026-08-11 095051.png b/Screenshot 2026-08-11 095051.png new file mode 100644 index 0000000000000000000000000000000000000000..a48d3fa96625e973269c134dc4e82213e4519e5a GIT binary patch literal 18593 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 ab+h`($lWm+ 'HTML', + 'faq' => 'FAQ Accordion', ]; const ADMIN_CONTROLLERS = [ diff --git a/src/Controller/CustomHtmlBlockController.php b/src/Controller/CustomHtmlBlockController.php index 7dd0dbe..bf9bec4 100644 --- a/src/Controller/CustomHtmlBlockController.php +++ b/src/Controller/CustomHtmlBlockController.php @@ -11,6 +11,7 @@ use Symfony\Component\HttpFoundation\Response; use Websmid\CustomHtml\Grid\Definition\Factory\CustomHtmlBlockGridDefinitionFactory; use Websmid\CustomHtml\Grid\Filters\CustomHtmlBlockFilters; use Websmid\CustomHtml\Model\CustomHtmlBlock; +use Websmid\CustomHtml\Model\CustomHtmlFaq; use Validate; class CustomHtmlBlockController extends FrameworkBundleAdminController @@ -107,6 +108,7 @@ class CustomHtmlBlockController extends FrameworkBundleAdminController return $this->redirectToRoute('customhtmlblock_index'); } + CustomHtmlFaq::deleteByBlockId($idCustomHtmlBlock); $block->delete(); $this->addFlash('success', $this->trans('Successful deletion.', 'Admin.Notifications.Success')); diff --git a/src/Form/DataHandler/CustomHtmlBlockDataHandler.php b/src/Form/DataHandler/CustomHtmlBlockDataHandler.php index f0f0517..d99f91a 100644 --- a/src/Form/DataHandler/CustomHtmlBlockDataHandler.php +++ b/src/Form/DataHandler/CustomHtmlBlockDataHandler.php @@ -4,6 +4,7 @@ namespace Websmid\CustomHtml\Form\DataHandler; use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\DataHandler\FormDataHandlerInterface; use Websmid\CustomHtml\Model\CustomHtmlBlock; +use Websmid\CustomHtml\Model\CustomHtmlFaq; use Validate; final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface @@ -13,6 +14,7 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface $block = new CustomHtmlBlock(); $block->id_ws_customhtml_group = (int) $formData['id_ws_customhtml_group']; $block->title = (string) $formData['title']; + $block->type = (string) $formData['type']; $block->show_title = (int) $formData['show_title']; $block->content = (string) $formData['content']; $block->active = (int) $formData['active']; @@ -22,6 +24,8 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface return false; } + $this->saveFaqs((int) $block->id, $formData['faqs'] ?? []); + return $block->id; } @@ -35,10 +39,35 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface $block->id_ws_customhtml_group = (int) $formData['id_ws_customhtml_group']; $block->title = (string) $formData['title']; + $block->type = (string) $formData['type']; $block->show_title = (int) $formData['show_title']; $block->content = (string) $formData['content']; $block->active = (int) $formData['active']; - return (bool) $block->update(); + if (!$block->update()) { + return false; + } + + $this->saveFaqs((int) $id, $formData['faqs'] ?? []); + + return true; + } + + private function saveFaqs(int $idBlock, array $faqs): void + { + CustomHtmlFaq::deleteByBlockId($idBlock); + + foreach (array_values($faqs) as $position => $faqData) { + if (empty($faqData['question'])) { + continue; + } + + $faq = new CustomHtmlFaq(); + $faq->id_ws_customhtml = $idBlock; + $faq->question = (string) $faqData['question']; + $faq->answer = (string) ($faqData['answer'] ?? ''); + $faq->ordering = $position; + $faq->add(); + } } } diff --git a/src/Form/DataProvider/CustomHtmlBlockDataProvider.php b/src/Form/DataProvider/CustomHtmlBlockDataProvider.php index 493e288..4149bbd 100644 --- a/src/Form/DataProvider/CustomHtmlBlockDataProvider.php +++ b/src/Form/DataProvider/CustomHtmlBlockDataProvider.php @@ -4,6 +4,7 @@ namespace Websmid\CustomHtml\Form\DataProvider; use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\DataProvider\FormDataProviderInterface; use Websmid\CustomHtml\Model\CustomHtmlBlock; +use Websmid\CustomHtml\Model\CustomHtmlFaq; use Validate; final class CustomHtmlBlockDataProvider implements FormDataProviderInterface @@ -19,8 +20,10 @@ final class CustomHtmlBlockDataProvider implements FormDataProviderInterface return [ 'id_ws_customhtml_group' => (int) $block->id_ws_customhtml_group, 'title' => $block->title, + 'type' => $block->type, 'show_title' => (bool) $block->show_title, 'content' => $block->content, + 'faqs' => $this->loadFaqs((int) $id), 'active' => (bool) $block->active, ]; } @@ -30,9 +33,21 @@ final class CustomHtmlBlockDataProvider implements FormDataProviderInterface return [ 'id_ws_customhtml_group' => 0, 'title' => '', + 'type' => 'html', 'show_title' => false, 'content' => '', + 'faqs' => [], 'active' => true, ]; } + + private function loadFaqs(int $idBlock): array + { + return array_map(static function (array $faq) { + return [ + 'question' => $faq['question'], + 'answer' => $faq['answer'], + ]; + }, CustomHtmlFaq::getByBlockId($idBlock)); + } } diff --git a/src/Form/Type/CustomHtmlBlockType.php b/src/Form/Type/CustomHtmlBlockType.php index cf9c45e..945eaf9 100644 --- a/src/Form/Type/CustomHtmlBlockType.php +++ b/src/Form/Type/CustomHtmlBlockType.php @@ -5,11 +5,13 @@ namespace Websmid\CustomHtml\Form\Type; use Symfony\Component\Form\AbstractType; 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\TextType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use PrestaShop\PrestaShop\Core\Form\ConfigurableFormChoiceProviderInterface; use PrestaShopBundle\Form\Admin\Type\SwitchType; use Symfony\Contracts\Translation\TranslatorInterface; +use Websmid\CustomHtml\Config\Config; class CustomHtmlBlockType extends AbstractType { @@ -37,6 +39,11 @@ class CustomHtmlBlockType extends AbstractType 'label' => $this->translator->trans('Title', [], 'Modules.Wscustomhtml.Admin'), 'required' => true, ]) + ->add('type', ChoiceType::class, [ + 'label' => $this->translator->trans('Block type', [], 'Modules.Wscustomhtml.Admin'), + 'choices' => array_flip(Config::BLOCK_TYPES), + 'required' => true, + ]) ->add('show_title', SwitchType::class, [ 'label' => $this->translator->trans('Show title', [], 'Modules.Wscustomhtml.Admin'), 'required' => false, @@ -45,6 +52,15 @@ class CustomHtmlBlockType extends AbstractType 'label' => $this->translator->trans('Content', [], 'Modules.Wscustomhtml.Admin'), 'required' => false, 'attr' => ['class' => 'autoload_rte'], + 'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-html'], + ]) + ->add('faqs', CollectionType::class, [ + 'entry_type' => CustomHtmlFaqType::class, + 'allow_add' => true, + 'allow_delete' => true, + 'by_reference' => false, + 'prototype' => true, + 'required' => false, ]) ->add('active', SwitchType::class, [ 'label' => $this->translator->trans('Active', [], 'Modules.Wscustomhtml.Admin'), diff --git a/src/Form/Type/CustomHtmlFaqType.php b/src/Form/Type/CustomHtmlFaqType.php new file mode 100644 index 0000000..bd952f9 --- /dev/null +++ b/src/Form/Type/CustomHtmlFaqType.php @@ -0,0 +1,32 @@ +translator = $translator; + } + + public function buildForm(FormBuilderInterface $formBuilder, array $options): void + { + $formBuilder + ->add('question', TextType::class, [ + 'label' => $this->translator->trans('Question', [], 'Modules.Wscustomhtml.Admin'), + 'required' => false, + ]) + ->add('answer', TextareaType::class, [ + 'label' => $this->translator->trans('Answer', [], 'Modules.Wscustomhtml.Admin'), + 'required' => false, + ]); + } +} diff --git a/src/Grid/Definition/Factory/CustomHtmlBlockGridDefinitionFactory.php b/src/Grid/Definition/Factory/CustomHtmlBlockGridDefinitionFactory.php index 8c168dd..33ccb58 100644 --- a/src/Grid/Definition/Factory/CustomHtmlBlockGridDefinitionFactory.php +++ b/src/Grid/Definition/Factory/CustomHtmlBlockGridDefinitionFactory.php @@ -15,7 +15,9 @@ use PrestaShop\PrestaShop\Core\Grid\Filter\Filter; use PrestaShop\PrestaShop\Core\Grid\Filter\FilterCollection; use PrestaShop\PrestaShop\Core\Hook\HookDispatcherInterface; use PrestaShopBundle\Form\Admin\Type\SearchAndResetType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\TextType; +use Websmid\CustomHtml\Config\Config; final class CustomHtmlBlockGridDefinitionFactory extends AbstractGridDefinitionFactory { @@ -71,6 +73,11 @@ final class CustomHtmlBlockGridDefinitionFactory extends AbstractGridDefinitionF ->setName($this->trans('Group', [], 'Modules.Wscustomhtml.Admin')) ->setOptions(['field' => 'group_title']) ) + ->add( + (new DataColumn('type')) + ->setName($this->trans('Type', [], 'Modules.Wscustomhtml.Admin')) + ->setOptions(['field' => 'type']) + ) ->add( (new DataColumn('active')) ->setName($this->trans('Active', [], 'Admin.Global')) @@ -120,6 +127,15 @@ final class CustomHtmlBlockGridDefinitionFactory extends AbstractGridDefinitionF 'attr' => ['placeholder' => $this->trans('Group', [], 'Modules.Wscustomhtml.Admin')], ]) ) + ->add( + (new Filter('type', ChoiceType::class)) + ->setAssociatedColumn('type') + ->setTypeOptions([ + 'required' => false, + 'choices' => array_flip(Config::BLOCK_TYPES), + 'placeholder' => $this->trans('All', [], 'Admin.Global'), + ]) + ) ->add( (new Filter('actions', SearchAndResetType::class)) ->setAssociatedColumn('actions') diff --git a/src/Grid/Query/CustomHtmlBlockQueryBuilder.php b/src/Grid/Query/CustomHtmlBlockQueryBuilder.php index 0c7fad2..ad20f83 100644 --- a/src/Grid/Query/CustomHtmlBlockQueryBuilder.php +++ b/src/Grid/Query/CustomHtmlBlockQueryBuilder.php @@ -45,6 +45,7 @@ final class CustomHtmlBlockQueryBuilder extends AbstractDoctrineQueryBuilder 'wchb.`id_ws_customhtml`', 'wchb.`title`', 'wchg.`title` AS group_title', + 'wchb.`type`', 'wchb.`active`', 'wchb.`ordering`', 'wchb.`date_add`' @@ -108,6 +109,12 @@ final class CustomHtmlBlockQueryBuilder extends AbstractDoctrineQueryBuilder continue; } + if ($filterName === 'type') { + $qb->andWhere('wchb.`type` = :type') + ->setParameter('type', pSQL($filterValue)); + continue; + } + $qb->andWhere('wchb.`' . $filterName . '` LIKE :' . $filterName) ->setParameter($filterName, '%' . pSQL($filterValue) . '%'); } diff --git a/src/Model/CustomHtmlBlock.php b/src/Model/CustomHtmlBlock.php index e485c60..87ac2d3 100644 --- a/src/Model/CustomHtmlBlock.php +++ b/src/Model/CustomHtmlBlock.php @@ -16,6 +16,9 @@ class CustomHtmlBlock extends ObjectModel /** @var string */ public $title = ''; + /** @var string */ + public $type = 'html'; + /** @var string */ public $content = ''; @@ -40,6 +43,7 @@ class CustomHtmlBlock extends ObjectModel 'fields' => [ 'id_ws_customhtml_group' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true], 'title' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 256], + 'type' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 32], 'content' => ['type' => self::TYPE_HTML, 'validate' => 'isCleanHtml'], 'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'show_title' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], @@ -58,7 +62,7 @@ class CustomHtmlBlock extends ObjectModel } $result = Db::getInstance()->executeS( - 'SELECT `id_ws_customhtml`, `id_ws_customhtml_group`, `title`, `content`, `active`, `show_title`, `ordering` + 'SELECT `id_ws_customhtml`, `id_ws_customhtml_group`, `title`, `type`, `content`, `active`, `show_title`, `ordering` FROM `' . _DB_PREFIX_ . 'ws_customhtml` WHERE `id_ws_customhtml_group` IN (' . implode(',', $groupIds) . ') AND `active` = 1 diff --git a/src/Model/CustomHtmlFaq.php b/src/Model/CustomHtmlFaq.php new file mode 100644 index 0000000..fd4a30b --- /dev/null +++ b/src/Model/CustomHtmlFaq.php @@ -0,0 +1,60 @@ + 'ws_customhtml_faq', + 'primary' => 'id_ws_customhtml_faq', + 'fields' => [ + 'id_ws_customhtml' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true], + 'question' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 512], + 'answer' => ['type' => self::TYPE_HTML, 'validate' => 'isCleanHtml'], + 'ordering' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], + 'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'], + 'date_upd' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'], + ], + ]; + + public static function getByBlockId(int $idBlock): array + { + $result = Db::getInstance()->executeS( + 'SELECT `id_ws_customhtml_faq`, `question`, `answer`, `ordering` + FROM `' . _DB_PREFIX_ . 'ws_customhtml_faq` + WHERE `id_ws_customhtml` = ' . (int) $idBlock . ' + ORDER BY `ordering` ASC' + ); + + return $result ?: []; + } + + public static function deleteByBlockId(int $idBlock): void + { + Db::getInstance()->delete('ws_customhtml_faq', '`id_ws_customhtml` = ' . (int) $idBlock); + } +} diff --git a/src/Module/AdminHooks.php b/src/Module/AdminHooks.php index 0d05f41..e691fba 100644 --- a/src/Module/AdminHooks.php +++ b/src/Module/AdminHooks.php @@ -3,6 +3,8 @@ namespace Websmid\CustomHtml\Module; use Websmid\CustomHtml\Config\Config; +use PrestaShop\PrestaShop\Core\Grid\Data\GridData; +use PrestaShop\PrestaShop\Core\Grid\Record\RecordCollection; use Tools; trait AdminHooks @@ -27,4 +29,17 @@ trait AdminHooks $this->context->controller->addCSS($this->getPathUri() . 'views/css/admin/grid.css'); } + + public function hookActionCustomhtmlblockGridDataModifier(array $params): void + { + $gridData = $params['data']; + + $records = array_map(static function (array $row) { + $row['type'] = Config::BLOCK_TYPES[$row['type']] ?? $row['type']; + + return $row; + }, $gridData->getRecords()->all()); + + $params['data'] = new GridData(new RecordCollection($records), $gridData->getRecordsTotal(), $gridData->getQuery()); + } } diff --git a/src/Module/DisplayHooks.php b/src/Module/DisplayHooks.php index 7dc4865..09e3e9a 100644 --- a/src/Module/DisplayHooks.php +++ b/src/Module/DisplayHooks.php @@ -3,6 +3,7 @@ namespace Websmid\CustomHtml\Module; use Websmid\CustomHtml\Model\CustomHtmlBlock; +use Websmid\CustomHtml\Model\CustomHtmlFaq; use Websmid\CustomHtml\Model\CustomHtmlGroup; use Context; @@ -54,6 +55,7 @@ trait DisplayHooks } $blocksByGroup = CustomHtmlBlock::getActiveByGroupIds(array_column($groups, 'id_ws_customhtml_group')); + $blocksByGroup = $this->attachFaqsToBlocks($blocksByGroup); $groups = array_map(static function (array $group) use ($blocksByGroup) { $group['blocks'] = $blocksByGroup[(int) $group['id_ws_customhtml_group']] ?? []; @@ -65,4 +67,17 @@ trait DisplayHooks return !empty($group['blocks']); })); } + + private function attachFaqsToBlocks(array $blocksByGroup): array + { + return array_map(static function (array $blocks) { + return array_map(static function (array $block) { + if ($block['type'] === 'faq') { + $block['faqs'] = CustomHtmlFaq::getByBlockId((int) $block['id_ws_customhtml']); + } + + return $block; + }, $blocks); + }, $blocksByGroup); + } } diff --git a/src/Module/Install.php b/src/Module/Install.php index 5ad1cda..85fc1a1 100644 --- a/src/Module/Install.php +++ b/src/Module/Install.php @@ -3,7 +3,9 @@ namespace Websmid\CustomHtml\Module; use Websmid\CustomHtml\Config\Config; +use Context; use Db; +use Hook; use Language; use Tab; @@ -31,6 +33,11 @@ trait Install return false; } + if (!$this->registerGroupHooks()) { + error_log('[ws_customhtml] registerGroupHooks() failed'); + return false; + } + return true; } @@ -55,6 +62,7 @@ trait Install `id_ws_customhtml` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `id_ws_customhtml_group` INT(11) NOT NULL, `title` VARCHAR(256) NOT NULL DEFAULT \'\', + `type` VARCHAR(32) NOT NULL DEFAULT \'html\', `content` LONGTEXT, `active` TINYINT(1) NOT NULL DEFAULT 1, `show_title` TINYINT(1) NOT NULL DEFAULT 0, @@ -65,6 +73,19 @@ trait Install ); '; + $sqlToExec[] = ' + CREATE TABLE IF NOT EXISTS ' . _DB_PREFIX_ . 'ws_customhtml_faq ( + `id_ws_customhtml_faq` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, + `id_ws_customhtml` INT(11) NOT NULL, + `question` VARCHAR(512) NOT NULL DEFAULT \'\', + `answer` TEXT, + `ordering` INT(11) NOT NULL DEFAULT 0, + `date_add` DATETIME NOT NULL, + `date_upd` DATETIME NOT NULL, + KEY `id_ws_customhtml` (`id_ws_customhtml`) + ); + '; + foreach ($sqlToExec as $sqlBlock) { Db::getInstance()->execute(trim($sqlBlock)); } @@ -83,6 +104,29 @@ trait Install return true; } + private function registerGroupHooks(): bool + { + $hookNames = Db::getInstance()->executeS( + 'SELECT DISTINCT `hook_name` FROM `' . _DB_PREFIX_ . 'ws_customhtml_group`' + ); + + $idShop = (int) Context::getContext()->shop->id; + + foreach ($hookNames as $row) { + $hookName = $row['hook_name']; + + if (Hook::isModuleRegisteredOnHook($this, $hookName, $idShop)) { + continue; + } + + if (!$this->registerHook($hookName)) { + return false; + } + } + + return true; + } + private function registerTabs(array $moduleTabs): bool { $languages = Language::getLanguages(false); diff --git a/upgrade/upgrade-1.2.0.php b/upgrade/upgrade-1.2.0.php new file mode 100644 index 0000000..8940450 --- /dev/null +++ b/upgrade/upgrade-1.2.0.php @@ -0,0 +1,38 @@ +executeS( + 'SHOW COLUMNS FROM `' . _DB_PREFIX_ . 'ws_customhtml` LIKE \'type\'' + ); + + if (empty($columns)) { + if (!Db::getInstance()->execute( + 'ALTER TABLE `' . _DB_PREFIX_ . 'ws_customhtml` + ADD COLUMN `type` VARCHAR(32) NOT NULL DEFAULT \'html\' AFTER `title`' + )) { + return false; + } + } + + if (!Db::getInstance()->execute( + 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ws_customhtml_faq` ( + `id_ws_customhtml_faq` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, + `id_ws_customhtml` INT(11) NOT NULL, + `question` VARCHAR(512) NOT NULL DEFAULT \'\', + `answer` TEXT, + `ordering` INT(11) NOT NULL DEFAULT 0, + `date_add` DATETIME NOT NULL, + `date_upd` DATETIME NOT NULL, + KEY `id_ws_customhtml` (`id_ws_customhtml`) + )' + )) { + return false; + } + + return $module->registerHook('actionCustomhtmlblockGridDataModifier'); +} diff --git a/views/templates/admin/form/blocks/block_form.html.twig b/views/templates/admin/form/blocks/block_form.html.twig index 01e4c2d..3267dc6 100644 --- a/views/templates/admin/form/blocks/block_form.html.twig +++ b/views/templates/admin/form/blocks/block_form.html.twig @@ -1,6 +1,12 @@ {% import '@PrestaShop/Admin/macros.html.twig' as ps %} {% form_theme customHtmlBlockForm '@PrestaShop/Admin/TwigTemplateForm/prestashop_ui_kit.html.twig' %} +{% macro faqFields(faqForm) %} + {{ form_row(faqForm.question) }} + {{ form_row(faqForm.answer) }} +{% endmacro %} +{% import _self as faqMacros %} + {% block customhtmlblock_form_block %}
{{ form_start(customHtmlBlockForm) }} @@ -10,6 +16,38 @@
+ {{ form_row(customHtmlBlockForm.id_ws_customhtml_group) }} + {{ form_row(customHtmlBlockForm.title) }} + {{ form_row(customHtmlBlockForm.type) }} + {{ form_row(customHtmlBlockForm.show_title) }} + {{ form_row(customHtmlBlockForm.content) }} + +
+ +
+ {% for faqRow in customHtmlBlockForm.faqs %} +
+
+ {{ faqMacros.faqFields(faqRow) }} + +
+
+ {% endfor %} +
+ +
+ + {{ form_row(customHtmlBlockForm.active) }} {{ form_rest(customHtmlBlockForm) }}
{/foreach} diff --git a/ws_customhtml.php b/ws_customhtml.php index fb0cfbf..b305729 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.1.0'; + $this->version = '1.2.0'; $this->author = 'Isabelle Oving-Anno | De Websmid BV'; $this->need_instance = 0; $this->bootstrap = true;