Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ea0940d
Add video block mixins, DEV-1111
iranimij Apr 29, 2025
e3c91b4
Add video block mixins for , DEV-1111
iranimij Apr 30, 2025
193be9e
Improve code, DEV-1111
iranimij Apr 30, 2025
33c1998
Add video blocker for page-builder, DEV-1111
iranimij Apr 30, 2025
4b4c10d
Add video blocker config, DEV-1111
iranimij May 20, 2025
5d8a8bc
Add video blocker config, DEV-1111
iranimij May 20, 2025
5c55e3b
Add video blocker to cms, DEV-1111
iranimij Jun 24, 2025
21ff2dc
Add video blocker to cms, DEV-1111
iranimij Jun 25, 2025
ce24408
Optimize import, DEV-1111
iranimij Jun 25, 2025
918d103
Reformat code, DEV-1111
iranimij Jun 25, 2025
e262dda
Improve code, DEV-1111
iranimij Jun 25, 2025
cb4c893
Improve code, DEV-1111
iranimij Jun 25, 2025
1336879
Improve code, DEV-1111
iranimij Jun 25, 2025
06035f5
Improve code, DEV-1111
iranimij Jun 25, 2025
2409444
Improve code, DEV-1111
iranimij Jul 17, 2025
511d7c0
Auto play video, DEV-1111
iranimij Jul 17, 2025
1c9e1d8
Sort dependencies, DEV-1111
iranimij Jul 17, 2025
f66bb13
Improve code, DEV-1111
iranimij Jul 18, 2025
895652e
Improve code, DEV-1111
iranimij Jul 30, 2025
a4e1fd0
Add plugin, DEV-1111
iranimij Jul 30, 2025
0569c76
Add observer, DEV-1111
iranimij Jul 30, 2025
0ddc4dc
Add observer, DEV-1111
iranimij Jul 30, 2025
3660533
Improve code, DEV-1111
iranimij Jul 30, 2025
ed91125
Sort dependencies, DEV-1111
sprankhub Jul 31, 2025
5ac6c42
Improve translations, DEV-1111
sprankhub Jul 31, 2025
019e9a4
Refactor, DEV-1111
sprankhub Jul 31, 2025
d5334ce
Fix PHP 7.4 support, DEV-1111
sprankhub Jul 31, 2025
27e60ef
Fix JS error, DEV-1111
sprankhub Jul 31, 2025
2084320
Fix the translation, DEV-1111
iranimij Jul 31, 2025
710cc91
Improve code, DEV-1111
iranimij Aug 1, 2025
0c1911e
Refactor, DEV-1111
sprankhub Aug 1, 2025
944dc13
Fix translations, DEV-1111
sprankhub Aug 1, 2025
655241d
Remove page builder dep, DEV-1111
sprankhub Aug 1, 2025
bb26bfa
Update translations, DEV-1111
sprankhub Aug 1, 2025
d7872fe
Update dependencies, DEV-1111
sprankhub Aug 1, 2025
8adbb4a
Update dependencies, DEV-1111
sprankhub Aug 1, 2025
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
6 changes: 6 additions & 0 deletions Model/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Config
public const XML_PATH_DATA_CULTURE = 'web/cookiebot/data_culture';
public const XML_PATH_USE_EU_CDN = 'web/cookiebot/use_eu_cdn';
public const XML_PATH_USE_GOOGLE_CONSENT_MODE = 'web/cookiebot/use_google_consent_mode';
public const XML_PATH_BLOCK_VIDEOS_UNTIL_CONSENT = 'web/cookiebot/block_videos_until_consent';

