Tutorial: Wie baue ich ein eigenes Framework für Oxygen XML?

Seit 2012 wird von der TELOTA-Initiative an der Berlin-Brandenburgischen Akademie der Wissenschaften »ediarum« entwickelt und eingesetzt. Dabei handelt es sich um ein Paket aus drei Softwarelösungen (Oxygen XML, eXistdb und ConTeXt), das es Wissenschaftlern in verschiedenen Editionsvorhaben ermöglicht, ihre Ergebnisse in TEI-XML zu bearbeiten, zu speichern und zu präsentieren. Damit die Eingabe und Bearbeitung möglichst komfortabel und einfach geschieht, wird als Eingabeoberfläche in ediarum die proprietäre Software Oxygen XML Author eingesetzt. Diese Software hat den Vorteil, dass die Bearbeiter nicht mehr direkt im XML-Code arbeiten, sondern in einer benutzerfreundlichen Oberfläche, die zum einen die XML-Datei optisch aufbereitet präsentiert und zum anderen Werkzeugleisten bereitstellt, mit denen per Knopfdruck XML-Elemente eingefügt bzw. Textteile ausgzeichnet werden können.

Da jedes XML-Schema andere Elemente verlangt, gibt es in Oxygen XML Author natürlich nicht die eine Werkzeugeleiste. Stattdessen werden sog. Frameworks (auch »Dokumenttypzuordnungen« genannt) benutzt. In ihnen werden die Ansichten und Werkzeugleisten definiert, die auf eine ganz bestimmte Sorte von XML-Dokumenten angewendet werden können. Oxygen XML liefert einige Frameworks für weit verbreitete XML-Schemata schon mit, so z.B. für Docbook und DITA – oder eben für die TEI. Viel spannender ist aber die Möglichkeit, selbst eigene Frameworks zu erstellen, die genau für das eigene XML-Schema passen und maßgeschneiderte Werkzeugleisten und Ansichten mitbringen. Dieses Tutorial gibt Ihnen einen Überblick darüber, wie ein solches Oxygen-Framework erstellt wird.

Empfohlene Voraussetzungen für dieses Tutorial
  • Vorkenntnisse: XML, XPATH, CSS
  • Software: Oxygen XML

Schritt 1 – Ein Oxygen-Framework anlegen

oxy_framework_tutorial_005Die Frameworks in Oxygen finden wir im Menü Optionen > Einstellungen unter dem Punkt »Dokumenttypen-Zuordnung«. Anfangs sehen wir dort nur alle mit Oxygen XML Author zusammen gelieferten Frameworks. Wir möchten aber ein eigenes neues anlegen – das können wir unten über die Schaltfläche »Neu« tun.

Im nun erscheinenden Fenster legen wir neben Namen und Beschreibung, vor allem den Speicherort fest. Damit wir das Framework nämlich am Ende weiterverteilen können, speichern wir es »extern«.  Dazu erstellen wir im Programmordner im Unterordner frameworks einen neuen Ordner, dem wir den Namen unseres Frameworks geben, z.B. »briefedition-xyz«. In diesem Ordner können wir dann die eigentliche »Framework option file« mit der Dateiendung ».framework« speichern, die unsere Frameworkkonfiguration enthält. Es handelt sich dabei eigentlich um eine einfache XML-Datei, die wir aber auch bequem über das Dialogfenster in Oxgen XML Author bearbeiten können. Dadurch, dass wir sie extern gespeichert haben, können wir sie am Schluss anderen zur Verfügung stellen.

Danach stellen wir noch den internen Bearbeitungsmodus auf »Autor«. Dadurch werden alle XML-Dateien automatisch erstmal in der Autoransicht geöffnet und nicht in der Codeansicht.

Wichtig: Vergessen Sie nicht, im Dialogfenster »Dokumenttypen« auf »OK« zu klicken, damit die Veränderungen gespeichert werden! Das sollten Sie im übrigen auch mehrmals während der Arbeit am Framework tun.

Über die Tabs können wir nun unser Framework konfigurieren. In diesem Tutorial folgen wir weitgehend der Anordnung der Tabs von links nach rechts.

Schritt 2 – Verknüpfungsregeln

