Harden admin AJAX and improve mega menu links
This commit is contained in:
@@ -387,6 +387,8 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
|||||||
|
|
||||||
private function ajaxRouter(): void
|
private function ajaxRouter(): void
|
||||||
{
|
{
|
||||||
|
$this->assertAdminToken();
|
||||||
|
|
||||||
$action = (string) Tools::getValue('action');
|
$action = (string) Tools::getValue('action');
|
||||||
|
|
||||||
if ($action === 'searchProducts') {
|
if ($action === 'searchProducts') {
|
||||||
@@ -401,6 +403,20 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
|||||||
exit(json_encode(['error' => true, 'message' => 'Unknown action']));
|
exit(json_encode(['error' => true, 'message' => 'Unknown action']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertAdminToken(): void
|
||||||
|
{
|
||||||
|
$token = (string) Tools::getValue('token');
|
||||||
|
$expectedToken = Tools::getAdminTokenLite('AdminModules');
|
||||||
|
|
||||||
|
if (!hash_equals($expectedToken, $token)) {
|
||||||
|
header('Content-Type: application/json', true, 403);
|
||||||
|
exit(json_encode([
|
||||||
|
'error' => true,
|
||||||
|
'message' => $this->trans('Invalid admin token.', [], 'Modules.Advancedmegamenu.Admin'),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function ajaxSearchProducts(): void
|
private function ajaxSearchProducts(): void
|
||||||
{
|
{
|
||||||
$term = pSQL((string) Tools::getValue('q'));
|
$term = pSQL((string) Tools::getValue('q'));
|
||||||
|
|||||||
@@ -101,9 +101,19 @@ class SpriteGenerator
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
imagewebp($sprite, $spritePath, 85);
|
$spriteWritten = imagewebp($sprite, $spritePath, 85);
|
||||||
imagedestroy($sprite);
|
imagedestroy($sprite);
|
||||||
|
|
||||||
|
if (!$spriteWritten || !is_file($spritePath)) {
|
||||||
|
foreach ($images as $iconImage) {
|
||||||
|
imagedestroy($iconImage['resource']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cleanup($spritePath, $cssPath);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$version = is_file($spritePath) ? (string) filemtime($spritePath) : (string) time();
|
$version = is_file($spritePath) ? (string) filemtime($spritePath) : (string) time();
|
||||||
$css = [];
|
$css = [];
|
||||||
$css[] = '.adv-megamenu__icon{display:inline-block;background-repeat:no-repeat;background-image:url("../../img/generated/menu-sprite.webp?v=' . $version . '");}';
|
$css[] = '.adv-megamenu__icon{display:inline-block;background-repeat:no-repeat;background-image:url("../../img/generated/menu-sprite.webp?v=' . $version . '");}';
|
||||||
|
|||||||
+41
-14
@@ -603,8 +603,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.adv-megamenu__mobile-link-row--with-image {
|
.adv-megamenu__mobile-link-row--with-image {
|
||||||
grid-template-columns: 112px minmax(0, 1fr) auto;
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
column-gap: 0.75rem;
|
}
|
||||||
|
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-main-link,
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-main-link:link,
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-main-link:visited {
|
||||||
|
display: block;
|
||||||
|
min-width: 0;
|
||||||
|
color: #2d241d;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-main-link--with-image,
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-main-link--with-image:link,
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-main-link--with-image:visited {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adv-megamenu__mobile-main-link-label {
|
||||||
|
display: block;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.adv-megamenu__mobile-thumb {
|
.adv-megamenu__mobile-thumb {
|
||||||
@@ -737,6 +758,23 @@
|
|||||||
border: 1px solid #ebe2d6;
|
border: 1px solid #ebe2d6;
|
||||||
box-shadow: 0 6px 16px rgba(45, 36, 29, 0.07);
|
box-shadow: 0 6px 16px rgba(45, 36, 29, 0.07);
|
||||||
background: #fffdfa;
|
background: #fffdfa;
|
||||||
|
transition: box-shadow 160ms ease, transform 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adv-megamenu .adv-megamenu__node-card-link,
|
||||||
|
.adv-megamenu .adv-megamenu__node-card-link:link,
|
||||||
|
.adv-megamenu .adv-megamenu__node-card-link:visited {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adv-megamenu .adv-megamenu__node-card-link:hover .adv-megamenu__node-card,
|
||||||
|
.adv-megamenu .adv-megamenu__node-card-link:focus .adv-megamenu__node-card,
|
||||||
|
.adv-megamenu .adv-megamenu__node-card-link:focus-visible .adv-megamenu__node-card {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 24px rgba(45, 36, 29, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.adv-megamenu__node-card .card-img-top {
|
.adv-megamenu__node-card .card-img-top {
|
||||||
@@ -792,17 +830,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.adv-megamenu .adv-megamenu__node-card .card-title a,
|
|
||||||
.adv-megamenu .adv-megamenu__node-card .card-title a:link,
|
|
||||||
.adv-megamenu .adv-megamenu__node-card .card-title a:visited {
|
|
||||||
color: #2d241d;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adv-megamenu .adv-megamenu__node-card .card-title a:hover {
|
|
||||||
color: #8a3c1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adv-megamenu__node-card .card-text {
|
.adv-megamenu__node-card .card-text {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: var(--adv-card-text-size);
|
font-size: var(--adv-card-text-size);
|
||||||
@@ -818,7 +845,7 @@
|
|||||||
.adv-megamenu__panel-tree,
|
.adv-megamenu__panel-tree,
|
||||||
.adv-megamenu__layout-card,
|
.adv-megamenu__layout-card,
|
||||||
.adv-megamenu__node-card,
|
.adv-megamenu__node-card,
|
||||||
.adv-megamenu__node-card .card-title a,
|
.adv-megamenu__node-card-link,
|
||||||
.adv-megamenu__node-card .card-text,
|
.adv-megamenu__node-card .card-text,
|
||||||
.adv-megamenu__layout-card .card-title,
|
.adv-megamenu__layout-card .card-title,
|
||||||
.adv-megamenu__layout-card .card-text {
|
.adv-megamenu__layout-card .card-text {
|
||||||
|
|||||||
+6
-7
@@ -9,6 +9,7 @@
|
|||||||
var treeInput = document.getElementById('advmegamenu_tree_json');
|
var treeInput = document.getElementById('advmegamenu_tree_json');
|
||||||
var ajaxUrl = root.getAttribute('data-ajax-url');
|
var ajaxUrl = root.getAttribute('data-ajax-url');
|
||||||
var token = root.getAttribute('data-token');
|
var token = root.getAttribute('data-token');
|
||||||
|
var defaultLangId = String(root.getAttribute('data-default-lang') || '');
|
||||||
var languages = parseJson(root.querySelector('.js-adv-languages-data'), []);
|
var languages = parseJson(root.querySelector('.js-adv-languages-data'), []);
|
||||||
var labels = parseJson(root.querySelector('.js-adv-i18n'), {});
|
var labels = parseJson(root.querySelector('.js-adv-i18n'), {});
|
||||||
var tree = parseJson(root.querySelector('.js-adv-tree-data'), []);
|
var tree = parseJson(root.querySelector('.js-adv-tree-data'), []);
|
||||||
@@ -605,7 +606,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchTimer = window.setTimeout(function () {
|
searchTimer = window.setTimeout(function () {
|
||||||
fetch(ajaxUrl + '&ajax=1&action=searchProducts&q=' + encodeURIComponent(value))
|
fetch(ajaxUrl + '&ajax=1&action=searchProducts&token=' + encodeURIComponent(token) + '&q=' + encodeURIComponent(value))
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
@@ -690,9 +691,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNodeTitle(node) {
|
function getNodeTitle(node) {
|
||||||
var defaultLang = languages[0] ? String(languages[0].id_lang) : null;
|
if (defaultLangId && node.lang[defaultLangId] && node.lang[defaultLangId].title) {
|
||||||
if (defaultLang && node.lang[defaultLang] && node.lang[defaultLang].title) {
|
return node.lang[defaultLangId].title;
|
||||||
return node.lang[defaultLang].title;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var langIds = Object.keys(node.lang || {});
|
var langIds = Object.keys(node.lang || {});
|
||||||
@@ -706,9 +706,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNodeLink(node) {
|
function getNodeLink(node) {
|
||||||
var defaultLang = languages[0] ? String(languages[0].id_lang) : null;
|
if (defaultLangId && node.lang[defaultLangId] && node.lang[defaultLangId].custom_link) {
|
||||||
if (defaultLang && node.lang[defaultLang] && node.lang[defaultLang].custom_link) {
|
return node.lang[defaultLangId].custom_link;
|
||||||
return node.lang[defaultLang].custom_link;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var langIds = Object.keys(node.lang || {});
|
var langIds = Object.keys(node.lang || {});
|
||||||
|
|||||||
@@ -33,23 +33,23 @@
|
|||||||
<div class="row adv-megamenu__node-card-row">
|
<div class="row adv-megamenu__node-card-row">
|
||||||
{foreach from=$nodes item=treeNode}
|
{foreach from=$nodes item=treeNode}
|
||||||
<div class="col-xl-3 col-lg-3 col-md-4 col-sm-6">
|
<div class="col-xl-3 col-lg-3 col-md-4 col-sm-6">
|
||||||
<article class="card adv-megamenu__node-card">
|
<a href="{$treeNode.url|escape:'htmlall':'UTF-8'}" class="adv-megamenu__node-card-link" {if $treeNode.new_window}target="_blank" rel="noopener"{/if}>
|
||||||
{if $treeNode.uses_sprite && $treeNode.icon_class}
|
<article class="card adv-megamenu__node-card">
|
||||||
<div class="adv-megamenu__node-card-sprite">
|
{if $treeNode.uses_sprite && $treeNode.icon_class}
|
||||||
<span class="{$treeNode.icon_class|escape:'htmlall':'UTF-8'}" aria-hidden="true"></span>
|
<div class="adv-megamenu__node-card-sprite">
|
||||||
</div>
|
<span class="{$treeNode.icon_class|escape:'htmlall':'UTF-8'}" aria-hidden="true"></span>
|
||||||
{elseif $treeNode.icon_url}
|
</div>
|
||||||
<img src="{$treeNode.icon_url|escape:'htmlall':'UTF-8'}" class="card-img-top" alt="{$treeNode.title|escape:'htmlall':'UTF-8'}" loading="lazy">
|
{elseif $treeNode.icon_url}
|
||||||
{/if}
|
<img src="{$treeNode.icon_url|escape:'htmlall':'UTF-8'}" class="card-img-top" alt="{$treeNode.title|escape:'htmlall':'UTF-8'}" loading="lazy">
|
||||||
<div class="card-body">
|
|
||||||
<h3 class="card-title">
|
|
||||||
<a href="{$treeNode.url|escape:'htmlall':'UTF-8'}">{$treeNode.title|escape:'htmlall':'UTF-8'}</a>
|
|
||||||
</h3>
|
|
||||||
{if $treeNode.description}
|
|
||||||
<p class="card-text">{$treeNode.description|escape:'htmlall':'UTF-8'}</p>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
<div class="card-body">
|
||||||
</article>
|
<h3 class="card-title">{$treeNode.title|escape:'htmlall':'UTF-8'}</h3>
|
||||||
|
{if $treeNode.description}
|
||||||
|
<p class="card-text">{$treeNode.description|escape:'htmlall':'UTF-8'}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</div>
|
</div>
|
||||||
@@ -185,16 +185,18 @@
|
|||||||
{foreach from=$node.children item=child}
|
{foreach from=$node.children item=child}
|
||||||
<li>
|
<li>
|
||||||
<div class="adv-megamenu__mobile-link-row{if $depth == 1 && (($child.uses_sprite && $child.icon_class) || $child.icon_url)} adv-megamenu__mobile-link-row--with-image{/if}">
|
<div class="adv-megamenu__mobile-link-row{if $depth == 1 && (($child.uses_sprite && $child.icon_class) || $child.icon_url)} adv-megamenu__mobile-link-row--with-image{/if}">
|
||||||
{if $depth == 1 && ($child.uses_sprite && $child.icon_class)}
|
<a href="{$child.url|escape:'htmlall':'UTF-8'}" class="adv-megamenu__mobile-main-link{if $depth == 1 && (($child.uses_sprite && $child.icon_class) || $child.icon_url)} adv-megamenu__mobile-main-link--with-image{/if}" {if $child.new_window}target="_blank" rel="noopener"{/if}>
|
||||||
<div class="adv-megamenu__mobile-thumb adv-megamenu__mobile-thumb--sprite">
|
{if $depth == 1 && ($child.uses_sprite && $child.icon_class)}
|
||||||
<span class="{$child.icon_class|escape:'htmlall':'UTF-8'}" aria-hidden="true"></span>
|
<div class="adv-megamenu__mobile-thumb adv-megamenu__mobile-thumb--sprite">
|
||||||
</div>
|
<span class="{$child.icon_class|escape:'htmlall':'UTF-8'}" aria-hidden="true"></span>
|
||||||
{elseif $depth == 1 && $child.icon_url}
|
</div>
|
||||||
<div class="adv-megamenu__mobile-thumb">
|
{elseif $depth == 1 && $child.icon_url}
|
||||||
<img src="{$child.icon_url|escape:'htmlall':'UTF-8'}" alt="{$child.title|escape:'htmlall':'UTF-8'}" loading="lazy">
|
<div class="adv-megamenu__mobile-thumb">
|
||||||
</div>
|
<img src="{$child.icon_url|escape:'htmlall':'UTF-8'}" alt="{$child.title|escape:'htmlall':'UTF-8'}" loading="lazy">
|
||||||
{/if}
|
</div>
|
||||||
<a href="{$child.url|escape:'htmlall':'UTF-8'}" {if $child.new_window}target="_blank" rel="noopener"{/if}>{$child.title|escape:'htmlall':'UTF-8'}</a>
|
{/if}
|
||||||
|
<span class="adv-megamenu__mobile-main-link-label">{$child.title|escape:'htmlall':'UTF-8'}</span>
|
||||||
|
</a>
|
||||||
{if $child.children|count || $child.category_branch|count || $child.layouts|count || $child.type == 'rich_content'}
|
{if $child.children|count || $child.category_branch|count || $child.layouts|count || $child.type == 'rich_content'}
|
||||||
<button type="button" class="js-adv-open-panel" data-target="adv-panel-{$child.id}">›</button>
|
<button type="button" class="js-adv-open-panel" data-target="adv-panel-{$child.id}">›</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user