CSP Checkout without CSP Theme
Hyvä Checkout can be used in a Strict CSP (Content Security Policy) environment without requiring a full migration of the entire theme to be CSP-compatible.
However, any components shared between the theme and checkout must be made CSP-compatible to ensure proper functionality.
Known Inline scripts
In the Hyvä default theme (hyva-themes/magento2-default-theme
), we identified scripts shared between the theme and checkout.
Read more about this on the How does Hyvä work without unsafe-inline
? page.
Scripts could be used depending on enabled features, make sure to update allowed script tags with:
Identified files:
-
Configurable Products and Swatches Options
Magento_ConfigurableProduct::product/view/type/options/js/configurable-options.phtml
Magento_Swatches::product/js/swatch-options.phtml
-
Analytics and ReCaptcha
Magento_GoogleAnalytics::ga.phtml
Magento_GoogleGtag::ads.phtml
Magento_GoogleGtag::ga.phtml
Magento_ReCaptchaFrontendUi::js/script_loader.phtml
Magento_ReCaptchaFrontendUi::js/script_token.phtml
Magento_ReCaptchaFrontendUi::js/script_token_invisible.phtml
Magento_ReCaptchaFrontendUi::js/script_token_recaptcha.phtml
Magento_ReCaptchaFrontendUi::recaptcha_checkbox.phtml
Magento_ReCaptchaFrontendUi::recaptcha_invisible.phtml
-
Theme
Magento_Theme::html/mobile-safari-bug-workaround.phtml
Alpine components
The Hyvä default theme (hyva-themes/magento2-default-theme
) shares a couple of components with the checkout.
We added diffs at the end of this page to manually update these files.
-
Cookie Notice Component
Magento_Cookie::notices.phtml
-
The Authentication Drawer
Magento_Customer::account/authentication-popup.phtml
-
Footer Components (Currency, Store, Language Selector, and Newsletter Subscription)
Magento_Directory::currency.phtml
Magento_Newsletter::subscribe.phtml
Magento_Store::switch/languages.phtml
Magento_Store::switch/stores.phtml
-
Header Components (Login as Customer Notice and Logout Link)
Magento_LoginAsCustomerFrontendUi::html/notices.phtml
Magento_LoginAsCustomerFrontendUi::html/notices/logout-link.phtml
-
Messaging Component
Magento_Theme::messages.phtml
There may be more, depending on checkout customizations and installed extensions.
All components shared between the theme and the checkout have to be made CSP-compatible.
No Alpine v2 CSP
Hyvä Themes using Alpine v2 must be upgraded to use Alpine v3, as there is no alpine-csp build of version 2.
To check the Alpine version a theme is using, visit a page in a desktop browser, open the developer console, and type Alpine.version
. If the reported version string starts with a 2, the theme must be updated before CSP compatibility can be achieved.
Please refer to the Hyvä Theme 1.2.0 upgrade notes for more information.
Migrating to the Hyvä Checkout CSP edition without updating the full theme
Install the required packages
First update the hyva-theme/magento2-theme-module
package to the newest version.
Then install the CSP version of hyva-themes/magento2-hyva-checkout
as specified in the installation instructions.
This will also upgrade magewirephp/magewire
to at least 1.12.0.
Make shared components in your theme CSP-compatible
In the default checkout, these are the messages component, the authentication drawer, and the newsletter subscription form (see above).
There may be more shared components due to extensions and checkout customizations also needing to be made CSP-compatible.
Alpine compatibility diffs
Manually applying patches to make a file Alpine CSP compatible, these diffs are taken from the hyva-themes/magento2-default-theme-csp
package.
Magento_Cookie::notices.phtml
--- a/Magento_Cookie/templates/notices.phtml
+++ b/Magento_Cookie/templates/notices.phtml
@@ -10,14 +10,16 @@ declare(strict_types=1);
use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\HeroiconsOutline;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Hyva\Theme\ViewModel\Store as StoreViewModel;
use Magento\Cookie\Block\Html\Notices;
use Magento\Framework\Escaper;
use Magento\Cookie\Helper\Cookie;
/** @var Notices $block */
-/** @var Escaper $escaper */
/** @var Cookie $cookieHelper */
+/** @var Escaper $escaper */
+/** @var HyvaCsp $hyvaCsp */
/** @var ViewModelRegistry $viewModels */
/** @var HeroiconsOutline $heroicons */
@@ -56,6 +58,9 @@ if ($cookieHelper->isCookieRestrictionModeEnabled()): ?>
checkAcceptCookies() {
this.showCookieBanner = ! isAllowedSaveCookie();
},
+ hideCookieBanner() {
+ this.showCookieBanner = false;
+ },
setAcceptCookies() {
const cookieExpires = this.cookieLifetime / 60 / 60 / 24;
hyva.setCookie(this.cookieName, this.cookieValue, cookieExpires);
@@ -64,15 +69,17 @@ if ($cookieHelper->isCookieRestrictionModeEnabled()): ?>
} else {
window.dispatchEvent(new CustomEvent('user-allowed-save-cookie'));
}
+ this.showCookieBanner = false;
}
}
}
+ window.addEventListener('alpine:init', () => Alpine.data('initCookieBanner', initCookieBanner), {once: true})
</script>
-
+ <?php $hyvaCsp->registerInlineScript() ?>
<section id="notice-cookie-block"
aria-label="<?= $escaper->escapeHtmlAttr(__('We use cookies to make your experience better.')) ?>"
- x-data="initCookieBanner()"
- x-init="checkAcceptCookies()"
+ x-data="initCookieBanner"
+ x-init="checkAcceptCookies"
x-defer="idle"
>
<template x-if="showCookieBanner">
@@ -82,7 +89,7 @@ if ($cookieHelper->isCookieRestrictionModeEnabled()): ?>
border-t-2 border-container-darker"
>
<button
- @click="showCookieBanner = false;"
+ @click="hideCookieBanner"
aria-label="<?= $escaper->escapeHtmlAttr(__('Close panel')) ?>"
title="<?= $escaper->escapeHtmlAttr(__('Close panel')) ?>"
class="absolute right-0 top-0 p-4"
@@ -108,7 +115,7 @@ if ($cookieHelper->isCookieRestrictionModeEnabled()): ?>
</a>
</p>
<div class="my-2">
- <button @click="setAcceptCookies(); showCookieBanner = false"
+ <button @click="setAcceptCookies"
id="btn-cookie-allow"
class="btn btn-primary"
>
Magento_Customer::account/authentication-popup.phtml
--- a/Magento_Customer/templates/account/authentication-popup.phtml
+++ b/Magento_Customer/templates/account/authentication-popup.phtml
@@ -11,6 +11,7 @@ declare(strict_types=1);
use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\Customer\LoginButton;
use Hyva\Theme\ViewModel\HeroiconsOutline;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Hyva\Theme\ViewModel\ReCaptcha;
use Hyva\Theme\ViewModel\StoreConfig;
use Magento\Framework\Escaper;
@@ -18,6 +19,7 @@ use Magento\Customer\Block\Account\Customer;
/** @var Escaper $escaper */
/** @var Customer $block */
+/** @var HyvaCsp $hyvaCsp */
/** @var ViewModelRegistry $viewModels */
/** @var ReCaptcha $recaptcha */
/** @var HeroiconsOutline $heroicons */
@@ -37,17 +39,20 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
function initAuthentication() {
return {
open: false,
+ close() {
+ this.open = false;
+ },
forceAuthentication: false,
checkoutUrl: '<?= $escaper->escapeUrl($block->getUrl('checkout/index')) ?>',
errors: 0,
hasCaptchaToken: 0,
displayErrorMessage: false,
errorMessages: [],
- setErrorMessages: function setErrorMessages(messages) {
- this.errorMessages = [messages];
- this.displayErrorMessage = this.errorMessages.length;
+ setErrorMessages(message) {
+ this.errorMessages = [message];
+ this.displayErrorMessage = true;
},
- submitForm: function () {
+ submitForm() {
// Do not rename $form, the variable is expected to be declared in the recaptcha output
const $form = document.querySelector('#login-form');
<?= $recaptcha ? $recaptcha->getValidationJsHtml('customer_login', 'auth-popup') : '' ?>
@@ -56,13 +61,17 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
this.dispatchLoginRequest($form);
}
},
- onPrivateContentLoaded: function (data) {
+ onPrivateContentLoaded() {
+ const data = this.$event.detail.data;
const isLoggedIn = data.customer && data.customer.firstname;
if (data.cart && !isLoggedIn) {
this.forceAuthentication = !data.cart.isGuestCheckoutAllowed;
}
},
- redirectIfAuthenticated: function (event) {
+ redirectIfAuthenticated() {
+ const event = this.$event;
+ this.open = this.forceAuthentication;
+
if (event.detail && event.detail.url) {
this.checkoutUrl = event.detail.url;
}
@@ -70,7 +79,10 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
window.location.href = this.checkoutUrl;
}
},
- dispatchLoginRequest: function(form) {
+ resetErrors() {
+ this.errors = 0;
+ },
+ dispatchLoginRequest(form) {
this.isLoading = true;
const username = this.$refs['customer-email'].value;
const password = this.$refs['customer-password'].value;
@@ -99,7 +111,7 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
).then(response => {
return response.json()
}
- ).then(data=> {
+ ).then(data => {
this.isLoading = false;
if (data.errors) {
this.setErrorMessages(data.message);
@@ -112,12 +124,15 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
}
}
}
+
+ window.addEventListener('alpine:init', () => Alpine.data('initAuthentication', initAuthentication), {once: true})
</script>
+<?php $hyvaCsp->registerInlineScript() ?>
<section id="authentication-popup"
- x-data="initAuthentication()"
- @private-content-loaded.window="onPrivateContentLoaded($event.detail.data)"
- @toggle-authentication.window="open = forceAuthentication; redirectIfAuthenticated(event)"
- @keydown.window.escape="open = false"
+ x-data="initAuthentication"
+ @private-content-loaded.window="onPrivateContentLoaded"
+ @toggle-authentication.window="redirectIfAuthenticated"
+ @keydown.window.escape="close"
>
<div
class="backdrop"
@@ -130,18 +145,18 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
x-transition:leave="ease-in-out duration-500"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
- @click="open = false"
+ @click="close"
></div>
<div role="dialog"
aria-modal="true"
- @click.outside="open = false"
+ @click.outside="close"
class="inset-y-0 right-0 z-30 flex max-w-full fixed"
x-cloak
x-show="open"
>
<div class="relative w-screen max-w-md pt-16 bg-container-lighter"
x-show="open"
- x-cloak=""
+ x-cloak
x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700"
x-transition:enter-start="translate-x-full"
x-transition:enter-end="translate-x-0"
@@ -151,7 +166,7 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
>
<div
x-show="open"
- x-cloak=""
+ x-cloak
x-transition:enter="ease-in-out duration-500"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
@@ -160,7 +175,7 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
x-transition:leave-end="opacity-0" class="absolute top-0 right-2 flex p-2 mt-2">
<button
type="button"
- @click="open = false;"
+ @click="close"
aria-label="<?= $escaper->escapeHtmlAttr(__('Close panel')) ?>"
class="p-2 text-gray-300 transition duration-150 ease-in-out hover:text-black"
>
@@ -187,7 +202,7 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
<form class="form form-login"
method="post"
- @submit.prevent="submitForm();"
+ @submit.prevent="submitForm"
id="login-form"
>
<?= $recaptcha ? $recaptcha->getInputHtml('customer_login', 'auth-popup') : '' ?>
@@ -200,7 +215,7 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
<input name="username"
id="form-login-username"
x-ref="customer-email"
- @change="errors = 0"
+ @change="resetErrors"
type="email"
required
autocomplete="<?= $isAutocompleteEnabled ? 'email' : 'off' ?>"
@@ -220,7 +235,7 @@ $isAutocompleteEnabled = $storeConfig->getStoreConfig('customer/password/autocom
required
x-ref="customer-password"
autocomplete="<?= $isAutocompleteEnabled ? 'current-password' : 'off' ?>"
- @change="errors = 0"
+ @change="resetErrors"
>
</div>
</div>
Magento_Directory::currency.phtml
--- a/Magento_Directory/templates/currency.phtml
+++ b/Magento_Directory/templates/currency.phtml
@@ -11,6 +11,7 @@ declare(strict_types=1);
use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\Currency;
use Hyva\Theme\ViewModel\HeroiconsSolid;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Magento\Framework\Escaper;
use Magento\Framework\View\Element\Template;
@@ -18,6 +19,7 @@ use Magento\Framework\View\Element\Template;
/** @var Template $block */
/** @var Escaper $escaper */
+/** @var HyvaCsp $hyvaCsp */
/** @var ViewModelRegistry $viewModels */
/** @var HeroiconsSolid $heroiconsSolid */
@@ -29,7 +31,7 @@ $currencyViewModel = $viewModels->require(Currency::class);
<?php if ($currencyViewModel->getCurrencyCount() > 1): ?>
<?php $currencies = $currencyViewModel->getCurrencies(); ?>
<?php $currentCurrencyCode = $currencyViewModel->getCurrentCurrencyCode(); ?>
- <div x-data="{ open: false }"
+ <div x-data="initCurrencySwitcher"
class="w-full sm:w-1/2 md:w-full pr-4"
>
<h2
@@ -40,13 +42,13 @@ $currencyViewModel = $viewModels->require(Currency::class);
</h2>
<div class="relative inline-block text-left">
<div>
- <button @click.prevent="open = !open"
- @click.outside="open = false"
- @keydown.window.escape="open=false"
+ <button @click.prevent="toggleOpen"
+ @click.outside="setOpenFalse"
+ @keydown.window.escape="setOpenFalse"
type="button"
class="inline-flex justify-center w-full form-select px-4 py-2 bg-white focus:outline-none"
aria-haspopup="true"
- :aria-expanded="open"
+ :aria-expanded="ariaExpanded"
>
<?= $escaper->escapeHtml($currentCurrencyCode) ?>
<?php if ($currencies[$currentCurrencyCode]): ?>
@@ -55,7 +57,7 @@ $currencyViewModel = $viewModels->require(Currency::class);
<?= $heroiconsSolid->chevronDownHtml("flex self-center h-5 w-5 -mr-1 ml-2", 25, 25) ?>
</button>
</div>
- <nav x-cloak=""
+ <nav x-cloak
x-show="open"
class="absolute right-0 top-full z-20 w-full lg:w-56 py-2 mt-1 overflow-auto origin-top-left rounded-sm shadow-lg sm:w-48 lg:mt-3 bg-container-lighter"
aria-labelledby="currency-heading"
@@ -67,7 +69,8 @@ $currencyViewModel = $viewModels->require(Currency::class);
role="link"
class="block px-4 py-2 lg:px-5 lg:py-2 hover:bg-gray-100"
aria-describedby="currency-heading"
- @click.prevent='hyva.postForm(<?= /* @noEscape */ $currencyViewModel->getSwitchCurrencyPostData($code) ?>)'
+ @click.prevent="switchCurrency"
+ data-currency-data="<?= $escaper->escapeHtmlAttr($currencyViewModel->getSwitchCurrencyPostData($code)) ?>"
>
<?= $escaper->escapeHtml($code) ?> - <?= $escaper->escapeHtml($name) ?>
</button>
@@ -77,4 +80,18 @@ $currencyViewModel = $viewModels->require(Currency::class);
</nav>
</div>
</div>
+ <script>
+ function initCurrencySwitcher() {
+ return hyva.createBooleanObject('open', false, {
+ ariaExpanded() {
+ return this.open() ? 'true' : 'false';
+ },
+ switchCurrency() {
+ hyva.postForm(this.$el.dataset.currencyData)
+ }
+ });
+ }
+ window.addEventListener('alpine:init', () => Alpine.data('initCurrencySwitcher', initCurrencySwitcher), {once: true})
+ </script>
+ <?php $hyvaCsp->registerInlineScript() ?>
<?php endif; ?>
```
### `Magento_Newsletter::subscribe.phtml`
```diff
--- a/Magento_Newsletter/templates/subscribe.phtml
+++ b/Magento_Newsletter/templates/subscribe.phtml
@@ -8,12 +8,14 @@
use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\HeroiconsOutline;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Hyva\Theme\ViewModel\ReCaptcha;
use Magento\Framework\Escaper;
use Magento\Newsletter\Block\Subscribe;
/** @var Subscribe $block */
/** @var Escaper $escaper */
+/** @var HyvaCsp $hyvaCsp */
/** @var ViewModelRegistry $viewModels */
/** @var ReCaptcha $recaptcha */
/** @var HeroiconsOutline $heroicons */
@@ -29,8 +31,8 @@ $recaptcha = $block->getData('viewModelRecaptcha');
class="form subscribe"
action="<?= $escaper->escapeUrl($block->getFormActionUrl()) ?>"
method="post"
- x-data="initNewsletterForm()"
- @submit.prevent="submitForm()"
+ x-data="initNewsletterForm"
+ @submit.prevent="submitForm"
id="newsletter-validate-detail"
aria-label="<?= $escaper->escapeHtmlAttr(__('Subscribe to Newsletter')) ?>"
>
@@ -98,5 +100,8 @@ $recaptcha = $block->getData('viewModelRecaptcha');
}
}
}
+
+ window.addEventListener('alpine:init', () => Alpine.data('initNewsletterForm', initNewsletterForm), {once: true})
</script>
+ <?php $hyvaCsp->registerInlineScript() ?>
</div>
Magento_Store::switch/languages.phtml
--- a/Magento_Store/templates/switch/languages.phtml
+++ b/Magento_Store/templates/switch/languages.phtml
@@ -9,6 +9,7 @@
declare(strict_types=1);
use Hyva\Theme\Model\ViewModelRegistry;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Hyva\Theme\ViewModel\Store;
use Hyva\Theme\ViewModel\StoreSwitcher;
use Magento\Framework\Escaper;
@@ -19,6 +20,7 @@ use Magento\Store\ViewModel\SwitcherUrlProvider;
/** @var Template $block */
/** @var Escaper $escaper */
+/** @var HyvaCsp $hyvaCsp */
/** @var ViewModelRegistry $viewModels */
/** @var SwitcherUrlProvider $switcherUrlProvider */
@@ -33,7 +35,7 @@ $storeSwitcherViewModel = $viewModels->require(StoreSwitcher::class);
$currentStore = $storeSwitcherViewModel->getStore();
?>
<?php if (count($storeSwitcherViewModel->getStores()) > 1): ?>
- <div x-data="{ open: false }"
+ <div x-data="initLanguageSwitcher"
class="w-full sm:w-1/2 md:w-full"
>
<div class="title-font font-medium text-gray-900 tracking-widest text-sm mb-3 uppercase">
@@ -41,9 +43,9 @@ $currentStore = $storeSwitcherViewModel->getStore();
</div>
<div class="relative inline-block text-left">
<div>
- <button @click.prevent="open = !open"
- @click.outside="open = false"
- @keydown.window.escape="open=false"
+ <button @click.prevent="toggleOpen"
+ @click.outside="setOpenFalse"
+ @keydown.window.escape="setOpenFalse"
type="button"
class="form-select w-full pl-4"
aria-haspopup="true"
@@ -52,21 +54,28 @@ $currentStore = $storeSwitcherViewModel->getStore();
<?= $escaper->escapeHtml($currentStore->getName()) ?>
</button>
</div>
- <nav x-cloak=""
+ <nav x-cloak
x-show="open"
class="absolute right-0 top-full z-20 w-56 py-2 mt-1 overflow-auto origin-top-left rounded-sm shadow-lg sm:w-48 lg:mt-3 bg-container-lighter">
<div class="my-1" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
- <?php foreach ($storeSwitcherViewModel->getStores() as $lang): ?>
- <?php if ($lang->getId() != $storeViewModel->getStoreId()): ?>
- <a href="<?= $escaper->escapeUrl($switcherUrlProvider->getTargetStoreRedirectUrl($lang)) ?>"
- class="block px-4 py-2 lg:px-5 lg:py-2 hover:bg-gray-100"
- >
- <?= $escaper->escapeHtml($lang->getName()) ?>
- </a>
- <?php endif; ?>
- <?php endforeach; ?>
+ <?php foreach ($storeSwitcherViewModel->getStores() as $lang): ?>
+ <?php if ($lang->getId() != $storeViewModel->getStoreId()): ?>
+ <a href="<?= $escaper->escapeUrl($switcherUrlProvider->getTargetStoreRedirectUrl($lang)) ?>"
+ class="block px-4 py-2 lg:px-5 lg:py-2 hover:bg-gray-100"
+ >
+ <?= $escaper->escapeHtml($lang->getName()) ?>
+ </a>
+ <?php endif; ?>
+ <?php endforeach; ?>
</div>
</nav>
</div>
</div>
+ <script>
+ function initLanguageSwitcher() {
+ return hyva.createBooleanObject('open')
+ }
+ window.addEventListener('alpine:init', () => Alpine.data('initLanguageSwitcher', initLanguageSwitcher), {once: true})
+ </script>
+ <?php $hyvaCsp->registerInlineScript() ?>
<?php endif; ?>
Magento_Store::switch/stores.phtml
--- a/Magento_Store/templates/switch/stores.phtml
+++ b/Magento_Store/templates/switch/stores.phtml
@@ -9,6 +9,7 @@
declare(strict_types=1);
use Hyva\Theme\Model\ViewModelRegistry;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Hyva\Theme\ViewModel\Store;
use Hyva\Theme\ViewModel\StoreSwitcher;
use Magento\Framework\Escaper;
@@ -19,6 +20,7 @@ use Magento\Store\ViewModel\SwitcherUrlProvider;
/** @var Template $block */
/** @var Escaper $escaper */
+/** @var HyvaCsp $hyvaCsp */
/** @var ViewModelRegistry $viewModels */
/** @var SwitcherUrlProvider $switcherUrlProvider */
@@ -33,7 +35,7 @@ $storeSwitcherViewModel = $viewModels->require(StoreSwitcher::class);
$currentStore = $storeSwitcherViewModel->getStore();
?>
<?php if (count($storeSwitcherViewModel->getGroups()) > 1): ?>
- <div x-data="{ open: false }"
+ <div x-data="initStoreSwitcher"
class="w-full sm:w-1/2 md:w-full"
>
<div class="title-font font-medium text-gray-900 tracking-widest text-sm mb-3 uppercase">
@@ -43,9 +45,9 @@ $currentStore = $storeSwitcherViewModel->getStore();
<div>
<?php foreach ($storeSwitcherViewModel->getGroups() as $group): ?>
<?php if ($group->getId() == $storeSwitcherViewModel->getCurrentGroupId()): ?>
- <button @click.prevent="open = !open"
- @click.outside="open = false"
- @keydown.window.escape="open=false"
+ <button @click.prevent="toggleOpen"
+ @click.outside="setOpenFalse"
+ @keydown.window.escape="setOpenFalse"
type="button"
class="form-select w-full pl-4"
aria-haspopup="true"
@@ -56,7 +58,7 @@ $currentStore = $storeSwitcherViewModel->getStore();
<?php endif; ?>
<?php endforeach; ?>
</div>
- <nav x-cloak=""
+ <nav x-cloak
x-show="open"
class="absolute right-0 top-full z-20 w-56 py-2 mt-1 overflow-auto origin-top-left rounded-sm shadow-lg sm:w-48 lg:mt-3 bg-container-lighter">
<div class="my-1" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
@@ -73,4 +75,11 @@ $currentStore = $storeSwitcherViewModel->getStore();
</nav>
</div>
</div>
+ <script>
+ function initStoreSwitcher() {
+ return hyva.createBooleanObject('open')
+ }
+ window.addEventListener('alpine:init', () => Alpine.data('initStoreSwitcher', initStoreSwitcher), {once: true})
+ </script>
+ <?php $hyvaCsp->registerInlineScript() ?>
<?php endif; ?>
Magento_LoginAsCustomerFrontendUi::html/notices.phtml
--- a/Magento_Cookie/templates/notices.phtml
+++ b/Magento_Cookie/templates/notices.phtml
@@ -10,14 +10,16 @@ declare(strict_types=1);
use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\HeroiconsOutline;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Hyva\Theme\ViewModel\Store as StoreViewModel;
use Magento\Cookie\Block\Html\Notices;
use Magento\Framework\Escaper;
use Magento\Cookie\Helper\Cookie;
/** @var Notices $block */
-/** @var Escaper $escaper */
/** @var Cookie $cookieHelper */
+/** @var Escaper $escaper */
+/** @var HyvaCsp $hyvaCsp */
/** @var ViewModelRegistry $viewModels */
/** @var HeroiconsOutline $heroicons */
@@ -56,6 +58,9 @@ if ($cookieHelper->isCookieRestrictionModeEnabled()): ?>
checkAcceptCookies() {
this.showCookieBanner = ! isAllowedSaveCookie();
},
+ hideCookieBanner() {
+ this.showCookieBanner = false;
+ },
setAcceptCookies() {
const cookieExpires = this.cookieLifetime / 60 / 60 / 24;
hyva.setCookie(this.cookieName, this.cookieValue, cookieExpires);
@@ -64,15 +69,17 @@ if ($cookieHelper->isCookieRestrictionModeEnabled()): ?>
} else {
window.dispatchEvent(new CustomEvent('user-allowed-save-cookie'));
}
+ this.showCookieBanner = false;
}
}
}
+ window.addEventListener('alpine:init', () => Alpine.data('initCookieBanner', initCookieBanner), {once: true})
</script>
-
+ <?php $hyvaCsp->registerInlineScript() ?>
<section id="notice-cookie-block"
aria-label="<?= $escaper->escapeHtmlAttr(__('We use cookies to make your experience better.')) ?>"
- x-data="initCookieBanner()"
- x-init="checkAcceptCookies()"
+ x-data="initCookieBanner"
+ x-init="checkAcceptCookies"
x-defer="idle"
>
<template x-if="showCookieBanner">
@@ -82,7 +89,7 @@ if ($cookieHelper->isCookieRestrictionModeEnabled()): ?>
border-t-2 border-container-darker"
>
<button
- @click="showCookieBanner = false;"
+ @click="hideCookieBanner"
aria-label="<?= $escaper->escapeHtmlAttr(__('Close panel')) ?>"
title="<?= $escaper->escapeHtmlAttr(__('Close panel')) ?>"
class="absolute right-0 top-0 p-4"
@@ -108,7 +115,7 @@ if ($cookieHelper->isCookieRestrictionModeEnabled()): ?>
</a>
</p>
<div class="my-2">
- <button @click="setAcceptCookies(); showCookieBanner = false"
+ <button @click="setAcceptCookies"
id="btn-cookie-allow"
class="btn btn-primary"
>
Magento_LoginAsCustomerFrontendUi::html/notices/logout-link.phtml
--- a/Magento_LoginAsCustomerFrontendUi/templates/html/notices/logout-link.phtml
+++ b/Magento_LoginAsCustomerFrontendUi/templates/html/notices/logout-link.phtml
@@ -8,23 +8,35 @@
use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\HeroiconsOutline;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Magento\Customer\Block\Account\AuthorizationLink;
use Magento\Framework\Escaper;
/** @var AuthorizationLink $block */
/** @var Escaper $escaper */
/** @var ViewModelRegistry $viewModels */
+/** @var HyvaCsp $hyvaCsp */
/** @var HeroiconsOutline $heroicons */
$heroicons = $viewModels->require(HeroiconsOutline::class);
$dataPostParam = '';
if ($block->isLoggedIn()) {
- $dataPostParam = sprintf(" @click.prevent='hyva.postForm(%s)'", $block->getPostParams());
+ $dataPostParam = sprintf(' @click.prevent="closeSession"');
}
?>
-
-<a
+<script>
+ function initCloseLoginAsCustomerSession() {
+ return {
+ closeSession() {
+ hyva.postForm(<?= /** @noEscape */ $block->getPostParams() ?>);
+ }
+ }
+ }
+ window.addEventListener('alpine:init', () => Alpine.data('initCloseLoginAsCustomerSession', initCloseLoginAsCustomerSession), {once: true})
+</script>
+<?php $hyvaCsp->registerInlineScript(); ?>
+<a x-data="initCloseLoginAsCustomerSession"
class="-mr-1 flex p-2 rounded-md text-white"
<?= /* @noEscape */ $block->getLinkAttributes() ?>
<?= /* @noEscape */ $dataPostParam ?>
Magento_Theme::messages.phtml
--- a/Magento_Theme/templates/messages.phtml
+++ b/Magento_Theme/templates/messages.phtml
@@ -10,10 +10,12 @@ declare(strict_types=1);
use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\HeroiconsOutline;
+use Hyva\Theme\ViewModel\HyvaCsp;
use Hyva\Theme\ViewModel\StoreConfig;
use Magento\Framework\Escaper;
/** @var Escaper $escaper */
+/** @var HyvaCsp $hyvaCsp */
/** @var ViewModelRegistry $viewModels */
/** @var HeroiconsOutline $heroicons */
/** @var StoreConfig $storeConfig */
@@ -38,8 +40,14 @@ $defaultSuccessMessageTimeout = $storeConfig->getStoreConfig('hyva_theme_general
}, true
)
},
- removeMessage(messageIndex) {
- this.messages[messageIndex] = undefined;
+ hasMessages() {
+ return !this.isEmpty();
+ },
+ hasMessage() {
+ return !!this.message;
+ },
+ removeMessage() {
+ this.messages[this.index] = undefined;
},
addMessages(messages, hideAfter) {
messages.map((message) => {
@@ -74,31 +82,38 @@ $defaultSuccessMessageTimeout = $storeConfig->getStoreConfig('hyva_theme_general
['@clear-messages.window']() {
this.messages = [];
}
+ },
+ getMessageUiId() {
+ return 'message-' + this.message.type;
}
}
}
+
+ window.addEventListener('alpine:init', () => Alpine.data('initMessages', initMessages), {once: true})
</script>
+<?php $hyvaCsp->registerInlineScript() ?>
<section id="messages"
- x-data="initMessages()"
+ x-data="initMessages"
x-bind="eventListeners"
aria-live="assertive"
role="alert"
>
- <template x-if="!isEmpty()">
+ <template x-if="hasMessages">
<div class="w-full">
<div class="messages container mx-auto py-3">
<template x-for="(message, index) in messages" :key="index">
<div>
- <template x-if="message">
- <div class="message" :class="message.type"
- :ui-id="'message-' + message.type"
+ <template x-if="hasMessage">
+ <div class="message"
+ :class="message.type"
+ :ui-id="getMessageUiId"
>
<span x-html="message.text"></span>
<button
type="button"
class="text-gray-600 hover:text-black"
aria-label="<?= $escaper->escapeHtml(__('Close message')) ?>"
- @click.prevent="removeMessage(index)"
+ @click.prevent="removeMessage"
>
<?= $heroicons->xHtml('stroke-current', 18, 18, ['aria-hidden' => 'true']); ?>
</button>