Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions Block/Turnstile.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace PixelOpen\CloudflareTurnstile\Block;

use PixelOpen\CloudflareTurnstile\Helper\Config;
use PixelOpen\CloudflareTurnstile\Model\Config\Source\RenderingMode;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use Magento\Framework\Filter\FilterManager;
Expand Down Expand Up @@ -85,4 +86,85 @@ public function getId(): string
{
return 'cloudflare-turnstile-' . $this->filter->translitUrl($this->getAction());
}

/**
* Check if Turnstile is enabled on frontend
*
* @return bool
*/
public function isEnabled(): bool
{
return $this->config->isEnabledOnFront();
}

/**
* Retrieve sitekey
*
* @return string
*/
public function getSiteKey(): string
{
return $this->config->getSiteKey();
}

/**
* Check if the current action/form is enabled
*
* @return bool
*/
public function isFormEnabled(): bool
{
$forms = $this->config->getFrontendForms();
return in_array($this->getAction(), $forms);
}

/**
* Retrieve theme from config if not overridden
*
* @return string
*/
public function getThemeFromConfig(): string
{
return $this->getTheme() ?: $this->config->getFrontendTheme();
}

/**
* Retrieve size from config if not overridden
*
* @return string
*/
public function getSizeFromConfig(): string
{
return $this->getSize() ?: $this->config->getFrontendSize();
}

/**
* Retrieve configured rendering mode
*
* @return string
*/
public function getRenderingMode(): string
{
return $this->config->getFrontendRenderingMode();
}

/**
* Use Knockout rendering (Luma/Blank themes)
*
* @return bool
*/
public function isKnockoutRendering(): bool
{
return $this->getRenderingMode() === RenderingMode::MODE_KNOCKOUT;
}

/**
* Use Hyvä/Alpine fallback rendering
*
* @return bool
*/
public function isFallbackRendering(): bool
{
return $this->getRenderingMode() === RenderingMode::MODE_FALLBACK;
}
}
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 100.4.0

- Added storefront setting to choose the widget rendering mode (Knockout vs Hyvä/Alpine fallback)
- Ensured only one renderer executes at a time to prevent Cloudflare "sitekey object" errors
- Added dedicated fallback container class (`cf-turnstile-manual`) so Turnstile auto-render does not attach to Hyvä widgets
- Improved frontend/admin CSS and JS to respect the selected rendering mode

## 100.3.0

- Allow adding a captcha to the native newsletter subscription block ([2](https://github.com/Pixel-Open/magento-cloudflare-turnstile/issues/2))
Expand Down
17 changes: 17 additions & 0 deletions Helper/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Store\Model\ScopeInterface;
use PixelOpen\CloudflareTurnstile\Model\Config\Source\RenderingMode;

class Config extends AbstractHelper
{
Expand All @@ -22,6 +23,7 @@ class Config extends AbstractHelper
public const TURNSTILE_CONFIG_PATH_FRONTEND_THEME = 'pixel_open_cloudflare_turnstile/frontend/theme';
public const TURNSTILE_CONFIG_PATH_FRONTEND_SIZE = 'pixel_open_cloudflare_turnstile/frontend/size';
public const TURNSTILE_CONFIG_PATH_FRONTEND_FORMS = 'pixel_open_cloudflare_turnstile/frontend/forms';
public const TURNSTILE_CONFIG_PATH_FRONTEND_RENDERING_MODE = 'pixel_open_cloudflare_turnstile/frontend/rendering_mode';

public const TURNSTILE_CONFIG_PATH_ADMINHTML_ENABLED = 'pixel_open_cloudflare_turnstile/adminhtml/enabled';
public const TURNSTILE_CONFIG_PATH_ADMINHTML_THEME = 'pixel_open_cloudflare_turnstile/adminhtml/theme';
Expand Down Expand Up @@ -119,6 +121,21 @@ public function getFrontendSize(): string
);
}

/**
* Retrieve frontend rendering mode
*
* @return string
*/
public function getFrontendRenderingMode(): string
{
$mode = $this->scopeConfig->getValue(
self::TURNSTILE_CONFIG_PATH_FRONTEND_RENDERING_MODE,
ScopeInterface::SCOPE_STORE
);

return $mode ?: RenderingMode::MODE_KNOCKOUT;
}

/**
* Retrieve admin size
*
Expand Down
40 changes: 40 additions & 0 deletions Model/Config/Source/RenderingMode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
/**
* Copyright (C) 2023 Pixel Développement
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace PixelOpen\CloudflareTurnstile\Model\Config\Source;

use Magento\Framework\Data\OptionSourceInterface;

class RenderingMode implements OptionSourceInterface
{
public const MODE_KNOCKOUT = 'knockout';

public const MODE_FALLBACK = 'fallback';

/**
* Retrieve option array
*
* @return array
*/
public function toOptionArray(): array
{
return [
[
'value' => self::MODE_KNOCKOUT,
'label' => __('Knockout (Luma/Blank themes)'),
],
[
'value' => self::MODE_FALLBACK,
'label' => __('Hyvä / Alpine fallback'),
],
];
}
}

7 changes: 4 additions & 3 deletions Model/ConfigProvider/Frontend.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ public function getConfig(): array
'config' => [
'enabled' => $this->config->isEnabledOnFront(),
'sitekey' => $this->config->getSiteKey(),
'theme' => $this->config->getFrontendTheme(),
'size' => $this->config->getFrontendSize(),
'forms' => $this->config->getFrontendForms(),
'theme' => $this->config->getFrontendTheme(),
'size' => $this->config->getFrontendSize(),
'forms' => $this->config->getFrontendForms(),
'renderingMode' => $this->config->getFrontendRenderingMode(),
]
];
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
## Installation

