Das MyCoRe XEditor-Framework

Das MyCoRe XEditor-Framework stellt Funktionalität zur Gestaltung von HTML-Formularen bereit, die XML-Dokumente bearbeiten oder erstellen. Die Formulardefinition enthält dazu Regeln zur Abbildung zwischen Eingabefeldern im Formular und XML-Elementen und -Attributen im bearbeiteten Dokument. Solche Formulare werden insbesondere zur Gestaltung von Eingabemasken für Metadaten und für komplexe Suchformulare verwendet. Eine Anleitung zum alten Editor Framework ist unter dem Punkt 'Veraltete Komponenten -> Editorframework' (Menüpunkt links unten) zu finden.

Architektur und Funktionsweise

XEditor-Formulare werden als MyCoReWebPage mit der Endung *.xed definiert. Die statische Webseite enthält neben anderen Elementen an einer Stelle ein <xed:form /> -Element, das das gesamte XEditor-Formular umschließt. Es ist derzeit nicht möglich, mehr als ein XEditor-Formular in der gleichen Webseite zu verwenden.

Ein XEditor-Formular besteht aus einer Kombination von HTML-Elementen und speziellen XEditor-Elementen. Die HTML-Elemente definieren Struktur und Layout des Formulars und werden vom XEditor-Framework weitgehend unverändert belassen. Zusätzliche XEditor-Elemente und -Attribute mit Namespace xmlns:xed="http://www.mycore.de/xeditor" definieren die Abbildung zwischen Eingabefeldern im HTML-Formular und Elementen und Attributen in dem XML-Dokument, das mit dem Formular bearbeitet oder erstellt wird. Weitere XEditor-Elemente regeln die Nachbearbeitung des resultierenden XMLs, steuern die Eingabevalidierung oder stellen Funktionen für dynamische Elemente wie z. B. wiederholbare Bereiche bereit. Wird eine Seite aufgerufen, die ein XEditor-Formular enthält, werden die speziellen XEditor-Element serverseitig in HTML-Strukturen transformiert und jedem Eingabefeld ein Element oder Attribut im bearbeiteten XML-Dokument zugeordnet. Browserseitig erscheint ein reines HTML-Formular.

Das Formular kann entweder leer starten, so dass beim Aufbau des Formulars ein neues XML-Dokument generiert wird, oder es kann ein vorhandenes XML-Dokument aus beliebiger Quelle laden und dessen Werte in den Eingabefeldern zur Bearbeitung bereitstellen. Elemente und Attribute, die nicht bestimmten Eingabefeldern im Formular zugeordnet sind, bleiben dabei unverändert im XML-Dokument erhalten. Bestimmte Bereiche des Formulars, einzelne Eingabefelder oder ganze Panels können wiederholt werden und entsprechen wiederholbaren Elementen im XML-Dokument. In einem Formular für Publikationsdaten kann z. B. ein Bereich zur Eingabe der Autoren wiederholbar gestaltet werden. Der Benutzer kann über Buttons Felder hinzufügen, entfernen oder vertauschen.

Beim Abschicken des Formulars werden die Werte zunächst an das XEditor-Servlet übermittelt. Dort werden sie in das XML-Dokument eingefügt. Das resultierende XML wird zusammen mit den abgeschickten Request-Parametern aus dem Formular via Request Dispatching an das eigentliche Zielservlet weitergeleitet. Wenn Validierungsregeln definiert sind, erfolgt zuvor eine serverseitig Eingabevalidierung. Schlägt die Validierung fehl, wird die Formularseite erneut angezeigt und dabei konfigurierbare Meldungen ausgegeben, damit der Benutzer die Eingabefehler korrigieren kann. Die Eingabevalidierung erfolgt immer serverseitig nach Abschicken des Formulars. Der Entwickler kann jedoch zusätzlich mit den üblichen Mitteln (HTML5, JavaScript) eine clientseitige Eingabevalidierung im Browser implementieren. Bereinigungsregeln (Cleanup Rules) dienen der Nachbearbeitung des resultierenden XML-Dokumentes und ermöglichen es, logisch leere Elemente und Attribute zu entfernen, für die z. B. zwar ein Eingabefeld angeboten wurde, aber kein Wert eingegeben wurde.

Bei Aufruf der Seite können Request Parameter verwendet werden. Diese Parameter sind, zusammen mit Konfigurationswerten aus mycore.properties oder Variablen aus der Session des Benutzers (MCRSession) an vielen Stellen im Formular verwendbar, um dessen Aufbau zu steuern. Alle Request Parameter aus dem ursprünglichen Aufruf der Seite werden zusätzlich zu den Formularwerten auch an das Zielservlet durchgereicht.

Über die Elemente <xed:if /> , <xed:choose /> und <xed:include /> können Formular dynamisch gestaltet werden, so dass sie abhängig von Aufruf- oder Konfigurationsparametern Teilbereiche unterschiedlich darstellen oder aus Teilen zusammensetzen.

Beschriftungen und Texte im Formular können über messages-*.properties Dateien mehrsprachig ausgegeben werden.

Grundkonfiguration des XEditor-Frameworks

In einer eigenen Anwendung müssen nur wenige Properties ggf. angepasst werden.

Wird ein XEditor-Formular aufgerufen, speichert das Framework das bearbeitete XML und weitere Informationen serverseitig in der Session des Benutzers.

MCR.XEditor.MaxEditorsInSession=50

definiert, wie viele Formulare maximal vorgehalten werden.

Das Layout von Validierungsmeldungen und Buttons zur Steuerung wiederholter Bereiche ist frei anpassbar. Hierzu kann die vorhandene Datei xeditor-custom.xsl aus dem MyCoRe Kern überschrieben oder durch ein anderes Stylesheet ersetzt werden, das eigene Templates für diese Elemente enthält.

MCR.URIResolver.xslIncludes.xeditor=xeditor-custom.xsl

Webseiten, die XEditor-Formulare enthalten, müssen bei Aufruf durch das MCRStaticXEditorFileServlet verarbeitet werden. Das Standard-Mapping, welches bereits beim Aktivieren des Moduls mit bereitgestellt wird, ist hierfür

<servlet-mapping>
  <servlet-name>MCRStaticXEditorFileServlet</servlet-name>
  <url-pattern>*.xed</url-pattern>
</servlet-mapping>

Ein erstes Beispiel

Das folgende Beispiel zeigt ein einfaches XEditor-Formular, das den Titel eines Dokumentes bearbeitet. Es ist an das Formular simpledoc.xed aus dem MyCoRe Skeleton Projekt angeleht.

<?xml version="1.0" encoding="UTF-8"?>

<MyCoReWebPage>
  <section xml:lang="all" title="Erfassungsmaske">

    <xed:form xmlns:xed="http://www.mycore.de/xeditor" role="form" class="form-horizontal">
      <xed:source uri="mcrobject:{$id}" />

      <xed:bind xpath="/mycoreobject">
        <xed:bind xpath="metadata">

          <xed:bind xpath="def.title[@class='MCRMetaLangText']/title">
            <div class="form-group">
              <label class="col-md-4 control-label" for="title">
                <xed:output i18n="editor.label.title" />
              </label>
              <div class="col-md-6">
                <input id="title" placeholder="{i18n:editor.placeholder.title}" class="form-control input-md" type="text" />
              </div>
            </div>
          </xed:bind>

          <div class="form-group">
            <label class="col-md-4 control-label" for="save"></label>
            <div class="col-md-8">
              <button id="save" class="btn btn-success updateobject" type="submit" xed:target="servlet" xed:href="UpdateObjectServlet">
                <xed:output i18n="common.button.save" />
              </button>
            </div>
          </div>

        </xed:bind>
      </xed:bind>
    </xed:form>

  </section>
</MyCoReWebPage>
xed:form / Zeile 6
bildet den äusseren Rahmen jedes XEditor-Formulars und entspricht dem <form>-Element von HTML.
xed:source / Zeile 7
lädt das zu bearbeitende XML-Dokument von einer Quelle, die als URI angegeben wird. In diesem Fall wird der Parameter id verwendet, um über den mcrobject: Resolver ein MyCoRe Metadaten-Objekt (Dokument, Publikation o.ä.) zu laden. Wenn das Formular im Browser über simpledoc.xed?id=skeleton_simpledoc_00000001 aufgerufen wird, wird das Objekt mit dieser ID geladen.
xed:bind / Zeilen 9, 10 und 12
regelt die Abbildung zwischen Bereichen im XML-Dokument und den zugehörigen Bereichen im Eingabeformular. Verschachtelte <xed:bind> Elemente werden verwendet, um hierarchisch einzelne Metadaten-Felder auf einzelne Eingabefelder abzubilden. In diesem Fall wird das Texteingabefeld für den Titel in Zeile 18 auf das XML-Element mit dem XPath /mycoreobject/metadata/def.title/title abgebildet.
xed:output / i18n: / Zeilen 15, 18 und 27
wird für die Ausgabe von Werten oder Bezeichnungen, in diesem Fall mehrsprachige Beschriftungen für das Eingabefeld und einen Platzhalter aus den messages-*.properties Dateien, verwendet.
xed:target / xed:href / Zeile 26
definiert für den Absenden-Button des Formulars, wohin das resultierende XML-Dokument geschickt werden soll, in diesem Fall an das UpdateObjectServlet, das die Änderungen speichert.

Formulare definieren

