Beiträge der Kategorie TYPO3

FlexformDataProcessor für Custom Content Elements

Um individuelle Inhaltselemente zu erstellen, nutze ich bereits seit Jahren gerne Mask (zum Teil mit mask_export) und gridelements. So toll mask auch ist, es hat seine Grenzen und dann erstelle ich auch mal komplett eigene Inhaltselemente – „from scratch“ sozusagen. Eine gute Anleitung hierfür gibt es bei TYPO3 Explained.

Wenn es um irgendwelche kleinen Einstellungen geht (Farbe, Ausrichtung etc.), dann finde ich es manchmal etwas übertrieben, dafür direkt ein Datenbankfeld zu generieren, vor allem, wenn man diese Einstellungen nur in einem Inhaltselement braucht. Was ich auch niemals mache, ist andere Felder zu zweckentfremden. Spätestens dann, wenn das Originalfeld benötigt wird, hat man ein Problem.

Genau für solchen Zweck gibt es bei TYPO3 die Flexforms. Sie können genauso konfiguriert werden wie TCA, ermöglichen das anlegen von Feldern in Tabs und sind insgesamt sehr flexibel (pun intended). In der Ausgabe hat man dann allerdings das Problem, dass alle Flexform-Werte in einem XML-Konstrukt stehen. Zum Glück hat TYPO3 die Data Prozessoren. Meine Recherche (vor ein paar Monaten) hat keinen fertigen FlexformDataProcessor zutage gebracht, so dass ich kurzerhand meinen eigenen geschrieben habe.

<?php
namespace Vendor\MyExtension\DataProcessing;
 
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
 
class FlexformDataProcessor implements DataProcessorInterface
{
    public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData)
    {
        if (empty($processedData['data']['pi_flexform'])) {
            return $processedData;
        }
 
        $flexformData = GeneralUtility::xml2array($processedData['data']['pi_flexform']);
        $flexformProcessed = [];
        foreach ($flexformData['data'] as $sheetKey => $sheetValue) {
            foreach ($sheetValue['lDEF'] as $fieldKey => $fieldValue) {
                $key = $sheetKey . '_' . $fieldKey;
                $flexformProcessed[$key] = $fieldValue['vDEF'];
            }
        }
 
        $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'flexform_data');
        $processedData[$targetVariableName] = $flexformProcessed;
 
        return $processedData;
    }
}

Und so kann man den Flexform Data Processor dann einsetzen:

tt_content.mycontentelement = FLUIDTEMPLATE
tt_content.mycontentelement {
    dataProcessing.10 = Vendor\MyExtension\DataProcessing\FlexformDataProcessor
    dataProcessing.10 {
        as = flexform_data
    }
}

Auf das Ergebnis kann man dann im Template mit {flexform_data} zugreifen.

Datensatz-Titel in Breadcrumb-Navigation anzeigen

Vor TYPO9 war es zwar nicht einfacher, den Titel des eigenen Datensatzes in der Breadcrumb (oder auch Rootline) Navigation anzuzeigen, aber es gab Beispiele wie Sand am Meer. Falls man nun bei der Generierung der Menüs auf Data Processoren setzt, dann findet man (also ich per Google) einige Anfragen in Foren, jedoch keine gute Anleitung. Und wie immer in solchen Fällen schaue ich in die Extension news, die mir schon so häufig weitergeholfen hat. Diese Extension ist einfach auf dem neuesten Stand und wenn man mal nicht weiter weiß, dann lohnt sich immer ein Blick in diese Extension. Danke Georg!

Das ist eine kleine Anleitung, wie man die Breadcrumb um den Titel der eigenen Datensätze bereichert. Jede Extension ist anders implementiert, daher ist das nicht als Copy&Paste einfach verwendbar.

Als erstes muss man einen Data Processor anlegen, das kann entweder in der Extension sein, deren Datensätze man anzapft oder in einer anderen. In diesem Beispiel lege ich die Datei AddRecordToMenuProcessor.php an im Unterordner Classes/DataProcessing.

Irgendwo in TypoScript hat man nun die Page-Definition, die an irgendeiner Stelle so aussieht:

