feat: webhook form finisher for generic post requests

addresses #869cupm97
This commit is contained in:
Björn Fromme
2026-06-16 11:18:07 +02:00
parent 90a5fab2fa
commit 9933a0d3f7
6 changed files with 129 additions and 23 deletions
@@ -0,0 +1,43 @@
<?php
namespace EP\EpTheme\Form;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;
abstract class AbstractMappedFinisher extends AbstractFinisher
{
protected function buildPayload(): array
{
$values = [];
$formValues = $this->finisherContext->getFormValues();
$mappingOption = (string)$this->parseOption('mapping');
$mappings = GeneralUtility::trimExplode(',', $mappingOption, true);
foreach ($mappings as $mapping) {
$parts = GeneralUtility::trimExplode(':', $mapping, true, 2);
if (2 !== count($parts)) {
continue;
}
[$remote, $local] = $parts;
if ('' === $remote || '' === $local || false === array_key_exists($local, $formValues)) {
continue;
}
$values[$remote] = $formValues[$local];
}
return $values;
}
protected function logWarning(string $message, array $context = []): void
{
GeneralUtility::makeInstance(LogManager::class)
->getLogger(static::class)
->warning($message, array_merge([
'finisher' => static::class,
], $context));
}
}
@@ -3,10 +3,8 @@
namespace EP\EpTheme\Form;
use EP\EpProducts\MyEP\ApiClient;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;
class BpnApiFinisher extends AbstractFinisher
class BpnApiFinisher extends AbstractMappedFinisher
{
/**
* @var ApiClient
@@ -24,30 +22,14 @@ class BpnApiFinisher extends AbstractFinisher
protected function executeInternal()
{
try {
$values = [];
$formValues = $this->finisherContext->getFormValues();
$mappingOption = (string)$this->parseOption('mapping');
$mappings = GeneralUtility::trimExplode(',', $mappingOption, true);
foreach ($mappings as $mapping) {
$parts = GeneralUtility::trimExplode(':', $mapping, true, 2);
if (2 !== count($parts)) {
continue;
}
[$remote, $local] = $parts;
if ('' === $remote || '' === $local || false === array_key_exists($local, $formValues)) {
continue;
}
$values[$remote] = $formValues[$local];
}
$values = $this->buildPayload();
if ([] !== $values) {
$this->apiClient->registerAddress($values);
}
} catch (\Throwable $e) {
// Intentionally ignore all errors so the form submission stays silent.
$this->logWarning('Bpn API finisher request failed.', [
'exception' => $e,
]);
}
}
}
@@ -0,0 +1,40 @@
<?php
namespace EP\EpTheme\Form;
use Symfony\Component\HttpClient\HttpClient;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class WebhookFinisher extends AbstractMappedFinisher
{
protected function executeInternal()
{
try {
$url = trim((string)$this->parseOption('url'));
if ('' === $url) {
return;
}
$payload = $this->buildPayload();
if ([] === $payload) {
return;
}
$response = HttpClient::create()->request('POST', $url, [
'json' => $payload,
]);
$statusCode = $response->getStatusCode();
if ($statusCode < 200 || $statusCode >= 300) {
$this->logWarning('Webhook finisher returned an unexpected response status.', [
'url' => $url,
'statusCode' => $statusCode,
]);
}
} catch (\Throwable $e) {
$this->logWarning('Webhook finisher request failed.', [
'exception' => $e,
]);
}
}
}
@@ -23,6 +23,8 @@ TYPO3:
finishersDefinition:
BpnApiFinisher:
implementationClassName: 'EP\EpTheme\Form\BpnApiFinisher'
WebhookFinisher:
implementationClassName: 'EP\EpTheme\Form\WebhookFinisher'
AddReferrerFinisher:
implementationClassName: 'EP\EpTheme\Form\AddReferrerFinisher'
@@ -12,6 +12,9 @@ TYPO3:
1000:
value: 'BpnApiFinisher'
label: 'BusPro Finisher'
1050:
value: 'WebhookFinisher'
label: 'Webhook Finisher'
1100:
value: 'AddReferrerFinisher'
label: 'Referrer Finisher'
@@ -31,6 +34,27 @@ TYPO3:
propertyPath: 'options.mapping'
propertyValidators:
10: 'NotEmpty'
1050:
identifier: 'WebhookFinisher'
editors:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
100:
label: 'Webhook Finisher'
110:
identifier: 'mapping'
templateName: 'Inspector-TextEditor'
label: 'Mapping'
propertyPath: 'options.mapping'
propertyValidators:
10: 'NotEmpty'
120:
identifier: 'url'
templateName: 'Inspector-TextEditor'
label: 'URL'
propertyPath: 'options.url'
propertyValidators:
10: 'NotEmpty'
1100:
identifier: 'AddReferrerFinisher'
editors:
@@ -46,6 +70,13 @@ TYPO3:
predefinedDefaults:
options:
mapping: 'lastName:text-1,firstName:text-2,gender:singleselect-1,email:email-1,phone:telephone-1'
WebhookFinisher:
formEditor:
iconIdentifier: 'form-finisher'
predefinedDefaults:
options:
mapping: 'lastName:text-1,firstName:text-2,gender:singleselect-1,email:email-1,phone:telephone-1'
url: ''
AddReferrerFinisher:
formEditor:
iconIdentifier: 'form-finisher'
@@ -11,6 +11,14 @@ TYPO3:
options:
identifier: 'BpnApiFinisher'
mapping: 'lastName:text-1,firstName:text-2,gender:singleselect-1,email:email-1,phone:telephone-1'
WebhookFinisher:
implementationClassName: 'EP\EpTheme\Form\WebhookFinisher'
formEditor:
label: 'Webhook Finisher'
options:
identifier: 'WebhookFinisher'
mapping: 'lastName:text-1,firstName:text-2,gender:singleselect-1,email:email-1,phone:telephone-1'
url: ''
AddReferrerFinisher:
implementationClassName: 'EP\EpTheme\Form\AddReferrerFinisher'
formEditor: