Download als PDF

Einfache Website aufbauen

Version 0.1 von Daniel Neumann, Anja Beuth

Voraussetzungen

Sie sollten sich bereits mit dem onion.net Editor auseinandergesetzt und eventuell bereits einige Tutorials für Einsteiger durchgespielt haben.

Hilfreich sind Kenntnisse in XML/XML-Schema/XSLT oder in einer beliebigen Programmiersprache.

Wenn Sie mit der onion.net Community Edition oder einem anderen leeren System arbeiten, aktivieren Sie zuerst das Modulsystem (siehe auch »Erste Schritte«). Anschließend sollten Sie das Modul »XSL Renderengine« herunterladen und importieren.

Beschreibung

In diesem Tutorial wird Schritt für Schritt eine einfache Website aufgebaut. Ausgangspunkt dabei ist ein leerer onion.net Editor. Nach und nach werden Informationsmodell, Struktur, Editorlokalisierung und Rendering aufgebaut.

Das Ergebnis ist eine einfache Website mit einer Navigation, einem Ariadne-Faden und natürlich pflegbarem Inhalt.  Die Website bietet also nur rudimentäre Funktionen, soll aber den generellen Aufbau und das Vorgehen in onion.net verdeutlichen.

Informationsmodell aufbauen

Im ersten Schritt werden wir die für die Website benötigten Schemata anlegen. Für unsere Website benötigen wir zwei verschiedene Seitentypen: eine Site und eine Page.

Die Site wird als Startseite der Website genutzt und kann in einem Web-Auftritt nur einmal vorhanden sein. Außerdem können in der Site Inhalte gepflegt werden, die für die gesamte Website gelten und sich vererben sollen. So soll beispielsweise das Logo auf jeder Seite des Webauftritts zu sehen sein. Aber die Grafik an sich sollte nach Möglichkeit nur an einer einzigen Stelle hinterlegt werden. 
Unter einer Site wiederum sollen Pages angelegt werden können. Aus dieser Struktur wird dann später automatisch die Navigation gebildet. Einleuchtend ist, dass unterhalb der Site keine weiteren Sites angelegt werden können sollen, sondern nur Pages, also Inhaltsseiten.

Da sich Site und Page grundsätzlich dennoch sehr ähneln, werden wir mit Vererbung arbeiten. Viele Inhalte sind sowohl für eine Site als auch für Pages zu pflegen, zum Beispiel Freitext im Inhaltsbereich. Darüber hinaus handelt es sich bei beiden Typen um besuchbare Strukturpunkte. Um die Vererbung zu realisieren erstellen wir in den Schemata ein Objekt Node, von dem Site und Page abgeleitet werden.

http://example.com

Wir legen zunächst ein abstraktes Schema "http://example.com" an, unter dem die neuen Schemata gruppiert werden sollen.

Wechseln Sie dazu im onion.net-Editor in die Schemaverwaltung und legen Sie unterhalb von http://onionworks.net/2004/schema/object/userdefined ein neues Schema mit dem Namen http://example.com an. Ein neues Schema können Sie über das Kontextmenü anlegen, das Sie über einen Rechtsklick auf ./userdefined öffnen können. Den Namen des Schemas vergeben Sie im Dialog "Neues Schema" im Feld "Ort". Wählen Sie in diesem Dialog auch die Option "Abstraktes Schema" aus.


Schema für abstrakten Typ "node" anlegen

Unterhalb von "http://example.com" legen Sie nun das Schema "http://example.com/node" an. Dabei handelt es sich auch um ein abstraktes Schema, da von Node keine konkreten Objekte erzeugt werden können sollen. 

Beim Anlegen eines neuen Schemas wird im Assistenten immer die SchemaLocation des Elternschemas als Vorschlag übernommen. In diesem Fall ist das Feld Ort also schon mit "http://example.com/" vorbelegt. Sie müssen nur noch jeweils den Namen des neu anzulegenden Schemas hinten anhängen.

Nach dem Anlegen des Schemas definieren Sie nun die Elemente, die dieses Schema beinhalten soll und dann gleichermaßen für die Site und Page verfügbar sind, die von Node abgeleitet werden. In unserem Beispiel gibt es ein Richtext-Feld, das den Inhalt einer Seite aufnehmen soll.

Standardmäßig erhält ein abstraktes Schema folgenden Inhalt:

<xs:schema xmlns:references="http://onionworks.net/2004/references" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:onion="http://onionworks.net/2004/schema" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" onion:schemaLocation="http://example.com/node" />

Erweitern Sie diesen Inhalt wie folgt: 

<xs:schema xmlns:references="http://onionworks.net/2004/references" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:onion="http://onionworks.net/2004/schema" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" onion:schemaLocation="http://example.com/node">
<xs:element name="node" type="node" />
<xs:complexType name="node">
<xs:sequence>
<xs:element name="text" type="xhtml:Flow" />
</xs:sequence>
</xs:complexType>
</xs:schema>

Damit wurde ein Element vom Typ "node" hinzugefügt, dessen Inhalt im »complexType name="node"« definiert wurde. Es besteht aus einem Element "text", das vom Typ "xhtml:Flow" ist und somit ein Richtext-Feld darstellt.

Der Namensraum "xhtml" ist allerdings noch nicht definiert, sodass ein Abspeichern der Änderungen nicht möglich ist. Daher fügen Sie in der zweiten Zeile eine xs:import-Anweisung ein:

<xs:import schemaLocation="" namespace="" />

Den Eintrag unter "schemaLocation" können Sie direkt per Drag And Drop erledigen.

Ziehen Sie aus der Baumansicht links unter dem Punkt "./external" das Schema "http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd" in das Attribut "schemaLocation". In das Attribut "namespace" tragen Sie nun den Wert "http://www.w3.org/1999/xhtml" ein.
Nun muss der Namensraum noch definiert werden. Erweitern Sie dazu das xs:schema-Element um folgendes Attribut.

xmlns:xhtml="http://www.w3.org/1999/xhtml"

Nun lässt sich die Schema-Änderung auch abspeichern. Insgesamt sieht das Schema "node" jetzt wie folgt aus:

<xs:schema xmlns:references="http://onionworks.net/2004/references" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:onion="http://onionworks.net/2004/schema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xhtml="http://www.w3.org/1999/xhtml" elementFormDefault="qualified" onion:schemaLocation="http://example.com/node">
<xs:import schemaLocation="http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd" namespace="http://www.w3.org/1999/xhtml" />
<xs:element name="node" type="node" />
<xs:complexType name="node">
<xs:sequence>
<xs:element name="text" type="xhtml:Flow" />
</xs:sequence>
</xs:complexType>
</xs:schema>

Schema für die Site anlegen

Legen Sie nun unterhalb des Schemas Node das Schema „site“ an. Site soll von Node abgeleitet werden. Wählen Sie dazu im Dialog „Neues Schema“ die Option „Inhaltsmodell ableiten“. Ihnen werden nun in einer Dropdown-Box Schemata angeboten, die Sie ableiten können. In unserem Fall ist das lediglich „node (ComplexType)“: Wählen Sie diesen Punkt aus.

Das Schema des abgeleiteten Objekts sieht nun wie folgt aus:

<xs:schema elementFormDefault="qualified" onion:schemaLocation="http://example.com/node/site">
<xs:redefine schemaLocation="http://example.com/node">
<xs:complexType name="node">
<xs:complexContent>
<xs:extension base="node"> </xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:redefine>
</xs:schema>

Eine Site soll zusätzlich das Logo der Website beinhalten können.

Dazu erweitern Sie die das noch leere Element <xs:extension base="node"> um folgendes Attribut:

<xs:attribute name="logo" type="xlink:binaryReference" />

