Beiträge der Kategorie TYPO3 v11

Fehler bei Aufruf der errorAction ausgeben

Falls es beim Aufruf einer Funktion zu einem Validierungsfehler kommt und dann auf die errorAction umgeleitet wird, dann steht dort im besten Fall nur ‚Es ist ein Fehler aufgetreten‘. Zugegeben, die Lösung unten ist quick and dirty, funktioniert aber:

In callActionMethod im ActionController (Zeile 574ff) einfach folgendes anpassen:

 if (!$validationResult->hasErrors()) {
	$this->eventDispatcher->dispatch(new BeforeActionCallEvent(static::class, $this->actionMethodName, $preparedArguments));
	$actionResult = $this->{$this->actionMethodName}(...$preparedArguments);
} else {
// NEU
	DebuggerUtility::var_dump($validationResult, 'errors', 20);
	die;
	$actionResult = $this->{$this->errorMethodName}();
}

Ich habe im Beispiel DebuggerUtility::var_dump verwendet, damit ich die Tiefe der Ausgabe angeben kann.

Manuelle Validierung eines Extbase-Objekts

Es basiert auf dem Beitrag von Torben Hansen von 2017 – es ist einfach nur eine aktuelle Version davon. In dem Fall musste ich sicherstellen, dass vor einer bestimmten Aktion die Benutzerdaten vollständig sind.

use TYPO3\CMS\Extbase\Validation\ValidatorResolver;
 
$user = $this->getCurrentUser();
$validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
$validator = $validatorResolver->getBaseValidatorConjunction(FrontendUser::class);
$validationResults = $validator->validate($user);
 
if ($validationResults->hasErrors()) {
    // @todo cycle through errors in $validationResults->getFlattenedErrors()
}

Extension Settings in Plugin überschreiben

In TYPO3 gibt es zwei Möglichkeiten settings oder allgemein Einstellungen für Plugins zu konfigurieren. Entweder man konfiguriert für die komplette Extension oder für einzelne Plugins. Da ich das nicht besser beschreiben kann, versuche ich es an einem Beispiel.

Angenommen, die Extension heißt my_extension und hat zwei Plugins pi1 und pi2. Die Konfiguration allgemein kann unter plugin.tx_myextension abgelegt werden und für die einzelnen Plugins unter plugin.tx_myextension_pi1 und plugin.tx_myextension_pi2. Die „allgemeine“ Konfiguration wird dabei an die Plugins vererbt.

plugin.tx_myextension.settings {
  var1 = Variable 1
}
 
plugin.tx_myextension_pi1.settings {
  var2 = Variable 2
}
 
plugin.tx_myextension_pi2.settings {
  var3 = Variable 3
}

Im ersten Plugin pi1 stehen damit in den Settings die Variablen var1 und var2 zur Verfügung, im Plugin pi2 die Variablen var1 und var3.

Was ist aber, wenn eine Variable entfernt werden soll? In diesem Fall funktioniert der > Operator leider nicht, da die Settings allgemein und Plugin als Array gemergt werden. Die Lösung ist das Wörtchen „__UNSET“, das ein entfernen des Schlüssels zur Folge hat.

plugin.tx_myextension_pi1.settings {
  var1 = __UNSET
}

Modul Template für Redakteure freigeben

Für ein Projekt, das ich übernommen habe, brauchen die Redakteure der Seite Zugriff auf TypoScript. Ich hätte es definitiv anders implementiert (Datensätze, Modul, etc.), mein Vorgänger hatte aber den Weg gewählt. Wenn also ein Benutzer Anpassungen am TypoScript vornehmen wollte, brauche er zwangsweise die Admin-Rechte. Seit TYPO3 10 gibt es noch die Abgrenzung zum System Maintainer, aber sobald ein Benutzer Admin-Rechte besitzt, können Sie nicht weiter eingeschränkt werden. „Mit großer Macht kommt große Verantwortung“ (Onkel Ben aus Spiderman) und ich traue den Redakteure nicht über den Weg mit Admin-Rechten. Also habe ich nach der Möglichkeit gesucht, das Modul und die Tabellen für die Redakteure freizugeben, damit sie wieder normale Redakteure sein können.

