feat(main): Add FAQ accordion
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -33,6 +33,14 @@ services:
|
||||
arguments:
|
||||
- '@websmid.customhtml.form.customhtmlgroup_data_handler'
|
||||
|
||||
websmid.customhtml.form.type.customhtml_faq:
|
||||
public: true
|
||||
class: 'Websmid\CustomHtml\Form\Type\CustomHtmlFaqType'
|
||||
arguments:
|
||||
- '@translator'
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
websmid.customhtml.form.type.customhtml_block:
|
||||
public: true
|
||||
class: 'Websmid\CustomHtml\Form\Type\CustomHtmlBlockType'
|
||||
|
||||
@@ -6,6 +6,12 @@ class Config
|
||||
{
|
||||
const HOOKS = [
|
||||
'actionAdminControllerSetMedia',
|
||||
'actionCustomhtmlblockGridDataModifier',
|
||||
];
|
||||
|
||||
const BLOCK_TYPES = [
|
||||
'html' => 'HTML',
|
||||
'faq' => 'FAQ Accordion',
|
||||
];
|
||||
|
||||
const ADMIN_CONTROLLERS = [
|
||||
|
||||
@@ -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'));
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomHtml\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class CustomHtmlFaqType extends AbstractType
|
||||
{
|
||||
private $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
{
|
||||
$this->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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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')
|
||||
|
||||
@@ -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) . '%');
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomHtml\Model;
|
||||
|
||||
use ObjectModel;
|
||||
use Db;
|
||||
|
||||
class CustomHtmlFaq extends ObjectModel
|
||||
{
|
||||
/** @var int */
|
||||
public $id;
|
||||
|
||||
/** @var int */
|
||||
public $id_ws_customhtml;
|
||||
|
||||
/** @var string */
|
||||
public $question = '';
|
||||
|
||||
/** @var string */
|
||||
public $answer = '';
|
||||
|
||||
/** @var int */
|
||||
public $ordering = 0;
|
||||
|
||||
/** @var string */
|
||||
public $date_add;
|
||||
|
||||
/** @var string */
|
||||
public $date_upd;
|
||||
|
||||
public static $definition = [
|
||||
'table' => '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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function upgrade_module_1_2_0($module)
|
||||
{
|
||||
$columns = Db::getInstance()->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');
|
||||
}
|
||||
@@ -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 %}
|
||||
<div class="card">
|
||||
{{ form_start(customHtmlBlockForm) }}
|
||||
@@ -10,6 +16,38 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(customHtmlBlockForm.id_ws_customhtml_group) }}
|
||||
{{ form_row(customHtmlBlockForm.title) }}
|
||||
{{ form_row(customHtmlBlockForm.type) }}
|
||||
{{ form_row(customHtmlBlockForm.show_title) }}
|
||||
{{ form_row(customHtmlBlockForm.content) }}
|
||||
|
||||
<div class="customhtmlblock-type-field customhtmlblock-type-faq">
|
||||
<label class="form-control-label">
|
||||
{{ 'FAQ items'|trans({}, 'Modules.Wscustomhtml.Admin') }}
|
||||
</label>
|
||||
<div
|
||||
id="customhtmlblock-faq-items"
|
||||
data-prototype="{{ faqMacros.faqFields(customHtmlBlockForm.faqs.vars.prototype)|e('html_attr') }}"
|
||||
data-index="{{ customHtmlBlockForm.faqs|length }}"
|
||||
>
|
||||
{% for faqRow in customHtmlBlockForm.faqs %}
|
||||
<div class="customhtmlblock-faq-item card mb-2">
|
||||
<div class="card-body">
|
||||
{{ faqMacros.faqFields(faqRow) }}
|
||||
<button type="button" class="btn btn-outline-danger btn-sm customhtmlblock-remove-faq">
|
||||
<i class="material-icons">delete</i> {{ 'Remove'|trans({}, 'Admin.Actions') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="button" id="customhtmlblock-add-faq" class="btn btn-outline-primary btn-sm">
|
||||
<i class="material-icons">add</i> {{ 'Add FAQ item'|trans({}, 'Modules.Wscustomhtml.Admin') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ form_row(customHtmlBlockForm.active) }}
|
||||
{{ form_rest(customHtmlBlockForm) }}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
|
||||
@@ -13,6 +13,44 @@
|
||||
window.prestashop.component.initComponents([
|
||||
'TinyMCEEditor',
|
||||
]);
|
||||
|
||||
function toggleBlockTypeFields() {
|
||||
var type = $('#custom_html_block_type').val();
|
||||
|
||||
if (type === 'html') {
|
||||
$('.customhtmlblock-type-html').show();
|
||||
$('.customhtmlblock-type-faq').hide();
|
||||
} else {
|
||||
$('.customhtmlblock-type-html').hide();
|
||||
$('.customhtmlblock-type-faq').show();
|
||||
}
|
||||
}
|
||||
|
||||
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
|
||||
toggleBlockTypeFields();
|
||||
|
||||
var removeFaqLabel = '{{ 'Remove'|trans({}, 'Admin.Actions')|e('js') }}';
|
||||
var faqContainer = $('#customhtmlblock-faq-items');
|
||||
|
||||
$('#customhtmlblock-add-faq').on('click', function () {
|
||||
var index = faqContainer.data('index');
|
||||
var prototype = faqContainer.data('prototype').replace(/__name__/g, index);
|
||||
|
||||
var item = $('<div class="customhtmlblock-faq-item card mb-2"><div class="card-body"></div></div>');
|
||||
item.find('.card-body')
|
||||
.append(prototype)
|
||||
.append(
|
||||
'<button type="button" class="btn btn-outline-danger btn-sm customhtmlblock-remove-faq">' +
|
||||
'<i class="material-icons">delete</i> ' + removeFaqLabel + '</button>'
|
||||
);
|
||||
|
||||
faqContainer.append(item);
|
||||
faqContainer.data('index', index + 1);
|
||||
});
|
||||
|
||||
faqContainer.on('click', '.customhtmlblock-remove-faq', function () {
|
||||
$(this).closest('.customhtmlblock-faq-item').remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -13,6 +13,44 @@
|
||||
window.prestashop.component.initComponents([
|
||||
'TinyMCEEditor',
|
||||
]);
|
||||
|
||||
function toggleBlockTypeFields() {
|
||||
var type = $('#custom_html_block_type').val();
|
||||
|
||||
if (type === 'html') {
|
||||
$('.customhtmlblock-type-html').show();
|
||||
$('.customhtmlblock-type-faq').hide();
|
||||
} else {
|
||||
$('.customhtmlblock-type-html').hide();
|
||||
$('.customhtmlblock-type-faq').show();
|
||||
}
|
||||
}
|
||||
|
||||
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
|
||||
toggleBlockTypeFields();
|
||||
|
||||
var removeFaqLabel = '{{ 'Remove'|trans({}, 'Admin.Actions')|e('js') }}';
|
||||
var faqContainer = $('#customhtmlblock-faq-items');
|
||||
|
||||
$('#customhtmlblock-add-faq').on('click', function () {
|
||||
var index = faqContainer.data('index');
|
||||
var prototype = faqContainer.data('prototype').replace(/__name__/g, index);
|
||||
|
||||
var item = $('<div class="customhtmlblock-faq-item card mb-2"><div class="card-body"></div></div>');
|
||||
item.find('.card-body')
|
||||
.append(prototype)
|
||||
.append(
|
||||
'<button type="button" class="btn btn-outline-danger btn-sm customhtmlblock-remove-faq">' +
|
||||
'<i class="material-icons">delete</i> ' + removeFaqLabel + '</button>'
|
||||
);
|
||||
|
||||
faqContainer.append(item);
|
||||
faqContainer.data('index', index + 1);
|
||||
});
|
||||
|
||||
faqContainer.on('click', '.customhtmlblock-remove-faq', function () {
|
||||
$(this).closest('.customhtmlblock-faq-item').remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{if !empty($block.faqs)}
|
||||
<div class="accordion customhtml-faq-accordion" id="customhtml-faq-{$block.id_ws_customhtml|intval}">
|
||||
{foreach from=$block.faqs item="faq" name="customhtmlFaqLoop"}
|
||||
<div class="card">
|
||||
<div class="card-header" id="customhtml-faq-heading-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}">
|
||||
<button
|
||||
class="btn btn-link{if !$smarty.foreach.customhtmlFaqLoop.first} collapsed{/if}"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#customhtml-faq-collapse-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}"
|
||||
aria-expanded="{if $smarty.foreach.customhtmlFaqLoop.first}true{else}false{/if}"
|
||||
aria-controls="customhtml-faq-collapse-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}"
|
||||
>
|
||||
{$faq.question|escape:'html':'UTF-8'}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
id="customhtml-faq-collapse-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}"
|
||||
class="collapse{if $smarty.foreach.customhtmlFaqLoop.first} show{/if}"
|
||||
aria-labelledby="customhtml-faq-heading-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}"
|
||||
data-parent="#customhtml-faq-{$block.id_ws_customhtml|intval}"
|
||||
>
|
||||
<div class="card-body">
|
||||
{$faq.answer nofilter}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -7,7 +7,11 @@
|
||||
{if $block.show_title && !empty($block.title)}
|
||||
<div class="customhtml-title">{$block.title|escape:'html':'UTF-8'}</div>
|
||||
{/if}
|
||||
<div class="customhtml-content">{$block.content nofilter}</div>
|
||||
{if $block.type == 'faq'}
|
||||
{include file="module:ws_customhtml/views/templates/hook/_faq_accordion.tpl" block=$block}
|
||||
{else}
|
||||
<div class="customhtml-content">{$block.content nofilter}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user