Wie man individuelle Inhaltselemente in TYPO3 erstellt

|David Steeb

Eine mit TYPO3 erstellte Webseite besteht aus verschiedenen Inhaltsteilen, die in semantischen Containern gespeichert werden und im Jargon von TYPO3 "Content Elements" genannt werden. Inhaltselemente enthalten eine bestimmte Zusammenstellung von Informationen, z.B. eine Überschrift, einen Text und ein Bild. Diese Menge an Informationen wird als "Content Type" bezeichnet. Inhaltselemente und Inhaltstypen können wiederverwendet oder neu angeordnet werden, ihre Sichtbarkeit bei der Ausgabe wird durch eine gewisse Business-Logik bestimmt, sie können zur Übersetzung übermittelt werden und vieles mehr. Für uns ist die Tatsache, dass wir (und du!) unsere eigenen Elemente selbst erstellen können, eines der Alleinstellungsmerkmale (USPs), die TYPO3 in Bezug auf Leistung und Flexibilität von anderen CMS unterscheidet.

Wenn du eine neue Instanz mit einem Basis-Installationspaket aufbaust, erhältst du eine Reihe von Standard-Inhaltselementen und -typen wie "Text", "Text mit Bild" und "Aufzählungszeichen". Diese sind zwar unerlässlich und nützlich, und man kann die Seitenausgabe entsprechend den eigenen Bedürfnissen gestalten, aber sobald man mit realen Designvorlagen für größere Projekte zu arbeiten beginnt, benötigt man mit ziemlicher Sicherheit zusätzliche Inhaltselementtypen.

Es gibt eine Reihe von Erweiterungen von Drittanbietern, die dir helfen, Inhaltselemente für bestimmte Zwecke zu erstellen. Die Verwendung dieser Erweiterungen kann zwar zu schnellen Ergebnissen führen, aber die zusätzlichen Abhängigkeiten von den Erweiterungen von Drittanbietern könnten im Laufe der Zeit zum Problem werden. Damit meinen wir in diesem Fall, dass du keine Kontrolle darüber hast, wann das nächste TYPO3-Update eine Aktualisierung der Extension durch den Entwickler des Drittanbieters erfordert. Die Aktualisierung könnte länger dauern als du brauchen kannst und dich dazu zwingen, zwischen einer früheren (potentiell unsicheren) Version oder der Störung deiner Website wählen zu müssen.

Bei b13 erstellen wir benutzerdefinierte Inhaltstypen in wenigen einfachen Schritten, ohne auf Erweiterungen von Drittanbietern zurückzugreifen. Wir nennen diese Arbeitsweise und die von uns erstellten benutzerdefinierten Inhaltstypen "core-nah". Sie sind nicht von Lösungen Dritter abhängig und werden bei Upgrades nicht beeinträchtigt. Wir haben vor kurzem einen Inhaltstyp zur Anzeige von Codeblöcken mit Syntax-Highlighting für diese Website, unseren eigenen Blog, erstellt. Wir dachten, dieser Inhaltstyp wäre ein perfektes Beispiel, um zu zeigen, was möglich ist und wie man es nach den Best Practices macht.

Backstory

Im Zuge unserer verstärkten Bemühungen, unsere Arbeit mit TYPO3 CMS und anderen Technologien aufzuzeigen, dachten wir uns, dass wir ziemlich oft Code in unserem Blog veröffentlichen werden. Wir wollten, dass Code-Schnipsel automatisch mit Syntax-Highlighting angezeigt werden, damit sie für die Leute leichter zu analysieren und zu verstehen sind.

Die Standardlösung ist das Hinzufügen einer JavaScript-Bibliothek (wie highlight.js) zum eigenen Quellcode, die JavaScript-Magie verwendet, um Codeblöcke zu identifizieren und sie in einem nützlichen Farbschema wiederzugeben. Diese Option stand uns nicht zur Verfügung; unsere Website ist vollständig AMP-kompatibel (nach dem Accelerated Mobile Pages-Standard), und die AMP Best-Practice besteht darin, die JS-Nutzung auf ein absolutes Minimum zu beschränken. Das Laden einer ganzen JS-Bibliothek, um dann nur einen Bruchteil davon auf einer bestimmten Seite zu verwenden, ist natürlich kein Weg, um die Ladezeiten von Seiten im Browser zu optimieren.