Um diesen Typen nutzen zu können, muss das xlink-Schema importiert werden. Dies geschieht wie oben schon beschrieben per Drag And Drop in ein <xs:import>-Element aus dem Ordner "./external". In diesem Fall können Sie das Schema sowohl in das Attribut "schemaLocation" als auch "namespace" ziehen:

<xs:import schemaLocation="http://www.w3.org/1999/xlink" namespace="http://www.w3.org/1999/xlink" />

Insgesamt sollte der Inhalt nun wie folgt aussehen:

<xs:schema xmlns:references="http://onionworks.net/2004/references" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:onion="http://onionworks.net/2004/schema" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" onion:schemaLocation="http://example.com/node/site">
<xs:import schemaLocation="http://www.w3.org/1999/xlink" namespace="http://www.w3.org/1999/xlink" />
<xs:redefine schemaLocation="http://example.com/node">
<xs:complexType name="node">
<xs:complexContent>
<xs:extension base="node">
<xs:attribute name="logo" type="xlink:binaryReference" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:redefine>
</xs:schema>

Schema für die Page anlegen

Legen Sie nun ein weiteres abgeleitetes Schema "page" unterhalb von node an. Gehen Sie dabei vor wie zuvor unter "site" beschrieben.

Die Page benötigt keine weiteren Elemente oder Attribute als Node, daher kann der Standard-Inhalt des abgeleiteten Schemas verwendet werden.

Kindschemata

Als nächstes legen Sie fest, welche Schemata unter welchen anderen Schemata angelegt werden dürfen. Als erstes soll eine Site unterhalb des onion.net-Root-Elements angelegt werden dürfen, damit überhaupt eine Website dieses Typs im onion.net-Editor angelegt werden kann.

Klappen Sie dazu in der Baumansicht links den Teilbaum ./system auf. Darin liegt das Schema ./systemroot.

Dies ist das Schema des Startpunkts der Baumansicht in der Dokumentenansicht.

Klicken Sie nun auf dieses systemroot-Objekt.

Im rechten Bereich sehen Sie ein Feld "Kindschemata". Dieses enthält all jene Objekte, die unterhalb dieses Schemas angelegt werden dürfen.

Ziehen Sie nun mittels Drag And Drop das eben angelegte Schema site aus der Baumansicht in das Feld "Kindschemata" und speichern das Schema ab.


Als nächstes muss noch definiert werden, dass unter einer Site Pages angelegt werden dürfen sowie weitere Pages unterhalb einer Page.

Klicken Sie dazu zunächst auf das Schema site. Ziehen Sie nun das Schema page in das Feld "Kindschemata" und speichern das Schema ab. Verfahren Sie nun genauso bei dem Schema page und erlauben Sie das Schema page als Kindschema.

Anlegen einer Website-Struktur

Nun können Sie damit anfangen, die ersten Dokumente Ihrer Website anzulegen. Wir entscheiden uns für folgende simple Struktur:

  • Homepage
    • Leistungen
      • Leistungsgruppe 1
      • Leistungsgruppe 2
    • Produkte
      • Produktgruppe 1
      • Produktgruppe 2
    • Kontakt
    • Impressum

Um diese Struktur anzulegen, gehen Sie wie folgt vor.

Klicken Sie in der Dokumentenansicht mit der rechten Maustaste auf das Root-Element und wählen Sie aus dem Kontext-Menü das Element "http://example.com/node/site".


Vergeben Sie im Titel-Feld nun den Namen "Homepage". Wie Sie sehen, können Sie nun auch schon einen Inhalt pflegen. Lassen Sie den Inhalt im Moment noch möglichst einfach – ein kurzer Satz wird genügen. (z.B. "Lorem ipsum dolor sit amet.").

Speichern Sie nun das Dokument ab. Es wird direkt in der Baumansicht ganz unten mit dem Namen "Homepage" erscheinen. Sie können es per Drag And Drop in der Baumansicht ganz nach oben ziehen.

Ein grauer Balken zeigt Ihnen an, wo das Objekt einsortiert wird, wenn Sie die Maustaste loslassen.

Legen Sie nun weitere Seiten unterhalb der "Homepage" an, indem Sie mit der rechten Maustaste auf das Dokument "Homepage" klicken und darunter eine "http://example.com/node/page" anlegen. Vergeben Sie auch bei den Pages einen eindeutigen Namen und pflegen Sie einen kurzen Text auf der Seite. Legen Sie die Dokumente an wie in der Baumstruktur oben vorgeschlagen.

Editor-Customizing

Beim Anlegen der Strukturpunkte ist Ihnen vielleicht aufgefallen, dass im Kontext-Menü und im Objekdetailfenster die Begriffe und SchemaLocations angezeigt wurden, die wir vorher im Schema-Editor vergeben hatten. Diese sehr technische Anzeige ist besonders für Redakteure nicht sehr angenehm. Darüber hinaus können die Objekte im Baum auch nicht so gut auseinander gehalten werden, da sie alle das gleiche Icon besitzen. Daher werden wir nun als nächstes den Editor anpassen.

Icons

Zunächst sollen die Objekte, die mittels der Schemata "site" und "page" angelegt werden können, Icons erhalten, damit sie im Editor besser unterschieden werden können.

Dazu wechseln Sie nun zurück auf die Dokumentenansicht. Öffnen Sie in der Baumansicht den Punkt "Editor". Mit einem Rechtsklick auf "Editor" erstellen Sie ein neues Modul für die Beispiel-Website. Vergeben Sie als Titel "Tutorial-Website" und speichern Sie das Modul ab.

Nach dem Speichern wird das Modul direkt in der Liste der Module unterhalb des Editor-Dokuments erscheinen.

Klicken Sie nun mit Rechts auf das Modul und wählen aus dem Kontextmenü den Punkt "Icons". Dies legt eine neue Konfiguration an, in der Icons für Schemata vergeben werden können. Vergeben Sie als Titel den Namen "Icons". Zum Abspeichern ist mindestens ein Icon erforderlich.

Unter dem Punkt "Konfiguration" können Sie nun für verschiedene Schemata jeweils ein großes und ein kleines Icon angeben.

Als Icon-Größe empfiehlt sich 20x20 Pixel für das kleine und 48x48 Pixel für das große Icon.

Lassen Sie die Icon-Konfiguration ausgewählt, wechseln Sie in die Schemaverwaltung und ziehen Sie das Schema "http://example.com" über das Icon für die Dokumentenansicht (die Ansicht wechselt dann automatisch zurück zur ausgewählten Icon-Konfiguration) in das Feld "Schema".


Nun können Sie ein kleines Icon über "Durchsuchen" von Ihrem Computer auswählen. Das Feld "Groß" ist ausgegraut, da es sich um ein optionales Feld handelt. Um es zu aktivieren, klicken Sie das Label "Groß" an. Nun können Sie auch ein großes Icon über "Durchsuchen" hinzufügen. Speichern Sie dann das Dokument ab, um die Icons in der Vorschau sehen zu können. Über den Link "Icon hinzufügen" können Sie Felder für weitere Schemata zur Konfiguration hinzufügen.
Fügen Sie noch drei weitere Icon-Felder hinzu, für die Schemata "node", "site" und "page", die Sie nun nach und nach per Drag And Drop in die Schema-Felder ziehen. Vergeben Sie für jedes dieser Schemata ein anderes Icon.

Im Web lassen sich über einschlägige Suchmaschinen viele Webseiten finden, die kostenlose Icons oder Iconsets zu bestimmten Themen zum Download anbieten.
 

Haben Sie für alle Schemata Icons vergeben, speichern Sie das Dokument und geben Sie es zurück.

Die Konfigurationen erscheinen oben rechts im Objektstrukturfenster.

Texte im Kontextmenü und im Objektdetailfenster

