Beiträge der Kategorie TYPO3 v8

Lösung für Bilder im Flexform ohne Alternative-Feld

Für ein Custom Content Element kann in einem Feld Überschrift und ein Text eingegeben werden und ein Bild verknüpfte werden. Die Felder sind alle im Flexform definiert und der Kunde bemängelte (zurecht), dass die Möglichkeit zur Eingabe des Alternative Text beim Bild fehle. Kurz gesucht und einen Eintrag im Bezug auf TYPO3 6 gefunden, der unter TYPO3 9 immer noch funktioniert.

So wird das Bild im Flexform definiert:

<image>
    <TCEforms>
        <label>Bild</label>
        <config>
            <type>inline</type>
            <maxitems>1</maxitems>
            <minitems>1</minitems>
            <foreign_table>sys_file_reference</foreign_table>
            <foreign_table_field>tablenames</foreign_table_field>
            <foreign_label>uid_local</foreign_label>
            <foreign_sortby>sorting_foreign</foreign_sortby>
            <foreign_selector>uid_local</foreign_selector>
            <foreign_selector_fieldTcaOverride type="array">
                <config>
                    <appearance>
                        <elementBrowserType>file</elementBrowserType>
                        <elementBrowserAllowed>jpg,png</elementBrowserAllowed>
                    </appearance>
                </config>
            </foreign_selector_fieldTcaOverride>
            <foreign_match_fields type="array">
                <fieldname>image</fieldname>
            </foreign_match_fields>
            <appearance type="array">
                <newRecordLinkAddTitle>1</newRecordLinkAddTitle>
                <headerThumbnail>
                    <field>uid_local</field>
                    <height>64</height>
                    <width>64</width>
                </headerThumbnail>
            </appearance>
        </config>
    </TCEforms>
</image>

Und folgenden Block muss man einfügen, damit das alt-Feld angezeigt wird:

<image>
    <TCEforms>
        <label>Bild</label>
        <config>
            [...]
            <foreign_types type="array">
                <numIndex index="2">
                    <showitem>title,description,alternative</showitem>
                </numIndex>
            </foreign_types>
            [...]
        </config>
    </TCEforms>
</image>

Das war der Artikel, den ich gefunden habe: https://typo3-english.typo3.narkive.com/0GGddVxh/typo3-6-0-using-fal-image-in-a-flexform-as-an-element

Grundsätzlich würde ich davon abraten, Bilder im Flexform zu referenzieren. Gerade in diesem Fall, wenn die Felder wie Überschrift, Text und Bild bereits in der Tabelle vorhanden sind, wäre es deutlich einfacher gewesen, zwar ein neues Inhaltselement zu definieren, jedoch die vorhandenen Felder zu nutzen. Es hat diverse Vorteile: man kann das Element in Typ Text „konvertieren“, ohne die Inhalte zu verlieren und die Inhalte können besser gefunden werden. Das Projekt habe ich von einem anderen Entwickler übernommen und das Element wird bereits häufig genutzt, so dass ein Umbau nicht mehr so einfach ist.

Geschrieben in TYPO3, TYPO3 v8, TYPO3 v9 | Kommentare deaktiviert für Lösung für Bilder im Flexform ohne Alternative-Feld

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

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.

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

TYPO3 Order by Random im Repository

Oder auch: Datensätze in zufälliger Reihenfolge ausgeben

Es kommt schonmal vor, dass man Datensätze aus dem Repository in zufälliger Reihenfolge ausgeben möchte. In meinem Fall habe ich Kundenzitate und möchte sie gerne zufällig sortiert in einem Slider ausgeben. Meine Recherche ergab, dass ORDER BY RAND() nicht geht seit Doctrine in TYPO3 eingezogen ist, diverse Links zu Diskussionen hier und da. Ich bin stur: es muss doch gehen. Da habe ich diesen Forumsbeitrag entdeckt, wo jemand es mit TypoScript hinbekommen hat, in TYPO3 8 wohlgemerkt. Da TypoScript ja auch von Doctrine verarbeitet wird, geht es also doch, man muss nur etwas um die Ecke denken.

