typeNum in Link funktioniert nicht. Typo3 type removed from URL (Typo3 9, Typo3 10)

Problem nach dem Update auf Typo3 9 oder 10:

Beim Erstellen eines Links über den ViewHelper f:link.page, f:uri.page, f:link.typolink, f:uri.typolink etc. wird der typeNum-Parameter ignoriert.
Auch beim Versuch, type manuell über additionalParams zu übergeben, erscheint kein &type=123456 in der generierten URL:

<f:link.page pageType="123456" additionalParams="{s:1,p:2,type:123456}" pageUid="3"<

wird zu:
https://www.domain.de/?s=1&p=2

Der type=123456-Parameter wird einfach entfernt.

Lösung:
Die site-yaml Konfiguration prüfen und darauf achten, dass ein routeEnhancer für den Page-Type definiert wurde!


routeEnhancers:
  PageTypeSuffix:
    type: PageType
    default: '/'
    index: ''
    map:
      beispiel: 123456

Hier – als kleiner Spickzettel – noch mal die TypoScript-Setup zum simplen Aufruf einer Methode über typeNum.

Mit entsprechender Einstellung im TypoScript und der Site-Konfiguration (yaml) wird die angegebene Methode beim Aufruf der URL https://www.beispiel.de/?type=123456 ausgeführt.


beispiel = PAGE
beispiel {
  typeNum = 123456
  config {
    disableAllHeaderCode = 1
    xhtml_cleaning = 0
    admPanel = 0
    additionalHeaders = Content-type: text/plain
    no_cache = 1
    contentObjectExceptionHandler = 0
  }
  10 = USER
  10.userFunc = Pfad\Zu\Deinem\Script->methodenName
}

Für Suchmaschinen: Typo3 Typolink ignores type-Parameter, typeNum not working Typo3, typeNum funktioniert nicht, type wird aus URL entfernt beim ViewHelper. Typoscript mit page-type wird nicht ausgeführt. Problem typeNum in Link-URL.

Typo3 tx_form: Empfänger im Frontend auswählbar machen. Eigenes Formular-Element und eigener Finisher

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">

Eigenes Custom Form Element für tx_form-Extension – Typo3 v8

How-To für das Erstellen eines Custom Form Elements für die tx_form-Extension unter Typo3 v8.

Falls man ein Custom Form Element erstellen möchte, haltet euch an das How-To. Für detaillierte Informationen und die Dokumentation zur ext:form besucht: https://docs.typo3.org/typo3cms/extensions/form/Index.html

Außerdem haben wir eine „Anleitung für Dummies und Normalsterbliche, die einfach einen Job zu erledigen haben“ erstellt – eine Kurzfassung mit farblicher Kennzeichnung und einem simplen Online-Generator.

Weiterlesen

Datei-Upload in eigenem Typo3 Backend Module

Zum Upload einer Datei im Backend oder Frontend von Typo3 8+ hilft die ExtendedFileUtility mit ein paar schönen Methoden.

Für Suchmaschinen: Upload einer Datei in eigenem Backend-Module von Typo3. Upload file in typo3 backend module. File-Upload Typo3 backend, move_uploaded_file() in Mod-Controller. Typo3 ExtendedFileUtility upload example. TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::init(). Upload files in TYPO3 from Frontend. Datei vom Frontend hochladen und im Controller an Ort bewegen. Typo3 upload file data. f:form.upload nutzen zum Upload einer Datei aus dem Backend oder Frontend.

Weiterlesen

Extbase/Fluid ViewHelper für TCA Tabellen-Wizard (tablewizard) in eigener Extension nutzen

Problem: Man möchte in einer eigenen Extension den Tabellen-Wizard für ein TCA-Feld nutzen und diese dann per css_styled_content in einem Fluid-Template ausgeben lassen. Leider konnte ich keinen ViewHelper dafür finden und auch die ersten Verdächtigen z.B. über so etwas wie f:format.html parseFuncTSPath =“tt_content.table.20″ scheiterten.

Für Suchmaschinen: tca tablewizard viewhelper für tabellen. Eigener ViewHelper für tablewizard Daten Typo3. Parse tablewizard data in fluid template. tablewizard String in Tabelle konvertieren in eigener Extension. Tabellendaten in Typo3 Fluid ViewHelper umwandeln. Konvertieren von Daten aus dem Tabellen-Assistenten für Typo3. tablewizard in eigener Extension nutzen und in Fluid per css_styled_content parsen lassen.