oxy_framework_tutorial_006Mit Hilfe der Verknüpfungsregeln werden Frameworks einem Dokumenttyp zugeordnet. Das kann z.B. anhand des Dateinamens oder des Wurzelelements geschehen. Da wir derzeit stets mit TEI-Dateien arbeiten und Konflikte vermeiden wollen, benutzen wir bei TELOTA mittlerweile ein eigens für diesen Zweck geschaffenes Attribut telota:doctype im Wurzelelement TEI der XML-Dateien. Das schließt aus, dass das Framework aus Versehen für andere Dateien benutzt wird.

Schritt 3 – Schema einbinden

oxy_framework_tutorial_008Es ist wichtig – nicht nur für Oxygen XML –, dass ein Schema für die XML-Dateien vorliegt. Nur dadurch kann Oxygen zum einen die Dateien korrekt mit CSS darstellen (dazu später mehr) und zum anderen die Eingabe des Benutzers validieren. Der einfachste Weg ist es, die URL des Schema in der XML-Datei zu notieren. Dann steht das Schema allerdings nicht zur Verfügung, wenn offline gearbeitet wird. Um das zu verhindern kann man in Oxygen im Tab »Validierung« eine Validierung anlegen, die man dann als Standard definiert, der automatisch zur Prüfung verwendet wird. Diese Standardvalidierung kann auf ein lokal gespeichertes Schema verweisen (etwa im Frameworksordner) und ignoriert auch ein evtl. im Dokument notiertes Schema.

Schritt 4 – Dateivorlagen

Damit die XML-Dateien nicht jedesmal von Grund auf neu geschrieben werden müssen, können im Framework Dateivorlagen hinterlegt werden. Dazu legen wir im Ordner frameworks/briefedition-xyz einen Unterordner »templates« an und tragen diesen Pfad im Tab »Vorlagen« ein. Wenn wir dort nun XML-Dateien ablegen, werden diese unter ihrem Dateinamen dem Benutzer beim Erstellen einer neuen Datei zur Auswahl angeboten. In den Dateivorlagen können übrigens Oxygen-Editorvariablen benutzt werden.

Tipp: In den meisten Pfadangaben, die Sie im Framework machen müssen, können Sie verschiedene Variablen benutzen. So lautet z.B. der Pfad zu den Dateivorlagen ${frameworkDir}/briefedition-xyz/templates – die Variable „frameworkDir“ steht hier für den kompletten Pfad zum Frameworksverzeichnis der Oxygeninstallation. Welche Variablen Sie nutzen können erfahren Sie meistens durch einen Klick auf das Symbol rechts neben dem Pfadeingabefeld.

Schritt 5 – CSS

Kern eines Oxygen-Frameworks ist der Tab »Autor«. Dort können wir in der Rubrik CSS ein Stylesheet verknüpfen, mit dem die XML-Dateien angezeigt werden sollen. Wir können auch mehrere Stylesheets hinterlegen, die dem Benutzer zur Auswahl angeboten werden. Dadurch ist es möglich, für bestimmte Arbeitsschritte bestimmte Elemente oder Attribute hervorzuheben bzw. Auszublenden.

Oxygen XML Author unterstützt einen großen Teil der CSS-Spezifikationen. Nicht unterstützt werden vorwiegend positionierungsrelevante CSS-Anweisungen. Einen detaillierten Überblick über die Unterstützung findet man in der Dokumentation im Abschnitt »CSS Support in Author«.

Aber Oxygen unterstützt nicht nur die gängigen CSS-Formatierungen, sondern auch viele eigene CSS-Eigenschaften, die ebenfalls in der Dokumentation aufgelistet werden, z.B.:

Darüber hinaus bietet Oxygen eigene Funktionen an, die in vielen CSS-Eigenschaften eingesetzt werden können und sehr viel möglich machen. So kann z.B. aus einem Attributwert mit mehreren verschiedenen Identifikationsnummern die passende herausgeholt, beschnitten und in einem Link weiterverwendet werden.

Sehr mächtig ist die Möglichkeit, mit Hilfe der Oxygen-CSS-Eigenschaften Formulare zu bauen – auch und gerade, um Attribute mit Werten zu versehen.

Schritt 6 – Werkzeugleiste bauen

Kern des Frameworks sind die »Aktionen«, die durch eine Schalftläche – sei es in einer Werkzeugleiste, im Menü oder im Kontextmenü – ausgelöst werden. Für jedes vom Bearbeiter einzufügende Element (mitsamt Attributen und Kindelementen) muss eine Aktion angelegt werden. Wir wollen das an zwei Beispielen demonstrieren.

