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
4 changes: 2 additions & 2 deletions Config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"router",
"translator",
"monolog.logger.mautic",
"mautic.helper.encryption",
"mautic.multicaptcha.helper.encryption",
"mautic.lead.model.lead",
"mautic.lead.model.company",
"mautic.helper.paths",
Expand All @@ -50,7 +50,7 @@
"router",
"translator",
"monolog.logger.mautic",
"mautic.helper.encryption",
"mautic.multicaptcha.helper.encryption",
"mautic.lead.model.lead",
"mautic.lead.model.company",
"mautic.helper.paths",
Expand Down
4 changes: 4 additions & 0 deletions Config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

use Mautic\CoreBundle\DependencyInjection\MauticCoreExtension;
use Mautic\CoreBundle\Helper\EncryptionHelper;

return static function(ContainerConfigurator $configurator): void {
$services = $configurator->services()
Expand All @@ -11,6 +12,9 @@
->autoconfigure()
->public();

// Legacy config.php treats FQCN arguments as strings, so use a local service alias.
$services->alias("mautic.multicaptcha.helper.encryption", EncryptionHelper::class);

$services->load("MauticPlugin\\MauticMultiCaptchaBundle\\", "../")
->exclude(sprintf("../{%s}", implode(",", MauticCoreExtension::DEFAULT_EXCLUDES)));
};
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,17 @@ Add the "Cloudflare Turnstile" field to the form and save changes.
| Explicit consent mode: | Implicit consent mode: |
|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| ![Cloudflare Turnstile](.github/doc/turnstile_preview.png "Mautic Form with Cloudflare Turnstile") | ![Cloudflare Turnstile implied consent](.github/doc/turnstile_preview_implicit.png "Mautic Form with Cloudflare Turnstile (implied consent)") |

## Integration service regression test

With this plugin installed in a Mautic checkout, run the following as the Mautic
filesystem user (use the project root containing `vendor/autoload.php`):

```sh
MAUTIC_ROOT=/path/to/mautic php -d memory_limit=1G plugins/MauticMultiCaptchaBundle/Tests/integration-services.php
```

For a Composer installation, the script is under `docroot/plugins/` instead.
The test compiles a fresh container in a temporary cache directory, instantiates
all three CAPTCHA integrations, and removes its cache afterward. It does not
change integration settings or validate challenges with external providers.
59 changes: 59 additions & 0 deletions Tests/integration-services.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php declare(strict_types=1);

// Run against an installed Mautic with this plugin in its plugins directory.
// MAUTIC_ROOT=/path/to/mautic php -d memory_limit=1G Tests/integration-services.php
$root = realpath(getenv('MAUTIC_ROOT') ?: '');
if (!$root || !is_file($root . '/vendor/autoload.php')) {
fwrite(STDERR, "Set MAUTIC_ROOT to the Mautic project root containing vendor/autoload.php.\n");
exit(1);
}

$applicationDir = is_dir($root . '/docroot/app') ? $root . '/docroot' : $root;
chdir($root);
defined('IN_MAUTIC_CONSOLE') or define('IN_MAUTIC_CONSOLE', 1);
defined('MAUTIC_ROOT_DIR') or define('MAUTIC_ROOT_DIR', $applicationDir);
require $applicationDir . '/app/config/bootstrap.php';

class MultiCaptchaTestKernel extends AppKernel
{
public function __construct(private string $testRoot, private string $testCache)
{
parent::__construct('prod', false);
}

public function getProjectDir(): string
{
return $this->testRoot;
}

public function getCacheDir(): string
{
return $this->testCache;
}
}

$cache = sys_get_temp_dir() . '/multicaptcha-services-' . bin2hex(random_bytes(8));
$kernel = new MultiCaptchaTestKernel($root, $cache);
$status = 0;
try {
// A fresh container is essential: an existing prod cache can hide broken wiring.
$kernel->boot();
$container = $kernel->getContainer();
foreach (['hcaptcha' => 'Hcaptcha', 'recaptcha' => 'Recaptcha', 'turnstile' => 'Turnstile'] as $id => $name) {
$expected = 'MauticPlugin\\MauticMultiCaptchaBundle\\Integration\\' . $name . 'Integration';
// Boot alone misses a literal class-name string injected instead of the helper.
$integration = $container->get('mautic.integration.' . $id);
if (!$integration instanceof $expected) {
throw new RuntimeException('Unexpected integration class for ' . $id);
}
echo 'PASS ' . $id . PHP_EOL;
}
echo 'PASS Mautic ' . $kernel->getVersion() . PHP_EOL;
} catch (Throwable $exception) {
fwrite(STDERR, get_class($exception) . ': ' . $exception->getMessage() . PHP_EOL);
$status = 1;
} finally {
$kernel->shutdown();
(new Symfony\Component\Filesystem\Filesystem())->remove($cache);
}
exit($status);