Die Erstellung einer Website mit einem CMS erfordert die Einbeziehung von mehr Komponenten, als wenn die Site nur mit reinem statischem HTML erstellt würde. Die Idee hinter der Verwendung eines CMS ist den eigentlichen Inhalt getrennt von den Definitionen zu dessen Darstellung zu speichern. Einige CMS speichern den Inhalt in XML Dateien, andere nutzen relationale Datenbanken. Die Unterschiede haben verschiedene Für und Wider, aber die Idee dahinter ist die selbe: "Der Inhalt wird von den Farben getrennt”.
Wenn TYPO3 Webseiten generiert, wird unformatierter Inhalt aus einer Datenquelle (Datenbank) mit den Formatanweisungen aus einem HTML-Template kombiniert. In diesem Prozess dient das Template als Kontrollelement, das festlegt in welcher Form Typo3 diese Kombination durchführen soll.
In der nächsten Abbildung sehen Sie, wie der Template Datensatz als Steuerelement - also sozusagen als "Programm” hinter der Erstellung des Frontends fungiert: schrittweise wird der Inhalt in der Datenbank gefunden, das Template gelesen, der Inhalt an den dafür vorgesehenen Platzhaltern eingefügt und eine ansehnliche Webseite wird ausgegeben!
In den meisten Webagenturen arbeiten bei der Erstellung einer Website eine ganze Anzahl von Leuten zusammen. In dieser Gruppe finden wir einen Grafikdesigner, einen Programmierer und einen Texter (zumindest für den ursprünglichen Inhalt zum Beginn). Jede dieser Personen hat unterschiedliche Kenntnisse und arbeitet folglich bei der Produktion der Site in unterschiedlichen Bereichen:
Wie in obigen Abbildung wird jeder Person von den verschiedenen Komponenten der vom CMS erzeugten Wesite eine zugewiesen:
Mr. Raphael ist der Künstler. Er versteht viel von visueller Darstellung, er kennt Photoshop, Dreamweaver, CSS, HTML usw. Raphael jongliert mit Flashfilmen, hat aber keine Ahnung von PHP, TypoScript, SQL und anderen technischen Fragen. Also erstellt Raphael die HTML-Templates für uns!
Mr. Benoit ist ein Entwickler. Er liebt Bits und Bytes, er mag Compilerananweisungen, reguläre Ausdrücke, Logik, PHP, SQL - und bald wird er auch TypoScript lieben. Wie jeder andere gute Programmierer glaubt er mehr an gute Blue Jeans als an extravagantes Design. Farben und Benutzbarkeit zu kombinieren ist nicht in seine Wiege gelegt worden. Daher ist Benoit für die Konfiguration von TypoScript in den Templates verantwortlich.
Mr. Picouto erstellt die Inhalte. Er versteht was von Marketing. Die Arbeit von Raphael und Benoit bewundert er, da sie in seinen Augen wahre Wunder vollbringen. Er selbst ist weder ein Designer noch ein Programmierer, aber er hat die Fähigkeit zu kommunizieren. Und mit dem Backend von TYPO3 kann er den Inhalt erstellen, ohne mehr Kenntnisse besitzen zu müssen als für die Bedienung einer Textverarbeitung.
So haben wir also drei Charaktere mit jeweils speziellen Fähigkeiten, ohne welche die Erstellung einer CMS basierten Website kein Spaziergang ist, es sei denn Sie besitzen alle diese Fähigkeiten: Darstellung, Technik und Marketing. Normalerweise ist dies nur in einem Team der Fall, bei einzelnen Personen ist es eher unüblich. Seien Sie also gewarnt (wenn Sie weniger qualifiziert sind, müssen Sie entweder viel dazulernen oder Sie benutzen einfach Standardtemplates mit einem vorgegebenen Design, welches Sie nur noch etwas anpassen müssen (das beschreibt diese Anleitung nicht).
In diesem Tutorial zeige ich Ihnen, wie Raphael, Benoit und Picouto zusammenarbeiten müssen um moderne Websites erstellen zu können. Jedes Teammitglied kann sich in seinem eigenen Bereich entfalten und Werkzeuge nutzen die es mag, um leicht zu pflegende Websites mit einem traumhaften Designerstellen zu können. Mit TYPO3.
Fachliche Voraussetzungen
Webdesigner mit HTML/CSS Wissen, nicht notwendigerweise Webentwickler.
Damit Sie diesen Abschnitt vollständig durcharbeiten können, sollten Sie über gute HTML- und CSS-Kenntnisse verfügen. Weiterhin brauchen Sie eine funktionierende TYPO3-Datenbank, wie wir sie bereits in einem früheren Abschnitt dieses Tutorials angelegt haben. Schließlich sollten Sie mit Konzepten der Programmierung vertraut sein, um alles vollständig verstehen zu können. Aber keine Angst, Ihnen wird alles erklärt was Sie wissen müssen - seien sie einfach aufmerksam und nehmen Sie sich die Zeit, die Beispiele zu nachzuvollziehen.
Das Webteam hat einen neuen Kunden: Main Dish & Son - und Raphael, der Künstler im Team, hat eine Vorlage in Form einer regulären HTML-Datei erstellt:
Diese HTML-Datei wird in dem Verzeichnis "fileadmin/template/main” relativ zur TYPO3-Installation (dummy package) gespeichert.
Um dieser Anleitung folgen zu können, sollten Sie jetzt den Inhalt des Ordners "part1/” von diesem Tutorial in das Verzeichnis "fileadmin/template/main” kopieren. Falls Sie die Extension zu diesem Tutorial noch nicht importiert haben, sollten Sie dies jetzt nachholen.
Falls Sie den vorigen Abschnitt über TypoScript übersprungen haben, sollten Sie zumindest den Seitenbaum wie im Kapitel "Erstellen einer Seitenstruktur” und ggf. ein leeres Template erstellen .
Zurück zu Raphaels Arbeit: die Vorlage ist tatsächlich lediglich nur eine reguläre HTML-Seite. Wenn aber TYPO3 diese Datei als Template nutzen soll, müssen einige Teile daraus dynamisch werden. Dazu gehört das Menü auf der linken Seite und der Bereich mit dem Blindtext im rechten Bereich in der Mitte.
Wenn Sie sich den Quelltext des HTML-Templates anschauen, werden Sie bemerken, dass es sich um ein XHTML-Dokument handelt, welches ein Stylesheet als Formatvorlage verwendet und mittels einer Tabelle verschiedenen Elemente anordnet.
Hier einige Kommentare zu diesem HTML-Template und den Herausforderungen, die auf uns zukommen:
Der Abschnitt ab dem Header des Dokuments muss in unsere Webseite übernommen werden, da hier der Bezug zu dem Stylesheet eingetragen ist.Aufgabe: Wir müssen überprüfen, ob dieser Teil in die von TYPO3 generierte Seite übernommen wird.
Das Menü auf der linken Seite wurde mit einem <div> Abschnitt pro Menüpunkt erstellt. Jedes dieser <div> Elemente hat eine ihm zugewiesene Klasse. Über diesen Namen der Klasse wird das Aussehen mittels der CSS-Formatvorlage gesteuert.Dies ist ein äußerst vernünftiges Prinzip einer Menügestaltung, da jedes Element nur aus wenig HTML-Code besteht (gut für die TypoScript-Implementierung), es ist leicht reproduzierbar (notwendig für ein dynamisches Menü).Aufgabe: Der Platzhalter für das Menü muss durch ein dynamisch generiertes Menü ersetzt werden.
Den Blindtext im Inhaltsbereich hat Raphael eingesetzt um das Erscheinungsbild darzustellen. Beachten Sie, wie dieser formatiert ist: mit <h1> und <p> Tags (der Klasse "bodytext”, das ist ebenfalls klug gewählt, da auch TYPO3 diese Tags/Klassen bei der Erzeugung der Seite verwenden wird! Raphael hat wohl schon TYPO3-Erfahrung, oder?)Aufgabe: Austausch des Blindtextes gegen dynamisch erzeugten Seiteninhalt.
Dieser break-Tag für den Zeilenumbruch sorgt dafür, dass der Fußbereich nicht zu dicht am eigentlichen Text der Seite liegt.
Letztendlich sollten Sie noch beachten, wie die Zellen der Tabelle für das Menü und den Inhalt mit Tags mit id-Attributen versehen sind. Dies ist nicht nur für das Stylesheet von Bedeutung. Es gibt dafür noch einen anderen wichtigen Grund. Aber zuerst nochmal etwas Theorie zu HTML-Templates:
Erstellen Sie zuerst eine neue Datei (fileadmin/template/test.html) mit folgendem Inhalt:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Untitled</title>
</head>
<body>
<!-- ###DOCUMENT_BODY### -->
<h1>
<!-- ###INSIDE_HEADER### -->
Header of the page
<!-- ###INSIDE_HEADER### -->
</h1>
<!-- ###DOCUMENT_BODY### -->
</body>
</html>
(Diese Datei finden Sie in der Tutorial Extension als "misc/test.html”)
Dann fügen Sie bitte das folgende in das Setup-Feld Ihres Template Datensatzes:
# Template content object:
temp.mainTemplate = TEMPLATE
temp.mainTemplate {template = FILE
template.file = fileadmin/template/test.html
}
# Default PAGE object:
page = PAGE
page.typeNum = 0
page.10 < temp.mainTemplate
Dies fügt ein cObject vom Typ "TEMPLATE” an Position "page.10” ein. Die "template”-Eigenschaft dieses TEMPLATE cObjects ist als weiteres cObject vom Typ "FILE” definiert, welches die soeben erstellte Datei "fileadmin/template/test.html” einliest. Die Eigenschaften des TEMPLATE cObject finden Sie hier.
Wenn Sie jetzt die Änderungen speichern und das Frontend aufrufen, sollten Sie folgende Ausgabe sehen:
Und wenn wir den Quellcode betrachten, sehen wir, dass das TEMPLATE cObject die Datei, so wie sie ist, liest und zurückliefert:
Nun zum wichtigsten Punkt des TEMPLATE cObjects: es liest nicht nur die HTML-Datei - es erlaubt uns auch, Teile daraus zu extrahieren und durch dynamischen Inhalt zu ersetzen!
Ein Teilabschnitt wird als Inhalt zwischen zwei gleichen Markierungen definiert, die von ### umschlossen sind und wiederum (optional) in HTML-Kommentaren enthalten sind. Die Datei "test.html” Datei hat zwei Teilabschnitte, den "DOCUMENT BODY” und den Abschnitt "INSIDE HEADER”. Wie Sie sehen können, werden die Markierungen dieser Teilabschnitte in HTML-Kommentaren eingeschlossen, so dass sie nicht sichtbar sind.
Nun ändern Sie bitte das Setup-Feld des Template Datensatzes wie folgt:
# Template content object:
temp.mainTemplate = TEMPLATE
temp.mainTemplate {
template = FILE
template.file = fileadmin/template/test.html
workOnSubpart = DOCUMENT_BODY
subparts.INSIDE_HEADER = TEXT
subparts.INSIDE_HEADER.value = HELLO WORLD!
}
# Default PAGE object:
page = PAGE
page.typeNum = 0
page.10 < temp.mainTemplate
Im Frontend sollte der HTML-Quelltext dann wie folgt aussehen:
Die Veränderungen an den Eigenschaften des TEMPLATE cObjects haben folgendes bewirkt:
In erster Linie wurde das TEMPLATE cObject angewiesen, mit dem Teilabschnitt zu arbeiten, der mit "###DOCUMENT_BODY###” markiert ist - folglich wurde die überflüssige Überschrift und die Body-Markierungen entfernt.
Danach wurde der Teilabschnitt, der durch "###INSIDE_HEADER###” gekennzeichnet war, durch ein TEXT cObject ersetzt, das die Eigenschaften des "subparts.INSIDE_HEADER” des TEMPLATE cObjects definiert.
Das sollte leicht zu verstehen sein. Was wir jetzt noch tun müssen ist auf Raphaels Vorlage zu verweisen, nämlich "fileadmin/template/main/template_1.html, ähnliche Markierungen für die Teilabschnitte einzufügen und dann das Blindmenü und den Blindtext durch ein dynamisch erzeugtes Menü und den tatsächlichen Seiteninhalt zu ersetzen.
Man könnte nun meinen, man müsse nur Raphaels Vorlage bearbeiten. Meiner Erfahrung nach können Sie nicht immer blind auf HTML-Autoren/Editoren vertrauen, d.h. es kann durchaus passieren, dass der HTML-Autor/Editor bei einer späteren Bearbeitung die Kommentar-Tags im Dokument verschiebt oder sogar entfernt. Stellen Sie sich vor Raphael nimmt einige Änderungen an der Vorlage mit Dreamweaver vor und die Markierungen der Teilabschnitte würden still und heimlich verschoben - das Template würde umgehend ausser Funktion gesetzt sein. Ferner ist der Pfad zu den Formatanweisungen des Stylesheets relativ zum Verzeichnis [domain]/fileadmin/template/main - nicht [domain]/, wo der HTML-Inhalt für das Frontend ausgegeben wird. Alle Pfadangabe müssten korrigiert werden.
Dies verlangt eine bessere Lösung. Dazu importieren Sie bitte die Template Auto-parser Erweiterung Extension - als Antwort auf alle Ihre Fragen...
Installation der Extension
Gehen Sie in den Extension Manager und importieren Sie die Erweiterung aus dem Online Repository.
Finden Sie die Erweiterung "automaketemplate” und klicken Sie auf das Importsymbol:
Dies sollte im Falle des Erfolges erscheinen:
Gehen Sie zurück, wählen Sie "Available Extensions to Install” und installieren Sie die Extension.
Nach dem Klicken auf die "Make updates” Schaltfläche gehen Sie zurück in das Template Modul und überprüfen Sie den Object Browser:
Die Extension sollte ein USER cObject im Objektpfad "plugin.tx_automaketemplate_pi1” eingefügt haben, wie Sie oben sehen. Dieses cObject kann nun anstelle des FILE cObjects verwendet werden, um Raphaels Template einzulesen, die Markierungen automatisch einzufügen und die Pfade anzupassen.
Konfiguration des Template Auto-parsers
Wie für jede Extension, die wir nutzen möchten, können wir das Handbuch auf typo3.org zu Rate ziehen. Klicken Sie auf diesen Link und Sie erhalten um eine Tabelle mit den Eigenschaften dieses cObjects einzusehen.
Um Ihnen zu verdeutlichen, was der Template Auto-parser macht, füge ich hier die Ausgabe des Plugins als einzigen Inhalt ein und werde danach das Plugin so konfigurieren, dass es den üblichen Kopf- und Fußbereich nicht ausgeben wird.
Hier das Setup-Feld des Template Datensatzes:
# Configuring the Auto-Parser:
plugin.tx_automaketemplate_pi1 {# Read the template file:
content = FILE
content.file = fileadmin/template/main/template_1.html
# Here we define which elements in the HTML that
# should be wrapped in subpart-comments:
elements {BODY.all = 1
BODY.all.subpartMarker = DOCUMENT_BODY
HEAD.all = 1
HEAD.all.subpartMarker = DOCUMENT_HEADER
HEAD.rmTagSections = title
TD.all = 1
}
# Prefix all relative paths with this value:
relPathPrefix = fileadmin/template/main/
}
# Default PAGE object:
page = PAGE
page.typeNum = 0
page.config.disableAllHeaderCode=1
page.10 =< plugin.tx_automaketemplate_pi1
Sichern sie das Template und rufen Sie das Frontend auf. Sie sollten eine exakte Darstellung von fileadmin/template/main/template_1.html sehen: "Na und...?”werden Sie denken. Aber das ist, wenn Sie mal in den Quellcode schauen, schon genial:
Zwei wichtige Dinge sehen Sie dort:
Eine Menge von Abschnitten des Template-Datensatzes wurden automatisch in Teilabschnitte gegliedert! ( 1 u. 2 )
Allen relativen Links/Verweisen wurde der Pfad "fileadmin/template/main/” vorangestellt. ( 3 )
Dies geschah weil der Auto-parser dazu angewiesen wurde "elements” wie folgt zu behandeln:
...
elements {BODY.all = 1
BODY.all.subpartMarker = DOCUMENT_BODY
HEAD.all = 1
HEAD.all.subpartMarker = DOCUMENT_HEADER
HEAD.rmTagSections = title
TD.all = 1
}
...
("Elements” sind alle Tags, die von einem Start- und Endtag eingeschlossen sind, wie z.B. beim <td> Tag. Tags ohne Endtag, wie z.B. <img>), müssen mit der "single” Eigenschaft des Template Auto-parsers definiert sein.
Die Konfiguration dieser Elemente bedeutet also, dass
a) das Element <body> soll mit der Markierung "###DOCUMENT_BODY###” als Teilabschnitt gekennzeichnet werden. soll
b) das Element <head> soll mit der Teilabschnittsmarkierung "###DOCUMENT_HEADER###” umgeben werden. soll. Weiterhin sollen alle <title> Bereiche gelöscht werden. (Wir möchten nicht den Titel von Raphaels Vorlage auf allen unseren TYPO3-Seiten).
c) Alle <td> Elemente, die gefunden wurden sollen markiert werden, solange aber keine Unterabschnitte definiert sind, werden nur <td>-Tags mit einem "id”- oder "class”-Attribut bearbeitet und mit einer Unterabschnittsmarkierung versehen. So wird der Tag <td id ="menu_1">den Inhalt aufnehmen und von den Unterabschnittsmarkierungen "<!--###menu_1###-->...<!--###menu_1###-->” umschlossen.
Was machen wir nun damit?
Die Antwort lautet, dass der Auto-parser es Raphael erlaubt, auf modernen CSS-Techniken basierte Templates mit bewussten Gebrauch von id- und class-Attributen zu erstellen. Gleichzeitig dienen diese Attribute als "Markierungen” für TYPO3, um Teile der Vorlage des Templates durch dynamischen Inhalt zu ersetzen. Einfach für Raphael, den Designer, klarer für Benoit, den Entwickler, und gut für Picoutos Finanzen, da für die Umwandlung und manuelle Anpassung der Vorlagen weniger Zeit benötigt wird.
Schauen Sie sich folgende Abbildung an. Das ist, was wir machen möchten:
Die Template-Datei wird vom Auto-parser gelesen, die Ausgabe wird an das TEMPLATE cObject weitergeleitet, welches die Unterabschnitte ersetzt und letztendlich werden die Body- und Headerabschnitte der TYPO3-Seite eingefügt.
Dies wird über das unten gezeigte TypoScript erledigt, das in das Setup-Feld des Template Datensatzes eingegeben wird. Es ist ein längeres Listing, aber nehmen Sie sich die Zeit es zu verstehen:
# Configuring the Auto-Parser for main template:
plugin.tx_automaketemplate_pi1 {# Read the template file:
content = FILE
content.file = fileadmin/template/main/template_1.html
# Here we define which elements in the HTML that
# should be wrapped in subpart-comments:
elements {BODY.all = 1
BODY.all.subpartMarker = DOCUMENT_BODY
HEAD.all = 1
HEAD.all.subpartMarker = DOCUMENT_HEADER
HEAD.rmTagSections = title
TD.all = 1
}
# Prefix all relative paths with this value:
relPathPrefix = fileadmin/template/main/
}
# Main TEMPLATE cObject for the BODY
temp.mainTemplate = TEMPLATE
temp.mainTemplate {# Feeding the content from the Auto-parser to the TEMPLATE cObject:
template =< plugin.tx_automaketemplate_pi1
# Select only the content between the <body>-tags
workOnSubpart = DOCUMENT_BODY
# Substitute the ###menu_1### subpart with some example content:
subparts.menu_1 = TEXT
subparts.menu_1.value = HELLO WORLD - MENU
# Substitute the ###content### subpart with some example content:
subparts.content = TEXT
subparts.content.value = HELLO WORLD - CONTENT
}
# Main TEMPLATE cObject for the HEAD
temp.headTemplate = TEMPLATE
temp.headTemplate {# Feeding the content from the Auto-parser to the TEMPLATE cObject:
template =< plugin.tx_automaketemplate_pi1
# Select only the content between the <head>-tags
workOnSubpart = DOCUMENT_HEADER
}
# Default PAGE object:
page = PAGE
page.typeNum = 0
# Copying the content from TEMPLATE for <body>-section:
page.10 < temp.mainTemplate
# Copying the content from TEMPLATE for <head>-section:
page.headerData.10 < temp.headTemplate
Das führt dann zu dieser Baumstruktur.
hello world - Menü
Wie Sie sehen, werden die cObjects "temp.mainTemplate” und "temp.headTemplate” an Ihre jeweilige Position im Objektbaum kopiert, da jedes dieser cObjects eine Referenz auf das cObject USER des Template Auto-parser Plugins beinhaltet.
Beachten Sie bitte, dass der Objektpfad "page.headerData” eine numerische Liste der Content Objects ist, die den Inhalt für den <head>-Bereich der Seite definiert.
Ergebnis
Das Ergebnis sieht dann so aus:
Beachten Sie, dass die Bereiche des Menüs und des Inhaltes mit den Inhalten der cObjects TEXT ersetzt wurden, die nur zum Testen dienten.
Der HTML-Quellcode sieht jetzt folgendermaßen aus:
Der <head> Bereich ist ohne <title> Tag eingefügt worden.
Der <body> Bereich mit dem ersetzten Inhalt eingefügt.
Die Unterabschnitte ###header### und ###footer### wurden nicht ersetzt, da sie im TEMPLATE cObject "temp.mainTemplate” nicht definiert wurden! Das ist also kein Fehler.
Die Unterabschnitte ###menu_1### und ###content### wurden richtig ersetzt, da das TEMPLATE cObject "temp.mainTemplate” dafür konfiguriert war.
Zusammenfassung
TYPO3 hat aus Raphaels Vorlage automatisch den Inhalt für den <head> und <body> Bereich extrahiert, hat die relativen Pfade für die Grafiken, Links, Formulare und Formatvorlagen angepasst, hat den dynamischen Inhalt eingefügt, sowohl im <head> als auch im <body> Bereich der ausgegebenen Seite.
Der grundlegende Rahmenkonstrukt (Framework) ist jetzt betriebsbereit - wir müssen nur noch etwas dynamischen Inhalt hinzufügen, und zwar für das Menü und den Seiteninhalt! Dies geschieht am effektivsten mit TypoScript cObjects um die Menü- und Inhaltselemente zu generieren.
Das zweistufige Menü auf der linken Seite des Templates muss dynamisch erstellt werden um die Seitenstruktur des Backends wiederzugeben. Obwohl uns Raphael eine gute und klare Vorlage mit einfachen Bezeichnern für die Menüeinträge erstellt hat, ist es nicht die beste Lösung, einfach diese zu verwenden und diese Bereiche vom Auto-parser als Teilabschnitte extrahieren zu lassen. Stattdessen sollten wir dies explizit programmieren. Das bedeutet, dass Mr. Benoit Raphaels Vorlage manuell überprüfen muss. Dabei muss er sorgfältig herausfinden, was ein einzelner Menüeintrag enthält und dann diese Teile herausfiltern und dazu benutzen, das Objekt für die Generierung des Menüs einzurichten. Das bedeutet desweiteren, dass Raphael die zugrunde liegende Struktur nicht mehr verändern kann, ohne Benoit diese Änderungen mitzuteilen. Benoit muss dann diese Änderungen am Templatedatensatz entsprechend vornehmen. Aber wenn Raphael seine Arbeit ordentlich gemacht hat wird dies nicht nötig sein. Die CSS Formatvorlage sollte dann für alle Änderungen am Design genutzt werden.
Menüobjekte und Zustände der Einträge
Zuerst werden wir das cObject HMENU zum Generieren des Menüs verwenden. Dieses Objekt nutzt "Menüobjekte” vom Typ TMENU, GMENU, GMENU_LAYERS usw. für jede gewünschte Menüebene (abhängig von der Art des Menüs, das Sie verwenden möchten - textbasiert, grafisch, mit Ebenen usw.).
Für jedes "Menüobjekt” (also z.B. für TMENU oder GMENU) legen Sie für die jeweilige Ebene allgemeine Eigenschaften fest (z.B. Höhe und Breite für ein grafisches GMENU Element). Die jeweiligen Eigenschaften des Elements sind immer für einen speziellen Zustand definiert. Der Normalzustand (NO) muss immer definiert sein, aber zusätzlich können wir z.B. einen aktiven Zustand definieren (ACT) - dies bedeutet "wie soll der Menüeintrag aussehen, wenn wir auf einer Seite auf der selben Ebene oder unterhalb dieses Eintrag sind”.
Dieses Tutorial soll nun nicht auf alle Einzelheiten des HMENU cObjects und aller davon abgeleiteten Möglichkeiten eingehen. Lesen Sie dazu bitte TypoScript by Example.
Schauen wir uns erstmal den Quellcode von Raphaels Vorlage an:
Wie Sie sehen, hat er für jeden Menüeintrag unabhängig von dessen Ebene und Zustand einen <div> Tag eingefügt. Der Unterschied liegt ausschließlich im Wert des Attributs der Klasse! Das ist ein kluges Design, da das Einfügen der Abschnittsmarkierungen so einfach ist und Benoit eine Menge Zeit bei der Implementierung spart! :-) Und die eigentliche visuelle Steuerung liegt außerhalb von TYPO3, nämlich in der CSS-Formatvorlage.
Achten Sie darauf, wie Raphael "aktive“ Menüeinträge gekennzeichnet hat.
Die Implementierung dieses Menüs ist sehr einfach. Und so geht's:
Zuerst definieren Sie ein temporäres Objekt am Anfang des Template-Datensatzes (vor der Definition von "temp.mainTemplate”):
# Menu 1 cObject
temp.menu_1 = HMENU
# First level menu-object, textual
temp.menu_1.1 = TMENU
temp.menu_1.1 {# Normal state properties
NO.allWrap = <div class="menu1-level1-no"> | </div>
# Enable active state and set properties:
ACT = 1
ACT.allWrap = <div class="menu1-level1-act"> | </div>
}
# Second level menu-object, textual
temp.menu_1.2 = TMENU
temp.menu_1.2 {# Normal state properties
NO.allWrap = <div class="menu1-level2-no"> | </div>
# Enable active state and set properties:
ACT = 1
ACT.allWrap = <div class="menu1-level2-act"> | </div>
}
Beachten Sie die roten Zeilen, die dem HTML-Code entsprechen, den Mr. Benoit aus Mr. Raphaels Vorlage kopiert hat. Nun ist er fest im Template Datensatz gespeichert.
Das einzige, was Sie jetzt noch tun müssen, ist dieses Objekt zu kopieren, so dass es das cObject des Teilabschnitts "menu_1” wird:
...
# Main TEMPLATE cObject for the BODY
temp.mainTemplate = TEMPLATE
temp.mainTemplate {# Feeding the content from the Auto-parser to the TEMPLATE cObject:
template =< plugin.tx_automaketemplate_pi1
# Select only the content between the <body>-tags
workOnSubpart = DOCUMENT_BODY
# Substitute the ###menu_1### subpart with dynamic menu:
subparts.menu_1 < temp.menu_1
# Substitute the ###content### subpart with some example content:
subparts.content = TEXT
subparts.content.value = HELLO WORLD - CONTENT
...
(Die geänderten Zeilen sind rot dargestellt)
... und das Ergebnis spricht für sich selbst:
Die Seitenstruktur (unten) wird exakt vom Menü oben wiedergegeben!
Und wie einfach ist es das Menüdesign zu ändern? Versuchen Sie es und ändern Sie es im Stylesheet "fileadmin/template/main/res/stylesheet.css”:
Wie Sie sehen erlaubt diese Methode die Hauptfaktoren der visuelllen Gestaltung (das HTML-Template und die Formatvorgaben im Stylesheet) außerhalb von TYPO3 anzusiedeln. Es gibt hierbei einen direkten und bequemen Zugang für Mr. Raphael, den HTML/CSS-Designer. Und Mr. Benoit musste nur wenig Aufwand betreiben um das Frontend mittels TypoScript so zu konfigurieren, dass es Raphaels Design verwendet. Und beide können in der individuell für sie angenehmsten Weise entwickeln.
Wenn Sie mehr mit dem HMENUObjekt und damit zusammenhängendenDingen experimentieren möchten, schauen Sie dazu wieder in TypoScript by Example.
Der beliebteste Weg, Seiteninhalte TYPO3 zu verwalten, ist Inhaltselemente in der Tabelle tt_content zu erstellen. Tatsächlich kenne ich Niemandem der dieses Konzept nicht verwendet, da es so flexibel, leistungsfähig und einfach einzubinden ist. Jedoch könnte dieses Konzept möglicherweise mit Ihren Vorstellungen von der Einbindung strukturierter Inhalte in HTML-Templates kollidieren.
Inhaltselemente haben zahlreiche vordefinierte Typen (Text, Text mit Bild, Listen, Eingabefeldern usw.) und ein statisches Template mit vordefinierten TypoScript wird für gewöhnlich ohne weiteres Konfigurieren diese Elemente für Sie generieren. Das ist wirklich genial! Da Inhaltselemente einen bestimmten Typ haben und jeder Typ zusätzliche Optionen, ist es unmöglich Inhaltselemente mit dem festen Konzept einer HTML-Vorlage zu generieren. Das passt einfach nicht zusammen und Sie werden das erkennen, wenn Sie etwas mehr damit experimentiert haben. Folglich werden mit PHP-Funktionen kombinierte cObjects genutzt, um Inhalte mit komplexen Bedingungen zu generieren.
Da wir keine Inhaltselemente durch die Verwendung von HTML-Templates darstellen können, wie gehen wir also in diesem Fall vor? Indem Sie die neueste Herangehensweise zur Darstellung von Inhaltselementen nutzen, die Extension "css_styled_content”. Hiermit wird der gesamte Inhalt in einfachen HTML-Elementen eingebunden - und das Festlegen der Formatvorgaben bleibt Ihrer externen CSS-Formatvorlage überlassen.
Schauen wir, wie das geht. Zuerst erstellen wir ein Inhaltselement:
Ein Inhaltselement
Im Listenmodul klicken Sie bitte auf das Icon der Seite "License C” und wählen dann "Neu”. Im rechten Frame klicken Sie dann auf den Link des Assistenten für den Seiteninhalt:
Wählen Sie "Text mit rechts liegendem Bild” und dann die Spalte "Normal":
Nachdem Sie den Inhalt eingegeben und gespeichert haben sollten Sie folgende Ausgabe sehen:
Jetzt haben wir auch ein Inhaltselement auf der Seite "License C” verfügbar:
Einfügen eines statischen Templates
Um das Inhaltselement anzeigen zu lassen, müssen wir ein statisches Template einfügen, welches die vielen hundert Zeilen TypoScript-Code für die spätere Ausgabe erstellt. Dazu ist der gesamte Template-Datensatz zu bearbeiten.
Zuerst müssen Sie natürlich die Extension "css_styled_content” installieren.
Klicken Sie auf das Installationssymbol und bestätigen Sie die Änderungen auf der nächsten Seite.
Zurück zum Template: Im Template Modul klicken Sie auf diesen Link:
Dann sollte der "CSS Styled Content” als "static file” im Bereich "Include Static (from extensions)” verfügbar sein:
Klicken Sie darauf und sichern Sie das Template. Danach wechseln in den "Template Analyzer“ und betrachten dort das Template:
Der "Template Analyzer” zeigt die Hierarchie der Template-Datensätze und statischen Templates, die im aktuellen "main template record” eingebunden sind. Durch Anwahl des statischen Templates wird dieses vor Generierung des TypoScript-codes im Template-Datensatz "NEW SITE“ eingebunden. Das bedeutet, dass jedes Objekt, das innerhalb von "EXT:css_styled_content/.....” definiert ist, im Template-Datensatz "NEW SITE” benutzt und dort hineinkopiert werden kann. Das werden wir jetzt auch tun, da die statische Vorlage ein Objekt "styles.content.get” enthält. Dabei handelt es sich um ein Inhaltselement, das alle Inhaltselemente aus der Spalte "Normal” der aktuellen Seite auswählt.
Ändern Sie also diesen Teil der Definition des "temp.mainTemplate” Objekts im Template-Datensatz:
...
# Main TEMPLATE cObject for the BODY
temp.mainTemplate = TEMPLATE
temp.mainTemplate {# Feeding the content from the Auto-parser to the TEMPLATE cObject:
template =< plugin.tx_automaketemplate_pi1
# Select only the content between the <body>-tags
workOnSubpart = DOCUMENT_BODY
# Substitute the ###menu_1### subpart with dynamic menu:
subparts.menu_1 < temp.menu_1
# Substitute the ###content### subpart with some example content:
subparts.content < styles.content.get
...
Nach einem Aktualisieren des Frontends im Browsers sollte die Seite "License C” anschließend so aussehen:
Und der HTML-Quelltext sieht dann so aus:
Anscheinend wurde die Überschrift des Inhaltselements in <h1> Tags eingeschlossen generiert. Wenn Sie einen anderen "Layout”-Typ wählen, kann dies auch <h2> (für Layout 2) sein.
Jede Zeile des Textes im Body wird in mit <p> Tags eingeschlossen und erhält die Zuweisung der Klasse "bodytext“. So können Sie die Darstellung von Seiteninhalten über Ihre Stylesheet-Formatvorlage steuern. Beachten Sie bitte wie Raphael bereits schon den Blindtext in der Template-Datei unter Verwendung von "<p class="bodytext">” Tags gestaltet hat!
Jedes eingefügte Inhaltselement erhält einen <a> Tag, welcher als Wert des Attributes name seine uid beinhaltet. Dieser Ankerpunkt kann zum direkten Aufruf einer bestimmten Position innerhalb der Seite genutzt werden.
Dies hat das HMENU Objekt beim Aufbau des zweistufigen Menüs eingefügt. Beachten Sie auch, wie der Link automatisch auf die richtige Seite verweist und wie eine zusätzliche "onFocus” Routine eingefügt ist.
Der Objektbaum?
Was haben wir jetzt durch das Kopieren von diesem angeblichen Inhaltselement "styles.content.get” wirklich in den Objektbaum eingefügt? Schauen wir im Object Browser nach:
Hier sind zwei interessant Punkte:
Der Objektpfad "styles.content.get” beinhaltete offensichtlich ein cObject vom Typ CONTENT. Wenn wir uns die Attribute anschauen, sieht es danach aus, dass dieses cObject Datensätze aus der Tabelle "tt_content” auswählt und sie nach dem Feldeintrag "sorting” geordnet ausgibt. Für weitere Informationen zum CONTENT cObject folgend sie diesem Link.
Desweiteren wurde ein komplett neues Toplevel Object (TLO) definiert - "tt_content”, Standardmäßig nutzt das cObject CONTENT dieses TLO (ebenfalls als cObject definiert) um die Ausgabe der gefundenen Datensätze zu generieren ( 1 ). Wie wir sehen, wird das "tt_content” TLO als USER cObject definiert, das eine PHP-Funktion der "css_styled_content” Extension aufruft - dafür haben wir ja auch diese Erweiterung installiert. Für weitere Einzelheiten schauen Sie bitte in die Dokumentation zur "css_styled_content” Extension.
Die ganz Neugierigen können auch mal in die Klasse "tx_cssstyledcontent_pi1” schauen und die dahinter steckende Logik betrachten. Hier dazu ein Ausschnitt aus der Funktion main():
function main($content,$conf){$this->conf = $conf;
// This value is the Content Element Type - determines WHAT kind of element to render...
$CTypeValue = (string)$this->cObj->data["CType"];
$content="";
switch($CTypeValue){case "header":
$content = $this->getHeader().$this->render_subheader();
break;
case "bullets":
$content = $this->getHeader().$this->render_bullets();
break;
case "table":
$content = $this->getHeader().$this->render_table();
break;
case "text":
$content = $this->getHeader().$this->render_text();
break;
case "image":
$content = $this->getHeader().$this->render_image();
break;
case "textpic":
$content = $this->render_textpic();
break;
...
Wenn Sie XHTML konforme Webseiten erstellen möchten, ist auch das mit TYPO3 möglich. Die Version 3.6.0 von TYPO3 zielt darauf ab, XHTML-Konformität so weit als möglich zu integrieren (Level: XHTML transitional).
In diesem Tutorial müssen wir sicherstellen, dass die HTML-Vorlage mit XHTML konform ist. Das ist der erste Schritt und darauf müssen Sie achten.
Der nächste Schritt ist dafür zu sorgen, dass TYPO3 den XHTML Dokumententyp ausgibt. Dies wird durch eine einfache Zeile im Setup-Feld des Haupttemplate-Datensatzes erreicht:
page.config.doctype = xhtml_trans
Die Menüs müssen natürlich auch entsprechend konform sein. Zum jetzigen Zeitpunkt werden Zeichen wie "&” auch als solche ausgegeben. Das sollte vermieden werden. In den TypoScript-Code für das Menü sollten diese vier Zeilen (in rot dargestellt) eingefügt werden:
# Menu 1 cObject
temp.menu_1 = HMENU
# First level menu-object, textual
temp.menu_1.1 = TMENU
temp.menu_1.1 {# Normal state properties
NO.allWrap = <div class="menu1-level1-no"> | </div>
NO.stdWrap.htmlSpecialChars = 1
# Enable active state and set properties:
ACT = 1
ACT.stdWrap.htmlSpecialChars = 1
ACT.allWrap = <div class="menu1-level1-act"> | </div>
}
# Second level menu-object, textual
temp.menu_1.2 = TMENU
temp.menu_1.2 {# Normal state properties
NO.allWrap = <div class="menu1-level2-no"> | </div>
NO.stdWrap.htmlSpecialChars = 1
# Enable active state and set properties:
ACT = 1
ACT.stdWrap.htmlSpecialChars = 1
ACT.allWrap = <div class="menu1-level2-act"> | </div>
}
Dadurch wird gewährleistet, das die Bezeichnungen der Menüeinträge die PHP-Funktion htmlspecialchars() durchlaufen, die z.B. "&” in "&” umwandelt.
Danach können Sie die Site z.B. mit Opera 7 testen. Opera besitzt eine angenehme Abkürzung zum Überprüfen der Formatkonformität der Seite.
Klicken Sie dazu mit der rechten Maustaste in die HTML-Seite, unter "Frame” finden Sie dann den Eintrag "Validate source”:
Opera sendet dann den Quelltext automatisch an die Seite "http://validator.w3.org/” und bei Erfolg sollten Sie folgendes Ergebnis sehen:
Wir haben nun unsere Website fertiggestellt. Mr. Benoit und Mr. Raphael haben ihre Aufgaben erledigt und vielleicht hat Mr. Picouto bereits nun den gesamten Inhalt der einzelnen Unterseiten des Baumes eingefügt. Das wissen wir nicht. Aber die Grundlage der Website selbst ist fertig. Sie basiert auf einer regulären HTML-Datei im Verzeichnis "fileadmin/template/main” und nur das Menü und der Inhalt werden wie benötigt dynamisch ersetzt.
Bessere Struktur des Templates
Das letzte was wir noch tun müssen ist den Template-Datensatz etwas besser zu organisieren. Auch wenn nur wenig TypoScript genutzt wird, sehen wir doch, dass der Datensatz schnell groß und unübersichtlich wird. Die Lösung ist eine Reihe von Template-Datensätzen zu erstellen, um diese einzubinden.
Dazu sollten Sie zuerst eine komplett neue Seite auf der untersten Ebene (root level) erstellen. Wählen Sie den Typ "sysFolder” (das bedeutet "Speicherplatz für jedes gewünschte Datenbankelement”)..
Dann erstellen Sie drei Template-Datensätze wie in dieser Abbildung:
Kreuzen Sie nirgendwo die "Clear” -Anwahlfelder an! Kopieren Sie lediglich nur die passenden Objektdefinitionen "temp.xxxx” des Templates "NEW SITE“ in die drei neuerstellten Template-Datensätze. aus der "NEW SITE” in die neue Vorlage. Die Reihenfolge spielt dabei keine Rolle.
Der nächste Schritt ist diese drei Templates in das Haupt-Template der "NEW SITE” einzubinden. Also bearbeiten Sie das Template von "NEW SITE”.
Die Reihenfolge ist wichtig: das zuerst aufgeführte Template wird zuerst eingefügt und da der Vorlagensatz "ext: Main TEMPLATE cObject” Kopien von z.B. dem Objekt "temp.menu_1” erstellt, muss dieses dann als letztes eingefügt werden.
Speichern Sie den Template-Datensatz und betrachten Sie die neue Struktur im Template Analyzer:
Wie Sie sehen können wird erst das statische Template eingebunden, dann die drei "Basisvorlagen” aus unserem Ordner und zum Schluss der Haupttemplatesatz (main template record). Sie können den Titel jeder Vorlage anklicken und darunter deren Inhalt ansehen.
Wie Sie in diesem Abschnitt gesehen haben, können Sie sehr komfortabel das HTML-Template eines Webdesigners implementieren. Volle Rückwärtskompatibilität zur zum ursprünglichen Template ist dabei sichergestellt (da keine Markierungen von Hand in die Vorlage eingefügt werden müssen). Weiterhin werden nur vertraute Techniken wie HTML und CSS genutzt - es werden keine verwirrenden XSLT-Vorlagen benötigt (obwohl Mr. Benoit natürlich auch XSLT für die Ausgabe auf PHP-Ebene hätte nutzen können).
Auch wenn der Designer, Mr. Raphael, fast grenzenlose Freiheit hat, sollte er doch die Grundlagen von statischem und dynamischem Inhalt kennen.
Klarstellen welche Teile dynamisch und welche statisch sind
Mr. Raphael muss in erster Linie verstehen - vermutlich zusammen mit dem ganzen Team - welche Teile der Seite statisch und welche dynamisch sind. Statische Teile bleiben von TYPO3 unberührt. Die dynamischen Teile sollen jedoch mit dem Inhalt ersetzt werden, der aus den Datenquellen innerhalb von TYPO3 kommt.
Wenn dynamische Bereiche erkannt werden, muss Raphael folgendes unternehmen:
Dieser Bereich muss von einem einzelnen HTML-Element umschlossen sein, dabei kann es sich um <div>, <td> oder <span> Elemente handeln.
Das umschließende Element muss entweder ein id oder class Attribut besitzen, welches das Template Auto-parser plugin finden und in Unterabschnittmarkierungen einschließen muss, z.B. <td id="content"> für die Tabellenzelle, die den Inhalt aufnehmen soll.
Es ist völlig in Ordnung den Blindtext in der Vorlage zu belassen - dieser wird ohnehin schließlich durch die dynamischen Teile ersetzt.
Vereinfachen Sie die Darstellungsformatierung, nutzen Sie CSS Formatvorlagen
Wir schreiben das Jahr 2004 und es wird Zeit dem <font> Tag und den bgcolor-Attributen auf Wiedersehen zu sagen. Packen Sie das alles in eine externe Formatvorlage. Für den Einsatz eines CMS wird dadurch erreicht so viele externe Techniken wie irgend möglich nutzen zu können und nur soviel CMS-spezifische Technologien (wie TypoScript Template-Datensätze) wie wirklich benötigt zu verwenden.
In diesem Abschnitt dieses Tutorials war das beste Beispiel hierfür das zweistufige Menü:
<!-- Menu table cell: -->
<td id="menu_1">
<div class="menu1-level1-no"><a href="#">Menu item 1</a></div>
<div class="menu1-level1-no"><a href="#">Menu item 2</a></div>
<div class="menu1-level1-act"><a href="#">Menu item 3 (act)</a></div>
<div class="menu1-level2-no"><a href="#">Level 2 item</a></div>
<div class="menu1-level2-no"><a href="#">Level 2 item</a></div>
<div class="menu1-level2-act"><a href="#">Level 2 item (act)</a></div>
<div class="menu1-level1-no"><a href="#">Menu item 2</a></div>
</td>
Schauen wir uns ein einzelnes Element an (oben rot dargestellt). Es hätte auch so oder ähnlich aussehen können:
<!-- Menu table cell: -->
<td id="menu_1">
<!-- Menu item level 1, begin -->
<table border="0" cellpadding="0" cellspacing="0" width="95%"><tr>
<td bgcolor="#eeeeee"><font face="verdana" size="2">
<a href="#" class="menu1-items">Menu item 1</a>
</font></td>
</tr>
<tr>
<td><img src="gray_dotted_line.gif" width="180" height="1" alt=""/></td>
</tr>
</table>
<!-- Menu item level 1, end -->
...
</td>
Jetzt denken Sie wieder zurück an unser TMENUITEM Object im Template-Datensatz. Es war so definiert, dass es Elemente ausschließlich mit diesem einfachen <div> Bereich umschließen sollte:
temp.menu_1.1 {
# Normal state properties
NO.allWrap = <div class="menu1-level1-no"> | </div>
...
... und ja, Sie haben es erraten: mit der oben genannten Implementierung würden wir die Elemente in etwa wie folgt eingeschlossen haben müssen:
temp.menu_1.1 {
# Normal state properties
NO.allWrap (
<table border="0" cellpadding="0" cellspacing="0" width="95%"><tr>
<td bgcolor="#eeeeee"><font face="verdana" size="2">
|
</font></td>
</tr>
<tr>
<td><img src="gray_dotted_line.gif" width="180" height="1" alt=""/></td>
</tr>
</table>
)
...
Aber auch das reichte noch nicht. Sie müssten noch den Klassennamen für den <a> Tag um eine weitere Eigenschaft des "No“ Status Objektes sowie den Pfad der Grafik der grau gepunkteten Linie ergänzen - irgendwas mit "fileadmin/template/main/”.
Benutzen Sie keine class Attribute für dynamische Links
Viele Designer sind der Meinung, es wäre am besten, wenn sie ihre Menüelemente und andere Links mit Klassenattributen für den <a> Tag versehen, z.B. so:
<!-- Menu table cell: -->
<td id="menu_1">
<div><a href="#" class="menu1-level1-no">Menu item 1</a></div>
...
</td>
Jedoch ist das nicht der Fall, da genau dieser <a> Tag dynamisch von TYPO3 erzeugt wird! Damit dies mit unserem TMENUITEM funktioniert benötigen wir weitere TypoScript Eigenschaften:
temp.menu_1.1 {# Normal state properties
NO.allWrap = <div> |</div>
NO.ATagParams = class="menu1-level1-no"
...
Das führt zu komplexerem TypoScript Code und die Stylesheet-Formatvorlage wird unflexibler. Um z.B. das Aussehen von Links über CSS zu steuern, können Sie folgendes nutzen:
A.menu1-level1-no { color: navy; }A.menu1-level1-no:hover { color: red; }... aber Sie können dann nicht die Bearbeitung der <div> Elemente kontrollieren, da diese nicht mit class oder id markiert sind! Wenn die den Weg, wie wir ihn oben beschritten haben, gehen, wo das <a> Element keine Klassenattribute , sondern nur das <div> Elemente welche hatte, dann haben Sie beide im Griff: sowohl die <div> Ebene als auch alle darin enthaltenen Links:
DIV.menu1-level1-no { padding: 2px 2px 2px 2px; }DIV.menu1-level1-no A { color: navy; }DIV.menu1-level1-no A:hover { color: red; }Finger weg von row/colspans!
Das ist der Nummer Eins Killer für dynamischen Inhalt - colspans im dynamischen Inhalt zu benutzen! Das wird dadurch geradezu ausgeschlossen. Zum Verständnis schauen Sie sich diese Implementierung des Menüs an:
<tr>
<!-- Menu table cell: -->
<td class="menu1-level1-no"><a href="#">Menu item 1</a></td>
<!-- Page Content Area table cell: -->
<td id="content" rowspan="7">
. . .
</td>
</tr>
<tr>
<td class="menu1-level1-no"><a href="#">Menu item 2</a></td>
</tr>
<tr>
<td class="menu1-level1-act"><a href="#">Menu item 3 (act)</a></td>
</tr>
<tr>
<td class="menu1-level2-no"><a href="#">Level 2 item</a></td>
</tr>
<tr>
<td class="menu1-level2-no"><a href="#">Level 2 item</a></td>
</tr>
<tr>
<td class="menu1-level2-act"><a href="#">Level 2 item (act)</a></td>
</tr>
<tr>
<td class="menu1-level1-no"><a href="#">Menu item 2</a></td>
</tr>
Das ist nahezu unmöglich zu implementieren. Der erste Menüeintrag ist in einer Tabellenzeile zusammen mit der Zelle für den Inhalt. Die restlichen sechs Menüeinträge befinden sich in eigenen Zeilen. Weiterhin benötigt die Zelle mit dem Inhalt ein colspan="7", um alle Zeilen des Menüs zu umspannen!
Aus technischer Sicht ist dies ein sehr schlechtes Design, da:
die Beschreibung des Menüs auf verschiedene Bereiche des HTML-Codes verteilt ist
es eine Abhängigkeit zwischen dem colspan Attribut und der (dynamischen!) Anzahl der Menüeinträge gibt.
Designer sollten solches Design vermeiden. Stattdessen sollten Designer wie Mr. Raphael lernen, die Beschreibung für dynamische Elemente wie Menüs so zu erstellen, dass sie leicht reproduzierbar sind. Hier ein gutes Beispiel:
Von unserer aktuellen Vorlage:
<!-- Menu table cell: -->
<td id="menu_1">
<div class="menu1-level1-no"><a href="#">Menu item 1</a></div>
<div class="menu1-level1-no"><a href="#">Menu item 2</a></div>
<div class="menu1-level1-act"><a href="#">Menu item 3 (act)</a></div>
...
</td>
Alternativ mit einer Tabelle:
<!-- Menu table cell: -->
<td id="menu_1">
<table border="0" cellspacing="0" cellpadding="0">
<tr><td class="menu1-level1-no"><a href="#">Menu item 1</a></td></tr>
<tr><td class="menu1-level1-no"><a href="#">Menu item 2</a></td></tr>
<tr><td class="menu1-level1-act"><a href="#">Menu item 3 (act)</a></td></tr>
...
</table>
</td>
Die Beispiele könnten auch noch komplexer sein, ähnlich dem obigen, das jedes Element in einer eigenen Tabelle einschloß. Entscheidend ist aber nicht die Menge an HTML pro Eintrag, entscheidend ist die Reproduzierbarkeit.
Mr. Raphael sollte diesen Umständen Rechnung tragen, damit Mr. Benoit nicht Stunden damit verbringen muss eine mangelhafte HTML-Struktur zu implementieren. Eine schnelle Umsetzung des Projektes als Ergebnis der symbiotischen Zusammenarbeit von Raphael und Benoit könnte auch Mr. Picouto glücklich machen - vielleicht lädt er sie ja zum Mittagessen ein!