Damit die Schemata beim Anlegen auch sprechende Namen erhalten, fügen wir nun Sprachkonfigurationen hinzu. Sind solche Sprachkonfigurationen für die Kontext-Menüs nicht vorhanden, wird standardmäßig die SchemaLocation im Kontext-Menü angezeigt.

Legen Sie dazu in Ihrem Modul Editor/Tutorial-Website über einen Rechtsklick ein neues Objekt "Ressourcen" an und vergeben Sie auch als Titel den Namen "Ressourcen". Darunter legen Sie ein weiteres Ressourcen-Objekt an und nennen es "de" für die Sprache, die wir nun konfigurieren möchten. In das Objekt "de" übernehmen Sie folgenden Standard-Inhalt und speichern das Dokument ab, damit es im Editor erscheint:

<resources>
<namespace name="Onion.Client.WebUI">
<type name="SchemaTypeNames"> </type>
<namespace name="Editor"> </namespace>
</namespace>
</resources>

Durch diese Struktur wird es möglich, Lokalisierungen, die nicht sprachabhängig sind, nur einmal definieren zu müssen. Dazu gehört zum Beispiel die Zuordnung eines Richtext-Feldes zu einer Editor-Gruppe oder auch die mehrzeilige Anzeige von einfachen xs:string-Elementen.

Im ersten Bereich "SchemaTypeNames" vergeben wir die Namen für die Schemata. Diese werden z.B. im Kontextmenü angezeigt, wenn ein neues Objekt angelegt werden soll. Sie erscheinen aber auch im Suchfenster, wenn sie nach einem bestimmten Datentyp suchen wollen. Daher kann es auch sinnvoll sein, abstrakte Datentypen mit einem sprechenden Namen zu versehen.

Fügen Sie die drei Elemente hinzu. Wie zuvor können Sie den Inhalt des Attributes "name" entweder per Drag And Drop aus der Schemaverwaltung einfügen oder direkt eintippen.

<item name="http://example.com/node">Tutorial-Node</item>
<item name="http://example.com/node/site">Tutorial-Site</item>
<item name="http://example.com/node/page">Tutorial-Page</item>

Nun vergeben wir noch Namen für die Elemente und Attribute, die innerhalb der Schemata definiert worden sind. Das wird im Abschnitt "nameSpace name='Editor'" vergeben. Erweitern Sie diesen Namespace um folgende Einträge:

<type name="http://example.com/node">
<item name="Element:node#Header">Seite</item>
<item name="Element:text#Label">Inhalt</item>
</type>
<type name="http://example.com/node/site">
<item name="Attribute:logo#Label">Logo</item>
</type>

Jedes Schema, von dem Elemente einen Namen (Label) bekommen sollen, wird in einem eigenen type-Element definiert. In den Item-Elementen können dann Elemente und Attribute angesprochen und deren Header bzw. Label geändert werden.

Insgesamt sieht die Editor-Konfiguration Ressourcen/de wie folgt aus:

<resources>
<namespace name="Onion.Client.WebUI">
<type name="SchemaTypeNames">
<item name="http://example.com/node/node">Tutorial-Node</item>
<item name="http://example.com/node/site">Tutorial-Site</item>
<item name="http://example.com/node/page">Tutorial-Page</item>
</type>
<namespace name="Editor">
<type name="http://example.com/node">
<item name="Element:node#Header">Seite</item>
<item name="Element:text#Label">Inhalt</item>
</type>
<type name="http://example.com/node/site">
<item name="Attribute:logo#Label">Logo</item>
</type>
</namespace>
</namespace>
</resources>

Geben Sie nun alle Dokumente in der Editor-Konfiguration zurück, die Sie bearbeitet haben. Drücken Sie nun die F5-Taste, um den Inhalt des Editors neu zu laden. Die geänderten Texte und Icons stehen dem Editor nun zur Verfügung, wie Sie an den geänderten Icons in der Baumansicht sofort sehen können.

Transformationen

Ihre Website ist nun schon fertig angelegt. Nur kann sie bisher noch nicht im Browser angezeigt werden. Diesen Umstand werden wir nun ändern, indem wir anfangen, Transformationen anzulegen, die für eine Ausgabe der Inhalte in den Browser sorgen werden.

Als erstes erstellen wir eine Transformationsgruppe. Dort sollen alle Transformationen zu der Tutorial-Website Platz finden.

Wenn Sie noch keine Transformationsgruppe erstellen können, müssen Sie zunächst das Modul "XSL Renderengine" importieren, welches auf der Seite Downloads-Seite herunterladen können.

Dazu öffnen Sie in der Baumstruktur der Detailansicht den Ordner "Transformations" und rechtsklicken auf das Objekt "Onion". Im Kontextmenü unter "Neu" wählen Sie nun "Transformationsgruppe". Vergeben Sie als Titel "example.com" und speichern Sie das Dokument ab.


Nun erstellen Sie darunter die erste Transformation.

Rechtsklicken Sie dazu auf die eben erstellte Transformationsgruppe und wählen Sie aus dem Kontextmenü "Neu" den Eintrag "Datentyp". Als Titel wählen Sie hier "node" aus, da die Transformation für unser Node-Schema gelten soll.

Allein durch den Titel wird noch keine Verbindung zu dem Schema hergestellt.

Klicken Sie im Datentyp node auf "name", um das optionale Feld anzulegen. In dieses Feld muss nun das Schema eingefügt werden. Dies machen Sie wie zuvor über Drag And Drop aus der Schemaverwaltung. Ziehen Sie also das Schema "node" in das Feld "name" und geben Sie dann sowohl den Datentyp als auch die Transformationsgruppe zurück.

Der Transformation ist nun ein XML-Schema eindeutig zugeordnet.

Erstellen Sie nun analog dazu unterhalb von "node" einen weiteren Datentypen, den Sie "site" nennen. Ziehen Sie in das name-Feld das entsprechende Schema hinein. Geben Sie dann auch diesen Datentypen zurück.

Literale Methode »default«

Wir erzeugen nun eine literale Methode für unseren Datentyp. Literale Methoden sind in der Lage, textbasierte Ausgaben, z.B. HTML im Web-Browser, zu erzeugen.

Rechtsklicken Sie dazu auf den Datentyp "node" und erstellen eine neue literale Methode aus dem Kontextmenü. Geben Sie dieser Methode den Namen "default".

Unter dem Titel können einige Einstellungen für diese Methode vorgenommen werden. Wir müssen in diesem Fall den Zugriffsschutz ändern. Der Zugriffsschutz steuert, von wo aus die Methode aufgerufen werden darf.

internal ausschließlich von anderen Transformationen
protected von anderen Transformationen und .NET-Code (bspw. Extensions)
public über öffentliche Schnittstellen (direkter Aufruf per URL)

Für die default-Methode wählen Sie "Öffentlich (public)" aus. 

Eine Methode mit dem Namen "default" gilt als Standardmethode für das Transformationssystem. Sobald eine öffentliche literale Methode mit diesem Namen für einen Datentyp vorhanden ist, erscheint im Kontextmenü automatisch der Punkt "Vorschau". Dieser öffnet das Rendering der Methode "default" für das entsprechende Objekt in einem neuen Browserfenster.

Ganz unten in der Detailansicht sehen Sie ein Eingabefeld. In dieses wird nun die eigentliche Transformation geschrieben. 

Schreiben Sie dort "<xsl:stylesheet" und drücken Sie STRG+Leertaste.

Über die Tastenkombination wird ein Assistent zur Vervollständigung der XSL Transformation geöffnet.

Möglicherweise wird der Assistent durch den Popup-Blocker des Browsers unterdrückt. In diesem Fall stellen Sie Ihren Browser so ein, dass Popups zugelassen werden und drücken Sie STRG+Leertaste erneut.