/**
* @var ScopeConfigInterface
Expand Down Expand Up @@ -49,4 +50,9 @@ public function isGoogleConsentModeEnabled(): bool
{
return $this->scopeConfig->isSetFlag(self::XML_PATH_USE_GOOGLE_CONSENT_MODE, ScopeInterface::SCOPE_STORE);
}

public function isBlockVideosUntilConsentEnabled(): bool
{
return $this->scopeConfig->isSetFlag(self::XML_PATH_BLOCK_VIDEOS_UNTIL_CONSENT, ScopeInterface::SCOPE_STORE);
}
}
39 changes: 39 additions & 0 deletions Model/ExternalVideoReplacer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace CustomGento\Cookiebot\Model;

class ExternalVideoReplacer
{
public function replaceIframeSources(string $content): string
{
$iframePatterns = [
// YouTube patterns
'/<iframe([^>]*)\s+src=["\'](https?:\/\/(?:www\.)?(?:youtube\.com|youtube-nocookie\.com)\/embed\/[^"\']+)["\']([^>]*)>/i',
'/<iframe([^>]*)\s+src=["\'](https?:\/\/(?:www\.)?youtu\.be\/[^"\']+)["\']([^>]*)>/i',
// Vimeo patterns
'/<iframe([^>]*)\s+src=["\'](https?:\/\/(?:www\.)?vimeo\.com\/[^"\']+)["\']([^>]*)>/i',
'/<iframe([^>]*)\s+src=["\'](https?:\/\/(?:www\.)?player\.vimeo\.com\/[^"\']+)["\']([^>]*)>/i'
];

foreach ($iframePatterns as $pattern) {
$content = preg_replace_callback($pattern, function (array $matches) {
$beforeSrc = $matches[1];
$iframeUrl = $matches[2];
$afterSrc = $matches[3];

// Check if data-cookieconsent already exists
if (preg_match('/data-cookieconsent=["\'][^"\']*["\']/', $beforeSrc . $afterSrc)) {
// If data-cookieconsent already exists, just change src to data-cookieblock-src
return '<iframe' . $beforeSrc . ' data-cookieblock-src="' . $iframeUrl . '"' . $afterSrc . '>';
}

return '<iframe' . $beforeSrc . ' data-cookieblock-src="' . $iframeUrl
. '" data-cookieconsent="marketing"' . $afterSrc . '>';
}, $content);
}

return $content;
}
}
59 changes: 59 additions & 0 deletions Observer/HtmlContentFilterObserver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace CustomGento\Cookiebot\Observer;

use CustomGento\Cookiebot\Model\Config;
use CustomGento\Cookiebot\Model\ExternalVideoReplacer;
use Exception;
use Magento\Framework\App\Response\Http;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Psr\Log\LoggerInterface;

class HtmlContentFilterObserver implements ObserverInterface
{
private LoggerInterface $logger;

private Config $config;

private ExternalVideoReplacer $externalVideoReplacer;

public function __construct(
LoggerInterface $logger,
Config $config,
ExternalVideoReplacer $externalVideoReplacer
) {
$this->externalVideoReplacer = $externalVideoReplacer;
$this->config = $config;
$this->logger = $logger;
}

public function execute(Observer $observer): void
{
if (!$this->config->isBlockVideosUntilConsentEnabled()) {
return;
}

try {
$response = $observer->getData('response');

if (!$response instanceof Http) {
return;
}

$content = $response->getBody();

if (empty($content) || !is_string($content)) {
return;
}

$modifiedContent = $this->externalVideoReplacer->replaceIframeSources($content);

$response->setBody($modifiedContent);
} catch (Exception $e) {
$this->logger->error('Error in HtmlContentFilterObserver: ' . $e->getMessage());
}
}
}
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
# CustomGento_Cookiebot
This Magento 2 module integrates Cookiebot into your store. This is useful to give the customer the possibility to block unwanted cookies and only allow wanted cookies. It requires an active Cookiebot subscription and can be completely configured in your Cookiebot account.

This Magento 2 module integrates Cookiebot into your store. This is useful to give the customer the possibility to block
unwanted cookies and only allow wanted cookies. It requires an active Cookiebot subscription and can be completely
configured in your Cookiebot account.

## Installation

- `composer require customgento/module-cookiebot-m2`
- `bin/magento module:enable CustomGento_Cookiebot`
- `bin/magento setup:upgrade`
- `bin/magento setup:di:compile`
- `bin/magento cache:flush`

## Configuration
After installing the extension, just go to Stores > Configuration > Web > Cookiebot Settings to enable Cookiebot and enter your Cookiebot ID. You find the ID in your Cookiebot account under Settings > Your scripts. All other configuration regarding the cookie overlay is done in your Cookiebot account.

Please mind that this extension uses the manual cookie blocking mode. Hence, you still need to update all scripts, which set not-strictly-necessary cookies in your system as described in the [Cookiebot manual implementation guide](https://www.cookiebot.com/en/manual-implementation/).
After installing the extension, just go to Stores > Configuration > Web > Cookiebot Settings to enable Cookiebot and
enter your Cookiebot ID. You find the ID in your Cookiebot account under Settings > Your scripts. All other
configuration regarding the cookie overlay is done in your Cookiebot account.

Please mind that this extension uses the manual cookie blocking mode. Hence, you still need to update all scripts, which
set not-strictly-necessary cookies in your system as described in
the [Cookiebot manual implementation guide](https://www.cookiebot.com/en/manual-implementation/).

## Manual vs. Automatic Cookie Blocking
Unfortunately, Cookiebot currently does not support automatic blocking when RequireJS is used as stated in their [docs](https://support.cookiebot.com/hc/en-us/articles/360015039559-Installing-Cookiebot-in-Magento-2-3-4). Since Magento 2 does use RequireJS, we need to use the manual cookie blocking mode :-(

Unfortunately, Cookiebot currently does not support automatic blocking when RequireJS is used as stated in
their [docs](https://support.cookiebot.com/hc/en-us/articles/360015039559-Installing-Cookiebot-in-Magento-2-3-4). Since
Magento 2 does use RequireJS, we need to use the manual cookie blocking mode :-(

## Disclaimer

You need a [Cookiebot](https://www.cookiebot.com/) account, so that this extension does anything useful for you.

## Copyright

&copy; 2020 - present CustomGento GmbH
124 changes: 124 additions & 0 deletions Test/Unit/Model/ExternalVideoReplacerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

declare(strict_types=1);

namespace CustomGento\Cookiebot\Test\Unit\Model;

use CustomGento\Cookiebot\Model\ExternalVideoReplacer;
use PHPUnit\Framework\TestCase;

class ExternalVideoReplacerTest extends TestCase
{
/**
* @var ExternalVideoReplacer
*/
private $externalVideoReplacer;

