Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4e479490b | ||
|
|
e804c37175 | ||
|
|
34fb95a699 | ||
|
|
ce480314f3 |
@@ -1 +1,3 @@
|
||||
/vendor/
|
||||
/views/img/banners/*
|
||||
!/views/img/banners/.gitkeep
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
+3
-2
@@ -2,7 +2,8 @@ services:
|
||||
websmid.customhtml.form.type.customhtml_group:
|
||||
public: true
|
||||
class: 'Websmid\CustomHtml\Form\Type\CustomHtmlGroupType'
|
||||
parent: 'form.type.translatable.aware'
|
||||
arguments:
|
||||
- '@translator'
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
@@ -44,8 +45,8 @@ services:
|
||||
websmid.customhtml.form.type.customhtml_block:
|
||||
public: true
|
||||
class: 'Websmid\CustomHtml\Form\Type\CustomHtmlBlockType'
|
||||
parent: 'form.type.translatable.aware'
|
||||
arguments:
|
||||
- '@translator'
|
||||
- '@websmid.customhtml.form.choice_provider.customhtml_group_choice_provider'
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
@@ -34,6 +34,16 @@ services:
|
||||
- '@prestashop.core.grid.filter.form_factory'
|
||||
- '@prestashop.core.hook.dispatcher'
|
||||
|
||||
websmid.customhtml.grid.position.customhtmlgroup_position_definition:
|
||||
public: true
|
||||
class: 'PrestaShop\PrestaShop\Core\Grid\Position\PositionDefinition'
|
||||
arguments:
|
||||
- 'ws_customhtml_group'
|
||||
- 'id_ws_customhtml_group'
|
||||
- 'ordering'
|
||||
- ~
|
||||
- 0
|
||||
|
||||
websmid.customhtml.grid.definition.factory.customhtmlblock_definition_factory:
|
||||
public: true
|
||||
class: 'Websmid\CustomHtml\Grid\Definition\Factory\CustomHtmlBlockGridDefinitionFactory'
|
||||
|
||||
@@ -39,6 +39,12 @@ customhtmlgroup_delete:
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomHtml\Controller\CustomHtmlGroupController::deleteAction'
|
||||
|
||||
customhtmlgroup_update_position:
|
||||
path: ws_customhtml/group/update-position
|
||||
methods: [POST]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomHtml\Controller\CustomHtmlGroupController::updatePositionAction'
|
||||
|
||||
customhtmlblock_index:
|
||||
path: ws_customhtml/block
|
||||
methods: [GET]
|
||||
|
||||
+17
-4
@@ -13,14 +13,27 @@ class Config
|
||||
const BLOCK_TYPES = [
|
||||
'html' => 'HTML',
|
||||
'faq' => 'FAQ Accordion',
|
||||
'banner' => 'Banner',
|
||||
'alert' => 'Alert',
|
||||
];
|
||||
|
||||
const ADMIN_CONTROLLERS = [
|
||||
'AdminCustomHtmlParent',
|
||||
'AdminCustomHtmlOverview',
|
||||
'AdminCustomHtmlBlockOverview',
|
||||
const ALERT_STYLES = [
|
||||
'success' => 'Success',
|
||||
'info' => 'Info',
|
||||
'warning' => 'Warning',
|
||||
'danger' => 'Danger',
|
||||
];
|
||||
|
||||
const BANNER_IMAGE_ALLOWED_EXTENSIONS = [
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'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
|
||||
// Tab::getIdFromClassName('AdminCustomHtmlParent') resolves during install.
|
||||
const TABS = [
|
||||
|
||||
@@ -95,9 +95,13 @@ class CustomHtmlBlockController extends FrameworkBundleAdminController
|
||||
$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', [
|
||||
'customHtmlBlockForm' => $form->createView(),
|
||||
'idCustomHtmlBlock' => $idCustomHtmlBlock,
|
||||
'currentImageUrl' => $currentImageUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
namespace Websmid\CustomHtml\Controller;
|
||||
|
||||
use Exception;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Position\Exception\PositionUpdateException;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Position\GridPositionUpdaterInterface;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Position\PositionUpdateFactoryInterface;
|
||||
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
|
||||
use PrestaShopBundle\Security\Voter\PageVoter;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
@@ -103,6 +106,27 @@ class CustomHtmlGroupController extends FrameworkBundleAdminController
|
||||
return $this->redirectToRoute('customhtmlgroup_index');
|
||||
}
|
||||
|
||||
public function updatePositionAction(Request $request): RedirectResponse
|
||||
{
|
||||
$positionDefinition = $this->get('websmid.customhtml.grid.position.customhtmlgroup_position_definition');
|
||||
|
||||
try {
|
||||
$positionUpdateFactory = $this->get(PositionUpdateFactoryInterface::class);
|
||||
$positionUpdate = $positionUpdateFactory->buildPositionUpdate([
|
||||
'positions' => $request->request->all('positions'),
|
||||
], $positionDefinition);
|
||||
|
||||
$updater = $this->get(GridPositionUpdaterInterface::class);
|
||||
$updater->update($positionUpdate);
|
||||
|
||||
$this->addFlash('success', $this->trans('Successful update.', 'Admin.Notifications.Success'));
|
||||
} catch (PositionUpdateException $e) {
|
||||
$this->addFlash('error', $this->getErrorMessageForException($e, []));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('customhtmlgroup_index');
|
||||
}
|
||||
|
||||
private function getToolbarButtons(?string $securitySubject): array
|
||||
{
|
||||
$toolbarButtons = [];
|
||||
|
||||
@@ -20,10 +20,17 @@ final class CustomHtmlGroupChoiceProvider implements
|
||||
ORDER BY `title` ASC'
|
||||
);
|
||||
|
||||
$titleCounts = array_count_values(array_column($groups, 'title'));
|
||||
$choices = [];
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$choices[$group['title']] = (int) $group['id_ws_customhtml_group'];
|
||||
$label = $group['title'];
|
||||
|
||||
if ($titleCounts[$label] > 1) {
|
||||
$label .= ' (#' . $group['id_ws_customhtml_group'] . ')';
|
||||
}
|
||||
|
||||
$choices[$label] = (int) $group['id_ws_customhtml_group'];
|
||||
}
|
||||
|
||||
return $choices;
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
namespace Websmid\CustomHtml\Form\DataHandler;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\DataHandler\FormDataHandlerInterface;
|
||||
use Websmid\CustomHtml\Config\Config;
|
||||
use Websmid\CustomHtml\Model\CustomHtmlBlock;
|
||||
use Websmid\CustomHtml\Model\CustomHtmlFaq;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Db;
|
||||
use Exception;
|
||||
use Validate;
|
||||
|
||||
final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
||||
@@ -21,6 +24,12 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
||||
$block->show_title = (int) $formData['show_title'];
|
||||
$block->custom_class = (string) ($formData['custom_class'] ?? '');
|
||||
$block->content = (string) $formData['content'];
|
||||
$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);
|
||||
|
||||
@@ -47,6 +56,12 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
||||
$block->show_title = (int) $formData['show_title'];
|
||||
$block->custom_class = (string) ($formData['custom_class'] ?? '');
|
||||
$block->content = (string) $formData['content'];
|
||||
$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'];
|
||||
|
||||
if (!$block->update()) {
|
||||
@@ -58,6 +73,49 @@ final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
||||
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
|
||||
{
|
||||
$maxOrdering = Db::getInstance()->getValue(
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Websmid\CustomHtml\Form\DataHandler;
|
||||
use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\DataHandler\FormDataHandlerInterface;
|
||||
use Websmid\CustomHtml\Model\CustomHtmlGroup;
|
||||
use Context;
|
||||
use Db;
|
||||
use Hook;
|
||||
use Module;
|
||||
use Validate;
|
||||
@@ -17,7 +18,7 @@ final class CustomHtmlGroupDataHandler implements FormDataHandlerInterface
|
||||
$group->title = (string) $formData['title'];
|
||||
$group->hook_name = (string) $formData['hook_name'];
|
||||
$group->active = (int) $formData['active'];
|
||||
$group->ordering = 0;
|
||||
$group->ordering = $this->getNextOrdering();
|
||||
|
||||
if (!$group->add()) {
|
||||
return false;
|
||||
@@ -28,6 +29,15 @@ final class CustomHtmlGroupDataHandler implements FormDataHandlerInterface
|
||||
return $group->id;
|
||||
}
|
||||
|
||||
private function getNextOrdering(): int
|
||||
{
|
||||
$maxOrdering = Db::getInstance()->getValue(
|
||||
'SELECT MAX(`ordering`) FROM `' . _DB_PREFIX_ . 'ws_customhtml_group`'
|
||||
);
|
||||
|
||||
return $maxOrdering !== false ? ((int) $maxOrdering + 1) : 0;
|
||||
}
|
||||
|
||||
public function update($id, array $formData)
|
||||
{
|
||||
$group = new CustomHtmlGroup((int) $id);
|
||||
|
||||
@@ -24,6 +24,12 @@ final class CustomHtmlBlockDataProvider implements FormDataProviderInterface
|
||||
'show_title' => (bool) $block->show_title,
|
||||
'custom_class' => $block->custom_class,
|
||||
'content' => $block->content,
|
||||
'image' => null,
|
||||
'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,
|
||||
];
|
||||
@@ -38,6 +44,12 @@ final class CustomHtmlBlockDataProvider implements FormDataProviderInterface
|
||||
'show_title' => false,
|
||||
'custom_class' => '',
|
||||
'content' => '',
|
||||
'image' => null,
|
||||
'description' => '',
|
||||
'extra_description' => '',
|
||||
'link' => '',
|
||||
'alert_type' => 'info',
|
||||
'timeout' => 0,
|
||||
'faqs' => [],
|
||||
'active' => true,
|
||||
];
|
||||
|
||||
@@ -6,6 +6,8 @@ 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\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;
|
||||
@@ -20,7 +22,6 @@ class CustomHtmlBlockType extends AbstractType
|
||||
|
||||
public function __construct(
|
||||
TranslatorInterface $translator,
|
||||
array $locales,
|
||||
ConfigurableFormChoiceProviderInterface $groupChoiceProvider
|
||||
) {
|
||||
$this->translator = $translator;
|
||||
@@ -35,23 +36,20 @@ class CustomHtmlBlockType extends AbstractType
|
||||
'choices' => $this->groupChoiceProvider->getChoices(),
|
||||
'required' => true,
|
||||
])
|
||||
->add('title', TextType::class, [
|
||||
'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('title', TextType::class, [
|
||||
'label' => $this->translator->trans('Title', [], 'Modules.Wscustomhtml.Admin'),
|
||||
'required' => true,
|
||||
'row_attr' => ['class' => 'col-8'],
|
||||
])
|
||||
->add('show_title', SwitchType::class, [
|
||||
'label' => $this->translator->trans('Show title', [], 'Modules.Wscustomhtml.Admin'),
|
||||
'required' => false,
|
||||
])
|
||||
->add('custom_class', TextType::class, [
|
||||
'label' => $this->translator->trans('Custom classes', [], 'Modules.Wscustomhtml.Admin'),
|
||||
'required' => false,
|
||||
'attr' => ['placeholder' => 'col-md-6'],
|
||||
'row_attr' => ['class' => 'col-3'],
|
||||
])
|
||||
->add('content', TextareaType::class, [
|
||||
'label' => $this->translator->trans('Content', [], 'Modules.Wscustomhtml.Admin'),
|
||||
@@ -59,6 +57,11 @@ class CustomHtmlBlockType extends AbstractType
|
||||
'attr' => ['class' => 'autoload_rte'],
|
||||
'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, [
|
||||
'entry_type' => CustomHtmlFaqType::class,
|
||||
'allow_add' => true,
|
||||
@@ -67,6 +70,40 @@ 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 customhtmlblock-type-alert'],
|
||||
])
|
||||
->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, [
|
||||
'label' => $this->translator->trans('Active', [], 'Modules.Wscustomhtml.Admin'),
|
||||
'required' => false,
|
||||
|
||||
@@ -12,7 +12,7 @@ class CustomHtmlGroupType extends AbstractType
|
||||
{
|
||||
private $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator, array $locales)
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use PrestaShop\PrestaShop\Core\Grid\Action\Row\Type\LinkRowAction;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Action\Type\SimpleGridAction;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\ColumnCollection;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ActionColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\PositionColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\DataColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\AbstractGridDefinitionFactory;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\DeleteActionTrait;
|
||||
@@ -81,6 +82,16 @@ final class CustomHtmlGroupGridDefinitionFactory extends AbstractGridDefinitionF
|
||||
->setName($this->trans('Active', [], 'Admin.Global'))
|
||||
->setOptions(['field' => 'active'])
|
||||
)
|
||||
->add(
|
||||
(new PositionColumn('position'))
|
||||
->setName($this->trans('Position', [], 'Admin.Global'))
|
||||
->setOptions([
|
||||
'id_field' => 'id_ws_customhtml_group',
|
||||
'position_field' => 'ordering',
|
||||
'update_method' => 'POST',
|
||||
'update_route' => 'customhtmlgroup_update_position',
|
||||
])
|
||||
)
|
||||
->add(
|
||||
(new DataColumn('date_add'))
|
||||
->setName($this->trans('Date added', [], 'Admin.Global'))
|
||||
|
||||
@@ -14,7 +14,7 @@ final class CustomHtmlGroupFilters extends Filters
|
||||
return [
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'orderBy' => 'wchg.id_ws_customhtml_group',
|
||||
'orderBy' => 'position',
|
||||
'sortOrder' => 'asc',
|
||||
'filters' => [],
|
||||
];
|
||||
|
||||
@@ -11,13 +11,6 @@ use PrestaShop\PrestaShop\Core\Grid\Search\SearchCriteriaInterface;
|
||||
|
||||
final class CustomHtmlBlockQueryBuilder extends AbstractDoctrineQueryBuilder
|
||||
{
|
||||
private $searchCriteriaApplicator;
|
||||
private $contextLanguageId;
|
||||
private $contextShopId;
|
||||
private $contextShopGroupId;
|
||||
private $filterApplicator;
|
||||
private $configuration;
|
||||
|
||||
public function __construct(
|
||||
Connection $connection,
|
||||
string $dbPrefix,
|
||||
@@ -29,12 +22,6 @@ final class CustomHtmlBlockQueryBuilder extends AbstractDoctrineQueryBuilder
|
||||
Configuration $configuration
|
||||
) {
|
||||
parent::__construct($connection, $dbPrefix);
|
||||
$this->searchCriteriaApplicator = $searchCriteriaApplicator;
|
||||
$this->contextLanguageId = $contextLanguageId;
|
||||
$this->contextShopId = $contextShopId;
|
||||
$this->contextShopGroupId = $contextShopGroupId;
|
||||
$this->filterApplicator = $filterApplicator;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function getSearchQueryBuilder(SearchCriteriaInterface $searchCriteria)
|
||||
|
||||
@@ -11,13 +11,6 @@ use PrestaShop\PrestaShop\Core\Grid\Search\SearchCriteriaInterface;
|
||||
|
||||
final class CustomHtmlGroupQueryBuilder extends AbstractDoctrineQueryBuilder
|
||||
{
|
||||
private $searchCriteriaApplicator;
|
||||
private $contextLanguageId;
|
||||
private $contextShopId;
|
||||
private $contextShopGroupId;
|
||||
private $filterApplicator;
|
||||
private $configuration;
|
||||
|
||||
public function __construct(
|
||||
Connection $connection,
|
||||
string $dbPrefix,
|
||||
@@ -29,12 +22,6 @@ final class CustomHtmlGroupQueryBuilder extends AbstractDoctrineQueryBuilder
|
||||
Configuration $configuration
|
||||
) {
|
||||
parent::__construct($connection, $dbPrefix);
|
||||
$this->searchCriteriaApplicator = $searchCriteriaApplicator;
|
||||
$this->contextLanguageId = $contextLanguageId;
|
||||
$this->contextShopId = $contextShopId;
|
||||
$this->contextShopGroupId = $contextShopGroupId;
|
||||
$this->filterApplicator = $filterApplicator;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function getSearchQueryBuilder(SearchCriteriaInterface $searchCriteria)
|
||||
@@ -46,6 +33,7 @@ final class CustomHtmlGroupQueryBuilder extends AbstractDoctrineQueryBuilder
|
||||
'wchg.`title`',
|
||||
'wchg.`hook_name`',
|
||||
'wchg.`active`',
|
||||
'wchg.`ordering`',
|
||||
'wchg.`date_add`',
|
||||
'(SELECT COUNT(*) FROM `' . $this->dbPrefix . 'ws_customhtml` wch
|
||||
WHERE wch.`id_ws_customhtml_group` = wchg.`id_ws_customhtml_group`) AS blocks_count'
|
||||
@@ -53,8 +41,10 @@ final class CustomHtmlGroupQueryBuilder extends AbstractDoctrineQueryBuilder
|
||||
|
||||
$this->applyFilters($qb, $searchCriteria);
|
||||
|
||||
$orderBy = $searchCriteria->getOrderBy() === 'position' ? 'wchg.ordering' : $searchCriteria->getOrderBy();
|
||||
|
||||
$qb
|
||||
->orderBy($searchCriteria->getOrderBy(), $searchCriteria->getOrderWay())
|
||||
->orderBy($orderBy, $searchCriteria->getOrderWay())
|
||||
->setFirstResult($searchCriteria->getOffset() ?? 0)
|
||||
->setMaxResults($searchCriteria->getLimit());
|
||||
|
||||
|
||||
@@ -25,6 +25,24 @@ class CustomHtmlBlock extends ObjectModel
|
||||
/** @var string */
|
||||
public $custom_class = '';
|
||||
|
||||
/** @var string */
|
||||
public $description = '';
|
||||
|
||||
/** @var string */
|
||||
public $extra_description = '';
|
||||
|
||||
/** @var string */
|
||||
public $link = '';
|
||||
|
||||
/** @var string */
|
||||
public $image = '';
|
||||
|
||||
/** @var string */
|
||||
public $alert_type = 'info';
|
||||
|
||||
/** @var int */
|
||||
public $timeout = 0;
|
||||
|
||||
/** @var int */
|
||||
public $active = 1;
|
||||
|
||||
@@ -49,6 +67,12 @@ class CustomHtmlBlock extends ObjectModel
|
||||
'type' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 32],
|
||||
'content' => ['type' => self::TYPE_HTML, 'validate' => 'isCleanHtml'],
|
||||
'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],
|
||||
'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'],
|
||||
@@ -57,6 +81,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
|
||||
{
|
||||
$groupIds = array_map('intval', $groupIds);
|
||||
@@ -66,7 +104,8 @@ class CustomHtmlBlock extends ObjectModel
|
||||
}
|
||||
|
||||
$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`, `alert_type`, `timeout`, `active`, `show_title`, `ordering`
|
||||
FROM `' . _DB_PREFIX_ . 'ws_customhtml`
|
||||
WHERE `id_ws_customhtml_group` IN (' . implode(',', $groupIds) . ')
|
||||
AND `active` = 1
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Websmid\CustomHtml\Module;
|
||||
|
||||
use Websmid\CustomHtml\Config\Config;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Data\GridData;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Record\RecordCollection;
|
||||
use Tools;
|
||||
@@ -21,9 +22,10 @@ trait AdminHooks
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = [])
|
||||
@@ -64,6 +69,7 @@ trait DisplayHooks
|
||||
|
||||
$blocksByGroup = CustomHtmlBlock::getActiveByGroupIds(array_column($groups, 'id_ws_customhtml_group'));
|
||||
$blocksByGroup = $this->attachFaqsToBlocks($blocksByGroup);
|
||||
$blocksByGroup = $this->attachBannerImageUrls($blocksByGroup);
|
||||
|
||||
$groups = array_map(static function (array $group) use ($blocksByGroup) {
|
||||
$group['blocks'] = $blocksByGroup[(int) $group['id_ws_customhtml_group']] ?? [];
|
||||
@@ -88,4 +94,17 @@ trait DisplayHooks
|
||||
}, $blocks);
|
||||
}, $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\',
|
||||
`content` LONGTEXT,
|
||||
`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,
|
||||
`show_title` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`ordering` INT(11) NOT NULL DEFAULT 0,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function upgrade_module_1_10_0($module)
|
||||
{
|
||||
$columnsToAdd = [
|
||||
'alert_type' => '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;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function upgrade_module_1_11_0($module)
|
||||
{
|
||||
$groups = Db::getInstance()->executeS(
|
||||
'SELECT `id_ws_customhtml_group` FROM `' . _DB_PREFIX_ . 'ws_customhtml_group` ORDER BY `id_ws_customhtml_group` ASC'
|
||||
);
|
||||
|
||||
foreach ($groups as $index => $group) {
|
||||
if (!Db::getInstance()->update(
|
||||
'ws_customhtml_group',
|
||||
['ordering' => (int) $index],
|
||||
'`id_ws_customhtml_group` = ' . (int) $group['id_ws_customhtml_group']
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -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`'
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function upgrade_module_1_8_0($module)
|
||||
{
|
||||
$existing = Db::getInstance()->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 \'\''
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function upgrade_module_1_9_0($module)
|
||||
{
|
||||
$existing = Db::getInstance()->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`'
|
||||
);
|
||||
}
|
||||
@@ -9,3 +9,36 @@ td.action-type .btn-group-action {
|
||||
align-items: 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,
|
||||
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_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_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_group"] .card-header,
|
||||
form[name="custom_html_group"] .card-footer {
|
||||
padding: 20px 300px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,66 @@
|
||||
/* Banners */
|
||||
.customhtml-block img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.customhtml-banner-content {
|
||||
padding: 2rem;
|
||||
bottom: 0px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.customhtml-banner-title {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.customhtml-banner-description,
|
||||
.customhtml-banner-extra-description {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.customhtml-banner-image--gradient {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
background: transparent linear-gradient(180deg, #27272777 0%, #272727 100%)
|
||||
0% 0% no-repeat padding-box;
|
||||
}
|
||||
/* Alert */
|
||||
.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;
|
||||
}
|
||||
|
||||
/* FAQ */
|
||||
.accordion-icon-minus {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.accordion-icon-plus {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.customhtml-faq-accordion .btn.collapsed .accordion-icon-minus {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.customhtml-faq-accordion .btn.collapsed .accordion-icon-plus {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -17,12 +17,23 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(customHtmlBlockForm.id_ws_customhtml_group) }}
|
||||
{{ form_row(customHtmlBlockForm.title) }}
|
||||
{{ form_row(customHtmlBlockForm.type) }}
|
||||
|
||||
<div class="row">
|
||||
{{ form_row(customHtmlBlockForm.title) }}
|
||||
{{ form_row(customHtmlBlockForm.show_title) }}
|
||||
{{ form_row(customHtmlBlockForm.custom_class) }}
|
||||
</div>
|
||||
|
||||
{{ 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">
|
||||
<label class="form-control-label">
|
||||
{{ 'FAQ items'|trans({}, 'Modules.Wscustomhtml.Admin') }}
|
||||
@@ -48,6 +59,13 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ form_row(customHtmlBlockForm.alert_type) }}
|
||||
{{ form_row(customHtmlBlockForm.timeout) }}
|
||||
{{ form_row(customHtmlBlockForm.description) }}
|
||||
{{ form_row(customHtmlBlockForm.extra_description) }}
|
||||
{{ form_row(customHtmlBlockForm.link) }}
|
||||
|
||||
{{ form_row(customHtmlBlockForm.custom_class) }}
|
||||
{{ form_row(customHtmlBlockForm.active) }}
|
||||
{{ form_rest(customHtmlBlockForm) }}
|
||||
</div>
|
||||
|
||||
@@ -17,13 +17,11 @@
|
||||
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();
|
||||
}
|
||||
$('.customhtmlblock-type-faq').hide();
|
||||
$('.customhtmlblock-type-banner').hide();
|
||||
$('.customhtmlblock-type-alert').hide();
|
||||
$('.customhtmlblock-type-' + type).show();
|
||||
}
|
||||
|
||||
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
|
||||
|
||||
@@ -17,13 +17,11 @@
|
||||
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();
|
||||
}
|
||||
$('.customhtmlblock-type-faq').hide();
|
||||
$('.customhtmlblock-type-banner').hide();
|
||||
$('.customhtmlblock-type-alert').hide();
|
||||
$('.customhtmlblock-type-' + type).show();
|
||||
}
|
||||
|
||||
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
customhtmlgroupGrid.addExtension(new window.prestashop.component.GridExtensions.FiltersResetExtension());
|
||||
customhtmlgroupGrid.addExtension(new window.prestashop.component.GridExtensions.ColumnTogglingExtension());
|
||||
customhtmlgroupGrid.addExtension(new window.prestashop.component.GridExtensions.SubmitRowActionExtension());
|
||||
customhtmlgroupGrid.addExtension(new window.prestashop.component.GridExtensions.PositionExtension());
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{if !empty($block.description)}
|
||||
<div
|
||||
id="customhtml-alert-{$block.id_ws_customhtml|intval}"
|
||||
class="alert alert-{$block.alert_type|escape:'html':'UTF-8'} alert-dismissible customhtml-alert"
|
||||
role="alert"
|
||||
data-timeout="{$block.timeout|intval}"
|
||||
>
|
||||
{if $block.show_title && !empty($block.title)}
|
||||
<strong class="customhtml-alert-title">{$block.title|escape:'html':'UTF-8'}</strong>
|
||||
{/if}
|
||||
<span class="customhtml-alert-text">{$block.description|escape:'html':'UTF-8'|nl2br}</span>
|
||||
<button type="button" class="close customhtml-alert-close" aria-label="{l s='Close' d='Modules.Wscustomhtml.Shop'}">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="customhtml-banner row flex-column position-relative">
|
||||
<div class="customhtml-banner-image">
|
||||
<div class="customhtml-banner-image--gradient"></div>
|
||||
{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="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.Shop'}</a>
|
||||
{/if}
|
||||
@@ -4,14 +4,32 @@
|
||||
<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}"
|
||||
class="btn btn-link col-12 d-flex text-left {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}"
|
||||
>
|
||||
<div class="col">
|
||||
{$faq.question|escape:'html':'UTF-8'}
|
||||
|
||||
</div>
|
||||
<div class="col-auto d-flex align-items-center ">
|
||||
<span class="accordion-icons accordion-icon-minus">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="2" viewBox="0 0 28 2">
|
||||
<line id="Line_18" data-name="Line 18" x2="26" transform="translate(1 1)" fill="none" stroke="#272727" stroke-linecap="round" stroke-width="2"/>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="accordion-icons accordion-icon-plus">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
<g id="Group_31" data-name="Group 31" transform="translate(-1615 -4031.5)">
|
||||
<line id="Line_21" data-name="Line 21" x2="26" transform="translate(1616 4045.5)" fill="none" stroke="#272727" stroke-linecap="round" stroke-width="2"/>
|
||||
<line id="Line_22" data-name="Line 22" x2="26" transform="translate(1629 4032.5) rotate(90)" fill="none" stroke="#272727" stroke-linecap="round" stroke-width="2"/>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -4,12 +4,23 @@
|
||||
<div class="customhtml-group customhtml-group-{$group.id_ws_customhtml_group|intval} row">
|
||||
{foreach from=$group.blocks item="block"}
|
||||
<div class="customhtml-block col{if !empty($block.custom_class)} {$block.custom_class|escape:'html':'UTF-8'}{/if}">
|
||||
{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)}
|
||||
<div class="customhtml-title">{$block.title|escape:'html':'UTF-8'}</div>
|
||||
{/if}
|
||||
{if $block.type == 'faq'}
|
||||
{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)}
|
||||
<div class="customhtml-title">{$block.title|escape:'html':'UTF-8'}</div>
|
||||
{/if}
|
||||
<div class="customhtml-content">{$block.content nofilter}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class Ws_CustomHtml extends Module implements WidgetInterface
|
||||
public function __construct()
|
||||
{
|
||||
$this->name = 'ws_customhtml';
|
||||
$this->version = '1.5.0';
|
||||
$this->version = '1.11.0';
|
||||
$this->author = 'Isabelle Oving-Anno | De Websmid BV';
|
||||
$this->need_instance = 0;
|
||||
$this->bootstrap = true;
|
||||
|
||||
Reference in New Issue
Block a user