Loading...
Loading...
Compare original and translation side by side
Compatibility: TYPO3 v13.x and v14.x with Powermail 13.x All code examples target PHP 8.2+ and TYPO3 v13/v14.
TYPO3 API First: Always use TYPO3's built-in APIs, core features, and established conventions before creating custom implementations. Do not reinvent what TYPO3 already provides. Always verify that the APIs and methods you use exist and are not deprecated in your target TYPO3 version (v13 or v14) by checking the official TYPO3 documentation.
Supplements:
- SKILL-CONDITIONS.md - Conditional field/page visibility (powermail_cond)
- SKILL-PHP84.md - PHP 8.4 patterns (property hooks, asymmetric visibility, new array functions)
- SKILL-EXAMPLES.md - Multi-step shop form with Austrian legal types, DDEV SQL + DataHandler CLI
兼容性: TYPO3 v13.x 和 v14.x 搭配 Powermail 13.x 所有代码示例针对 PHP 8.2+ 和 TYPO3 v13/v14。
优先使用TYPO3 API: 在创建自定义实现前,务必优先使用TYPO3的内置API、核心功能及既定规范,不要重复造轮子。请务必通过官方TYPO3文档验证你使用的API和方法在目标TYPO3版本(v13或v14)中存在且未被弃用。
补充资源:
- SKILL-CONDITIONS.md - 条件字段/页面可见性(powermail_cond)
- SKILL-PHP84.md - PHP 8.4 模式(属性钩子、非对称可见性、新数组函数)
- SKILL-EXAMPLES.md - 带奥地利法律类型的多步骤店铺表单、DDEV SQL + DataHandler CLI示例
Form (tx_powermail_domain_model_form)
└── Page (tx_powermail_domain_model_page)
└── Field (tx_powermail_domain_model_field)
Mail (tx_powermail_domain_model_mail)
└── Answer (tx_powermail_domain_model_answer)
└── references FieldForm (tx_powermail_domain_model_form)
└── Page (tx_powermail_domain_model_page)
└── Field (tx_powermail_domain_model_field)
Mail (tx_powermail_domain_model_mail)
└── Answer (tx_powermail_domain_model_answer)
└── references FieldformcreateconfirmationoptinConfirmdisclaimermarketingformcreateconfirmationoptinConfirmdisclaimermarketingcomposer require in2code/powermailcomposer require in2code/powermail| Type | Key | Value Type | Notes |
|---|---|---|---|
| Text | | TEXT (0) | Standard input |
| Textarea | | TEXT (0) | Multi-line |
| Select | | TEXT/ARRAY (0/1) | Multiselect possible |
| Checkbox | | ARRAY (1) | Multiple values |
| Radio | | TEXT (0) | Single selection |
| Submit | | — | Form submit button |
| Captcha | | TEXT (0) | Built-in CAPTCHA |
| Reset | | — | Form reset button |
| Static text | | — | Display only |
| Content element | | — | CE reference |
| HTML | | TEXT (0) | Raw HTML |
| Password | | PASSWORD (4) | Hashed storage |
| File upload | | UPLOAD (3) | File attachments |
| Hidden | | TEXT (0) | Hidden input |
| Date | | DATE (2) | Datepicker |
| Country | | TEXT (0) | Country selector |
| Location | | TEXT (0) | Geolocation |
| TypoScript | | TEXT (0) | TS-generated content |
| 类型 | 标识 | 值类型 | 说明 |
|---|---|---|---|
| 文本 | | TEXT (0) | 标准输入框 |
| 文本域 | | TEXT (0) | 多行输入框 |
| 下拉选择框 | | TEXT/ARRAY (0/1) | 支持多选 |
| 复选框 | | ARRAY (1) | 支持多值选择 |
| 单选框 | | TEXT (0) | 单选选择 |
| 提交按钮 | | — | 表单提交按钮 |
| 验证码 | | TEXT (0) | 内置CAPTCHA验证 |
| 重置按钮 | | — | 表单重置按钮 |
| 静态文本 | | — | 仅用于展示 |
| 内容元素 | | — | 关联内容元素(CE) |
| HTML代码 | | TEXT (0) | 原生HTML内容 |
| 密码框 | | PASSWORD (4) | 哈希加密存储 |
| 文件上传 | | UPLOAD (3) | 文件附件上传 |
| 隐藏字段 | | TEXT (0) | 隐藏输入字段 |
| 日期选择器 | | DATE (2) | 日期选择控件 |
| 国家选择器 | | TEXT (0) | 国家下拉选择 |
| 位置选择器 | | TEXT (0) | 地理定位选择 |
| TypoScript生成 | | TEXT (0) | 通过TS生成内容 |
Answer::VALUE_TYPE_TEXT = 0; // String values
Answer::VALUE_TYPE_ARRAY = 1; // JSON-encoded arrays (checkboxes, multiselect)
Answer::VALUE_TYPE_DATE = 2; // Timestamps
Answer::VALUE_TYPE_UPLOAD = 3; // File references
Answer::VALUE_TYPE_PASSWORD = 4; // Hashed passwordsAnswer::VALUE_TYPE_TEXT = 0; // 字符串值
Answer::VALUE_TYPE_ARRAY = 1; // JSON编码的数组(复选框、多选下拉)
Answer::VALUE_TYPE_DATE = 2; // 时间戳
Answer::VALUE_TYPE_UPLOAD = 3; // 文件引用
Answer::VALUE_TYPE_PASSWORD = 4; // 哈希加密的密码plugin.tx_powermail {
settings {
setup {
# Form settings
main {
pid = {$plugin.tx_powermail.settings.main.pid}
form = {$plugin.tx_powermail.settings.main.form}
confirmation = 0
optin = 0
morestep = 0
}
# Receiver mail
receiver {
enable = 1
subject = Mail from {firstname} {lastname}
body = A new mail from your website
senderNameField = firstname
senderEmailField = email
# Override receiver: receiver.overwrite.email = admin@example.com
# Attach uploads: receiver.attachment = 1
# Add CC: receiver.overwrite.cc = copy@example.com
}
# Sender confirmation mail
sender {
enable = 1
subject = Thank you for your message
body = We received your submission
senderName = Website
senderEmail = noreply@example.com
}
# Double Opt-In
optin {
subject = Please confirm your submission
senderName = Website
senderEmail = noreply@example.com
}
# Thank you page
thx {
redirect = # Page UID for redirect after submit
}
# Spam protection
spamshield {
_enable = 1
indicator {
honeypod = 5
link = 3
name = 3
session = 1
unique = 2
blacklistString = 7
blacklistIp = 7
rateLimit = 10
}
# Factor threshold (0-100): reject if >=
factor = 75
}
# Validation
misc {
htmlForLabels = 1
showOnlyFilledValues = 1
ajaxSubmit = 0
file {
folder = uploads/tx_powermail/
size = 25000000
extension = jpg,jpeg,gif,png,tif,txt,doc,docx,xls,xlsx,ppt,pptx,pdf,zip,csv,svg
}
}
}
}
}plugin.tx_powermail {
settings {
setup {
# 表单设置
main {
pid = {$plugin.tx_powermail.settings.main.pid}
form = {$plugin.tx_powermail.settings.main.form}
confirmation = 0
optin = 0
morestep = 0
}
# 收件人邮件
receiver {
enable = 1
subject = Mail from {firstname} {lastname}
body = A new mail from your website
senderNameField = firstname
senderEmailField = email
# 覆盖收件人:receiver.overwrite.email = admin@example.com
# 附加上传文件:receiver.attachment = 1
# 添加抄送:receiver.overwrite.cc = copy@example.com
}
# 发件人确认邮件
sender {
enable = 1
subject = Thank you for your message
body = We received your submission
senderName = Website
senderEmail = noreply@example.com
}
# 双重选择加入(Double Opt-In)
optin {
subject = Please confirm your submission
senderName = Website
senderEmail = noreply@example.com
}
# 感谢页设置
thx {
redirect = # 提交后跳转的页面UID
}
# 垃圾邮件防护
spamshield {
_enable = 1
indicator {
honeypod = 5
link = 3
name = 3
session = 1
unique = 2
blacklistString = 7
blacklistIp = 7
rateLimit = 10
}
# 阈值(0-100):得分超过该值则拒绝
factor = 75
}
# 验证设置
misc {
htmlForLabels = 1
showOnlyFilledValues = 1
ajaxSubmit = 0
file {
folder = uploads/tx_powermail/
size = 25000000
extension = jpg,jpeg,gif,png,tif,txt,doc,docx,xls,xlsx,ppt,pptx,pdf,zip,csv,svg
}
}
}
}
}plugin.tx_powermail.settings.setup.prefill {
# By field marker
email = TEXT
email.data = TSFE:fe_user|user|email
firstname = TEXT
firstname.data = TSFE:fe_user|user|first_name
# Prefill from GET/POST
subject = TEXT
subject.data = GP:subject
}plugin.tx_powermail.settings.setup.prefill {
# 通过字段标记预填充
email = TEXT
email.data = TSFE:fe_user|user|email
firstname = TEXT
firstname.data = TSFE:fe_user|user|first_name
# 从GET/POST参数预填充
subject = TEXT
subject.data = GP:subject
}plugin.tx_powermail.settings.setup.marketing {
enable = 1
# Tracked: refererDomain, referer, country, mobileDevice, frontendLanguage, browserLanguage, pageFunnel
}plugin.tx_powermail.settings.setup.marketing {
enable = 1
# 追踪项:refererDomain、referer、country、mobileDevice、frontendLanguage、browserLanguage、pageFunnel
}plugin.tx_powermail.settings.setup.finishers {
# Lower number = runs first
0.class = In2code\Powermail\Finisher\RateLimitFinisher
10.class = In2code\Powermail\Finisher\SaveToAnyTableFinisher
20.class = In2code\Powermail\Finisher\SendParametersFinisher
100.class = In2code\Powermail\Finisher\RedirectFinisher
# Custom finisher
50.class = Vendor\MyExt\Finisher\CrmFinisher
50.config {
apiUrl = https://crm.example.com/api
apiKey = secret123
}
}plugin.tx_powermail.settings.setup.finishers {
# 数值越小,执行顺序越靠前
0.class = In2code\Powermail\Finisher\RateLimitFinisher
10.class = In2code\Powermail\Finisher\SaveToAnyTableFinisher
20.class = In2code\Powermail\Finisher\SendParametersFinisher
100.class = In2code\Powermail\Finisher\RedirectFinisher
# 自定义完成器
50.class = Vendor\MyExt\Finisher\CrmFinisher
50.config {
apiUrl = https://crm.example.com/api
apiKey = secret123
}
}<?php
declare(strict_types=1);
namespace Vendor\MyExt\Finisher;
use In2code\Powermail\Finisher\AbstractFinisher;
use In2code\Powermail\Finisher\FinisherInterface;
use In2code\Powermail\Domain\Model\Mail;
final class CrmFinisher extends AbstractFinisher implements FinisherInterface
{
/**
* Method name MUST end with "Finisher"
* Can have initialize*Finisher() called before
*/
public function myCustomFinisher(): void
{
/** @var Mail $mail */
$mail = $this->getMail();
$settings = $this->getSettings();
$configuration = $this->getConfiguration(); // TS config.*
// Access form answers
foreach ($mail->getAnswers() as $answer) {
$fieldMarker = $answer->getField()->getMarker();
$value = $answer->getValue();
// Process...
}
// Access by marker
$answers = $mail->getAnswersByFieldMarker();
$email = $answers['email'] ?? null;
// Check if form was actually submitted (not just displayed)
if (!$this->isFormSubmitted()) {
return;
}
}
}<?php
declare(strict_types=1);
namespace Vendor\MyExt\Finisher;
use In2code\Powermail\Finisher\AbstractFinisher;
use In2code\Powermail\Finisher\FinisherInterface;
use In2code\Powermail\Domain\Model\Mail;
final class CrmFinisher extends AbstractFinisher implements FinisherInterface
{
/**
* 方法名称必须以“Finisher”结尾
* 执行前会调用initialize*Finisher()方法
*/
public function myCustomFinisher(): void
{
/** @var Mail $mail */
$mail = $this->getMail();
$settings = $this->getSettings();
$configuration = $this->getConfiguration(); // TS配置中的config.*
// 遍历获取所有表单回复
foreach ($mail->getAnswers() as $answer) {
$fieldMarker = $answer->getField()->getMarker();
$value = $answer->getValue();
// 处理逻辑...
}
// 通过标记获取回复
$answers = $mail->getAnswersByFieldMarker();
$email = $answers['email'] ?? null;
// 检查表单是否实际提交(而非仅展示)
if (!$this->isFormSubmitted()) {
return;
}
}
}| Class | Key | Purpose |
|---|---|---|
| 0 | Consumes rate limiter tokens |
| 10 | Save answers to custom DB tables |
| 20 | POST form data to external URL |
| 100 | Redirect after submission |
| 类名 | 标识 | 用途 |
|---|---|---|
| 0 | 消耗速率限制器的令牌 |
| 10 | 将回复保存到自定义数据库表 |
| 20 | 将表单数据POST到外部URL |
| 100 | 提交后跳转页面 |
plugin.tx_powermail.settings.setup.dbEntry {
1 {
_enable = TEXT
_enable.value = 1
_table = fe_users
_ifUnique.email = update # update|skip|none
username.value = {email}
email.value = {email}
first_name.value = {firstname}
last_name.value = {lastname}
pid.value = 123
}
}plugin.tx_powermail.settings.setup.dbEntry {
1 {
_enable = TEXT
_enable.value = 1
_table = fe_users
_ifUnique.email = update # update|skip|none
username.value = {email}
email.value = {email}
first_name.value = {firstname}
last_name.value = {lastname}
pid.value = 123
}
}<?php
declare(strict_types=1);
namespace Vendor\MyExt\EventListener;
use In2code\Powermail\Events\CustomValidatorEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener('vendor-myext/custom-validator')]
final class CustomValidatorListener
{
public function __invoke(CustomValidatorEvent $event): void
{
$mail = $event->getMail();
$field = $event->getField();
// Validate specific field by marker
if ($field->getMarker() === 'company_vat') {
$answer = null;
foreach ($mail->getAnswers() as $a) {
if ($a->getField()?->getUid() === $field->getUid()) {
$answer = $a;
break;
}
}
if ($answer !== null && !$this->isValidVat((string)$answer->getValue())) {
$event->setIsValid(false);
$event->setValidationMessage('Invalid VAT number');
}
}
}
private function isValidVat(string $vat): bool
{
return (bool)preg_match('/^[A-Z]{2}\d{8,12}$/', $vat);
}
}<?php
declare(strict_types=1);
namespace Vendor\MyExt\EventListener;
use In2code\Powermail\Events\CustomValidatorEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener('vendor-myext/custom-validator')]
final class CustomValidatorListener
{
public function __invoke(CustomValidatorEvent $event): void
{
$mail = $event->getMail();
$field = $event->getField();
// 针对特定标记的字段进行验证
if ($field->getMarker() === 'company_vat') {
$answer = null;
foreach ($mail->getAnswers() as $a) {
if ($a->getField()?->getUid() === $field->getUid()) {
$answer = $a;
break;
}
}
if ($answer !== null && !$this->isValidVat((string)$answer->getValue())) {
$event->setIsValid(false);
$event->setValidationMessage('Invalid VAT number');
}
}
}
private function isValidVat(string $vat): bool
{
return (bool)preg_match('/^[A-Z]{2}\d{8,12}$/', $vat);
}
}| Validator | Purpose |
|---|---|
| Email, URL, phone, number, letters, min/max length, regex |
| File size, extension whitelist |
| Password match and strength |
| Built-in CAPTCHA |
| Multi-method spam detection |
| Unique field values |
| Validate against foreign table |
| TypoScript-based custom rules |
| 验证器 | 用途 |
|---|---|
| 邮箱、URL、电话、数字、字符、长度、正则验证 |
| 文件大小、扩展名白名单验证 |
| 密码匹配与强度验证 |
| 内置CAPTCHA验证 |
| 多维度垃圾邮件检测 |
| 字段值唯一性验证 |
| 关联外部表验证 |
| 基于TypoScript的自定义规则验证 |
| Method | Weight | Description |
|---|---|---|
| 5 | Hidden honeypot field |
| 3 | Excessive links detection |
| 3 | Suspicious name patterns |
| 1 | Session token validation |
| 2 | Duplicate submission check |
| 7 | Blacklisted content |
| 7 | Blacklisted IP addresses |
| 10 | Request rate limiting |
| 方法名称 | 权重 | 说明 |
|---|---|---|
| 5 | 隐藏蜜罐字段检测 |
| 3 | 过多链接检测 |
| 3 | 可疑名称模式检测 |
| 1 | Session令牌验证 |
| 2 | 重复提交检测 |
| 7 | 黑名单内容检测 |
| 7 | 黑名单IP检测 |
| 10 | 请求速率限制检测 |
// Before form is rendered
FormControllerFormActionEvent
// Before confirmation page
FormControllerConfirmationActionEvent
// After mail is saved to database
FormControllerCreateActionAfterMailDbSavedEvent
// After submit view is built
FormControllerCreateActionAfterSubmitViewEvent
// Before final view is rendered
FormControllerCreateActionBeforeRenderViewEvent
// Controller initialization
FormControllerInitializeObjectEvent// 表单渲染前触发
FormControllerFormActionEvent
// 确认页展示前触发
FormControllerConfirmationActionEvent
// 邮件保存到数据库后触发
FormControllerCreateActionAfterMailDbSavedEvent
// 提交视图构建完成后触发
FormControllerCreateActionAfterSubmitViewEvent
// 最终视图渲染前触发
FormControllerCreateActionBeforeRenderViewEvent
// 控制器初始化时触发
FormControllerInitializeObjectEvent// Modify receiver email addresses
ReceiverMailReceiverPropertiesServiceSetReceiverEmailsEvent
// Modify receiver name
ReceiverMailReceiverPropertiesServiceGetReceiverNameEvent
// Modify sender email (receiver mail)
ReceiverMailSenderPropertiesGetSenderEmailEvent
// Modify sender name (receiver mail)
ReceiverMailSenderPropertiesGetSenderNameEvent
// Modify sender email (confirmation mail)
SenderMailPropertiesGetSenderEmailEvent
// Modify sender name (confirmation mail)
SenderMailPropertiesGetSenderNameEvent
// Modify email body before sending
SendMailServiceCreateEmailBodyEvent
// Before email is sent (last chance to modify)
SendMailServicePrepareAndSendEvent// 修改收件人邮箱地址
ReceiverMailReceiverPropertiesServiceSetReceiverEmailsEvent
// 修改收件人名称
ReceiverMailReceiverPropertiesServiceGetReceiverNameEvent
// 修改发件人邮箱(收件人邮件)
ReceiverMailSenderPropertiesGetSenderEmailEvent
// 修改发件人名称(收件人邮件)
ReceiverMailSenderPropertiesGetSenderNameEvent
// 修改发件人邮箱(确认邮件)
SenderMailPropertiesGetSenderEmailEvent
// 修改发件人名称(确认邮件)
SenderMailPropertiesGetSenderNameEvent
// 发送前修改邮件内容
SendMailServiceCreateEmailBodyEvent
// 邮件发送前触发(最后修改机会)
SendMailServicePrepareAndSendEvent// Control if mail should be saved to DB
CheckIfMailIsAllowedToSaveEvent
// Custom validation logic
CustomValidatorEvent
// Prefill field values
PrefillFieldViewHelperEvent
PrefillMultiFieldViewHelperEvent
// File upload processing
UploadServicePreflightEvent
UploadServiceGetFilesEvent
GetNewPathAndFilenameEvent
// Before password is hashed
MailFactoryBeforePasswordIsHashedEvent
// Modify mail variables/markers
MailRepositoryGetVariablesWithMarkersFromMailEvent
// Validation data attributes
ValidationDataAttributeViewHelperEvent
// Double opt-in confirmation
FormControllerOptinConfirmActionAfterPersistEvent
FormControllerOptinConfirmActionBeforeRenderViewEvent
// Disclaimer/unsubscribe
FormControllerDisclaimerActionBeforeRenderViewEvent// 控制邮件是否允许保存到数据库
CheckIfMailIsAllowedToSaveEvent
// 自定义验证逻辑
CustomValidatorEvent
// 预填充字段值
PrefillFieldViewHelperEvent
PrefillMultiFieldViewHelperEvent
// 文件上传处理
UploadServicePreflightEvent
UploadServiceGetFilesEvent
GetNewPathAndFilenameEvent
// 密码哈希前触发
MailFactoryBeforePasswordIsHashedEvent
// 修改邮件变量/标记
MailRepositoryGetVariablesWithMarkersFromMailEvent
// 验证数据属性
ValidationDataAttributeViewHelperEvent
// 双重Opt-In确认
FormControllerOptinConfirmActionAfterPersistEvent
FormControllerOptinConfirmActionBeforeRenderViewEvent
// 免责声明/退订
FormControllerDisclaimerActionBeforeRenderViewEvent<?php
declare(strict_types=1);
namespace Vendor\MyExt\EventListener;
use In2code\Powermail\Events\ReceiverMailReceiverPropertiesServiceSetReceiverEmailsEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener('vendor-myext/dynamic-receiver')]
final class DynamicReceiverListener
{
public function __invoke(
ReceiverMailReceiverPropertiesServiceSetReceiverEmailsEvent $event
): void {
$mail = $event->getMail();
$answers = $mail->getAnswersByFieldMarker();
// Route to department based on form field
$department = $answers['department'] ?? null;
if ($department !== null) {
$value = (string)$department->getValue();
$emails = match ($value) {
'sales' => ['sales@example.com'],
'support' => ['support@example.com'],
default => $event->getReceiverEmails(),
};
$event->setReceiverEmails($emails);
}
}
}<?php
declare(strict_types=1);
namespace Vendor\MyExt\EventListener;
use In2code\Powermail\Events\ReceiverMailReceiverPropertiesServiceSetReceiverEmailsEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener('vendor-myext/dynamic-receiver')]
final class DynamicReceiverListener
{
public function __invoke(
ReceiverMailReceiverPropertiesServiceSetReceiverEmailsEvent $event
): void {
$mail = $event->getMail();
$answers = $mail->getAnswersByFieldMarker();
// 根据表单字段值路由到对应部门邮箱
$department = $answers['department'] ?? null;
if ($department !== null) {
$value = (string)$department->getValue();
$emails = match ($value) {
'sales' => ['sales@example.com'],
'support' => ['support@example.com'],
default => $event->getReceiverEmails(),
};
$event->setReceiverEmails($emails);
}
}
}<?php
declare(strict_types=1);
namespace Vendor\MyExt\EventListener;
use In2code\Powermail\Events\CheckIfMailIsAllowedToSaveEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener('vendor-myext/skip-db-save')]
final class SkipDbSaveListener
{
public function __invoke(CheckIfMailIsAllowedToSaveEvent $event): void
{
// Skip DB save for specific forms
$form = $event->getMail()->getForm();
if ($form !== null && $form->getTitle() === 'Contact (no storage)') {
$event->setSavingOfMailAllowed(false);
}
}
}<?php
declare(strict_types=1);
namespace Vendor\MyExt\EventListener;
use In2code\Powermail\Events\CheckIfMailIsAllowedToSaveEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener('vendor-myext/skip-db-save')]
final class SkipDbSaveListener
{
public function __invoke(CheckIfMailIsAllowedToSaveEvent $event): void
{
// 针对特定表单跳过数据库保存
$form = $event->getMail()->getForm();
if ($form !== null && $form->getTitle() === 'Contact (no storage)') {
$event->setSavingOfMailAllowed(false);
}
}
}plugin.tx_powermail {
view {
templateRootPaths {
0 = EXT:powermail/Resources/Private/Templates/
10 = EXT:my_ext/Resources/Private/Templates/Powermail/
}
partialRootPaths {
0 = EXT:powermail/Resources/Private/Partials/
10 = EXT:my_ext/Resources/Private/Partials/Powermail/
}
layoutRootPaths {
0 = EXT:powermail/Resources/Private/Layouts/
10 = EXT:my_ext/Resources/Private/Layouts/Powermail/
}
}
}plugin.tx_powermail {
view {
templateRootPaths {
0 = EXT:powermail/Resources/Private/Templates/
10 = EXT:my_ext/Resources/Private/Templates/Powermail/
}
partialRootPaths {
0 = EXT:powermail/Resources/Private/Partials/
10 = EXT:my_ext/Resources/Private/Partials/Powermail/
}
layoutRootPaths {
0 = EXT:powermail/Resources/Private/Layouts/
10 = EXT:my_ext/Resources/Private/Layouts/Powermail/
}
}
}| Template | Purpose |
|---|---|
| Main form rendering |
| Confirmation page |
| Thank you page |
| Admin notification email |
| User confirmation email |
| Double opt-in email |
| All-fields summary |
| 模板名称 | 用途 |
|---|---|
| 主表单渲染模板 |
| 确认页模板 |
| 感谢页模板 |
| 管理员通知邮件模板 |
| 用户确认邮件模板 |
| 双重Opt-In邮件模板 |
| 所有字段汇总模板 |
Partials/Form/Field/Input.html
Partials/Form/Field/Textarea.html
Partials/Form/Field/Select.html
Partials/Form/Field/Check.html
Partials/Form/Field/Radio.html
Partials/Form/Field/File.html
Partials/Form/Field/Date.html
Partials/Form/Field/Captcha.html
Partials/Form/Field/Hidden.html
Partials/Form/Field/Password.html
Partials/Form/Field/Country.html
Partials/Form/Field/Location.html
Partials/Form/Field/Html.html
Partials/Form/Field/Content.html
Partials/Form/Field/Typoscript.html
Partials/Form/Field/Submit.html
Partials/Form/Field/Reset.htmlPartials/Form/Field/Input.html
Partials/Form/Field/Textarea.html
Partials/Form/Field/Select.html
Partials/Form/Field/Check.html
Partials/Form/Field/Radio.html
Partials/Form/Field/File.html
Partials/Form/Field/Date.html
Partials/Form/Field/Captcha.html
Partials/Form/Field/Hidden.html
Partials/Form/Field/Password.html
Partials/Form/Field/Country.html
Partials/Form/Field/Location.html
Partials/Form/Field/Html.html
Partials/Form/Field/Content.html
Partials/Form/Field/Typoscript.html
Partials/Form/Field/Submit.html
Partials/Form/Field/Reset.html<!-- In ReceiverMail.html / SenderMail.html -->
{mail} <!-- Mail domain object -->
{mail.senderName} <!-- Sender name -->
{mail.senderMail} <!-- Sender email -->
{mail.form.title} <!-- Form title -->
{mail.answers} <!-- All answers (ObjectStorage) -->
<!-- Iterate answers -->
<f:for each="{mail.answers}" as="answer">
{answer.field.title}: {answer.value}
</f:for>
<!-- PowermailAll marker (all fields formatted) -->
{powermail_all}<!-- 在ReceiverMail.html / SenderMail.html中 -->
{mail} <!-- Mail领域模型对象 -->
{mail.senderName} <!-- 发件人名称 -->
{mail.senderMail} <!-- 发件人邮箱 -->
{mail.form.title} <!-- 表单标题 -->
{mail.answers} <!-- 所有回复(ObjectStorage类型) -->
<!-- 遍历所有回复 -->
<f:for each="{mail.answers}" as="answer">
{answer.field.title}: {answer.value}
</f:for>
<!-- PowermailAll标记(所有字段格式化展示) -->
{powermail_all}<!-- Enable JS validation and/or AJAX submit -->
<vh:validation.enableJavascriptValidationAndAjax
form="{form}"
additionalAttributes="{...}" />
<!-- Validation data attributes on fields -->
<vh:validation.validationDataAttribute field="{field}" />
<!-- Error CSS class -->
<vh:validation.errorClass field="{field}" class="error" />
<!-- Upload attributes (accept, multiple) -->
<vh:validation.uploadAttributes field="{field}" /><!-- 启用JS验证和/或AJAX提交 -->
<vh:validation.enableJavascriptValidationAndAjax
form="{form}"
additionalAttributes="{...}" />
<!-- 为字段添加验证数据属性 -->
<vh:validation.validationDataAttribute field="{field}" />
<!-- 错误CSS类 -->
<vh:validation.errorClass field="{field}" class="error" />
<!-- 上传控件属性(accept、multiple) -->
<vh:validation.uploadAttributes field="{field}" /><!-- Country selector -->
<vh:form.countries
settings="{settings}"
field="{field}"
mail="{mail}" />
<!-- Advanced select with optgroups -->
<vh:form.advancedSelect
field="{field}"
mail="{mail}" />
<!-- Multi-upload -->
<vh:form.multiUpload field="{field}" /><!-- 国家选择器 -->
<vh:form.countries
settings="{settings}"
field="{field}"
mail="{mail}" />
<!-- 带选项组的高级下拉选择框 -->
<vh:form.advancedSelect
field="{field}"
mail="{mail}" />
<!-- 多文件上传 -->
<vh:form.multiUpload field="{field}" /><!-- Prefill single-value field -->
<vh:misc.prefillField field="{field}" mail="{mail}" />
<!-- Prefill multi-value field (select, check, radio) -->
<vh:misc.prefillMultiField field="{field}" mail="{mail}" cycle="{cycle}" /><!-- 单值字段预填充 -->
<vh:misc.prefillField field="{field}" mail="{mail}" />
<!-- 多值字段预填充(下拉、复选、单选) -->
<vh:misc.prefillMultiField field="{field}" mail="{mail}" cycle="{cycle}" /><!-- Check if field is not empty -->
<vh:condition.isNotEmpty val="{value}">
<f:then>Has value</f:then>
</vh:condition.isNotEmpty>
<!-- Check if array -->
<vh:condition.isArray val="{value}">
<f:then>Is array</f:then>
</vh:condition.isArray>
<!-- Check file exists -->
<vh:condition.fileExists file="{path}">
<f:then>File available</f:then>
</vh:condition.fileExists><!-- 检查字段是否非空 -->
<vh:condition.isNotEmpty val="{value}">
<f:then>已填写内容</f:then>
</vh:condition.isNotEmpty>
<!-- 检查是否为数组 -->
<vh:condition.isArray val="{value}">
<f:then>是数组类型</f:then>
</vh:condition.isArray>
<!-- 检查文件是否存在 -->
<vh:condition.fileExists file="{path}">
<f:then>文件存在</f:then>
</vh:condition.fileExists><!-- Edit link in backend module -->
<vh:be.editLink table="tx_powermail_domain_model_mail" uid="{mail.uid}">
Edit
</vh:be.editLink><!-- 后端模块中的编辑链接 -->
<vh:be.editLink table="tx_powermail_domain_model_mail" uid="{mail.uid}">
编辑
</vh:be.editLink>plugin.tx_powermail.settings.setup.misc.ajaxSubmit = 1plugin.tx_powermail.settings.setup.misc.ajaxSubmit = 1plugin.tx_powermail.settings.setup.main.optin = 1
plugin.tx_powermail.settings.setup.optin {
subject = Please confirm your submission
senderName = My Website
senderEmail = noreply@example.com
}hidden=1optinConfirmActionplugin.tx_powermail.settings.setup.main.optin = 1
plugin.tx_powermail.settings.setup.optin {
subject = Please confirm your submission
senderName = My Website
senderEmail = noreply@example.com
}hidden=1optinConfirmAction#mail:searchterm#form:searchterm#mail:搜索关键词#form:搜索关键词services:
Vendor\MyExt\EventListener\CrmSyncListener:
tags:
- name: event.listener
identifier: 'vendor-myext/crm-sync'#[AsEventListener]services:
Vendor\MyExt\EventListener\CrmSyncListener:
tags:
- name: event.listener
identifier: 'vendor-myext/crm-sync'#[AsEventListener]// By field marker (most common)
$answers = $mail->getAnswersByFieldMarker();
$email = $answers['email']?->getValue();
// By field UID
$answers = $mail->getAnswersByFieldUid();
// Filter by value type
$uploads = $mail->getAnswersByValueType(Answer::VALUE_TYPE_UPLOAD);// 通过字段标记获取(最常用)
$answers = $mail->getAnswersByFieldMarker();
$email = $answers['email']?->getValue();
// 通过字段UID获取
$answers = $mail->getAnswersByFieldUid();
// 按值类型筛选
$uploads = $mail->getAnswersByValueType(Answer::VALUE_TYPE_UPLOAD);// Add custom data (available in all finishers/events)
$mail->addAdditionalData('crm_id', $crmResponse['id']);
// Retrieve in another finisher/event
$crmId = $mail->getAdditionalData()['crm_id'] ?? null;// 添加自定义数据(所有完成器/事件中均可访问)
$mail->addAdditionalData('crm_id', $crmResponse['id']);
// 在其他完成器/事件中获取
$crmId = $mail->getAdditionalData()['crm_id'] ?? null;ext_conf_template.txtext_conf_template.txtTableGarbageCollectionTaskTableGarbageCollectionTaskrouteEnhancers:
PowermailOptIn:
type: Plugin
routePath: '/optin/{mail}/{hash}'
namespace: 'tx_powermail_pi1'
requirements:
mail: '\d+'
hash: '[a-zA-Z0-9]+'routeEnhancers:
PowermailOptIn:
type: Plugin
routePath: '/optin/{mail}/{hash}'
namespace: 'tx_powermail_pi1'
requirements:
mail: '\d+'
hash: '[a-zA-Z0-9]+'ReceiverMailReceiverPropertiesServiceSetReceiverEmailsEventReceiverMailReceiverPropertiesServiceSetReceiverEmailsEvent<?php
declare(strict_types=1);
namespace Vendor\MyExt\SpamShield;
use In2code\Powermail\Domain\Validator\SpamShield\AbstractMethod;
final class ApiCheckMethod extends AbstractMethod
{
public function spamCheck(): bool
{
$mail = $this->getMail();
// Return true if spam detected
return $this->callExternalApi($mail);
}
}plugin.tx_powermail.settings.setup.spamshield.methods {
100 {
class = Vendor\MyExt\SpamShield\ApiCheckMethod
_enable = 1
configuration {
apiUrl = https://spam-api.example.com
}
}
}<?php
declare(strict_types=1);
namespace Vendor\MyExt\SpamShield;
use In2code\Powermail\Domain\Validator\SpamShield\AbstractMethod;
final class ApiCheckMethod extends AbstractMethod
{
public function spamCheck(): bool
{
$mail = $this->getMail();
// 检测到垃圾邮件时返回true
return $this->callExternalApi($mail);
}
}plugin.tx_powermail.settings.setup.spamshield.methods {
100 {
class = Vendor\MyExt\SpamShield\ApiCheckMethod
_enable = 1
configuration {
apiUrl = https://spam-api.example.com
}
}
}plugin.tx_powermail.settings.setup.manipulateVariablesInPowermailAllMarker {
timestamp = TEXT
timestamp.data = date:U
timestamp.strftime = %Y-%m-%d %H:%M:%S
}plugin.tx_powermail.settings.setup.manipulateVariablesInPowermailAllMarker {
timestamp = TEXT
timestamp.data = date:U
timestamp.strftime = %Y-%m-%d %H:%M:%S
}Conditions tables: See SKILL-CONDITIONS.md Section 12 fortables.tx_powermailcond_*
条件相关表: 条件功能的表结构请查看SKILL-CONDITIONS.md第12节的表。tx_powermailcond_*
| Column | Type | Purpose |
|---|---|---|
| int AUTO_INCREMENT | Primary key |
| int | Storage page UID |
| int | Last modification timestamp |
| int | Creation timestamp |
| tinyint | Soft-delete flag |
| tinyint | Visibility flag |
| int | Language UID (0 = default, -1 = all) |
| int | UID of the default language record |
| mediumblob | Diff source for translation |
| int | Publish start (Unix timestamp) |
| int | Publish end (Unix timestamp) |
| 列名 | 类型 | 用途 |
|---|---|---|
| int AUTO_INCREMENT | 主键ID |
| int | 存储页面UID |
| int | 最后修改时间戳 |
| int | 创建时间戳 |
| tinyint | 软删除标记 |
| tinyint | 可见性标记 |
| int | 语言UID(0=默认,-1=所有语言) |
| int | 关联默认语言记录的UID |
| mediumblob | 翻译差异源数据 |
| int | 发布开始时间戳 |
| int | 发布结束时间戳 |
| Column | Type | Description |
|---|---|---|
| varchar(255) | Form title |
| tinyint | Backend note renderer (internal) |
| varchar(255) | CSS class for form wrapper |
| varchar(255) | IRRE children count or element browser list |
| varchar(3) | Autocomplete on/off/empty |
| tinyint | Test record flag |
language (l10n_parent, sys_language_uid)| 列名 | 类型 | 说明 |
|---|---|---|
| varchar(255) | 表单标题 |
| tinyint | 后端备注渲染标记(内部使用) |
| varchar(255) | 表单容器的CSS类 |
| varchar(255) | IRRE子元素数量或元素浏览器列表 |
| varchar(3) | 自动完成开关(on/off/empty) |
| tinyint | 测试记录标记 |
language (l10n_parent, sys_language_uid)| Column | Type | Description |
|---|---|---|
| int | Parent form UID |
| varchar(255) | Page/step title |
| varchar(255) | CSS class for fieldset |
| int | IRRE children count |
| int | Sort order within form |
parent_form (form)language (l10n_parent, sys_language_uid)| 列名 | 类型 | 说明 |
|---|---|---|
| int | 关联父表单UID |
| varchar(255) | 页面/步骤标题 |
| varchar(255) | 字段集的CSS类 |
| int | IRRE子元素数量 |
| int | 在表单中的排序顺序 |
parent_form (form)language (l10n_parent, sys_language_uid)| Column | Type | Description |
|---|---|---|
| int | Parent page UID |
| varchar(255) | Field label |
| varchar(255) | Field type key (input, select, check, ...) |
| text | Options for select/radio/check (one per line) |
| varchar(255) | File path reference |
| int | CE reference for type=content |
| text | Static text for type=text |
| text | Default/prefill value |
| text | Placeholder text |
| text | Placeholder for repeat field (password) |
| text | TypoScript for type=typoscript |
| int | Validation type (0=none, 1=email, ...) |
| varchar(255) | Regex or config for validation |
| varchar(255) | CSS class for field wrapper |
| varchar(255) | Help text / description |
| tinyint | Allow multi-select |
| varchar(255) | Datepicker format |
| varchar(255) | Prefill from fe_user property |
| tinyint | This field is the sender email |
| tinyint | This field is the sender name |
| tinyint | Required field |
| tinyint | Custom marker enabled |
| varchar(255) | Field marker (variable name) |
| varchar(255) | Custom mandatory error text |
| varchar(20) | Autocomplete attribute |
| varchar(100) | Autocomplete section |
| varchar(8) | Autocomplete type |
| varchar(8) | Autocomplete purpose |
| int | Sort order within page |
parent_page (page)language (l10n_parent, sys_language_uid)| 列名 | 类型 | 说明 |
|---|---|---|
| int | 关联父页面UID |
| varchar(255) | 字段标签 |
| varchar(255) | 字段类型标识(input、select等) |
| text | 下拉/单选/复选的选项配置(每行一个) |
| varchar(255) | 文件路径引用 |
| int | 关联内容元素UID(type=content时使用) |
| text | 静态文本内容(type=text时使用) |
| text | 默认/预填充值 |
| text | 占位符文本 |
| text | 重复字段的占位符(密码框) |
| text | TypoScript配置(type=typoscript时使用) |
| int | 验证类型(0=无,1=邮箱等) |
| varchar(255) | 正则或验证配置参数 |
| varchar(255) | 字段容器的CSS类 |
| varchar(255) | 帮助文本/描述 |
| tinyint | 是否允许多选 |
| varchar(255) | 日期选择器格式配置 |
| varchar(255) | 从fe_user属性预填充的字段 |
| tinyint | 是否作为发件人邮箱字段 |
| tinyint | 是否作为发件人名称字段 |
| tinyint | 是否为必填字段 |
| tinyint | 是否启用自定义标记 |
| varchar(255) | 字段标记(变量名) |
| varchar(255) | 自定义必填错误提示文本 |
| varchar(20) | 自动完成属性值 |
| varchar(100) | 自动完成分区 |
| varchar(8) | 自动完成类型 |
| varchar(8) | 自动完成用途 |
| int | 在页面中的排序顺序 |
parent_page (page)language (l10n_parent, sys_language_uid)| Column | Type | Description |
|---|---|---|
| varchar(255) | Submitter name |
| varchar(255) | Submitter email |
| varchar(255) | Mail subject |
| varchar(1024) | Receiver email(s) |
| text | Mail body (RTE) |
| int | Frontend user UID (if logged in) |
| tinytext | Submitter IP address |
| text | Browser user agent |
| int | Submission timestamp |
| int | Source form UID |
| int | IRRE children count |
| varchar(255) | Spam score |
| text | HTTP referer domain |
| text | Full HTTP referer |
| text | Visitor country |
| tinyint | Mobile device flag |
| int | Frontend language UID |
| text | Browser Accept-Language |
| text | Pages visited before submit |
form (form)feuser (feuser)| 列名 | 类型 | 说明 |
|---|---|---|
| varchar(255) | 提交人名称 |
| varchar(255) | 提交人邮箱 |
| varchar(255) | 邮件主题 |
| varchar(1024) | 收件人邮箱(支持多个) |
| text | 邮件正文(富文本) |
| int | 关联前端用户UID(若登录) |
| tinytext | 提交人IP地址 |
| text | 浏览器User-Agent信息 |
| int | 提交时间戳 |
| int | 关联源表单UID |
| int | IRRE子元素数量 |
| varchar(255) | 垃圾邮件得分 |
| text | HTTP来源域名 |
| text | 完整HTTP来源地址 |
| text | 访问者国家 |
| tinyint | 移动设备标记 |
| int | 前端语言UID |
| text | 浏览器Accept-Language信息 |
| text | 提交前访问的页面路径 |
form (form)feuser (feuser)| Column | Type | Description |
|---|---|---|
| int | Parent mail UID |
| text | Answer value (JSON for arrays) |
| int | 0=text, 1=array, 2=date, 3=upload, 4=password |
| int | Source field UID |
mail (mail)deleted (deleted)hidden (hidden)language (l10n_parent, sys_language_uid)| 列名 | 类型 | 说明 |
|---|---|---|
| int | 关联父邮件UID |
| text | 回复值(数组以JSON存储) |
| int | 0=文本, 1=数组, 2=日期, 3=上传, 4=密码 |
| int | 关联源字段UID |
mail (mail)deleted (deleted)hidden (hidden)language (l10n_parent, sys_language_uid)tx_powermail_domain_model_form
│ 1
├──── * tx_powermail_domain_model_page (IRRE via form)
│ │ 1
│ └──── * tx_powermail_domain_model_field (IRRE via page)
│ │
│ │ referenced by
│ ▼
│ tx_powermail_domain_model_answer.field
│
└──── * tx_powermail_domain_model_mail (via form)
│ 1
└──── * tx_powermail_domain_model_answer (IRRE via mail)tx_powermail_domain_model_form
│ 1
├──── * tx_powermail_domain_model_page(通过form关联IRRE)
│ │ 1
│ └──── * tx_powermail_domain_model_field(通过page关联IRRE)
│ │
│ │ 被引用
│ ▼
│ tx_powermail_domain_model_answer.field
│
└──── * tx_powermail_domain_model_mail(通过form关联)
│ 1
└──── * tx_powermail_domain_model_answer(通过mail关联IRRE)t3ver_wsidt3ver_oidt3ver_statet3ver_staget3ver_wsid > 0t3ver_*t3ver_wsid = <ws_id>t3ver_state = 1t3ver_stage = 1t3ver_wsid = 0Detailed SQL and DataHandler examples: See SKILL-EXAMPLES.md for complete workspace-aware queries, publishing workflows, and CLI options.
t3ver_wsidt3ver_oidt3ver_statet3ver_staget3ver_wsid > 0t3ver_*t3ver_wsid = <工作区ID>t3ver_state = 1t3ver_stage = 1t3ver_wsid = 0详细SQL和DataHandler示例: 完整的工作区兼容查询、发布流程及CLI选项,请查看SKILL-EXAMPLES.md。
| Level | What gets translated | Key columns |
|---|---|---|
| Form | Title | |
| Page | Title (step heading) | |
| Field | Title, settings, placeholder, mandatory_text, description | |
Automatically stored with | | |
| Answer | Stored with language of submission | |
| 层级 | 翻译内容 | 核心列名 |
|---|---|---|
| 表单 | 标题 | |
| 页面 | 标题(步骤头) | |
| 字段 | 标题、设置、占位符、必填提示、描述 | |
| 邮件 | 自动存储前端语言的 | |
| 回复 | 存储提交时的语言信息 | |
sys_language_uid = 0sys_language_uid = 1l10n_parentmarkertypesettingssys_language_uid = 0sys_language_uid = 1l10n_parentmarkertypesettings-- Form
INSERT INTO tx_powermail_domain_model_form (pid, title, sys_language_uid, l10n_parent)
VALUES (1, 'Contact Form', 0, 0);
-- Assume UID = 10
-- Page
INSERT INTO tx_powermail_domain_model_page (pid, form, title, sorting, sys_language_uid, l10n_parent)
VALUES (1, 10, 'Your Details', 1, 0, 0);
-- Assume UID = 20
-- Fields
INSERT INTO tx_powermail_domain_model_field
(pid, page, title, type, marker, mandatory, sender_name, sorting, sys_language_uid, l10n_parent)
VALUES
(1, 20, 'First Name', 'input', 'firstname', 1, 1, 1, 0, 0), -- UID 30
(1, 20, 'Last Name', 'input', 'lastname', 1, 0, 2, 0, 0), -- UID 31
(1, 20, 'Email', 'input', 'email', 1, 0, 3, 0, 0), -- UID 32
(1, 20, 'Message', 'textarea', 'message', 0, 0, 4, 0, 0), -- UID 33
(1, 20, 'Subject', 'select', 'subject', 1, 0, 5, 0, 0), -- UID 34
(1, 20, 'Send', 'submit', 'submit', 0, 0, 6, 0, 0); -- UID 35
-- Select options for subject (English)
UPDATE tx_powermail_domain_model_field
SET settings = 'General Inquiry\nSupport Request\nPartnership\nOther'
WHERE uid = 34;
-- Mark email field as sender_email
UPDATE tx_powermail_domain_model_field SET sender_email = 1 WHERE uid = 32;-- 表单
INSERT INTO tx_powermail_domain_model_form (pid, title, sys_language_uid, l10n_parent)
VALUES (1, 'Contact Form', 0, 0);
-- 假设生成的UID = 10
-- 页面
INSERT INTO tx_powermail_domain_model_page (pid, form, title, sorting, sys_language_uid, l10n_parent)
VALUES (1, 10, 'Your Details', 1, 0, 0);
-- 假设生成的UID = 20
-- 字段
INSERT INTO tx_powermail_domain_model_field
(pid, page, title, type, marker, mandatory, sender_name, sorting, sys_language_uid, l10n_parent)
VALUES
(1, 20, 'First Name', 'input', 'firstname', 1, 1, 1, 0, 0), -- UID 30
(1, 20, 'Last Name', 'input', 'lastname', 1, 0, 2, 0, 0), -- UID 31
(1, 20, 'Email', 'input', 'email', 1, 0, 3, 0, 0), -- UID 32
(1, 20, 'Message', 'textarea', 'message', 0, 0, 4, 0, 0), -- UID 33
(1, 20, 'Subject', 'select', 'subject', 1, 0, 5, 0, 0), -- UID 34
(1, 20, 'Send', 'submit', 'submit', 0, 0, 6, 0, 0); -- UID 35
-- 主题字段的英文下拉选项
UPDATE tx_powermail_domain_model_field
SET settings = 'General Inquiry\nSupport Request\nPartnership\nOther'
WHERE uid = 34;
-- 将邮箱字段标记为发件人邮箱
UPDATE tx_powermail_domain_model_field SET sender_email = 1 WHERE uid = 32;-- Form translation (l10n_parent = 10, the English form)
INSERT INTO tx_powermail_domain_model_form (pid, title, sys_language_uid, l10n_parent)
VALUES (1, 'Kontaktformular', 1, 10);
-- Page translation (l10n_parent = 20)
INSERT INTO tx_powermail_domain_model_page
(pid, form, title, sorting, sys_language_uid, l10n_parent)
VALUES (1, 10, 'Ihre Daten', 1, 1, 20);
-- Field translations (l10n_parent points to English field UID)
INSERT INTO tx_powermail_domain_model_field
(pid, page, title, type, marker, mandatory, sender_name, sorting, sys_language_uid, l10n_parent)
VALUES
(1, 20, 'Vorname', 'input', 'firstname', 1, 1, 1, 1, 30),
(1, 20, 'Nachname', 'input', 'lastname', 1, 0, 2, 1, 31),
(1, 20, 'E-Mail-Adresse', 'input', 'email', 1, 0, 3, 1, 32),
(1, 20, 'Nachricht', 'textarea', 'message', 0, 0, 4, 1, 33),
(1, 20, 'Betreff', 'select', 'subject', 1, 0, 5, 1, 34),
(1, 20, 'Absenden', 'submit', 'submit', 0, 0, 6, 1, 35);
-- German select options for subject
UPDATE tx_powermail_domain_model_field
SET settings = 'Allgemeine Anfrage\nSupportanfrage\nPartnerschaft\nSonstiges'
WHERE sys_language_uid = 1 AND l10n_parent = 34;
-- Mark email field as sender_email (must be set on translation too)
UPDATE tx_powermail_domain_model_field
SET sender_email = 1
WHERE sys_language_uid = 1 AND l10n_parent = 32;-- 表单翻译(l10n_parent = 10,即英文表单的UID)
INSERT INTO tx_powermail_domain_model_form (pid, title, sys_language_uid, l10n_parent)
VALUES (1, 'Kontaktformular', 1, 10);
-- 页面翻译(l10n_parent = 20)
INSERT INTO tx_powermail_domain_model_page
(pid, form, title, sorting, sys_language_uid, l10n_parent)
VALUES (1, 10, 'Ihre Daten', 1, 1, 20);
-- 字段翻译(l10n_parent指向对应英文字段的UID)
INSERT INTO tx_powermail_domain_model_field
(pid, page, title, type, marker, mandatory, sender_name, sorting, sys_language_uid, l10n_parent)
VALUES
(1, 20, 'Vorname', 'input', 'firstname', 1, 1, 1, 1, 30),
(1, 20, 'Nachname', 'input', 'lastname', 1, 0, 2, 1, 31),
(1, 20, 'E-Mail-Adresse', 'input', 'email', 1, 0, 3, 1, 32),
(1, 20, 'Nachricht', 'textarea', 'message', 0, 0, 4, 1, 33),
(1, 20, 'Betreff', 'select', 'subject', 1, 0, 5, 1, 34),
(1, 20, 'Absenden', 'submit', 'submit', 0, 0, 6, 1, 35);
-- 主题字段的德语下拉选项
UPDATE tx_powermail_domain_model_field
SET settings = 'Allgemeine Anfrage\nSupportanfrage\nPartnerschaft\nSonstiges'
WHERE sys_language_uid = 1 AND l10n_parent = 34;
-- 将翻译后的邮箱字段标记为发件人邮箱
UPDATE tx_powermail_domain_model_field
SET sender_email = 1
WHERE sys_language_uid = 1 AND l10n_parent = 32;<?php
declare(strict_types=1);
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start([], []);
// Localize form (UID 10) to German (sys_language_uid=1)
$cmdMap = [
'tx_powermail_domain_model_form' => [
10 => [
'localize' => 1, // target language UID
],
],
];
$dataHandler->start([], $cmdMap);
$dataHandler->process_cmdmap();
// DataHandler auto-creates translations of all IRRE children (pages + fields)
// Then update the translated titles:
$translatedFormUid = $dataHandler->copyMappingArray_merged['tx_powermail_domain_model_form'][10] ?? null;
if ($translatedFormUid) {
$data = [
'tx_powermail_domain_model_form' => [
$translatedFormUid => [
'title' => 'Kontaktformular',
],
],
];
$dataHandler->start($data, []);
$dataHandler->process_datamap();
}<?php
declare(strict_types=1);
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start([], []);
// 将UID为10的表单翻译为德语(sys_language_uid=1)
$cmdMap = [
'tx_powermail_domain_model_form' => [
10 => [
'localize' => 1, // 目标语言UID
],
],
];
$dataHandler->start([], $cmdMap);
$dataHandler->process_cmdmap();
// DataHandler会自动创建所有IRRE子元素(页面+字段)的翻译
// 然后更新翻译后的标题:
$translatedFormUid = $dataHandler->copyMappingArray_merged['tx_powermail_domain_model_form'][10] ?? null;
if ($translatedFormUid) {
$data = [
'tx_powermail_domain_model_form' => [
$translatedFormUid => [
'title' => 'Kontaktformular',
],
],
];
$dataHandler->start($data, []);
$dataHandler->process_datamap();
}emailemaillocalizesettingssys_language_uidemailemaillocalizesettingssys_language_uidFor a comprehensive multi-step mini-shop example with Austrian legal types (Gesellschaftsformen), conditional fields per legal type, GDPR compliance, and two implementation approaches (DDEV SQL + DataHandler CLI command), see SKILL-EXAMPLES.md.
如需了解包含奥地利法律类型(Gesellschaftsformen)、按法律类型显示条件字段GDPR合规的多步骤迷你店铺示例,以及两种实现方式(DDEV SQL + DataHandler CLI命令),请查看SKILL-EXAMPLES.md。