Dank eines PHP-Pakets namens highlight.php, das genau das tut, was highlight.js im Browser macht, ist es nicht mehr nötig, eine ganze JS-Bibliothek zu laden. Auf diese Weise verlagern wir auch das Rendering auf den (viel schnelleren) Webserver und liefern dem Benutzer vorformatierte Ausgaben. Mit highlight.php können wir die Codeschnipsel im TYPO3-Backend erzeugen, sie vollständig cachen und das Hinzufügen von JavaScript in unserem Frontend vermeiden.

Hierfür verwenden wir unseren Inhaltstyp "Code-Block". Damit können wir Code-Schnipsel als Inhalt direkt im TYPO3-Backend eingeben. Das System weiß (weil wir es so eingerichtet und unsere Templates und das Rendering konfiguriert haben), dass dieser Inhalt mit Code-Syntax-Highlighting für die Ausgabe im Frontend gerendert wird - für die Anzeige auf der Seite, wo ihr, liebe Leser, das Beste daraus machen könnt.

Erstellen eines benutzerdefinierten Inhaltstyps für TYPO3

Um unseren Inhaltstyp in verschiedenen TYPO3-Instanzen wiederzuverwenden, haben wir die "contentblock"-Extension erstellt. Ihr einziger Zweck ist es, den "Contentblock"-Inhaltstyp zu deiner TYPO3-Installation hinzuzufügen. In diesem Beitrag werden wir uns die verschiedenen Teile des Codes und der Konfiguration ansehen, die benötigt werden, um den Content-Typ in TYPO3 hinzuzufügen, und dies am Beispiel dieser Erweiterung. Unsere Contentblock-Erweiterung ist hier verfügbar und kann mit diesem Composer-Befehl installiert werden: composer req b13/codeblock

Was gehört zu einem Inhaltstyp?

Ein Content-Typ für das Seitenmodul von TYPO3 wird durch eine Reihe von Konfigurationen definiert. Wir gehen die sieben Schritte durch, die für die Erstellung und Aktivierung des gewünschten Content-Typs und der UI-Elemente, die für die Unterstützung der Content-Redakteure benötigt werden, erforderlich sind. Wir werden Dateien und Konfigurationen zu einigen verschiedenen Teilen des TYPO3-Backends wie folgt hinzufügen:

  1. Datenbank-Feld: Hinzufügen eines Feldes zur Datenbanktabelle für "tt_content"
  2. Backend TCA:
    1. Hinzufügen einer Konfiguration für das zusätzliche Backend-Formularfeld für die Tabelle "tt_content" im TCA (Table Configuration Array).
    2. Hinzufügen des Inhaltstyps zur Liste der verfügbaren Inhaltstypen.
    3. Hinzufügen der Konfigurationsoptionen für die verfügbaren Felder für Redakteure im Backend.
  3. Assistent für neue Inhaltselemente: Hinzufügen des Content-Typs zum "New Content Element Wizard"
  4. Backend-Inhaltselementvorschau im Seitenmodul: Hinzufügen einer Redakteursvorschau im TYPO3 Backend-Seitenmodul, um den Redakteuren den Inhalt anzuzeigen.
  5. Erstellen eines DataProcessors: Verarbeitung des Inhalts mit einem DataProcessor, um die Ausgabe mit highlight.php-Syntax-Highlighting zu versehen.
  6. TypoScript-Konfiguration: Jeder Inhaltselementtyp benötigt eine TypoScript-Konfiguration, um zu definieren, was im Frontend angezeigt werden soll.
  7. Fluid Template: Wir fügen ein Fluid-Template hinzu, um unseren Inhaltselement-Typ im Frontend darzustellen.

Betrachten wir nun jeden Schritt im Detail.

Schritt-für-Schritt-Anleitung

1. Ein Datenbankfeld hinzufügen

Wenn eine Erweiterung die Datenbank modifizieren muss, wird eine Datei ext_tables.sql zu der Erweiterung hinzugefügt. Die Syntax ist SQL, und auch wenn wir ein Feld zu einer bestehenden Tabelle hinzufügen, verwenden wir die CREATE TABLE Syntax. TYPO3 kümmert sich um das korrekte Hinzufügen unseres Feldes. Weitere Informationen zum Ändern von Datenbanktabellen und -feldern findest du im TYPO3-Handbuch hier.

Wir möchten eine Dropdown-Liste aller verfügbaren Programmiersprachen anzeigen, die highlight.php unterstützt, so dass Redakteure leicht eine bestimmte Programmiersprache (und ein entsprechendes spezifisches Syntax-Highlighting-Schema) angeben können. Für unseren benutzerdefinierten Inhaltstyp fügen wir ein Feld code_language zur Tabelle tt_content hinzu.

