From b9f6760a2076092ffad1c69dc3c752f2e95a0df9 Mon Sep 17 00:00:00 2001 From: Torsten Oppermann <torsten@sgalinski.de> Date: Tue, 27 Mar 2018 20:37:13 +0200 Subject: [PATCH] [TASK] WIP - Finishing forms integration --- Classes/Finisher/Forms/FormsFinisher.php | 53 ++---- Configuration/MailTemplates/Template1.php | 39 +++++ Configuration/MailTemplates/Template2.php | 39 +++++ Configuration/TypoScript/setup.ts | 3 +- Configuration/Yaml/Forms/BaseSetup.yaml | 25 +++ Configuration/Yaml/Forms/FormEditorSetup.yaml | 163 ++++++++++++++++-- Configuration/Yaml/Forms/FormEngineSetup.yaml | 37 ++-- .../Private/Forms/Partials/Field/Field.html | 22 +++ .../Forms/Partials/Field/Required.html | 3 + Resources/Private/Forms/Partials/Page.html | 10 ++ Resources/Private/Forms/Partials/Text.html | 13 ++ Resources/Private/Forms/contactForm.yaml | 78 +++++++++ .../Templates/SgMail/Template1/template.html | 3 + .../Templates/SgMail/Template2/template.html | 5 + 14 files changed, 423 insertions(+), 70 deletions(-) create mode 100644 Configuration/MailTemplates/Template1.php create mode 100644 Configuration/MailTemplates/Template2.php create mode 100644 Configuration/Yaml/Forms/BaseSetup.yaml create mode 100644 Resources/Private/Forms/Partials/Field/Field.html create mode 100644 Resources/Private/Forms/Partials/Field/Required.html create mode 100644 Resources/Private/Forms/Partials/Page.html create mode 100644 Resources/Private/Forms/Partials/Text.html create mode 100644 Resources/Private/Forms/contactForm.yaml create mode 100644 Resources/Private/Templates/SgMail/Template1/template.html create mode 100644 Resources/Private/Templates/SgMail/Template2/template.html diff --git a/Classes/Finisher/Forms/FormsFinisher.php b/Classes/Finisher/Forms/FormsFinisher.php index ab569563..9ec86972 100644 --- a/Classes/Finisher/Forms/FormsFinisher.php +++ b/Classes/Finisher/Forms/FormsFinisher.php @@ -17,17 +17,9 @@ namespace SGalinski\SgMail\Finisher\Forms; */ use SGalinski\SgMail\Service\MailTemplateService; -use TYPO3\CMS\Core\Mail\MailMessage; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Domain\Model\FileReference; use TYPO3\CMS\Extbase\Object\ObjectManager; -use TYPO3\CMS\Fluid\View\StandaloneView; use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher; -use TYPO3\CMS\Form\Domain\Finishers\Exception\FinisherException; -use TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload; -use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; -use TYPO3\CMS\Form\Service\TranslationService; -use TYPO3\CMS\Form\ViewHelpers\RenderRenderableViewHelper; /*************************************************************** * Copyright notice @@ -80,18 +72,6 @@ use TYPO3\CMS\Form\ViewHelpers\RenderRenderableViewHelper; * Scope: frontend */ class FormsFinisher extends AbstractFinisher { - const FORMAT_PLAINTEXT = 'plaintext'; - const FORMAT_HTML = 'html'; - - /** - * @var array - */ - protected $defaultOptions = [ - 'recipientName' => '', - 'senderName' => '', - 'format' => self::FORMAT_HTML, - 'attachUploads' => TRUE - ]; /** * Executes this finisher @@ -99,31 +79,26 @@ class FormsFinisher extends AbstractFinisher { * @see AbstractFinisher::execute() * * @throws \InvalidArgumentException - * @throws FinisherException + * @throws \BadFunctionCallException + * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException + * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException */ protected function executeInternal() { - $formValues = $this->finisherContext->getFormValues(); - $formRuntime = $this->finisherContext->getFormRuntime(); - debug($formRuntime); - debug($formValues); - $objectManager = GeneralUtility::makeInstance(ObjectManager::class); /** @var \SGalinski\SgMail\Service\MailTemplateService $mailTemplateService */ - $mailTemplateService = $objectManager->get(MailTemplateService::class); + $mailTemplateService = $objectManager->get( + MailTemplateService::class, $this->parseOption('template'), 'sg_mail', $formValues + ); - // parse options - $extensionKey = $this->parseOption('extensionKey'); - $templateName = $this->parseOption('templateName'); - $recipientAddress = $this->parseOption('recipientAddress'); - $senderAddress = $this->parseOption('senderAddress'); - $senderName = $this->parseOption('senderName'); + $mailTemplateService->setIgnoreMailQueue(TRUE); + $mailTemplateService->setToAddresses($this->parseOption('mailTo')); + $mailTemplateService->setFromAddress($this->parseOption('mailFrom')); + $mailTemplateService->setFromName($this->parseOption('userName')); + $mailTemplateService->setReplyToAddress($this->parseOption('replyTo')); + $mailTemplateService->setCcAddresses($this->parseOption('cc')); + $mailTemplateService->setBccAddresses($this->parseOption('bcc')); - $mailTemplateService->setExtensionKey($extensionKey); - $mailTemplateService->setTemplateName($templateName); - $mailTemplateService->setToAddresses($recipientAddress); - $mailTemplateService->setFromAddress($senderAddress); - $mailTemplateService->setFromName($senderName); - $mailTemplateService->setLanguage($GLOBALS['TSFE']->config['config']['language']); + $mailTemplateService->sendEmail(); } } diff --git a/Configuration/MailTemplates/Template1.php b/Configuration/MailTemplates/Template1.php new file mode 100644 index 00000000..bb92923e --- /dev/null +++ b/Configuration/MailTemplates/Template1.php @@ -0,0 +1,39 @@ +<?php +/*************************************************************** + * Copyright notice + * + * (c] sgalinski Internet Services (https://www.sgalinski.de] + * + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option] any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +return [ + 'extension_key' => 'sg_mail', + 'template_key' => 'template1', + 'description' => 'LLL:EXT:project_theme/Resources/Private/Language/locallang.xlf:mail.contact_user.description', + 'subject' => 'LLL:EXT:project_theme/Resources/Private/Language/locallang.xlf:mail.contact_user.description', + 'markers' => [ + [ + 'marker' => 'text-1', + 'type' => \SGalinski\SgMail\Service\MailTemplateService::MARKER_TYPE_STRING, + 'value' => 'Max Mustermann', + 'description' => 'LLL:EXT:project_theme/Resources/Private/Language/locallang.xlf:mail.marker.name', + ] + ] +]; diff --git a/Configuration/MailTemplates/Template2.php b/Configuration/MailTemplates/Template2.php new file mode 100644 index 00000000..a7ec2e8f --- /dev/null +++ b/Configuration/MailTemplates/Template2.php @@ -0,0 +1,39 @@ +<?php +/*************************************************************** + * Copyright notice + * + * (c] sgalinski Internet Services (https://www.sgalinski.de] + * + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option] any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +return [ + 'extension_key' => 'sg_mail', + 'template_key' => 'template2', + 'description' => 'LLL:EXT:project_theme/Resources/Private/Language/locallang.xlf:mail.contact_admin.description', + 'subject' => 'LLL:EXT:project_theme/Resources/Private/Language/locallang.xlf:mail.contact_admin.description', + 'markers' => [ + [ + 'marker' => 'text-1', + 'type' => \SGalinski\SgMail\Service\MailTemplateService::MARKER_TYPE_STRING, + 'value' => 'Max Mustermann', + 'description' => 'LLL:EXT:project_theme/Resources/Private/Language/locallang.xlf:mail.marker.name', + ] + ] +]; diff --git a/Configuration/TypoScript/setup.ts b/Configuration/TypoScript/setup.ts index 227a3c8a..8711db48 100644 --- a/Configuration/TypoScript/setup.ts +++ b/Configuration/TypoScript/setup.ts @@ -24,12 +24,13 @@ module.tx_sgmail { # frontend configuration for ext:forms plugin.tx_form.settings.yamlConfigurations { + 1499086547 = EXT:sg_mail/Configuration/Yaml/Forms/BaseSetup.yaml 1499086867 = EXT:sg_mail/Configuration/Yaml/Forms/FormEngineSetup.yaml } # Backend configuration for ext:forms -debug = 1 module.tx_form.settings.yamlConfigurations { + 1499086547 = EXT:sg_mail/Configuration/Yaml/Forms/BaseSetup.yaml 1499086867 = EXT:sg_mail/Configuration/Yaml/Forms/FormEditorSetup.yaml 1499088215 = EXT:sg_mail/Configuration/Yaml/Forms/FormEngineSetup.yaml } \ No newline at end of file diff --git a/Configuration/Yaml/Forms/BaseSetup.yaml b/Configuration/Yaml/Forms/BaseSetup.yaml new file mode 100644 index 00000000..97fc0839 --- /dev/null +++ b/Configuration/Yaml/Forms/BaseSetup.yaml @@ -0,0 +1,25 @@ +TYPO3: + CMS: + Form: + persistenceManager: + allowedExtensionPaths: + 10: EXT:sg_mail/Resources/Private/Forms/ + allowSaveToExtensionPaths: true + allowDeleteFromExtensionPaths: true + + prototypes: + standard: + finishersDefinition: + MailToSenderFinisher: + implementationClassName: SGalinski\SgMail\Finisher\Forms\FormsFinisher + MailToReceiverFinisher: + implementationClassName: SGalinski\SgMail\Finisher\Forms\FormsFinisher + formElementsDefinition: + Form: + renderingOptions: + templateRootPaths: + 100: 'EXT:sg_mail/Resources/Private/Forms/Templates/' + partialRootPaths: + 100: 'EXT:sg_mail/Resources/Private/Forms/Partials/' + layoutRootPaths: + 100: 'EXT:sg_mail/Resources/Private/Forms/Layouts/' \ No newline at end of file diff --git a/Configuration/Yaml/Forms/FormEditorSetup.yaml b/Configuration/Yaml/Forms/FormEditorSetup.yaml index a407bd0e..4cb773ea 100644 --- a/Configuration/Yaml/Forms/FormEditorSetup.yaml +++ b/Configuration/Yaml/Forms/FormEditorSetup.yaml @@ -10,43 +10,170 @@ TYPO3: 900: # Extend the finisher dropdown in the menu selectOptions: + 25: + value: 'MailToSenderFinisher' + label: 'SgMail - Mail to Website user' 35: - value: 'SgMailFinisher' - label: 'SgMail Finisher' + value: 'MailToReceiverFinisher' + label: 'SgMail - E-Mail to Admin' propertyCollections: finishers: # define the finishers field 25: - identifier: 'SgMailFinisher' + identifier: 'MailToSenderFinisher' editors: __inheritances: 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' 100: - label: "SgMail - E-Mail to sender(form submitter)" + label: "SgMail - E-Mail to website user" 110: - identifier: 'extensionKey' + identifier: 'template' + templateName: 'Inspector-SingleSelectEditor' + label: 'Unique Template name' + propertyPath: 'options.template' + selectOptions: + 0: + value: 'template1' + label: 'Template 1' + 1: + value: 'template2' + label: 'Template 2' + 2: + value: 'template3' + label: 'Template 3' + 3: + value: 'template4' + label: 'Template 4' + 4: + value: 'template5' + label: 'Template 5' + 120: + identifier: 'mailTo' templateName: 'Inspector-TextEditor' - label: 'Extension key' - propertyPath: 'options.extensionKey' + label: 'The email address of the website user' + propertyPath: 'options.mailTo' + enableFormelementSelectionButton: true propertyValidators: 10: 'NotEmpty' - 140: - identifier: 'templateName' + 20: 'FormElementIdentifierWithinCurlyBracesInclusive' + 125: + identifier: 'mailFrom' templateName: 'Inspector-TextEditor' - label: 'Template name' - propertyPath: 'options.templateName' -# enableFormelementSelectionButton: true + label: 'The email address of the website' + propertyPath: 'options.mailFrom' + 160: + identifier: 'subject' + templateName: 'Inspector-TextEditor' + label: 'The Subject of the E-Mail' + propertyPath: 'options.subject' + 170: + identifier: 'userName' + templateName: 'Inspector-TextEditor' + label: 'The Name of the Website user' + propertyPath: 'options.userName' + enableFormelementSelectionButton: true propertyValidators: 10: 'NotEmpty' -# 20: 'FormElementIdentifierWithinCurlyBracesInclusive' - # Hier ist definiert welche optionen das javascript im backend beim hinzufügen lädt. - # Am besten sollten es die selben sein wie in finisher. + 20: 'FormElementIdentifierWithinCurlyBracesInclusive' + 190: + identifier: 'replyTo' + templateName: 'Inspector-TextEditor' + label: 'The reply to Address of the E-Mail' + propertyPath: 'options.replyTo' + 200: + identifier: 'cc' + templateName: 'Inspector-TextEditor' + label: 'The cc Address of the E-Mail' + propertyPath: 'options.cc' + 210: + identifier: 'bcc' + templateName: 'Inspector-TextEditor' + label: 'The bcc Address of the E-Mail' + propertyPath: 'options.bcc' + 35: + identifier: 'MailToReceiverFinisher' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: "SgMail - E-Mail to Website Admin" + 110: + identifier: 'template' + templateName: 'Inspector-SingleSelectEditor' + label: 'Unique Template name' + propertyPath: 'options.template' + selectOptions: + 0: + value: 'template1' + label: 'Template 1' + 1: + value: 'template2' + label: 'Template 2' + 2: + value: 'template3' + label: 'Template 3' + 3: + value: 'template4' + label: 'Template 4' + 4: + value: 'template5' + label: 'Template 5' + 120: + identifier: 'mailTo' + templateName: 'Inspector-TextEditor' + label: 'The email address of the admin' + propertyPath: 'options.mailTo' + 125: + identifier: 'mailFrom' + templateName: 'Inspector-TextEditor' + label: 'The email address of the website' + propertyPath: 'options.mailFrom' + 160: + identifier: 'subject' + templateName: 'Inspector-TextEditor' + label: 'The Subject of the E-Mail' + propertyPath: 'options.subject' + 190: + identifier: 'replyTo' + templateName: 'Inspector-TextEditor' + label: 'The reply to Address of the E-Mail' + propertyPath: 'options.replyTo' + 200: + identifier: 'cc' + templateName: 'Inspector-TextEditor' + label: 'The cc Address of the E-Mail' + propertyPath: 'options.cc' + 210: + identifier: 'bcc' + templateName: 'Inspector-TextEditor' + label: 'The bcc Address of the E-Mail' + propertyPath: 'options.bcc' + # Backend options finishersDefinition: - SgMailFinisher: + MailToSenderFinisher: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'A Label that seems to be never used...' + predefinedDefaults: + options: + template: 'template1' + mailTo: '' + subject: '' + userName: '' + replyTo: '' + cc: '' + bcc: '' + MailToReceiverFinisher: formEditor: iconIdentifier: 't3-form-icon-finisher' label: 'A Label that seems to be never used...' predefinedDefaults: options: - apiKey: '' - email: '' \ No newline at end of file + template: 'template2' + mailTo: '' + mailFrom: '' + subject: '' + userName: '' + replyTo: '' + cc: '' + bcc: '' \ No newline at end of file diff --git a/Configuration/Yaml/Forms/FormEngineSetup.yaml b/Configuration/Yaml/Forms/FormEngineSetup.yaml index 171f9c7c..8bd1be55 100644 --- a/Configuration/Yaml/Forms/FormEngineSetup.yaml +++ b/Configuration/Yaml/Forms/FormEngineSetup.yaml @@ -1,12 +1,25 @@ -TYPO3: - CMS: - Form: - prototypes: - standard: - finishersDefinition: - FormsFinisher: - FormEngine: - label: "When is this label used? And for what?" - elements: - extensionKey: {label: extensionKey, config: {type: input}} - templateName: {label: templateName , config: {type: input}} \ No newline at end of file +#TYPO3: +# CMS: +# Form: +# prototypes: +# standard: +# finishersDefinition: +# MailToSenderFinisher: +# FormEngine: +# label: '' +# elements: +# format: +# label: 'tt_content.finishersDefinition.EmailToSender.format.label' +# config: +# type: select +# renderType: 'selectSingle' +# minitems: 1 +# maxitems: 1 +# size: 1 +# items: +# 10: +# 0: 'tt_content.finishersDefinition.EmailToSender.format.1' +# 1: 'html' +# 20: +# 0: 'tt_content.finishersDefinition.EmailToSender.format.2' +# 1: 'plaintext' \ No newline at end of file diff --git a/Resources/Private/Forms/Partials/Field/Field.html b/Resources/Private/Forms/Partials/Field/Field.html new file mode 100644 index 00000000..ad65ddf9 --- /dev/null +++ b/Resources/Private/Forms/Partials/Field/Field.html @@ -0,0 +1,22 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:formvh="http://typo3.org/ns/TYPO3/CMS/Form/ViewHelpers" data-namespace-typo3-fluid="true"> +<f:form.validationResults for="{element.rootForm.identifier}.{element.identifier}"> + <div class="form-group{f:if(condition: '{validationResults.errors.0}', then: ' has-error')}"> + <div class="{element.properties.containerClassAttribute}"> + <f:format.raw> + {elementContent} + <f:if condition="{validationResults.flattenedErrors}"> + <span class="error help-block" role="alert"> + <f:for each="{validationResults.errors}" as="error"> + {formvh:translateElementError(element: element, code: error.code, arguments: error.arguments, defaultValue: error.message)} + <br /> + </f:for> + </span> + </f:if> + </f:format.raw> + <f:if condition="{element.properties.elementDescription}"> + <span class="help-block">{formvh:translateElementProperty(element: element, property: 'elementDescription')}</span> + </f:if> + </div> + </div> +</f:form.validationResults> +</html> diff --git a/Resources/Private/Forms/Partials/Field/Required.html b/Resources/Private/Forms/Partials/Field/Required.html new file mode 100644 index 00000000..96eb49cd --- /dev/null +++ b/Resources/Private/Forms/Partials/Field/Required.html @@ -0,0 +1,3 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:formvh="http://typo3.org/ns/TYPO3/CMS/Form/ViewHelpers" data-namespace-typo3-fluid="true"> +<span class="required">*</span> +</html> diff --git a/Resources/Private/Forms/Partials/Page.html b/Resources/Private/Forms/Partials/Page.html new file mode 100644 index 00000000..190d01ca --- /dev/null +++ b/Resources/Private/Forms/Partials/Page.html @@ -0,0 +1,10 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:formvh="http://typo3.org/ns/TYPO3/CMS/Form/ViewHelpers" data-namespace-typo3-fluid="true"> +<formvh:renderRenderable renderable="{page}"> + <f:if condition="{page.label}"> + <h2>{formvh:translateElementProperty(element: page, property: 'label')}</h2> + </f:if> + <f:for each="{page.elements}" as="element"> + <f:render partial="{element.templateName}" arguments="{element: element}" /> + </f:for> +</formvh:renderRenderable> +</html> diff --git a/Resources/Private/Forms/Partials/Text.html b/Resources/Private/Forms/Partials/Text.html new file mode 100644 index 00000000..b997ec6b --- /dev/null +++ b/Resources/Private/Forms/Partials/Text.html @@ -0,0 +1,13 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:formvh="http://typo3.org/ns/TYPO3/CMS/Form/ViewHelpers" data-namespace-typo3-fluid="true"> +<formvh:renderRenderable renderable="{element}"> + <f:render partial="Field/Field" arguments="{element: element}" contentAs="elementContent"> + <f:form.textfield + property="{element.identifier}" + id="{element.uniqueIdentifier}" + class="{element.properties.elementClassAttribute} form-control" + errorClass="{element.properties.elementErrorClassAttribute}" + additionalAttributes="{formvh:translateElementProperty(element: element, property: 'fluidAdditionalAttributes')}" + /> + </f:render> +</formvh:renderRenderable> +</html> diff --git a/Resources/Private/Forms/contactForm.yaml b/Resources/Private/Forms/contactForm.yaml new file mode 100644 index 00000000..d401f7dd --- /dev/null +++ b/Resources/Private/Forms/contactForm.yaml @@ -0,0 +1,78 @@ + +renderingOptions: + submitButtonLabel: Submit +type: Form +identifier: contactForm +label: 'Contact Form' +prototypeName: standard +finishers: + - + options: + format: html + template: template1 + mailTo: '{text-2}' + subject: '' + userName: '{text-1}' + replyTo: '' + cc: '' + bcc: '' + identifier: MailToSenderFinisher + - + options: + template: template2 + mailTo: admin@sgalinski.de + mailFrom: info@sgalinski.de + subject: subject2 + userName: '' + replyTo: '' + cc: '' + bcc: '' + identifier: MailToReceiverFinisher +renderables: + - + renderingOptions: + previousButtonLabel: 'Previous step' + nextButtonLabel: 'Next step' + type: Page + identifier: page-1 + label: Step + renderables: + - + defaultValue: '' + type: Text + identifier: text-1 + label: Name + properties: + fluidAdditionalAttributes: + placeholder: Name + required: required + elementDescription: '' + validators: + - + identifier: NotEmpty + - + defaultValue: '' + type: Text + identifier: text-2 + label: 'Ihre E-Mail-Adresse' + properties: + fluidAdditionalAttributes: + placeholder: 'Ihre E-Mail-Adresse' + required: required + validators: + - + identifier: NotEmpty + - + identifier: EmailAddress + - + defaultValue: '' + type: Text + identifier: text-3 + label: Nachricht + properties: + fluidAdditionalAttributes: + placeholder: Nachricht + required: required + validators: + - + identifier: NotEmpty diff --git a/Resources/Private/Templates/SgMail/Template1/template.html b/Resources/Private/Templates/SgMail/Template1/template.html new file mode 100644 index 00000000..10cf718c --- /dev/null +++ b/Resources/Private/Templates/SgMail/Template1/template.html @@ -0,0 +1,3 @@ +{text-1} + +Template Contact User: Template1 \ No newline at end of file diff --git a/Resources/Private/Templates/SgMail/Template2/template.html b/Resources/Private/Templates/SgMail/Template2/template.html new file mode 100644 index 00000000..5ae76b21 --- /dev/null +++ b/Resources/Private/Templates/SgMail/Template2/template.html @@ -0,0 +1,5 @@ +{text-1} +{text-2} + + +Template 2 -- GitLab