Beiträge der Kategorie TYPO3

TCA für Typen überschreiben

Trotz der vielen Anleitungen, ist es jedesmal ein Try and Error das TCA für einen bestimmten Typ zu überschreiben. In diesem Fall sollte für das Element ‚video‘ die Auswahl der Dateitypen eingeschränkt werden.

Um ein Feld für alle Typen zu überschreiben, packt man folgenden Block in my_ext/Configuration/TCA/Overrides/tt_content.php

$GLOBALS['TCA']['tt_content']['columns']['header']['label'] = 'Foobar';

Was überschrieben werden kann, findet man im Backend über das Modul Configuration raus, in dem man sich das TCA anzeigen lässt.

Will man nun etwas für einen bestimmten Typen überschreiben, dann setzt man statt ['columns'] folgendes ein: ['types']['video']['columnsOverrides'].

$GLOBALS['TCA']['tt_content']['types']['introtext']['columnsOverrides']['header']['label'] = 'Foobar';

Bei den Felden mit Kindelementen wird es etwas komplizierter – das ist für alle Typen:

$GLOBALS['TCA']['tt_content']['columns']['assets']['config']['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserAllowed'] = 'youtube,vimeo';
$GLOBALS['TCA']['tt_content']['columns']['assets']['config']['filter'][0]['parameters']['allowedFileExtensions'] = 'youtube,vimeo';

Nach dem gleichen Prinzip wie beim Header kann diese Konfiguration nur für einen bestimmten Inhaltstyp vorgenommen werden:

$GLOBALS['TCA']['tt_content']['types']['video']['columnsOverrides']['assets']['config']['filter'][0]['parameters']['allowedFileExtensions'] = 'youtube,vimeo';
$GLOBALS['TCA']['tt_content']['types']['video']['columnsOverrides']['assets']['config']['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserAllowed'] = 'youtube,vimeo';

Tags:

Geschrieben in TYPO3, TYPO3 v10 | Kommentare deaktiviert für TCA für Typen überschreiben

Event Listener mit Dependancy Injection

Um die News-Ausgabe mit weiteren Daten anzureichern, stellt News Events bereit, auf die reagiert werden kann. Das Event wird dabei in Configuration/Services.yaml registiert. In meinem Fall brauche ich in dem EventListener den Zugriff auf ein Repository. ObjectManager->get gilt seit TYPO3 10 als veraltet, injectXXX-Funktionen werden nicht ausgeführt. Was nun? Ein Fall für Dependancy Injection!

Der Basis Event-Listener sieht so aus und wird (in meinem Fall) in Classes/Event abgelegt:

<?php
use GeorgRinger\News\Event\NewsDateMenuActionEvent;
class EventListener
{
    /**
     * @var \MyNamespace\MyExtension\Domain\Repository\FooRepository
     */
    protected $fooRepository;
 
    /**
     * @param \MyNamespace\MyExtension\Domain\Repository\FooRepository $fooRepository
     */
    public function __construct(\MyNamespace\MyExtension\Domain\Repository\FooRepository $fooRepository)
    {
        $this->fooRepository = $fooRepository;
    }
 
    public function enrichWithAdditionalData(NewsDateMenuActionEvent $event): void
    {
 
    }
}

In Configuration/Services.yaml wird der Event-Listener registriert. Gleichzeitig wird definiert, welchen Typ die Variable $fooRepository beim Erstellen des Listeners haben muss. Wichtig sind dabei die letzten zwei Zeilen: dabei wird die Klasse FooRepository als public definiert, damit sie per Depencancy Injection in einem Listener instanziiert werden kann.

services:
  _defaults:
    autowire: true
    autoconfigure: true

  MyNamespace\MyExtension\Event\EventListener:
    arguments:
      $fooRepository: '@MyNamespace\MyExtension\Domain\Repository\FooRepository'
    tags:
      - name: event.listener
        identifier: 'news-event-listener'
        method: 'enrichWithAdditionalData'
        event: GeorgRinger\News\Event\NewsDateMenuActionEvent

  MyNamespace\MyExtension\Domain\Repository\FooRepository:
    public: true