page.10 = FLUIDTEMPLATE
page.10 {
    dataProcessing {
        [...]
 
        30 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
        30 {
            special = rootline
            special.range = 0|-1
            includeNotInMenu = 1
            as = menuBreadcrumb
        }
    }
}

Hinter die vorhandenen Data Processoren wird nun ein eigener angehängt:

page.10 = FLUIDTEMPLATE
page.10 {
    dataProcessing {
        [...]
 
        40 = Vendor\MyExtension\DataProcessing\AddRecordToMenuProcessor
        40.menus = menuBreadcrumb
    }
}

Nun arbeitet man in der angelegten Datei weiter.

namespace Vendor\MyExtension\DataProcessing;
 
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
 
class AddRecordToMenuProcessor implements DataProcessorInterface
{
 
    /**
     * @param ContentObjectRenderer $cObj
     * @param array $contentObjectConfiguration
     * @param array $processorConfiguration
     * @param array $processedData
     * @return array
     */
    public function process(
        ContentObjectRenderer $cObj,
        array $contentObjectConfiguration,
        array $processorConfiguration,
        array $processedData
    ) {
        if (!$processorConfiguration['menus']) {
            return $processedData;
        }
        if (!ExtensionManagementUtility::isLoaded('my_record_extension')) {
            return $processedData;
        }
        $record = $this->getRecord();
        if ($record) {
            $menus = GeneralUtility::trimExplode(',', $processorConfiguration['menus'], true);
            foreach ($menus as $menu) {
                if (isset($processedData[$menu])) {
                    $this->addRecordToMenu($record, $processedData[$menu]);
                }
            }
        }
        return $processedData;
    }
 
    public function addRecordToMenu($record, array &$menu) {
        // remove last element
        array_pop($menu);
 
        $menu[] = [
            'data' => $record,
            'title' => $record['name'],
            'active' => 1,
            'current' => 1,
            'link' => GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'),
            'isRecord' => true
        ];
    }
 