Table-Wizard im TCA nutzen
Dazu nutzt man im TCA das Script wizard_table.php:

'tabellen_daten_feld' => array(
   'label' => 'Lebenslauf',
   'l10n_mode' => 'mergeIfNotBlank',
   'config' => array (
      'type' => 'text',
      'cols' => '30',   
      'rows' => '5',
      'default' => '',
      'wizards' => array(
         '_PADDING' => 2,
         'list' => Array(
            'notNewRecords' => 1, 
            'type' => 'script',
            'title' => 'Table wizard',
            'icon' => 'wizard_table.gif',
            'script' => 'wizard_table.php',
            // So kann die Option "kleine Felder" automatisch deaktiviert werden
            // 'script' => 'wizard_table.php?TABLE[textFields]=0',
            'params' => array(
               'xmlOutput' => 0
            )
         ),
      ),
   ),
),

ViewHelper für das Rendern der Tabelle
Der ViewHelper nutzt css_styled_content um die per Zeilenumbruch und mit dem Pipe-Symbol (|) getrennte Spalten und Zeilen in eine Tabelle umzuwandeln.

registerArgument('table', 'string', 'Die Tabellen-Daten, Delimiter ist das Pipe-Symbol', false, null);
      $this->registerArgument('layout', 'string', 'Die Zahl, die an der Klasse contenttable-XX erscheint', false, null);
   }

    /**
     * Rendert die Tabelle mit csc_styled_content
     */

    public function render() {

      if (!$this->arguments['table']) {
         $this->arguments['table'] = html_entity_decode($this->renderChildren());
      }
      
      $this->cscController->cObj = $this->configurationManager->getContentObject();
      $this->cscController->cObj->data = $this->arguments;

      return $this->cscController->render_table( null, array('field'=>'table') );
      
      
    }
}
?>

Der ViewHelper kann so eingesetzt werden:
Dabei kann unter „layout“ eine Zahl angegeben werden, die bei an der Tabelle als z.B. class=“contenttable-2″ erscheint.

{namespace VH=Pfad\ZuDeinen\ViewHelpers}

{tabellen_daten_feld}



{VH:table(layout:2,table:tabellen_daten_feld)}

Multiple YouTube Videos auf Seite in iFrame andere anhalten / stoppen

Problem: Man hat auf einer Seite mehrere YouTube Videos als iFrame eingebunden. Die Videos lassen sich aber parallel starten und dadurch laufen alle Videos gleichzeitig. Dieses Script stoppt alle anderen Videos auf der Seite automatisch, wenn eines der Videos gestartet wird:

Für Suchmaschinen: youtube alle videos stoppen wenn ein video startet, stop all iframes youtube videos when one video starts. Andere Videos anhalten, wenn ein Video läuft. Automatisch alle YouTube Filme anhalten wenn neues Video play gedrückt wird. Immer nur ein Video abspielen in YouTube iFrame auch wenn mehrere Videos auf der gleichen Seite eingebunden wurden.

(function ($) {
   $(function () {

      var tag = document.createElement('script');
      tag.src = "https://www.youtube.com/iframe_api";
      tag.type='text/javascript';
      $('body').append(tag);
   
      $('iframe[src*="youtube"]').each(function(){
         var $me = $(this);
         var src = $me.attr('src');
         if (src.indexOf('enablejsapi=1') == -1) {
            $me.attr({src: src + (src.indexOf('?') == -1 ? '?' : '&') + 'enablejsapi=1'});
         }
      });
   
      window.onYouTubeIframeAPIReady = function () {
   
         var playerCurrentlyPlaying = null;
         var player_cnt = 0;
         
         $('iframe[src*="youtube"]').each(function(){
            
            var $me = $(this);
            var player_id = $me.attr("id");
            
            if (!player_id) {
               player_id = "ytpl-"+(player_cnt++);
               $me.attr({id:player_id});
            }
            
            var player = new YT.Player( player_id, { 
               events: {   
                  'onReady': function () {},
                  'onStateChange': function (event) {
         
                     if (event.data == YT.PlayerState.PLAYING) { 
                        if (playerCurrentlyPlaying && playerCurrentlyPlaying != $me.data().player) {
                           playerCurrentlyPlaying.pauseVideo();
                        }
                        playerCurrentlyPlaying = $me.data().player;
                     }         
                  }
               }   
            });
            
            $me.data({player:player});
         });   
      }
   
   });
})(jQuery);