Doku hier: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DependencyInjection/Index.html

Mask – IRRE Inhaltselemente ausgeben

Beim Erstellen von Mask Inhaltslementen kann man als Typ „Inhalte“ wählen. In der Anleitung fehlt die Angabe, wie man diese Inhaltselemente im Frontend ausgegeben bekommt. Dabei ist es so einfach:

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
      xmlns:mask="http://typo3.org/ns/MASK/Mask/ViewHelpers"
      data-namespace-typo3-fluid="true">
 
<f:if condition="{data.tx_mask_irre_contents}">
    <f:for each="{data.tx_mask_irre_contents}" as="content">
        <mask:content uid="{content.uid}" />
    </f:for>
</f:if>

Geschrieben in TYPO3, TYPO3 v10 | Kommentare deaktiviert für Mask – IRRE Inhaltselemente ausgeben

Seitentitel anpassen

Das ist wieder so ein Fall von „ich kann es mir nicht merken und die Google-Suche liefert immer die falschen Ergebnisse“.

Früher konnte TYPO3 nur Seitentitel der Form [Sitename]: [Seitentitel] generieren. Die einzige Möglichkeit, das zu ändern, bestand darin, die Option config.noPageTitle zu aktivieren und den Titel unter headerData selbst zusammenzustellen. Dabei gibt es schon seit langem Konfigurationsmöglichkeiten für den Seitentitel. Zum einen kann man die Reihenfolge einstellen, zum anderen kann man den Trenner angeben.

Ich war mir nicht sicher, wie die dafür notwendigen Konfigurationen heißen, daher wollte ich mal die Suche im Internet bemühen. Aber die Google Suche leitet mich nur auf Seiten mit dem config.noPageTitle Beispiel. Dabei ist es so einfach:

config {
    pageTitleFirst = 1
    pageTitleSeparator = -
    pageTitleSeparator.noTrimWrap = | | |
}

Ergebnis:
Meine Site - Seite ABC

Geschrieben in TYPO3 | Kommentare deaktiviert für Seitentitel anpassen

Abschnittsnavigation (aka menu_section) mit Gridelements

Wenn man mit Gridelements und somit verschachtelten Inhaltselementen arbeitet, stellt man vielleicht irgendwann fest, dass die Sortierung nicht funktioniert, sobald in der Abschnittsnavigation anzuzeigende Inhaltselemente in Container (2-Spalten etc.) angeordnet werden. Beispiel (in Klammern ist die Position intern):

  • Container A (1)
    • Element A.1 (1)
    • Element A.2 (2)
  • Container B (2)
    • Element B.1 (1)
    • Element B.2 (2)

Je nachdem, in welcher Reihenfolge man die Elemente erstellt hat, kann z.B. folgende Abschnittsnavigation ausgegeben werden:

  • B.1
  • A.1
  • A.2
  • B.2

Ich habe z.B. folgende Lösung mit einem ViewHelper gefunden. Ich wollte jedoch eine Lösung, die auch für bereits vorhandene Inhaltselemente funktioniert und die unabhängig vom Template eingesetzt werden kann. So habe ich mich für einen DataProcessor entschieden.

Als erstes hole ich mir alle Inhaltselemente – unabhängig davon, ob sectionIndex gesetzt ist oder nicht. Dann hänge ich meinen eigenen DataProcessor ein.

tt_content.menu_section.dataProcessing.10.dataProcessing.20 {
    where >
}
tt_content.menu_section.dataProcessing.10.dataProcessing.30 = NP\MyExtension\DataProcessing\SectionProcessor