    /**
     * @return array
     */
    public function getRecord()
    {
        $vars = GeneralUtility::_GET('tx_myrecordextension_pluginname');
        if(!isset($vars['record']) {
            return;
        }
        $recordUid = (int)$vars['record'];
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
        $recordRepository = $objectManager->get(Vendor\MyRecordExtension\RecordRepository::class);
        $record = $recordRepository->findByUid($recordUid);
        $fields = ['name', 'title', 'email', 'phone'];
        $recordAsArray = [];
        foreach ($fields as $field) {
            $recordAsArray[$field] = $record->_getProperty($field);
        }
        return $recordAsArray;
    }
 
}

Die Funktion process ist notwendig, da man das Interface DataProcessorInterface implementiert. In meinem Fall hab ich den Data Processor in eine andere Extension packen müssen und daher brauche ich die Abfrage, ob die Extension geladen ist (Zeile 28). Ansonsten kann man sich das sparen.

Es wird versucht einen Datensatz der Extension zu holen und falls es funktioniert, wird das Menü um einen Eintrag mit den Daten des Datensatzes erweitert.

In der Funktion getRecord wird der Datesatz geholt. Georg nutzt in seiner news-Extension einen anderen Ansatz: nämlich mit ConnectionPool und QueryBuilder. Es hat den Vorteil, dass man ein Array und nicht wie in meinem Fall ein Object zurückbekommt. Daher brauche in dann die Konvertierung in ein Array. Es ist bestimmt nicht der beste Weg ein Objekt in ein Array zu konvertieren. Ich habe bisher nur nichts anderes gefunden, bin daher für Tipps in diese Richtung dankbar.

In der Funktion addRecordToMenu wird das Menü erweitert. Da habe ich auch einen etwas anderen Ansatz als in news. Dort wird das Menü erweitert. Ich schmeiße den letzten Eintrag raus und füge stattdessen meinen hinzu. Das hat aus meiner Sicht den Vorteil, dass man keinen Link zur Detailseite ohne Parameter hat. Den Ansatz aus News also dann verwenden, wenn die Detailansicht die Listenansicht überlädt. Falls man so wie in meinem Fall, eine separate Detailseite hat, die ohne Parameter keinen Sinn macht oder womöglich gar nicht funktioniert, dann muss man den Ansatz mit rausschmeißen-hinzufügen verfolgen.

Geschrieben in TYPO3, TYPO3 v9 | Kommentare deaktiviert für Datensatz-Titel in Breadcrumb-Navigation anzeigen

Fehlermeldungen in Fluid / Extbase Formularen

Folgendes Setup: Model mit Validatoren bei einigen Properties und ein Formular, um ein neues Objekt dieses Typs anzulegen.

Das Formular kann wie folgt definiert werden:

<f:form action="create" object="{my}" objectName="my" method="post">
    <f:render partial="Form/AllErrors" arguments="{_all}" />
    <label for="foo" class="">Label</label>
    <div class="formWrap">
        <f:form.textfield property="foo" id="foo" />
    </div>
</f:form>

Sobald man für das Formular object und objectName definiert, kann man mit dem Attribut property in den Feldern arbeiten, dabei wird der Wert falls vorhanden automatisch ausgefüllt. Wenn nun während der Validierung Fehler auftreten, dann werden in diesem Beispiel die Fehler ALLE ausgegeben mit dem folgenden Partial:

<f:form.validationResults>
    <f:if condition="{validationResults.flattenedErrors}">
        <div class="errors">
            <f:for each="{validationResults.flattenedErrors}" as="errors" key="propertyPath">
                <ul>
                    <f:for each="{errors}" as="error">
                        <li>{propertyPath}: <span>{error}</span></li>
                    </f:for>
                </ul>
            </f:for>
        </div>
    </f:if>
</f:form.validationResults>

Es wäre natürlich schöner, wenn die Fehlermeldung am Feld dran stehen würde und nicht irgendwo am Anfand des Formulars. Man kann dem Tag f:form.validationResults durch das Argument for mitteilen, für welche Property man die Fehler haben möchte. Und so sieht das geänderte Formular nun so aus:

<f:form action="create" object="{my}" objectName="my" method="post">
    <label for="foo">Label</label>
    <div class="formWrap">
        <f:form.textfield property="foo" id="foo" />
        <f:form.validationResults for="my.foo">
            <f:render partial="Form/FormFieldError"
                      arguments="{validationResults: validationResults}"/>
        </f:form.validationResults>
    </div>
</f:form>

Das Partial dazu sieht so aus:

<f:if condition="{validationResults.errors}">
    <f:for each="{validationResults.errors}" as="error">
        <div class="error-note">
            <small class="has-error">{error.message}</small>
        </div>
    </f:for>
</f:if>

In meinem Fall sollte zusätzlich eine Klasse beim Label gesetzt werden, wenn es zu Validierungsfehlern kommt. Der validationResults ViewHelper ist aber keine Condition, man kann ihn auch nicht inline mit der Klasse „füttern“. Der validationResults ViewHelper setzt nur die Fehler für die entsprechende Property in den Template Variable Container und gibt danach die Kinder aus. Die Kinder können dann auf die entsprende Variable zugreifen, also ein bisschen wie f:alias oder f:map. Und so sieht der Formular-Code am Ende aus:

<f:form action="create" object="{my}" objectName="my" method="post">
    <f:form.validationResults for="my.foo">
        <label for="form-customer_id"
               class="{f:if(condition: validationResults.errors, then: 'has-error')}">Label</label>
        <div class="formWrap">
            <f:form.textfield property="foo" id="foo" />
            <f:render partial="Form/FormFieldError"
                      arguments="{validationResults: validationResults}"/>
        </div>
    </f:form.validationResults>
</f:form>

Geschrieben in TYPO3, TYPO3 v8 | Kommentare deaktiviert für Fehlermeldungen in Fluid / Extbase Formularen

I love TYPO3

Auf dem Camp (Bericht hier) hatte Jochen Weiland aufgefordert, mehr über TYPO3 zu schreiben. Der Aufhänger für diese Aufforderung war: er berichtete über Kommentare von TYPO3-Außenseitern der Art „TYPO3 ist doch schon tot“. Tja, totgeglaubte leben länger.

Ich arbeite mit TYPO3 seit Version 3.7, das ist finsteres Mittelalter. Bereits im Informatik-Studium mit viel C, C++ und Java habe ich Gefallen an PHP und MySQL gefunden. Wir haben damals Informix SQL gelernt und als ein Kommilitone MySQL erwähnte, war der Kommentar des Professors „die halten sich nicht lange“. Tja, siehe oben. PHP war damals rein funktionsbasiert, wahrscheinlich sah ich das Potential schon damals – ähm. Für meine Diplomarbeit schrieb ich eine Web-Applikation mit dem frisch erschienenen PHP 5 – es gab Klassen. OOP! Irgendwann gegen Ende des Studiums erzählte mir ein Kommilitone von einem verrückten Dänen, der im Keller ein CMS geschrieben hat und das sei richtig geil. Als ich ein paar Monate später eine Seite umsetzen musste, wo der Kunde seine Inhalte selbst bearbeiten kann, fiel mir dieses Gespräch wieder ein und so lernte ich TYPO3 kennen.

Jahre vergingen, ich habe mich mal mehr, mal weniger mit TYPO3 beschäftigt. Nach einer TYPO3 Agentur wechselte ich zu einer Agentur, die kein TYPO3 anbot und vermisste es nach einiger Zeit. TYPO3 krebste lange in der Version 4 herum, weil die Version 5 ja was neues werden sollte. Nach außen sah das so aus, als würde es da nicht voran gehen. Das Backend war nach wie vor altbacken, andere CMS spossen aus dem Boden (WordPress, Contao), dagegen sah TYPO3 alt aus. Da durfte ich mir anhören „TYPO3 wird nicht mehr lange geben“ und wurde irgendwie traurig.

Dann kam Version 6 – Namespaces, Extbase und Fluid – endlich! Seitdem gibt es regelmäßig Releases, Updates, neue Technologien werden eingebaut und TYPO3 macht mehr Spaß denn je. Was ist an TYPO3 liebe, ist die Flexibilität bei sauberer Datenbank-Struktur und hoher Code-Qualität. Ok, es gibt im Core nach wie vor ein paar dunkle Ecken, die immer noch eingestaubt und gruselig sind. Dafür ist der aufgeräumte Rest genial. Ich bin überzeugt, dass es nichts gibt, was man mit TYPO3 nicht umsetzen könnte. Gelegentlich schaue ich über den Tellerrand – ich habe schonmal mit anderen CMS (Drupal, WordPress, Contao) und Frameworks (CakePHP, Laravel) gearbeitet, um dann mit einem guten Gefühl weiter mit TYPO3 arbeiten zu können.

Ja, die Einstiegshürde ist hoch – TypoScript. Gut, wo ist denn bitte keine Einstiegshürde? Ich gehe doch auch nicht zur Fahrschule, setze mich ans Steuer und kann fahren? Für viele Dinge im Leben muss man lernen, Erfahrung sammeln, manchmal Frustrationen aufbauen, um voran zu kommen. TYPO3 hat den Vorteil, dass es gut dokumentiert ist und dass es eine Community gibt, die einem helfen kann. Beispiele gibt es wie Sand am Meer und Google hilft ja auch weiter, damals im finsteren Mittelalter war das noch anders.

Dann höre ich manchmal, TYPO3 ist zu kompliziert für Redakteure. Meine Antwort: das System ist falsch eingerichtet. TYPO3 hat ein geniales Benutzermanagement mit einer differenzierten Rechteverwaltung. d.h. nicht TYPO3 ist Schuld, es ist nur falsch konfiguriert. Ich kann verstehen, dass Frau Müller überfordert ist, wenn Sie 80 Buttons hat, die sie klicken kann und eine Menüleiste, bei der sie scrollen muss. Da ist es die Aufgabe des Entwicklers/Integrators TYPO3 entsprechend einzurichten und Frau Müller nur die 5 Buttons und 2 Menüpunkte zu geben, die sie braucht.

Und zum Schluß – seit einigen Jahren gibt es die TYPO3 Zertifizierung. Welches andere CMS hat das bitte? „The TYPO3 certification program is a global standard for TYPO3 knowledge“[1]. Wenn man also mit einem TYPO3 zertifizierten Entwickler zusammen arbeitet, dann ist zumindest sicher gestellt, dass er die API kennt. Da ich die Prüfung abgelegt habe, weiß ich, dass sie nicht ohne ist und dass ich trotz jahrelanger TYPO3 Erfahrung noch einiges lernen musste.

So, genug geschwärmt – wieder an die Arbeit. Mit TYPO3 natürlich 😉

[1] https://typo3.org/certification/

TYPO3 Camp Rhein-Ruhr 2019

An diesem Wochenende fand das TYPO3 Camp Rhein-Ruhr statt. Ich war zum 4. oder 5. Mal dort und fand es mal wieder toll. Zunächst mal ist es schön, wenn man bekannte Gesichter wieder trifft oder jemanden persönlich kennenlernt, von dem man bisher nur gelesen oder mit dem man nur geschrieben hat. Die Auswahl an Talks ist phantastisch – am Samstag wurde in 6 Räumen von 12 bis 19 Uhr stündlich vorgetragen, vorgestellt oder diskutiert. Abends ab 19 Uhr dann „Get together“ (wie das so schön neudeutsch heißt) und gegen 9 dann Whisky-Tasting. Ja, richtig – Whisky. Kein Billigfusel mit Cola. Ich gestehe, dass ich bei meinem ersten Camp-Besuch damals überhaupt auf den Geschmack von Whisky gekommen bin. Man braucht nur den richtigen Einstieg, etwas Hintergrundinformationen und Möglichkeiten, verschiedene Whiskys zu probieren. Mittlerweile freue ich mich Monate im Voraus auf das nächste Camp und Whisky.

Sehr gut fand ich dieses Jahr die Möglichkeit, statt T-Shirt den Betrag zu spenden. Der Betrag aller T-Shirt-Spenden wurde noch erhöht und geht an eine Organistion, die Kita-Kinder mit frischem, regionalem Essen versorgt.

Die Sponsoren hatten auch echt geniale Goodies beigelegt: ein Flachmann mit „flush cashes“ Aufdruck. Wir haben beim Informatik-Studium damals beim Trinken auch immer gewitzelt, dass wir die „Caches flushen“ oder „Format C:“. Man konnte sich auch ein Lego-Männchen (in meinem Fall Lego-Weibchen) zusammenbasteln – mit TYPO3 Logo auf der Brust. Ich bin ja eh etwas Lego-verrückt…

Die Talks waren fast alle für Developer – gut für mich. Helmut lieferte eine Einführung in Composer und ich dufte ihn danach mit ein paar Fragen löchern. Ich nutze Composer in (allen) meinen Projekten, und es schmerzt fast, wenn es nicht zur Verfügung steht. Weiterhin habe ich mir vorgenommen nach dem Camp, Rector PHP und Webpack Encore auszuprobieren – beides vorgestellt von Sebastian Schreiber.

Rector PHP ist ein Tool, dass automatische Code-Updates ermöglicht. Dabei kann der Code semantisch korrigiert und auf fehlenden/veraltete Funktionen überprüft werden. Um im TYPO3 Umfeld zu bleiben – früher t3lib_div, wurde es dann zu GeneralUtility. Oder: bei ViewHelpern konnte man noch in Version 7 die Argumente der Funktion render übergeben. Ab Version 9 ist das Registrieren der Argumente in registerArguments Pflicht. Rector PHP kann den Code entsprechend umschreiben!

Andere Talks (Depencancy Injection, Quo vadis Extbase) geben einen Überblick darüber, was sich ändert in TYPO3 in den nächsten Versionen und worauf man achten sollte – hilfreich auf jeden Fall.

Und so fuhr ich gestern nach Hause, mein Kopf voller neuer und interessanter Informationen und freue mich ab jetzt wieder auf das Camp nächstes Jahr. Und Whisky 😉

TYPO3 Fluid-Templates und PhpStorm

Manchmal hab ich folgendes Problemchen: ich möchte z.B. falls Bedingung X eintrifft einen öffnenden Tag ausgeben und bei Bedingung Y einen schließenden Tag. PhpStorm fängt dann an, die Tags zu korrigieren, damit das Template aus seiner Sicht eine gültige Struktur hat. Ich habe bei meinem TYPO3-Kollegen Thomas Deuling einen einfachen Trick gesehen, wie man PhpStorm austricksen kann. Man schreibt die Tags einfach in !
Hier ein Beispiel für ein Media/Gallery Partial:

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
      xmlns:ce="http://typo3.org/ns/TYPO3/CMS/FluidStyledContent/ViewHelpers" data-namespace-typo3-fluid="true">
<f:if condition="{gallery.rows}">
    <f:for each="{gallery.rows}" as="row">
        <f:if condition="{gallery.rows -> f:count()} > 1">
            <f:format.raw value="<div class='row'>"/>
        </f:if>
        <f:for each="{row.columns}" as="column">
            <f:if condition="{row.columns -> f:count()} > 1">
                <f:format.raw value="<div class='col'>"/>
            </f:if>
            <f:if condition="{column.media}">
                <f:render partial="Media/Type"
                          arguments="{file: column.media, dimensions: column.dimensions, data: data, settings: settings}"/>
            </f:if>
            <f:if condition="{row.columns -> f:count()} > 1">
                <f:format.raw value="</div>"/>
            </f:if>
        </f:for>
        <f:if condition="{gallery.rows -> f:count()} > 1">
            <f:format.raw value="</div>"/>
        </f:if>
    </f:for>
</f:if>
</html>

Es hat den kleinen Nachteil, dass man selbst den Code etwas schlechter lesen kann, aber wenigstens stimmt die Struktur und PhpStorm versucht nicht die Tags zu korrigieren.

Vorschau von Bildern aus Page Media im Backend

Es hat mich ein wenig gewundert, dass es keine Extension gibt, die das anbietet. Ich nutze das Feld „Media“ unter Resources in den Seiteneingenschaften bei fast allen Projekten. Und gestern ist mir eingefallen, dass es ja eigentlich cool wäre, wenn man das eingepflegte Bild auch im Backend sehen könnte. In der TYPO3 Facebook-Gruppe hat die Nachfrage nach einer Extension nicht zum gewünschten Ergebnis geführt. Also beschloss ich (auch zu Übungszwecken) eine solche Extension selbst zu schreiben. Hier mein Kochrezept und Ergebnis.

Als erstes erstellt man einen Extensionordner mit einer ext_emconf.php. Meine Extension heißt übrigens np_pageresourcepreview.

In TYPO3 gibt es ja mittlerweile Hooks für so ziemlich alles. So auch einen um die Seite im Backend zu rendern. Man registrierert den Hook aus der neuen Extension in ext_localconf.php.

<?php
defined('TYPO3_MODE') || die('Access denied.');
 
if (TYPO3_MODE === 'BE') {
 
    // Hook into the page module
    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'][$_EXTKEY] =
        \Npostnik\NpPageresourcepreview\Hook\PageHook::class . '->render';
}

Im Ordner Classes/Hook erstellt man eine Datei PageHook.php und schreibt folgendes rein:

<?php
namespace Npostnik\NpPageresourcepreview\Hook;
 
use TYPO3\CMS\Backend\Controller\PageLayoutController;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
 
class PageHook
{
 
