Beiträge von natalia

Flux und Inhaltselemente in News

In der Extension “News” gibt es ein schönes Feature. Dabei können News-Beiträge mit Inhaltselementen beliebig erweitert werden. In meinem aktuellen Projekt wurde die Seite mit Flux umgesetzt und dafür wurden alle Standard-Inhaltselemente per TSconfig deaktiviert. Damit man die “normalen” Text-Elemente in den News verwenden kann, muss man im TSconfig der News-Storage-Seite folgendes hinterlegen:

# reset remove items
TCEFORM.tt_content.CType.removeItems = html
# allow only text
TCEFORM.tt_content.CType.keepItems = text

Wichtig ist zunächst die removeItems zurückzusetzen (removeItems = black list). Diese Einstellung überschreibt andernfalls alles andere. Mit keepItems können Elemente angegeben werden, die stehen bleiben, während der Rest entfernt wird. (keepItems = white list)

Laravel – erster Eindruck

Soeben bin ich fast fertig mit einer Applikation, mit der ich Playlisten für meine Les Mills Kurse erstellen kann. Ich habe mich entschieden, die Applikation in Laravel zu schreiben, weil es spannend ist, mal was neues kennenzulernen (Laravel ist beliebste PHP-Framework im Moment) und weil ich TYPO3 dafür zu viel fand.

Als Einstieg in Laravel habe ich mir “Laravel From Scratch” Tutorials angeschaut. Die Videos fand ich echt gut, auch wenn der Entwickler verdammt schnell tippt und Fenster wechselt. Ich musste immer wieder mal stoppen und sortieren, wo er gerade ist. Mir hat gut gefallen, dass er ziemlich schnell eine Ausgabe erzeugt, auch wenn es nicht immer der eleganteste Weg ist. In den späteren Tutorials passt er es auch an und zeigt einen besseren Weg. z.b. werden am Anfang die Ausgaben direkt in die Routing-Datei geschrieben. Später erzeugt er auch View, Models und Controller.

Etwas verwirrend an Laravel finde ich die Migrations. Zum einen das Wording, zum anderen, dass man Felder nicht nach dem Erstellen der Tabelle hinzufügen kann. Wenn ich also bereits eine Tabelle angelegt und befüllt habe, brauche ich eine neue Migration, die ein Feld zu der Tabelle hinzufügt. Alternativ kann ich durch Rollback meine Tabelle löschen, die Migration anpassen und neu erstellen.

Gut gefällt mir das Routing. Gerade dann, wenn man mit Route::resource() automatisch Routen mit einem Controller verknüpfen kann. Zusammen mit CRUD kann man so schnell Funktionen fürs Erstellen und Bearbeiten implementieren. Ebenfalls positiv finde ich Blade-Templates. Mein Lieblings-Templating-Framework ist nach wie vor Flow, mein Horror ist Smarty. Blade ist ein guter Kompromiss zwischen “sieht aus wie HTML” und “PHP verwendenen in Templates”. In den Tutorials schreibt Jeffrey seine Formulare mit <form ...> in die Templates. Dann fand ich einige Beispiele mit Form::open, was mir natürlich besser gefällt. Seit Laravel 5.5 ist der Form-Helper ausgelagert worden. Nachdem ich das herausgefunden hatte, schrieben sich die Formulare noch schneller. Wenn man das Prinzip mit den View-Helpern verstanden hat, kann man relativ schnell eigene implementieren. So brauchte ich z.B. einen View-Helper, der mir aus meinem Rating (1 bis 5) Sternchen malt. Wäre auch mit einem Include gegangen, ist aber so noch eleganter.

Ebenfalls gut lassen sich Relationen abbilden. Mit hasMany, belongsTo kann man die Verknüpfungen zwischen Models definieren. Ebenfalls eine gute Idee sind die Scopes. Man definiert im Model einen Scope mit einer Einschränkung und kann im Controller oder von einem anderen Model auf den Scope zugreifen. So habe ich in meiner Applikation Scopes für Tracks definiert (Standard mit bonus=0 und Bonus mit bonus=1) und kann die Tracks in der Release-Einzelansicht in zwei verschiedenen Bereichen ausgeben.

