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
|
||||
{
|
||||
$this->assertAdminToken();
|
||||
|
||||
$action = (string) Tools::getValue('action');
|
||||
|
||||
if ($action === 'searchProducts') {
|
||||
@@ -401,6 +403,20 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
||||
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
|
||||
{
|
||||
$term = pSQL((string) Tools::getValue('q'));
|
||||
|
||||
@@ -101,9 +101,19 @@ class SpriteGenerator
|
||||
);
|
||||
}
|
||||
|
||||
imagewebp($sprite, $spritePath, 85);
|
||||
$spriteWritten = imagewebp($sprite, $spritePath, 85);
|
||||
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();
|
||||
$css = [];
|
||||
$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 {
|
||||
grid-template-columns: 112px minmax(0, 1fr) auto;
|
||||
column-gap: 0.75rem;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
}
|
||||
|
||||
.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 {
|
||||
@@ -737,6 +758,23 @@
|
||||
border: 1px solid #ebe2d6;
|
||||
box-shadow: 0 6px 16px rgba(45, 36, 29, 0.07);
|
||||
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 {
|
||||
@@ -792,17 +830,6 @@
|
||||
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 {
|
||||
margin: 0;
|
||||
font-size: var(--adv-card-text-size);
|
||||
@@ -818,7 +845,7 @@
|
||||
.adv-megamenu__panel-tree,
|
||||
.adv-megamenu__layout-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__layout-card .card-title,
|
||||
.adv-megamenu__layout-card .card-text {
|
||||
|
||||
+6
-7
@@ -9,6 +9,7 @@
|
||||
var treeInput = document.getElementById('advmegamenu_tree_json');
|
||||
var ajaxUrl = root.getAttribute('data-ajax-url');
|
||||
var token = root.getAttribute('data-token');
|
||||
var defaultLangId = String(root.getAttribute('data-default-lang') || '');
|
||||
var languages = parseJson(root.querySelector('.js-adv-languages-data'), []);
|
||||
var labels = parseJson(root.querySelector('.js-adv-i18n'), {});
|
||||
var tree = parseJson(root.querySelector('.js-adv-tree-data'), []);
|
||||
@@ -605,7 +606,7 @@
|
||||
}
|
||||
|
||||
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) {
|
||||
return response.json();
|
||||
})
|
||||
@@ -690,9 +691,8 @@
|
||||
}
|
||||
|
||||
function getNodeTitle(node) {
|
||||
var defaultLang = languages[0] ? String(languages[0].id_lang) : null;
|
||||
if (defaultLang && node.lang[defaultLang] && node.lang[defaultLang].title) {
|
||||
return node.lang[defaultLang].title;
|
||||
if (defaultLangId && node.lang[defaultLangId] && node.lang[defaultLangId].title) {
|
||||
return node.lang[defaultLangId].title;
|
||||
}
|
||||
|
||||
var langIds = Object.keys(node.lang || {});
|
||||
@@ -706,9 +706,8 @@
|
||||
}
|
||||
|
||||
function getNodeLink(node) {
|
||||
var defaultLang = languages[0] ? String(languages[0].id_lang) : null;
|
||||
if (defaultLang && node.lang[defaultLang] && node.lang[defaultLang].custom_link) {
|
||||
return node.lang[defaultLang].custom_link;
|
||||
if (defaultLangId && node.lang[defaultLangId] && node.lang[defaultLangId].custom_link) {
|
||||
return node.lang[defaultLangId].custom_link;
|
||||
}
|
||||
|
||||
var langIds = Object.keys(node.lang || {});
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<div class="row adv-megamenu__node-card-row">
|
||||
{foreach from=$nodes item=treeNode}
|
||||
<div class="col-xl-3 col-lg-3 col-md-4 col-sm-6">
|
||||
<a href="{$treeNode.url|escape:'htmlall':'UTF-8'}" class="adv-megamenu__node-card-link" {if $treeNode.new_window}target="_blank" rel="noopener"{/if}>
|
||||
<article class="card adv-megamenu__node-card">
|
||||
{if $treeNode.uses_sprite && $treeNode.icon_class}
|
||||
<div class="adv-megamenu__node-card-sprite">
|
||||
@@ -42,14 +43,13 @@
|
||||
<img src="{$treeNode.icon_url|escape:'htmlall':'UTF-8'}" class="card-img-top" alt="{$treeNode.title|escape:'htmlall':'UTF-8'}" loading="lazy">
|
||||
{/if}
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">
|
||||
<a href="{$treeNode.url|escape:'htmlall':'UTF-8'}">{$treeNode.title|escape:'htmlall':'UTF-8'}</a>
|
||||
</h3>
|
||||
<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>
|
||||
{/foreach}
|
||||
</div>
|
||||
@@ -185,6 +185,7 @@
|
||||
{foreach from=$node.children item=child}
|
||||
<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}">
|
||||
<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}>
|
||||
{if $depth == 1 && ($child.uses_sprite && $child.icon_class)}
|
||||
<div class="adv-megamenu__mobile-thumb adv-megamenu__mobile-thumb--sprite">
|
||||
<span class="{$child.icon_class|escape:'htmlall':'UTF-8'}" aria-hidden="true"></span>
|
||||
@@ -194,7 +195,8 @@
|
||||
<img src="{$child.icon_url|escape:'htmlall':'UTF-8'}" alt="{$child.title|escape:'htmlall':'UTF-8'}" loading="lazy">
|
||||
</div>
|
||||
{/if}
|
||||
<a href="{$child.url|escape:'htmlall':'UTF-8'}" {if $child.new_window}target="_blank" rel="noopener"{/if}>{$child.title|escape:'htmlall':'UTF-8'}</a>
|
||||
<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'}
|
||||
<button type="button" class="js-adv-open-panel" data-target="adv-panel-{$child.id}">›</button>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user