    /**
     * @var StandaloneView
     */
    protected $view;
 
    public function render(array $params, PageLayoutController $parentObject)
    {
        $pageinfo = $parentObject->pageinfo;
        if($pageinfo['media'] == 0) {
            return '';
        }
        $fileRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\FileRepository::class);
        $fileObjects = $fileRepository->findByRelation('pages', 'media', $pageinfo['uid']);
 
        //load partial paths info from typoscript
        $this->view = GeneralUtility::makeInstance(StandaloneView::class);
        $this->view->setFormat('html');
 
        $resourcesPath = 'EXT:np_pageresourcepreview/Resources/';
        $this->view->setTemplatePathAndFilename($resourcesPath . 'Private/Templates/PageHook.html');
        $this->view->assign('files', $fileObjects);
        $this->view->assign('page', $parentObject->pageinfo);
        return $this->view->render();
    }
 
}

Und schließlich erstellt man im Ordner Resources/Private/Templates die Datei PageHook.html mit folgendem Inhalt:

<f:if condition="{page}">
    <f:for each="{files}" as="file">
        <f:image image="{file}" maxHeight="300" />
    </f:for>
</f:if>

Das ist schon alles. Installieren und feddich.

Datepicker im TYPO3 Backend

Eigentlich ist es total simpel, ich bin jedoch fast verzweifelt. Im Template braucht man folgenden Schnipsel:

<f:form.textfield type="datetime" property="timeRestriction" id="timeRestriction" class="form-control t3js-datetimepicker t3js-clearable" data="{date-type:'datetime',date-offset:'0'}" />
<span class="input-group-btn">
	<label class="btn btn-default" for="timeRestriction">
		<span class="fa fa-calendar"></span>
	</label>
</span>

Dann (da war mein Fehler) muss man im Controller das JavaScript für den Datepicker aktivieren:

    /**
     * Set up the doc header properly here
     *
     * @param ViewInterface $view
     */
    protected function initializeView(ViewInterface $view)
    {
        $pageRenderer = $this->view->getModuleTemplate()->getPageRenderer();
        $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DateTimePicker');
    }

Formhandler Vorbelegung der Felder aus Attributen der Seite

Der Formhandler gilt als veraltet, schade eigentlich. Zum Glück hat sich Phorax dessen angenommen und stellt in einem Fork einen zu TYPO3 8 kompatiblen Formhandler bereit.

In meinem aktuellen Projekt sollen Auswahlfelder in einem Formular vorbelegt werden. Dabei kann der Redakteur im Backend in den Eigenschaften der Seite einstellen, was vorausgewählt werden soll.

Ich habe die Seite um die notwendigen Felder erweitert. In der Formhandler-Konfiguration kann mittels TypoScript auf diese Werte zugegriffen werden, um die Felder vorzubelegen.