In TYPO3 11 hat das folgende gut funktioniert. Wahrscheinlich auch unter TYPO3 10.

1. Zugriffsrechte für Modul anpassen in my_extension/ext_tables.php
$GLOBALS['TBE_MODULES']['_configuration']['web_ts']['access'] = 'user,group';

2. Tabellen für Benutzer freigeben (dann tauchen sie in der Liste der erlaubten Tabellen auf) in my_extension/Configuration/TCA/Overrides/sys_template.php
$GLOBALS['TCA']['sys_template']['ctrl']['adminOnly'] = 0;

3. Benutzergruppe oder Benutzer bearbeiten und dort das Modul „Template“ und die Tabelle „sys_template“ erlauben.

In TYPO3 12 hat sich die Registrierung der Module geändert. Abgesehen davon gibt es nun mehrere Template-Module. Es ist natürlich auch ein Vorteil, denn die Redakteure brauchen sie ja nicht alle.

1. Event-Handler für die Modul-Konfiguration registrieren
Backend Module configuration

2. Event Listener implementieren

namespace Vendor\MyExtension\Backend\EventListener;
 
use TYPO3\CMS\Backend\Module\BeforeModuleCreationEvent;
 
final class ModuleEventListener
{
    public function __invoke(BeforeModuleCreationEvent $event): void
    {
        // Change module icon of page module
        $modules = ['web_ts', 'web_typoscript_recordsoverview', 'web_typoscript_infomodify'];
        if (in_array($event->getIdentifier(), $modules)) {
            $event->setConfigurationValue('access', 'user');
        }
    }
}

Es reicht an dieser Stelle nicht, nur web_ts aufzulisten. Die verfügbaren Module sind die Keys in vendor/typo3/cms-tstemplate/Configuration/Backend/Modules.php

3. Benutzergruppe oder Benutzer bearbeiten und dort die Module „Template“, gewünschte Submodule und die Tabelle „sys_template“ erlauben.

LimitToPages in Site Config dynamisch setzen

Im Moment implementiere ich eine Extension, bei der es unter anderem digitale Produkte gibt. Die Produkte können jeweils von einem Typ sein – z.B. Kurs oder Tutorial. Der Kunde hat sich für die Produkte sprechende URLs gewünscht, die den Produkttyp enthalten – z.B. /kurs/produkt-1 oder /tutorial/produkt-2. Da schien es mir sinnvoll, pro Produkt-Typ eine separate Detailseite anzugelen, deren Slug entsprechend zu setzen und in der Site Config hübsche URLs zu konfigurieren.

Nun gibt es folgendes Problem: die Produkt-Typen stehen in der Datenbank und können erweitert werden. Die Site Config hingegen ist ja dateibasiert und wird versioniert. Bei der Konfiguration der hübschen URLs kann mit limitToPages angegeben werden, für welche Seiten die URLs angewendet werden. Mal absehen davon, dass die IDs der Detailseiten sich lokal, auf Stage und Live durchaus unterscheiden können, wäre es nicht schön, wenn limitToPages dynamisch aus der Datenbank ermittelt werden könnte.

Seit TYPO3 10.3. gibt es die Möglichkeit, in der Site Config Placeholder zu verwenden und diese mit eingenen Prozessoren zu verarbeiten. In der Dokumentation findet sich ein Beispiel: YAML API.

Und so sieht man Implementierung aus. In der Site Configuration config.yaml gebe ich die Platzhalter an:

limitToPages: '%detailPid(product)%'

In LocalConfiguration.php wird der Processor registriert:

<?php
[...]
'yamlLoader' => [
    'placeholderProcessors' => [
        \Example\MyExtension\Configuration\Processor\DetailPagePlaceholderProcessor::class => [
            'after' => [
                \TYPO3\CMS\Core\Configuration\Processor\Placeholder\ValueFromReferenceArrayProcessor::class
            ]
        ]
    ]
],
[...]

Und das ist der Processor:

<?php
declare(strict_types=1);
 
namespace Example\MyExtension\Configuration\Processor;
 
