feat(main): Add banner option
This commit is contained in:
@@ -1 +1,3 @@
|
|||||||
/vendor/
|
/vendor/
|
||||||
|
/views/img/banners/*
|
||||||
|
!/views/img/banners/.gitkeep
|
||||||
|
|||||||
@@ -13,14 +13,19 @@ class Config
|
|||||||
const BLOCK_TYPES = [
|
const BLOCK_TYPES = [
|
||||||
'html' => 'HTML',
|
'html' => 'HTML',
|
||||||
'faq' => 'FAQ Accordion',
|
'faq' => 'FAQ Accordion',
|
||||||
|
'banner' => 'Banner',
|
||||||
];
|
];
|
||||||
|
|
||||||
const ADMIN_CONTROLLERS = [
|
const BANNER_IMAGE_ALLOWED_EXTENSIONS = [
|
||||||
'AdminCustomHtmlParent',
|
'jpg' => 'image/jpeg',
|
||||||
'AdminCustomHtmlOverview',
|
'jpeg' => 'image/jpeg',
|
||||||
'AdminCustomHtmlBlockOverview',
|
'png' => 'image/png',
|
||||||
|
'gif' => 'image/gif',
|
||||||
|
'webp' => 'image/webp',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const BANNER_IMAGE_MAX_SIZE = 5242880;
|
||||||
|
|
||||||
// Order matters: the parent tab must be registered before its children so
|
// Order matters: the parent tab must be registered before its children so
|
||||||
// Tab::getIdFromClassName('AdminCustomHtmlParent') resolves during install.
|
// Tab::getIdFromClassName('AdminCustomHtmlParent') resolves during install.
|
||||||
const TABS = [
|
const TABS = [
|
||||||
|
|||||||
@@ -95,9 +95,13 @@ class CustomHtmlBlockController extends FrameworkBundleAdminController
|
|||||||
$this->addFlash('error', $this->getErrorMessageForException($e, []));
|
$this->addFlash('error', $this->getErrorMessageForException($e, []));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$block = new CustomHtmlBlock($idCustomHtmlBlock);
|
||||||
|
$currentImageUrl = Validate::isLoadedObject($block) ? CustomHtmlBlock::getBannerImageUrl($block->image) : '';
|
||||||
|
|
||||||
return $this->render('@Modules/ws_customhtml/views/templates/admin/form/customhtmlblock_edit.html.twig', [
|
return $this->render('@Modules/ws_customhtml/views/templates/admin/form/customhtmlblock_edit.html.twig', [
|
||||||
'customHtmlBlockForm' => $form->createView(),
|
'customHtmlBlockForm' => $form->createView(),
|
||||||
'idCustomHtmlBlock' => $idCustomHtmlBlock,
|
'idCustomHtmlBlock' => $idCustomHtmlBlock,
|
||||||
|
'currentImageUrl' => $currentImageUrl,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
namespace Websmid\CustomHtml\Form\DataHandler;
|
namespace Websmid\CustomHtml\Form\DataHandler;
|
||||||
|
|
||||||
use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\DataHandler\FormDataHandlerInterface;
|
use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\DataHandler\FormDataHandlerInterface;
|
||||||
|
use Websmid\CustomHtml\Config\Config;
|
||||||
use Websmid\CustomHtml\Model\CustomHtmlBlock;
|
use Websmid\CustomHtml\Model\CustomHtmlBlock;
|
||||||
use Websmid\CustomHtml\Model\CustomHtmlFaq;
|
use Websmid\CustomHtml\Model\CustomHtmlFaq;
|
||||||
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
use Db;
|
use Db;
|
||||||
|
use Exception;
|
||||||
use Validate;
|
use Validate;
|
||||||
|
|
||||||
final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
||||||
@@ -21,6 +24,10 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
|||||||
$block->show_title = (int) $formData['show_title'];
|
$block->show_title = (int) $formData['show_title'];
|
||||||
$block->custom_class = (string) ($formData['custom_class'] ?? '');
|
$block->custom_class = (string) ($formData['custom_class'] ?? '');
|
||||||
$block->content = (string) $formData['content'];
|
$block->content = (string) $formData['content'];
|
||||||
|
$block->description = (string) ($formData['description'] ?? '');
|
||||||
|
$block->extra_description = (string) ($formData['extra_description'] ?? '');
|
||||||
|
$block->link = (string) ($formData['link'] ?? '');
|
||||||
|
$block->image = $this->saveUploadedImage($formData['image'] ?? null, '');
|
||||||
$block->active = (int) $formData['active'];
|
$block->active = (int) $formData['active'];
|
||||||
$block->ordering = $this->getNextOrdering($idGroup);
|
$block->ordering = $this->getNextOrdering($idGroup);
|
||||||
|
|
||||||
@@ -47,6 +54,10 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
|||||||
$block->show_title = (int) $formData['show_title'];
|
$block->show_title = (int) $formData['show_title'];
|
||||||
$block->custom_class = (string) ($formData['custom_class'] ?? '');
|
$block->custom_class = (string) ($formData['custom_class'] ?? '');
|
||||||
$block->content = (string) $formData['content'];
|
$block->content = (string) $formData['content'];
|
||||||
|
$block->description = (string) ($formData['description'] ?? '');
|
||||||
|
$block->extra_description = (string) ($formData['extra_description'] ?? '');
|
||||||
|
$block->link = (string) ($formData['link'] ?? '');
|
||||||
|
$block->image = $this->saveUploadedImage($formData['image'] ?? null, $block->image);
|
||||||
$block->active = (int) $formData['active'];
|
$block->active = (int) $formData['active'];
|
||||||
|
|
||||||
if (!$block->update()) {
|
if (!$block->update()) {
|
||||||
@@ -58,6 +69,49 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates and stores an uploaded banner image, replacing the previous
|
||||||
|
* one if any. Never trusts the client-supplied filename or MIME header —
|
||||||
|
* the real MIME type is detected server-side and a random filename is
|
||||||
|
* generated, so nothing from the upload itself ends up on disk.
|
||||||
|
*/
|
||||||
|
private function saveUploadedImage(?UploadedFile $file, string $currentFilename): string
|
||||||
|
{
|
||||||
|
if (null === $file) {
|
||||||
|
return $currentFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$file->isValid()) {
|
||||||
|
throw new Exception('The uploaded image could not be read.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file->getSize() > Config::BANNER_IMAGE_MAX_SIZE) {
|
||||||
|
throw new Exception('The uploaded image is too large (max 5 MB).');
|
||||||
|
}
|
||||||
|
|
||||||
|
$mimeType = $file->getMimeType();
|
||||||
|
$extension = array_search($mimeType, Config::BANNER_IMAGE_ALLOWED_EXTENSIONS, true);
|
||||||
|
|
||||||
|
if (false === $extension) {
|
||||||
|
throw new Exception('The uploaded file is not a supported image type (jpg, png, gif, webp).');
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetDir = CustomHtmlBlock::getBannerImageDir();
|
||||||
|
|
||||||
|
if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true) && !is_dir($targetDir)) {
|
||||||
|
throw new Exception('Unable to create the banner image directory.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$newFilename = bin2hex(random_bytes(16)) . '.' . $extension;
|
||||||
|
$file->move($targetDir, $newFilename);
|
||||||
|
|
||||||
|
if (!empty($currentFilename) && is_file($targetDir . $currentFilename)) {
|
||||||
|
unlink($targetDir . $currentFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newFilename;
|
||||||
|
}
|
||||||
|
|
||||||
private function getNextOrdering(int $idGroup): int
|
private function getNextOrdering(int $idGroup): int
|
||||||
{
|
{
|
||||||
$maxOrdering = Db::getInstance()->getValue(
|
$maxOrdering = Db::getInstance()->getValue(
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ final class CustomHtmlBlockDataProvider implements FormDataProviderInterface
|
|||||||
'show_title' => (bool) $block->show_title,
|
'show_title' => (bool) $block->show_title,
|
||||||
'custom_class' => $block->custom_class,
|
'custom_class' => $block->custom_class,
|
||||||
'content' => $block->content,
|
'content' => $block->content,
|
||||||
|
'image' => null,
|
||||||
|
'description' => $block->description,
|
||||||
|
'extra_description' => $block->extra_description,
|
||||||
|
'link' => $block->link,
|
||||||
'faqs' => $this->loadFaqs((int) $id),
|
'faqs' => $this->loadFaqs((int) $id),
|
||||||
'active' => (bool) $block->active,
|
'active' => (bool) $block->active,
|
||||||
];
|
];
|
||||||
@@ -38,6 +42,10 @@ final class CustomHtmlBlockDataProvider implements FormDataProviderInterface
|
|||||||
'show_title' => false,
|
'show_title' => false,
|
||||||
'custom_class' => '',
|
'custom_class' => '',
|
||||||
'content' => '',
|
'content' => '',
|
||||||
|
'image' => null,
|
||||||
|
'description' => '',
|
||||||
|
'extra_description' => '',
|
||||||
|
'link' => '',
|
||||||
'faqs' => [],
|
'faqs' => [],
|
||||||
'active' => true,
|
'active' => true,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use Symfony\Component\Form\AbstractType;
|
|||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
use PrestaShop\PrestaShop\Core\Form\ConfigurableFormChoiceProviderInterface;
|
use PrestaShop\PrestaShop\Core\Form\ConfigurableFormChoiceProviderInterface;
|
||||||
@@ -35,23 +36,20 @@ class CustomHtmlBlockType extends AbstractType
|
|||||||
'choices' => $this->groupChoiceProvider->getChoices(),
|
'choices' => $this->groupChoiceProvider->getChoices(),
|
||||||
'required' => true,
|
'required' => true,
|
||||||
])
|
])
|
||||||
->add('title', TextType::class, [
|
|
||||||
'label' => $this->translator->trans('Title', [], 'Modules.Wscustomhtml.Admin'),
|
|
||||||
'required' => true,
|
|
||||||
])
|
|
||||||
->add('type', ChoiceType::class, [
|
->add('type', ChoiceType::class, [
|
||||||
'label' => $this->translator->trans('Block type', [], 'Modules.Wscustomhtml.Admin'),
|
'label' => $this->translator->trans('Block type', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
'choices' => array_flip(Config::BLOCK_TYPES),
|
'choices' => array_flip(Config::BLOCK_TYPES),
|
||||||
'required' => true,
|
'required' => true,
|
||||||
])
|
])
|
||||||
|
->add('title', TextType::class, [
|
||||||
|
'label' => $this->translator->trans('Title', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
|
'required' => true,
|
||||||
|
'row_attr' => ['class' => 'col-8'],
|
||||||
|
])
|
||||||
->add('show_title', SwitchType::class, [
|
->add('show_title', SwitchType::class, [
|
||||||
'label' => $this->translator->trans('Show title', [], 'Modules.Wscustomhtml.Admin'),
|
'label' => $this->translator->trans('Show title', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
'required' => false,
|
'required' => false,
|
||||||
])
|
'row_attr' => ['class' => 'col-3'],
|
||||||
->add('custom_class', TextType::class, [
|
|
||||||
'label' => $this->translator->trans('Custom classes', [], 'Modules.Wscustomhtml.Admin'),
|
|
||||||
'required' => false,
|
|
||||||
'attr' => ['placeholder' => 'col-md-6'],
|
|
||||||
])
|
])
|
||||||
->add('content', TextareaType::class, [
|
->add('content', TextareaType::class, [
|
||||||
'label' => $this->translator->trans('Content', [], 'Modules.Wscustomhtml.Admin'),
|
'label' => $this->translator->trans('Content', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
@@ -59,6 +57,11 @@ class CustomHtmlBlockType extends AbstractType
|
|||||||
'attr' => ['class' => 'autoload_rte'],
|
'attr' => ['class' => 'autoload_rte'],
|
||||||
'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-html'],
|
'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-html'],
|
||||||
])
|
])
|
||||||
|
->add('image', FileType::class, [
|
||||||
|
'label' => $this->translator->trans('Banner image', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
|
'required' => false,
|
||||||
|
'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-banner'],
|
||||||
|
])
|
||||||
->add('faqs', CollectionType::class, [
|
->add('faqs', CollectionType::class, [
|
||||||
'entry_type' => CustomHtmlFaqType::class,
|
'entry_type' => CustomHtmlFaqType::class,
|
||||||
'allow_add' => true,
|
'allow_add' => true,
|
||||||
@@ -67,6 +70,27 @@ class CustomHtmlBlockType extends AbstractType
|
|||||||
'prototype' => true,
|
'prototype' => true,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
])
|
])
|
||||||
|
->add('description', TextareaType::class, [
|
||||||
|
'label' => $this->translator->trans('Description', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
|
'required' => false,
|
||||||
|
'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-banner'],
|
||||||
|
])
|
||||||
|
->add('extra_description', TextareaType::class, [
|
||||||
|
'label' => $this->translator->trans('Extra description', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
|
'required' => false,
|
||||||
|
'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-banner'],
|
||||||
|
])
|
||||||
|
->add('link', TextType::class, [
|
||||||
|
'label' => $this->translator->trans('Link', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
|
'required' => false,
|
||||||
|
'attr' => ['placeholder' => 'https://example.com or /internal-page'],
|
||||||
|
'row_attr' => ['class' => 'customhtmlblock-type-field customhtmlblock-type-banner'],
|
||||||
|
])
|
||||||
|
->add('custom_class', TextType::class, [
|
||||||
|
'label' => $this->translator->trans('Custom classes', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
|
'required' => false,
|
||||||
|
'attr' => ['placeholder' => 'col-md-6'],
|
||||||
|
])
|
||||||
->add('active', SwitchType::class, [
|
->add('active', SwitchType::class, [
|
||||||
'label' => $this->translator->trans('Active', [], 'Modules.Wscustomhtml.Admin'),
|
'label' => $this->translator->trans('Active', [], 'Modules.Wscustomhtml.Admin'),
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
|||||||
@@ -25,6 +25,18 @@ class CustomHtmlBlock extends ObjectModel
|
|||||||
/** @var string */
|
/** @var string */
|
||||||
public $custom_class = '';
|
public $custom_class = '';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $description = '';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $extra_description = '';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $link = '';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $image = '';
|
||||||
|
|
||||||
/** @var int */
|
/** @var int */
|
||||||
public $active = 1;
|
public $active = 1;
|
||||||
|
|
||||||
@@ -49,6 +61,10 @@ class CustomHtmlBlock extends ObjectModel
|
|||||||
'type' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 32],
|
'type' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 32],
|
||||||
'content' => ['type' => self::TYPE_HTML, 'validate' => 'isCleanHtml'],
|
'content' => ['type' => self::TYPE_HTML, 'validate' => 'isCleanHtml'],
|
||||||
'custom_class' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => false, 'size' => 128],
|
'custom_class' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => false, 'size' => 128],
|
||||||
|
'description' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => false, 'size' => 1024],
|
||||||
|
'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],
|
||||||
'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||||
'show_title' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
'show_title' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||||
'ordering' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
'ordering' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||||
@@ -57,6 +73,20 @@ class CustomHtmlBlock extends ObjectModel
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static function getBannerImageDir(): string
|
||||||
|
{
|
||||||
|
return _PS_MODULE_DIR_ . 'ws_customhtml/views/img/banners/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getBannerImageUrl(string $filename): string
|
||||||
|
{
|
||||||
|
if (empty($filename)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return _MODULE_DIR_ . 'ws_customhtml/views/img/banners/' . $filename;
|
||||||
|
}
|
||||||
|
|
||||||
public static function getActiveByGroupIds(array $groupIds): array
|
public static function getActiveByGroupIds(array $groupIds): array
|
||||||
{
|
{
|
||||||
$groupIds = array_map('intval', $groupIds);
|
$groupIds = array_map('intval', $groupIds);
|
||||||
@@ -66,7 +96,8 @@ class CustomHtmlBlock extends ObjectModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
$result = Db::getInstance()->executeS(
|
$result = Db::getInstance()->executeS(
|
||||||
'SELECT `id_ws_customhtml`, `id_ws_customhtml_group`, `title`, `type`, `content`, `custom_class`, `active`, `show_title`, `ordering`
|
'SELECT `id_ws_customhtml`, `id_ws_customhtml_group`, `title`, `type`, `content`, `custom_class`,
|
||||||
|
`description`, `extra_description`, `link`, `image`, `active`, `show_title`, `ordering`
|
||||||
FROM `' . _DB_PREFIX_ . 'ws_customhtml`
|
FROM `' . _DB_PREFIX_ . 'ws_customhtml`
|
||||||
WHERE `id_ws_customhtml_group` IN (' . implode(',', $groupIds) . ')
|
WHERE `id_ws_customhtml_group` IN (' . implode(',', $groupIds) . ')
|
||||||
AND `active` = 1
|
AND `active` = 1
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace Websmid\CustomHtml\Module;
|
namespace Websmid\CustomHtml\Module;
|
||||||
|
|
||||||
use Websmid\CustomHtml\Config\Config;
|
use Websmid\CustomHtml\Config\Config;
|
||||||
|
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||||
use PrestaShop\PrestaShop\Core\Grid\Data\GridData;
|
use PrestaShop\PrestaShop\Core\Grid\Data\GridData;
|
||||||
use PrestaShop\PrestaShop\Core\Grid\Record\RecordCollection;
|
use PrestaShop\PrestaShop\Core\Grid\Record\RecordCollection;
|
||||||
use Tools;
|
use Tools;
|
||||||
@@ -21,9 +22,10 @@ trait AdminHooks
|
|||||||
|
|
||||||
public function hookActionAdminControllerSetMedia(): void
|
public function hookActionAdminControllerSetMedia(): void
|
||||||
{
|
{
|
||||||
$controllerName = $this->context->controller->controller_name ?? '';
|
$request = SymfonyContainer::getInstance()->get('request_stack')->getCurrentRequest();
|
||||||
|
$routeName = $request ? (string) $request->attributes->get('_route') : '';
|
||||||
|
|
||||||
if (!in_array($controllerName, Config::ADMIN_CONTROLLERS, true)) {
|
if (0 !== strpos($routeName, 'customhtml')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ trait DisplayHooks
|
|||||||
|
|
||||||
$blocksByGroup = CustomHtmlBlock::getActiveByGroupIds(array_column($groups, 'id_ws_customhtml_group'));
|
$blocksByGroup = CustomHtmlBlock::getActiveByGroupIds(array_column($groups, 'id_ws_customhtml_group'));
|
||||||
$blocksByGroup = $this->attachFaqsToBlocks($blocksByGroup);
|
$blocksByGroup = $this->attachFaqsToBlocks($blocksByGroup);
|
||||||
|
$blocksByGroup = $this->attachBannerImageUrls($blocksByGroup);
|
||||||
|
|
||||||
$groups = array_map(static function (array $group) use ($blocksByGroup) {
|
$groups = array_map(static function (array $group) use ($blocksByGroup) {
|
||||||
$group['blocks'] = $blocksByGroup[(int) $group['id_ws_customhtml_group']] ?? [];
|
$group['blocks'] = $blocksByGroup[(int) $group['id_ws_customhtml_group']] ?? [];
|
||||||
@@ -88,4 +89,17 @@ trait DisplayHooks
|
|||||||
}, $blocks);
|
}, $blocks);
|
||||||
}, $blocksByGroup);
|
}, $blocksByGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function attachBannerImageUrls(array $blocksByGroup): array
|
||||||
|
{
|
||||||
|
return array_map(static function (array $blocks) {
|
||||||
|
return array_map(static function (array $block) {
|
||||||
|
if ($block['type'] === 'banner') {
|
||||||
|
$block['image_url'] = CustomHtmlBlock::getBannerImageUrl($block['image']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $block;
|
||||||
|
}, $blocks);
|
||||||
|
}, $blocksByGroup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ trait Install
|
|||||||
`type` VARCHAR(32) NOT NULL DEFAULT \'html\',
|
`type` VARCHAR(32) NOT NULL DEFAULT \'html\',
|
||||||
`content` LONGTEXT,
|
`content` LONGTEXT,
|
||||||
`custom_class` VARCHAR(128) NOT NULL DEFAULT \'\',
|
`custom_class` VARCHAR(128) NOT NULL DEFAULT \'\',
|
||||||
|
`description` TEXT,
|
||||||
|
`extra_description` TEXT,
|
||||||
|
`link` VARCHAR(512) NOT NULL DEFAULT \'\',
|
||||||
|
`image` VARCHAR(255) NOT NULL DEFAULT \'\',
|
||||||
`active` TINYINT(1) NOT NULL DEFAULT 1,
|
`active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
`show_title` TINYINT(1) NOT NULL DEFAULT 0,
|
`show_title` TINYINT(1) NOT NULL DEFAULT 0,
|
||||||
`ordering` INT(11) NOT NULL DEFAULT 0,
|
`ordering` INT(11) NOT NULL DEFAULT 0,
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('_PS_VERSION_')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgrade_module_1_6_0($module)
|
||||||
|
{
|
||||||
|
$columnsToAdd = [
|
||||||
|
'description' => 'TEXT',
|
||||||
|
'extra_description' => 'TEXT',
|
||||||
|
'link' => 'VARCHAR(512) NOT NULL DEFAULT \'\'',
|
||||||
|
'template' => 'VARCHAR(32) NOT NULL DEFAULT \'image_left\'',
|
||||||
|
'image' => 'VARCHAR(255) NOT NULL DEFAULT \'\'',
|
||||||
|
];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('_PS_VERSION_')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgrade_module_1_7_0($module)
|
||||||
|
{
|
||||||
|
$existing = Db::getInstance()->executeS(
|
||||||
|
'SHOW COLUMNS FROM `' . _DB_PREFIX_ . 'ws_customhtml` LIKE \'template\''
|
||||||
|
);
|
||||||
|
|
||||||
|
if (empty($existing)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Db::getInstance()->execute(
|
||||||
|
'ALTER TABLE `' . _DB_PREFIX_ . 'ws_customhtml` DROP COLUMN `template`'
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,3 +9,30 @@ td.action-type .btn-group-action {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 544px) {
|
||||||
|
.form-horizontal
|
||||||
|
.form-group.row
|
||||||
|
> .form-control-label:first-of-type:not(.label-on-top) {
|
||||||
|
flex: 0 0 auto !important;
|
||||||
|
max-width: unset !important;
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
form[name="custom_html_block"] .card-body {
|
||||||
|
padding: 25px 50px !important;
|
||||||
|
}
|
||||||
|
form[name="custom_html_block"] .card-header,
|
||||||
|
form[name="custom_html_block"] .card-footer {
|
||||||
|
padding: 20px 50px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1500px) {
|
||||||
|
form[name="custom_html_block"] .card-body {
|
||||||
|
padding: 25px 300px !important;
|
||||||
|
}
|
||||||
|
form[name="custom_html_block"] .card-header,
|
||||||
|
form[name="custom_html_block"] .card-footer {
|
||||||
|
padding: 20px 300px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,3 +2,16 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.customhtml-banner-content {
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customhtml-banner-title {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customhtml-banner-description,
|
||||||
|
.customhtml-banner-extra-description {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,12 +17,23 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{{ form_row(customHtmlBlockForm.id_ws_customhtml_group) }}
|
{{ form_row(customHtmlBlockForm.id_ws_customhtml_group) }}
|
||||||
{{ form_row(customHtmlBlockForm.title) }}
|
|
||||||
{{ form_row(customHtmlBlockForm.type) }}
|
{{ form_row(customHtmlBlockForm.type) }}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{{ form_row(customHtmlBlockForm.title) }}
|
||||||
{{ form_row(customHtmlBlockForm.show_title) }}
|
{{ form_row(customHtmlBlockForm.show_title) }}
|
||||||
{{ form_row(customHtmlBlockForm.custom_class) }}
|
</div>
|
||||||
|
|
||||||
{{ form_row(customHtmlBlockForm.content) }}
|
{{ form_row(customHtmlBlockForm.content) }}
|
||||||
|
|
||||||
|
{% if currentImageUrl is defined and currentImageUrl %}
|
||||||
|
<div class="customhtmlblock-type-field customhtmlblock-type-banner customhtmlblock-current-image">
|
||||||
|
<label class="form-control-label">{{ 'Current image'|trans({}, 'Modules.Wscustomhtml.Admin') }}</label>
|
||||||
|
<div><img src="{{ currentImageUrl }}" alt="" style="max-width: 200px; max-height: 200px;"></div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{{ form_row(customHtmlBlockForm.image) }}
|
||||||
|
|
||||||
<div class="customhtmlblock-type-field customhtmlblock-type-faq">
|
<div class="customhtmlblock-type-field customhtmlblock-type-faq">
|
||||||
<label class="form-control-label">
|
<label class="form-control-label">
|
||||||
{{ 'FAQ items'|trans({}, 'Modules.Wscustomhtml.Admin') }}
|
{{ 'FAQ items'|trans({}, 'Modules.Wscustomhtml.Admin') }}
|
||||||
@@ -48,6 +59,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ form_row(customHtmlBlockForm.description) }}
|
||||||
|
{{ form_row(customHtmlBlockForm.extra_description) }}
|
||||||
|
{{ form_row(customHtmlBlockForm.link) }}
|
||||||
|
|
||||||
|
{{ form_row(customHtmlBlockForm.custom_class) }}
|
||||||
{{ form_row(customHtmlBlockForm.active) }}
|
{{ form_row(customHtmlBlockForm.active) }}
|
||||||
{{ form_rest(customHtmlBlockForm) }}
|
{{ form_rest(customHtmlBlockForm) }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,13 +17,10 @@
|
|||||||
function toggleBlockTypeFields() {
|
function toggleBlockTypeFields() {
|
||||||
var type = $('#custom_html_block_type').val();
|
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-html').hide();
|
||||||
$('.customhtmlblock-type-faq').show();
|
$('.customhtmlblock-type-faq').hide();
|
||||||
}
|
$('.customhtmlblock-type-banner').hide();
|
||||||
|
$('.customhtmlblock-type-' + type).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
|
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
|
||||||
|
|||||||
@@ -17,13 +17,10 @@
|
|||||||
function toggleBlockTypeFields() {
|
function toggleBlockTypeFields() {
|
||||||
var type = $('#custom_html_block_type').val();
|
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-html').hide();
|
||||||
$('.customhtmlblock-type-faq').show();
|
$('.customhtmlblock-type-faq').hide();
|
||||||
}
|
$('.customhtmlblock-type-banner').hide();
|
||||||
|
$('.customhtmlblock-type-' + type).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
|
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<div class="customhtml-banner row align-items-center">
|
||||||
|
<div class="col-md-6 customhtml-banner-image">
|
||||||
|
{if !empty($block.image_url)}<img src="{$block.image_url|escape:'html':'UTF-8'}" alt="{$block.title|escape:'html':'UTF-8'}">{/if}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 customhtml-banner-content">
|
||||||
|
{include file="module:ws_customhtml/views/templates/hook/_banner_content.tpl" block=$block}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{if $block.show_title && !empty($block.title)}
|
||||||
|
<h3 class="customhtml-banner-title">{$block.title|escape:'html':'UTF-8'}</h3>
|
||||||
|
{/if}
|
||||||
|
{if !empty($block.description)}
|
||||||
|
<div class="customhtml-banner-description">{$block.description|escape:'html':'UTF-8'|nl2br}</div>
|
||||||
|
{/if}
|
||||||
|
{if !empty($block.extra_description)}
|
||||||
|
<div class="customhtml-banner-extra-description">{$block.extra_description|escape:'html':'UTF-8'|nl2br}</div>
|
||||||
|
{/if}
|
||||||
|
{if !empty($block.link)}
|
||||||
|
<a href="{$block.link|escape:'html':'UTF-8'}" class="btn btn-primary customhtml-banner-link">{l s='Learn more' d='Modules.Wscustomhtml.Admin'}</a>
|
||||||
|
{/if}
|
||||||
@@ -4,12 +4,17 @@
|
|||||||
<div class="customhtml-group customhtml-group-{$group.id_ws_customhtml_group|intval} row">
|
<div class="customhtml-group customhtml-group-{$group.id_ws_customhtml_group|intval} row">
|
||||||
{foreach from=$group.blocks item="block"}
|
{foreach from=$group.blocks item="block"}
|
||||||
<div class="customhtml-block col{if !empty($block.custom_class)} {$block.custom_class|escape:'html':'UTF-8'}{/if}">
|
<div class="customhtml-block col{if !empty($block.custom_class)} {$block.custom_class|escape:'html':'UTF-8'}{/if}">
|
||||||
|
{if $block.type == 'faq'}
|
||||||
{if $block.show_title && !empty($block.title)}
|
{if $block.show_title && !empty($block.title)}
|
||||||
<div class="customhtml-title">{$block.title|escape:'html':'UTF-8'}</div>
|
<div class="customhtml-title">{$block.title|escape:'html':'UTF-8'}</div>
|
||||||
{/if}
|
{/if}
|
||||||
{if $block.type == 'faq'}
|
|
||||||
{include file="module:ws_customhtml/views/templates/hook/_faq_accordion.tpl" block=$block}
|
{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}
|
||||||
{else}
|
{else}
|
||||||
|
{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>
|
<div class="customhtml-content">{$block.content nofilter}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ class Ws_CustomHtml extends Module implements WidgetInterface
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->name = 'ws_customhtml';
|
$this->name = 'ws_customhtml';
|
||||||
$this->version = '1.5.0';
|
$this->version = '1.7.0';
|
||||||
$this->author = 'Isabelle Oving-Anno | De Websmid BV';
|
$this->author = 'Isabelle Oving-Anno | De Websmid BV';
|
||||||
$this->need_instance = 0;
|
$this->need_instance = 0;
|
||||||
$this->bootstrap = true;
|
$this->bootstrap = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user