plugin.Tx_Formhandler.settings.predef.myform {
 
    preProcessors {
        1.class = PreProcessor\LoadDB
        1.config {
            1 {
                meinfeld1.mapping = tx_myext_field1_preselect
                meinfeld2.mapping = tx_myext_field2_preselect
            }
 
            select {
                table = pages
                where.data = page:uid
                where.intval = 1
                where.wrap = uid=|
            }
        }
    }
}

Tags:

Geschrieben in TYPO3, TYPO3 v8 | Kommentare deaktiviert für Formhandler Vorbelegung der Felder aus Attributen der Seite

TYPO3 Order by Uid List in Repository

Ich habe ein Flexform mit einem Feld, in dem man Datensätze auswählen kann, die man berücksichtigen möchte. Nun sollen diese Datensätze in der Reihenfolge selektiert werden, die man im Flexform gewählt hat. Wie das Flexform zu konfigurieren ist, spare ich mir hier. Die Abfrage im Repository ist jedoch interessant. Theoretisch war es früher möglich (vor Doctrine) mit einem ORDER BY FIND_IN_SET die Elemente direkt in der richtigen Reihenfolge aus der Datenbank zu holen. Vielleicht geht es doch immer noch irgendwie (immerhin habe ich es geschafft, Elemente zufällig zu sortieren), in meinem Beispiel soll es eine PHP Funktion tun.

Und das ist mein Code: kurz und fein, wie ich finde.

public function findByUidOrdered($uidList = [])
{
    $query = $this->createQuery();
    if(count($uidList) > 0) {
        $query->matching($query->in('uid', $uidList));
    }
    $result = $query->execute()->toArray();
    usort($result, function($a, $b) use ($uidList) {
        $aIndex = array_search($a->getUid(), $uidList);
        $bIndex = array_search($b->getUid(), $uidList);
        return ($aIndex < $bIndex) ? -1 : 1;
    });
    return $result;
}

Funktioniert wunderbar in TYPO3 8.7.

Geschrieben in TYPO3, TYPO3 v8 | Kommentare deaktiviert für TYPO3 Order by Uid List in Repository