Der Vorteil eines gut durchdachten Frameworks ist, dass man relativ schnell eine funktionierende Applikation bekommt. Auch wenn ich mich erst seit 2 Wochen mit Laravel beschäftige, habe ich meine Plan umsetzen können und die Applikation funktioniert hervorragend. Fazit: TYPO3 bleibt nach wie vor mein Steckenpferd, Laravel hat mir jedoch gut gefallen und ich könnte mir vorstellen, mich bei Bedarf da weiter einzuarbeiten.

LesMixer – Playlists für LesMills Kurse

Schon von ca. einem Jahr hatte ich die Idee zu einer (für mich sinnvollen) Anwendung. Als Hobby unterrichte ich Kurse im Fitnessstudio: BODYPUMP und BODYCOMBAT. Beides sind Fitnessprogramme von Les Mills. Das besondere: sie sind vorchoreografiert. Alle 3 Monate gibt es eine neue Release (neue Musik und neue Bewegungen), diese lerne ich dann und unterrichte sie. Nach ca. 6 bis 8 Wochen sorge ich für Abwechslung in den Kursen, in dem ich “mixe”, d.h. ich ersetze Tracks aus dem aktuellen Programm durch ältere Tracks. Beim Austauschen den Tracks gibt es einiges zu beachten, da der programmtypische Stundenablauf beibehalten werden soll. Jeder Track hat eine Funktion. So ist bei BODYPUMP der 2.Track immer Squats (d.h. es werden die Beine trainiert). Beim Austauschen des Tracks kann ich dann nur einen Squat-Track aus einer älteren Release verwenden. Dann sollten die ausgetauschten Lieder gemeinsam auch musikalisch Kontraste bilden und die Länge aller Lieder sollte zusammen eine bestimmte Zeit nicht überschreiten.

Bis vor kurzem mixte ich “manuell”. Ich überlegte mir, welche Lieder ich machen könnte, hörte kurz rein (klickte mich vorher durch Ordner) und überprüfte die Gesamtlänge der Auswahl. Also dachte ich mir, da kann ich doch eine kleine Anwendung schreiben, die mir die Liedauswahl erleichtert. Zunächst braucht man eine Datenbasis: ich muss bisherige Releases einpflegen können. Danach wäre es noch praktisch, diese Daten durchsuchen zu können: nach Künstler, Musiktitel oder Bewegungen.

Ich hätte es auch in TYPO3 implementieren können. Manchmal möchte ich meinen Horizont erweitern und ein neues Framework oder eine neue Anwendung kennenlernen. Das geht am besten mit Learning-by-Doing. Von einigen habe ich schon gehört, wie toll Laravel ist und wollte es selbst ausprobieren. Nachdem ich mir einen kurzen Überblick über Laravel verschafft habe (Video-Tutorials), ging es an die Umsetzung meiner Applikation names “LesMixer”.

Um mir die Datenpflege zu erleichtern, habe ich einige sinnvolle Funktionen eingebaut. Da ich die Musik in Ordner abgelegt hatte, konnte ich automatisch beim Auswählen eines Programms und einer Release die Mp3-Dateien im Ordner einlesen und zur Auswahl anbieten. Sobald eine Datei ausgewählt wird, werden Künstler, Titel und Länge (Mp3-Metadaten) und in die entsprechenden Felder eingesetzt. d.h. Tracks für eine neue Release einzupflegen ist denkbar einfach. Sobald die Tracks eingepflegt sind, kann man sie direkt in der Applikation abspielen. Die Suche oben durchsucht Künstler und Track-Titel.

Eine Playlist (Mix) zu erstellen, ist einfach. Sobald man ein Programm ausgewählt hat, werden die Tracktypen gelistet (der Ablauf ist ja immer gleich) und man kann für jeden Tracktyp einen konkreten Track auswählen. Irgendwann möchte ich diesen Vorgang automatisieren. Da ich ein Rating eingebaut habe, könnte ich mir vorstellen, in Zukunft eine Playlist generieren zu lassen unter Berücksichtigung bestimmter Einschränkungen.