protected function setUp(): void
{
$this->externalVideoReplacer = new ExternalVideoReplacer();
}

/**
* @dataProvider iframeDataProvider
*/
public function testReplaceIframeSources(string $input, string $expected): void
{
$result = $this->externalVideoReplacer->replaceIframeSources($input);
$this->assertEquals($expected, $result);
}

public function iframeDataProvider(): array
{
return [
// YouTube test cases
'youtube.com embed' => [
'<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" width="560" height="315"></iframe>',
'<iframe data-cookieblock-src="https://www.youtube.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing" width="560" height="315"></iframe>'
],
'youtube-nocookie.com embed' => [
'<iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" width="560" height="315"></iframe>',
'<iframe data-cookieblock-src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing" width="560" height="315"></iframe>'
],
'youtu.be URL' => [
'<iframe src="https://youtu.be/dQw4w9WgXcQ" width="560" height="315"></iframe>',
'<iframe data-cookieblock-src="https://youtu.be/dQw4w9WgXcQ" data-cookieconsent="marketing" width="560" height="315"></iframe>'
],
'youtube.com without www' => [
'<iframe src="https://youtube.com/embed/dQw4w9WgXcQ" width="560" height="315"></iframe>',
'<iframe data-cookieblock-src="https://youtube.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing" width="560" height="315"></iframe>'
],
'youtube-nocookie.com without www' => [
'<iframe src="https://youtube-nocookie.com/embed/dQw4w9WgXcQ" width="560" height="315"></iframe>',
'<iframe data-cookieblock-src="https://youtube-nocookie.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing" width="560" height="315"></iframe>'
],
'youtu.be without www' => [
'<iframe src="https://youtu.be/dQw4w9WgXcQ" width="560" height="315"></iframe>',
'<iframe data-cookieblock-src="https://youtu.be/dQw4w9WgXcQ" data-cookieconsent="marketing" width="560" height="315"></iframe>'
],
'http instead of https for youtube' => [
'<iframe src="http://www.youtube.com/embed/dQw4w9WgXcQ" width="560" height="315"></iframe>',
'<iframe data-cookieblock-src="http://www.youtube.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing" width="560" height="315"></iframe>'
],

// Vimeo test cases
'vimeo.com embed' => [
'<iframe src="https://vimeo.com/123456789" width="640" height="360"></iframe>',
'<iframe data-cookieblock-src="https://vimeo.com/123456789" data-cookieconsent="marketing" width="640" height="360"></iframe>'
],
'player.vimeo.com embed' => [
'<iframe src="https://player.vimeo.com/video/123456789" width="640" height="360"></iframe>',
'<iframe data-cookieblock-src="https://player.vimeo.com/video/123456789" data-cookieconsent="marketing" width="640" height="360"></iframe>'
],
'www.vimeo.com embed' => [
'<iframe src="https://www.vimeo.com/123456789" width="640" height="360"></iframe>',
'<iframe data-cookieblock-src="https://www.vimeo.com/123456789" data-cookieconsent="marketing" width="640" height="360"></iframe>'
],
'www.player.vimeo.com embed' => [
'<iframe src="https://www.player.vimeo.com/video/123456789" width="640" height="360"></iframe>',
'<iframe data-cookieblock-src="https://www.player.vimeo.com/video/123456789" data-cookieconsent="marketing" width="640" height="360"></iframe>'
],
'http vimeo' => [
'<iframe src="http://vimeo.com/123456789" width="640" height="360"></iframe>',
'<iframe data-cookieblock-src="http://vimeo.com/123456789" data-cookieconsent="marketing" width="640" height="360"></iframe>'
],
// General test cases
'single quotes' => [
'<iframe src=\'https://www.youtube.com/embed/dQw4w9WgXcQ\' width="560" height="315"></iframe>',
'<iframe data-cookieblock-src="https://www.youtube.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing" width="560" height="315"></iframe>'
],
'multiple iframes different services' => [
'<iframe src="https://www.youtube.com/embed/video1"></iframe><iframe src="https://vimeo.com/video2"></iframe><iframe src="https://www.google.com/maps/embed?pb=test"></iframe>',
'<iframe data-cookieblock-src="https://www.youtube.com/embed/video1" data-cookieconsent="marketing"></iframe><iframe data-cookieblock-src="https://vimeo.com/video2" data-cookieconsent="marketing"></iframe><iframe src="https://www.google.com/maps/embed?pb=test"></iframe>'
],
'mixed content' => [
'<p>Some text</p><iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ"></iframe><p>More text</p><iframe src="https://vimeo.com/123456789"></iframe>',
'<p>Some text</p><iframe data-cookieblock-src="https://www.youtube.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing"></iframe><p>More text</p><iframe data-cookieblock-src="https://vimeo.com/123456789" data-cookieconsent="marketing"></iframe>'
],
'iframe with frameborder and allowfullscreen' => [
'<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allowfullscreen=""></iframe>',
'<iframe data-cookieblock-src="https://www.youtube.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing" frameborder="0" allowfullscreen=""></iframe>'
],
'iframe with existing data-cookieconsent' => [
'<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing"></iframe>',
'<iframe data-cookieblock-src="https://www.youtube.com/embed/dQw4w9WgXcQ" data-cookieconsent="marketing"></iframe>'
],
'iframe with different data-cookieconsent value' => [
'<iframe src="https://vimeo.com/123456789" data-cookieconsent="statistics"></iframe>',
'<iframe data-cookieblock-src="https://vimeo.com/123456789" data-cookieconsent="statistics"></iframe>'
],
'no matching iframe' => [
'<iframe src="https://example.com/video"></iframe>',
'<iframe src="https://example.com/video"></iframe>'
],
'no iframe at all' => [
'<p>Just some text content</p>',
'<p>Just some text content</p>'
],
'empty content' => [
'',
''
]
];
}
}
5 changes: 5 additions & 0 deletions ViewModel/Script.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ public function isGoogleConsentModeEnabled(): bool
{
return $this->config->isGoogleConsentModeEnabled();
}