<xed:form />

... umschliesst das gesamte XEditor-Formular, entspricht dem HTML <form /> Element und wird in ein solches transformiert. Das XEditor-Framework setzt das Attribut @action des <form /> Elementes, damit die Formularwerte an das XEditor-Servlet gesendet werden. Bei Absenden des Formulars werden die Werte zunächst vom XEditor-Servlet verarbeitet und danach ggf. an das eigentliche Zielservlet per Request Dispatching als XML-Dokument weitergegeben. Weitere optionale Attribute werden 1:1 von <xed:form /> zu <form /> kopiert.

Die Standard Submit-Methode ist "post", kann aber über <xed:form method="get" ... überschrieben werden.

Sollen im bearbeiteten XML-Dokument zusätzliche Namespaces verwendet werden, sollten diese als Namespace Attribute (xmlns:...) beim <xed:form /> Element definiert werden. Die in MyCoRe häufig verwendeten Namespaces für MyCoRe, MODS, METS, XLink etc. sind bereits vordefiniert und müssen nicht angegeben werden.

Zu bearbeitendes XML laden: <xed:source />

Syntax:

<xed:source uri="..." />

Beispiel:

<xed:source uri="mcrobject:{$id}" />

Lädt das in diesem Formular zu bearbeitende XML-Dokument von einer URI.

<xed:source /> ist nur erforderlich, wenn das Formular nicht "leer" starten soll, sondern ein existierendes XML-Dokument zur Bearbeitung laden soll. Das können z. B. die Metadaten eines MyCoRe-Objektes sein, oder auch eine XML-Datei, die als Template (Vorlage) dient.

Das Attribut @uri kann eine beliebige URI sein, die der MyCoRe URI Resolver unterstützt (z. B. mcrobject:, resource:, webapp:, file:, http:.) und gibt die Quelle an, von der das XML-Dokument gelesen wird.

Das Attribut @uri kann optional ein oder mehrere Variablen enthalten, die bei Aufruf des Formulars durch Parameter ersetzt werden. Variablen stehen in geschweiften Klammern und beginnen mit einem $ Zeichen, z. B.

<xed:source uri="mcrobject:{$id}" />

Die Variablen werden bei Aufruf des Formulars durch den konkreten Wert ersetzt, wobei als Quelle die HTTP Request Parameter aus der URL des Aufrufs, Konfigurationswerte aus mycore.properties oder Werte aus der MCRSession verwendet werden können.

<xed:source /> ist wiederholbar. Wenn mehrere <xed:source /> Elemente angegeben sind, wird das erste Element verwendet, das keine Variablen enthält, oder bei dem alle angegebenen Variablen durch konkrete Werte ersetzt werden können. So kann man z. B. abhängig von Aufruf- oder Konfigurationsparametern XML-Dokumente aus verschiedenen Quellen laden.

Beispiel:

<xed:source uri="xslStyle:mcrobject2{$type}:mcrobject:{$id}" />
<xed:source uri="mcrobject:{$id}" />
<xed:source uri="webapp:template.xml" />
  • Wenn das Formular mit [Seite].xed?id=DocPortal_document_07910403 aufgerufen wird, wird die zweite URI verwendet, denn der Parameter $type ist nicht gegeben. Die URI mcrobject:DocPortal_document_0791040 lädt die Metadaten des MyCoRe-Objektes mit der ID DocPortal_document_07910403 zur Bearbeitung.
  • Wenn das Formular stattdessen mit [Seite].xed?type=similar&id=DocPortal_document_07910403 aufgerufen wird, wird die erste URI verwendet, und ein XML-Dokument von xslStyle=mcrobject2similar:mcrobject:DocPortal_document_07910403 geladen, d.h. die Metadaten des MyCoRe-Objektes mit der ID DocPortal_document_07910403 werden geladen, durch das XSL Stylesheet mcrobject2similar.xsl transformiert und in das Formular geladen.
  • Wenn das Formular ohne Parameter aufgerufen wird, wird die dritte URI verwendet, und eine statische Vorlage aus der Datei template.xml der Webapplikation geladen.

Es wäre auch denkbar, die komplette URI als Parameter zu übergeben. Davon kann nur abgeraten werden, da es die Aufruf-URLs unnötig lang macht, Implementierungsdetails offenlegt und potentiell zu Sicherheitslücken führt:

<xed:source uri="{$uri}" />
Aufruf mit [Seite].xed?uri=mcrobject:DocPortal_document_07910403

Abbildung zwischen XML und HTML: <xed:bind />

Legt die Abbildung zwischen Eingabe-Komponenten im HTML-Formular und -Knoten im XML-Dokument fest.

Syntax: <xed:bind xpath="..." default="..." initially="..." set="..." name="...">

Abbildung auf XML-Knoten über XPath-Ausdrücke

Das Attribut @xpath kann ein beliebiger XPath 1.0 Ausdruck sein, der ein oder mehrere Elemente oder Attribute im zu bearbeiteten XML selektiert. Das Element drückt aus, dass alle durch das <xed:bind /> umschlossenene Bereiche des Formulars sich auf die selektierten Elemente und Attribute beziehen. Innerhalb des <xed:bind /> Elementes können beliebige HTML- oder XEditor-Elemente ineinander geschachtelt werden, so dass sich eine hierachische Abbildung zwischen XML-Dokumente und HTML-Formular ergibt.

Beispiel:

<xed:bind xpath="mods:titleInfo">
  <xed:bind xpath="mods:title">
    <div>
      <label for="title">Titel:</label>
      <input id="title" type="text" />
    </div>
  </xed:bind>
  <xed:bind xpath="mods:subtitle">
    <div>
      <label for="subtitle">Untertitel:</label>
      <input id="subtitle" type="text" />
    </div>
  </xed:bind>
</xed:bind>

In diesem Beispiel bezieht sich das Eingabefeld mit der ID "title" auf das XML-Element mods:titleInfo/mods:title, das Eingabefeld mit der ID "subtitle" auf das XML-Element mods:titleInfo/mods:subtitle. Der XEditor setzt dabei für HTML input-Elemente vom Typ text, password, hidden, file, color, date, datetime, datetime-local, email, month, number, range, search, tel, time, url, week automatisch die @name und @value Attribute, für HTML checkbox- und radio-Elemente die @name und @checked Attribute, sowie für HTML select/option Elemente die @name bzw. @selected Attribute. Das Attribut @name der Eingabekomponente wird dabei auf den absoluten XPath des XML-Knotens gesetzt, der durch das aktuelle Bindung durch die verschachtelten <xed:bind /> Elemente ausgewählt wurde. Die Attribute @value bzw. @selected oder @checked werde so gesetzt, dass ihr Wert dem Inhalt des selektierten Knotens entspricht.

Ein XEditor-Formular kann also so gestaltet werden, dass man den Entwurf mit einem HTML-Formular beginnt, dann die @name, @value, @checked, @selected Attribute der Eingabekomponenten entfernt und diese funktional duch umgebende <xed:bind /> Elemente ersetzt.

Beispiel:

XML-Dokument, das durch das Formular bearbeitet wird:

<document>
  <title>Titel der Publikation</title>
  <services>
    <service>OAI</service>
  <services>
</document>

XEditor, der dieses Dokument bearbeitet:

<xed:bind xpath="document">
  <xed:bind xpath="title">
    <div>
      <label>Titel:</label>
      <input type="text" />
    </div>
  </xed:bind>
  <xed:bind xpath="services/service">
    <div>
      <label>Dienste:</label>
      <input type="checkbox" value="OAI" />Über OAI-Schnittstelle publizieren
      <input type="checkbox" value="POD" />Print on Demand anbieten
    </div>
  </xed:bind>
</xed:bind>

Resultierendes HTML:

<div>
  <label>Titel:</label>
  <input type="text" name="/document/title" value="Titel der Publikation" />
</div>
<div>
  <label>Dienste:</label>
  <input type="checkbox" value="OAI" name="/document/service" checked="checked" />Über OAI-Schnittstelle publizieren
  <input type="checkbox" value="POD" name="/document/service" />Print on Demand anbieten
</div>

Prädikate und Generieren von XML-Knoten

Der XPath-Ausdruck kann beliebige Prädikate enthalten, z. B.

<xed:bind xpath="mods:titleInfo[@type='translated'][@xml:lang='en']/mods:title">
  <div>
    <label for="title_en">Englische Übersetzung des Titels:</label>
    <input id="title_en" type="text" />
  </div>
</xed:bind>

Falls der angegebene XPath-Ausdruck keinen bereits existierenden XML-Knoten selektiert, wird dieser automatisch generiert. Dabei werden auch alle angegebenen Prädikate berücksichtigt. Es gilt die Einschränkung, dass der XPath-Ausdruck nur Elemente und Attribute auf der Child-Achse generieren kann und keine XPath-Funktionen oder -Operatoren (bis auf den Vergleichsoperator) verwenden darf. Ausdrücke oder Prädikate, die nicht generiert werden können, werden ignoriert.

Beispiel:

<xed:bind xpath="mods:name[@type='personal'][mods:role/mods:roleTerm[@type='code'][@authority='marcrelator']='aut']/mods:displayForm">
  <label>Autor:</label>
  <input type="text" ... />
</xed:bind>

erzeugt, falls kein mods:name Element existiert, das alle Prädikate erfüllt, folgende XML-Elemente:

<mods:name type="personal">
  <mods:role>
    <mods:roleTerm type="code" authority="marcrelator">aut</mods:roleTerm>
  </mods:role>
  <mods:displayForm />
</mods:name>

Gleichzeitig wird dem Eingabefeld das Attribut @name=".../mods:name/mods:displayForm" zugewiesen.

Beispiel: <xed:bind xpath="mods:name[@type='personal'][contains('aut edt',mods:role/mods:roleTerm)]" />

Falls kein mods:name Element existiert, das alle diese Prädikate erfüllt, wird ein neues Element <mods:name type="personal" /> generiert. Aus dem zweiten Prädikat kann wegen der Verwendung der contains() Funktion nicht allgemeingültig abgeleitet werden, welche XML-Knoten zu generieren wären, damit es erfüllt ist. Dieses Prädikat wird daher ignoriert. Bei der Implementierung des Formulars muss der Entwickler selbst sicherstellen, dass diese Bedingung nachträglich auch für neu generierte Knoten erfüllt wird, in diesem Fall z. B. indem für mods:roleTerm eine Auswahlliste angeboten wird.

Die Generierung fehlender XML-Elemente und -Attribute erfolgt bereits im Moment der Verarbeitung des XEditor-Formulars, schon vor der Darstellung im Browser. D.h. dass sich zum Zeitpunkt der Anzeige des HTML-Formulars alle im Formular enthaltenen Eingabekomponenten auf bereits existierende oder neue, leer generierte Elemente oder Attribute beziehen. Beim Abschicken des Formulars werden die Textknoten der XML-Element bzw. die Werte der Attribute entsprechend aus den abgeschickten Formularwerten gesetzt.

Es ist auch möglich, leere <xed:bind /> Elemente zu verwenden, um XML-Knoten nur zu generieren oder deren Existenz sicherzustellen, ohne dafür Eingabekomponenten im HTML-Formular zu definieren. Beispiel:

<xed:bind xpath="sortBy">
  <xed:bind xpath="field[@name='year'][@order='descending']" />
  <xed:bind xpath="field[@name='title'][@order='ascending']" />
</xed:bind>

Default- und Initialwerte

Das optionale Attribut @default legt einen Wert fest, der als Default-Wert verwendet wird, wenn der XML-Knoten neu generiert wird, oder aber vor oder nach der Bearbeitung im Formular leer ist, d.h. keinen Textwert hat. Beispiele:

<xed:bind xpath="@numPerPage" default="10">
  <select id="numPerPage">
    <option>5</option>
    <option>10</option>
    <option>20</option>
    <option>50</option>
  </select>
</xed:bind>
<xed:bind xpath="mods:genre" default="article" />

Das optionale Attribut @initially legt einen Wert fest, der nur dann als initialer Wert verwendet wird, falls der XML-Knoten nicht bereits existiert und neu generiert wird. Im Gegensatz zu @default wird dieser Wert aber nicht gesetzt, wenn der Knoten existiert oder wenn er durch die Eingabe leer gesetzt wird. Das ist insbesondere bei einzeln stehenden Checkboxen als Sonderfall erforderlich:

<xed:bind xpath="@publish" initially="true" default="false">
  <input type="checkbox" value="true" /> Dokument publizieren