Und so sieht unsere ext_tables.sql-Datei aus:

1
2
3
CREATE TABLE tt_content (
    code_language text DEFAULT '' NOT NULL
);
ext_tables.sql

2. Hinzufügen der Konfiguration zum TCA

Das Table Configuration Array (TCA) ist die Konfigurationsschicht über der Datenbank, auf der TYPO3 arbeitet. Das TCA enthält alle Konfigurationsoptionen für Datenbankfelder und teilt dem System mit, wie ein bestimmtes Feld und seine Werte oder Wertoptionen für Backend-Benutzer in verschiedenen Formen angezeigt werden sollen. Mehr dazu in der Dokumentation zum TCA hier.

Für unsere Zwecke werden wir dem TCA für die Tabelle "tt_content" drei Dinge hinzufügen. Die gesamte Konfiguration ist in der Datei Configuration/TCA/Overrides/tt_content.php enthalten:

a) Hinzufügen der Konfiguration für unser neu erstelltes Feld code_language

Wir fügen das neue Feld als Auswahlelement hinzu. Die Werte, die ein Backend-Benutzer auswählen kann, werden aus einem separaten DataProvider generiert (siehe Ordner Classes/DataProvider), die genaue Funktionsweise ist für dieses Anwendungsbeispiel ein wenig außerhalb des Rahmens. Wir hätten auch einfach eine Liste von Elementen manuell hinzufügen können. Wir verwenden das ExtensionManagementUtility des TYPO3-Core, um die neue Feldkonfiguration zum TCA für tt_content hinzuzufügen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Add dropdown for code language to TCA.
$additionalColumns = [
'code_language' => [
'label' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.code_language',
'config' => [
'type' => 'select',
'default' => '',
'itemsProcFunc' => 'B13\\Codeblock\\DataProvider\\CodeLanguages->getAll',
'renderType' => 'selectSingle',
],
],
];

\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('tt_content', $additionalColumns);

\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes(
'tt_content',
'code_language',
'codeblock',
'before:bodytext'
);

Hinzufügen unseres Inhaltstyps zur Liste der verfügbaren Inhaltstypen (Datenbankfeld CType)

Auch hier verwenden wir die ExtensionManagementUtilty-API, um die TCA-Konfiguration für das Feld "CType" zu ändern und unseren neuen Inhaltstyp der Liste hinzuzufügen.

1
2
3
4
5
6
7
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
['LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType', 'codeblock', 'content-codeblock'],
'html',
'after'
);

Anmerkung: Die Verwendung von content-codeblock als iconIdentifier (dritter Wert im Array, der als dritter Parameter im folgenden Code-Snippet notiert ist) erfordert, dass das Icon zuerst registriert wird, was in der ext_localconf.php zu sehen ist.

c) Hinzufügen einer Konfiguration für das Bearbeitungsformular unseres Content-Typs

Diese Konfiguration legt die Liste der Felder, ihre Sortierreihenfolge und die Gruppierung in Paletten und Reitern für die Backend-Ansicht fest, die der Editor beim Bearbeiten eines Elements vom Typ "Codeblock" sieht. Bitte beachte in diesem Zusammenhang, wie die Konfigurationseigenschaft "columnsOverrides" verwendet werden kann, um das Standard-Anzeigeverhalten des Feldes "bodytext" zu ändern - in diesem Fall, um eine Monospace-Schriftart zu verwenden, um das Backend übersichtlich und benutzerfreundlich für die Redakteure zu gestalten!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$GLOBALS['TCA']['tt_content']['types']['codeblock'] = [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
--palette--;;general,
--palette--;;headers,
bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel,
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,
--palette--;;frames,
--palette--;;appearanceLinks,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
--palette--;;language,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;hidden,
--palette--;;access,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories,
categories,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes,
rowDescription,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended,
',
'columnsOverrides' => [
'bodytext' => [
'config' => [
'fixedFont' => true,
],
],
]
];
Abbildung 1: Das Backend-Formular für unseren Codeblock-Inhaltstyp.
Abbildung 1: Das Backend-Formular für unseren Codeblock-Inhaltstyp.

3. Einen Eintrag im Assistenten für neue Inhaltselemente hinzufügen

Um ein anklickbares Element zum "Assistenten für neue Inhaltselemente" hinzuzufügen, verwenden wir einige Zeilen PageTS, wie in Configuration/PageTs/PageTs.tsconfig zu sehen:

1
2
3
4
5
6
7
8
9
10
11
mod.wizards.newContentElement {
wizardItems {
common.elements.codeblock {
title = LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType
description = LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.wizard.description
tt_content_defValues.CType = codeblock
iconIdentifier = content-codeblock
}
common.show := addToList(codeblock)
}
}
PageTs.tsconfig

Um einen IconIdentifier zu verwenden, musst du das Icon zuerst registrieren, siehe Kommentar oben.

Abbildung 2: Das Element Codeblock ist im Assistenten für neue Inhaltselemente aufgelistet.
Abbildung 2: Das Element Codeblock ist im Assistenten für neue Inhaltselemente aufgelistet.

4. Eine Elementvorschau zum Seitenmodul hinzufügen

Abhängig vom Inhalt deines "Code"-Feldes zeigt die Elementvorschau im Seitenmodul nichts außer deiner Überschrift an:

Abbildung 3: Die Standardvorschau eines Inhaltselements zeigt möglicherweise keine nützlichen Informationen an.
Abbildung 3: Die Standardvorschau eines Inhaltselements zeigt möglicherweise keine nützlichen Informationen an.

Dies liegt an der Standardwiedergabe von Inhaltselementen, die überhaupt keinen PHP-Code anzeigen, an der Entfernung von HTML-Tags für HTML-Inhalte usw. Schließlich ist die Backend-Vorschau als schematische Vorschau des Inhalts und der Struktur gedacht, nicht als vollständige, visuelle Darstellung des Inhalts.

Um dies zu ändern und unsere Inhalte für die Redakteure sichtbar zu machen, fügen wir auch eine benutzerdefinierte Vorschau-Konfiguration für unser Element hinzu. Dies geschieht in Classes/Hooks/CodeblockPreviewRenderer.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class CodeblockPreviewRenderer implements PageLayoutViewDrawItemHookInterface
{
/**
* Preprocesses the preview rendering of a content element of type "codeblock"
*
* @param \TYPO3\CMS\Backend\View\PageLayoutView $parentObject Calling parent object
* @param bool $drawItem Whether to draw the item using the default functionality
* @param string $headerContent Header content
* @param string $itemContent Item content
* @param array $row Record row of tt_content
*/

public function preProcess(
PageLayoutView &$parentObject,
&$drawItem,
&$headerContent,
&$itemContent,
array &$row
)
{
if ($row['CType'] === 'codeblock') {
if ($row['bodytext']) {
$bodytext = GeneralUtility::fixed_lgd_cs($row['bodytext'], 1000);
$itemContent .= $parentObject->linkEditContent(nl2br(htmlentities($bodytext)), $row) . '<br />';
}

$drawItem = false;
}
}
}
CodeblockPreviewRenderer.php

Registriere den Hook in der Datei ext_localconf.php der Extension:

1
2
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['tt_content_drawItem']['codeblock'] =
\B13\Codeblock\Hooks\CodeblockPreviewRenderer::class;
ext_localconf.php

Et voilà! Wir haben jetzt eine brauchbare Vorschau im Seitenmodul:

Abbildung 4: Code-Vorschau im Backend-Seiten-Modul.
Abbildung 4: Code-Vorschau im Backend-Seiten-Modul.

Anmerkung:

Für viele Elemente kannst du Fluid-Templates als Backend-Vorschau verwenden, oder du brauchst nicht einmal benutzerdefinierte Felder zu Datenbanken hinzuzufügen. Du kannst eine Backend-Ansichtsvorlage unter Verwendung von PageTS festlegen, wenn du nur bestimmte Felder anzeigen willst; in unserem Fall müssen wir den Inhalt auf eine besondere Art und Weise verarbeiten. Um ein Fluid-Template mit TSConfig für eine Inhaltselementvorschau zu setzen, verwendest du die typo3/systext/backend/Classes/View/PageLayoutView.php, Funktion tt_content_drawItem($row) in deiner TYPO3-Codebasis!

5. Erstellen eines DataProcessors

Wir wollen das Code-Snippet mit highlight.php verarbeiten, unserem Inhaltselement hinzufügen und das Ergebnis in unserem Frontend darstellen. Dazu müssen wir den Inhalt mit einem DataProcessor verarbeiten. Die vollständige Datei findest du unter Classes/DataProcessing/HighlightProcessor.php

Wir verwenden den Wert eines bestimmten Feldes ($processorConfiguration['field']), verarbeiten ihn mit highlight.php und geben eine formatierte Version des Wertes zurück, die in der Frontend-Ausgabe verwendet werden soll.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData)
{
$fieldName = $processorConfiguration['field'];
$targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'bodytext_formatted');
$highlight = GeneralUtility::makeInstance(Highlighter::class);