```
composer require pixelopen/magento-cloudflare-turnstile
composer require shinesoftware/magento-cloudflare-turnstile
```

## Configuration
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"magento/framework": "*"
},
"type": "magento2-module",
"version": "100.3.0",
"version": "100.3.1",
"autoload": {
"files": [
"registration.php"
Expand All @@ -23,4 +23,4 @@
"role": "Developer"
}
]
}
}
8 changes: 8 additions & 0 deletions etc/adminhtml/system.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="rendering_mode" translate="label comment" type="select" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Rendering mode</label>
<source_model>PixelOpen\CloudflareTurnstile\Model\Config\Source\RenderingMode</source_model>
<comment><![CDATA[Select how the widget is rendered. Use <strong>Knockout</strong> for Luma/Blank based themes, or the <strong>Hyvä / Alpine fallback</strong> when Knockout is not available.]]></comment>
<depends>
<field id="enabled">1</field>
</depends>
</field>
<field id="theme" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Theme</label>
<source_model>PixelOpen\CloudflareTurnstile\Model\Config\Source\Theme</source_model>
Expand Down
1 change: 1 addition & 0 deletions etc/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<enabled>0</enabled>
<theme>light</theme>
<size>normal</size>
<rendering_mode>knockout</rendering_mode>
</frontend>
<adminhtml>
<enabled>0</enabled>
Expand Down
10 changes: 10 additions & 0 deletions view/adminhtml/web/css/turnstile.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,14 @@
.cloudflare-turnstile {
font-weight: bold;
color: #c00;
margin-top: 1rem;
}

.cf-turnstile-manual {
margin-top: 0;
min-height: 65px;
}

.cloudflare-turnstile .cf-turnstile {
margin-top: 0;
}
80 changes: 63 additions & 17 deletions view/base/templates/turnstile.phtml
Original file line number Diff line number Diff line change
@@ -1,22 +1,68 @@
<?php /** @var \PixelOpen\CloudflareTurnstile\Block\Turnstile $block */ ?>
<?php /** @var \Magento\Framework\Escaper $escaper */ ?>
<div id="<?= $escaper->escapeHtmlAttr($block->getId()) ?>" class="cloudflare-turnstile" data-bind="scope:'cloudflare-turnstile-<?= $escaper->escapeHtmlAttr($block->getId()) ?>'">
<!-- ko template: getTemplate() --><!-- /ko -->
<?php
/**
* Copyright (C) 2023 Pixel Développement
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/** @var \PixelOpen\CloudflareTurnstile\Block\Turnstile $block */
/** @var \Magento\Framework\Escaper $escaper */

// Get configuration values from block
$isEnabled = $block->isEnabled();
$sitekey = $block->getSiteKey();
$action = $block->getAction();
$isFormEnabled = $block->isFormEnabled();
$theme = $block->getThemeFromConfig();
$size = $block->getSizeFromConfig();
$elementId = $block->getId();
$renderingMode = $block->getRenderingMode();
$isKnockout = $block->isKnockoutRendering();
$isFallback = $block->isFallbackRendering();

// Early return if not enabled
if (!$isEnabled || !$isFormEnabled || !$sitekey) {
return;
}
?>

<div id="<?= $escaper->escapeHtmlAttr($elementId) ?>" class="cloudflare-turnstile"
data-action="<?= $escaper->escapeHtmlAttr($action) ?>"
data-sitekey="<?= $escaper->escapeHtmlAttr($sitekey) ?>"
data-theme="<?= $escaper->escapeHtmlAttr($theme) ?>"
data-size="<?= $escaper->escapeHtmlAttr($size) ?>"
data-render-mode="<?= $escaper->escapeHtmlAttr($renderingMode) ?>"
<?php if ($isKnockout): ?>
data-bind="scope:'cloudflare-turnstile-<?= $escaper->escapeHtmlAttr($elementId) ?>'"
<?php endif; ?>
>
<?php if ($isKnockout): ?>
<!-- ko template: getTemplate() --><!-- /ko -->
<?php endif; ?>
<?php if ($isFallback): ?>
<!-- Fallback container for themes without Knockout.js (e.g., Hyvä Theme) -->
<div id="cf-turnstile-fallback-<?= $escaper->escapeHtmlAttr($elementId) ?>" class="cf-turnstile-manual" style="display: none;"></div>
<?php endif; ?>
</div>
<script type="text/x-magento-init">
{
"#<?= $escaper->escapeJs($block->getId()) ?>": {
"Magento_Ui/js/core/app": {
"components": {
"cloudflare-turnstile-<?= $escaper->escapeJs($block->getId()) ?>": {
"component": "PixelOpen_CloudflareTurnstile/js/view/turnstile",
"action": "<?= $escaper->escapeJs($block->getAction()) ?>",
"size": "<?= $escaper->escapeJs($block->getSize()) ?>",
"theme": "<?= $escaper->escapeJs($block->getTheme()) ?>",
"configSource": "turnstileConfig"

<?php if ($isKnockout): ?>
<script type="text/x-magento-init">
{
"#<?= $escaper->escapeJs($elementId) ?>": {
"Magento_Ui/js/core/app": {
"components": {
"cloudflare-turnstile-<?= $escaper->escapeJs($elementId) ?>": {
"component": "PixelOpen_CloudflareTurnstile/js/view/turnstile",
"action": "<?= $escaper->escapeJs($action) ?>",
"size": "<?= $escaper->escapeJs($size) ?>",
"theme": "<?= $escaper->escapeJs($theme) ?>",
"configSource": "turnstileConfig"
}
}
}
}
}
}
</script>
</script>
<?php endif; ?>

Loading