Pagination Widget im Backend anpassen

Ich habe ein Modul implementiert, in dem bestimmte Datensätze gelistet werden. Über der Auflistung hatte ich einen Filter für die Datensätze implementiert und für die Paginierung das Fluid BE-Pagination Widget verwendet. Nun musste ich erstaunt feststellen, dass das BE-Paginate-Widget (be.widget.paginate) keine weiteren Argumente mitschleppen kann und es auch nicht konfigurierbar ist. Das bedeutet: beim Blättern gehen die Einstellungen aus dem Filter verloren.

Ich habe es hinbekommen mit einem eigenen ViewHelper und einem eigenen Template fürs Pagination-Widget.

(mehr …)

Tags: ,

Geschrieben in TYPO3 | Kommentare deaktiviert für Pagination Widget im Backend anpassen

Magnific Popup: springen von fix positioniertem Content

Wenn man Maginific-Popup zum Vergrößern von Bilder einsetzt, dann springen die fix positionierten Elemente beim Öffnen des Popups. Warum? Magnific Popup entfernt das Overflow Attribut des html-Elements und fügt stattdessen ein margin-right hinzu, das der Breite der Scrollbalken entspricht. Das schaltet das Scrolling der Seite aus. Ein Problem stellen die fix positionierten Elemente dar: sie springen leicht beim Öffnen des Popups. Wenn man bei fix positionierten Elementen diese entweder von rechts ausrichtet oder die Breite auf 100% setzt, dann ändert sich beim Öffnen des Popups die gesamte Breite der Seite, da die Scrollbalken entfernt werden. Die fix positionierten Elemente sind vom margin-right im html-Element wenig beeidruckt.

Lösung: Magnific Popup bietet viele Callbacks, die man nutzen kann, um die fixe Positionierung der Elemente zu korrigieren.

var magnificpopupSettings = {
        callbacks: {
            beforeOpen: function () {
                if($('nav.top-bar').css('position') == 'fixed') {
                    $('nav.top-bar').css('margin-right', '17px');
                    $('#top-link').css('margin-right', '17px');
                }
            },
            afterClose: function() {
                if($('nav.top-bar').css('position') == 'fixed') {
                    $('nav.top-bar').css('margin-right', '');
                    $('#top-link').css('margin-right', '');
                }
            }
        }
    };
};

Diskussion zum Thema: Jumping background

Geschrieben in javascript | Kommentare deaktiviert für Magnific Popup: springen von fix positioniertem Content

Übersetzung bestimmte Sprache im Fluid Template ausgeben (LocalizationUtility)

Für mein aktuelles Projekt sollte es etwas abgefahrenes sein: In einem Template sollen nebeneinander ein Wort in Sprache 1 und dann das gleiche Wort in Sprache 2 ausgegeben werden. Problem ist, das der <f:translate key="" /> ViewHelper immer nur mit der aktuellen Sprache arbeitet. Mein Ansatz ist also einen eigenen ViewHelper zu schreiben. Nun ruft der TranslateViewHelper (in einer statischen Funktion) die LocalizationUtility auf und diese liefert die Übersetzung. d.h. ich werde wohl auch eine eigene LocalizationUtility brauchen.

Mein ViewHelper soll in der Verwendung so aussehen: <my:translate key="lang[fr]:years" />
Ich kann/möchte kein weiteres Argument an den ViewHelper dranhängen, weil mein TranslateViewHelper dann nicht mehr von zum Teil statischen Core-TranslateViewHelper ableiten kann. Und der $key wird eh durchgereicht, also nutze ich den Key, um meine Sprache zu definieren.

Eigenen TranslateViewHelper implementieren, der “meine” LocalizationUtility nutzt

namespace My\Extension\ViewHelper;
use My\Extension\Utility\LocalizationUtility;
class TranslateViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\TranslateViewHelper
{
    protected static function translate($id, $extensionName, $arguments)
    {
        return LocalizationUtility::translate($id, $extensionName, $arguments);
    }
}