// Let highlight.php decide which code language to use from all registered if "detect automatically" is selected.
if (!$processedData['data']['code_language']) {
$languages = $highlight->listLanguages();
$highlight->setAutodetectLanguages($languages);
$highlighted = $highlight->highlightAuto($processedData['data'][$fieldName]);
} else {
$highlighted = $highlight->highlight($processedData['data']['code_language'], $processedData['data'][$fieldName]);
}

$processedData[$targetVariableName]['code'] = $highlighted->value;
$processedData[$targetVariableName]['language'] = $highlighted->language;
$processedData[$targetVariableName]['lines'] = preg_split('/\r\n|\r|\n/', $highlighted->value);
return $processedData;
}
HighlightProcessor.php

6. TypoScript-Konfiguration für die Frontend-Ausgabe hinzufügen

Damit TYPO3 etwas im Frontend ausgeben kann, müssen wir festlegen, was es tun soll, wenn es versucht, ein Inhaltselement vom Typ "Codeblock" zu rendern. Wir erweitern die TypoScript-Konfiguration für tt_content um unsere benutzerdefinierte Content-Type-Konfiguration in Configuration/TypoScript/setup.typoscriptund fügen die Konfiguration für die Datenverarbeitung zu unserer Elementkonfiguration hinzu, um den Wert des "bodytext"-Feldes zu verarbeiten:

1
2
3
4
5
6
7
8
9
10
11
12
tt_content.codeblock =< lib.contentElement
tt_content.codeblock {
templateName = Codeblock

templateRootPaths.0 = EXT:codeblock/Resources/Private/Templates

dataProcessing.1567071612 = B13\Codeblock\DataProcessing\HighlightProcessor
dataProcessing.1567071612 {
field = bodytext
as = bodytext_formatted
}
}

7. Ein Fluid-Template für unseren Inhaltstyp hinzufügen

Als letzten Schritt müssen wir die Fluid-Vorlage erstellen, um den Inhalt in unserem Frontend zu rendern. Der Name und das Verzeichnis des Fluid-Templates werden im obigen TypoScript-Setup definiert und das Template ist unter Resources/Private/Templates/Codeblock.html zu finden.

1
2
3
4
5
6
7
8
9
10
11
<f:layout name="Default" />

<f:section name="Main">

<pre>
<code class="hljs
{bodytext_formatted.language}">
{bodytext_formatted.code -> f:format.raw()}
</code>
</pre>

</f:section>

Ergebnis: Eine wieder verwendbare Vorlage für Inhaltstypen

Jedes TYPO3-Projekt bei b13 umfasst individuelle Designs, individuelle Templates und benutzerdefinierte Inhaltstypen, die den spezifischen Anforderungen des Kunden entsprechen. Nicht alle Anwendungsfälle erfordern so viele Änderungen, wie wir in diesem ziemlich detaillierten Beispiel aufgenommen haben. Dennoch haben wir mit diesen einfachen Schritten einen neuen Inhaltstyp in wenigen Minuten erstellt. 

Die grundlegenden Bausteine, wie oben beschrieben, sind jedes Mal die gleichen. Wenn du das Konzept einmal verinnerlicht hast, ist es einfach, neue, benutzerdefinierte Inhaltstypen hinzuzufügen, so dass deine Redakteure Inhalte erstellen können, die in jedem Template gut aussehen.

Wir sind noch einen Schritt weiter gegangen und haben dir gezeigt, wie du deine Arbeit wertvoller machen kannst, indem du eine Erweiterung erstellst, die du wiederverwenden kannst, um einen bestimmten Inhaltstyp zu einem oder allen deinen Projekten hinzuzufügen.

Ein zusätzlicher Vorteil ist, dass auf diese Weise erstellte benutzerdefinierte Inhaltstypen (wir nennen sie "core-nah") bei einer Aktualisierung nicht kaputtgehen und du und deine TYPO3-Instanz sich nicht auf Drittanbieter-Entwickler verlassen müssen.

Wir führen Workshops zur Erstellung von Site-Extensions und zur Migration von Drittanbieterlösungen zu benutzerdefinierten Inhaltstypen durch, die genau wie die nativen Inhaltstypen des TYPO3-Kerns funktionieren. 

Wir beraten dich gerne, wenn du mehr erfahren möchtest oder Hilfe bei der Integration deiner Templates benötigst.

Kontaktiere uns um mehr zu lernen

...oder wenn du Hilfe bei der Integration deiner Templates brauchst

Let's connect