XSLT ist eine erweiterbare Transformationssprache. Hier finden sich die standardisierten EXSLT-Erweiterungen sowie nützliche onion.net-Erweiterungen, die z.B. Methoden zur Bildmanipulation oder PDF-Generierung enthalten. 

Klicken Sie in dem Assistenten auf "Namensräume aktualisieren", ohne einen Wert auszuwählen.

Es wird folgende Standard-Transformation angelegt:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="" omit-xml-declaration="yes" indent="no" />
<xsl:template match="/"> </xsl:template>
</xsl:stylesheet>

Tragen Sie im <xsl:output>-Element "xml" in das "method"-Attribut ein. Innerhalb des <xsl:template>-Elements geben Sie die gewünschte Ausgabe ein. Wir begnügen uns zunächst mit einem "Hallo Welt!". Speichern Sie nun die Transformation.

Komplett sieht die Transformation nun wie folgt aus:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
<xsl:template match="/">Hallo Welt!</xsl:template>
</xsl:stylesheet>

Um das Template direkt zu testen, rechtsklicken Sie auf die Site oder eine Page, die Sie weiter oben angelegt haben. Da sowohl die Site als auch die Page von Node abgeleitet sind, wird diese Methode "default" zur Ausgabe genommen, wenn es keine Methode default direkt für das Schema Site oder Page gibt.

Da der Editor nun eine "default"-Methode für unsere Website-Dokumente gefunden hat, bietet er uns die Funktion "Vorschau" im Kontextmenü an.

Möglicherweise müssen Sie die default-Methode erst ein Mal zurück geben und mit F5 den Editor aktualisieren, um den Vorschau-Knopf im Kontext-Menü angezeigt zu bekommen.

Wenn Sie diesen Menüpunkt anklicken, öffnet sich ein Browserfenster mit dem Text "Hallo Welt!", genau wie in der Transformation angegeben.

HTML-Gerüst für das Rendering erstellen

Doch wir begnügen uns natürlich nicht mit der Ausgabe "Hallo Welt!". Stattdessen soll im ersten Schritt bereits ein HTML-Grundgerüst ausgegeben werden, in das wir nach und nach die Funktionalitäten wie z.B. Navigation integrieren werden.

Da wir nun Daten aus den angezeigten Dokumenten ausgeben möchten, müssen wir die "Datensicht" der Methode default ändern.

Dazu löschen Sie den Wert "no-data" und geben stattdessen "meta" ein. Damit können wir auf die Attribute des Nodes zugreifen, was wir z.B. für die Ausgabe des Titels des Dokuments nutzen werden. Übernehmen Sie dann folgenden Inhalt in die default-Methode:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:web="http://onionworks.net/2004/renderengine/web" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" indent="no" />
<xsl:template match="/onion:object">
<web:responseHeaders expires="0" />
<html lang="de" xml:lang="de">
<head>
<title>
<xsl:value-of select="@onion:name" />
</title>
</head>
<body>
<div id="page">
<div id="contentNav">
<div id="header">
<p>Hier erscheint der Header</p>
</div>
<div id="path">
<ul>
<li>Hier fügen wir eine Pfadnavigation ein</li>
</ul>
</div>
<div id="navigation">
<p>Hier kommt die Navigation hin</p>
</div>
<div id="content">
<h1>
<xsl:value-of select="@onion:name" />
</h1>
<p>In diesem Bereich wird der Content der Seite ausgegeben</p>
</div>
<br class="clearBoth" />
</div>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Neben Änderungen am xsl:output, um den richtigen Doctype zu rendern und Einfügen des Elements <web:reponseHeaders /> für das Setzen von Caching-Informationen enthält dieses Template größtenteils statischen HTML-Code. Die einzige Ausnahme ist die Ausgabe des Seiten-Titels sowohl im Title-Element im Header als auch im div mit der ID "content".

Dadurch, dass wir als Datensicht "meta" gewählt haben, können wir auf das Attribut "onion:name" zugreifen. Darin steht der Wert, den wir im Feld "Titel" als Dokumentnamen angegeben haben.

Öffnen Sie nun die Vorschau eines Dokuments Ihrer Website.

Sie sehen nun schon das HTML-Grundgerüst. Neben dem Blindtext, der die Bereiche der Website kennzeichnet, werden schon erste Felder der Dokumente ausgegeben. Zum einen im Titel der Website, zum anderen in der Überschrift h1.

CSS hinzufügen

Im nächsten Schritt soll das Aussehen des HTML mittels CSS verbessert werden. Wir könnten nun in den <head>-Bereich in der default-Methode einen <style type="text/css">-Bereich einfügen, doch das würde die Methode unnötig lang und unübersichtlich werden lassen. Stattdessen erstellen wir eine neue literale Methode "head", die eine neue literale Methode "css" aufruft.

Wir legen die Methode "css" unter "site" an und nicht unter "node". Dies hat folgenden Hintergrund: Das CSS enthält keine dynamischen Inhalte und ist daher für jede Seite exakt gleich. Daher sollte es auch nur unter einer URL erreichbar sein und nicht unter einer eigenen URL für jede Seite der Website.

Durch die Vererbung wird beim Rendern eines Dokuments allerdings zunächst die Methode in dem Datentyp gesucht, der dem Dokument entspricht, z.B. eine Page. Liegt in diesem Datentyp keine passende Methode, wird durch die Vererbung bei dem übergeordneten Datentyp gesucht, in diesem Fall "node".

Daher gehen wir den Weg über die Zwischenmethode "head" die dann die "css"-Methode auf der Site aufruft bzw. die URL für die Einbindung des Stylesheets erzeugt.

Die »head«-Methode anlegen

Wir werden die Methode "css" in einer weiteren Methode "head" aufrufen und dabei wie folgt vorgehen:

Da wir von einer "page" aus die "site" finden möchten, legen wir unter "node" eine Methode "head" an, die nichts anderes macht, als eine Methode mit dem gleichen Namen auf dem Eltern-Element aufzurufen. Damit gehen wir die Struktur entlang nach oben, da die Methode node.head() sowohl für die Site als auch für die Page gilt.

Damit dieses "Nach-oben-laufen" automatisch an der "site" stoppt, erzeugen wir dort die eigentliche Methode "head", die wir aufrufen möchten, da in dieser Methode dann für die Site letztendlich die Methode "css" aufgerufen wird. Daher legen wir diese Methode "css" ebenfalls auf "site" ab.

Wir beginnen mit der eigentlichen Methode "head", die das CSS einbinden soll.

Legen Sie nun die literale Methode "head" unter dem Datentypen "site" an. Diese muss nicht öffentlich sein, da sie nur intern aufgerufen wird. Als Datensicht reicht "no-data", da auf keine Daten der Dokumente zugegriffen werden muss.

In dieser Methode wird ein HTML-<link>-Element generiert, das wir später in der default-Methode innerhalb des HTML-Head-Bereichs laden werden. Dies erledigen wir über folgendes XSLT in der "head"-Methode:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
<xsl:template match="/">
<link type="text/css" rel="stylesheet" href="{c.literalUri('css')}" media="screen, projection" />
</xsl:template>
</xsl:stylesheet>

Für die Page existiert noch keine Methode "head". Wie oben beschrieben werden wir dazu eine Methode unter "node" anlegen. Dies hat den Vorteil, dass sie auch direkt für zukünftige Seitentypen gültig ist, die von "node" abgeleitet werden.

Legen Sie nun unterhalb von "node" eine literale Methode "head" an. Diese benötigt die Datensicht "meta". Übernehmen Sie dann folgende Transformation und speichern die Methode ab.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
<xsl:template match="/onion:object">
<c.literalCall id="{@onion:parent}" method="head" />
</xsl:template>
</xsl:stylesheet>