Im DataProcessor werden die Datensätze gefiltert, so dass nur die mit sectionIndex = 1 übrig bleiben. Weiterhin wird die Sortierung korrigiert und es wird neu sortiert. Beim neuen Sorting wird der Wert der Elemente, die direkt auf der Seite abgelegt sind, mit 10000 multipliziert. Falls das Element in einem Container liegt, dann wird die Sortierung auf den Sorting-Wert des Containers mal 10000 plus die eigene Sortierung gesetzt.

Den DataProcessor lege ich unter Classes\DataProcessing\SectionProcessor.php ab:

namespace NP\MyExtension\DataProcessing;
 
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
 
class SectionProcessor implements DataProcessorInterface
{
 
    /**
     * Process content object data
     *
     * @param ContentObjectRenderer $cObj The data of the content element or page
     * @param array $contentObjectConfiguration The configuration of Content Object
     * @param array $processorConfiguration The configuration of this processor
     * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View)
     * @return array the processed data as key/value store
     */
    public function process(
        ContentObjectRenderer $cObj,
        array $contentObjectConfiguration,
        array $processorConfiguration,
        array $processedData
    ) {
        $processedContent = [];
        $sortMapping = [];
        foreach ($processedData['content'] as $content) {
            if ($content['data']['sectionIndex'] == 1) {
                $content['data']['sorting'] = $this->getRealSorting($content, $processedData['content']);
                $processedContent[] = $content;
            }
        }
        // debug($processedContent, 'processed content');
        usort($processedContent, [$this, 'sortContent']);
        $processedData['content'] = $processedContent;
        return $processedData;
    }
 
    /**
     * @param array $content
     * @param array $allElements
     */
    protected function getRealSorting($content, $allElements)
    {
        if ($content['data']['colPos'] != '-1') {
            return $content['data']['sorting'] * 10000;
        }
        $sorting = $content['data']['sorting'];
        foreach ($allElements as $element) {
            if ($element['data']['uid'] == $content['data']['tx_gridelements_container']) {
                $sorting += $element['data']['sorting'] * 10000;
            }
        }
        return $sorting;
    }
 
    /**
     * @param array $a
     * @param array $b
     * @return bool
     */
    protected function sortContent($a, $b)
    {
        return $a['data']['sorting'] > $b['data']['sorting'];
    }
}

Dieser Ansatz wird limitiert durch das Verschachtelungslevel der Elemente. Da könnte man diesen auf die schnelle implementierten Ansatz noch optimieren.

Geschrieben in gridelements, TYPO3, TYPO3 v9 | Kommentare deaktiviert für Abschnittsnavigation (aka menu_section) mit Gridelements

TYPO3 auf Ddev installieren

Ja, es gibt Anleitungen da draußen, in denen das drin steht. Ich kann es mir einfach nicht merken … weder den Link noch den Inhalt.

Projekt-Ordner erstellen
mkdir spielwiese11

In Projekt-Ordner wechseln
cd spielwiese11

Ddev Projekt initalisieren
ddev config
Es kommen ein paar Fragen, bei docroot „public“ eingeben und bei Project type „typo3“.

Ddev starten
ddev start

In Ddev wechseln
ddev ssh

TYPO3 per Composer installieren
Hier ist der TYPO3 Composer Helper sehr hilfreich. Einfach die Version auswählen, die installiert werden soll (Minimal, Default oder Full), kopieren, einfügen und ausführen.

Dann kann man die Seite bereits unter https://spielwiese11.ddev.site aufrufen und den Rest im Browser erledigen.

Quellen:
https://docs.typo3.org/m/typo3/guide-contributionworkflow/main/en-us/Appendix/SettingUpTypo3Ddev.html

TYPO3 Extension Settings im Scheduler Task auslesen

Ich brauche die Settings eines Plugins im Scheduler. Es gibt zwar zahlreiche Snippets und Hinweise, wie es gehen soll, die alle nicht funktioniert haben. Dieses Snippet ist getestet unter TYPO3 10.

Da man sich im Backend befindet, müssen die Settings von plugin nach module kopiert werden:

module.tx_myextension < plugin.tx_myextension

Meine Extension heißt beispielhaft my_extension und ich habe im TypoScript die Konfiguration für alle Plugins gesetzt.

Im Scheduler-Task kann man auf die Settings dann wie folgt zugreifen. Zu beachten dabei die Schreibweise des Extensionnames, es ist CamelCase.

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
 
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
$configuration = $configurationManager->getConfiguration(
    ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
    'MyExtension'
);

Geschrieben in TYPO3, TYPO3 v10 | Kommentare deaktiviert für TYPO3 Extension Settings im Scheduler Task auslesen

Sonderbare Meldung im Modal beim Löschen der Seite

Es ist ein sehr spezieller Fall, den wahrscheinlich kaum jemand kennen wird, aber mir hat es nochmal etwas neues über die Konfiguration in TYPO3 beigebracht.

Mir wurde gemeldet, dass beim Löschen einer Seite aus dem Kontext-Menü im Seitenbaum folgende Meldung im Modal erscheint:

No content provided, please check your Modal configuration.

Ich wusste nicht direkt, welche Modal-Konfiguration gemeint ist, und habe mich durch den Code gehangelt. Erstmal die Meldung suchen – der Locallang-Key ist „mess.delete“. Dann suchen, wo diese Meldung generiert wird – das war im \TYPO3\CMS\Backend\ContextMenu\ItemProviders\RecordProvider in der Funktion getDeleteAdditionalAttributes. Dann rausgefunden, dass die Funktion nicht die korrekten Attribute liefert und dass es an if ($this->backendUser->jsConfirmation(JsConfirmation::DELETE)) { ... } scheitert.

Schaut man sich die Funktion jsConfirmation genaue an, dann bekommt sie als Parameter eine Bitmaske. Diese Bitmaske steht in User TSconfig unter options.alertPopus. Ich hatte in meinem Fall diesen Wert gesetzt auf 250. Vergleicht man es mit der Dokumentation, ist dort 255 für „Alle Popups“ angegeben.

255 steht für die Bitmaske 11111111 und
250 steht für die Bitmaske 11111010

Das erste Bit von hinten steht für „On Type Change“, das dritte Bit von hinten für „Löschen“. Das ergibt sich aus den Angaben der Werte in der Dokumentation. 1 entspricht der Bitmaske 1, 4 entspricht der Bitmaske 100. Wenn man nun die Zahlen vergleicht, dann unterscheidet sich 255 und 250 genau an diesen zwei Stellen.

Es wäre natürlich besser, wenn das Modal gar nicht erst angezeigt wird, wenn in der Bitmaske das Löschen-Bit 0 ist. In diesem Fall habe ich es einfach dadurch „gelöst“, dass ich den Wert für options.alertPopups auf 255 gesetzt habe. Dann kommt zumindest keine verstörende Meldung 🙂

Tags: ,

Geschrieben in TYPO3 | Kommentare deaktiviert für Sonderbare Meldung im Modal beim Löschen der Seite

TCA für Image Manipulation (crop) in eigener Extension

Es gibt ein paar Beispiele im Netz, wie man Bild-Manipuation für tt_content aktiviert und Crop-Varianten (cropVariants) definiert. Ich habe kein Beispiel gefunden, wie man in seiner eigenen Extension direkt einem Bild-Feld Crop-Eigenschaften mitgeben kann. Nach langer Suche und viel rumprobieren ist das mein Ergebnis, das funktioniert.