Dann muss “nur” noch die LocalizationUtility angepasst werden. In der translate-Funktion wird der $key geparst und falls er einen Hinweis auf eine konkrete Sprache enthält, wird diese in $overrideLanguageKey gesetzt. Wichtig ist keine der anderen Variablen zu ändern, denn sobald ich sie für ein Label ändere, gilt die Änderung für alle folgenden Labels. Später überprüfe ich, ob $overrideLanguageKey gesetzt ist, lade die Übersetzungen und liefere die angeforderte Übersetzung zurück. Das ist nicht der komplette Code, sondern nur die relevanten Auszüge.

<?php
namespace My\Extension\Utility;
class LocalizationUtility extends \TYPO3\CMS\Extbase\Utility\LocalizationUtility
{
    /**
     * @var string
     */
    protected static $overrideLanguageKey = '';
 
    /**
     * Returns the localized label of the LOCAL_LANG key, $key.
     *
     * @param string $key The key from the LOCAL_LANG array for which to return the value.
     * @param string $extensionName The name of the extension
     * @param array $arguments the arguments of the extension, being passed over to vsprintf
     * @return string|NULL The value from LOCAL_LANG or NULL if no translation was found.
     */
    public static function translate($key, $extensionName, $arguments = null)
    {
        if (GeneralUtility::isFirstPartOfStr($key, 'lang[')) {
            preg_match('#^lang\[(.*?)\]\:(.*?)$#', $key, $matches);
            // set key
            $key = $matches[2];
            self::$overrideLanguageKey = $matches[1];
        }
        [...]
            self::initializeLocalization($extensionName);
            /* NEW */
            $languageKey = self::$languageKey;
            if(self::$overrideLanguageKey) {
                self::initializeLocalizationFor($extensionName, self::$overrideLanguageKey);
                $languageKey = self::$overrideLanguageKey;
            }
            // The "from" charset of csConv() is only set for strings from TypoScript via _LOCAL_LANG
            if (!empty(self::$LOCAL_LANG[$extensionName][$languageKey][$key][0]['target'])
                || isset(self::$LOCAL_LANG_UNSET[$extensionName][$languageKey][$key])
            ) {
                // Local language translation for key exists
                $value = self::$LOCAL_LANG[$extensionName][$languageKey][$key][0]['target'];
            } elseif (!empty(self::$alternativeLanguageKeys)) {
            [...]
    }
 
    /**
     * @param string $extensionName
     * @param string $languageKey
     * @return void
     */
    protected static function initializeLocalizationFor($extensionName, $languageKey)
    {
        $locallangPathAndFilename = 'EXT:' . GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName) . '/' . self::$locallangPath . 'locallang.xlf';
        $renderCharset = TYPO3_MODE === 'FE' ? self::getTypoScriptFrontendController()->renderCharset : self::getLanguageService()->charSet;
 
        /** @var $languageFactory LocalizationFactory */
        $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
 
        self::$LOCAL_LANG[$extensionName] = $languageFactory->getParsedData($locallangPathAndFilename, $languageKey, $renderCharset);
    }
}

Im Template können schließlich beide TranslateViewHelper nebeneinander verwendet werden:

<f:translate key="years" />
<my:translate key="lang[fr]:years" />

Tags: ,

Geschrieben in TYPO3 | Kommentare deaktiviert für Übersetzung bestimmte Sprache im Fluid Template ausgeben (LocalizationUtility)

Hook-Implementierung für Linkhandler

Heute habe ich die Extension linkhandler (allerdings die neue Version) konfiguriert, damit die Redakteure in der Site auf Artikel und weitere von mir programmierte Dateitypen verlinken können. Die Konfiguration des Linkhandler in TsConfig ist sehr gut beschrieben, so ich mir hier die Konfiguration spare. Was allerdings gefehlt hat, war ein Beispiel für den beschriebenen Hook. Ich habe in dem Code unten jeweils Dummy-Namespaces und Extension-Namen eingesetzt, der Code funktioniert, muss aber modifiziert werden.