Beispiel 1: Schaltfläche zum Auszeichnen einer Textänderung

In der Briefedition sollen von den Bearbeitern bei der Transkription auch diejenigen Stellen im Text ausgezeichnet werden, bei denen der Autor im Manuskript erst ein Wort durchgestrichen und es durch ein anderes ersetzt hat. In TEI-XML könnte das z.B. so aussehen:

<subst>
	<del rend="struck-through">bin</del>
	<add place="above">sind</add>
</subst>

oxy_framework_tutorial_011Nach dem Anlegen einer neuen Aktion sind ID, Name und Beschreibung der Aktion auszufüllen, außerdem können kleine Grafiken als Symbol für die Schaltfläche eingebunden werden. Der Hauptbestandteil einer Aktion sind allerdings die »Vorgänge«. Ein Vorgang besteht aus einer Bedingung, die in XPATH notiert ist sowie einer Javaoperation mitsamt ihren zu setzenden Argumenten. Für eine Aktion (d.h. Schaltfläche) kann man mehrere Vorgänge anlegen (ein großer Vorteil, wie wir im nächsten Beispiel sehen werden).

Die Bedingung kann man in diesem Fall dazu verwenden, dass der Vorgang nur ausgeführt wird, wenn sich der Cursor innerhalb von tei:text befindet (und nicht etwa im teiHeader). Da unsere Aktion nur diesen einen Vorgang enthalten wird, wird Oxygen (ab Version 14.2) die Schaltfläche ausgrauen, wenn dem nicht so ist. Die Bedingung können wir dann als XPath so notieren:

ancestor-or-self::text

Danach können wir (im Feld untendrunter) die Javaoperation auswählen, also den eigentlichen Einfüge- bzw. Auszeichnungsvorgang definieren. Oxygen liefert schon Javaoperationen für die meisten Anwendungsfälle mit. Für unseren Fall ist »Surround with Fragment« nützlich, da wir vllt. nicht nur einfach das Element an der Stelle des Cursors einfügen, sondern einen markierten Text damit umgeben wollen. Wir wählen also diese Javaoperation aus. Danach sehen wir die zu setzenden Argumente. Wichtig ist hier vor allem das erste Argument, dass aus dem XML-Code besteht, den wir einfügen wollen. Ihn können wir nach einem Doppelklick auf das Feld bequem eintragen.

Wenn Sie Namensräume benutzen, müssen Sie den Namensraum im Attribut @xmlns im obersten Element mit angeben.

Sie können Attribute mit festen Werten einsetzen oder aber den Benutzer nach diesen Werten fragen. Dafür steht in Oxygen XML die Editorvariable ask bereit. Mit ihr können wir den Benutzer auffordern, einen frei wählbaren Wert einzugeben. In vielen Fällen möchten wir ihn aber eher aus einer Liste von Werten wählen lassen. Das ist mit derselben Variable auch möglich – und empfehlenswert. Darüber hinaus ist es mit weiteren Editorvariablen möglich, automatisch das aktuelle Datum oder eine generierte Identifikationsnummer zu setzen. In der Oxygen-XML-Dokumentation finden Sie eine Übersicht aller Variablen.

Unser Beispiel sieht dann wie folgt aus:

<subst xmlns="http://www.tei-c.org/ns/1.0">
  <del rend="${ask('Art der Löschung', combobox, ('struck-through':'Durchgestrichen';'overwritten':'Überschrieben';'':'';), 'struck-through')}"></del>
  <add place="${ask('Ort der Hinzufügung', generic)}"></add>
</subst>

Jetzt nur noch bestätigen und unsere erste Aktion ist erstellt. Diese können wir nun je nach Bedarf in das Menü, das Kontextmenü oder eine Toolbar (»Symbolleiste«) einfügen. Dieser Abschnitt sollte selbsterklärend sein, aber ein Hinweis zum praktischen Einsatz kann nicht schaden: es ist sinnvoll im Menü alle Aktionen zu hinterlegen. In den Werkzeugleisten dann nur die wichtigsten und im Kontextmenü nur wenige, oft benutzte Aktionen, für die die Platzierung des Cursors relevant ist.

Beispiel 2: Schaltfläche zum Einfügen eines Strukturelement