Deutscher Paket Dienst (DPD) SOAP Schnittstelle mit PHP nutzen um Versandetikett als PDF zu generieren

Hier ein schnelles Beispiel, wie die per PHP SoapClient() und DPD-WSDL-Schnittstelle ein Versand-Etikett als PDF generiert werden kann.

Für Suchmaschinen: PHP SOAP-Header für Authentifizierung, authenticate SOAP-Header, DPD-Api Versandetikett generieren, Generate DPD Label, DPD parse SOAP response, Fatal error: Uncaught SoapFault exception: [soap:Server] Fault occurred while processing. in getLabel.php:107 Stack trace: #0 getLabel.php(107): SoapClient->__call(’storeOrders‘, Array) #1 getLabel.php(107): SoapClient->storeOrders(Array).

getAuth(array(
      'delisId'          => 'your-Id',
      'password'         => 'your-Password',
      'messageLanguage'   => 'de_DE'
   ));
   
   // ...und das Token merken
   $auth = $res->return;
   
   
   // Jetzt das Label generieren:
   
   $c = new SoapClient('https://public-ws-stage.dpd.com/services/ShipmentService/V3_1?wsdl');
   
   $token = array(
      'delisId'         => $auth->delisId,
      'authToken'         => $auth->authToken,
      'messageLanguage'   => 'de_DE'
   );
   
   // Set the header with the authentication token
   $header = new SOAPHeader('http://dpd.com/common/service/types/Authentication/2.0', 'authentication', $token);
   $c->__setSoapHeaders($header);
   
   try {
      $res = $c->storeOrders( array
         (
            "printOptions" => array(
               "paperFormat" => "A4",
               "printerLanguage" => "PDF"
            ),
            "order" => array(
               "generalShipmentData" => array(
                  "sendingDepot" => $auth->depot,
                  "product" => "CL",
                  "mpsCompleteDelivery" => false,
                  "sender" => array(
                     "name1" => "Sender Name",
                     "street" => "Sender Street 2",
                     "country" => "DE",
                     "zipCode" => "65189",
                     "city" => "Wiesbaden",
                     "customerNumber" => "123456789"
                  ),
                  "recipient" => array(
                     "name1" => "John Malone",
                     "street" => "Johns Street 34",
                     "country" => "DE",
                     "zipCode" => "65201",
                     "city" => "Wiesbaden"
                  )
               ),
               "parcels" => array(
                  "parcelLabelNumber" => "09123829120"
               ),
               "productAndServiceData" => array(
                  "orderType" => "consignment"
               )
            )
         )
      );
   } catch (SoapFault $exception) {
      echo $exception->getMessage();
      die();
   }
   
   // Et voilà!
   
   header('Content-type: application/pdf');
   echo $res->orderResult->parcellabelsPDF;
?>

direct_mail_subscription / real_url: Kurze URLs in Opt-In Bestätigungsmails von Newsletter-Anmeldung

Mit folgendem Trick bekommt man in die Bestätigungsmails von direct_mail_subscription eine kürzere URL. Hintergrund des Ganzen: Die URLs sind in den E-Mails teilweise so lang, dass sie umbrechen, was bei manchem Benutzern zu unvollständigen URLs bei Klick auf den Link führt.

Per Default sieht der Link in dem Subpart „EMAIL_TEMPLATE_CREATE_SAVED“ der Opt-In-Mail so aus:

http://www.beispiel.de/pfad/zu/seite.html?1=1&cmd=setfixed&sFK=approve&rU=3919&fD%5B_FIELDLIST%5D=uid&fD%5Bhidden%5D=0&aC=12732843

Schöner wäre es z.B. so:

http://www.beispiel.de/opt/nl/a/3919/12732843/0/0/

Folgende Schritte zu einer kurzen URL:

1. In den Seiteneigenschaften der Opt-In-Seite unter „Pfadsegment für untergeordnete Seiten“ einen kurzen Begriff eingeben, im Beispiel hier „opt“ und das Häkchen vor „Den gesamten Pfad überschreiben“ setzen.