Damit man z.B. Links zu eigenen Datensätzen erstellen kann, braucht man ein wenig TsConfig:

TCEMAIN.linkHandler.tx_extension_myrecord {
    handler = Cobweb\Linkhandler\RecordLinkHandler
    label = Mein Datentyp
    configuration { ... }
    scanBefore = page
}

In ext_localconf.php wird der Hook registriert:

$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkhandler']['generateLink'][] = \My\Extension\Hooks\LinkhandlerParameterProcessor::class;

Dann erstellt man im Ordner Classes/Hooks der eigenen Extension die Datei LinkhandlerParameterProcessor.php mit folgendem Inhalt:

<?php
namespace My\Extension\Hooks;
 
use Cobweb\Linkhandler\ProcessLinkParametersInterface;
 
class LinkhandlerParameterProcessor implements ProcessLinkParametersInterface
{
 
    /**
     * @param \Cobweb\Linkhandler\TypolinkHandler $linkHandler Back-reference to the calling object
     * @return void
     */
    public function process($linkHandler) {
        $configurationKey = $linkHandler->getConfigurationKey();
        if($configurationKey == 'tx_extension_myrecord.') {
            $record = $linkHandler->getRecord();
            $additionalParams = '&tx_extension_test[parent]='.$record['parent'];
            $additionalParams.= '&tx_extension_test[record]='.$record['uid'];
            $additionalParams.= '&tx_extension_test[controller]=Test';
            $typolinkConf = $linkHandler->getTypolinkConfiguration();
            $typolinkConf['additionalParams'] = $additionalParams;
            $typolinkConf['title'] = $record['title'];
            $linkHandler->setTypolinkConfiguration($typolinkConf);
        }
    }
 
}

Mit $linkHandler->getConfigurationKey() kommt man an der Configuration Key – nämlich den, den man vorher im TsConfig verwendet hat. Es gibt einige Funktionen im LinkHandler wie getTableName() oder getLinkParameters(), von denen scheinbar keine verwendet wird. Somit bleibt nur der Weg über die Typolink Konfiguration: mit getTypolinkConfiguration() auslesen und mit setTypolinkConfiguration() wieder setzen.

In meinem Fall habe ich den Hook benötigt, weil ich an den Link nicht nur die UID des Datensatzes hängen musste, sondern auch noch einen weiteren Parameter aus dem Datensatz.

Nachtrag: Ich habe erst später festgestellt, das ich mein Problem auch komplett mit TypoScript lösen kann. Na ja, vielleicht hilft ja das Beispiel jemandem

plugin.tx_linkhandler {
    tx_extension_myrecord {
        typolink {
            title = {field:title}
            title.insertData = 1
            parameter = 74
            additionalParams = &tx_extension_test[parent]={field:parent}&tx_extension_test[record]={field:uid}&tx_extension_test[controller]=Test
            additionalParams.insertData = 1
            useCacheHash = 1
        }
    }
}

Tags: ,

Geschrieben in TYPO3 | Kommentare deaktiviert für Hook-Implementierung für Linkhandler

Name von fe_user beim Speichern im Backend aus first_name und last_name setzen

Ich glaube mich daran zu erinnern, dass es mal in irgendeiner Registrierungsextension implementiert war. Man wünscht sich, dass der Name eines FE Benutzers (fe_user) sich automatisch aus Vor- und Nachname ergibt, andernfalls sind die Felder ja redundant.

Im Backend kann man mit einem Hook realisieren.

Erstmal muss man das Namensfeld des fe_user auf readOnly setzen, damit man es im BE nicht bearbeiten kann. Das geht am besten in Configuration/TCA/Overrides/fe_users.php der eigenen Extension:

$GLOBALS['TCA']['fe_users']['columns']['name']['config']['readOnly'] = 1;

Dann definiert man einen Hooks für das Backend-Formular: in ext_localconf.php folgendes einsetzen (My\Extension entsprechend ersetzen):