Im Gegensatz zum ersten Beispiel, wo wir lediglich einen per Cursor markierten Textabschnitt ausgezeichnet haben, möchten wir nun ein sog. Strukturelement einfügen. Ein Element also, dass an einer einzigen, ganz bestimmten Stelle im XML-Baum notiert wird. In unserem Fall möchten wir z..B. den Absender, den Empfänger und das Schreibdatum in teiHeader/profileDesc notieren. Das könnte so aussehen:

<creation>
	<date when="1808-01-02" cert="high"/>
	<persName type="absender" key="p216">Blanc, Ludwig Gottfried </persName>
	<persName type="empfänger" key="p1914">Schleiermacher, Friedrich Daniel Ernst</persName>
</creation>

Alle drei Elemente sowie das Elternelement creation sollen erst vom Bearbeiter hinzugefügt werden – und jedes für sich. Als Beispiel schauen wir uns die Aktion an, die den Absender einfügt – also das mittlere Element persName. Hier haben wir nun das Problem, dass wir folgende Varianten der vorhandenen XML-Strkutur beim Einfügen bedenken müssen:

  1. Das Element creation ist (samt Kindelementen natürlich) gar nicht vorhanden
  2. Das Element creation ist vorhanden, nicht jedoch das Kindelement date
  3. Das Element creation und das Kindelement date sind vorhanden

Abhängig vom jeweiligen Szenario ändert sich natürlich das einzufügende XML-Snippet und/oder die Position des einzufügenden XML-Snippet. Wir möchten aber keine drei Schaltflächen erstellen, sondern lediglich eine einzige. Unser Vorteil ist nun, dass wir pro Aktion mehrere Vorgänge anlegen und sie jeweils von Bedingungen abhänging machen können. Im konkreten Beispiel  ergeben sich daher folgende Varianten:

Vorgang 1:
Bedingung, dass Element creation nicht vorhanden ist:
not(//teiHeader/profileDesc/creation)
Ausgelöster Vorgang: Fügt creation mitsamt des Kindelementes persName[@type='absender'] als erstes Kindelement von profileDesc ein. Wir benutzen hier (und in den beiden anderen Varianten) die Java-Operation »InsertFragmentOperation«, die es uns erlaubt einen Einfügeposition relativ zu einem anderen ebenfalls wählbaren Element definieren.

Vorgang 2:
Bedingung, dass Element creation vorhanden ist, nicht jedoch date:
//profileDesc/creation and not(//creation/date)
Ausgelöster Vorgang: Fügt persName[@type=’absender‘] als erstes Kindelement von creation ein.

Vorgang 3:
Bedingung, dass Element creation und Kindelement date vorhanden sind:
//profileDesc/creation and //profileDesc/creatioin/date
Ausgelöster Vorgang: Das Element persName[@type=’absender‘] wird nach dem Element date eingefügt.

Zu beachten ist beim Anlegen mehrerer Vorgänge noch deren Ausführungspriorität. Es wird nämlich nur derjenige Vorgang ausgeführt, dessen Bedingung zuerst erfüllt sind. Daher ist bei der Formulierung der Bedingungen auch darauf zu achten.

oxy_framework_tutorial_013An diesem Beipsiel wird deutlich, dass wir mit ein und derselben Schaltfläche drei verschiedene Einfügevorgänge abdecken können, ohne dass der Nutzer sich darüber Gedanken machen muss. Wenn wir jetzt noch allen Bedingungen //creation/persName[@type='absender'] hinzufügen, sorgen wir außerdem dafür, dass nur ein einzier Absender angelegt werden kann und ansonsten die Schaltfläche nicht zur Verfügung steht (d.h. ausgegraut angezeigt wird).

Die so erstellten Aktionen können nun im selben Tab in das Menü, das Kontextmenü oder eine Werkzeugleiste eingefügt werden.

Neben den standardmäßig in Oxygen XML vorhandenen Java-Operationen hat TELOTA für seine Lösung »ediarum« noch einige weitere Operationen programmiert, die u.a. den gleichzeitigen Zugriff auf einen Index ermöglichen. Wir haben diese Java-Operationen mittlerweile zur Verfügung gestellt und erläutern in einem eigenen Tutorial die Implementierung von Indexfunktionen.

Schritt 7 – Verpacken und verteilen

Wenn wir unser Oxygen-Framework fertig gestellt und getestet haben, möchten wir es den Benutzern zur Verfügung zu stellen. Dafür steht in Oxygen XML seit der Version 14.0 das »Add-on«-Feature bereit, mit dem man Frameworks über einen Webserver an Oxygen XML Installationen ausliefern und später auch updaten kann. Das funktioniert so:

  1. Den Ordner framworks/briefedition-xyz als ZIP-Datei packen
  2. Die ZIP-Datei auf einen Webserver hochladen
  3. Auf dem Webserver eine XML-Datei mit einem bestimmten Format (siehe Beispieldateien zu diesem Tutorial) anlegen und dort die Angaben zum Framework ergänzen. Am wichtigsten ist hier die Versionsnummer und der Pfad zur ZIP-Datei
  4. In einer Oxygen-XML-Installation unter Optionen > Einstellungen > Add-Ons die URL der XML-Datei eintragen
  5. Über Hilfe > Add-Ons verwalten kann man nun alle über diese URL verfügbaren Add-ons installieren oder updaten. Hat man in den Einstellungen (siehe vorhergehenden Punkt) das entsprechende Kästchen angehakt, wird man auch automatisch über Updates informiert.

Dadurch können zukünftig Updates schnell und unkompliziert ausgeliefert und vom Benutzer selbst aktualisiert werden.

Wie geht’s weiter?

Dieses Tutorial sollte nur einen ersten Überblick über die Erstellung eines Oxygen-Frameworks geben. In der offziellen Dokumentation von Oxygen XML finden Sie ausführliche Informationen zu diesem Thema. Außerdem ist geplant, demnächst weitere Tipps und Blogartikel zu ediarum veröffentlichen.

Download

Das im Beispiel angelegte Oxygen-Framework können Sie sich hier herunterladen. Darin enthalten ist auch eine Beispiel-XML-Datei für die Bereitstellung des Frameworks als »Add-On« auf einem Webserver.

Update 29.01.2015: Korrigierte Version des Oxygen-Frameworks für dieses Tutorial hochgeladen.

Kommentare

7 Kommentare zu “Tutorial: Wie baue ich ein eigenes Framework für Oxygen XML?”

  1. Tutorial: Wie baue ich ein eigenes Framework für Oxygen XML? | DHd-Blog am Oktober 30th, 2013 13:51

    […] eigene (TEI-)XML-Schema passen und maßgeschneiderte Werkzeugleisten und Ansichten mitbringen. Das Tutorial auf digiversity gibt einen Überblick darüber, wie ein solches Oxygen-Framework erstellt […]

  2. Zusätzliche Javaoperationen für Oxygen XML Frameworks veröffentlicht — digiversity am Dezember 16th, 2013 10:42

    […] sofort sind zusätzliche Javaoperationen für Oxygen XML Frameworks als Java-Archive (JAR) und als Quellcode zur freien Verwendung verfügbar. Die Javaoperationen […]

  3. Tutorial: Indexfunktionen für Oxygen XML Frameworks — digiversity am Dezember 16th, 2013 10:44

    […] Basiert auf dem Tutorial: Wie baue ich ein eigenes Framework für Oxygen XML? […]

  4. Indexfunktionen in Oxygen XML Frameworks (Tutorial & Download) | DHd-Blog am Dezember 16th, 2013 10:56

    […] hat nun die im Rahmen von ediarum entwickelten zusätzlichen Javaoperationen für Oxygen XML Frameworks als Java-Archive (JAR) und als Quellcode auf github zur Verfügung gestellt. Sie ergänzen die […]

  5. Sandro am Mai 10th, 2016 15:01

    Liebes Ediarum-Team,

    ich versuche gerade nach diesem Tutorial eigene Aktionen zu kreieren. Weder werden mir die Icons aus der ediarum.jar noch mein eigenes angezeigt.

    Haben Sie vielleicht eine Idee, was ich vergessen haben könnte.

    Mit herzlichen Grüßen
    Sandro Schwarz

  6. Sandro am Mai 10th, 2016 15:20

    Wenn ich das TEI-framework um meine Aktion erweitere funktioniert es.

  7. Stefan Dumont am August 1st, 2016 14:08

    Lieber Sandro,

    ich sehe leider jetzt erst Deinen Kommentar. Schick uns doch eine kurze Mail (siehe http://www.bbaw.de/die-akademie/mitarbeiter/dumont), falls das Problem weiterbestehen sollte. Wir helfen gerne weiter.

    Beste Grüße
    Stefan Dumont