Meine Lösung sieht wie folgt aus: Query mit Doctrine zusammenbauen, zusätzliches Feld einfügen das einen Zufallswert enthält und nachdem sortiert wird, Ergebnisse holen und mit dem DataMapper in Objekte transformieren.

use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
/**
 * @param int $limit
 */
public function findRandom($limit)
{
    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->getTableName());
    $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
    $records = $queryBuilder
        ->select('*')
        ->addSelectLiteral('rand() AS random_sort')
        ->from($this->getTableName())
        ->orderBy('random_sort')
        ->setMaxResults($limit)
        ->execute()
        ->fetchAll();
    $dataMapper = GeneralUtility::makeInstance(DataMapper::class);
    $result = $dataMapper->map($this->objectType, $records);
    return $result;
}
 
/**
 * Return the current tablename
 *
 * @return string
 */
protected function getTableName()
{
    $dataMapper = GeneralUtility::makeInstance(DataMapper::class);
    $tableName = $dataMapper->getDataMap($this->objectType)->getTableName();
    return $tableName;
}

Funktionert wunderbar in TYPO3 8.7.

TYPO3 E-Mail-Adressen in JsonView verschlüsseln

Ich leistete Support bei einem Projekt (d.h. ich hatte es nicht selbst programmiert) und hatte mit einer Ansprechpartner-Liste zu tun, die mit Ajax (JSON-Format) nachgeladen wurde. Bei dieser Site sollte die TYPO3 E-Mail-Adressverschlüsselung aktiviert werden. Für die Ansprechpartner-Liste war ein Controller geschrieben, der die Ansprechparter aus dem Repository holte und ins JSON-Format umwandelte, ein Fluid-Template wurde nicht benutzt. Als ich die E-Mail-Adressverschlüsselung aktiviert hatte (hier nur ein Beispiel), griff sie an der Stelle nicht und die E-Mail-Adressen wurden unverschlüsselt ausgeliefert.

config.spamProtectEmailAddresses = 2
config.spamProtectEmailAddresses_atSubst = (at)

Da ich nicht viel umbauen wollte, hatte ich die E-Mail-Adresserschlüsselung in den Controller implementiert. Wenn man sich den Fluid EmailViewHelper naschaut, dann ist die entscheidende Zeile eigentlich folgende:

list($linkHref, $linkText) = $GLOBALS['TSFE']->cObj->getMailTo($email, '');

Da ich letztens über die JsonView gelesen hatte, ließ mir die Aufgabe keine Ruhe. Wie könnte man die JsonView verwenden und dabei die in TYPO3 eingestellte E-Mail-Adressverschlüsselung beibehalten?

Ich habe eine kleine Demo-Extension, in der ich Personen-Datensätze verwalte mit Name, Email und noch ein paar weiteren Feldern. Im AjaxController aktiviere ich die JsonView und konfiguriere sie. In der Konfiguration gebe ich 2 Felder an, die nicht in der Datenbank existieren. Dann füge ich dem Model getter-Funktionen für diese zwei Felder hinzu.

Controller

class AjaxController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{
 
    protected $defaultViewObjectName = JsonView::class;
 
    /**
     *
     */
    public function personListAction()
    {
        $jsonViewConfiguration = [
            'persons' => [
                '_descendAll' => [
                    '_exclude' => ['pid'],
                    '_only' => ['name', 'email', 'emailHref', 'emailText']
                ]
            ]
        ];
        $personRepository = $this->objectManager->get(PersonRepository::class);
        $persons = $personRepository->findAll();
        $this->view->setVariablesToRender(['persons']);
        $this->view->setConfiguration($jsonViewConfiguration);
        $this->view->assign('persons', $persons);
    }
}

Model

class Person extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
{
    [...]
    /**
     * @return string
     */
    public function getEmailHref()
    {
        list($linkHref, $linkText) = $GLOBALS['TSFE']->cObj->getMailTo($this->email, '');
        return $linkHref;
    }
 