$GLOBALS ['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['extkey'] = 'My\Extension\Hook\TceMain';

Dann im Ordner Classes/Hook der Extension die Datei TceMain.php erstellen und darin eine Funktion wie folgt definieren:

<?php
namespace My\Extension\Hook;
 
use TYPO3\CMS\Backend\Utility\BackendUtility;
 
class TceMain
{
 
    /**
     * @param $status
     * @param $table
     * @param $id
     * @param array $fieldArray
     * @param \TYPO3\CMS\Core\DataHandling\DataHandler $pObj
     */
    public function processDatamap_postProcessFieldArray($status, $table, $id, array &$fieldArray, \TYPO3\CMS\Core\DataHandling\DataHandler &$pObj)
    {
        if ($table == 'fe_users') {
            if (array_key_exists('first_name', $fieldArray) || array_key_exists('last_name', $fieldArray)) {
                $oldRow = BackendUtility::getRecord('fe_users', $id);
                $firstName = (array_key_exists('first_name', $fieldArray)) ? $fieldArray['first_name'] : $oldRow['first_name'];
                $lastName = (array_key_exists('last_name', $fieldArray)) ? $fieldArray['last_name'] : $oldRow['last_name'];
                $fieldArray['name'] = $firstName . ' ' . $lastName;
            }
        }
    }
 
}

Dabei wird beim Speichern des Datensatzes der Name automatisch aktualisiert, falls first_name oder last_name bearbeitet wurden. Diese Funktion funktioniert in beiden Fällen: wenn man nur einen Datensatz vollständig bearbeitet oder wenn man mehrere Datensätze gleichzeitig bearbeitet.

Tags: , ,

Geschrieben in TYPO3 | Kommentare deaktiviert für Name von fe_user beim Speichern im Backend aus first_name und last_name setzen

Landauswahl aus static_info_tables in Extension femanager

Für die Benutzerregistrierung nutze ich die Extension femanager, die sich hervorragend konfigurieren lässt.

Im Extension Manual ist beschrieben, wie man die static_info_tables Tabellen als Quelle für die Länderauswahl einsetzen kann. Das funktioniert erstmal nur im Frontend. Wenn man die Templates so anpasst, wie im Manual beschrieben, dann landet der 3-stellige Iso Code des Landes in der Datenbank.

Nun fände ich es auch schön, wenn die entsprechende Auswahl auch im Backend benutzt werden würde. Und so kann man es konfigurieren:

Als erstes muss das TCA der fe_users Tabelle entsprechend umkonfiguriert werden. Dazu in einer eigenen Extension entweder in ext_tables.php oder (besser) in Configuration/Tca/Overrides/fe_users.php folgenden Code einfügen. Damit wird die Länderauswahl mittels itemProcFunc erstellt. In diesem Fall kann man leider nicht einfach ‘foreign_table’ verwenden, da als Key automatisch die uid verwendet wird.

$GLOBALS['TCA']['fe_users']['columns']['country']['config'] = [
    'type' => 'select',
    'renderType' => 'selectSingle',
    'itemsProcFunc' => 'My\Extension\UserFunc\TcaProcFunc->staticInfoTablesItems',
    'maxitems' => 1
];

Dann legt man die Klasse TcaProcFunc in Classes/UserFunc an mit folgendem Inhalt:

<?php
namespace My\Extension\UserFunc;
 
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
 
class TcaProcFunc
{
 
    /**
     * @param array $config
     * @return array
     */
    public function staticInfoTablesItems($config)
    {
        $key = 'isoCodeA3';
        $value = 'shortNameLocal';
        $sortbyField = 'isoCodeA3';
        $sorting = 'asc';
 
        $objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\ObjectManager');
        $countryRepository = $objectManager->get('SJBR\StaticInfoTables\Domain\Repository\CountryRepository');
 
        $countries = $countryRepository->findAllOrderedBy($sortbyField, $sorting);
        $countryList = [];
        $countryList[] = ["", ""];
        foreach ($countries as $country) {
            /** @var $country \SJBR\StaticInfoTables\Domain\Model\Country */
            $countryList[] = [ObjectAccess::getProperty($country, $value), ObjectAccess::getProperty($country, $key)];
        }
        $config['items'] = $countryList;
        return $config;
    }
 
}

Der Code der Function ist aus dem ViewHelper GetCountriesFromStaticInfoTablesViewHelper aus der Extension femanager geklaut 🙂

Tags: ,

Geschrieben in TYPO3 | Kommentare deaktiviert für Landauswahl aus static_info_tables in Extension femanager

Vorschau von Plugin-Einstellungen im Backend in TYPO3

Wenn man eingene Plugins implementiert und dabei mit Flexforms arbeitet, kann man bei einem eingesetzen Plugin im Backend nicht erkennen, welche Einstellungen im Flexform vorgenommen wurden. Es gibt in TYPO3 einen Hook, der es ermöglicht, sich in die Backend-Ansicht einzuklinken und diese Einstellungen dort auszugeben. Und so wird das implementiert:

Als erstes muss man den Hook registrieren in ext_localconf.php der eigenen Extension. Der Name der Klasse ist egal. Unter $pluginSignature gibt man den Namen des Plugin an, so wie man ihn registriert hat.

if (TYPO3_MODE === 'BE') {
    // Page module hook - show flexform settings in page module
    $extensionName = \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToUpperCamelCase($_EXTKEY);
    $pluginSignature = strtolower($extensionName) . '_pi';
    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info'][$pluginSignature][$_EXTKEY] =
        'My\Extension\Hook\CmsLayout->getExtensionSummary';
}

Dann erstellt man die entsprechende Klasse. In meinem Fall die Klasse CmsLayout im Ordner Classes/Hook. Diese Klasse muss die Funktion getExtensionSummary haben, die als Parameter ein Array übergeben bekommt. Die Funktion gibt einen String zurück, der dann im Backend als Plugin-Zusammenfassung ausgegeben wird.

Dabei ist zu beachten, dass der Hook für alle Plugins aufgerufen wird, d.h. man muss selbst rausfinden, ob man sich in seinem Plugin befindet oder nicht.

Bei mir hat sich mittlerweile dieser Klassenaufbau etabliert:

namespace My\Extension\Hook;
class CmsLayout
{
    public function getExtensionSummary(array $params) {
        $result = null;
        if (strpos($params['row']['list_type'], 'myextension_') !== FALSE) {
            $pluginName = str_replace('myextension_', '', $params['row']['list_type']);
            if ($params['row']['pi_flexform'] != '') {
                $this->flexformData = \TYPO3\CMS\Core\Utility\GeneralUtility::xml2array($params['row']['pi_flexform']);
            }
            $methodName = 'bePreview' . ucfirst(GeneralUtility::underscoredToLowerCamelCase($pluginName));
            if (method_exists($this, $methodName)) {
                $result = $this->$methodName($params['row']);
            }
        }
        return $result;
    }
 
    protected function getFieldFromFlexform($key, $sheet = 'sDEF') {
        $flexform = $this->flexformData;
        if (isset($flexform['data'])) {
            $flexform = $flexform['data'];
            if (is_array($flexform) && is_array($flexform[$sheet]) && is_array($flexform[$sheet]['lDEF'])
                && is_array($flexform[$sheet]['lDEF'][$key]) && isset($flexform[$sheet]['lDEF'][$key]['vDEF'])
            ) {
                return $flexform[$sheet]['lDEF'][$key]['vDEF'];
            }
        }
 
        return NULL;
    }
 
    protected function bePreviewPi($row) {
        // whatever
        return '';
    }
}

In der Funktion getExtensionSummary werden die Flexform-Daten geparst, falls es sich um eines meiner Plugins handelt. Dann wird die Preview-Funktion aufgerufen, falls sie existiert. Damit spare ich mir if- oder switch-Abfragen.

Hinweis 8.5.: Ich habe mitbekommen, dass sich die Flexform-Definition in der TYPO3 Version 8 geändert hat. d.h. es müssten entsprechend Anpassungen an diesem Code vorgenommen werden.