Im Formular-Editor des FormFrameworks von Typo3 soll man verschiedene Empfänger angeben können. Im Frontend erscheint eine Dropdown-Liste der Empfänger. Der User kann eine Zieladresse aus dem Dropdown auswählen und so bestimmen, wer die Mail erhält.
Anleitung im Backend FormEditor
- Neues Formular-Element in Formular einfügen
- Im Formular-Element eine Liste der möglichen Empfänger angeben. Pro Zeile ein Empfänger in dieser Schreibweise:
email@adresse.de;Empfängername
- Neuen Finisher für das Formular auswählen
1. Wähle einen Namen für Deine Extension
Wenn Du die beiden Felder hier ausfüllst, wird im Quelltext alles automatisch für Dich ersetzt:
2. Eine Extension anlegen
(oder eine vorhandene nutzen)
Wir ignorieren mal alle Normen und halten es simpel. Folgende Dateien und Struktur brauchst Du in Deiner Extension.
Ersetze nng
durch den Ordner-Namen Deiner Extension, in der Du arbeitest.
Ersetze das Wort RecipientSelector
im Dateinamen durch Deinen Bezeichner.
nng
│
├── Classes
│ ├── Finishers
│ │ └── RecipientSelectorFinisher.php
│ ├── ViewHelpers
│ │ └── RecipientSelectorViewHelper.php
├── Configuration
│ ├── TypoScript
│ │ └── setup.txt
│ └── Yaml
│ ├── BaseSetup.yaml
│ ├── FormEditorSetup.yaml
│ └── FormEngineSetup.yaml
├── Resources
│ ├── Private
│ │ └── FormFramework
│ │ └── Frontent
│ │ └── Partials
│ │ └── RecipientSelector.html
│ └── Public
│ ├── Icons
│ │ └── RecipientSelector-icon.svg
│ └── JavaScript
│ └── Backend
│ └── FormEditor
│ └── RecipientSelectorViewModel.js
├── ext_emconf.php
├── ext_localconf.php
└── ext_typoscript_setup.txt
3. Inhalte der Dateien
Folgender Code muss in die einzelnen Dateien:
Configuration/TypoScript/setup.txt
# Einstellungen für das Frontend
plugin.tx_form.settings.yamlConfigurations {
153450370601 = EXT:nng/Configuration/Yaml/FormEditorSetup.yaml
}
# Einstellungen für das Backend
module.tx_form.settings.yamlConfigurations {
153450370601 = EXT:nng/Configuration/Yaml/FormEditorSetup.yaml
153450370602 = EXT:nng/Configuration/Yaml/FormEngineSetup.yaml
}
Configuration/Yaml/FormEditorSetup.yaml
# – -------------------------------------------------------------------------
# Diese YAML wird im Backend UND Frontend geladen.
#
TYPO3:
CMS:
Form:
prototypes:
standard:
formElementsDefinition:
# – -------------------------------------------------------
# Registrierung zusätzlicher Fluid-Pfade
Form:
renderingOptions:
templateRootPaths:
125: 'EXT:nng/Resources/Private/FormFramework/Frontend/Templates/'
partialRootPaths:
125: 'EXT:nng/Resources/Private/FormFramework/Frontend/Partials/'
layoutRootPaths:
125: 'EXT:nng/Resources/Private/FormFramework/Frontend/Layouts/'
# – -------------------------------------------------------
# Registrierung des neuen Formular-Elementes
RecipientSelector:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
formEditor:
label: 'Empfänger-Auswahl'
group: custom
groupSorting: 1000
iconIdentifier: 'nng-selectrecipient'
predefinedDefaults:
properties:
nng-recipients: ''
editors:
300:
identifier: 'nng-recipients'
templateName: 'Inspector-TextareaEditor'
label: 'Empfänger-Liste'
propertyPath: 'properties.nng-recipients'
# – -------------------------------------------------------
# Registrierung des Finishers
finishersDefinition:
RecipientSelector:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.finishersEmailMixin'
implementationClassName: 'Nng\RecipientSelector\Finishers\RecipientSelectorFinisher'
FormEngine:
label: "C E-Mail an Empfänger aus Auswahl (nng)"
formEditor:
iconIdentifier: 'nng-selectrecipient'
label: 'D E-Mail an Empfänger aus Auswahl (nng)'
predefinedDefaults:
options:
pageUid: ''
additionalParameters: ''
Configuration/Yaml/FormEngineSetup.yaml
# – -------------------------------------------------------------------------
# Diese YAML wird NUR im Backend geladen.
#
TYPO3:
CMS:
Form:
prototypes:
standard:
# – -----------------------------------------------------
# JS für Formular-Element im Backend bereitstellen
formEditor:
dynamicRequireJsModules:
additionalViewModelModules:
125: 'TYPO3/CMS/Nnsite
/Backend/FormEditor/RecipientSelectorViewModel'
formEditorPartials:
FormElement-RecipientSelector: 'Stage/SimpleTemplate'
formElementsDefinition:
Form:
# – -----------------------------------------------------
# Backend: Definition des Finishers
formEditor:
editors:
# 900 ist fix. Hier werden die Finisher definiert.
900:
selectOptions:
125:
value: 'RecipientSelector'
label: 'E-Mail an Empfänger aus Auswahl (nnsite
)'
propertyCollections:
finishers:
125:
identifier: 'RecipientSelector'
editors:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
100:
label: "E-Mail an Empfänger aus Auswahl (nnsite
)"
120:
identifier: 'fieldSubject'
templateName: 'Inspector-TextEditor'
label: 'Betreff'
propertyPath: 'options.fieldSubject'
enableFormelementSelectionButton: true
propertyValidators:
10: 'NotEmpty'
500:
identifier: 'fieldFromEmail'
templateName: 'Inspector-TextEditor'
label: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderAddress.label'
propertyPath: 'options.fieldFromEmail'
enableFormelementSelectionButton: true
propertyValidatorsMode: 'OR'
propertyValidators:
10: 'NaiveEmail'
20: 'FormElementIdentifierWithinCurlyBracesExclusive'
fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderAddress.fieldExplanationText'
600:
identifier: 'fieldFromName'
templateName: 'Inspector-TextEditor'
label: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderName.label'
propertyPath: 'options.fieldFromName'
enableFormelementSelectionButton: true
propertyValidators:
10: 'FormElementIdentifierWithinCurlyBracesInclusive'
fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderName.fieldExplanationText'
Classes/ViewHelpers/RecipientSelectorViewHelper.html
Das eigentliche Fluid-Template für die Ausgabe im Frontend.
<?php
namespace Extname
\RecipientSelector\ViewHelpers
;
use \TYPO3\CMS\Extbase\Utility\DebuggerUtility;
use \TYPO3\CMS\Core\Utility\GeneralUtility;
class RecipientSelectorViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper {
/**
* @var boolean
*/
protected $escapeChildren = false;
/**
* @var boolean
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('str', 'string', 'Text', false);
}
/**
*
*
* @throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception
* @return string Rendered tag
*/
public function render() {
$str = $this->arguments['str'] ?: $this->renderChildren();
$rows = explode("\n", $str);
foreach ($rows as $k => $row) {
$parts = explode(";", $row);
$rows[$k] = ['key' => $k, 'email' => $parts[0], 'label'=>$parts[1]];
}
return $rows;
}
}
Classes/Finishers/RecipientSelectorFinisher.php
<?php
namespace Extname\RecipientSelector'\Finishers;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\PathUtility;
class RecipientSelectorFinisher extends \TYPO3\CMS\Form\Domain\Finishers\EmailFinisher {
/**
* Finisher
*
*/
protected function executeInternal()
{
$formRuntime = $this->finisherContext->getFormRuntime();
$formValues = $this->finisherContext->getFormValues();
$fromEmail = $this->parseOption('fieldFromEmail');
$fromName = $this->parseOption('fieldFromName');
$subjectEmail = $this->parseOption('fieldSubject');
$recipientElement = false;
// Finde das ERSTE Formular-Element, in dem die Empfänger definiert wurden
foreach ($formValues as $k => $val) {
$element = $formRuntime->getFormDefinition()->getElementByIdentifier($k);
if ($element->getType() == 'RecipientSelector') {
$recipientElement = [
'key' => $k,
'element' => $element,
'value' => $val,
];
}
}
if (!$recipientElement) {
die('Finisher ist im Formular angegeben, aber Formular-Element für Auswahl des Empfängers fehlt.');
}
$recipients = explode("\n", $recipientElement['element']->getProperties()['nnsite
-recipients'] ?? '');
foreach ($recipients as $n=>$recipient) {
$parts = explode(";", $recipient);
$recipients[$n] = [
'key' => $n,
'label' => $parts[1],
'email' => $parts[0],
];
}
if (!$recipients) {
die('Keine Zieladressen im Formular angegeben.');
}
$recipientName = $recipients[$recipientElement['value']]['label'] ?? '';
$recipientEmail = $recipients[$recipientElement['value']]['email'] ?? '';
if (!$recipients) {
die('Zieladresse nicht bekannt.');
}
$this->setOption('templateName', 'html');
$this->setOption('subject', $subjectEmail);
$this->setOption('recipientName', $recipientName);
$this->setOption('recipientAddress', $recipientEmail);
$this->setOption('senderName', $fromName);
$this->setOption('senderAddress', $fromEmail);
parent::executeInternal();
}
}
Resources/Private/FormFramework/Frontend/Partials/RecipientSelector.html
Das eigentliche Fluid-Template für die Ausgabe im Frontend.
<div>
<h2>{element.properties.elementDescription}</h2>
<f:form.textfield
property="{element.identifier}"
id="{element.uniqueIdentifier}"
class="{element.properties.elementClassAttribute} form-control form-field"
errorClass="{element.properties.elementErrorClassAttribute}"
additionalAttributes="{formvh:translateElementProperty(element:element, property: 'fluidAdditionalAttributes')}"
/>
</div>
Resources/Public/Icons/RecipientSelector-icon.svg
Ein simples, quadratisches SVG-Icon für die Darstellung im Backend.
Resources/Public/JavaScript/Backend/FormEditor/RecipientSelectorViewmodel.js
// Resources/Public/JavaScript/Backend/FormEditor/RecipientSelectorViewmodel.js
define([
'jquery',
'TYPO3/CMS/Form/Backend/FormEditor/Helper'
], function ($, Helper) {
'use strict';
return (function ($, Helper) {
/**
* @private
*
* @var object
*/
var _formEditorApp = null;
/**
* @private
*
* @return object
*/
function getFormEditorApp() {
return _formEditorApp;
};
/**
* @private
*
* @return object
*/
function getPublisherSubscriber() {
return getFormEditorApp().getPublisherSubscriber();
};
/**
* @private
*
* @return object
*/
function getUtility() {
return getFormEditorApp().getUtility();
};
/**
* @private
*
* @param object
* @return object
*/
function getHelper() {
return Helper;
};
/**
* @private
*
* @return object
*/
function getCurrentlySelectedFormElement() {
return getFormEditorApp().getCurrentlySelectedFormElement();
};
/**
* @private
*
* @param mixed test
* @param string message
* @param int messageCode
* @return void
*/
function assert(test, message, messageCode) {
return getFormEditorApp().assert(test, message, messageCode);
};
/**
* @private
*
* @return void
* @throws 1491643380
*/
function _helperSetup() {
assert('function' === $.type(Helper.bootstrap),
'The view model helper does not implement the method "bootstrap"',
1491643380
);
Helper.bootstrap(getFormEditorApp());
};
/**
* @private
*
* @return void
*/
function _subscribeEvents() {
/**
* @private
*
* @param string
* @param array
* args[0] = formElement
* args[1] = template
* @return void
*/
getPublisherSubscriber().subscribe('view/stage/abstract/render/template/perform', function (topic, args) {
if (args[0].get('type') === 'RecipientSelector') {
getFormEditorApp().getViewModel().getStage().renderSimpleTemplateWithValidators(args[0], args[1]);
}
});
};
/**
* @public
*
* @param object formEditorApp
* @return void
*/
function bootstrap(formEditorApp) {
_formEditorApp = formEditorApp;
_helperSetup();
_subscribeEvents();
};
/**
* Publish the public methods.
* Implements the "Revealing Module Pattern".
*/
return {
bootstrap: bootstrap
};
})($, Helper);
});
ext_emconf.php
Der übliche Code, um eine Extension zu registrieren – falls nicht schon vorhanden.
<?php
$EM_CONF[$_EXTKEY] = [
'title' => 'Meine Extension',
'description' => '',
'category' => 'plugin',
'author' => 'David Bascom',
'author_email' => 'deine@email.de',
'state' => 'stable',
'internal' => '',
'uploadfolder' => '1',
'createDirs' => '',
'clearCacheOnLoad' => 0,
'version' => '0.1',
'constraints' => [
'depends' => [
'typo3' => '8.7.0',
],
'conflicts' => [],
'suggests' => [],
],
];
ext_localconf.php
Registriert das Icon
<?php
if (TYPO3_MODE === 'BE') {
/** @var \TYPO3\CMS\Core\Imaging\IconRegistry $iconRegistry */
$iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class);
$iconRegistry->registerIcon(
'RecipientSelector-icon',
\TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class,
['source' => 'EXT:nng/Resources/Public/Icons/RecipientSelector-icon.svg']
);
}
ext_typoscript_setup.txt
Lädt das TypoScript, ohne das im Root-Template die „Enthält Erweiterungen“ gewählt werden müssen.
<INCLUDE_TYPOSCRIPT: source="FILE:EXT:nng/Configuration/Typoscript/setup.txt">
Hi. Vielen Dank für die ausführliche Darstellung.
In folgender Datei habe ich ein Problem: Configuration/Yaml/FormEngineSetup.yaml
Die RecipientselectorViewModel.js kann nicht geladen werden.
Das dürfte mit folgender Zeile zusammenhängen:
additionalViewModelModules:
1632292624872: ‚TYPO3/CMS/Nnsite/Backend/FormEditor/RecipientselectorViewModel‘
Wie ist der Pfad anzupassen, bzw. was genau passiert da eigentlich?
Bin dankbar für jeden Schubs in die richtige Richtung!
Oben beschriebenes Problem habe ich lösen/umgehen können in dem ich den individuellen Namen meines Site-Packages anstelle von ‚Nnsite‘ eingesetzt habe und die RecipientselectorViewModel.js an entsprechenden Ort in meinem Package gelegt habe.
Das Backend funktioniert damit, im Frontend werden aber nicht gefundene Klassen angemeckert (‚Site-Packege-Name’\Recipientselector\Finishers\RecipientselectorFinisher). Vermutlich eine Namespace-Geschichte, vielleicht zu lösen wenn ich obiges komplett in ‚mein‘ Site-Package integriere.
Ist dann aber wenig flexibel, daher: Oder vereinfacht gefragt: Gibt es die beschriebene Funktionalität als fertige Extension die ich in ein 11er TYPO3 laden kann und dann meine Formulare entsprechend erweitert? Gerne spende ich dafür auch einen Betrag an die Kaffekasse.