'stage_image' => [
    'exclude' => true,
    'label' => 'LABEL',
    'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
        'stage_image',
        [
            'appearance' => [
                'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference'
            ],
            'overrideChildTca' => [
                'types' => [
                    '0' => [
                        'showitem' => '
                        --palette--;;imageoverlayPalette,
                        --palette--;;filePalette'
                    ],
                    \TYPO3\CMS\Core\Resource\File::FILETYPE_IMAGE => [
                        'showitem' => '
                        --palette--;;imageoverlayPalette,
                        --palette--;;filePalette'
                    ],
                ],
                'columns' => [
                    'crop' => [
                        'config' => [
                            'cropVariants' => [
                                'default' => [
                                    'title' => 'Desktop',
                                    'allowedAspectRatios' => [
                                        'default' => [
                                            'title' => 'Rechteckig 22:9',
                                            'value' => 2.4
                                        ],
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
            'foreign_match_fields' => [
                'fieldname' => 'stage_image',
                'tablenames' => 'xxx',
                'table_local' => 'sys_file',
            ],
            'maxitems' => 1,
        ],
        $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
    ),
],

Geschrieben in TYPO3, TYPO3 v10 | Kommentare deaktiviert für TCA für Image Manipulation (crop) in eigener Extension

TYPO3 Meldung bei gesperrten Records im Backend

Ich arbeite ja fast immer in der englischen Version, daher ist mir dieser Fehler nicht aufgefallen. Wenn ein anderer Benutzer einen Datensatz (z.B. eine Seite) zum Bearbeiten geöffnet hat, dann wird im TYPO3 Seitenbaum eine Meldung angezeigt. Im Englischen lautet die Meldung wie folgt:
„The user ‚%s‘ began to edit this record %s ago.“ Dann wird der Benutzertyp und das Datum eingesetzt, passt soweit.

Ist das Backend auf deutsch eingestellt, sieht diese Meldung etwas komisch aus.

Die Anführungsstriche werden mit HTML-Entities dargestellt.

Die Suche im Netz gab diesen wertvollen Beitrag aus der TYPO3-Doku aus: https://docs.typo3.org/m/typo3/reference-coreapi/9.5/en-us/ApiOverview/Internationalization/ManagingTranslations.html

Die Übersetzungen fürs Backend können also in einer eigenen Extension überschrieben werden.

Das in die ext_localconf.php einer eigenen Extension:

$GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']['de']['EXT:core/Resources/Private/Language/locallang_core.xlf'][] = 'EXT:my_extension/Resources/Private/Language/de.locallang-core.xlf';

Dann unter dem angegebenen Pfad die xlf-Datei anlegen und folgendes rein:

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.0">
    <file source-language="en" datatype="plaintext" date="2013-03-09T18:44:59Z" product-name="examples">
        <header/>
        <body>
            <trans-unit id="labels.lockedRecord" resname="labels.lockedRecord" approved="yes">
                <source>The user '%s' began to edit this record %s ago.</source>
                <target state="translated">Der Benutzer '%s' hat mit der Bearbeitung dieses Datensatzes vor %s begonnen.</target>
            </trans-unit>
            <trans-unit id="labels.lockedRecord_content" resname="labels.lockedRecord_content" approved="yes">
                <source>The user '%s' began to edit content on this page %s ago.</source>
                <target state="translated">Der Benutzer '%s' hat mit der Bearbeitung dieser Seite vor %s begonnen.</target>
            </trans-unit>
            <trans-unit id="labels.lockedRecordUser" resname="labels.lockedRecordUser" approved="yes">
                <source>The %s '%s' began to edit this record %s ago.</source>
                <target state="translated">Der %s '%s' hat mit der Bearbeitung dieses Datensatzes vor %s begonnen.</target>
            </trans-unit>
            <trans-unit id="labels.lockedRecordUser_content" resname="labels.lockedRecordUser_content" approved="yes">
                <source>The %s '%s' began to edit content on this page %s ago.</source>
                <target state="translated">Der %s '%s' hat mit der Bearbeitung dieser Seite vor %s begonnen.</target>
            </trans-unit>
        </body>
    </file>
</xliff>

Klappt! Hab ich schon erwähnt, dass ich TYPO3 toll finde 😀

Geschrieben in TYPO3, TYPO3 v9 | Kommentare deaktiviert für TYPO3 Meldung bei gesperrten Records im Backend