public function isBlockVideosUntilConsentEnabled(): bool
{
return $this->config->isBlockVideosUntilConsentEnabled();
}
}
14 changes: 9 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
}
],
"require": {
"magento/framework": "~102.0||~103.0",
"magento/module-catalog": "~103.0||~104.0",
"magento/module-cms": "~103.0||~104.0",
"php": "~7.4.0||~8.1.0||~8.2.0||~8.3.0||~8.4.0"
"ext-pcre": "*",
"magento/framework": "^103.0",
"magento/module-catalog": "^104.0",
"magento/module-cms": "^104.0",
"magento/module-config": "^101.2",
"magento/module-store": "^101.1",
"php": "^7.4.0||^8.1.0||^8.2.0||^8.3.0||^8.4.0",
"psr/log": "^1.1||^2||^3"
},
"require-dev": {
"phpunit/phpunit": "~6.5||~9.5",
"phpunit/phpunit": "^6.5||^9.5",
"roave/security-advisories": "dev-latest"
},
"autoload": {
Expand Down
4 changes: 4 additions & 0 deletions etc/adminhtml/system.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
<label>Google Consent Mode</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="block_videos_until_consent" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Block YouTube / Vimeo Videos Until Marketing Consent</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
</group>
</section>
</system>
Expand Down
1 change: 1 addition & 0 deletions etc/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<data_culture/>
<use_eu_cdn>0</use_eu_cdn>
<use_google_consent_mode>1</use_google_consent_mode>
<block_videos_until_consent>0</block_videos_until_consent>
</cookiebot>
</web>
</default>
Expand Down
6 changes: 6 additions & 0 deletions etc/events.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="controller_front_send_response_before">
<observer name="customgento_cookiebot_html_content_filter" instance="CustomGento\Cookiebot\Observer\HtmlContentFilterObserver"/>
</event>
</config>
4 changes: 4 additions & 0 deletions etc/module.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="CustomGento_Cookiebot">
<sequence>
<module name="Magento_Backend"/>
<module name="Magento_Catalog"/>
<module name="Magento_Cms"/>
<module name="Magento_Config"/>
<module name="Magento_ProductVideo"/>
<module name="Magento_Store"/>
</sequence>
</module>
</config>
5 changes: 5 additions & 0 deletions i18n/de_DE.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"Please <a href=""javascript:Cookiebot.renew()"" class=""cookiebot-iframe-consent-link"">accept %1 cookies</a> to view this %2 content.","Bitte <a href=""javascript:Cookiebot.renew()"" class=""cookiebot-iframe-consent-link"">akzeptieren Sie %1 Cookies</a>, um diesen %2 Inhalt anzuzeigen."
"Please <a href=""javascript:Cookiebot.renew()"">accept marketing cookies</a> to view this content.","Bitte <a href=""javascript:Cookiebot.renew()"">akzeptieren Sie Marketing-Cookies</a>, um diesen Inhalt anzuzeigen."
"Cookiebot Settings","Cookiebot Einstellungen"
"Enable Cookiebot Integration","Cookiebot-Integration aktivieren"
"Cookiebot ID","Cookiebot ID"
"Data Culture","Data Culture"
"Use European CDN","Europäisches CDN verwenden"
"Google Consent Mode","Google Consent Mode"
"Block YouTube / Vimeo Videos Until Marketing Consent","Blockiere YouTube / Vimeo Videos bis zur Marketingeinwilligung"
5 changes: 5 additions & 0 deletions i18n/en_US.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"Please <a href=""javascript:Cookiebot.renew()"" class=""cookiebot-iframe-consent-link"">accept %1 cookies</a> to view this %2 content.","Please <a href=""javascript:Cookiebot.renew()"" class=""cookiebot-iframe-consent-link"">accept %1 cookies</a> to view this %2 content."
"Please <a href=""javascript:Cookiebot.renew()"">accept marketing cookies</a> to view this content.","Please <a href=""javascript:Cookiebot.renew()"">accept marketing cookies</a> to view this content."
"Cookiebot Settings","Cookiebot Settings"
"Enable Cookiebot Integration","Enable Cookiebot Integration"
"Cookiebot ID","Cookiebot ID"
"Data Culture","Data Culture"
"Use European CDN","Use European CDN"
"Google Consent Mode","Google Consent Mode"
"Block YouTube / Vimeo Videos Until Marketing Consent","Block YouTube / Vimeo Videos Until Marketing Consent"
Loading