Interessant an dieser Methode ist der c.literalCall. Hier wird wiederum die Methode "head" aufgerufen, allerdings mit der ID des Parent-Dokuments. Wird die Methode also auf einer Page aufgerufen, ruft sie sich selbst (bzw. eine Methode mit gleichem Namen) auf dem Parent wieder auf. Ist das Parent die "site", wird die Methode "head" genommen, die unter "site" zu finden ist. Diese sorgt dann für die Einbindung.

Die »css«-Methode anlegen

Als href des <link>-Elements wird hier ein Link auf die literale Methode "css" gebaut. Diese legen wir jetzt im Folgenden an.

Erstellen Sie unter dem Datentypen "site" die literale Methode "css". Diese Methode muss öffentlich aufrufbar sein und als MIME-Typ "text/css" haben. Geben Sie folgenden Inhalt ein:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:web="http://onionworks.net/2004/renderengine/web" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="text" />
<xsl:template match="/">
<web:responseHeaders expires="60" />
body { color: #374041; background-color: #f5f5f5; font-family: Verdana, Arial, sans; font-size: 12px; } /* #### reset styles #### */ * { margin: 0; padding: 0; } /* #### Layout #### */ #page { width: 980px; margin: 0 auto; background-color: #fff; } /* Header */ #header #logo { border: 0 none; } /* Content */ #content { margin: 2em 2em 0 18em; padding: 1em 2em 2em 2em; -moz-border-radius: 10px; -webkit-border-radius: 10px; border: 1px solid #707070; background: -moz-linear-gradient(0 0, #ebf3fc, #fff); background: -webkit-gradient(linear, 0 0, 0 100%, from(#ebf3fc), to(#fff)); /* IE 5.5 - IE 7 */ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ebf3fc, endColorstr=#FFFFFFFF); /* IE 8 */ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ebf3fc, endColorstr=#FFFFFFFF)"; } /* #### Styling #### */ h1, h2, h3, h4, h5, h6 { color: #b51d48; text-shadow: 2px 2px 2px #99a7a8; } h1 { font-size: 2em; padding-bottom: 0.2em; border-bottom: 1px solid #374041; } p, ol, ul, h1, h2, h3 { margin-bottom: 0.5em; } ol, ul { margin-left: 3em; } a { text-decoration: none; color: #b51d48; } a.active { font-weight: bold; } a:hover { text-decoration: underline; } #contentNav { border: 1px solid #707070; overflow: hidden; } /* Navigation */ #path { font-size: 0.9em; overflow: hidden; padding: 0.5em; border-top: 1px solid #707070; border-bottom: 1px solid #707070; } #path ul { list-style: none; margin-left: 0; } #path li { float: left; margin-right: 1em; } #navigation { float: left; margin-top: 1em; width: 150px; padding-left: 0.5em; } #navigation ul { list-style: none; margin-left: 0; } #navigation ul ul { margin-left: 1em; } #navigation li a { display: block; line-height: 1.2em; padding: 0.5em 0; } /* #### helper #### */ .clearBoth { clear: both; } </xsl:template>
</xsl:stylesheet>

Einbinden in die »default«-Methode

Nun laden wir die Methode "head" in unserer "default"-Methode unter "node".

Erweitern Sie dazu das Element <head> in der "default"-Methode um folgenden Aufruf.

<c.literalCall method="head" />

Insgesamt wird der Head-Bereich in der default-Methode dann so aussehen:

<head>
<title>
<xsl:value-of select="@onion:name" />
</title>
<c.literalCall method="head" />
</head>

Wenn Sie nun eine Vorschau auf eines der Website-Dokumente machen, werden nun auch die Stylesheets genutzt, wodurch die Website nun mit einem anderen Erscheinungsbild dargestellt wird.


Logo einbinden und anzeigen

In dem Dokument "site" haben Sie die Möglichkeit, ein Logo einzubinden. Dies soll in diesem Schritt nun auch in die Ausgabe eingebunden werden.

Laden Sie zunächst ein Logo in Ihre Site "Homepage" und speichern das Dokument ab. Leihen Sie dazu die "Homepage" aus (falls noch nicht geschehen) und klicken Sie auf das Feld "Logo", um es einzublenden. Klicken Sie danach auf "Durchsuchen", um eine Grafik vom lokalen Rechner auszuwählen. Benutzen Sie dazu ein Grafikformat, das im Web angezeigt werden kann, also JPG (RGB-Farbraum), PNG oder GIF. Damit das Layout nicht auseinanderbricht, wählen Sie die Bildgröße nicht zu hoch. Passend wäre z.B. eine Größe von 350x200 Pixeln.

Um ein binäres Dokument anzuzeigen, benötigen wir auch eine binäre Methode (im Gegensatz zur literalen Methode, die Text ausgeben kann).

Binäre »default«-Methode

Legen Sie unter "node" eine neue binäre Methode namens "binary.default" an. Die Methode muss öffentlich sein und die Datensicht "content" haben. Nutzen Sie dazu folgende Standard-Methode. Diese Methode liefert einfach jegliche Art von binärer Datei aus und übergibt sie an den Browser.

Der Präfix "binary." im Dateinamen ist notwendig, da es ja auf dem Datentyp Node bereits eine Methode mit dem Namen "default" gibt. Binären Methoden kann daher der Präfix "binary." vorangestellt werden, der nur systemisch ausgewertet wird. Im Objektstrukturfenster sehen Sie, dass der Präfix keinerlei Einfluss auf den eigentlichen Methodennamen hat.
Gleiches gilt für XML-Methoden mit dem Präfix "xml.".

<xsl:stylesheet xmlns:onion="http://onionworks.net/2004/data" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b="http://onionworks.net/2004/renderengine/binary" xmlns:reg="http://exslt.org/regular-expressions" version="1.0">
<xsl:param name="select" />
<xsl:template match="/">
<xsl:variable name="ref" select="c.resolveNode($select)" />
<xsl:value-of select="b.write($ref)" />
<xsl:variable name="mimeType">
<xsl:choose>
<xsl:when test="count(reg:match($select, 'A\d', 'gi')) > 0">
<xsl:variable name="element" select="c.resolveNode(substring-before($select, 'A'))" />
<xsl:value-of select="$element/@*[local-name() = concat(local-name($ref), '.mimeType')]" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$ref/@onion:mimeType" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<b:output mimeType="{$mimeType}">
<b:webResponse expires="60" />
</b:output>
</xsl:template>
</xsl:stylesheet>

Die binäre Methode für das Logo aufrufen

Nun haben wir eine Methode, die Binärdateien ausgeben kann. Im nächsten Schritt erzeugen wir eine Methode, die dieses Logo einbindet, damit es auf unserer Website angezeigt wird. Genau wie bei der head-Methode gehen wir hier so vor, dass unter Node eine Methode »logo« liegt, die die Anfrage an den Parent weitergibt. Auf dem Datentyp Site wiederum liegt dann die »logo«-Methode, die für die Ausgabe des Logos sorgt.

Auf diese Weise vererbt sich das Logo von der Startseite auf alle Unterseiten. Dasselbe gilt ja auch für das CSS.

Legen Sie dazu unter "site" eine literale Methode "logo" mit der Datensicht "content" mit folgendem Inhalt an.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
<xsl:template match="/node">
<xsl:if test="count(@logo)">
<a href="{c.literalUri()}">
<img src="{c.binaryUri(c.id(), 'default', 'select', c.generateId(@logo))}" alt="" id="logo" />
</a>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

In dieser Methode wird zunächst geprüft, ob das Attribut "logo" (@logo) überhaupt gefüllt ist. Da das Attribut kein Pflichtfeld ist, kann es auch leer sein. Ohne eine Überprüfung hätten wir dann ein "broken image".

Ist ein Logo vorhanden, wird es als Bild eingebunden. Außerdem wird das Logo mit der Startseite verlinkt. Der Aufruf von »c.literalUri()« ohne Angabe weiterer Parameter bewirkt, dass eine URL auf das aktuelle Dokument unter Benutzung der Methode »default()« gebaut wird.

Da das aktuelle Dokument die Site ist, ist das Logo somit immer mit der Startseite verlinkt.

Interessant bei der Einbindung des Logos als Bild ist das Befüllen des src-Attributes. Hier wird eine binäre Methode aufgerufen, die den Namen "default" hat. Dies ist genau unsere vorhin angelegte binary.default zum Anzeigen von binären Daten. Wie Sie sehen, müssen wir den Präfix "binary." auch hier nicht angeben. Der Parameter "select" wird mit dem Element des Dokuments befüllt, das die Binärdaten enthält.

Für eine genauere Beschreibung der Methode »c.generateId()« siehe die Extension-Referenz.

Damit sich die Methode beim Aufruf einer Page bis zur Site hocharbeiten kann, benötigen wir noch eine logo-Methode unterhalb von Node (analog zur "head"-Methode).

Legen Sie unter "node" die literale Methode "logo" an mit der Datensicht "meta". Der Inhalt ist fast identisch mit dem der "head"-Methode:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
<xsl:template match="/onion:object">
<c.literalCall id="{@onion:parent}" method="logo" />
</xsl:template>
</xsl:stylesheet>

Einbinden in die »default«-Methode

Nun muss der Aufruf der "logo"-Methode noch in unser HTML-Gerüst in der »default«-Methode eingebunden werden.

Ersetzen Sie dazu den Inhalt des divs mit der ID "header" durch den Aufruf der literalen Methode "logo":

<c.literalCall method="logo" />

Wenn Sie nun eine Vorschau auf eine Page oder die Site machen, wird anstelle des Textes das Logo in den Head-Bereich gerendert. Es ist darüber hinaus mit der Site verlinkt, sodass von jeder Seite auf die Startseite gewechselt werden kann.

Navigation

Anhand der Struktur der angelegten Dokumente sollen nun automatisch zwei Navigationen erzeugt werden.

Zunächst benötigen wir natürlich eine "Standard"-Navigation, die alle Menüpunkte der ersten Ebene unterhalb der Site darstellt, alle Kinder des aktuell ausgewählten Menüpunkts sowie alle Geschwister der dazwischen liegenden Ebenen. Sie dient dazu, sich innerhalb der Seitenstrutur zu bewegen, also über die Website zu "surfen".

Zum anderen wollen wir eine Pfad-Navigation erzeugen, die alle Dokumente von der Startseite bis zur aktuelle Seite anzeigt. Diese Navigation nennt man auch "Ariadne-Faden" oder "Brotkrumenpfad". Sie dient dem Nutzer als Orientierung, da sie ihm jederzeit den Pfad zur aktuellen Seite anzeigt.

Da das Erstellen der Pfad-Navigation etwas simpler ist, beginnen wir damit.

Pfad-Navigation

Die Pfad-Navigation stellt wie gesagt den Klickpfad zur aktuellen Seite dar. Das bedeutet, das wir für den Aufbau der Pfad-Navigation im Prinzip nur von der aktuellen Seite aus immer eine Ebene nach oben gehen müssen bis zur Site. Die Abbildung zeigt beispielhaft die Pfad-Navigation für die Seite "Produktgruppe 2".


Ähnlich wie bei der Methode "head" werden wir eine Rekursion aufbauen. Dafür erstellen wir eine Methode "path" die immer wieder auf dem jeweiligen Parent aufgerufen wird. Der erste Aufruf (auf der untersten Page) rendert nur den Titel der Page, der nicht verlinkt ist. Auf den Parent-Seiten wird der Titel der Page zusätzlich noch verlinkt. Diese Steuerung, ob ein Link erzeugt werden soll oder nicht, geschieht über einen Parameter, der beim Aufruf der Methode übergeben wird. Dieser ist standardmäßig auf "false" gesetzt (also auch beim ersten Aufruf), wird dann bei den Parent-Aufrufen explizit mit "true" übergeben.

Damit die Reihenfolge stimmt und nicht die unterste Seite als erste gerendert wird, wird im Rücklauf der Rekursion erst der Link tatsächlich ausgegeben.

Die »path«-Methode anlegen

Legen Sie unter Node eine literale Methode "path" an. Diese benötigt die Datensicht "meta", um auf den Parent zugreifen zu können.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:param name="link" c.type="Boolean" c.impliedValue="false" />
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
<xsl:template match="/onion:object">
<xsl:call-template name="renderParent" />
<li>
<xsl:choose>
<xsl:when test="$link">
<a href="{c.literalUri()}">
<xsl:value-of select="@onion:name" />
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@onion:name" />
</xsl:otherwise>
</xsl:choose>
</li>
</xsl:template>
<xsl:template name="renderParent">
<c.literalCall id="{@onion:parent}" method="path" link="true" />
</xsl:template>
</xsl:stylesheet>

Dieses XSLT ist in zwei Templates unterteilt. Zum einen das Standard-Template, das über <xsl:template match="..."> aufgerufen wird. Als erstes wird hier nun das Template mit dem Namen "renderParent" aufgerufen. Dieses finden Sie am Ende der Methode.

In diesem Template wird, vergleichbar mit der "head"-Methode, die gleichnamige Methode "path" wieder mit der ID des Parents aufgerufen. Hier wird also die Rekursion angestoßen.

Dies geschieht so lange, bis unter "site" wieder eine eigene Methode "path" gefunden und somit die Rekursion gestoppt wird, da keine weiteren Aufrufe stattfinden.

Nach dem Template-Aufruf wird dann der Titel des aktuellen Objekts ausgegeben. Der Link wird in Abhängigkeit des Steuerparameters "link" erzeugt oder eben nicht. Dadurch, dass diese Anweisung erst nach dem Rendering für den Parent steht, wird die korrekte Reihenfolge von oben nach unten erzeugt.

Da wir ein semantisch korrektes HTML erstellen wollen, muss unsere Pfad-Navigation eine Liste sein. Daher ist jedes Pfad-Element von einem <li> umgeben. Das nötige <ul> wird beim initialen Aufruf hinzugefügt, welcher in der "default"-Methode stattfindet.

Legen Sie nun unter "site" auch eine literale Methode "path" an. Diese benötigt auch die Datensicht "meta". Übernehmen Sie folgenden Inhalt:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:import href="../path" />
<xsl:template name="renderParent" />
</xsl:stylesheet>

Diese Methode importiert den Inhalt der Methode "path" aus Node (durch das vorangestellte "../" wird in den hierarchisch darüberliegenden Datentypen gesucht), überschreibt jedoch das darin enthaltene Template "renderParent" durch ein leeres Template. Das heißt die Ausgabe des aktuellen Dokumenttitels findet statt, allerdings nicht mehr der Aufruf der Methode "path" des Parents. 

Der Import der "allgemeinen" Methode auf dem Node hat den Vorteil, dass das Markup für ein Element der Pfad-Navigation nur an einer einzigen Stelle steht. Wenn sich etwas ändert, beispielsweise eine Klasse an das <li> gefügt oder der Text mit einem <span>-Element umschlossen werden soll, muss dies nur an einer einzigen Stelle geändert werden.

Ändern Sie die "default"-Methode wie folgt. Ersetzen Sie den Inhalt des div mit der ID "path" durch den Aufruf der literalen Methode path mit umschließendem <ul> und einem beschreibenden Text:

<ul>
<li>
<xsl:value-of select="'Sie befinden sich hier: '" />
</li>
<c.literalCall method="path" />
</ul>

Wenn Sie nun eine Vorschau aufrufen, können Sie die klickbare Pfadnavigation sehen.

Hauptnavigation

Im nächsten Schritt erstellen wir die Navigation. Der Aufbau der Navigation besteht grundsätzlich aus zwei Teilen. Ein Teil ist die Ausgabe aller Seiten und Unterseiten ab der Startseite. Dies ist relativ einfach. Wir müssten nur eine Methode auf der Startseite aufrufen und dann rekursiv alle Kinder durchgehen. Allerdings ist das nicht genau der Effekt, den wir haben wollen. Die Unternavigation einer Seite soll nur dann angezeigt werden, wenn wir uns auf der Seite befinden. Die Navigation soll so aussehen, wie in der Abbildung zu sehen. Man erkennt auf einen Blick: Wir befinden uns auf der Seite „Produkte“. Daher sind auch die beiden Unterseiten zu sehen. Obwohl auch die Seite „Leistungen“ Unterseiten besitzt, werden diese aber nicht angezeigt. 

Für diese Funktionalität müssen wir wissen, in welchem Pfad wir uns befinden. Der zweite Teil, der für das Navigationrendering benötigt wird, ist also an die Pfad-Navigation aus dem letzten Kapitel angelehnt.

Für diesen Teil verwenden wir eine Methode "navigation", die alle Dokumente vom aktuellen Dokument bis zur Site hinauf sammelt.

Für das eigentliche Rendering der Navigation wird dann die Methode "navigation.render" verwendet. Zunächst auf der Site aufgerufen, geht sie alle Kinder durch und hebt gegebenenfalls die aktuelle Seite hervor bzw. stößt das Rendering der Unternavigation an.

Eine Methode »link« anlegen

Da wir an mehreren Stellen aus der Rekursion heraus einen Link für den jeweiligen Navigationspunkt erstellen müssen, legen wir zuerst eine Methode "link" an, die diese Aufgabe übernimmt. Dies hat den Hintergrund, dass für die Erstellung des Links ja die Informationen aus der Datensicht "meta" benötigt werden (wir brauchen ja den Titel). Für den Aufbau der Struktur müssen aber die Kinder durchgegangen werden, wofür die Datensicht "children" verwendet wird. 

Legen Sie unter Node eine literale Methode "link" an. Diese benötigt die Datensicht "meta".

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
<xsl:param name="highlight" c.optional="true" c.type="Boolean" />
<xsl:template match="/onion:object">
<a href="{c.literalUri()}">
<xsl:if test="$highlight">
<xsl:attribute name="class">active</xsl:attribute>
</xsl:if>
<xsl:value-of select="@onion:name" />
</a>
</xsl:template>
</xsl:stylesheet>
Inhalt der literalen Methode »link()« auf dem "node"

Die »navigation«-Methode anlegen

Legen Sie unter Node die literale Methode "navigation" an. Diese benötigt die Datensicht "meta". Übernehmen Sie folgende Transformation:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:param name="pathItems" c.impliedValue="" />
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
<xsl:template match="/onion:object">
<c.literalCall id="{@onion:parent}" method="navigation" pathItems="{concat(c.id(), ' ', $pathItems)}" />
</xsl:template>
</xsl:stylesheet>
Inhalt der literalen Methode »navigation()« auf "node"

Diese Methode macht nichts anderes, als die "path"-Methode für unsere Pfad-Navigation. Sie sammelt die Seiten des Klickpfades vom aktuellen Dokument bis zur Site. Allerdings wollen wir diese ja im Gegensatz zur Pfad-Navigation nicht direkt ausgeben, sondern hinterher weiterverarbeiten. Daher werden die Pfad-Elemente nicht direkt als Listenelemente ausgegeben, sondern im Parameter "pathItems" gesammelt. Jede Seite des Klickpfades erweitert diese Liste um die eigene ID am Anfang. Wenn die Methode dann auf der Site aufgerufen wird, enthält der Parameter von links nach rechts gelesen die Ids der Seiten, auf die der Nutzer geklickt hat. Diese werden einfach mit einem Leerzeichen separiert. Auf der Site enthält "pathItems" dann etwa folgenden beispielhaften Wert:

onion://data/objects/123 onion://data/objects/456 onion://data/objects/789  

Erstellen Sie nun unter Site eine literale Methode "navigation". Die wir keine Inhalte oder Dokumentinformationen benötigen, kommen wir mit der Datensicht "no-data" aus.

Diese soll die Aufrufe der Parent-Methoden beenden und die Methode "navigation.render" mit der Liste der Page-IDs als Parameter aufrufen:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:param name="pathItems" c.impliedValue="" />
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
<xsl:template match="/">
<c.literalCall method="navigation.render" pathItems="{$pathItems}" />
</xsl:template>
</xsl:stylesheet>
Inhalt der literalen Methode »navigation()« auf "site"

Die Hauptnavigation rendern

Nun kümmern wir uns um das Rendern der Navigation. Nachdem wir über die Methode "navigation" den Klickpfad von unten nach oben durchgegangen sind, wird nun über die Methode "navigation.render" auf der Startseite begonnen, die Navigation aufzubauen.

Dazu legen Sie unter Node die literale Methode "navigation.render" an. Als Datensicht verwenden Sie hier "children", da für die Navigation die Kindelemente des aktuellen Menüpunkts angezeigt werden sollen.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:param name="pathItems" />
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
<xsl:template match="/onion:children">
<xsl:variable name="currentPathItem" select="substring-before($pathItems, ' ')" />
<xsl:variable name="followingPathItems" select="substring-after($pathItems, ' ')" />
<xsl:if test="onion:object">
<ul>
<xsl:for-each select="onion:object">
<li>
<xsl:choose>
<xsl:when test="@onion:href = $currentPathItem">
<c.literalCall id="{@onion:href}" method="link" highlight="true" />
<c.literalCall id="{@onion:href}" method="navigation.render" pathItems="{$followingPathItems}" />
</xsl:when>
<xsl:otherwise>
<c.literalCall id="{@onion:href}" method="link" />
</xsl:otherwise>
</xsl:choose>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Inhalt der literalen Methode »navigation.render()« auf "node"

Der Parameter "pathItems" enthält nun zu Beginn wie gesagt alle Dokumente des Klickpfades. Die erste ID darin repräsentiert genau die aktive Seite der ersten Navigationsebene. Angelehnt an der obigen Abbildung wäre das die ID der Seite "Produkte".

Diese ID speichern wir nun in die Variable "currentPathItem". Mit der Funktion "substring-before()" wählen wir alles vor dem ersten Leerzeichen aus. Also genau die erste ID. In die Variable "followingPathItems" wird der Rest gespeichert.

Dann gehen wir alle Kinder des aktuellen Knotens durch. Im Falle der Startseite also "Leistungen", "Produkte", "Kontakt" und "Impressum". Für jedes dieser Dokumente prüfen wir, ob es sich hierbei um das "currentPathItem" handelt, also die aktive Seite in der ersten Ebene. Ist dies nicht der Fall, rufen wir einfach die Hilfsmethode "link" auf, um den Navigationspunkt zu erstellen. Ansonsten erstellen wir ebenfalls den Navigationspunkt der jeweiligen Seite, rufen danach aber rekursiv erneut die Methode "navigation.render" mit den übrig gebliebenen Pfad-Bestandteilen (also dem Wert der Variablen "followingPathItems") auf.

Auf diese Weise wird dann, wenn vorhanden, die nächste Navigationsebene für den aktiven Pfad erstellt.

Die if-Abfrage, die den ul-Block einrahmt, dient dazu, ein leeres ul abzufangen. Dieses soll nur dann erstellt werden, wenn es tatsächlich Navigationspunkte gibt.

Nun müssen wir nur noch das Rendering der Navigation initial in der „default“-Methode anstoßen.

Fügen Sie dazu den Aufruf der Methode „navigation“ in die default-Methode ein, damit die Navigation auch gerendert wird. Ersetzen Sie dazu den Inhalt des divs mit der ID navigation durch folgenden Aufruf:

<c.literalCall method="navigation" />

Inhalte ausgeben

Als nächstes kümmern wir uns um die Ausgabe des Contents, der in unserem Beispiel aus einem Richtext-Feld besteht. Eine einfache Möglichkeit zur Ausgabe wäre, den Inhalt des Richtext-Feldes per xslt-Element <copy-of /> zu übernehmen. Problematisch daran ist allerdings, dass dann keine eingebundenen Bilder oder Links funktionieren würden.

Befüllen Sie daher Ihre Dokumente mit ein paar Inhalten, die über normalen Text hinausgehen: Fügen Sie über die Editor-Schaltfläche "Neues Bild" Bilder hinzu (in einem Web-fähigen Format wie JPG, PNG oder GIF) und verlinken Sie Dokumente untereinander.

Dokumente verlinken geht am einfachsten, wenn Sie im Editor einen Text markieren und dann per Drag And Drop die Zielseite aus der Baumansicht in das Editor-Fenster ziehen. Dann bekommt die Markierung automatisch den Link. (Wichtig ist dabei, dass Sie die Seite nicht direkt auf dem markierten Text fallen lassen, sondern irgendwo anders im Editor-Fenster.)

Die »content«-Methode anlegen

Legen Sie nun eine literale Methode "content" unter Node an. Diese benötigt die Datensicht "content". Fügen Sie bei der Erstellung den Namensraum für das Präfix "xhtml" (siehe Codebeispiel) hinzu.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:onion="http://onionworks.net/2004/data" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
<xsl:template match="/node">
<xsl:apply-templates select="text/node()" />
</xsl:template>
</xsl:stylesheet>
Inhalt der literalen Methode »content()« auf "node"

Als Inhalt übernehmen wir zunächst obige Transformation. Ausgeben würde diese Methode bislang noch nichts. Das Template sorgt so dafür, dass innerhalb unseres Richtextfeldes mit dem Namen "text" für alle nodes, also in diesem Fall HTML-Elemente, behandelt werden.

Nun müssen noch die HTML-Elemente ausgegeben werden.

Erweitern Sie dazu die Transformation um weitere <xsl:template>-Elemente vor dem schließenden </xsl:stylesheet>.

Zunächst behandeln wir interne Links auf Bilder und andere Dokumente:

<xsl:template match="xhtml:img">
<img alt="{@alt}" title="{@title}" longdesc="{@longdesc}" class="{@class}" src="{c.binaryUri('default', 'select', c.generateId(@src))}" />
</xsl:template>
<xsl:template match="xhtml:a[starts-with(@href, 'onion:')]">
<a href="{c.literalUri(@href)}">
<xsl:apply-templates select="@*[local-name() != 'href' and local-name() != 'objectReference'] | node()" />
</a>
</xsl:template>
Behandlung von Bildern und internen Links

Das erste Template gilt für alle img-Elemente, die in dem text-Element gefunden werden. Es werden alle Attribute (gekennzeichnet durch das @), die im Richtext-Editor gepflegt wurden, für das img-Tag der Ausgabe übernommen. Auf diese Weise können Redakteure Meta-Informationen wie zum Beispiel den Alternativtext am Bild pflegen. Wenn ein Attribut nicht übernommen werden soll (zum Beispiel eine Klasse), dann lässt man dies einfach weg. Um einen funktionierenden Verweis auf ein binäres Element zu erzeugen, generieren wir im src-Attribut einen Verweis auf die binäre Methode "default", die wir auch schon für die Logo-Ausgabe weiter oben eingesetzt haben.

Alle XHTML-Elemente, die der Richtext-Editor generiert, sind dem Namensraum "xhtml" ("http://www.w3.org/1999/xhtml") zugeordnet. Daher muss bei den Templates, die diese Elemente behandeln sollen, der Namensraum mit gematcht werden. Dies geschieht durch voranstellen des Namensraum-Präfixes, z.B. "xhtml:img".

Das Template darunter greift bei allen Links (a-Elementen), deren Linkziel (href-Attribut) mit "onion:" beginnt. So können wir in diesem Template sicher sein, nur auf interne Links zu reagieren.

Für das Link-Element generieren wir eine im Browser aufrufbare URL mittels "c.literalUri()". Danach werden alle Attribute des Link-Elements des Editors übernommen, mit Ausnahme des "href"- sowie des "objectReference"-Attributs. Außerdem werden alle weiteren Nodes innerhalb des Link-Elements hinzugefügt. Dies können z.B. weitere Texte oder Bilder sein.

Mehr zu den Kernfunktionen, die onion.net mitliefert, finden Sie in der Extension Referenz.

Alle anderen Elemente können nun zunächst ohne spezielle Behandlung herausgeschrieben werden.

<xsl:template match="node()">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="@*">
<xsl:copy />
</xsl:template>
<xsl:template match="text()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
Allgemeine Behandlung von Elementen, Attributen und Text

Das erste dieser drei Templates erzeugt für jedes Richtext-Element node() ein xhtml-Element des selben Namens ("local-name()") und übernimmt dann alle Attribute sowie alle darunter liegenden nodes.

Das zweite Template kümmert sich darum, dass Attribute übernommen werden. 

Handelt es sich um ein Text-Node, greift das dritte der obigen Templates. Es kopiert den Inhalt des text-Elements und fügt auch hier wieder alle Attribute sowie darunter liegenden nodes ein.

Haben Sie diese Templates alle in die "content"-Methode eingefügt, speichern Sie diese ab und geben Sie sie zurück.

Fügen Sie nun noch den Aufruf der "content"-Methode in die "default"-Methode ein. Löschen Sie dazu folgende Zeile aus der "default"-Methode:

<p>In diesem Bereich wird der Content der Seite ausgegeben</p>
Zu löschende Zeile in der Methode »default()«

Fügen Sie an der Stelle, an der Sie gerade die obige Zeile gelöscht haben folgendes ein:

<c.literalCall method="content" />

Geben Sie nun die Methode zurück und öffnen erneut eine Vorschau Ihrer Website-Dokumente. Nun wird neben den anderen Elementen zusätzlich auch der im Richtext gepflegte Inhalt ausgegeben.

Schlusswort

Sie haben nun mit Hilfe dieser Anleitung eine kleine simple Website erstellt, die pflegbare Inhalte ausgibt und eine funktionierende Navigation sowie eine Pfad-Navigation für die Orientierung innerhalb der Website enthält. Darüber hinaus haben Sie die grundlegende Vorgehensweise für die Arbeit in onion.net und einige Zusammenhänge kennengelernt.

Mit diesem Wissen können Sie nun die kleine simple Website sukzessive erweitern und das Rendering verbessern. Dies ist an vielen Stellen möglich. So ist zum Beispiel das Rendern der Navigation relativ aufwändig.

Darüber hinaus bieten die literalen Methoden, die Sie im Laufe dieses Tutorials angelegt haben, die Möglichkeit, ein Caching einzustellen, was zur Erhöhung der Performance beim Rendering beiträgt.

Außerdem können Sie natürlich auch noch am Design eine Menge basteln. Möglicherweise müssen dazu auch noch einige Erweiterungen im Rendering vorgenommen werden. Zum Beispiel ist es ja für Tabellen ratsam, die Zeilen alternierend hervorzuheben, um die Lesbarkeit zu erhöhen.

Mit diesem Tutorial haben Sie den Grundstein gelegt, sich weiter mit den verschiedenen Aufgaben und Möglichkeiten einer Website zu beschäftigen und das Ergebnis, was Sie mit diesem Tutorial erarbeitet haben, nach Ihren Wünschen zu erweitern.

Das onion.net Team wünscht Ihnen viel Spaß dabei!

P.S.: Die weiterführenden Tutorials können nun für Sie interessant sein.