2. In der typo3conf/realurl_conf.php folgende neue Regel für die postVarSets definieren:

'nl'     =>   array(
    array(   'GETvar'     => 'sFK', 
        'valueMap'    => array(
          'a'  => 'approve',
          'd' => 'DELETE'
        ),
      ),
    array( 'GETvar'   => 'rU', ),
    array( 'GETvar'   => 'aC', ),
    array(   'GETvar'   => 'cmd', 
        'valueMap'    => array(
          'setfixed'  => 'setfixed'
        ),
        'valueDefault'  => 'setfixed',
      ),
    array(   'GETvar'   => 'fD[_FIELDLIST]',
        'valueMap'  => array(
          'uid'    => 'uid'
        ),
        'valueDefault'  => 'uid',
      ),
    array( 'GETvar'   => 'fD[hidden]',
        'valueMap'  => array(
          '0'      => '0'
        ),
        'valueDefault'  => '0',
      ),
),

3. In dem Mail-Templates folgende Links für Bestätigung/Abmeldung verwenden:

Bitte klicken Sie auf diesen Link um die Anmeldung fertig zu stellen:
###THIS_URL###opt/nl/a/###FIELD_uid###/###SYS_AUTHCODE###/0/0/

Falls Sie keinen Newsletter möchten, klicken Sie hier:
###THIS_URL###opt/nl/d/###FIELD_uid###/###SYS_AUTHCODE###/0/0/

TemplaVoila-Field in TypoScript auslesen

Für Suchmaschinen, Phrasen nach denen ich selbst gesucht habe: „per ts auf tv field zugreifen“, Per TypoScript auf TV Feld zugreifen, TV-Feld per TypoScript auslesen. Feldwert von TemplaVoila in HMENU verwenden. Auf ein Datenfeld in TV per TS zugreifen. Mit Typoscript auf Felder eines FCE zugreifen. Zugriff auf TV aus Typoscript.

Aufgabe: Der Redakteur soll bei den Seiteneigenschaften ein Icon auswählen können, das vor dem Menüpunkt dargestellt wird. In TemplaVoilà wurde dazu bei einem Seitentemplate ein Feld angelegt (field_navi_icon) vom Typ „Auswahlliste“. Das Feld wird nicht gemappt, sondern ist später einfach nur eine Auswahlliste unter den Seiteneigenschaften einer Seite. Die Werte dieser Liste sind z.B.

select

  
    Default Icon
    
  
  
    Palme (icon-1)
    icon-1
  
  
    Segelboot (icon-2)
    icon-2
  
  
    Foto (icon-3)
    icon-3
  
  
  
    Kein Icon (no-icon)
    no-icon
  


Da mir leider keine Möglichkeit bekannt ist, direkt per TypoScript auf ein bestimmtes Feld von TemplaVoilà zuzugreifen, schreiben wir dazu eine kleine benutzerdefinierte Funktion. Die Datei unter „fileadmin/scripts/class.userfunctions.php“ speichern:

pi_loadLL();
    $this->pi_setPiVarDefaults();
    $this->pi_initPIflexForm();

    $def = array(  'field'      => 'tx_templavoila_flex',
            'flex_data'     => 'field_content', 
            'sheet_pointer' => 'sDEF', 
            'lang'       => 'lDEF', 
            'value_def'   => 'vDEF',
            'pid'      => $GLOBALS['TSFE']->id 
          );
    
    if ($conf['pid.']) {
      $conf['pid'] = $this->cObj->cObjGetSingle($conf['pid'], $conf['pid.']);
    }
    
    foreach ($def as $k=>$v) if (!$conf[$k]) $conf[$k] = $v;
    
    if ($conf['pid']) {
      $flex_array = t3lib_div::xml2array($this->cObj->TEXT(array('data'=>'DB : pages : '.$conf['pid'].' : tx_templavoila_flex')));
    } else {
      $flex_array = t3lib_div::xml2array($this->cObj->data[''.$conf['field'].'']);
    }
    $tv_field = $this->pi_getFFvalue($flex_array, $conf['flex_data'], $conf['sheet_pointer'], $conf['lang'], $conf['value_def']);
    
    return $tv_field;
  }

}
?>