</xed:bind>
  • Sofern das Attribut @publish im bearbeiteten XML noch nicht existiert, wird es als @publish="true" generiert. Die Checkbox ist dann aktiviert, da sie den @value="true" hat.
  • Sofern das Attribut @publish im bearbeiteten XML schon existiert (mit entweder "true" oder "false", wird es mit dem existierenden Wert im Formular übernommen. Die Checkbox ist aktiviert, falls das existierende Attribut den Wert "true" hat, ansonsten ist sie nicht aktiviert.
  • Wenn vor Absenden des Formulars die Checkbox deaktiviert wurde, übermittelt der Browser beim Absenden keinen Wert. In diesem Fall setzt der XEditor als Default-Wert @publish="false", da das Attribut leer übermittelt wird.

Das optionale Attribut @set legt einen Wert fest, der beim Aufbau des Formulars immer als initialer Wert gesetzt wird, und der den bisherigen Wert überschreibt, auch wenn der XML-Knoten bereits existiert. Beispiel:

<xed:bind xpath="version" set="2.0" />

setzt in jedem Fall das XML-Element auf <version>2.0</version>, auch wenn bereits ein <version /> Element mit anderem Wert existierte.

Der XPath-Ausdruck und die weiteren Attribute von <xed:bind /> können auch auf Variablen zugreifen, wobei HTTP Request Parameter aus der URL des Aufrufs, Konfigurationswerte aus mycore.properties oder Werte aus der MCRSession verwendet werden können. Beispiel:

<xed:bind xpath="mods:titleInfo[@xml:lang=$DefaultLang]/mods:title">...</xed:bind>

<xed:bind xpath="mods:genre" initially="{$genre}" /> setzt bei Aufruf des Formulars mit [Seite].xed?genre=article das Element <mods:genre>article</mods:genre>, sofern noch kein mods:genre Element existiert.

Die durch <xed:bind /> ausgewählten XML-Elemente oder Attribute können unter einer Variablen abgelegt werden, die an nachfolgender Stelle in XPath-Ausdrücken wiederverwendet werden kann. Das Attribut @name spezifiziert den Namen der Variablen. Beispiel:

<xed:bind xpath="mods:name">
  <xed:bind xpath="@ID" name="id_name" />
  Name: <input type="text" />
  <xed:bind xpath="../mods:note[@xlink:href=$id_name]">
    Notiz: <input type="text" />
  </xed:bind>
</xed:bind>

Im Beispiel wird der Wert von mods:name/@ID in der Variablen $id_name abgelegt, und später das entsprechende mods:note Element selektiert, das in seinem Attribut @xlink:href diese ID referenziert.

Wiederholbare Bereiche

Bereiche mit <xed:repeat /> wiederholbar machen

<xed:repeat /> ist eine Variante von <xed:bind /> für wiederholbare Formularbereiche, die einem wiederholbaren Element im bearbeiteten XML-Dokument entsprechen. In einem Formular für Publikationsdaten können so z. B. Eingabebereiche für die Daten mehrerer Autoren wiederholt werden. Der Benutzer kann über Buttons die wiederholten Abschnitte vertauschen, einzelne entfernen oder weitere einfügen.

Syntax:

<xed:repeat xpath="..." min="..." max="..." method="...">...</xed:repeat>

Das Attribut @xpath kann ein beliebiger XPath 1.0 Ausdruck sein, der ein oder mehrere wiederholte Elemente im zu bearbeiteten XML selektiert.

Der durch <xed:repeat /> umschlossene Bereich wird so oft im Formular wiederholt eingefügt, wie die Anzahl der durch den XPath-Ausdruck selektierten Elemente. Ist das Attribut @min angegeben, wird der Bereich mindestens so oft wie darin angegeben wiederholt. Selektiert der XPath-Ausdruck weniger als @min Elemente, werden weitere Elemente erzeugt, bis @min Elemente vorhanden sind.

Beispiel:

<xed:repeat xpath="mods:name[@type='personal']" min="2">
  <div>
    <xed:bind xpath="mods:namePart[@type='given']">
      <label>Vorname:</label><input type="text" />
    </xed:bind />
    <br />
    <xed:bind xpath="mods:namePart[@type='family']">
      <label>Nachname:</label><input type="text" />
    </xed:bind>
  </div>
</xed:repeat>

Das Beispiel wiederholt einen Bereich zur Eingabe von Personen im MODS Datenmodell mit Vorname und Nachname. Wenn im bearbeiteten XML drei mods:name[@type='personal'] Elemente vorhanden sind, wird der Bereich dreimal wiederholt und jeder wiederholte Abschnitt an das entsprechende XML-Element gebunden. Wenn aktuell nur ein mods:name[@type='personal'] vorhanden ist, wird ein weiteres Element erzeugt, da @min="2", und es werden zwei Bereiche für Personennamen im Formular dargestellt.

Das optionale Attribut @method gibt an, auf welche Weise neue Elemente erzeugt werden. Mit method="build" wird ein neues Element über den XPath-Ausdruck gebaut, analog zum Verhalten von xed:bind, d.h. alle über Prädikate angegebenen Attributwerte (und ggf. weitere Unterstrukturen) werden erzeugt. Mit method="clone" wird ein neues Element durch Klonen des vorangehenden Elementes inkl. aller Kindelemente, Attribute und Textknoten erzeugt. Wenn das Attribut @method nicht angegeben ist, wird das Default-Verhalten durch das Property MCR.XEditor.InsertTarget.DefaultMethod=build festgelegt.

Das Attribut @max gibt an, wie oft der Bereich maximal wiederholt werden kann, wenn der Benutzer einen Button zum Einfügen einer weiteren Wiederholung klickt. Allerdings wird immer mindestens so oft wiederholt, wie bereits Elemente im bearbeiteten XML-Dokument vorhanden sind.

Buttons zum Bearbeiten wiederholter Bereiche: <xed:controls />

<xed:controls /> bestimmt Art und Position der Buttons zur Bearbeitung des wiederholten Bereiches. Diese Buttons werden als Submit Buttons implementiert, d.h. bei Klick auf den Button wird das Formular abgeschickt, die Änderung serverseitig durchgeführt und im Anschluss das veränderte Formular neu dargestellt.

  • Die Position der Buttons kann an beliebiger Stelle innerhalb des durch <xed:repeat /> umschlossenen Bereiches frei bestimmt werden.
  • Das Element <xed:controls /> wird an der Position eingefügt, wo die Buttons erscheinen sollen.
  • Der Text von <xed:controls /> bestimmt, welche Buttons an der jeweiligen Stelle erscheinen. Möglich sind ein oder mehrere durch Leerzeichen getrennte Werte:
    • Ein "up" Button erscheint ab dem zweiten Element und vertauscht es im Formular und im bearbeiteten XML-Dokument mit seinem Vorgänger.
    • Ein "down" Button erscheint bei allen bis auf das letzte Element und vertauscht es im Formular und im bearbeiteten XML-Dokument mit seinem Nachfolger.
    • Ein "remove" Button entfernt das aktuelle Elemente im Formular und im XML-Dokument.
    • Ein "insert" Button fügt hinter dem aktuellen Element einen neues Element ein und stellt es im Formular dar.
    • Ein "append" Button kann als Alternative zu "insert" verwendet werden und erscheint nur für das letzte Element, um nur am Ende ein weiteres Element einzufügen.

Ein leeres <xed:controls /> Element entspricht der Standard-Auswahl <xed:controls>insert remove up down</xed:controls> Sollen keine Buttons erscheinen, lässt man das <xed:controls /> Element weg. Sollen nur bestimmte Buttons erscheinen, gibt man diese im Text des Elementes an.

Beispiel:

<xed:repeat xpath="mods:name[@type='personal']" min="2">
  <div>
    ...
    <xed:controls>append remove up down</xed:controls>
  </div>
</xed:repeat>

HTML-Formatierung und Layout der Buttons werden durch ein XSL-Template in xeditor-custom.xsl definiert und können durch lokales Überschreiben in der eigenen Webanwendung beliebig angepasst werden. Das Template wird für jeden Button einzeln aufgerufen. Das Template muss einen Parameter $name als Name des Buttons setzen und so beim Absenden des Formulars durchreichen. Die Buttons können als HTML <button />, <input type="submit" /> oder <input type="image" /> implementiert werden.

Beispiel:

<xsl:template match="text()" mode="xed.control">
  <!-- append insert remove up down -->
  <xsl:param name="name" />
  <!-- name to submit as request parameter when button/image is clicked -->
  <xsl:if test=".='append'">
    <input type="submit" value="+" name="{$name}" />
  </xsl:if>
  ...
</xsl:template>

IDs generieren

Über die Funktion xed:generate-id() kann eine eindeutige ID für den gerade via xed:bind / xed:repeat ausgewählten XML-Knoten generiert werden. Diese IDs benötigt man, um im HTML Quellcode z. B. eindeutige Referenzen für die zugehörigen Labels zu haben.

Syntax:

xed:generate-id([XPath])

Im folgenden Beispiel wird für jede Wiederholung des Eingabefeldes für mods:namePart eine eindeutige ID generiert:

<xed:repeat xpath="mods:name[@type='personal']" min="2">
  <div>
    <xed:bind xpath="mods:namePart[@type='given']">
      <label for="{xed:generate-id()}">Vorname:</label>
      <input type="text" id="{xed:generate-id()}" />
    </xed:bind />
    ...
  </div>
</xed:repeat>

Ausgabe von Werten und mehrsprachigen Bereichen

Werte ausgeben mit <xed:output />

Syntax:

<xed:output value="..." i18n="..." />

Ermöglicht die Ausgabe eines Wertes. Der Wert kann über einen XPath-Ausdruck definiert werden und/oder das aktuelle Binding im bearbeiteten XML-Dokument verwenden. Auch eine Kombination mit mehrsprachigen Bezeichnern aus messages-*.properties Dateien ist möglich.

Wenn kein Attribut angegeben ist, wird der erste durch das aktuelle Binding ausgewählte Wert ausgegeben. Beispiel:

<xed:bind xpath="title">
  <label>Titel:</label>
  <xed:output />
</xed:bind>

Wenn das Attribut @value angegeben ist, wird der darin enthaltene XPath-Ausdruck ausgewertet und ausgegeben. Der XPath-Ausdruck kann die XML-Struktur des bearbeiteten Dokumentes, sowie Variablen verwenden, die aus HTTP Request Parametern, Konfigurationsparametern aus mycore.properties oder MCRSession Variablen stammen. Beispiele:

<label>Name: </label>
<xed:output value="concat(mods:namePart[@type='family'],', ',mods:namePart[@type='given'])" />
<xed:bind xpath="title">
  Diese Publikation mit dem Titel "<xed:output />" dürfen Sie als <xed:output value="$SomeUser" /> bearbeiten.
</xed:bind>

Wenn das Attribut @i18n angegeben ist, wird der mehrsprachige Bezeichner aus den messages-*.properties Dateien ausgegeben, dessen Schlüssel angegeben ist. Beispiel:

<label>
  <xed:output i18n="edit.title" />:
</label>

Das Attribut @i18n kann auch Variablen enthalten, die dann, wie in einem XPath-Ausdruck, ersetzt werden.

Wenn sowohl das Attribut @value als auch das Attribut @i18n angegeben ist, wird zunächst der XPath-Ausdruck aus dem Attribut @value ausgewertet, und der resultierende Wert in den mehrsprachigen Bezeichner eingesetzt. Der Bezeichner in den messages-*.properties Dateien muss einen entsprechenden Platzhalter vorsehen. Beispiel:

In messages-de.properties: edit.datePublished=Das Dokument wurde am {0} veröffentlicht.

In messages-en.properties: edit.datePublished=The document was publised at {0}.

<label>
  <xed:output value="date[@type='published']" i18n="edit.datePublished" />
</label>

Ausgabe eines Wertes innerhalb von HTML-Attributen

Syntax:

{XPath} oder {i18:key} oder {i18n:key,XPath} oder {i18n:translate(...)}

Enthält ein Attribut einen Ausdruck, der durch geschweifte Klammern umschlossen ist, wird dieser Ausdruck ausgewertet und an dieser Stelle im Attribut eingesetzt. Bei dem Ausdruck kann es sich um einen XPath-Ausdruck handeln, oder um einer mehrsprachigen Bezeichner aus den messages-*.properties Dateien. Der XPath-Ausdruck kann die XML-Struktur des bearbeiteten Dokumentes, sowie Variablen verwenden, die aus HTTP Request Parametern, Konfigurationsparametern aus mycore.properties oder MCRSession Variablen stammen.

Beispiele:

<img src="{@href}" />
<a href="{$WebApplicationBaseURL}receive/{@xlink:href}">Link zum reverenzierten Objekt</a>

Wenn der Ausdruck mit "i18n:" beginnt, wird der mehrsprachige Bezeichner aus den messages-*.properties Dateien ausgegeben, dessen Schlüssel angegeben ist. Es ist möglich, einen XPath-Ausdruck mit einem i18n Schlüssel zu kombinieren, indem Schlüssen und XPath Ausdruck durch ein Komma getrennt werden. In diesem Fall wird zunächst der XPath-Ausdruck ausgewertet, und der resultierende Wert in den mehrsprachigen Bezeichner eingesetzt. Der Bezeichner in den messages-*.properties Dateien muss einen entsprechenden Platzhalter vorsehen.

Alternativ kann - wie in XSL Stylesheets - die Funktion i18n:translate(...) aufgerufen werden, die einem Aufruf der Methoden der Java-Klasse org.mycore.services.i18n.MCRTranslation entspricht.

Beispiele:

<input type="submit" xed:target="cancel" value="{i18n:label.cancel}" />
<input type="text" placeholder="{i18n:translate('placeholder.date.issued')}" />
<img alt="{i18n:image.title,title[@type='main']}" src="{@href}" />

Mehrsprachige Formularbereiche: <xed:multi-lang>

Ermöglicht es, abhängig von der aktuell verwendeten Sprache (Variable $CurrentLang der MCRSession), Teile des Formulars in verschiedenen Varianten anzubieten.

Syntax:

<xed:multi-lang>
  <xed:lang xml:lang="..."></xed:lang>
  <xed:lang xml:lang="..."></xed:lang>
</xed:multi-lang>

Das Attribut @xml:lang definiert die Sprache (nach IETF BCP 47), für die der umschlossene Block verarbeitet und angezeigt werden soll. Wenn kein <xed:lang /> Bereich für die aktuelle Sprache definiert ist, wird der Bereich verwendet, der für die Default-Sprache (MCR.Metadata.DefaultLang aus mycore.properties) definiert ist. Wenn auch für die Default-Sprache kein Bereich definiert ist, wird der erste <xed:lang /> Bereich verwendet.

Beispiel:

<xed:multi-lang>
  <xed:lang xml:lang="de">
     <label>Straße:</label>...
     <label>Hausnummer:</label>...
     <label>PLZ:</label>...
     <label>Ort:</label>...
  </xed:lang>
  <xed:lang xml:lang="en">
     <label>Number:</label>...
     <label>Street:</label>...
     <label>City:</label>...
     <label>ZIP Code:</label>...
  </xed:lang>
</xed:multi-lang>

Ausgabe mittels externer XML Ressourcen: <xed:load-resource>

Lädt ein XML-Dokument von einer URI und stellt es in einer Variablen bereit, um in XPath-Ausdrücken darauf zuzugreifen.

Syntax:

<xed:load-resource name="..." uri="..." />

Das Attribut @name gibt den Namen der Variablen an, unter dem das XML-Element bereitgestellt werden soll. Das Attribut @uri gibt die URI an, von der das XML geladen werden soll. Dies kann eine beliebige URI sein, die der MyCoRe URI Resolver unterstützt (z. B. classification:, resource:, webapp:, file:, http:.) Innerhalb des Attributes @uri kann auch auf XPath Ausdrücke oder Variablen zugregriffen werden.

<xed:load-resource /> kann in Kombination mit <xed:output /> verwendet werden, um Werte im Formular auszugeben, die sowohl vom bearbeiteten XML als auch von Parametern oder externen XML-Ressourcen abhängen. Beispielsweise kann man Bezeichnungen von Klassifikationskategorien im Formular ausgeben, wobei die IDs der Kategorien im im bearbeiteten XML-Dokument liegen, die Bezeichnungen aber in der Klassifikation verwaltet werden:

<xed:load-resource name="genres" uri="classification:metadata:-1:children:modsgenre" />

lädt die Klassifikation "modsgenre" und stellt sie in einer Variablen $genres bereit.

<xed:bind xpath="mods:genre" name="genre" />

wählt das Element mods:genre und stellt es in der Variablen $genre bereit.

<xed:output value="$genres//category[@ID=$genre]/label[lang($CurrentLang)]/@text" />

gibt dann die Bezeichnung der Kategorie aus der Klassifikation aus, deren ID der Wert von <mods:genre /> ist, in der aktuellen Sprache.

Das Formular absenden

Abbruch-URL definieren: <xed:cancel />

Syntax:

<xed:cancel url="..." />

Beispiel:

<xed:cancel url="/receive/{$id}" />

Definiert die URL, zu der bei Abbruch zurückgekehrt werden soll, z. B. bei Betätigen eines Abbrechen-Buttons, um die Bearbeitung aus dem Formular heraus abzubrechen.

<xed:cancel /> sollte angegeben werden, wenn das Formular einen Abbrechen-Button enthält.

  • Wenn eine relative URL angegeben ist, wird diese als relativ zu der Seite interpretiert, die das Formular enthält.
  • Wenn ein absoluter Pfad angegeben ist, wird dieser als relativ zur Basis-URL der Webanwendung interpretiert.
  • Wenn eine vollständige URL angegeben ist, wird diese unverändert verwendet.
  • Wenn keine URL angegeben ist, wird zur Basis-URL der Webanwendung geleitet.

Das Attribut @url kann optional ein oder mehrere Variablen enthalten, die bei Aufruf des Formulars durch Parameter ersetzt werden. Variablen stehen in geschweiften Klammern und beginnen mit einem $ Zeichen, z. B.

<xed:cancel url="/receive/{$id}" />

Die Variablen werden bei Aufruf des Formulars durch den konkreten Wert ersetzt, wobei als Quelle die HTTP Request Parameter aus der URL des Aufrufs, Konfigurationswerte aus mycore.properties oder Werte aus der MCRSession verwendet werden können.

<xed:cancel /> ist wiederholbar. Wenn mehrere <xed:cancel /> Elemente angegeben sind, wird das erste Element verwendet, das keine Variablen enthält, oder bei dem alle angegebenen Variablen durch konkrete Werte ersetzt werden können. So kann man z. B. abhängig von Aufrufparametern bei Abbruch zu verschiedenen Zielseiten zurückkehren.

Beispiel:

<xed:cancel url="/receive/{$id}" />
<xed:cancel url="/index.xml" />
  • Wenn das Formular mit [Seite].xed?id=DocPortal_document_07910403 aufgerufen wird, wird bei Abbruch zur Seite /receive/DocPortal_document_07910403 zurückgekehrt.
  • Wenn das Formular ohne Parameter aufgerufen wird, wird zur Seite index.xml zurückgekehrt.

Es wäre auch denkbar, die komplette URL als Parameter zu übergeben. Davon kann nur abgeraten werden, da es die Aufruf-URLs unnötig lang macht, Implementierungsdetails offenlegt und potentiell zu Sicherheitslücken führt:

<xed:cancel url="{$cancelURL}" />
Aufruf mit [Seite].xed?cancelURL=receive/DocPortal_document_07910403

Submit-Buttons definieren

Syntax:

<input type="submit|image" xed:target="..." ... /> oder <button type="submit" xed:target="..." ... />

Submit-Buttons schicken das Formular ab. Das Attribute @xed:target gibt das Ziel an und kann verschiedene Werte annehmen. Alle weiteren Eigenschaften der Buttons werden über gewöhnliche HTML Syntax gestaltet.

Vordefiniert sind die folgenden Ziele:

xed:target="cancel"Bearbeitung abbrechen
xed:target="debug"Für das Debugging, zeigt das bearbeitete XML und die Bearbeitungsschritte im Browser an
xed:target="layout"Für das Debugging, zeigt das bearbeitete XML über den MyCoRe LayoutService an (d.h. durch einen Transformer, der üblicherweise via XSL Stylesheet zu HTML rendert)
xed:target="servlet"Sendet das resultierende XML-Dokument an ein Servlet
xed:target="subselect"Externe Auswahl von Werten, siehe unter Subselects

Abbrechen-Buttons definieren

Syntax:

<input type="submit|image" xed:target="cancel" ... />
oder
<button type="submit" xed:target="cancel" ... />

Abbrechen-Buttons können auf verschiedene Weise gestaltet werden. Das Attribut @xed:target="cancel" weist die Buttons als Abbrechen-Buttons aus. Alle weiteren Eigenschaften der Buttons werden über gewöhnliche HTML Syntax gestaltet. Die URL, zu der bei Betätigen des Buttons zurückgekehrt wird; wird über das Element <xed:cancel /> definiert.

Beispiel:

<input type="submit" class="roundedButton" xed:target="cancel" value="{i18n:button.cancel}" />

In diesem Beispiel wird die sprachabhängige Beschriftung des Buttons mit value="{i18n:button.cancel}" aus dem Schlüssel button.cancel aus den messages-*.properties Dateien gelesen.

Debug-Buttons definieren

Syntax:

<input type="submit|image" xed:target="debug" ... />
oder
<button type="submit" xed:target="debug" ... />

Ein Debug-Button ist während der Implementierung eines Formulars nützlich, um anzuzeigen, welches XML-Dokument aus der Bearbeitung im Formular resultiert, ohne dieses an das Zielservlet zu senden. Das Attribut @xed:target="debug" weist die Buttons als Abbrechen-Buttons aus. Alle weiteren Eigenschaften der Buttons werden über gewöhnliche HTML Syntax gestaltet.

Beispiel:

<input type="submit" class="roundedButton" xed:target="debug" value="Debug" />

Submit-Buttons zum Absenden an ein Servlet

Syntax:

<input type="submit|image" xed:target="servlet" xed:href="..." ... />
oder
<button type="submit" xed:target="servlet" xed:href="..." ... />

Das Attribut @xed:target="servlet" definiert, dass beim Absenden des Formulars das resultierende XML via Request Dispatching an ein Ziel-Servlet gesendet werden soll. Das Attribut @xed:href gibt die URL oder den Namen dieses Servlets an, so wie er in der web.xml der Anwendung definiert ist. Alle weiteren Eigenschaften der Buttons werden über gewöhnliche HTML Syntax gestaltet.

Beispiel:

<input type="submit" class="roundedButton" xed:target="servlet" xed:href="MCRSearchServlet" value="{i18n:button.search}" />

In diesem Beispiel wird die sprachabhängige Beschriftung des Buttons mit value="{i18n:button.search}" aus dem Schlüssel button.search aus den messages-*.properties Dateien gelesen. Der Button schickt das resultierende XML-Dokument, hier angenommen die XML-Darstellung einer Suchanfrage, an das MCRSearchServlet, um eine Suche auszuführen.

Das Ziel-Servlet erhält das resultierende XML-Dokument als JDOM Document in einem Request Attribut mit dem Schlüssel MCRXEditorSubmission übergeben:

HttpServletRequst request;
org.jdom.Document xml = (org.jdom.Document) (request.getAttribute("MCRXEditorSubmission"));

Sofern Validierungsregeln angegeben sind, wird das Zielservlet nur aufgerufen, wenn die Validierung erfolgreich ist. Wenn sie nicht erfolgreich ist, wird zum Formular zurückgekehrt und auf die fehlerhafte Eingabe hingewiesen.

Eingabevalidierung und Nachbearbeitung

Eingabevalidierung

Die Eingabevalidierung erfolgt im XEditor-Framework serverseitig nach Absenden des Formulars. Davon unberührt bleibt die Möglichkeit, mit eigenen Mitteln (HTML5, JavaScript) zusätzlich eine clientseitige Validierung im Browser zu implementieren.

Die Validierungsregeln beziehen sich auf die Knoten des bearbeiteten XML-Dokumentes, d.h. auf die Werte von XML-Elementen und Attributen im resultierenden XML, nicht auf einzelne Eingabefelder. Dies erleichtert es besonders, Validierungsregeln an unterschiedlichen Stellen oder in verschiedenen Formularen wiederzuverwenden.

Wenn das Formular abgeschickt wird, werden alle gesetzten Validierungsregeln serverseitig geprüft. Schlägt die Validierung fehl, wird das Formular erneut angezeigt. Es besteht die Möglichkeit, an beliebiger Stelle Meldungen auszugeben, um anzuzeigen, welche Regeln nicht erfüllt sind.

Validierungsregeln mit <xed:validate>

Setzt eine Validierungsregel. Die Validierungsmethode wird durch ein oder mehrere kombinierbare Attribute spezifiziert.

Auf welchen XML-Knoten sich die Validierungsregel bezieht, kann auf zwei Weisen festgelegt werden:

Lokale Validierungsregeln stehen innerhalb eines <xed:bind /> Elementes und beziehen sich auf das dadurch gebundene XML. Beispiel:

<xed:bind xpath="mods:dateIssued">
  <input type="text" ... />
  <xed:validate required="true" />
</xed:bind> 

oder auch unabhängig von der Position des konkreten Eingabefeldes

<xed:bind xpath="mods:dateIssued">
  <xed:validate required="true" />
</xed:bind>
<xed:bind xpath="mods:dateIssued">
  <input type="text" ... />
</xed:bind> 

Globale Validierungsregeln stehen an beliebiger Stelle im Formular und beziehen sich auf die durch das Attribut @xpath ausgewählten XML-Knoten. Beispiel:

<xed:validate xpath="//mods:identifier[@type='issn']" matches="[\dX]{4}\-[\dX]{4}" />

validiert die Syntax aller auftretenden mods:identifier[@type='issn'] Elemente. Globale Validierungsregeln erleichtern die Wiederverwendung und können bevorzugt für Syntaxprüfungen verwendet werden.

Ist das Attribut @required="true" gesetzt, ist das gewählte Element ein Pflichfeld, d.h. es muss mindestens ein nicht leerer Wert (bei wiederholten Elementen) auftreten.

Das Attribut @matches gibt einen regulären Ausdruck in der Syntax von java.util.regex.Pattern an, gegen den der Text des XML-Elementes oder Attributes validiert wird.

Beispiel:

<xed:validate xpath="//mods:identifier[@type='issn']" matches="[\dX]{4}\-[\dX]{4}" />

Das Attribut @test gibt einen XPath Test an, gegen den ein XML-Knoten validiert wird. Beispiel:

<xed:validate xpath="/document/validTo" test="(string-length(.) = 0) or (number(.) >= number(../validFrom))" />

prüft, ob /document/validTo >= /document/validFrom ist.

<xed:validate xpath="/user" test="password = passwordRepeated" />

prüft, ob das eingegebene Passwort mit dessen Wiederholung identisch ist.

Die Attribute @maxLength, @minLenght, @min, @max @type, @class, @method und @format validieren auf die gleiche Weise wie im alten Editor Framework (siehe dort).

<xed:validate xpath="document/yearIssued" min="2000" type="integer" />

stellt sicher, dass es sich beim Erscheinungsjahr um eine ganze Zahl >= 2000 handelt.

<xed:validate xpath="//mods:*[@encoding='w3cdtf']" matches="\d{4}(\-d{2}(\-d{2})?)?" type="datetime" format="yyyy;yyyy-MM;yyyy-MM-dd" />

validiert alle Datumsangaben, so dass sie entweder nur das Jahr, Jahr plus Monat oder Jahr, Monat und Tag in MODS W3CDTF Syntax enthalten.

Validierungsmeldungen ausgeben

Wenn die Eingabevalidierung fehlschlägt, wird die Variable $xed-validation-failed='true' gesetzt. Beispiel:

<xed:if test="$xed-validation-failed">
 Bitte korrigieren Sie die fehlerhaften Eingaben!
</xed:if>

Das Attribut xed:validate/@display definiert, an welcher Stelle die Validierungsnachricht ausgegeben werden soll, falls die Validierungsregel nicht erfüllt ist.

@display="here" Ausgabe der Meldung an genau dieser Stelle, d.h. dort, wo die Validierungsregel im Formular definiert ist.

@display="local" Ausgabe der Meldung an einer lokalen Stelle in der Nähe der Eingabekomponente. Die Meldung wird dort ausgegeben, wo das umgebende <xed:bind /> den nicht validen XML-Knoten selektiert, und wo darin enthalten das Element <xed:display-validation-message /> angegeben ist. Beispiel:

<xed:bind xpath="title">
  <xed:display-validation-message />
  <input type="text" />
  <xed:validate display="local" required="true">Bitte geben Sie einen Titel ein!</xed:validate>
</xed:bind>

@display="global" Ausgabe der Meldung an globaler Stelle, gesammelt mit anderen Meldungen. Die Meldung wird dort ausgegeben, wo das Element <xed:display-validation-message /> auftritt. Beispiel:

<xed:if test="$xed-validation-failed">
  Bitte korrigieren Sie die folgenden fehlerhaften Eingaben:
  <xed:display-validation-messages />
 </xed:if>
...
<xed:bind xpath="title">
  <input type="text" />
  <xed:validate display="global" required="true">Bitte geben Sie einen Titel ein!</xed:validate>
</xed:bind>

Es ist auch möglich, die Fehlermeldung an mehreren Stellen auszugeben, indem man mehrere Werte im Attribut @display durch Leerzeichen getrennt angibt.

Die Fehlermeldung wird entweder durch das von <xed:validate /> umschlossene HTML gebildet, oder über einen i18n-Schlüssel mehrsprachig aus den messages-*.properties Dateien gelesen. Beispiel:

<xed:validate ... required="true" i18n="editor.title.required" />
<xed:validate ... required="true">
  Dieses Feld ist ein <strong>Pflichtfeld</strong>!
</xed:validate>

HTML-Formatierung und Layout der Fehlermeldung werden durch XSL-Templates in xeditor-custom.xsl definiert und können durch lokales Überschreiben in der eigenen Webanwendung beliebig angepasst werden. Beispiel:

<xsl:template match="xed:validate" mode="message">
  <span class="help-inline">
    <xsl:apply-templates select="node()" mode="xeditor" />
  </span>
</xsl:template>

umgibt alle ausgegebenen Fehlermeldungen mit einem <span class="help-inline" />.

Eingabekomponenten mit fehlerhaften Eingaben können ausserdem markiert werden, z. B. mit eigenen CSS-Klassen oder Stilen. Dazu wird für jeden validierten XML-Knoten die Variable $xed-validation-marker gesetzt. Der Wert, der in dieser Variable enthalten ist, ist über die mycore.properties konfigurierbar und wird typischerweise ein oder mehrere CSS-Klassen enthalten:

MCR.XEditor.Validation.Marker.error=has-error
MCR.XEditor.Validation.Marker.success=has-success
MCR.XEditor.Validation.Marker.default=

Wenn die Validierungsregel verletzt ist, wird die Variable $xed-validation-marker auf den Wert von MCR.XEditor.Validation.Marker.error gesetzt. Der Wert von MCR.XEditor.Validation.Marker.success wird gesetzt, wenn die Validierungsregel erfüllt ist, d.h. es wurde validiert und der XML-Knoten ist valide. Der Wert von MCR.XEditor.Validation.Marker.default wird gesetzt, wenn die Eingabekomponente nicht validiert wurde (z. B. weil keine Regel angegeben ist).

Die Werte können auch leer sein, um z. B. nur fehlerhafte Komponenten zu markieren.

Beispiel:

<xed:bind xpath="year">
  <div class="form-group {$xed-validation-marker}">
    <input type="text" />
    <xed:validate display="here" required="true">Bitte Jahr eingeben!</xed:validate>
  </div>
</xed:bind>

Wenn das Element <year /> nicht eingegeben wird, bekommt das umgebende <div /> Element die CSS-Klassen @class="form-group has-error".

Wenn das Jahr eingegeben und validiert wurde, aber das Formular noch einmal angezeigt wird, z. B. weil andere Komponenten nicht valide sind, bekommt das umgebende <div /> Element im Beispiel die CSS-Klassen @class="form-group has-success".

Die Anwendung beschränkt sich nicht auf CSS-Klassen oder Stile. Je nach Definition in mycore.properties könnte z. B. auch ein placeholder gesetzt oder anderes HTML generiert werden. Beispiel:

<xed:bind xpath="year">
  <xed:if test="$xed-validation-marker = 'has-error'">
    Vielleicht sollten Sie mal die <a href="anleitung.html">Anleitung</a> lesen.
  </xed:if>
  ...
</xed:bind>

Resultierendes XML bereinigen: Cleanup-Rules

Syntax:

<xed:cleanup-rule xpath="..." relevant-if="..." />

Cleanup-Rules sind Bereinigunsregeln, die logisch leere XML-Elemente oder -Attribute vor Übergabe an das Zielservlet aus dem resultierenden XML herauslöschen. Das Attribut @xpath gibt in Form eines XPath-Ausdruckes an, auf welche Elemente oder Attribute sich die Regel bezieht. Das Attribut @relevant-if gibt in Form eines XPath-Tests an, unter welchen Bedingungen diese XML-Knoten als relevant erachtet werden.

Es gelten die folgenden Standard-Regeln:

<xed:cleanup-rule xpath="//@*" relevant-if="string-length(.) &gt; 0" />
<xed:cleanup-rule xpath="//*" relevant-if="@* or * or (string-length(text()) &gt; 0)" />
<xed:cleanup-rule xpath="/mycoreobject/structure|/mycoreobject/service" relevant-if="true()" />

Die Standard-Regeln entfernen leere Attribute und Elemente aus dem resultierenden XML-Dokument. Ein Element ist leer, wenn es keine Kindelemente, keine Attribute und keinen Text enthält. Ausnahmen bilden die <structure /> und <service /> Elemente eines MyCoRe-Objektes, die immer -auch leer- erhalten bleiben, um den Vorgaben des XML-Schemas zu entsprechen. Diese Regeln lassen sich durch eigene Regeln ergänzen oder auch ganz oder teilweise überschreiben. Die Regeln werden in der Reihenfolge der Deklaration angewandt, d.h. die zuletzt angegebene Regel "gewinnt"".

Beispiel (Suchmaske):

<xed:cleanup-rule xpath="//condition" relevant-if="string-length(@value) &gt; 0" />

entfernt alle <condition /> Elemente, deren @value Attribut leer ist. In einer MyCoRe Suchmaske werden die Suchfelder als <condition /> Elemente dargestellt, mit den Attributen @field für das zu durchsuchende Feld, @operator für den zu verwendenden Suchoperator, und @value für den eingegebenen und zu suchenden Wert, z. B.

<condition field="title" operator="contains" value="Optik" />

Die Attribute @field und @operator werden in der Suchmaske durch hidden-Felder oder Auswahllisten festgelegt. Wenn aber kein zu suchender Wert eingegeben wird, ist die gesamte Suchbedingung nicht relevant, und sie wird durch die Bereinigungsregel entfernt.

Beispiel (MODS):

<xed:cleanup-rule xpath="//mods:*" relevant-if="(string-length(text()) &gt; 0) or mods:* or (string-length(@xlink:href) &gt; 0)" />
<xed:cleanup-rule xpath="//mods:name" relevant-if="mods:namePart" />

Im MODS Datenmodell werden Personennamen z. B. wie folgt dargestellt:

<mods:name type="personal">
  <mods:namePart type="given">John</namePart>
  <mods:namePart type="family">Huston</namePart>
  <mods:role>
    <mods:roleTerm type="code" authority="marcrelator">aut</roleTerm>
  </mods:role>
</mods:name>

Angenommen, ein Eingabeformular besitzt für an einer Publikation beteiligte Personen Eingabefelder für Vor- und Nachname (mods:namePart), sowohl eine Auswahlliste für die Rolle (Autor, Herausgeber etc. entsprechend mods:roleTerm). Dann entfernt die zweite Bereinigungsregel das mods:name Element, wenn keine mods:namePart Elemente vorhanden sind, d.h. keine Namensteile eingegeben wurden. Die erste Bereinigungsregel entfernt alle mods Elemente, die keine anderen mods Elemente enthalten, oder kein xlink:href Attribut. Somit wird das mods:namePart Element entfernt, wenn kein Text enthalten ist. Das Attribut @type ist nicht relevant, da die Standard-Regel für mods Elemente überschrieben wurde.

Bereinigungsregeln werden rekursiv und iterativ auf das resultierende XML-Dokumente angewandt, bis sich das Dokument nicht mehr ändert, d.h. aufgrund der Regeln keine weiteren Knoten als zu entfernen identifiziert werden.

Nachbearbeitung über XSL: <xed:post-processor />

Syntax:

<xed:post-processor xsl="..." />

Gibt mit dem Attribut @xsl ein Stylesheet an, das zur Nachbearbeitung des resultierenden XML-Dokumentes verwendet werden soll. Über ein solches XSL-Stylesheet kann das XML-Dokument nach Bearbeitung im Formular, aber noch vor Übergabe an das Zielservlet transformiert werden.

Über die XEditor-Syntax können Eingabefelder flexibel auf XML-Elemente und Attribute abgebildet werden. In einigen Fällen kann die XML-Struktur aber zu komplex sein, um sie einfach auf Eingabefelder abzubilden. In solchen Fällen kann man das zu bearbeitende XML-Dokument

  • vor der Bearbeitung im Formular durch ein XSL-Stylesheet in eine für die Bearbeitung geeignetere Form umwandeln: Dies ist einfach über Verwendung des xslStyle URI Resolvers möglich, z. B. <xed:source uri="xslStyle:mods-preprocessor:mcrobject:{$id}" />
  • nach der Bearbeitung im Formular wieder durch ein XSL-Stylesheet in die gewünschte XML-Struktur zurück transformieren, z. B. <xed:post-processor xsl="mods-postprocessor.xsl" />

Beispiel:

Im MODS Datenmodell wird eine Publikation, die auf einer einzelnen Seite veröffentlicht wird, dargestellt als

<mods:detail type="page">
  <mods:number> Seitenzahl </mods:number>
</mods:detail>

Wenn sich die Publikation über mehrere Seiten erstreckt, wird ein von-bis Bereich angegeben:

<mods:extent unit="pages">
  <mods:start> Seite von </mods:start>
  <mods:end> Seite bis </mods:end>
</mods:extent>

Angenommen, das Eingabeformular soll grundsätzlich nur die Eingabefelder "Seite von" und "Seite bis" anbieten, dann könnte man den Sonderfall einer einzelnen Seite abbilden, indem

  • in einem Stylesheet mods-preprocessor.xsl alle mods:detail[@type='page']/mods:number zu mods:extent mit identischen mods:start und mods:end Werten transformiert werden
  • in einem Stylesheet mods-postprocessor.xsl alle mods:extent mit identischen oder einzelnen mods:start und mods:end Werten zu einem einzelnen mods:detail[@type='page']/mods:number transformiert werden

Komplexe Strukturen mit Includes und Fallunterscheidungen

<xed:include>

Fügt an der verwendeten Stelle Komponenten in das Formular ein, indem sie über eine ID referenziert werden, und/oder von einer externen URI nachgeladen werden.

Syntax:

<xed:include uri="..." ref="..." static="true|false" />

Wird das Attribut @uri angegeben, wird ein XML-Element von der angegebenen URI geladen, und der Inhalt (nicht das Wurzelelement selbst) an dieser Stelle eingefügt. @uri kann jede URI sein, die der MyCoRe URI Resolver unterstützt (z. B. classification:, resource:, webapp:, file:, http:, xslStyle:).

Wird das Attribut @ref angegeben, wird der Inhalt desjenigen XML-Elements eingefügt, dessen ID (@id) dem Wert von @ref entspricht.

Die Attribute @uri und @ref sind kombinierbar. Sie können XPath-Ausdrücke oder Verweise auf Variablen enthalten. D.h. man kann über <xed:include /> Teile des Formulars dynamisch nachladen, die abhängig vom bearbeiteten XML und/oder von Request Parametern bei Aufruf des Formulars sind.

Beispiel: Nachladen von Klassifikationskategorien als Auswahl in einer Select-Box:

<select>
  <option value="">Bitte wählen...</option>
  <xed:include uri="xslStyle:items2options:classification:editor:-1:children:modsgenre" />
</select>

Die URI "xslStyle:items2options:classification:editor:-1:children:modsgenre" lädt die Klassifikation "modsgenre" und transformiert alle Kategorien in <option /> Auswahloptionen in der HTML-Syntax.

Beispiel: Nachladen von Teilen eines Formulars abhängig von Request Parametern:

<xed:include uri="webapp:ubo/editor/editor-genres.xed" ref="{$genre}" /> 

Wird das Formular z. B. mit [Seite].xed?genre=article aufgerufen, wird an dieser Stelle der Inhalt des Elementes mit @id='article' eingefügt.

Das inkludierte XML sollte von einem Element <xed:template /> umschlossen sein. Der XEditor ignoriert solche Bereiche, wenn sie nicht durch <xed:include /> referenziert werden.

Beispiel: Suchmaske, Auswahl Erscheinungsjahr

<label>Suche nach Erscheinungsjahr:</label>
zwischen
<pre class="brush: xml"><![CDATA[<xed:bind xpath="condition[@field='year'][1]">
  <xed:bind xpath="@operator" default="&lt;=" />
  <xed:include ref="year.input" />
</xed:bind>

und

<xed:bind xpath="condition[@field='year'][2]">
  <xed:bind xpath="@operator" default="&gt;=" />
  <xed:include ref="year.input" />
</xed:bind>
<xed:template id="year.input">
  <xed:bind xpath="@operator">
    <select>
      <option>=</option>
      <option>&lt;</option>
      <option>&lt;=</option>
      <option>&gt;</option>
      <option>&gt;=</option>
    </select>
  </xed:bind>
  <xed:bind xpath="@value">
    <input type="text" placeholder="{i18n:ubo.placeholder.year}" />
  </xed:bind>
</xed:template>

Hier wird eine Vorlage für die Auswahl des Suchoperators (<, >, = etc.) und die Eingabe der Jahreszahl an zwei Stellen wiederverwendet.

Häufig verändern sich über eine URI inkludierte Vorlagen über die gesamte Laufzeit der Anwendungen nicht. Solche statischen Vorlagen können mit

<xed:include static="true" ... />

markiert werden. Der XEditor wird die externe Ressource (z. B. eine statische XML-Datei aus der Webapplikation) nur einmal einlesen, in einem Cache aufbewahren und wiederverwenden.

Für Ressourcen, die sich über die Laufzeit der Applikation ändern, z. B. Klassifikationen, bei denen nachträglich Kategorien online ergänzt werden, sollte @static nicht gesetzt sein.

<xed:if>

Syntax:

<xed:if test="...">...</xed:if>

Ermöglicht es, einen Teil des Formulars nur unter einer bestimmten Bedingung auszuführen, die durch einen XPath-Test bestimmt wird. Der XPath-Test kann die XML-Struktur des bearbeiteten Dokumentes, sowie Variablen verwenden, die aus HTTP Request Parametern, Konfigurationsparametern aus mycore.properties oder MCRSession Variablen stammen. Nur wenn die Testbedingung erfüllt ist, wird der durch <xed:if /> umschlossene Formularabschnitt verarbeitet und angezeigt.

Beispiele:

<xed:if test="string-length(@valueURI) &lt; 0">
  <a href="{@valueURI}">
        <xed:output value="concat(mods:namePart[@type='family'],', ',mods:namePart[@type='given'])" />
  </a>
</xed:if>
<xed:if test="$mode='advanced'">
  <h3>Erweiterter Modus mit zusätzlichen Eingabefeldern:</h3>
  ...
</xed:if>

<xed:choose>

Syntax:

<xed:choose>
  <xed:when test="...">...</xed:when>
  <xed:when test="...">...</xed:when>
  <xed:otherwise>...</xed:otherwise>
</xed:choose>

Ermöglicht es, abhängig von ein oder mehreren Bedingungen unterschiedliche Teile oder Varianten eines Formulars auszuführen.

Jedes <xed:when /> Element definiert einen XPath-Test. Der XPath-Test kann die XML-Struktur des bearbeiteten Dokumentes, sowie Variablen verwenden, die aus HTTP Request Parametern, Konfigurationsparametern aus mycore.properties oder MCRSession Variablen stammen. Nur wenn die Testbedingung erfüllt ist, wird der durch <xed:when /> umschlossene Formularabschnitt verarbeitet und angezeigt. Wenn mehrere <xed:when /> Elemente angegeben sind, wird nur das erste Element berücksichtigt, dessen Testbedingung erfüllt ist.

Das Element <xed:otherwise /> ist optional. Es wird nur dann ausgeführt, wenn keine der Testbedingungen der vorangehenden <xed:when /> Elemente erfüllt ist. In diesem Fall wird der duch <xed:otherwise /> umschlossene Formularabschnitt verarbeitet und angezeigt.

Beispiel:

<xed:choose>
  <xed:when test="@status='new'">
        <label>Veröffentlichen am:</label>
        <xed:bind xpath="dateToPublish">
          <input type="text />
        </xed:bind>
  </xed:when>
  <xed::otherwise> <!- status='published' ->
        <label>Veröffentlichungsdatum: </label>
        <xed:output value="dateToPublished" />
  </xed:otherwise>
</xed:choose>

Java-Aufrufe in XPath-Ausdrücken

Über die Funktion xed:call-java() können statische, externe Java-Methoden aufgerufen werden. Als Parameter können dabei auch Variabeln aus Request Parametern oder Knoten aus dem gerade bearbeiteten XML-Dokument übergeben werden.

Syntax:

xed:call-java('Klasse','Methode'[,Parameter...])

Ein solcher Java-Aufruf kann z. B. verwendet werden, um Teile eines Formulars abhängig von der Rolle des aktuell angemeldeten Benutzers anzuzeigen oder zu verbergen. Administratoren könnten z. B. andere oder komplexere Such- und Eingabefelder verwenden als andere Benutzer. Beispiel:

<xed:if test="xed:call-java('org.mycore.common.xml.MCRXMLFunctions','isCurrentUserInRole','admin')">
  Dieser Teil des Formulars ist nur für Administratoren sichtbar!
</xed:if>

Hier ein Beispiel für einen Aufruf mit Parametern und der dazugehörigen Implementierung in Java. XPath-Ausdrücke geben in der Regel einen Menge von Elementen zurück, auch wenn der konkrete Ausdruck nur ein einzelnes Element selektiert. Der XEditor versucht analog zum Überladen von Methoden die am besten passende Methode zu selektieren.

<xed:output xpath="xed:call-java('Checker','isValid',mods:identifier[@type='issn'])" />
public class Checker {
  /* Dummy */
  public static String isValid( List<Element> nodes ) {
    return "The ID you gave me is valid. Have a nice day.";
  }
}

Erwartet die aufgerufene Java-Methode einen Übergabeparameter vom Typ String, muss die xpath-Angabe entsprechend als String string(/mycoreobject/@ID)übergeben werden. Nachfolgend ein Beispiel bei dem außerdem ein Property aus der Datei mycore.property geprüft wird.

<xed:when test="$MIR.registerDOI='true' and xed:call-java('org.mycore.pi.frontend.MCRIdentifierXSLUtils','hasIdentifierRegistered','Datacite',string(/mycoreobject/@ID),'')">

Externe Auswahl oder Eingabe über Subselects

Subselects dienen der externen Auswahl ein oder mehrerer Werte, z. B. Suche einer Person im Personalverzeichnis der Einrichtung, und Übername von Name und Personalnummer in das Autorenfeld einer Publikation.

Subselects werden als spezielle Submit-Buttons im Formular definiert. Bei Klick auf den Button werden alle aktuellen Formularwerte serverseitig zwischengespeichert. Dann wird zu einer beliebigen externen URL umgeleitet. Diese URL kann durch ein eigenes Servlet implementiert sein, das die Auswahl oder Eingabe von Werten geeignet steuert. Die resultierenden Werte werden nach Abschluss der Auswahl als Request an das XEditor-Servlet zurückübermittelt. Dieses baut die Werte in das bearbeitete XML-Dokument ein und stellt das Formular erneut dar.

Syntax:

<input type="submit|image" xed:target="subselect" xed:href="..." ... />
oder
<button type="submit" xed:target="subselect" xed:href="..." ... />

Das Attribut @xed:href gibt die URL an, die aufgerufen werden soll. Diese kann auch XPath-Ausdrücke enthalten, die auf das bearbeitete XML-Dokument zugreifen, oder Variablen referenzieren, die aus Aufruf- oder Konfigurationsparametern stammen. Beispiel:

<xed:bind xpath="mods:name[@type='personal']">
  <input type="submit" xed:target="subselect" xed:href="../ubo/lsfSearch.html?lastName={mods:namePart[@type='family']}" value="{i18n:button.selectPerson}" />
  <xed:output value="concat(mods:namePart[@type='family'],', ',mods:namePart[@type='given'])" />
</xed:bind>

Das Beispiel veranschaulicht die externe Auswahl einer Person zur Übernahme in ein mods:name Element. Das <xed:output /> Element gibt, sofern bereits vorhanden oder schon zuvor ausgewählt, Vor- und Nachname der Person im Formular aus.

Bei Klick auf den Button wird das Formular abgeschickt, die Werte zwischengespeichert und die URL ../ubo/lsfSearch.html aufgerufen. Dabei wird auch ein Request Parameter übermittelt, der den aktuell im XML gesetzten Nachnamen überträgt: ?lastName={mods:namePart[@type='family']}

Die externe Seite könnte den übermittelten Namen suchen und/oder ein Suchfeld bereitstellen. Neben dem angegebenen Parameter lastName bekommt die Seite einen weiteren Parameter _xed_subselect_session übermittelt, der als Rückreferenz dient und die Session ID des Formulars enthält, z. B.

../ubo/lsfSearch.html?lastName=Doe&amp;_xed_subselect_session=123-456

Nach erfolgter Auswahl muss die externe Seite diesen Parameter zusammen mit den gewünschten Werten als Request an das XEditorServlet rückübermitteln, z. B. durch Aufruf von

servlets/XEditor? _xed_submit_return= &amp; _xed_session=123-456 &amp; mods:namePart[@type='family']=Doe &amp; mods:namePart[@type='given']=John

Zur besseren Lesbarkeit sind die Parameter in obiger URL hier nicht UTF-8 encoded dargestellt. Eine reale Anwendung müsste sowohl Parameter Namen als auch die Werte UTF-8 kodiert übermitteln.

Alle Parameter ausser _xed_submit_return und _xed_session (mit dem Wert von _xed_subselect_session) werden als relative XPath-Ausdrücke interpretiert. Im Beispiel sind diese relativ zu der Stelle im Formular, an der der Subselect Button auftritt, und zu dem dort via <xed:bind /> selektierten XML-Element. In diesem Fall ist das ein mods:name[@type='personal'] Element, in dem Werte eingefügt werden. Die Parameternamen verhalten sich damit so, als ob sie XPath-Ausdrücke in einem <xed:bind /> Element unterhalb von <mods:name /></xed:bind> wären.

Das XEditor-Servlet baut die übermittelten Werte in das bearbeitete XML-Dokument ein

<mods:name type="personal">
  <mods:namePart type="family">Doe</mods:namePart>
  <mods:namePart type="given">John</mods:namePart>
</mods:name>

und stellt das Formular per Redirect im Anschluss erneut dar. Das <xed:output /> Element würde im Beispiel dann an dieser Stelle neben dem Subselect-Button den Wert "Doe, John" ausgeben.

Das XEditor-Framework erweitern

  • TODO: Wie man eine eigene Template-Sprache auf Basis von XSL definiert, vgl. MIR Modul in MyCoRe oder UBO Modul in miless
  • TODO: Wie man eigene Targets für Submit Buttons definiert

 Frank Lützenkirchen, Kathleen Neumann - 2016-05-19