    /**
     * @return string
     */
    public function getEmailText()
    {
        list($linkHref, $linkText) = $GLOBALS['TSFE']->cObj->getMailTo($this->email, '');
        return $linkText;
    }
}

JSON Output

[
{
"email": "bruce@wayne-enterprises.com",
"emailHref": "javascript:linkTo_UnCryptMailto('ocknvq,dtwegBycapg\\/gpvgtrtkugu0eqo');",
"emailText": "bruce(at)wayne-enterprises.com",
"name": "Bruce Wayne"
},
[...]

Wirklich glücklich bin ich mit dieser Lösung nicht. Ich habe das Gefühl, dass ich Darstellungslogik, die eigentlich in den View gehört, im Model unterbringe. Mir würde es besser gefallen, wenn man Prozessoren für Objekte definieren könnte. Leider gibt die Konfiguration des JsonView das im Moment nicht her.

Geschrieben in TYPO3, TYPO3 v8 | Kommentare deaktiviert für TYPO3 E-Mail-Adressen in JsonView verschlüsseln

Fluid f:comment-ViewHelper und Inline JavaScript

In Vorbereitung auf die TYPO3 Developer Prüfung schaue ich mir gerade vieles in TYPO3 genauer an. So wie im Moment die Standard Fluid-ViewHelper. Dabei bin ich beim f:comment ViewHelper hängen geblieben. Die Dokumentation beschreibt da einen Fall, denn ich so nicht reproduzieren konnte. Mein TYPO3 8.7.19 verhält sich so:

Inhalte gewrappt in f:comment werden nicht ausgegeben – weder im Quellcode, noch sichtbar.

<f:comment>
    <p>Wrapped in f:comment: Anzahl Einträge {data -> f:count()} </p>
</f:comment>

Inhalte gewrappt in CDATa werden ebenfalls nicht ausgegeben. Das ist der Dokumentation noch anders beschrieben.

<![CDATA[<p>Wrapped in CDATA: Anzahl Einträge {data -> f:count()} </p>]]>

Es gibt allerdings einen neuen ViewHelper f:format.cdata, der Inhalt bei der Ausgabe in CDATA wrappt. Eigentlich toll.

<f:format.cdata><p>Wrapped in f:format.cdata: Anzahl Einträge {data -> f:count()} </p></f:format.cdata>

Problem dabei ist, dass dann folgendes im Quellcode ausgegeben wird:

<![CDATA[<p>Wrapped in f:format.cdata: Anzahl Einträge 10 </p>]]>

Der Browser (in meinem Fall Chrome) stellt auf der Seite ]]> dar. Wünschenswert wäre, dass auf der Seite gar keine Ausgabe erfolgt. Ich habe mich noch nie ausführlich mit CDATA beschäftigt, weiß daher nicht, ob es an f:format.cdata oder meinem Browser liegt.

Und nun zum nächsten Problemchen: Inline JavaScript. Ich hatte etwas recherchiert und die Vorschläge, wie man Fluid vom Parsen von Inline-JavaScript abhält, beziehen sich alle auf Wrappen in CDATA. Was ja eben nicht funktioniert. Also f:format.cdata ViewHelper benutzen:

<script type="text/javascript">
    <f:format.cdata>
        function sayHello() {
            alert("hello world");
        }
        sayHello();
    </f:format.cdata>
</script>

Allerdings wirft der obige Code einen JavaScript-Fehler. Der Browser stört sich an dem < am Beginn von CDATA. Eine funktionierende Lösung sieht damit so aus:

<script type="text/javascript">
    //<f:format.cdata>
        function sayHello() {
            alert("hello world");
        }
        sayHello();
    //</f:format.cdata>
</script>

Gibt es denn eine Möglichkeit, einen Block auszugeben, den aber von Fluid nicht parsen zu lassen? Wofür könnte man es brauchen? Ich werde weiter schauen…

Geschrieben in TYPO3, TYPO3 v8 | Kommentare deaktiviert für Fluid f:comment-ViewHelper und Inline JavaScript