Files
JTL-Shop-ALTCHA-Spamschutz/fp_altcha_spamschutz/src/Handler/TemplateHandler.php
T

164 lines
6.0 KiB
PHP
Raw Normal View History

2026-09-21 15:26:59 +02:00
<?php
declare(strict_types=1);
namespace Plugin\fp_altcha_spamschutz\src\Handler;
use JTL\phpQuery\phpQueryObject;
use JTL\Plugin\PluginInterface;
use JTL\Smarty\JTLSmarty;
use Plugin\fp_altcha_spamschutz\src\Service\AltchaService;
/**
* Fuegt das ALTCHA-Widget (Container + Skript) in Registrierungs- und Newsletter-Formular ein.
* Wird ueber HOOK_SMARTY_OUTPUTFILTER aufgerufen, also nach dem Rendern der Seite, auf dem
* fertigen HTML-Dokument (phpQuery, jQuery-aehnliche PHP-DOM-API).
*/
class TemplateHandler
{
private PluginInterface $plugin;
private AltchaService $altchaService;
public function __construct(PluginInterface $plugin, AltchaService $altchaService)
{
$this->plugin = $plugin;
$this->altchaService = $altchaService;
}
/**
* @param array<string, mixed> $args Enthaelt 'smarty' (JTLSmarty) und 'document' (phpQueryObject).
*/
public function generalTemplateIntegration(array $args): void
{
// WICHTIG: Dieser Hook laeuft bei JEDEM Seitenaufruf im gesamten Shop. Ein Fehler hier
// darf niemals die Anzeige irgendeiner Shop-Seite verhindern -- deshalb komplett
// defensiv mit try/catch umschlossen. Im Zweifel wird einfach kein Widget angezeigt,
// statt die Seite kaputt zu machen.
try {
$this->doTemplateIntegration($args);
} catch (\Throwable $e) {
$this->logDebug($args, 'Fehler bei Template-Integration: ' . $e->getMessage());
2026-09-21 15:26:59 +02:00
}
}
/**
* @param array<string, mixed> $args
*/
private function doTemplateIntegration(array $args): void
{
/** @var phpQueryObject $document */
$document = $args['document'];
$registerEnabled = $this->altchaService->isEnabledForRegistration();
$newsletterEnabled = $this->altchaService->isEnabledForNewsletter();
2026-09-21 15:26:59 +02:00
$injected = false;
$registerFormCount = 0;
if ($registerEnabled) {
2026-09-21 15:26:59 +02:00
$registerForm = $document->find('form.register-form');
$registerFormCount = \count($registerForm);
if ($registerFormCount > 0) {
2026-09-21 15:26:59 +02:00
$this->injectWidget($document, $registerForm);
$injected = true;
}
}
$newsletterFormCount = 0;
if ($newsletterEnabled) {
2026-09-21 15:26:59 +02:00
$newsletterForm = $document->find('input[name="abonnieren"]')->closest('form');
$newsletterFormCount = \count($newsletterForm);
if ($newsletterFormCount > 0) {
2026-09-21 15:26:59 +02:00
$this->injectWidget($document, $newsletterForm);
$injected = true;
}
}
$this->logDebug($args, \sprintf(
'Hook ausgefuehrt: register_enabled=%d register_forms_gefunden=%d '
. 'newsletter_enabled=%d newsletter_forms_gefunden=%d widget_eingefuegt=%d',
$registerEnabled ? 1 : 0,
$registerFormCount,
$newsletterEnabled ? 1 : 0,
$newsletterFormCount,
$injected ? 1 : 0
));
2026-09-21 15:26:59 +02:00
if ($injected) {
$this->includeScript($document);
}
}
/**
* Schreibt eine Diagnosemeldung, sofern in den Plugin-Einstellungen "Debug-Logging aktivieren"
* eingeschaltet ist. Die Meldung geht sowohl in das PHP-Errorlog des Servers als auch -- damit
* sie auch ohne Serverzugriff einsehbar ist -- als HTML-Kommentar in die ausgelieferte Seite
* (fuer Besucher unsichtbar, im Seitenquelltext aber direkt sichtbar).
*
* @param array<string, mixed> $args
*/
private function logDebug(array $args, string $message): void
{
if (!$this->altchaService->isDebug()) {
return;
}
error_log('[fp_altcha_spamschutz] ' . $message);
$document = $args['document'] ?? null;
if ($document instanceof phpQueryObject) {
$safeMessage = htmlspecialchars($message, \ENT_QUOTES, 'UTF-8');
$document->find('body')->append('<!-- fp_altcha_debug: ' . $safeMessage . ' -->');
}
}
2026-09-21 15:26:59 +02:00
private function injectWidget(phpQueryObject $document, phpQueryObject $form): void
{
// Nicht doppelt einfuegen (z. B. wenn die Seite mehrfach gefiltert wird).
if (\count($form->find('.fp-altcha')) > 0) {
return;
}
$challenge = $this->altchaService->createChallengeArray();
$challengeJson = htmlspecialchars(
(string) json_encode($challenge, \JSON_UNESCAPED_SLASHES),
\ENT_QUOTES,
'UTF-8'
);
$isGerman = empty($_SESSION['cISOSprache']) || $_SESSION['cISOSprache'] === 'ger';
$labelPreparing = $isGerman ? 'Sicherheitsprüfung wird vorbereitet …' : 'Preparing security check …';
$labelPreparing = htmlspecialchars($labelPreparing, \ENT_QUOTES, 'UTF-8');
$html = '<div class="fp-altcha form-group" data-fp-altcha="' . $challengeJson . '">'
. '<span class="fp-altcha-status text-muted small"><i class="fa fa-shield" aria-hidden="true"></i> '
. $labelPreparing . '</span>'
. '<input type="hidden" name="altcha" class="fp-altcha-input" value="">'
. '</div>';
// Direkt vor dem Absende-Button einfuegen.
$submitButton = $form->find('button[type="submit"], input[type="submit"]');
if (\count($submitButton) > 0) {
$submitButton->eq(0)->before($html);
2026-09-21 15:26:59 +02:00
} else {
$form->append($html);
}
}
private function includeScript(phpQueryObject $document): void
{
if (\count($document->find('script[data-fp-altcha-script]')) > 0) {
return;
}
2026-09-21 16:15:17 +02:00
// Bewusst analog zum bewaehrten Muster anderer, quelloffener JTL5-Plugins direkt ueber
2026-09-21 15:26:59 +02:00
// die globale URL_SHOP-Konstante aufgebaut, statt eine Plugin-Pfad-API zu erraten.
$assetUrl = \URL_SHOP . '/plugins/fp_altcha_spamschutz/assets/fp-altcha.js';
$assetUrl = htmlspecialchars($assetUrl, \ENT_QUOTES, 'UTF-8');
$document->find('body')->append(
'<script data-fp-altcha-script defer src="' . $assetUrl . '"></script>'
);
}
}