Die benutzerdefinierte Funktion binden wir jetzt per TypoScript-Setup ein, damit wir später darauf zugreifen können:

page {
   includeLibs.myUserFuncs = fileadmin/scripts/class.userfunctions.php
}

Per TypoScript wird ein „normales“ HMENU/TMENU aufgebaut, allerdings mit dem kleinen Zusatz, das Feld in TemplaVoilà auszulesen und in das li-Tag der Navi als Klasse mit einzubauen:

lib.navi_main = HMENU

lib.navi_main {
  
  entryLevel = 1
  
  special = directory
  // Dieser Wert muss natürlich angepasst werden:
  special.value = 67
  
  wrap = 
    |
1 = TMENU 1.expAll = 1 1.noBlur = 1 1.wrap = | 1.NO = 1 1.NO { before.cObject = LOAD_REGISTER before.cObject.navi_icon.cObject = USER before.cObject.navi_icon.cObject { userFunc = user_functions->get_tv_field flex_data = field_navi_icon pid = TEXT pid.field = uid } stdWrap.wrap = | wrapItemAndSub =
  • |
  • |*|
  • |
  • |*|
  • |
  • wrapItemAndSub.insertData = 1 ATagParams.dataWrap = class="lv-0" } 1.ACT < .1.NO 1.ACT { stdWrap.dataWrap = | wrapItemAndSub =
  • |
  • |*|
  • |
  • |*|
  • |
  • ATagParams.dataWrap = class="lv-0 active" } 1.CUR < .1.ACT 1.CUR { stdWrap.dataWrap = | wrapItemAndSub =
  • |
  • |*|
  • |
  • |*|
  • |
  • ATagParams.dataWrap = class="lv-0 selected" } }

    Das Ergebnis ist ein Menü, das im HTML-Quelltext z.B. so aussieht:

    
    

    Jeder Menüpunkt hat den Feld-Wert aus TemplaVoilà als Klasse angehängt bekommen. Die zusätzliche Angabe der Ziel-pid als Klasse „pid-{uid}“ finde ich ebenfalls sehr praktisch. Alles weitere ist Sache von CSS. Dort nutze ich das span-Element z.B. zur Darstellung des Icons als Hintergrundbild.

    Typo3 Formhandler, SwiftMailer, PowerMail oder direct_mail sendet keine Mails

    Merkhilfe und Checkliste falls Formhandler keine Mails versendet. Ich fange tief im Eingemachten an, weil mich das Problem eben bis in das Server-Betriebssystem und die Konfigurationsdateien von Ubuntu getrieben hat. Für Suchmaschinen: Ubuntu -f Parameter, Formhandler Return-Path overwritten by postfix, postfix falschen Absender, postfix ignoriert From-Header in php-Mail-Befehl, From-Header wird von Postfix überschrieben

    Erst mal einen kleinen Test machen um zu prüfen, wo das Problem zu suchen ist. Je nach Server-Konfiguration kann es sein, dass eine gültige From-Angabe im Header des mail()-Befehls zwingend ist, oder dass sie per -f-Parameter übergeben werden muss. Folgendes PHP-Script auf den Server spielen und testen, ob Mails grundsätzlich über den php-Befehl mail() versendet werden:

    $header = 'From: webmaster@example.com'."\r\n".'Reply-To: webmaster@example.com'."\r\n".'X-Mailer: PHP/' . phpversion();
    $ok = mail ( "deine@email.de" , "das ist ein Test" , "Test", $header );
    

    Wenn die Mail ankam: Es muss irgendwas einfaches sein. Dazu schreibe ich später einen Beitrag 😉

    Wenn die Mail nicht ankam: Zuerst SPAM-Ordner prüfen! Auch dort nichts? Dann die Zeile mit dem mail() oben durch den 5. undokumentierten -f-Parameter ergänzen:

    $ok = mail ( "deine@email.de" , "das ist ein Test" , "Test", $header, "-f deine@email.de" );

    Die E-Mail-Adresse muss eine gültige (und dem Server bekannte) E-Mail-Adresse sein. Bei z.B. HostEurope ist das die E-Mail, die unter „Einstellungen -> Skripte -> Standard-E-Mail-Adresse“ zwingend im KIS angegeben sein muss.

    Wenn die E-Mail jetzt angekommen ist, dann liegt das an der Server-Einstellung. Dazu habe ich zwei Lösungen gefunden, eine bleibt eher Anwendungsseitig und zwingt Typo3 beim Aufruf von mail() noch den -f-Parameter zu verwenden. Die andere Lösung geht ins Eingemachte und setzt den Parameter zwingend für Apache / PHP und Postfix.

    Hilfreich sind übrigens auch immer die Logfiles von Postfix, die man unter var/log/mail.info findet. Wenn dort immer „OK“ steht, aber die Mails trotzdem nicht versendet wurden, ist man mit dem -f-Parameter auf der richtigen Spur.

    Lösung 1: Im Formhandler-Setup und der localconf.php

    1. In der typo3conf/localconf.php diese Zeile ergänzen:

    $TYPO3_CONF_VARS['SYS']['forceReturnPath'] = '1';

    2. In der Setup der Formhandler-Konfiguration darauf achten, dass die return_path-Angabe existiert. Als return_path die gleiche Angabe wie beim -f-Parameter verwenden.

    finishers {
       2.class = Tx_Formhandler_Finisher_Mail
       2.config {
          admin {
             ...
             return_path = deine@email.de
             ...
          }
          user {
             ...
             return_path = deine@email.de
             ...
          }
       }
    }

    Lösung 2: Postfix und php.ini

    1. Die Postfix-Konfiguration bearbeiten unter etc/postfix/main.cf

    myhostname = domainname.de

    2. In der etc/php5/apache2/php.ini den Absender für sendmail_path ergänzen:

    sendmail_path = /usr/sbin/sendmail -t -i -fwww-data@eswe-verkehr.de

    3. Postfix und Apache rebooten per Terminal-Befehl

    # sudo /etc/init.d/postfix restart
    # sudo /etc/init.d/apache2 restart

    Sollte das nicht reichen, hilft evtl. der Abschnitt „Mail“ in diesem speziellen Beitrag:
    http://labor.99grad.de/2016/10/03/installation-typo3-unter-ampps/

    Ansatz 3: Typo3 4.7 SwiftMailer

    Bei manchen Servern hatten wir das Problem, dass der seit Typo3 4.7 implementierte SwiftMailer Ärger machte. Leider fehlt seit 4.7 die Möglichkeit über „substituteOldMailAPI=0“ auf den alten Mail-Befehl umzuschalten.
    Folgender Test ist für dieses Problem typisch: Im Install-Tool unter „Grundkonfiguration“ gibt es einen Abschnitt, bei dem man sich eine Testmail schicken lassen kann. Wenn diese Mail nicht ankommt (was wir bei den Virtual Server Paketen von HostEurope mit PowerMail und direct_mail_subscription hatten) liegt es wieder an dem -f Parameter, der in der localconf.php so festgelegt werden muss:

    ## Für HostEurope V-Server: Problem, das sonst direct_mail_subscription / powermail / Testmail aus dem Install-Tool nicht funktioniert haben 
    $TYPO3_CONF_VARS['MAIL']['defaultMailFromAddress'] = 'hier@die-email.de';
    $TYPO3_CONF_VARS['MAIL']['defaultMailFromName'] = 'Dein Name';
    $TYPO3_CONF_VARS['MAIL']['transport'] = 'sendmail';
    $TYPO3_CONF_VARS['MAIL']['transport_sendmail_command'] = '/usr/sbin/sendmail -t -f hier@die-email.de';
    

    Der -f-Parameter unter „transport_sendmail_command“ muss bei HostEurope genau der Einstellung für die „Standard-E-Mail“ unter „Webpaket -> Konfiguration -> Skripte“ im KIS entsprechen.

    Ansatz 4 – für Typo3 > 8 · LocalConfiguration.php

    Darauf achten, das in der LocalConfiguration.php eine Default E-Mail Adresse angegeben ist:

    'MAIL' => [
    'transport'=>'mail',
    'defaultMailFromAddress'=>'email@domain.de',
    'defaultMailFromName'=>'Name Absender',
    ...
    ],

    Wichtig: Die defaultMailFromAddress muss bei Installationen auf HostEurope genau der Einstellung für die „Standard-E-Mail“ unter „Webpaket -> Konfiguration -> Skripte“ im KIS entsprechen.