use TYPO3\CMS\Core\Configuration\Processor\Placeholder\PlaceholderProcessorInterface;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Utility\GeneralUtility;
 
class DetailPagePlaceholderProcessor implements PlaceholderProcessorInterface
{
    public function canProcess(string $placeholder, array $referenceArray): bool
    {
        return strpos($placeholder, '%detailPid(') !== false;
    }
 
    public function process(string $value, array $referenceArray)
    {
        $detailPages = $this->getDetailPages();
 
        // Throw this exception if the placeholder can't be substituted
        if (empty($detailPages)) {
            throw new \UnexpectedValueException('No detail page found', 1692345790);
        }
        return $detailPages;
    }
 
    private function getDetailPages()
    {
        $table = 'tx_myextension_domain_model_type';
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
        $queryBuilder
            ->getRestrictions()
            ->removeAll()
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
 
        $result = $queryBuilder
            ->select('*')
            ->from($table)
            ->where(
                $queryBuilder->expr()->gt('detail_pid',
                    $queryBuilder->createNamedParameter(0, Connection::PARAM_INT))
            )
            ->execute()
            ->fetchAllAssociative();
        $detailPid = [];
        foreach ($result as $row) {
            $detailPid[] = $row['detail_pid'];
        }
        return array_unique($detailPid);
    }
}

Ich will nicht ausschließen, dass das Beispiel nicht funktioniert, denn das ist ein angepasster Demo-Code, der keine tatsächlichen Kundennamen oder Extension-Namen enthält.

Tabellenname in Repository ermitteln

Das ist ein Update zu meinem Beitrag von 2012. Mit diesem Coder kann im Repository der Tabellen-Name des zugehörigen Models ermittelt werden. Falls mal mit QueryBuilder eine Abfrage gebastelt werden muss. So funktioniert es nun in TYPO3 11:

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
[...]
$dataMapper = GeneralUtility::makeInstance(DataMapper::class);
$tableName = $dataMapper->getDataMap($this->objectType)->getTableName();

Geschrieben in TYPO3, TYPO3 v11 | Kommentare deaktiviert für Tabellenname in Repository ermitteln

Warning im TYPO3 Backend

Nach einem Update auf TYPO3 11 und PHP 8.1 hatte ich im Backend folgende Warnung:


Core: Error handler (BE): PHP Warning: Undefined array key type in /var/www/html/vendor/symfony/expression-language/Node/GetAttrNode.php line 97

Ich hatte schon die Vermutung, dass es mit TypoScript-Conditions zu tun hat. Dieser Blog-Artikel auf typo3-probleme.de hat mich auf die richtige Anpassung gebracht.

So muss die Condition angepasst werden, damit es keine Warnung mehr gibt:

[request.getQueryParams()['type'] == 1452982642]
[traverse(request.getQueryParams(), 'type') == 1452982642]

Exports aus TYPO3 Backend

Für einen Kunden habe ich Module entwickelt, in denen die dargestellten Daten als CSV exportiert werden könnne. Ursprünglich für TYPO3 8 entwickelt, wurden die Module in den letzten Jahren mehrfach aktualisiert, im Moment für TYPO3 11.

Ein Export in einem Backend-Modul kann wie folgt implementiert werden:

public function exportContactListAction(): ResponseInterface
    {
        $records = $this->recordRepository->findAll();
 
        $filename = 'records_list' . '_' . date('Y-m-d') . '.csv';
        $export = $this->createExport($records);
 
        $response = $this->responseFactory->createResponse()
            ->withHeader('Content-Type', 'application/octet-stream')
            ->withHeader('Content-Length', (string)strlen($export))
            ->withHeader('Content-Disposition', 'attachment; filename=' . PathUtility::basename($filename));
        $response->getBody()->write($export);
 
        return $response;
    }

In der Funktion createExport kann ein beliebiger Content generiert werden. Als praktisch hat sich für mich folgende Funktion erwiesen:

CsvUtility::csvValues($dataRow, ';') . "\n";

Man packt die zu exportierenden Daten in ein Array und macht ein Csv daraus.

Geschrieben in TYPO3, TYPO3 v11 | Kommentare deaktiviert für Exports aus TYPO3 Backend