MyTISM ist plattformunabhängiges, objektorientiertes, dezentrales, multiuserähiges, individuell anpassbares und quelloffenes 3-Tier-Datenbank- und Anwendungs-Framework incl. GUI und Web-Application-Server, entwickelt und betreut von OAshi s.a r.l.

In diesem Handbuch finden Sie alle Informationen, die Sie für die Bedienung eines MyTISM-Systems benötigen.

Note
Beachten Sie bitte, dass sich dieses Dokument noch im Aufbaustadium befindet und noch grosse Lücken aufweist, die wir natürlich nach und nach füllen werden.

Bei Fragen, Problemen oder Anregungen, sei es bzgl. MyTISM selber oder dieser Dokumentation, wenden Sie sich bitte an uns; Kontaktinfos finden Sie im WWW unter http://www.mytism.de/mytism/contact.

Vorstellung von MyTISM

Warum?

Eine gute Frage. Warum tut man sich heutzutage noch den Aufwand an, ein Datenbank-Framework und all das Drumherum bis zum Applikationsserver von Grund auf neu zu entwickeln, wenn es die entsprechenden Werkzeuge am Markt doch in Hülle und Fülle bereits gibt? Oder ist es nur der Wunsch eines jeden Programmierers, "sein" Framework zu bauen und zu verwenden?

Die Wahrheit ist: Als ich mir auf der Suche nach den passenden Puzzleteilen die verschiedenen Frameworks angeschaut habe (wohlgemerkt Stand Mitte 2001) mußte ich feststellen, daß es zwar immer wieder gute Teile gab, aber nichts, was zusammengepaßt hätte. Osage, XwingML, verschiedene Wrapper für native GUIs, RAD-Tools zum Erstellen von Swing-Code etc; ganz zu schweigen von Standards wie J2EE mit ihren CMP, BMP und weiß Gott was sonst noch so alles. Aber es paßte einfach nicht wirklich ins Bild. Immer, wenn ich einen Prototypen zusammensetzte, blieb ich entweder an einer Lizenz-Ecke hängen oder es hakte einfach technisch an der Möglichkeit der Umsetzung.

Noch lange nicht am Ende dieses Weges angelangt, kann ich nach inzwischen drei Jahren Entwicklung (diese Zeilen entstanden im September 2004) sagen, daß es, und das ist das schönste Lob von allen, immer wieder Programmierer gibt, die einen Blick auf das entstandene Werk werfen und feststellen, daß es sich substantiell von allem unterscheidet, was sie bislang gesehen haben. Das kann natürlich auch an der Unwissenheit oder Unerfahrenheit dieser Programmierer liegen, aber trotzdem interpretiere ich es als ein großes Kompliment für unsere Firma und auch persönlich für mich.

Historie

Den Anfang nahm alles im August 2000 (das Wochenende vom 26-27, um genau zu sein), als ich anfing, ein neues Projekt für einen Industriekunden zu durchdenken, und mir, nachdem ich schon monatelang um OODBMSe mit einem befreundeten Programmierer diskutiert hatte, der Paradigmenbruch eines Java-Programmierers, der Datenbankanwendungen schreibt, wirklich bewußt wurde.

Irgendwie ist es fast wie Autofahren: Am Anfang freut man sich, daß man das Vehikel wenigstens schadfrei über die Straßen bewegen kann und freut sich, heil anzukommen. Doch je mehr Routine man erlangt, umso mehr fallen Unzulänglichkeiten wie schlechtes Fahrwerk, Sitze, leistungsschwache Motoren oder auch einfach das unbehende und unvorausschauende Treiben anderer Verkehrsteilnehmer auf - nicht, daß sie vorher nicht dagewesen sind, man war möglicherweise sogar ein Teil davon. Aber mit der Routine kommt auch der Drang, besser zu werden, die Prozesse zu optimieren, schneller voran zu kommen, die Resourcen sinnvoller zu nutzen.

So ist man am Anfang froh, überhaupt aus einer DBMS irgendwie Daten zu bekommen, bestaunte jede abgesetzte Query und war froh, die Daten an Ort und Stelle irgendwie laden und wieder zurückspeichern zu können. Auch war es anfangs keine Mühe, sondern eher fast magisch, an dem Schema der SQL-Datenbank herumzuschrauben und Trigger zu setzen, kleine SQL-Scripts zu schreiben etc. Alles war ein großer Spaß und ich war stolz, eine SQL-Datenbank "zu beherrschen". Wie naiv man doch manchmal ist…​

Aber mit der Routine kam auch die Redundanz. Schon wieder eine Tabelle ändern, die Query erweitern um eine Spalte, ein bißchen Javacode anpassen, in der GUI das Feld dazubauen, den Serializer erweitern und so weiter. Alles Jobs, um die man sich als Programmierer nicht gerade reißt. Zumindest ich reiße mich nicht darum. Dazu kam, daß man ständig das Verhalten der Anwendung teilweise in der Datenbank, teilweise in der Persistenz und zu guter Letzt auch noch in der GUI bestimmte. Wo wurde nochmal diese zehnstellige Zahl abgefragt? Welche Klasse prüft die Artikelnummer? Die Arbeit mit Datenbankanwendungen wurde schnell zu einer leidigen Pflichtübung und hatte mit Design nur noch wenig zu tun. Ich drückte mich um jedes geänderte Feld, um jede veränderte Definition, weil sie sich mehr oder weniger durch den gesamten Code zog und nach jedem weiteren Programmteil noch aufwändiger wurde. Mal abgesehen davon, daß der Wechsel der SQL-Servers ein reiner Albtraum gewesen wäre, aber dazu kam es Gott sei Dank nie.

Die erste Verbesserung kam dann in jenem August 2000, als ich konkret daran ging, die Anwendung dieses Kunden zu entwerfen und nach einigen Tests mit Osage und anderen ORM’s, deren Namen ich heute nicht mehr weiß, einen Test mit der Castor-API machte. Dieser Test wurde dann schnell zum Prototypen und bald hatte ich ein Problem weniger: Der Persistenz-Code war jetzt an einer Stelle zu finden und viele Aspekte der Business-Logik waren nun in Objekten, die persistiert wurden, gekapselt. Außerdem konnte man mit einem Subset vom OQL Abfragen aus objektorientierter Sicht erstellen, die die API in SQL-Queries abhängig vom verwendeten SQL-Server verwandelte. Sie schirmte einen sozusagen vom SQL-Code völlig ab - eine völlig faszinierende Sache aus Sicht des Programmierers.

Leider war der Castor noch nicht fertig (er ist es heute noch nicht), und seine Unzulänglichkeiten zwangen mich zeitweise wochenlang in die Fehlersuche und haben mich wahrscheinlich um Jahre altern lassen. Aber er ließ Dinge geschehen, die für mich magisch waren und deren Mechanismen ich im Detail erst viel später verstand. Das einzige, an dem ich immer noch basteln mußte, waren die Mapping-Files, die eigentlichen BO-Klassen und die Datenbank an sich. Wenn diese drei ordentlich synchron gepflegt wurden, lief alles reibungslos. Wenn nicht, dann…​

Dieses erste Projekt mit einem ORM war ein voller Erfolg; es lief vom ersten Tag fast störungsfrei und bewältigte eine Aufgabe, die recht komplex viele Datenströme zur rechten Zeit zum richtigen Ort schaffen mußte und als Schalt- und Regelzentrale eine anspruchsvolle und verantwortungsvolle Stellung in der Firma hatte. Einige der Probleme, die vorher bestanden und die durch eine vorherige Lösung bereits längst erledigt sein sollten, wurden nun endlich erledigt. Einige Folgeaufträge stand das nächste neue Projekt an, ein Vertriebssystem.

Ein altes chinesisches Sprichwort sagt: Für einen Mann mit einem Hammer sieht alles wie ein Nagel aus. Wie wahr. Da hatten wir nun dieses Projekt mit dem Castor ORM und einen Kunden, dessen zugegeben etwas hoch gesteckte Ziel so nicht kauffertig zu erwerben war. Was lag näher, als auch ihm eine individuelle Lösung zu bauen - 60% davon sind ja schon fertig. Nun, zumindest schien das so, und nach einer kurzen Preisverhandlung sollte 3-4 Monate später das Programm fertig sein. Das war im März 2001. Im August 2001 hatte ich eine eher wackelige Fassade dessen, was der Kunde eigentlich wollte (wobei die Frage im Raum steht, welcher Kunde schon am Anfang eines solchen Projekts weiß, was er wirklich will). Synchronisation war noch in weiter Ferne, das Transportprotokoll RMI und die Geschwindigkeit insgesamt lausig. Aber mit wenigen Testdaten blieb alles im RAM und so fiel es nicht weiter auf, daß das eigentliche Produkt, so wie ich es im Kopf hatte, noch viel Arbeit kosten würde.

So wurde zunächst die Formularengine grob zusammengestoppelt und die damals recht junge JNLP-Spezifikation zur Installation der Clients fertiggestellt. Eine erste Version des Schema-Generators erlaubte im Compile-Zyklus eine Anpassung der Datenbank, generierte Sourcecode und erstellte statische Mapping-Files für den späteren Server-Start. Das Projektverzeichnis war ein einziger Wust von kleinen Dateien, in denen kleine Informationsbröckchen lagen, die Auskunft über die Konfiguration einzelner Teile der Software gab. Ein Apache auf der gleichen Maschine lieferte eine kleine Website mit den JNLP-Deskriptoren samt jars aus. Somit war ein zumindest installationsfähiger Client samt Server zusammengestellt, der die anfänglichen Anforderungen des Kunden, der langsam aber sicher auch etwas ungeduldig wurde, bediente (November 2001).

Zur gleichen Zeit hatte ich einen Kunden, der ein altes PHP-Framework von uns um Mehrsprachigkeit und Skins erweitern wollte. Mir war klar, daß in diesem Code-Moloch eigentlich ein Neubau die einzige mögliche Strategie der Weiterentwicklung lag. Aber warum jetzt ein PHP-Framework bauen, wenn gerade ein Java-ORM Framework entstand? Die Idee zu Equinox entstand und wurde von mir bereits im Oktober 2001 gegenüber diesem Kunden über den grünen Klee gelobt. Eigentlich hatte dieser Programmteil als erster seinen Namen; dieser Kunde brauchte einen Namen für ein Produkt, also nannte Thorsten es Equinox.

Die Formularengine bekam alsbald eine Script-Schnittstelle, der Schema-Generator wurde zerteilt und erzeugte im Compile-Stage nur noch den Quellcode der persistenten Klassen und der Server brauchte für die Initialisierung nur noch eine ini und die Log-Konfiguration. Daran hat sich bis heute auch nicht mehr viel verändert, wenngleich der Inhalt der ini inzwischen leicht gewachsen ist. Außerdem entstand in den Weihnachstagen eine erste Version des Synchronisationsmechanismus, der auf Basis der Zeitstempel an den Objekten funktionierte, der sogenannte Statesync. Er funktionierte mit einer nicht allzu großen Menge an Objekten gemessen an der Komplexität erstaunlich gut. Aber er tat dies weder vollständig noch in einer annehmbaren Geschwindigkeit. Ich hatte die Geschwindigkeit des Gesamtsystems einfach grob überschätzt. Außerdem brauchte er Unmengen an RAM. Eine Alternative mußte her, und die war nur im Logsync zu finden - diese Variante ist heute die einzig Mögliche in MyTISM.

Im Lauf der ersten Monate von 2002, inzwischen arbeiteten drei Programmierer aktiv an der Entwicklung von MyTISM, wie es inzwischen hieß, wurden das Logging verfeinert. Erst jetzt wurde der Float-Datentyp in MyTISM eingebaut. Die Formularengine konnte inzwischen Scripts nach allen Regeln der Kunst, ein BX-Objekt (eine nicht persistente Entität, die jedoch per Schema zugreifbar und damit funktionell identisch mit anderen BOs) wurde eingeführt. Ende April gab es eine erste Version der GUI des Hotelvermittlers, der zweite Kunde für MyTISM überhaupt. Anfang Mai wurden dann die ersten wirklich lauffähigen Versionen von Equinox gesichtet; der Equinox-Kunde wollte Juni 2002 live gehen mit der neuen Website, aber leider hatte er durch viele Updates an seine alten Site derartig Resourcen gebunden, daß es uns nicht möglich war, mit der nötigen Intensität an Equinox weiterzubauen. Außerdem waren der Designer und der ERP-Supplier nicht unbedingt unseren Anforderungen gewachsen, so daß wir viele Arbeiten, die eigentlich nicht unser Job waren, mit erledigen mußten. Aber alles in allem ging die Entwicklung von Equinox voran, und die ersten Seiten krabbelten aus dem Web, frisch aus MyTISM, in XML gewandelt, per XSLT transformiert und dann ausgegeben…​

Die Logschreiberei war im April soweit beendet, daß ein weiterer Mechanismus gebaut werden konnte: der Export-Handler, der auf Basis der Logs Daten an Fremdsysteme ausliefern konnte - per Timetravel sogar mit echten "Snapshots" der Daten zum jeweiligen Zeitpunkt.

FIXME To be continued …​

Zukunft?

FIXME To be written …​

SOLSTICE - der Client

Solstice ist ein Frontend bzw. eine Benutzeroberfläche für MyTISM - oder besser gesagt, das Frontend, auch wenn, dank der modularen Bauweise von MyTISM, andere Frontends ohne weiteres möglich sind.

Grundlagen

Begriffe

BO / CBO / SBO

BO ist die Abkürzung für "Business Object" - jeder Datensatz in MyTISM ist ein BO.
Eine "Person" ist ein BO vom Typ "Person" und hat z.B. die Felder "Vorname", "Nachname", "Geschlecht", …​
BOs werden der Übersichtlichkeit halber nochmal unterteilt in "Complex Business Object" (CBO) und "Simple Business Object" (SBO). Quertabellen wie z.B. "Geschlecht", die nur wenige Einträge (wie in diesem Fall "männlich" und "weiblich") haben, sind typische Vertreter für ein SBO. Eine "Rechnung" ist da schon was komplexeres und demzufolge vom Typ "CBO".

Lesezeichen

Bei einem Lesezeichen handelt es sich um eine gespeicherte Abfrage. Angezeigt werden alle BOs aus einer Tabelle, die nicht als gelöscht markiert sind. Die angezeigten Daten kann man mittels der Query-Zeile noch weiter einschränken / filtern (siehe "Suchfunktion").

Formulare

Bei einem Formular handelt es sich um die Definition bzgl. der Darstellung eines Objekts, also: in welchen Feldern werden die einzelnen Objekt-Werte angezeigt, wo sitzen diese Felder, usw. In einem Formular werden BOs angezeigt, bearbeitet und neu angelegt.

Schablone

Bei einer Schablone handelt es sich um die "Bauanleitung" für ein neues Objekt, also: von welchem Typ soll ein neues Objekt erzeugt werden und in welchem Formular soll es dargestellt und bearbeitet werden.

Reports

Reports bieten Daten in einer druckbaren Form an. Möchte man z.B. eine Rechnung drucken, dann muss man das Aussehen und die Anordnung der Rechnungsdaten in Form eines Reports einmal definieren und kann fortan diesen für den Ausdruck (oder Erstellung eines PDFs) verwenden.
Reports werden in einem eigenen Kapitel ausführlicher beschrieben.

Codebaustein

Bei einem Codebaustein handelt es sich um ein Stück XML-Quellcode, welches man mit einer entsprechenden Anweisung in den Quellcode eines anderen Strukturelements einbinden kann. Dies dient dazu, doppelten Code zu vermeiden und gleiche, oft benötigte Quelltext-Teile zentral verwalten und ändern zu können.

Virtuelle Attribute / Scripted Attribute

MyTISM bietet die Möglichkeit im laufenden Betrieb Datenfelder in Formulare, Lesezeichen und Reports nachzubauen. Diese nennt man "virtuelle Attribute" oder auch "scripted Attribute".

Für alle im Schema der jeweiligen MyTISM-Installation definierten BOs werden beim Start des Servers automatisch jeweils ein Lesezeichen (das alle BOs der entsprechenden Klasse anzeigt) sowie ein Formular und eine Schablone erstellt. Daneben existieren für manche Klassen auch noch angepasste, "schönere" vorgebaute Strukturelemente, die ebenfalls automatisch eingespielt werden.

Bedienung

Formulare, Lesezeichen, etc. können in Solstice zwischen verschiedenen Ordnern verschoben und kopiert werden und es können sog. Verknüpfungen angelegt werden (ähnlich wie eine Kopie, aber das Objekt wird nicht wirklich kopiert; es wird lediglich ein "Verweis" auf das Originalobjekt angelegt, d.h. die Verknüpfung und das Originalobjekt sind weiterhin miteinander verbunden und alle Änderungen, die an einem der beiden gemacht werden, wirken sich auf "das andere" aus).

Verschieben, Kopieren und Verknüpfungen erstellen macht man, indem man mit der linken Maustaste auf das Objekt klickt, das Objekt - mit weiterhin gehaltener Maustaste - an die gewünschte Stelle "zieht" und dann die Maustaste loslässt.

Hat man beim Loslassen keine weitere Taste gedrückt, wird eine Verknüpfung erstellt; hat man beim Loslassen die Taste STRG (engl. CTRL) gedrückt, so wird das Objekt verschoben; hat man die Taste ALT gedrückt, so wird das eine Kopie des Objekts an dieser Stelle angelegt.

Automatik-Elemente können nur kopiert werden; einen Alias zu erstellen oder das Element zu verschieben wird komplett ignoriert.

Die Lesezeichen-, Formular- und Schablonen-Objekte an sich können bearbeitet werden, indem man entweder das Objekt anwählt und ALT+EINGABE drückt oder im Kontextmenü des Objekts (Objekt anwählen, rechte Maustaste drücken) den Menüpunkt Information wählt.

Referenz Tastaturkürzel

to be continued

F2

Funktion: Speichern
Wo: Formular

F3

Funktion: Speichern und Schliessen
Wo: Formular

F3

Funktion: Speichern und Schliessen
Wo: Formular

F4

Funktion: Popup aufklappen
Wo: Formular

F5

Funktion: Aktualisierung der Daten/Anzeige
Wo: Lesezeichen, Menü-Baum

ESC

Funktion: Ansicht schliessen
Wo: Formular, Lesezeichen

STRG-F

Funktion: Suchen (Strukturelemente: Formular, Lesezeichen, Report, …​)
Wo: überall

STRG-S

Funktion: Speichern
Wo: Formular

Lesezeichen

Lesezeichen zeigen in Tabellen- bzw. Listenform eine Menge von BOs an.

Ein Doppelklick auf ein Objekt der Liste öffnet es im zugehörigen Formular. Wenn mehrere Formulare für die entsprechende Objektklasse existieren, wird das mit der höchsten Priorität gewählt.

Per Klick mit der rechten Maustaste kann man sich alle (auf Grund der jeweiligen Berechtigung sichtbaren) zur Verfügung stehenden Formulare anzeigen lassen. So kann man ein bestimmtes Formular auswählen, in welchem das ausgewählte Objekt dann angezeigt wird.

Suchfunktion / Filter

In der Eingabezeile oberhalb der Tabellenanzeige kann man Suchbegriffe eingeben. Durch Drücken von RETURN/ENTER werden alle BOs gesucht und angezeigt, in denen der eingegebene Begriff vorkommt.

Gesucht wird per Volltextsuche über alle Text- und Zahlenfelder eines Objekts. Die Erweiterung der Suche auf Attribute aus verknüpften BOs ist im Abschnitt "Anpassungen" beschrieben.

Suchbegriffe können auch kombiniert werden (ODER-Verknüpfung): Ein Pluszeichen (direkt) vor einem Suchbegriff bedeutet, dass der Begriff vorkommen muss. Ein vorangestelltes Minuszeichen bedeutet umgekehrt, dass der Begriff nicht vorkommen darf.

Wenn man über die "innere Struktur" der angezeigten BOs Bescheid weiss und sich mit OQL auskennt, kann man auch sehr spezielle Suchanfragen absetzen. Solche Suchanfragen werden mit einer offenen eckigen Klammer eingeleitet.

Beispiele:

[Von = "01.06.04 08:00"
[Zeittyp.Name = "Feiertag"
[Tid In List ( "Text1", "Text2" )
Nicht-Interaktive Filter

Es gibt auch Filter, die im Lesezeichen immer aktiv sind, ohne dass man sie bedienen muss (Beispiel: Alle Ereignisse der letzten Woche). Diese werden mit einem filter-Tag (ohne Attribut type) beschrieben.

Beispiel: Zeige nur Einträge, die beim Öffnen des Lesezeichens nicht älter als ein Monat sind:
<filter>
   <![CDATA[
      age(Ende) < "1 month"
   ]]>
</filter>
  • age() ist eine Funktion der Datenbank

  • Es wird festgestellt, ob das Alter des Eintrags der Eigenschaft Ende älter als ein Monat ist.

Note
Weil im Filter ein Kleiner-Zeichen vorkommt, steht er in einer CDATA-Sektion. Alternativ könnte man auch <filter>age(Ende) &amp;lt; "1 month"</filter> schreiben.

Interaktive Filter

Es ist auch möglich, Filter für Lesezeichen vorzudefinieren. Durch Aktivieren der angezeigten Checkbox bzw. Auswahl aus der angezeigten Selectbox kann dann die Menge der anzeigten Objekte sehr einfach gefiltert werden.

Filter werden immer auf die gleiche Weise erzeugt: Unter dem Tag <filter> befinden sich im XML (je nach Filtertyp mehrere) Textknoten, die zu einer WHERE-Klausel auswerten.

Boolesche (Ja/Nein/Egal) Filter

Ein Boolescher Filter erscheint als Checkbox.

Beispiel Checkbox-Filter:
<Query type="Text">
  <filter type="bool" title="nur männlich">
    <ifTrue>
      Geschlecht.Tid="MAENNLICH"
    </ifTrue>
    <ifFalse>
      Geschlecht.Tid="WEIBLICH" or Geschlecht.Tid="NA"
    </ifFalse>
    <ifNull>
      Geschlecht=null
    </ifNull>
  </filter>
</Query>

Das Query-Tag enthält hier einen Filter, der auf Wunsch alle weiblichen (eigentlich: alle nicht-männlichen) Personen herausfiltert.

boolescher filter

Multiple-Choice-Filter

Ein Multiple-Choice-Filter erscheint in seinem Formular als Combo-Box.

Statische Multiple-Choice-Filter
Beispiel statischer MultipleChoice-Filter:
<Query type="text">
  <filter type="multipleChoice" title="Auswahl">
    <choice title="Alle"></choice>
    <choice title="Nur Rechnungen">Bot.Name="Rechnung"</choice>
    <choice title="Nur Direktverkaeufe">Bot.Name="Direktverkauf"</choice>
  </filter>
</Query>
Beispiel statischer MultipleChoice-Filter mit vordefinierter identischer WHERE-Klausel:
<Query type="text">
  <filter type="multipleChoice" title="Auswahl">
    <clause>Bot.Name="{}"</clause>
    <choice title="Alle"></choice>
    <choice title="Nur Rechnungen">Rechnung</choice>
    <choice title="Nur Direktverkaeufe">Direktverkauf</choice>
  </filter>
</Query>

Hier agiert {} in der clause als Platzhalter für einsetzbare Werte, die in choice-Tags angegeben sind. Bei "Alle" (leeres Tag) erhält es eine Wildcard-Funktion.

Beispiel MultipleChoice-Filter mit SQL-Funktionen (hier Datumsberechnung):
<Query>
    <filter type="multipleChoice" title="$R{Geschrieben}">
      <choice title="$R{seitHeute}"><![CDATA[
        age(date_trunc( "day", BuchungsDatum))<"1 days"
      ]]></choice>
      <choice title="$R{seitGestern}"><![CDATA[
        age(date_trunc( "day", BuchungsDatum))<"2 days"
      ]]></choice>
      <choice title="$R{letzteWoche}"><![CDATA[
        age(date_trunc( "day", BuchungsDatum))<"7 days"
      ]]></choice>
      <choice title="$R{letztenMonat}"><![CDATA[
        age(date_trunc( "day", BuchungsDatum))<"30 days"
      ]]></choice>
      <choice title="$R{irgendwann}"/>
    </filter>
</Query>
Dynamische Multiple-Choice-Filter mit choiceQuery

Es ist auch möglich, dynamische Multiple-Choice-Filter mit Hilfe einer Query anzugeben.

Beispiel dynamischer MultipleChoice-Filter:
<Query>
  <!-- Liste enthaelt alle Filialen mit gesetzter Tid und zeigt in der Liste den Kurznamen der Filiale an -->
  <filter type="multipleChoice" title="Filiale">
    <choiceQuery query="Filiale a WHERE Not Ldel And Not is_undefined( Tid )"
                 displayFormat="Kurzname">
      Filiale.Kurzname = "{Kurzname}"
    </choiceQuery>
  </filter>
</Query>

Das Resultat der choiceQuery wird Wert für Wert als Filtereinträge im Formular angezeigt. Statt der Angabe eines "choice-title" werden die Resultate mittels des "format"-Attributs formatiert und als Auswahlwerte angezeigt.

Abhängigkeiten für dynamische Multiple-Choice-Filter mit choiceQuery

Multiple-Choice-Filter, die ihre Werte per choiceQuery ermitteln, können Abhängigkeiten zu anderen Filtern definieren und aufgrund der darin gesetzten Werte ihre eigene Auswahl modifizieren.

Die Abhängigkeiten werden mittels des Attributs dependsOn angegeben. Es können ein oder durch Komma getrennt auch mehrere andere Filter über ihren Namen als Abhängigkeiten definiert werden.

Ändert sich der Wert in einem Filter, von dem man abhängig ist, so werden die Werte automatisch aktualisiert.

Per Attribut dependsOnQuery wird die Query angegeben, mit der die Werte ermittelt werden, inklusive der aktuell gesetzen Werte in den Filtern, von denen man abhängig ist. In die dependsOnQuery können die Werte aus den anderen Filtern über ihren Namen eingesetzt werden, indem man den Namen des Filters in geschweifte Klammern {…​} schreibt.

Das gleiche gilt für das Attribut dependsOnDefaultQuery, das zur Ermittlung des Default-Wertes inklusive der aktuell gesetzen Werte in den Filtern dient.

Beispiele dynamischer MultipleChoice-Filter mit Abhängigkeiten:
<Table entity="Lagerplatz">
  <Query type="Text">
    <filter name="Halle" type="multipleChoice" title="$R{Halle}">
      <choiceQuery query="Halle bo WHERE Not Ldel ORDER BY Name">Regal.Halle.Id = {Id}</choiceQuery>
    </filter>
    <filter type="multipleChoice" title="$R{Regal}" dependsOn="Halle">
      <choiceQuery query="Regal bo WHERE Not Ldel ORDER BY Nummer" dependsOnQuery="Regal bo WHERE Not Ldel AND Halle.Id = {Halle} ORDER BY Nummer">Regal = {Id}</choiceQuery>
    </filter>
<Query type="Text">
  <filter name="Maschine" type="multipleChoice" title="$R{Maschine}">
    <choiceQuery query="Maschine a where not Ldel order by Name">
      exists (within MaschinenPositionen p where p.Maschine.Id = {Id})
    </choiceQuery>
  </filter>
  <filter type="multipleChoice" title="$R{MaschinenFehlercode}" dependsOn="Maschine">
    <choiceQuery query="MaschinenFehlercode a where not Ldel
                        and (Inaktiv = null or not Inaktiv)
                        and MaschinenUnabhaengig
                        order by Name"
        dependsOnQuery="MaschinenFehlercode a where not Ldel
                        and (Inaktiv = null or not Inaktiv)
                        and (exists (within Maschinen m where m.Id={Maschine})
                             or MaschinenUnabhaengig)
                        order by Name">
      MaschinenFehler = {Id}
    </choiceQuery>
  </filter>
Caution
Momentan wird für die gesetzten Werte in Multiple-Choice-Filtern, von denen man abhängig ist, nur die Id und nicht das BO selbst eingesetzt. Dies wird sich noch ändern.
Caution
Die Notation ist noch verläufig und kann sich nochmals ändern. Insbesondere fehlt die Möglichkeit, für gesetzte NULL-Werte in anderen Filtern abweichende Klauseln angeben zu können, was oftmals jedoch nötig ist. Oftmals hilft eine Konstruktion, bei der man in der dependsOnQuery, die den Wert eines anderen Filters benutzt, zusätzlich eine Klausel "or '{Maschine}'='NULL' hinzufügt.
Dynamische Multiple-Choice-Filter mit choiceScript
Beispiel dynamischer MultipleChoice-Filter mit Skript:
<Query>
  <!-- Liste soll nur Kunden zur Auswahl enthalten, von denen es auch eine Rechnung gibt -->
  <filter type="multipleChoice" title="Kunde">
    <clause>Kunde.AbstraktePerson.Name1 = "{}"</clause>
    <choiceScript>
al = _bol.queryBO( "SELECT a.Kunde.AbstraktePerson.Name1 FROM Rechnung a WHERE NOT Ldel ORDER BY Kunde.AbstraktePerson.Name1" );
erg = new TreeSet();
for ( n : al ) {
  erg.add( n );
}
return new ArrayList( erg );
    </choiceScript>
  </filter>
</Query>
Weiteres Beispiel dynamischer MultipleChoice-Filter mit Skript:
<Query>
  <!-- Liste enthaelt immer die letzten 10 Jahre -->
  <filter type="multipleChoice" title="Jahr">
    <clause>date_part("year",$IP{attrDatum})={}</clause>
    <choiceScript>
cal = new GregorianCalendar();
cal.setTime( new Date() );
year = cal.get( GregorianCalendar.YEAR );
list = new ArrayList();
for( i = 0; i &lt;= 10; i++ ) {
  list.add( String.valueOf( year - i ) );
}
return list;
    </choiceScript>
  </filter>
</Query>
Text-Filter
Beispiel Text-Filter:
<Query type="text">
  <filter type="string" title="MaschinenAuftrag" cols="30">
    <clause>  upper( Projekt.Posten.Bemerkung )
              like upper( "%{}%" )
           or upper( Projekt.Posten.Matchcode )
              like upper( "%{}%" )</clause>
  </filter>
</Query>
Ausserdem kann ein Präprozessor-Skript an Text-Filter übergeben werden:
<filter title="$R{Namen}" type="string">
  <clause>Name in list ( {} )</clause>
  <inputPreprocessor>
    input.split( ',' ).collect{ "'${it.trim()}'" }.join( ',' )
  </inputPreprocessor>
</filter>

Hier wird die Eingabe des Benutzers in der input Variable vorher zur Bearbeitung an das Skript gereicht.
Dieses gibt einen String zurück welcher dann in die clause eingefügt wird.

Trenner

Filter können in Gruppen unterteilt werden, indem man ein Label hinzufügt. Dieses kann wie das normale Label konfiguriert werden und gruppiert alle nach dem Label kommenden Filter. Dies bedeutet, dass man die Filter durch einen Klick auf das Label einklappen kann.

Die Standard-Konfiguration vergrößert die Schrift um 10% und hinterlegt das Label mit einem grauen Farbverlauf. Dies kann natürlich nach Wunsch überschrieben werden.

lesezeichen label

<separator text="Artikeleigenschaften" icon="/20x20/Box.gif"/>
<filter...
...
<separator text="Verkauf2" collapsed="true"/>
<filter...
...
<separator prefSize="200c"
           text="Verkauf"
           fontSize="+10%"
           gradientStartColor="160 160 255"
           gradientStopPosition="SOUTH"/>

Mehrfachsortierung

Der Name sagt es schon - mittels der Mehrfachsortierung kann in einem Lesezeichen nach mehreren Spalten sortiert werden.

Im Lesezeichen selbst klickt man hierfür wie gewohnt auf den Titel der Spalte. Hält man dabei die CTRL-Taste gedrückt, werden bisher definierte Sortier-Spalten beibehalten.

Die Reihenfolge, in der die Spalten sortiert werden, ist anhand der Größe der Symbole zu erkennen.

Um eine Sortierung dauerhaft bzw. als Default-Einstellung im Lesezeichen-Code zu definieren, gibt es die Column-Tags "sort" und "sortLevel".

In der alternativen Notation für Spalten wird die Sortierung durch die Schlüsselwörter ASC und DESC durch Komma getrennt hinter den Attributnamen geschrieben. Der sort-level kann als Zahl direkt hinter ASC oder DESC geschrieben werden und muss innerhalb der Spaltendefinitionen eindeutig sein.

Beispiel für die Default-Einstellung der Sortierung in der alternativen Notation von Tabellenspalten:
<Table entity="Rechnung">
  <Query type="Text"/>
  <View>
    <Columns>
      Kunde, ASC2
      Belegdatum, DESC1
      Netto 'Netto-Betrag'
      Brutto 'Brutto-Betrag'
      Bankeinzug
    </Columns>
  </View>
</Table>

Erklärung: zuerst wird absteigend nach der Spalte "Belegdatum" sortiert, danach aufsteigend nach der Spalte "Kunde".

Massenänderungen / Skripting

Aus jeder Tabellenansicht (also Lesezeichen und Table-Popups bzw. Anzeigen in Formulare mit 1:n-Beziehung) heraus kann man sehr einfach Massenänderungen durchführen, d.h. eine oder mehrere Eigenschaften mehrerer BOs auf einmal ändern.

Hierfür markiert man in der Tabellenansicht die zu ändernden Datensätze (oops, sorry, Objekte) und ruft mit der rechten Maustaste das Kontextmenü auf.

Man hat nun die Möglichkeit, die Änderung mit dem Formular oder per Skript durchzuführen. Gibt man in einem oder mehreren Feldern des Formulars einen Wert bzw. Werte ein, werden diese beim Speichern auf alle markierten Objekte angewendet. So werden z.B. auch bei hinzugefügten und neu angelegten Objekten diese kopiert und an jedes markierte Objekt angehangen.

Beispiel: an mehrere Rechnungen soll ein Artikel als Rechnungsposten angehangen werden. Die betroffenen Rechnungen werden markiert und mittels des Massenänderungsformulars wird der besagte Artikel als Rechnungsposten angehangen. Nach abgeschlossener Massenänderung findet sich dieser Rechnungsposten als jeweils eigener Datensatz (Objekt) an allen markierten Rechnungen.

Mit dem Skript eröffnen sich per BeanShell-Programmierung weitaus grössere und komplexere Möglichkeiten. Neben den BeanShell-Befehlen stehen noch Funktionen aus den automatisch generierten Klassen zur Verfügung (zu finden in .PROJEKT/classes/de/PROJEKT/bo/).

Beispiel (um Projekteinträge an ein anderes Projekt zu hängen, aus der OAshi-Applikation "OAshi.Venice")

// Bitte modifizieren Sie dieses vorgefertigte Script nach Ihren Wuenschen
// bo.Id = (Long) ;
// bo.Crea = (Datetime) ;
// bo.Lmod = (Datetime) ;
// bo.Ldel = (Boolean) ;
// bo.Bot = // (BOT) ;
// bo.addDateien( (Datei) );
// bo.removeDateien( (Datei) );
// bo.Tid = (String) ;
// bo.Mitarbeiter = // (Mitarbeiter) ;
// bo.Datum = (Datetime) ;
// bo.Dauer = (Integer) ;
// bo.Kunde = // (Kunde) ;
prjs = ctx.queryBO( "select bo from de.m.bo.Projekt bo where bo.Kuerzel = \"tapla\"" );
bo.Projekt = prjs.get( 0 );
// bo.Beschreibung = (String) ;
// bo.BemerkungIntern = (String) ;
// bo.Kostenstelle = // (Kostenstelle) ;
// bo.InRechnungStellen = (Boolean) ;

Formulare

Formulare sind Eingabemasken, mit deren Hilfe BOs erstellt oder bearbeitet werden können. Sie definieren welche (Eingabefelder für welche) Attribute angezeigt werden.

Eingabemöglichkeiten nach Datentypen

(FIXME Diese Sektion passt eigentlich nicht wirklich hier hin; sollte man später mal alles sauber anordnen …​)

Timespan (Zeitspanne)

FIXME Standardmässig wird für die Eingabe im Solstice jetzt der SimpleTimespanChooser verwendet, der eine einfachere Eingabe als hier angegeben erlaubt.

Zeitspannen werden intern als Anzahl von Sekunden abgespeichert. Eingegeben werden können jedoch intuitivere Werte wie z.B. eine Anzahl von Minuten, Stunden, Tage etc. Es gibt dafür grob drei Gruppen von Formaten:

Altes Standardformat

Dieses Format wird verwendet wenn kein spezielles displayFormat angegeben ist.

Beispiele:

  • 30s = Dreissig Sekunden

  • 10m = Zehn Minuten

  • 1d 2h = Ein Tag und zwei Stunden

  • 3w = Drei Wochen

  • 5y 3M = Fünf Jahre und drei Monate

Folgende Bezeichner können dabei verwendet werden:

Table 1. Eingabe Timespan

Bezeichner

Name

Entspricht

y

Jahr (year)

365d

M

Monat (month)

30d

w

Woche (week)

7d

d

Tag (day)

24h = 1440m = 86400s

h

Stunde (hour)

60m = 3600s

m

Minute (minute)

60s

s

Sekunde (second)

1s

Achten Sie darauf, dass sie bei Benutzung mehrere Bezeichner immer mit den grössten anfangen.

Beispiele:

  • Richtig: 1m 30s

  • Falsch: 30s 1m

  • Richtig: 1d 5h 20m

  • Falsch: 1d 20m 5h

Achten Sie auch darauf, dass zwischen Zahl und Bezeichner keine Leerzeichen stehen dürfen und dass der Bezeichner immer nach der Zahl kommen muss.

Beispiele:

  • Richtig: 1m 30s

  • Falsch: 1 m 30 s

  • Falsch: m1 s30

  • Falsch: 1 30s

  • Falsch: 1x 30s

  • Falsch: a1 30s

  • Falsch: m 1 30s

Alle eingegebenen Zeitspannen werden automatisch in ein kanonisches, d.h. festgelegtes, eineindeutiges Format umgewandelt.

Beispiele:

  • 55s bleibt 55s

  • 73s wird zu 1m 13s

  • 30h wird zu 1d 6h

  • 10d wird zu 1w 3d

  • 200w wird zu 3y 10M 5d

  • 70m 340s wird zu 1h 15m 40s

  • 70M 340s wird zu 5y 9M 5d 5m 40s

  • 13y 6M 45d wird zu 13y 7M 2w 1d

"Doppelpunkt"-Format(e)

Dieses Format wird verwendet wenn als displayFormat "HH:mm:ss" bzw. "HH:mm" angegeben ist. Die Stundenanzahl hat dabei immer mindestens zwei Ziffern, bei Bedarf können aber auch mehr dargestellt/verwendet werden.

Beispiele für "HH:mm:ss":

  • 00:00:30 = Dreissig Sekunden

  • 00:10:00 = Zehn Minuten

  • 26:00:00 = Ein Tag und zwei Stunden

  • 504:00:00 = Drei Wochen

  • 45960:00:00 = Fünf Jahre und drei Monate

"Marker"-Format(e)

Bei diesen Formaten wird die Zeitspanne als nur eine Zahl dargestellt. Ein Marker-Buchstabe im displayFormat gibt dabei an, in welche Einheit die Zeitspanne umgerechnet bzw. angezeigt wird.

Beispiele für Darstellung bzw. akzeptierte Eingaben für eine Zeitspanne von 455984 Sekunden mit verschiedenen displayFormat-Alternativen:

  • ###,##0.00s = 455,984.00

  • #####0s = 455984

  • ###,##0.00m = 7,599.73

  • #####0m = 7600

  • ###,##0.00h = 126.66

  • #####0h = 127

  • ###,##0.00d = 5.28

  • #####0d = 5

Als Marker erlaubt sind, wie im Beispiel zu sehen, 's' für Sekunden, 'm' für Minuten, 'h' für Stunden, 'd' für Tage, 'w' für Wochen, 'M' für Monate (= 30 Tage) und 'y' für Jahre (= 365 Tage). Bei Aus- oder Eingabe werden diese Marker-Buchstaben nicht angezeigt bzw. eingegeben.

Als Besonderheit gibt es noch den Marker '*'. Bei Verwendung dieses Markers wird (bei der Ausgabe) automatisch die "beste" Einheit gewählt, d.h. diejenige, bei der eine Zahl >= 1.0 herauskommt. Als Spezialfall wird bei diesem Format der passende Marker-Buchstabe mit ausgegeben, bzw. muss bei der Eingabe ebenfalls an die Zahl angehägt werden, damit die korrekte Einheit gewählt werden kann.

Die Zeichen vor dem Marker-Buchstaben sind ein Pattern für java.text.DecimalFormat, welches für die Formatierung der Zahl verwendet wird.

Diverses

  • Messagebox erzeugen: ctx.showMessageDialog( "bla" )

  • Sperren von Formularfeldern: dem jeweiligen Feld mit name="ich" einen Namen geben und im Formular-Code dann: ich.setEditable( false );

  • Der Parameter lazy wird in der Formular-Definition im Tab-Tag verwendet (Bsp.: <Tab lazy="false" …​) und gibt an, ob die Daten die im Formular hinter diesem Tab (Reiter) stecken, direkt beim Öffnen des Formulars geladen werden sollen (lazy="false") oder erst wenn man den Tab anklickt (lazy="true" - das ist die Standard-Einstellung).

  • Um die Suche in nicht direkt zum BO gehörenden, aber am BO hängenden Attributen zu ermöglichen, muss man im Formular-XML-Code im Element <Element><TablePopup><Table> ein evtl. bestehendes Element <Query> ergänzen bzw. einfügen (siehe Beispiel):

    <Element>
      <TablePopup>
        <Table>
          <Query type="Text">
            <addProperty>
              Bot.Name
            </addProperty>
          </Query>
          <View>
            ...
          <View>
        </Table>
      </TablePopup>
    </Element>
  • Farbliches Aussehen der Reiter wird im jeweiligen Benutzer (Formular, Parameter, ganz unten) eingestellt. Diese "Defaults" kommen aus Projekt/gui/Client.nrx (nach "xpath" suchen)

Schablonen

Wie in "Grundlagen" bereits beschrieben, dienen Schablonen dazu, neue BOs anzulegen. Eine Schablone definiert, von welcher Klasse ein neues Objekt erzeugt werden soll und welches Formular zur Darstellung und Bearbeitung benutzt werden soll. Möglicherweise werden auch bereits bestimmte Werte in das neu zu erzeugende BO geschrieben.

Die meisten Attribute des Formulars sind aus den anderen Strukturelementen bekannt und/oder selbsterklärend. Wichtige spezielle Attribute:

BOTyp

Von welcher Klasse soll ein Objekt erzeugt werden?

Formular

Welches Formular (passend zum BOTyp bitte) soll für die Bearbeitung des neuen Objekts/Eintragen der Werte benutzt werden?

Parameter

Hier kann (per XML) ein Script definiert werden, über das z.B. Werte im neuen Objekt bereits vorbelegt werden. Weitere Konfigurationsmöglichkeiten bzw. Angaben sind hier z.Zt. nicht möglich.

Erzeugen des neuen Objektes

Note
vgl. de/ipcon/gui/solstice/Client.openNew()

Im Normalfall wird ein Objekt der angegebenen Klasse (BOTyp, s.o.) einfach durch Aufruf des entsprechenden No-Argument-Konstruktors erzeugt. Will man aber selber z.B. direkt Werte des neuen Objektes setzen, kann man die Objekterzeugung mittels Script selbst in die Hand nehmen. Dazu gibt man als Parameter für das Formular ein entsprechendes BeanShell-Script an, welches die gewünschten Aktionen durchführt. Das Script muss ein neu erstelltes Objekt der gewünschten Klasse zurückliefern.

Das Beispiel zeigt den Inhalts des Parameter-Feldes einer Schablone für MyTISMBenachrichtigungsAuftrag; wie man sieht können so auch andere Objekte direkt mit erzeugt und konfiguriert werden:

<Schablone>
  <newInstance>
    ba = tx.include( new MyTISMBenachrichtigungsAuftrag() );
    ba.setAbsender( ctx.getCurrentUser() );
    bv = tx.include( new MyTISMBenachrichtigungsVorlage() );
    bv.setIstEinweg( true );
    ba.setVorlage( bv );
    return ba;
  </newInstance>
</Schablone>

Folgende Variablen sind im Script immer verfügbar (vgl. s.o. und de/ipcon/gui/BasicClient.initScript()); ggf. können aber auch noch weitere Variablen übergeben worden sein:

ctx

Der verwendete ClientContextI. FIXME gibt es den wirklich immer?

ftx

Der verwendete FormContext.

tx

Die Transaction, die für die Erstellung des Objekts verwendet wird.

Die alte Methode der Definition von Default-Werten im Schema wird aus Kompatibilitätsgründen zwar noch unterstützt, sollte aber nicht mehr verwendet werden.

Sichern und Wiederherstellen von Strukturelementen

Unter dem Menüpunkt Entwicklung gibt es die Funktion Struktur-Synchronisation…​. Hiermit werden alle Strukturelemente (Formulare, Lesezeichen, Schablonen, Reports, etc.), bei denen ein (im Prinzip frei wählbarer) Dateiname definiert ist als XML-Dateien in einem Verzeichnis gespeichert bzw. Strukturelemente aus diesen Dateien wieder in die Datenbank eingespielt.

Der Dialog für den Import/Export von Strukturelementen aus/in XML-Dateien

Die Bedienung sollte größtenteils selbsterklärend sein.

  • Mit den diversen Filtern ist es möglich, die Liste nach vorgegebenen Kriterien auszudünnen.

  • Unter Meldungen kann man die Anzeige der Log-Meldungen aktivieren und angeben, wie genau man dort über die Vorgänge informiert werden will.

  • Der Knopf Vergleichen erlaubt es, die Liste manuell zu aktualisieren.

  • Der Knopf Alles synchronisieren exportiert bzw. importiert automatisch alle Strukturelemente, abhängig von ihrem Status und speichert danach auch automatisch die entstandenen Änderungen ab.

  • Sync automatisch durchführen überwacht Datenbank und Verzeichnis selbsttätig auf Änderungen und synchronisiert diese automatisch. FIXME: Es kann sein, dass das noch nicht ganz korrekt funktioniert - Funktion wird fast nie benutzt.

Damit die exportierten Objekte auch einigermassen geordnet in Unterverzeichnissen liegen, die ihrem Ordnernamen in Solstice entsprechen, sollte man dies im Dateinamen mit angeben. So würde man für das Formular "MeinFormular", welches im Ordner "EigeneFormulare" liegt z.B. folgenden Dateinamen eintragen: EigeneFormulare/MeinFormular. Die vorgebauten Formulare für Strukturelemente bieten einen Knopf "Dateiname vorschlagen" mit welchem man einen aus dem Elterpfad generierten Dateinamen automatisch eintragen lassen kann.

Die exportierten Objekte enthalten je nach Typ folgende Kürzel:

  • bkm: Lesezeichen (für engl. "Bookmark")

  • frm: Formular (für engl. "Form")

  • tpl: Schablone (für engl. "Template")

  • rpt: Report (für Reports werden aus technischen Gründen übrigens zwei Dateien abgespeichert, die zweite der beiden Dateien hat gar kein "Mittelkürzel")

  • bst: Codebaustein

Ausführung von Skripts bei Server-Ereignissen

Im Normalfall werden bei Server-Ereignissen, wie Herunterfahren oder Systemnachrichten voreingestellte Aktionen ausgeführt; meist wird (nur) eine Nachricht angezeigt. Mittels im Benutzer-Profil definierter Skripts kann man jedoch auch in anderer Weise auf diese Ereignisse reagieren. Beispiel:

<Configuration>
  <Profile name="default">
    <onSystemMessage>_client.log.warn( "Systemmessage: " + _msg + "." )</onSystemMessage>
    <onShutdownInitiated>_client.log.warn( "Shutdown initiated: " + _msg + " in " + _cSecsDelay + " seconds." )</onShutdownInitiated>
    <onShutdownStopped>_client.log.warn( "Shutdown stopped." )</onShutdownStopped>
    <onShutdown>_client.log.warn( "Server has been shut down." ); _client.close()</onShutdown>
    <!-- Sonstiger Profil-Code -->
  </Profile>
</Configuration>

Folgende Möglichkeiten stehen zur Verfügung:

  • onSystemMessage: Wird aufgerufen, wenn eine Systemnachricht angekommen ist. Die Variable _msg enthält den Nachrichtentext.

  • onShutdownInitated: Wird aufgerufen, wenn die Bennachrichtigung über ein bevorstehendes Herunterfahren des Servers angekommen ist. Die Variable _cSecsDelay enthält die Anzahl der Sekunden, die das Herunterfahren noch entfernt ist; _msg enthält ggf. den Text einer zusätzlichen Information zum Herunterfahren, sofern einer mitgeliefert wurde.

  • onShutdownStopped: Wird aufgerufen, wenn das Herunterfahren aus irgendeinem Grund abgebrochen wurde.

  • onServerLocked: Wird aufgerufen, wenn der Server gesperrt (keine Anmeldungen mehr erlaubt) wurde.

  • onServerUnlocked: Wird aufgerufen, wenn der Server wieder entsperrt wurde.

Reports

Grundlagen

Mittels Reports können aus MyTISM heraus Listen oder andere Dokumente als PDF oder auch in anderen Formaten erzeugt werden. Reports stützen sich dabei auf die Daten von Objekten aus der MyTISM-Datenbank, welche sie dann mit Hilfe des im Report definierten Vorlage-Layouts darstellen.

Erstellung eines neuen Reports

  1. Neues Report-Objekt erzeugen mittels Schablone /Admins/MyTISM (Vorgebaut)/Grundelemente/Report (Vorgebaut)

  2. Einen sinnvollen Namen und eine kurze Beschreibung vergeben.

  3. Mit Knopf Tid vorschlagen einen Kurznamen/externen Schlüssel vergeben lassen oder einen von Hand vergeben.

  4. BO-Typ wählen. Welche Objekte die Datengrundlage für den Report bilden. Für einen Report, mit dem Vertragsdokumente erzeugt werden sollen würde man hier z.B. Vertrag auswählen.

  5. Priorität angeben. Hierüber wird definiert, an welcher Stelle der Liste der möglichen Reports z.B. in Kontextmenüs o.Ä. dieser Report erscheint. Sinnvoller Wert abhängig von ggf. sonst noch für den BO-Typ vorhandenen Reports. Vorschlag: 100.

  6. Auch für Unterklassen des BO-Typs nutzbar bedeutet: Report kann nicht nur für Objekte von genau dem angegebenen BO-Typ verwendet werden, sondern auch für Objekte von möglicherweise existierenden Unterklassen (Beispiel: Report für Vertrag; ohne Auch für Unterklassen des BO-Typs nutzbar wird Report wirklich nur für Objekte vom Typ Vertrag angeboten, nicht aber z.B. für Unterklasse Mietvertrag. Mit Auch für Unterklassen des BO-Typs nutzbar ist Report auch für Mietvertrag verfügbar.)

  7. Ist eine Liste bedeutet: Report benutzt nicht nur die Daten eines einzigen Objektes (z.B. zum generieren eines einzelnen Vertragsdokuments) sondern listet eine Menge von Objekten auf (z.B. Auflistung aller Verträge zur Übersicht).

  8. Ist eigenständig bedeutet: Report kann das oder die als Datengrundlage zu benutzenden Objekte selbstständig anhand einer im Report definierten Abfrage ermitteln. Ansonsten kann der Report nur auf eine in einem Lesezeichen o.Ä. getätigte Objektauswahl angewandt werden.

  9. Den Report bestimmten Gruppen zuweisen. Nur für Mitglieder dieser Gruppen ist der Report verfügbar. Vorschlag: Admins und Benutzer.

  10. Mögliche (Druck-)Ziele und voreingestelltes Standard-Druckziel wählen. Hiermit wird bestimmt, ür welche Ausgabeformat der Report gedacht ist. Vorschlag für Standard-Ziel: Vorschau.

  11. Sprachen bestimmt, in welchen Sprachen der Report generiert werden kann FIXME Mehr Erklärungen hierzu

  12. Report-Objekt speichern und schliessen.

  13. Report im Navigationsbaum in den Ordner, in dem er letztendlich zu liegen kommen soll, verschieben.

  14. Report wieder zur Bearbeitung öffnen.

  15. Mit Knopf Dateiname vorschlagen einen Dateinamen vergeben lassen.

    Der Dialog zum Erstellen und bearbeiten von Reports

  16. Wechseln auf Reiter Anker-Definition.

  17. Beispiel einfachste Version: <set entity="Vertrag"/> (statt "Vertrag" den internen Namen des oben angegebenen BO-Typs eintragen). Genauere Infos in Abschnitt zur Anker-Definition.

    Note
    anchorclass mit vollem Klassennamen, wie dort angegeben, ist veraltet, bitte entity und Kurznamen verwenden.

    Der Dialog zum Erstellen und bearbeiten von Reports

  18. Report speichern und schliessen.

  19. Report mittels Struktursynchronisation in ein Verzeichnis exportieren.

  20. Als nächstes muss die Definition der eigentlichen Report-Layoutvorlage erstellt werden. Dies geschieht am einfachsten mit dem externen Programm iReport. Falls noch nicht vorhanden, laden Sie dieses bitte herunter und installieren sie es.

    Important
    Bitte benutzen Sie zum Bearbeiten der Reports nur iReport 2.0.5 (Die letzte Version, die ein Ändern der Kompatibilitätseinstellung ermöglicht) und wählen Sie unter Options → Compatibility…​ "JasperReports 2.0.0 - 2.0.1", da MyTISM eine angepasste und fehlerkorrigierte Version der JasperReports-Bibliotheken verwendet, welche allerdings bisher die neueren Report-Formate nicht unterstützt.
  21. Programm iReport starten; durch Struktursync (s.o.) automatisch erstellte Vorlage öffnen.

    Note
    Für Reports werden bei der Struktursynchronisation zwei Dateien erstellt: REPORTNAME.rpt.xml und REPORTNAME.xml. Bei der zweiten Datei handelt es sich um die in iReportladbare Report-Layout-Definition.
  22. Vorlage-Layout wie gewünscht erstellen/bearbeiten.

    FIXME Mehr Infos zu Bildern, Subreports, Parametern, Feldern, etc.

  23. Report(-Definition) in iReport speichern.

  24. Zurück in MyTISM den Report wieder mittels Struktursynchronisation importieren und ausprobieren; z.B. durch Auswählen eines passenden Objekts (im Beispiel: Vertrag) aus einem entsprechenden Lesezeichen und im Kontextmenü drucken mit "Mein erster Report :-)" wählen). Das Fenster für die Struktursynchronisation kann offen gelassen werden, so dass bei weiterer Bearbeitung der Layout-Vorlage das erneute Importieren immer wieder auf einfache Weise aufgerufen werden kann.

Der Dialog zum Erstellen und bearbeiten von Reports

(Eingabe-)Parmeter für Reports

Reports können händig einzugebende Werte via sog. Parameter übergeben bekommen. Diese müssen in der Reportdefinition definiert und konfiguriert werden und werden dann beim "Ausführen" des Reports im Solstice-Client in einem Dialog abgefragt.

Beispiel:
[...]
<reportFont name="heading" isDefault="false" fontName="Arial" size="10" [...]/>
<parameter name="GruppierenNach" isForPrompting="true" class="java.lang.String">
  <property name="choiceScript"
            value="model.addEntry( '$R{Produkt}' );
                   model.addEntry( '$R{Saison}', 'Ich will einen anderen Titel in der Box :-)' );
                   model.addEntry( '$R{Kurzbezeichnung}' );
                   model.addEntry( '$R{Lieferant}' );"/>
  <property name="chooseOnly" value="true"/>
</parameter>
<parameter name="Stichtag" isForPrompting="true" class="java.util.Date">
  <property name="format" value="MEDIUM_"/>
</parameter>
<field name="THIS" class="java.lang.Object"/>
[...]
format

format wird von fast allen Parametern unterstützt und enthält ein CBOFormat mit welchem die Eingabewerte geparsed werden.

choiceScript

Bei Angabe der choiceScript-Property wird eine Combobox angezeigt, die die entsprechenden Werte aus dem Skript enthält. Weitere Konfigurations-Properties hier sind chooseOnly - bei "true" kann der Nutzer nur die vorgegebenen Werte wählen und keine eigenen eingeben und nullable - bei "true" muss kein Wert ausgewählt werden. Für weitere Infos hierzu siehe JavaDoc in der de.ipcon.form.FComboBox-Klasse.

rawInputDefinition

Sehr fortgeschrittene Benutzer können als Notlösung zur Behandlung von Spezialfällen mittels rawInputDefinition die Definition des Eingabeelements selbst direkt angeben. Dies erfordert allerdings genaue Kentnisse der MyTISM-Formular-XML-Sprache und - weil der Code als Zeichenkette im Attribut angegeben werden muss - mühsames Escapen von Sonderzeichen. Der Name der property in der Eingabelementdefinition muss dabei dem zum Typ der Report-Parameter-Klasse passenden Attribut eines BX entsprechen, also z.B. VString wenn der Report-Parameter-Typ java.lang.String ist oder VBO wenn der Report-Parameter-Typ ein Subtyp von BO ist.

Beispiel für Auswahl-Popup für einen Benutzer:
  <parameter name="EinBenutzer" isForPrompting="true" class="de.ipcon.db.core.Benutzer">
    <property name="rawInputDefinition" value="&lt;Popup property=&quot;VBO&quot;&gt;&lt;Table&gt;&lt;Query type=&quot;Text&quot; entity=&quot;Benutzer&quot;&gt;&lt;filter&gt;NOT AnmeldungVerweigern OR AnmeldungVerweigern = null&lt;/filter&gt;&lt;/Query&gt;&lt;Columns&gt;Name, ASC|Beschreibung&lt;/Columns&gt;&lt;/Table&gt;&lt;/Popup&gt;"/>
  </parameter>

Was ist ein Report überhaupt?

FIXME Teilweise veraltet und unvollständig, sollte angepasst bzw. aktualisiert und ergänzt werden.

Wenn Programmierer und Designer von Anwendungen (das ist übrigens ihre bevorzugte Berufsbezeichnung :-)) von Drucken sprechen, setzen sie das meist gleich mit "Reporting"; ein neudeutsches Wort für das zu-Papier-bringen von Daten in einer Datenbank. Obwohl nicht ganz korrekt, deckt Reporting doch die größte Menge aller Druckfunktionen innerhalb einer Anwendung ab. Doch wie funktioniert das genau?

Ein Unterschied zu einem "datengesteuerten" Text wie z.B. einem Serienbrief, ist, daß der Text nicht einfach von Seite zu Seite fließt, sondern daß es "Schaltpunkte" gibt, z.B. an Seitengrenzen, Spaltenenden, am Reportanfang oder -Ende. Aber es gibt auch Schaltpunkte, die von den Daten ausgelöst werden; diese Schaltpunkte werden in der Fachsprache auch Gruppenwechsel genannt. Dieser Ausdruck kommt aus der EDV-Sprache der frühen Sechziger Jahre, und wurde wohl von Cobol-Programmierern in unser Jahrtausend geschleppt. Eigentlich könnte man sie auch Spalteninhaltsschalter oder Spaltentrigger nennen, das träfe den Kern der Sache eigentlich besser. Aber da sich diese Nomenklatur nun seit über vierzig Jahre wacker hält, macht auch diese Anleitung keinen Unterschied und trägt diese Wortschöpfung noch in einige Köpfe mehr, auf das sie niemals aussterben möge…​ :-)

Früher, also in der Welt der hierarchischen oder auch SQL-Datenbanken, wurden Reports auf der Basis einer mehr oder weniger großen Matrix gebildet, deren Spalten die einzelnen Felder und die Zeilen die Datensätze bildeten. Man kann es sich im Prinzip wie eine Serienbrief-Steuerdatei in Excel vorstellen: die Spalten heißen Familienname, Rufname, Anrede und so weiter, und in den Zeilen steht Adresse nach Adresse. Die Gruppierung ergibt sich nun dadurch, daß man diese Zeilen nun so durchsortiert, daß erst alles nach der Anrede sortiert, dann nach dem Familiennamen und zuletzt nach dem Rufnamen. Wenn man sich diese Matrix anschaut, stehen nun alle Frauen oben und alle Herren unten (weil ein F halt vor einem H kommt, völlig wertfrei…​). Also bildet sich hier eine Gruppe, nämlich die Frauen und die Männer. Nehmen wir weiter an, es gibt in unserer Liste mit 1000 Adressen 20 Herren mit dem Nachnamen Müller. Auch diese bilden eine Gruppe.

Nach diesem Prinzip lassen sich auch andere Gruppen bilden, z.B. in dem man eine Altersgruppe bildet und eine Spalte dafür baut; so könnte man die Personen in unter 18, bis 25, bis 40 und über 60 einteilen und jeweils eine Kennzahl 1-4 vergeben. Sortiert man nach dieser Kennzahl, hat man schon wieder eine Gruppe.

Voraussetzung für all das ist natürlich, daß die Sortierkriterien einer strengen Hierarchie folgen, also eine Sortierung NACH einer vorherigen nur dann greift, wenn der Wert der vorangegangenen Sortierung keine eindeutige Aussage machen kann. Dieser Sachverhalt ist eigentlich logisch, er sollte nur nochmals erwähnt werden, um klar zu machen, daß die Sortierkriterien nacheinander greifen.

Kommen wir nun zurück zu unser Personenmatrix. Die sieht nun in einer vereinfachten Version so aus: (je weiter links die Spalte, umso eher ist danach sortiert; das dient der Übersichtlichkeit)

1  Frau  Auer     Anneliese
2  Frau  Bertels  Beate
3  Frau  Meyer    Magarethe
4  Frau  Müller   Mathilde
5  Frau  Muster   Margit
6  Herr  Bauer    Thomas
7  Herr  Chronos  Christian
8  Herr  Hinz     Karl-Heinz
9  Herr  Hoff     Michael
10 Herr  Müller   Markus

Diese Gruppierung wird nun benutzt, um sogenannte Bänder zuzuordnen; das sind rechteckige Ausschnitte aus einer Spalte oder Seite, die über die gesamte Breite gehen, ganz so, als ob ein Band um die Seite gewickelt wäre. Die Höhe dieser Bänder richtet sich meist nach ihrem Inhalt. Grob gesagt, weist man jeder Gruppe zwei Bänder zu, nämlich ein Kopf- und ein Fußband, die wiederum um das Detailband herum angeordnet werden. Die höchste Gruppierung, also die erste Sortierstufe, wird nun "ganz außen" um das Detailband gelegt, dann folgen die nachfolgenden Sortierung bis ganz innen dann das Detailband liegt. Jeder Schaltvorgang einer Gruppe, ein sogenannter Gruppenwechsel, führt nun dazu, daß Füße bis zur jeweiligen schaltenden Gruppe gedruckt werden und dann, beginnend mit dem Kopf der Schaltgruppe alle Kopfbänder erneut gedruckt werden.

Damit das nicht zu einfach wird, kann man Gruppen Abhängigkeiten voneinander hinzufügen, damit sie bei anderen Gruppenwechseln oder auch Seiten- oder Spaltenwechseln ebenfalls mitwechseln. Eine Anwendung dafür sind z.B. der Wiederandruck einer Warengruppe nach einem Seitenwechsel, damit man weiß, welche Warengruppe auf dieser Seite zu sehen ist, auch wenn die vorherige Seite nicht mehr sichtbar ist.

Damit das jetzt nicht zu kompliziert wird, machen wir nun ein einfaches Beispiel. Unser vereinfachter Reportgenerator beherrscht nur Report-Titel, Seiten-Kopf/Fuß, Details und definierte Gruppen. Der Reportgenerator in MyTISM beherrscht ein klein wenig mehr; aber das sehen wir später.

Wir setzen eine Gruppierung nach der Anrede in unserem Report voraus. Gehen wir davon aus, daß unsere avisierte Seite nur maximal drei Personen samt Daten faßt (incl. Kopf und Fuß), dann sähe die Bandanordnung folgendermaßen aus: (Die Einrückung soll die "Hierarchie" der Gruppen deutlich machen und ist kein Druckmerkmal!)

Report-Titel
   Seiten-Kopf "Seite 1"
      Gruppen-Kopf "Frau"
         Details (1)
         Details (2)
         Details (3)
   Seiten-Fuß "Seite 1"
   Seiten-Kopf "Seite 2"
         Details (4)
         Details (5)
      Gruppen-Fuß "Frau"
      Gruppen-Kopf "Mann"
         Details (6)
   Seiten-Fuß "Seite 2"
   Seiten-Kopf "Seite 3"
         Details (7)
         Details (8)
         Details (9)
   Seiten-Fuß "Seite 3"
   Seiten-Kopf "Seite 4"
         Details (10)
   Seiten-Fuß "Seite 4"
Report-Präambel

Wie man sieht, löst der Gruppenwechsel von Datensatz 5 nach 6 (also der Gruppenwechsel von Frau nach Herr) das Drucken des Gruppen-Fußes "Frau" und den Druck des Gruppen-Kopfes "Mann" aus. Die Seiten schieben sich ohne besondere Definition einfach dazwischen, je nachdem wieviel Platz benötigt wird (hier immer drei Adressen).

Eine leicht abgeänderte Variante könnte auch den Wechsel der Anredegruppe mit einem Seitenwechsel verbinden ("start on a new page"), das sähe dann so aus:

[...]
         Details (5)
      Gruppen-Fuß "Frau"
   Seiten-Fuß "Seite 2"
   Seiten-Kopf "Seite 3"
      Gruppen-Kopf "Mann"  -- Der Gruppenwechsel der Anredegruppe löst einen Seitenwechsel aus
         Details (6)
         Details (7)
         Details (8)
   Seiten-Fuß "Seite 3"
   Seiten-Kopf "Seite 4"
         Details (9)
         Details (10)
   Seiten-Fuß "Seite 4"
Report-Präambel

Wie man sieht, würde dadurch der Ausdruck der Seite 2 unterbrochen durch den Wechsel der Anredegruppe; der Seitenwechsel würde früher erfolgen als im obigen Beispiel. Eine weitere Variation wäre es, den Wechsel der Seite generell mit einem Seitenkopf zu verbinden ("print header on each page")

[...]
   Seiten-Kopf "Seite 2"
      Gruppen-Kopf "Frau"  --Der Seitenwechsel läßt den Kopf der Anredegruppe nochmals ausdrucken
         Details (4)
         Details (5)
      Gruppen-Fuß "Frau"
      Gruppen-Kopf "Mann"
         Details (6)
   Seiten-Fuß "Seite 2"
   Seiten-Kopf "Seite 3"
      Gruppen-Kopf "Mann"  -- Hier wird der Gruppenkopf von "Mann" nochmals gedruckt, wegen des Seitenwechsels.
         Details (7)
         Details (8)
         Details (9)
   Seiten-Fuß "Seite 3"
   Seiten-Kopf "Seite 4"
      Gruppen-Kopf "Mann"  -- s.o.
         Details (10)
   Seiten-Fuß "Seite 4"
Report-Präambel

Jedesmal, wenn eine neue Seite beginnt, wird der Gruppenkopf der Anredegruppe erneut gedruckt.

Kommen wir zu einem etwas komplexeren Beispiel. Hier erstmal die Daten:

1  Blech    Flach 3mm
2  Blech    Flach 3,5mm
3  Blech    Rund  2mm
4  Blech    Rund  2,5mm
5  Blech    Senk  2mm
6  Blech    Senk  3mm
7  Blech    Senk  4mm
8  Gewinde  Flach 3mm
9  Gewinde  Flach 3,5mm
10 Gewinde  Flach 4mm
11 Gewinde  Rund  2mm
12 Gewinde  Rund  3mm
13 Gewinde  Senk  3mm
14 Gewinde  Senk  4mm
15 Holz     Flach 3mm
16 Holz     Flach 4mm
17 Holz     Senk  3mm
18 Holz     Senk  4mm

Hier sind ein paar Schrauben zum Reporting zusammengekommen. Die Spalten sind die Eigenschaften das Material, in welches die Schraube gedreht werden sollte, die Art des Kopfes und ihr Durchmesser. Wie nennen diese Eigenschaften im Folgenden M, K und D. Sortiert haben wir nach M, K und D, in dieser Reihenfolge (wie oben, ordnen wir die Sortierspalten von links nach rechts).

Unser Report hat nun zwei Gruppen, nämlich M und K, ohne besondere Verbindungen untereinander. Dadurch ergibt sich in unserem vereinfachten Reportgenerator folgendes Bild (wir setzen wie oben drei Detailbänder pro Seite voraus):

Report-Titel
   Seiten-Kopf "Seite 1"
      Gruppen-Kopf "Blech"
         Gruppen-Kopf "Flach"
            Details (1)
            Details (2)
         Gruppen-Fuß "Flach"
         Gruppen-Kopf "Rund"
            Details <3>      -- Der Platz ist alle nach drei Elementen, also kommt ein Seitenwechsel
   Seiten-Fuß "Seite 1"
   Seiten-Kopf "Seite 2"
            Details (4)
         Gruppen-Fuß "Rund"
         Gruppen-Kopf "Senk"
            Details (5)
            Details (6)
   Seiten-Fuß "Seite 2"
   Seiten-Kopf "Seite 3"
            Details (7)
         Gruppen-Fuß "Rund"
      Gruppen-Fuß "Blech"
      Gruppen-Kopf "Gewinde"
         Gruppen-Kopf "Flach"
            Details (8)
            Details (9)
   Seiten-Fuß "Seite 2"
   Seiten-Kopf "Seite 3"
            Details (10)
         Gruppen-Fuß "Flach"
         Gruppen-Kopf "Rund"
            Details (11)
            Details (12)
   Seiten-Fuß "Seite 2"
   Seiten-Kopf "Seite 3"
         Gruppen-Fuß "Rund"
         Gruppen-Kopf "Senk"
            Details (13)
            Details (14)
         Gruppen-Fuß "Senk"
      Gruppen-Fuß "Gewinde"
      Gruppen-Kopf "Holz"
         Gruppen-Kopf "Flach"
            Details (15)
   Seiten-Fuß "Seite 2"
   Seiten-Kopf "Seite 3"
            Details (16)
         Gruppen-Fuß "Flach"
         Gruppen-Kopf "Senk"
            Details (17)
            Details (18)
         Gruppen-Fuß "Senk"
      Gruppen-Fuß "Holz"
   Seiten-Fuß "Seite 2"
Report-Präambel

Durch die Einrückungen kann man schön sehen, wie beim Wechsel einer Gruppe die Füße bzw. Köpfe der eingeschlossenen Gruppen reagieren. Sie reagieren ganz ähnlich wie frühe mechanische Addiermaschinen, die auch mit eine kleinen Mitnehmernase die jeweils nächste Ziffer beim Überlaufen mitnahmen - wem auch immer diese Parallele etwas sagt.

Die Anker Definition oder wie komme ich an die Daten…​

FIXME Teilweise veraltet und unvollständig, sollte angepasst bzw. aktualisiert und ergänzt werden.

Jetzt, nachdem wir wissen, wie Report-Generatoren normalerweise arbeiten, kommen wir nun zum MyTISM-eigenen Ansatz. Vielleicht vorab ein paar Worte zum Warum. Also warum hat MyTISM einen eigenen Ansatz für den Reportgenerator? Könnte es nicht einfach direkt auf die SQL-Datenbank zeigen und ein Report-Tool von der Stange benutzen? Natürlich. Das geht sogar trotzdem; und dank der 1:1-Benennung der Felder in der Datenbank fällt das ausgebufften Report-Bauern auch ausgesprochen leicht. Die Sache hat aber einen Haken: Was, wenn MyTISM sein Mapping verändert (spätere Versionen von MyTISM werden das tun um die Performance zu erhöhen, je nach Inhalt der Datenbank)? Dann sind alle SQL-Befehle kaputt und die Reports sind dahin; oder man muß auf diese Möglichkeit von MyTISM verzichten und vielleicht auf ein ganzes Stück Performance verzichten. Außerdem ist man natürlich beschränkt auf die Funktionalität eines SQL-Servers und kann virtuelle Eigenschaften der Objekte in MyTISM nicht nutzen - Unternehmensregeln, Kennzahlen etc sollten ja schon konstistent aus allen Sichten der Anwendung kommen, oder nicht?

Nun ist MyTISM objektorientiert, das heißt zunächst einmal, daß wir uns das "auseinandernehmen" in Spalten sparen können. Wir müssen uns nur auf bestimmte Schlüsselwerte wie die Gruppierungsausdrücke und Sortierattribute konzentieren und müssen nicht jedes Feld das irgendwo im Report vorkommt, schon beim Datensammeln mit einsammeln (die Aliteration soll nur die Müßigkeit dieses Unterfangens unterstreichen…​).

Das Kernwerkzeug dieser Anstrengung ist die Ankerdefinition. Sie legt fest, an welchen Objekttypen ein Report verankert wird; das heißt:

  • Welchen Typs ist das/die übergebenen Objekte?

  • Welche Relationen werden aufgefaltet?

  • Wie wird sortiert?

Zunächst zur Form. Die Ankerdefinition ist ein klitzekleiner Schnipsel XML, sieht also genauso aus und verhält sich auch genauso, wie der Formular-, Lesezeichen- und Schablonen-Code aus dem Rest des Solstice-Clients. Das dürfte schon einmal keinen Kulturschock mehr auslösen…​ :-)

Der Typ definiert sich einfach als einer der Objekttypen, die im Schema verzeichnet sind; die Namen sind allgemein de.kundenprojektkürzel.bo.Objekttyp oder de.ipcon.db.core.Objekttyp, also z.B. de.m.bo.Rechnung für einen Rechnungsreport. Eine Rechnung ist übrigens ein gutes Beispiel für einen Report, der keine Liste von Objekten, sondern ein einzelnes Objekt zu Papier bringt. Hier müßte also die "IstListe"-Eigenschaft des Reports auf "nein" gesetzt werden - im Unterschied zum Schraubenreport oben.

<set entity="Rechnung">
[...]
</set>

Das Auffalten der Relationen ist etwas ganz besonderes. Ein SQL-Kenner wird sofort einen Join erkennen, allerdings sehr iterativ formuliert - da MyTISM das Schema und die zugrundeliegende Datenbank selbst baut, kennt es die erforderlichen Kommandos an die Datenbank auch selbst und der Anwender kann sich die Mühe sparen. Die Notation ist simpel:

<many property="Posten" alias="P">
[...]
</many>

Was passiert jetzt? Wir nehmen uns jetzt ein einzelnes Rechnungsobjekt R:

R

Damit hätten wir genau eine "Zeile" (sofern man in MyTISM hier von Zeilen sprechen kann). Jetzt nehmen wir an, wir hätten die many-property oben definiert und die Rechnung hat 5 Posten P1 bis P5. Das sieht dann so aus:

R P1
R P4
R P3
R P2
R P5

Man sieht, die Posten "laufen" sozusagen Zeile für Zeile durch und nehmen die Rechnung quasi mit. Diese bildet plötzlich eine Gruppe. Das kann man auch nochmal machen; nehmen wir an, die Posten haben eine many-property "Seriennummern", dann würde eine innerhalb der many-Klammer "Posten" eingeschlossene Definition

<many property="Seriennummern" alias="SN">
[...]
</many>

möglicherweise zu folgendem Ergebnis führen:

R P2 SN1
R P2 SN2
R P1 SN3
R P1 SN1
R P1 SN2
...

Sie sehen, es bilden sich automatisch Gruppen an den Auffaltpunkten. Aber das ist alles noch nicht notwendigerweise sortiert. Das kommt jetzt im kompletten Beispiel:

<set entity="Rechnung">
  <many property="Posten" alias="P">
    <sort ascending="true" byProperty="Position">
  </many>
</set>

Das Ergebnis:

R P1
R P2
R P3
R P4
R P5

Jetzt ist alles sortiert und so aufbereitet, daß der Report funktioniert. Wir sind also jetzt bereits fertig mit der Ankerdefinition. Aber wie kommen wir jetzt an die Felder? Das ist der Clou: Alle n-1 Relationen und deren Attribute sind direkt zugreifbar, ohne weitere Definition. Das macht das Schema in MyTISM. Wir müssen nur wissen, was wir wollen; wie MyTISM das aus der Datenbank holt, ist uns erst einmal egal.

In der Benennung ist lediglich der Punkt "." durch einen "_" auszutauschen, außerdem können die Alias-Namen als Vereinfachung verwendet werden, damit die Namen nicht zu lang werden:

Nummer
Adressat_StandardKontakt_Anschrift_Strasse (aus dem CRM-Schema)
P_Artikel_Listenpreis
P_Einzelpreis
P_Gesamtpreis
P_Position

Man kann in MyTISM also die Matrix quasi überspringen und muß nur das eigentliche "Gerippe", also die Gruppen und Sortierungen in der Ankerdefinition angeben; ein komplexer SQL-Query entfällt ersatzlos. Es gibt auch noch weitere Methoden, um noch komplexere Szenarien abzubilden, wie eingebettete OQL-Queries, Script-Schnipsel oder auch virtuelle Properties; diese werden aber nicht mehr in diesem Dokument beschrieben.

Um diese Eigenschaften jetzt in Ausdrücken ("Expressions") innerhalb eines Reports zu verwenden, muß eine Feld-Klammer $F\{} drumherum. Das sieht dann so aus:

$F{P_Position}
$F{Nummer}

Es gibt zwei weitere Klammern: Die Parameter-Klammer $P\{} und die Variablen-Klammer $V\{}. Die Parameter-Klammer wird verwendet, um dem Report mitgegebene Parameter auszugeben, wie z.B. "Einkaufspreise mitdrucken", "mit Unterschrift" oder "mit Faxlogo". Diese werden im Report als Parameter deklariert. Variablen sind Report-interne Werte, die nicht aus den eigentlichen Daten stammen, wie Seitenzahlen oder Zwischensummen. Diese können ebenfalls direkt im Report definiert werden.

Ein solcher Ausdruck ist eigentlich ein Stück Programmcode, der als Ausdruck benutzt wird. Das läßt natürlich viel Spielraum für diverse Feinheiten, wie das aneinanderhängen von Feldern:

"RG-Nr"+$F{Nummer}
"Seite"+$V{PAGE_NUMBER}
$F{Familienname}+", "+$F{Rufname}
new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date())

Die letzten Beispiele zeigen aber auch die Grenzen dieser Sache auf: Komplexe Formatierungsmethoden, wie "wenn Rufname leer ist, mach das ',' auch mit weg" sind aufwendig zu definieren. Dafür hat MyTISM aber das CBOFormat im Petto.

Das CBOFormat und seine Verwendung im Report

FIXME Teilweise veraltet und unvollständig, sollte angepasst bzw. aktualisiert und ergänzt werden.

Es gibt noch eine weitere Möglichkeit, Objekteigenschaften innerhalb eines Reports anzusprechen und etwas eleganter miteinander zu verbinden. Das CBOFormat. Seine Möglichkeiten haben wir bereits im Kapitel CBOFormat besprochen.

Damit allerdings der Reportgenerator auch etwas mit dem CBOFormat anfangen kann, muss dass ganze in Form eines Feldes verpackt werden.

$F{Objektname}.describe("Eigenschaft1")
$F{Objektname}.describe("Eigenschaft1(', 'Eigenschaft2)")
$F{Objektname}.describe("Eigenschaft1' 'Eigenschaft2")

Es ist natürlich möglich, auf das "HauptBO" eines Reports zuzugreifen, indem man ein Feld namens "THIS" mit dem Typ "java.lang.Object" anlegt. Das selbe empfehle ich mit allen many-relationalen Aliasen zu machen, und ansonsten nur mit dem CBOFormat zu arbeiten. Dessen Flexibilität machen das Reportdesign sehr leicht und fehlerunanfälliger - es entfällt halt die ständige Aktualisierung der Feld-Definition vor deren Benutzung im Report. Anstatt dann folgendes zu tun:

<field name="Familienname" class="java.lang.String"/>
<field name="Rufname" class="java.lang.String"/>
<field name="Titel" class="java.lang.String"/>
<field name="Geburtstag" class="java.util.Datetime"/>
[...]
<textFieldExpression class="java.lang.String">$F{Familienname}</textFieldExpression>
[...]
<textFieldExpression class="java.lang.String">$F{Rufname}</textFieldExpression>
[...]
<textFieldExpression class="java.lang.String">$F{Titel}</textFieldExpression>
[...]
<textFieldExpression class="java.lang.Datetime">$F{Geburtstag}</textFieldExpression>
[...]

Kann man folgendes tun:

<field name="THIS" class="java.lang.Object"/>
[...]
<textFieldExpression class="java.lang.String">$F{THIS}.describe("Familienname")</textFieldExpression>
[...]
<textFieldExpression class="java.lang.String">$F{THIS}.describe("Rufname")</textFieldExpression>
[...]
<textFieldExpression class="java.lang.String">$F{THIS}.describe("Titel")</textFieldExpression>
[...]
<textFieldExpression class="java.lang.String">$F{THIS}.describe("Geburtstag")</textFieldExpression>
[...]

Auf ein Beispiel für bedingte Formatierung verzichte ich an dieser Stelle, ich glaube jeder hat eine ungefähre Vorstellung davon, wie immens groß der Unterschied dann wäre.

  1. Keine Felder außer dem Stützpunkten müssen definiert werden

  2. Keine Synchronität zwischen definierten Feldern und benutzten Feldern

  3. Flexibilität durch Features von CBOFormat (bedingte Formatierung)

Einziger Nachteil ist die Beschränkung auf Stringwerte - in gewissen Situationen kann man dann aber immer noch auf die herkömmliche Methodik zurückgreifen.

Der Aufbau eines Reports

FIXME Teilweise veraltet und unvollständig, sollte angepasst bzw. aktualisiert und ergänzt werden.

Die Sprache, in der Reports für MyTISM geschrieben werden, heisst XML. ReportDefinitonen liegen in Form von Textdateien vor, die mit jedem beliebigen Texteditor bearbeitet werden können. Mit einem Texteditor ist das ganze allerdings sehr mühselig und so richtig sehen, was man da tut, kann man auch nicht.

Aus diesem Grund haben wir uns dazu entschlossen, einen grafischen Editor zu verwenden, der speziell zum erstellen von Reports gemacht wurden. Er heisst „iReport“. In den folgenden Erläuterungen gehen ich von den Möglichkeiten des Editors iReport aus.

Ein neuer Report und seine Eigenschaften

Mit File - New Document oder der Schaltfläche wird ein Neuer Report erstellt. Folgender Dialog öffnet sich: Darin werden zuerst diverse Report Eigenschaften definiert. z.B: Report Name: Jeder Report muss einen Namen besitzen, dieser darf keine Leerzeichen beinhaltet. Seitengrösse: Entweder kann man hier ein Vordefiniertes Seitenformat auswählen oder selbst die Seitengrösse angeben. Seitenausrichtung: Auswahl zwischen Hoch- oder Querformat Auf der Registerkarte „Page Margin“ Seitenränder: Definition der Ränder für oben, unten, links und rechts

Trouble-Shooting

Seitenwechsel / Überlappende Felder / "wachsende" Felder bei dynamischem

Text

Damit sich die Grösse von Textfeldern an den variablen Inhalt anpasst, muss "isStretchWithOverflow" auf "true" gesetzt werden.

Damit nachfolgende Felder sich automatisch nach unten verschieben, muss dort "positionType" auf "float" gesetzt werden (andere mögliche Werte sind "relativeToTop", "relativeToBottom" )

Damit beim wachsenden Feldern ein Band nicht komplett auf die nächste Seite wandert, kann man "isSplitAllowed" auf "true" stellen (der Default ist "false"). Dann entscheidet der Report anhand des verbleibenden Platzes wo er das Band umbricht.

Um den Band-Umbruch zu beeinflussen, kann man am Textfeld einen Wert für "minHeightToStartNewPage" definieren.

(Noch) Ungeordnetes

FIXME Hier einige noch ungeordnete Notizen, die später mal "sauber" in die Dokumentation integriert werden sollen.

addVirtualProperties in Reports

Definieren z.B. wie:

<set entity="StueckListe">
  <addVirtualProperty name="VorhandeneZusatzstoffeAlsString" entity="StueckListe">
    <getter>de.ipcon.tools.misc.join( getVorhandeneZusatzstoffe().values() )</getter>
  </addVirtualProperty>
</set>

Achtung: Aufruf in textFieldExpressions ohne "get":

Richtig:
<textFieldExpression   class="java.lang.String"><$F{THIS}.getVorhandeneZusatzstoffe().isEmpty() ? "-" : $F{THIS}.VorhandeneZusatzstoffeAlsString</textFieldExpression>
Falsch:
<textFieldExpression   class="java.lang.String"><$F{THIS}.getVorhandeneZusatzstoffe().isEmpty() ? "-" : $F{THIS}.getVorhandeneZusatzstoffeAlsString()</textFieldExpression>

Sonst gibt es Fehlermeldung, dass die Methode nicht existiert.

Benachrichtigungen

Grundlagen

MyTISM beinhaltet ein flexibles und mächtiges Benachrichtigungssystem, mit dessen Hilfe man Benutzern Informationen zukommen lassen und sie über Ereignisse informieren kann.

Mögliche Wege für Benachrichtigungen sind z.Zt. per e-Mail und (per Nachrichtenfenster) in Solstice; Benachrichtigung per Fax, per Skype oder über andere Instant-Messaging-Protokolle, etc. können bei Bedarf ebenfalls implementiert werden. Ausserdem kann jeder Benutzer die für ihn angefallenen Benachrichtigungen direkt per Lesezeichen in Solstice ansehen.

Vorbereitung und Konfiguration

Benachrichtigungssystem-Lizenz einspielen

Das Benachrichtigungssystem ist eine optionale Erweiterung des Standard-MyTISM-Systems. Um es aktivieren und nutzen zu können, müssen Sie zuerst eine gültige Benachrichtigungssystem-Lizenz erworben und auf dem Server eingespielt haben.

Benachrichtigungssystem (de)aktivieren

Das Benachrichtigungssystem wird - wenn eine entsprechende Lizenz vorhanden ist - automatisch aktiviert, d.h. es ist keine besondere Aktivierung nötig. Ggf. müssen allerdings Angaben zu Verschlüsselung und Signatur und zur zu benutzenden Mailer-Konfiguration eingetragen werden.

Sollten sie das Benachrichtigungssystem - aus welchen Gründen auch immer - deaktivieren wollen, müssen Sie in der Datei mytism.ini im Abschnitt Notifications den Schalter activateNotifications auf "never" setzen. Sollte auch der entsprechende Abschnitt noch nicht existieren, fügen Sie ihn einfach ein:

[Notifications]
activateNotifications=never

Wenn Sie synchronisierende Server benutzen, muss zum Deaktivieren der Schalter activateNotifications auf allen Servern auf never gesetzt werden. Server bei denen diese Einstellung nicht gemacht wurde, erzeugen ansonsten weiterhin Benachrichtigungen.

Achten Sie auch darauf, dass nicht der umgekehrte Fall eintritt und einige Server fälschlicherweise activateNotifications=never eingetragen haben, obwohl das Benachrichtigungssystem eigentlich aktiv sein soll.

Diese Einstellung wird vom Server permanent überwacht und kann zur Laufzeit geändert werden. Das Benachrichtigungssystem wird dann automatisch je nach Einstellung gestartet (wobei die Konfigurationsdaten und Schlüsseldatei neu eingelesen werden) oder gestoppt.

Erzeugung / Loggen von Benachrichtigungsversendungen

Es ist möglich zu konfigurieren, in welchen Fällen Benachrichtigungsversendungen (die Log-Objekte, die genau angeben ob oder ob nicht Benachrichtigungen an gegebene Empfänger versendet werden konnten und ggf. was das Problem war) angelegt werden.

Konfiguration in der mytism.ini:
[Notifications]
versendungenLogLevel=success,failure,softfailure

Erlaubt sind ein leerer Eintrag (dann wird nie eine Benachrichtigungsversendungen angelegt) oder beliebige Kombinationen aus den obigen drei Werten (Groß-/Kleinschreibung, Reihenfolge und Leerzeichen zwischen den Werten sind egal).

success

Erfolgreiche Versendungen werden protokolliert.

failure

Versendungen, bei denen es ein "echtes" Problem gab (z.B. Mailserver nicht erreichbar) werden protokolliert.

softfailure

Versendungen, bei denen es ein untergeordnetes Problem gab - z.Zt. nur wenn eine Solstice-Benachrichtigung nicht ausgeliefert werden konnte, weil der entsprechende Benutzer nicht angemeldet war - werden protokolliert.

Standard (wenn gar kein versendungenLogLevel-Eintrag existiert) ist succes,failure, d.h. es werden für Alles - außer fehlgeschlagene Solstice-Benachrichtigungen aufgrund nicht angemeldeter Benutzer - Benachrichtigungsversendungen angelegt.

Wenn man die Konfiguration im laufenden Betrieb ändern will reicht es bisher leider nicht, den Wert einfach zu ändern (wie z.B. bei der Konfiguration der Einstellungen für e-Mail). Es muss das Benachrichtigungssystem - wenn es denn schon läuft - erst deaktiviert und dann wieder aktiviert werden (also erst activateNotifications=never, speichern und dann activateNotifications=if_possible oder activateNotifications=mandatory und wieder speichern).

Die Erzeugung von Benachrichtigungsversendungen lässt sich natürlich für verschiedene Nodes unterschiedlich konfigurieren; so wäre es z.B. denkbar softfailures auf Nodes, auf denen normalerweise niemand angemeldet ist, zu deaktivieren; auf Nodes die normalerweise regen Benutzerverkehr haben aber aktiviert zu lassen.

Verschlüsselung und digitale Signatur

Von MyTISM verschickte Benachrichtigungen - z.Zt. gilt das nur für e-Mails - können nach dem OpenPGP-Standard verschlüsselt und/oder mit einer digitalen Signatur versehen werden.

Important
Damit OpenPGP-Verschlüsselung und -Signaturen korrekt genutzt werden können müssen die "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" auf dem System eingespielt werden. Diese können bei Oracle heruntergeladen werden (http://www.oracle.com/technetwork/java/javase/downloads/index.html, ganz am Ende der Seite) und müssen dann entpackt und in das Verzeichnis der JRE (je nach System /usr/lib/java/…​ oder /usr/lib/jvm/…​, o.Ä.), Unterverzeichnis /lib/security kopiert werden.

Ob Benachrichtigungen verschlüsselt und/oder signiert werden, kann bei jedem Alarm bzw. Benachrichtigungsauftrag konfiguriert werden; außerdem kann jeder Benutzer seine bevorzugten Einstellungen wählen. Wenn bei keinem dieser Objekte explizite Einstellungen dafür eingetragen wurden, werden die Standardvorgaben des Systems verwendet, die hier konfiguriert werden:

[Notifications]
encrypt=if_possible
sign=if_possible
privateKeyFile=/.<project>/.gnupg/secring.gpg

Die Angabe bei encrypt gibt an, ob Benachrichtigungen standardmässig verschlüsselt werden sollen. Erlaubte Werte sind

never

Standardmässig sollen Benachrichtigungen nie verschlüsselt werden, selbst wenn entsprechende öffentliche Schlüssel bei den Benutzern hinterlegt sind.

if_possible

(Der Standardwert, wenn überhaupt nichts konfiguriert wird) Standardmässig sollen Benachrichtigungen verschlüsselt werden, wenn für den Empfänger der Benachrichtigung ein entsprechender öffentlicher Schlüssel hinterlegt ist; ist kein Schlüssel verfügbar wird unverschlüsselt gesendet.

mandatory

Standardmässig sollen Benachrichtigungen immer verschlüsselt werden; wenn für den Empfänger der Benachrichtigung kein entsprechender öffentlicher Schlüssel hinterlegt ist, wird der Fehler im Server-Log vermerkt und die Benachrichtigung nicht gesendet.

Die Angabe bei sign gibt an, ob Benachrichtigungen standardmässig digital signiert werden sollen. Erlaubte Werte sind

never

Standardmässig sollen Benachrichtigungen nie signiert werden, selbst wenn ein entsprechender privater Schlüssel für das System hinterlegt ist.

if_possible

(Der Standardwert, wenn überhaupt nichts konfiguriert wird) Standardmässig sollen Benachrichtigungen signiert werden, wenn für das System ein privater Schlüssel hinterlegt ist; ist kein solcher Schlüssel hinterlegt, werden Benachrichtigungen unsigniert gesendet.

mandatory

Standardmässig sollen Benachrichtigungen immer signiert werden. Wenn dieser Wert angegeben ist und kein privater Schlüssel hinterlegt ist, loggt der Server eine Fehlermeldung und das Benachrichtigungssystem ist nicht verfügbar.

Die Angabe privateKeyFile gibt den Pfad zur Datei mit dem privaten Signaturschlüssel für das System an. Der Name und Pfad der Datei ist im Prinzip frei wählbar, sinnvoll ist es aber sich an den von GnuPG vorgegebenen Standard (wie im obigen Beispiel) zu halten.

Note
Lediglich der private Schlüssel muss hinterlegt werden und auch nur, falls von MyTISM versandte Mails signiert werden sollen. Der öffentliche Schlüssel ist nicht notwendig (da MyTISM ja keine verschlüsselten Mails empfangen und entschlüsseln muss).

Der private Schlüssel wird bei jedem (Neu-)Start des Benachrichtigungssystems neu eingelesen.

Kurzanleitung Erzeugung und Hinterlegung Schlüssel

Note
Zum Erzeugen und Verwalten von OpenPGP-Schlüsseln via GnuPG gibt es viele Anleitungen im Netz; hier nur eine Kurzübersicht mit einigen MyTISM-spezifischen Anmerkungen.

Unter Linux kann dieser - bei installiertem GnuPG - z.B. mit dem Befehl gpg --gen-key erzeugt werden:

Important
Hier bei allen gpg-Aufrufen immer den --homedir-Parameter angeben! Ansonsten werden alle Dinge im Schlüsselring des aktuellen Benutzers gemacht, was nicht sinnvoll ist. Statt dem im Beispiel verwendeten /tmp/gnupg kann natürlich auch ein anderes Verzeichnis angegeben werden.
  1. Verzeichnis /tmp/gnupg muss existieren, ggf. mit mkdir /tmp/gnupg erzeugen.

  2. gpg --homedir /tmp/gnupg --gen-key

  3. Art: 1 / RSA+RSA ist ok.

  4. Länge: Je nach Paranoia Standard von 2048 bestätigen oder 4096 eingeben.

  5. Gültigkeit: 0 (verfällt nie) ist normalerweise ok.

  6. "Richtig?" bestätigen.

  7. Namen eingeben: Sinnvoller, erkennbarer Name für das MyTISM-System.

  8. e-Mail-Adresse eingeben: Dies sollte die e-Mail-Adresse sein, die als Absender in vom MyTISM-System verschickten Mails eingetragen wird. Oft also die unter from eingetragene Adresse. Falls die Mails mit unterschiedlichen Adressen versandt werden, können diese Adressen später ergänzt werden (s.u.).

  9. Kommentar: Keinen eingeben, leer lassen.

  10. "Fertig" auswählen

  11. Passphrase: Keine Passphrase eingeben. Da das MyTISM-System bisher keine Passphrase zum Zugriff auf den Schlüssel angeben kann, darf dieser nicht mit einer geschützt werden. Ggf. wird diese Funktionalität später noch nachgebaut, aber normalerweise sollte die Schlüsseldatei sowieso entsprechend geschützt werden, so dass niemand Zugriff darauf bekommt.

  12. Warten bis Schlüssel fertig erzeugt wurde.

  13. Die erzeugte Datei /tmp/gnupg/secring.gpg an der Stelle, die in der mytism.ini unter privateKeyFile dafür angegeben wurde, hinterlegen.

  14. Öffentliche Schlüsseldatei den Empfängern zur Verfügung stellen (Via Mail schicken, auf Schlüsselserver hochladen, …​) - Export mittels gpg --homedir /tmp/gnupg --export --armor <schlüssel-id>.

  15. (Optional aber sinnvoll; weitere Infos dazu im Netz) Auf anderem Wege (Telefon, persönliches Treffen) noch die Fingerprints vergleichen und ggf. den Schlüssel von den Empfängern signieren lassen.

Weitere e-Mail-Adresse zu Schlüssel hinzufügen

  1. gpg --homedir /tmp/gnupg --edit-key <schlüssel-id>

  2. Am GPG-Kommando-Prompt: adduid

  3. Name eingeben (kann einfach der selbe sein, wie ursprünglich eingegeben).

  4. e-Mail-Adresse eingeben: Gewünschte, weitere e-Mail-Adresse eingeben.

  5. Kommentar: Keinen eingeben, leer lassen.

  6. "Fertig" auswählen

  7. Am GPG-Kommando-Prompt: save

  8. Aktualisierte private Schlüsseldatei im MyTISM-System hinterlegen.

  9. Aktualisierte öffentliche Schlüsseldatei den Empfängern zur Verfügung stellen (Via Mail schicken, auf Schlüsselserver hochladen, …​) - Export mittels gpg --homedir /tmp/gnupg --export --armor <schlüssel-id>.

e-Mail-Einstellungen

Caution
Alle unten angegebenen Werte - mit Ausnahme der Daten bei authMethod, useTLS, useInlinePGP und suppressMsgID - sind nur Beispielwerte! Für Ihr konkretes Projekt müssen Sie natürlich die für die zu benutzenden e-Mail-Konten passenden Werte eintragen. Welche das dann sind kann diese Doku allerdings nicht beantworten, wenden Sie sich im Zweifelsfall an Ihren für e-Mail verantwortlichen Administrator :-)

Damit Benachrichtigungen per e-Mail verschickt werden können, muss vor dem Start von MyTISM konfiguriert werden, über welche(n) Mailserver und mit welchen Einstellungen Mails verschickt werden sollen.

Der einfachste Fall ist der, dass genau ein MyTISM-Knoten immer über genau einen Mailserver Mails verschickt; dieser Fall wird daher zuerst beschrieben. Komplexere Konfigurationsszenarien finden Sie im Abschnitt Konfiguration mehrerer Mailserver zur Versendung.

Die Konfiguration für einen Mailserver ist in der Datei mytism.ini in einem Abschnitt "Mailer" zusammengefasst. Dort wird u.A. angegeben, über welchen Mailserver die Mails verschickt werden sollen und welche e-Mail-Adresse als Standard-Absender der Benachrichtigungsmails benutzt werden soll.

Mailserver zur Versendung

Die Adresse des Mailservers, über den ausgehende e-Mails versendet werden sollen wird bei smtpHost angegeben:

[Mailer]
smtpHost=mail.example.com

Soll die Kommunikation mit dem Mailserver über einen bestimmten Port laufen, so kann dieser getrennt mit einem Doppelpunkt direkt hinter dem SMTPHost angegeben werden, also z.B.:

[Mailer]
smtpHost=mail.example.com:587

Wenn der Mailserver nur verschlüsselte Verbindungen erlaubt kann dies mit useTLS aktiviert werden:

[Mailer]
smtpHost=mail.example.com
useTLS=1

useTLS=0 TLS ist der Standard, d.h. verschlüsselte Verbindungen sind erst einmal deaktiviert.

Standard-Absenderadresse

Die Angabe bei from wird genauso wie sie hier angegeben wird in den "From"-Header von vom Benachrichtigungssystem verschickten e-Mails übernommen; im Prinzip können Sie dort also jeden beliebigen Text angeben:

from=mytism@example.com
from=MyTISM-System <mytism@example.com>
from=Ihr werdet mich nie finden!

(Wobei der letzte Eintrag zwar technisch möglich ist, in der Realität aber natürlich nicht viel Sinn macht ;-))

Außerdem ist zu beachten, dass es sich bei der Angabe bei from nur um den Standardwert handelt. Wenn für Benachrichtigungen explizit ein eigener Absender angegeben wurde, wird stattdessen die Email-Adresse dieses Absenders eingetragen.

Bei vom Alarmsystem verschickten e-Mails wird für Alarme, die keinen explizit gesetzten Verantwortlichen haben z.B. obige Angabe verwendet, da der Alarmsystem-Benutzer (der Standard-Absender dort) keinen Wert bei Email eingetragen hat.

Authentifizierung am Mailserver

Die meisten Mailserver verlangen eine Authentifizierung, bevor sie das Verschicken von Mails zulassen. Von MyTISM unterstützt werden zur Zeit die Methoden "POP before SMTP" (bei der die Authentifizierung am Mailserver durch ein Pseudo-Abholen von Mail über das POP3-Protokoll erfolgt) und SMTP Auth (Authentifizierung direkt für das Versenden). Ob und wenn ja welche Authentifizierungsmethode benutzt werden soll wird mittels des Attributes authMethod festgelegt.

Keine Authentifizierung notwendig/benutzen

Sollte der Mailserver keine Authentifizierung verlangen, setzen Sie none als Wert:

[Mailer]
from=mytism@example.com
smtpHost=mail.example.com
authMethod=none

Alternativ können Sie in diesem Fall den Eintrag auch ganz weglassen, da dies die Standardeinstellung ist, die bei Fehlen des Eintrages automatisch benutzt wird.

Note
Der früher übliche Wert 0 wird auch noch unterstützt, sollte aber nicht mehr verwendet und falls vorhanden ersetzt werden.
POP before SMTP

Authentifizierung per "POP before SMTP" stellen Sie mit dem Wert pop_before_smtp ein; in diesem Fall müssen Sie ausserdem noch den Benutzer- bzw. Konto-Namen und das dazugehörige Passwort für das e-Mail-Konto, das "abgeholt" werden soll, angeben:

[Mailer]
from=mytism@example.com
smtpHost=mail.example.com
authMethod=pop_before_smtp
username=pop3kontoname
password=pop3passwort

Evtl. für dieses Konto vorhandene Mails werden allerdings nicht wirklich abgeholt; es wird lediglich eine POP3-Anmeldung durchgeführt.

Für die POP3-Anmeldung wird im Normalfall derselbe Rechner kontaktiert, der als smtpHost eingetragen ist. In manchen Fällen kann es aber auch sein, dass zum Versenden und Abholen von Mails unterschiedliche Rechner eingetragen werden müssen. In diesem Fall können Sie das Attribut POPBeforeSMTPHost dazu benutzen, anzugeben, an welchem Rechner die POP3-Anmeldung erfolgen soll:

[Mailer]
from=mytism@example.com
smtpHost=mail.example.com
authMethod=pop_before_smtp
username=pop3kontoname
password=pop3passwort
POPBeforeSMTPHost=pop.example.com
Note
Der früher übliche Wert 1 wird auch noch unterstützt, sollte aber nicht mehr verwendet und falls vorhanden ersetzt werden.
SMTP Auth

Authentifizierung per SMTP Auth wählen Sie mit dem Wert smtp_auth; auch in diesem Fall ist ein Benutzername und ein Passwort notwendig:

[Mailer]
from=mytism@example.com
smtpHost=mail.example.com
authMethod=smtp_auth
username=smtpauthname
password=smtpauthpasswort
Note
Der früher übliche Wert 2 wird auch noch unterstützt, sollte aber nicht mehr verwendet und falls vorhanden ersetzt werden.

Format für Verschlüsselung und digitale Signatur

Benutzer können konfigurieren, ob sie OpenPGP-verschlüsselte oder signierte e-Mails im (veralteten, aber von einigen Mailern noch benutzten) "Inline"-Format oder im neuen MIME-Format bekommen wollen. Für Benutzer, die diese Einstellung nicht explizit konfiguriert haben, können Sie einen System-weiten Standard vorgeben.

Standardmässig Inline-Format nicht benutzten, e-Mails im MIME-Format generieren:
[Mailer]
-- ...
useInlinePGP=0

Diese Einstellung wird auch verwendet, wenn kein useInlinePGP-Eintrag existiert.

Standardmässig Inline-Format benutzten:
[Mailer]
-- ...
useInlinePGP=1

Generierung des "Message-ID"-Headers unterdrücken

Im Normalfall wird für die versendeten e-Mails ein "Message-ID"-Header generiert der u.A. das Sendedatum und den Namen des Servers, von dem aus die Mail verschickt wurde, beinhaltet. Soll für die e-Mails kein solcher Message-ID generiert werden, kann diese Generierung unterbunden werden

Standardmässig wird ein Message-ID-Header generiert:
[Mailer]
-- ...
suppressMsgID=0

Diese Einstellung wird auch verwendet, wenn kein suppressMsgID-Eintrag existiert.

Keinen "Message-ID"-Header generieren:
[Mailer]
-- ...
suppressMsgID=1
Note
Selbst wenn vom MyTISM-System kein "Message-ID"-Header in die Mail eingefügt wird kann es sein, dass ein anderer Mailserver, der die Mail im Zuge des Transports zum Empfänger weiterleitet, einen solchen Header später trotzdem einträgt.

Konfiguration mehrerer Mailserver zur Versendung

In manchen Fällen kann es notwendig sein, Mails aufgrund gewisser Kriterien (z.B. in Abhängigkeit von der Mailadresse des Empfängers) über unterschiedliche Mailserver zu verschicken. Dies kann erreicht werden, indem man in der mytism.ini mehr als eine Mailer-Konfiguration hinterlegt und aus diesen dann je nach zu versendender e-Mail mittels definierter e-Mail-Routing-Regeln die passende auswählt.

[Mailer.Alt1]
from=mytism@example.com
smtpHost=mail.example.com

[Mailer.Alt2]
from=mytism@someOtherServer.org
smtpHost=smtp.someOtherServer.org
useTLS=1

Die Benamsung der Sektionen muss nach dem Schema Mailer.POSTFIX erfolgen, wobei das POSTFIX beliebig gewählt werden kann. Außerdem ist der Name “Mailer” zugelassen.

Mittels e-Mail-Routing-Regeln können ausgehende Benachrichtigungen aufgrund der Empfänger, des Absenders oder des Betreffs gefiltert und dann explizit mittels einer ebenfalls dort referenzierten Mailer-Konfiguration über einen bestimmten Mailserver verschickt werden.

Die Filter können als reguläre Ausdrücke angegeben werden; alle müssen zutreffen, damit die Regel für eine e-Mail-Versendung benutzt wird. Ausnahme: Falls eine Mail an mehrere Empfänger gehen soll, muss der Empfänger-Filter auf mindestens einen davon zutreffen.

Eine neue e-Mail-Routing-Regel.

Beim Starten bzw. bei Änderungen an der mytism.ini wird überprüft, ob alle in e-Mail-Routing-Regeln angesprochenen Mailer-Konfigurationen (für diesen Knoten) auch wirklich vorhanden sind. Wenn nicht wird gewarnt und eine Versendung von e-Mails ist bis zu einer Korrektur der mytism.ini nicht möglich.

e-Mail-Versendung bei mehreren Knoten

e-Mail-Routing-Regeln sind immer für einen bestimmten Knoten bestimmt. Wenn eine Regel für eine Benachrichtigung zutrifft wird die Versendung dieser Benachrichtigung nur von dem in der Regel spezifizierten Server vorgenommen.

Durch Definition von Mailer-Konfigurationen auf mehreren Servern und entsprechende e-Mail-Routing-Regeln, die jeweils eine passende davon auswählen, ist es somit möglich, e-Mails je nach Situation von verschiedenen Servern (Knoten) verschicken zu lassen.

Wenn eine Benachrichtigung erzeugt wurde, arbeiten alle Knoten die vorhandenen Regeln in der Reihenfolge der angegebenen Position ab (ausgenommen CATCHALL, s.u.). Sobald eine Regel aufgrund der angegebenen Filter zutrifft, schaut jeder Knoten, ob diese Regel ihm zugewiesen ist. Wenn nein bricht der Knoten die Bearbeitung ab und macht nichts. Wenn ja generiert der Knoten eine e-Mail aus der Benachrichtigung und versendet diese unter Benutzung der in der Regel angegebenen Mailer-Konfiguration.

Es wird automatisch eine CATCHALL-Regel (ohne Filter) gebaut. Diese wird immer als letzte behandelt, falls keine der vorherigen Regeln zutraf. Die CATCHALL-Regel ist immer dem autoritativen Knoten zugewiesen und benutzt dessen Konfiguration namens “Mailer”.

Sollten mehrere Regeln mit gleicher Position existieren, werden diese in der alphabetischen Reihenfolge des Namens durchgegangen. Sollten selbst diese gleich sein, wird noch nach Erstellungsdatum sortiert.

Testmail beim Serverstart versenden

Man kann veranlassen, dass beim Starten des Servers automatisch eine Testmail an eine festgelegte e-Mail-Adresse versendet wird.

Testmail an support@oashi.com verschicken:
[Mailer]
-- ...
checkAddress=support@oashi.com

Solstice-Einstellungen

Plugin in Benutzer-Profil eintragen

Um Benachrichtigungen im Solstice-GUI-Client zu erhalten muss im Profil jeden Benutzers das folgende Plugin eingetragen sein:

<Plugin class="de.ipcon.form.notification.ClientNotificationManager" silent="yes"/>

Normalerweise enthält das Profil neu angelegter Benutzer automatisch diese Zeile. Falls jedoch die Profileinträge von Hand editiert wurden, kann es evtl. sein, dass diese Zeile gelöscht wurde. Auch bei bereits angelegten Benutzer kann diese Zeile noch nicht enthalten sein, wenn Sie vor der Einführung des Benachrichtigungssystems erzeugt wurden. In diesem Fall tragen Sie die Zeile bitte ein.

Der (optionale) Parameter silent gibt an, ob neu eintreffende Benachrichtigungen direkt geöffnet werden sollen (silent="no", die Standardeinstellung wenn der Parameter nicht angegeben wurde) oder alle neuen Benachrichtigungen erst beim Anklicken des Knopfes in der "Taskleiste" angezeigt werden sollen (silent="yes").

Leserechte für Benachrichtigungen geben

Um Benachrichtigungen anzeigen zu können, müssen Benutzer die entsprechenden Objekte natürlich laden bzw. lesen können. Alle Benutzer, für die Benachrichtigungen im Solstice angezeigt werden sollen, müssen daher Leserechte für die Benachrichtigungsklassen (jene in der Schema-Datei core-benachrichtigung.xml), insb. MyTISMBenachrichtigung, MyTISMBenachrichtigungsAuftrag, MyTISMBenachrichtigungsVorlage und MyTISM*BOEintrag haben.

Sollen Benutzer (im Solstice) auf Benachrichtigungen antworten bzw. selber welche verschicken können, müssen sie für die Klassen MyTISMBenachrichtigungsAuftrag und MyTISM*BOEintrag auch Erstellen- und Schreibrechte bekommen.

Eine weitere Konfiguration ist hier sonst nicht erforderlich. Es muss lediglich das Benachrichtigungssystem an sich auf jedem Server aktiviert sein, bei dem die dort angemeldeten Benutzer Benachrichtigungen auf diesem Wege erhalten sollen.

Einstellungen für die Benutzer

Damit ein Benutzer Benachrichtigungen empfangen kann, müssen noch ein paar spezielle Einstellungen vorgenommen bzw. BOs angelegt werden:

Einfache Konfiguration

Auf dem Reiter "Einfache Benachrichtigungskonfiguration" können Sie u.A. eine e-Mail-Adresse eintragen und angeben, ob der Benutzer Benachrichtigungen (auch) im Solstice-Client erhalten soll. Wenn Sie diese Konfigurationsfelder nutzen werden im Hintergrund automatisch entsprechende MyTISMAdressen erstellt bzw. mit den eingegebenen Werten aktualisiert.

Verschlüsselung und digitale Signatur

Hier können Einstellungen zur OpenPGP-Verschlüsselung und -Signatur vorgenommen werden, falls der Benutzer eine von den Standardeinstellungen des Systems abweichende Konfiguration nutzen möchte.

Öffentlicher OpenPGP-Schlüssel

Hier können Sie den öffentlichen OpenPGP-Schlüssel des Benutzers hinein kopieren. Dieser ist erforderlich, wenn der Benutzer verschlüsselte Mails erhalten möchte.

Mittels des Knopfes "Schlüssel aus Datei importieren" wird der Schlüssel automatisch aus der Datei /.gnupg/pubring.gpg im Home-Verzeichnis des aktuellen Linux-Benutzers eingelesen.

Caution
In gewissen Fällen wird der Schlüssel inkorrekt importiert, Grund dafür ist noch unbekannt, es scheint mit dem Format der Schlüsseldatei zusammen zu hängen.

Mittels des Knopfes "Schlüssel vom Schlüsselserver importieren" wird der Schlüssel automatisch von dem in der Einstellungenvariable pgpKeyServer angegebenen Schlüsselserver importiert.

Will verschlüsselte Benachrichtigungen

Nie = Nie verschlüsseln, selbst wenn ein öffentlicher Schlüssel verfügbar ist; Wenn möglich = Verschlüsseln wenn ein öffentlicher Schlüssel verfügbar ist, sonst unverschlüsselt senden; Zwingend = Verschlüsselung erforderlich, wenn nicht möglich Benachrichtigung nicht senden. Ansonsten: Standardeinstellung des Systems verwenden.

Will signierte Benachrichtigungen

Nie = Nie signieren, selbst wenn ein privater Schlüssel verfügbar ist; Wenn möglich = Signieren wenn ein privater Schlüssel verfügbar ist, sonst unsigniert senden; Zwingend = Signatur erforderlich. Ansonsten: Standardeinstellung des Systems verwenden.

Will e-Mails im (veralteten) Inline-Format

Ob verschlüsselte und/oder signierte e-Mails im (veralteten, aber von manchen Mailern noch benutzten) "Inline"-Format erzeugt werden sollen. Wenn nicht aktiviert wird das neuere MIME-Format verwendet; wenn "grau" wird die Standardeinstellung des Systems verwendet.

Mittels des Knopfes "Testbenachrichtigung senden" können Sie eine Testbenachrichtigung mit Standardtext an den Benutzer auslösen.

Konfiguration der Benachrichtigungsadressen und OpenPGP-Einstellungen.

MyTISMAdresse(n) manuell anlegen

MyTISMAdressen dienen dazu "Ziele" für Benachrichtigungen anzugeben. Dabei kann es sich um e-Mail-Adressen handeln (MyTISMAdresseEmail) oder eine MyTISMAdresseSolstice die für Benachrichtigung per Nachrichtenfenster im Solstice-GUI-Client steht (in gewisser Weise ein Spezialfall, da keine eigentlichen "Adressdaten" angegeben werden müssen). MyTISMAdresseFax (mit Faxnummer) steht z.Zt. ebenfalls schon zur Verfügung, leider ist die Möglichkeit Benachrichtigungen per Fax zu verschicken aber noch in Arbeit. Weitere Unterklassen, z.B. für Instant Messaging, können bei Bedarf in Zukunft hinzukommen.

Für jeden Benutzer, der Benachrichtigungen bekommen soll, muss mindestens eine Adresse angelegt werden.

Sie können die Adressen in einer bestimmten Reihenfolge anordnen, nach der sie beim Eintreffen einer Benachrichtigung abgearbeitet werden. Diese Reihenfolge wird durch das Attribut Position festgelegt.

Normalerweise wird nach dem ersten erfolgreichen Verschicken einer Benachrichtigung an eine Adresse die Bearbeitung abgebrochen, d.h. evtl. nachfolgende Adressen werden nicht mehr benutzt. Diese dienen somit nur als Ersatz/Fallback falls die vorherige(n) Adresse(n) nicht erreicht werden können. Wenn Sie bei einer Adresse allerdings das Flag WeiterAuchWennErfolgreich setzen, wird auch nach erfolgreichem Verschicken an diese Adresse weitergemacht. Damit kann erreicht werden, dass Benachrichtigungen über mehrere Kanäle versendet werden.

Manuelles Versenden von Benachrichtigungen

Note
Die erforderliche Schablone ist nur verfügbar, falls sie von Ihrem MyTISM-Administrator für Benutzer bereit gestellt wurde.

Um eine Benachrichtigung an Benutzer und/oder Gruppen zu versenden, müssen Sie einen neuen MyTISMBenachrichtigungsAuftrag anlegen. Es gibt hier zwei Varianten, solche mit und solche ohne Vorlage. Eine Schablone zum Anlegen gibt es bisher nur für Aufträge ohne Vorlage, da Aufträge mit Vorlage eher für automatisch generierte Benachrichtigungen gedacht sind.

In der Schablone "MyTISM-Benachrichtigungsauftrag ohne Vorlage (Vorgebaut; Versenden)" können Sie folgende Werte angeben:

Für Benutzer bzw. Für Gruppe(n)

Wer soll die Benachrichtigung erhalten?

Betreff

Der Betreff der Nachricht.

Text

Die eigentliche Mitteilung.

Zeichensatz

Hier können Sie festlegen, in welchem Zeichensatz die Nachricht gehalten ist; wird normalerweise nicht benötigt, leer lassen.

Priorität

Hier können Sie die Wichtigkeit/Priorität der Nachricht festlegen; wird normalerweise nicht benötigt, leer lassen.

Anhänge

Hier können Sie Objekte an die Nachricht anhängen, die bei Anzeige im Solstice-Client direkt zugreifbar sind.
Damit diese Objekte als Anhänge von als e-Mail versendeten Benachrichtigungen genutzt werden können, müssen sie bestimmte Bedingungen erfüllen (fortgeschrittenes Thema).

Sobald Sie den Auftrag speichern, wird dieser vom Benachrichtigungssystem entgegengenommen und für jeden angegebenen Benutzer und alle Benutzer in den angegebenen Gruppen wird eine entsprechende Benachrichtigung versandt.

Benachrichtigungen einsehen

Note
Das erforderliche Lesezeichen ist nur verfügbar, falls es von Ihrem MyTISM-Administrator für Benutzer bereit gestellt wurde.

Mitglieder der Gruppe Benutzer können alle Ihre Benachrichtigungen auch im Lesezeichen "Meine Benachrichtigungen" einsehen.

Das vorgebaute Lesezeichen in dem jeder Benutzer seine Benachrichtigungen einsehen kann.

Wer kann Benachrichtigungen erhalten?

Empfänger von Benachrichtigungen können alle Objekte sein, die das Interface de.ipcon.db.core.NotificationReceiverI bzw. de.ipcon.db.core.NotificationReceiverCollectionI implementieren. Das sind im MyTISM-Grundsystem Benutzer, Gruppen und MyTISMAdressen.

Wenn zusätzliche MyTISM-Module eingebunden sind kommen weitere Objekte dazu, z.B. Personen oder Tickets.

Applikationen können ebenfalls weitere entsprechende Objekte mitbringen.

Analyse von Problemen und Fehlerbehebung

Als Administrator oder Benutzer mit entsprechenden Berechtigungen können Sie kontrollieren, ob das Benachrichtigungssystem korrekt funktioniert und Benachrichtigungen wie gewünscht versendet werden bzw. wurden.

Versendung von Benachrichtigungen kontrollieren

Um zu kontrollieren, ob, wann und an wen Benachrichtigungen versendet wurden gibt es mehrere Möglichkeiten. Neben den Ausgaben im Server-Log werden auch in der MyTISM-Datenbank entsprechende Versendungen protokolliert.

Kontrolle im Server-Log

Wenn Benachrichtigungen erstellt werden, wird immer eine Meldung ins Server-Log geschrieben:

[...]
INFO  17:53:17.173 [r-NotificationModule] BenachrichtigungsAuftragHandler createBenachrichtigungen(94)- Created Benachrichtigungen for MyTISMBenAuftragMitVorlage[12345678] (scheduled at 2016-02-03 17:53:17.012) with success for 5 receiver(s).
[...]
Important
Falls Sie mehrere MyTISM-Nodes nutzen achten Sie darauf im Log des Servers nachzusehen, auf dem das Benachrichtigungssystem aktiviert ist.

Kontrolle in der Datenbank

Über das Lesezeichen "Admins/MyTISM (Vorgebaut)/Benachrichtigungen/MyTISM-Benachrichtigungsaufträge (Vorgebaut)" können Sie sich die gespeicherten Benachrichtigungsaufträge für einen gewünschten Zeitraum anzeigen lassen.

Durch Aktivieren des Filters "Mit ''echten'' Fehlern" können Sie sich schnell einen Überblick über aufgetretene Probleme verschaffen.

  • Wählen Sie den Zeitraum, innerhalb dessen die fragliche Benachrichtigung oder Benachrichtigung versendet wurde bzw. hätten werden sollen und kontrollieren Sie, ob es entsprechende Benachrichtigungsaufträge gibt.

  • Sind diese vorhanden prüfen Sie, welche Benachrichtigungen dafür erzeugt wurden. Öffnen Sie den Auftrag und wechseln Sie auf den Reiter "Benachrichtigungen". Kontrollieren Sie, ob für alle gewünschten Empfänger eine Benachrichtigung erstellt wurde.

  • Kontrollieren Sie für alle Benachrichtigungen ob die Checkboxen in der Spalte "Erfolgreich versendet" markiert sind. Falls ja, sollte die Benachrichtigung auch erfolgreich versendet bzw. im Solstice-Client angezeigt worden sein.

  • Im Fehlerfall können Sie auf dem Reiter "Versendungen" sehen, welche Versendungen nicht erfolgreich waren und welcher Fehler dabei aufgetreten ist. Was protokolliert wird hängt hier allerdings von der Einstellung zur Protokollierung von Benachrichtigungsversendungen ab.

Benachrichtigungen für spezifische Objekte finden

Oft tritt der Fall auf, dass kontrolliert werden soll, ob eine Benachrichtigung erzeugt und/oder verschickt wurde, die sich auf ein bestimmtes Objekt bezieht.

In den meisten Anwendungsfällen sollten sie hier mit einer der beiden folgenden Methoden weiterkommen:

Methode 1
Note
Funktioniert nur, wenn das entsprechende Objekt in den Kontext des Auftrags aufgenommen oder als Anhang angegeben wurde. Dies ist normalerweise der Fall, wenn die Benachrichtigungen von einem Hinweis, BOBasiertenTermin oder einer Wiedervorlage (siehe Alarmsystem) erzeugt wurden.
  1. Ermitteln Sie die MyTISM-Id des gewünschten Objekts.

  2. Öffnen Sie das Lesezeichen /Admins/MyTISM/Benachrichtigungen/MyTISM-Benachrichtigungsaufträge (Vorgebaut)

  3. Tragen sie folgendes in das Suchfeld ein: [exists( within KontextBOs b where b.BO.Id = <objekt-id> ) or exists( within AnhangBOs c where c.BO.Id = <objekt-id> ) - <objekt-id> ersetzen Sie durch die oben ermittelte Id des Objekts.

  4. Schicken Sie die Suchanfrage ab.

Falls entsprechende Benachrichtigungsaufträge gefunden werden, können sie über diese die erzeugten Benachrichtigungen und deren Versendungen einsehen.

Methode 2
Note
Funktioniert nur, wenn Daten - am besten die MyTISM-Id - des entsprechenden Objekts in den Text der Benachrichtigung aufgenommen wurden.
Caution
Normalerweise deutlich langsamer als Methode 1!
  1. Ermitteln Sie die MyTISM-Id des gewünschten Objekts oder irgend einen anderen, möglichst eindeutigen/spezifischen Wert nach dem Sie im Text der Benachrichtigungen suchen können.

  2. Öffnen Sie das Lesezeichen /Admins/MyTISM/Benachrichtigungen/MyTISM-Benachrichtigungen

  3. Tragen sie folgendes in das Suchfeld ein: [TextFest ilike '%<suchbegriff>%' - <suchbegriff> ersetzen Sie durch die oben ermittelte Id oder den Suchbegriff für das Objekt.

  4. Schicken Sie die Suchanfrage ab.

Falls entsprechende Benachrichtigungen gefunden werden, können sie über diese deren Versendungen einsehen.

Checkliste mögliche Fehlerquellen

Note
Zur Kontrolle, um die entsprechenden unten genannten Log-Meldungen zu provozieren, können Sie das Benachrichtigungssystem "durchstarten". Ändern Sie dazu in der Datei mytism.ini den Eintrag activateNotifications auf "never" falls er auf "if_possible" oder "mandatory" steht und dann wieder zurück auf den ursprünglichen Wert.
Das Benachrichtigungssystem wurde nicht aktiviert oder bei der Aktivierung sind Fehler aufgetreten

Wenn das Logfile, in dem der Serverstart protokolliert wurde, noch verfügbar ist muss eine Meldung

[...]
INFO  11:21:29.244 [t-NotificationModule] NotificationModule doStart(118)- Notification system successfully initialized and ready.
[...]

vorhanden sein. Prüfen Sie ebenfalls die entsprechende Konfiguration in der Datei mytism.ini.

Das Benachrichtigungssystem wurde zur Laufzeit deaktiviert

Prüfen Sie die entsprechende Konfiguration in der Datei mytism.ini. activateNotifications muss auf "if_possible" oder "mandatory" stehen.

[Notifications]
activateNotifications=if_possible
Ein Handler konnte nicht gestartet werden

Das Benachrichtigungssystem ist zwar aktiv, aber ein oder mehrere Handler (wie z.B. der, welcher sich um die Versendung von Benachrichtigungen per e-Mail kümmert) konnten nicht korrekt gestartet werden. Achten Sie hier ebenfalls auf entsprechende Meldungen im Server-Log, vor der Aktivierungsmeldung des Benachrichtigungssystems.

Tipps und Tricks

Testbenachrichtigungen senden

Mittels des Knopfes "Testbenachrichtigung senden" im entsprechenden Reiter des Benutzer-Formulars können Sie eine Testbenachrichtigung mit Standardtext an den gewählten Benutzer - also z.B. sich selbst - senden lassen.

Alarme

Grundlagen

Es ist möglich, in MyTISM sog. Alarme zu definieren, bei deren Auslösung die für den jeweiligen Alarm eingetragenen Empfänger benachrichtigt oder andere Aktionen ausgeführt werden. Es gibt vier Varianten von Alarmen, die für jeweils unterschiedliche Zwecke gedacht sind.

Einfacher Termin

Dies ist die einfachste Alarm-Variante; der Alarm wird einfach zu einem vorher eingetragenen, festen Zeitpunkt ausgelöst.
Alternativ gibt es auch die Möglichkeit, den Alarm mit einer konfigurierbaren Frequenz wiederholt auslösen zu lassen.

Beispiel: Am 22. Juli 2011 um 14:00 Uhr ist eine Projektbesprechung angesetzt. Alle Projektteilnehmer sollen eine Viertelstunde vorher eine Benachrichtigung erhalten.

BO-basierter Termin

Diese Alarm-Variante ähnelt der Variante "Einfacher Termin" insofern, als dass die Alarme ebenfalls zu einem festgelegten Zeitpunkt ausgelöst werden. Allerdings "überwacht" ein BO-basierter Termin eine Menge von Objekten ("BOs") und legt für jedes dieser Objekte einen eigenen Auslösezeitpunkt fest.

Beispiel: Für alle Mitarbeiter ist der jeweilige Geburtstag eingetragen. Die Mitarbeiter sollen jedes Jahr eine automatische Gratulation erhalten (ob das wirklich so eine tolle Idee ist, sei mal dahingestellt …​).

Hinweise

Diese Alarm-Variante dient dazu, Alarme auszulösen, wenn bestimmte Ereignisse in der MyTISM-Anwendung auftreten bzw. bestimmte Änderungen an Objekten erfolgen.

Beispiel: Der Chef der Buchhaltung möchte benachrichtigt werden, sobald der Bestand eines Kontos unter 100,- EUR sinkt.

Wiedervorlagen

Diese Alarm-Variante dient dazu, Alarme auszulösen, wenn bestimmte Ereignisse in der MyTISM-Anwendung nicht innerhalb einer festgelegten Zeit aufgetreten sind bzw. bestimmte Änderungen an Objekten innerhalb einer festgelegten Zeit nicht erfolgt sind.

Beispiel: Der Projektleiter möchte benachrichtigt werden, wenn sich der Status eines Projekts zwei Tage lang nicht geändert hat.

Gegenbenenfalls kann es in Ihrer MyTISM-Anwendung auch noch eigene Untervarianten dieser Alarm-Typen geben, die für spezielle Zwecke gedacht sind. Diese besitzen ggf. zusätzlich zu den normalen Eigenschaften der Alarme noch zusätzliche Eigenschaften und Funktionen. Ob solche Untervarianten existieren, wofür sie benutzt werden und weitere Informationen hierzu kann Ihnen Ihr MyTISM-Administrator geben.

Vorbereitung und Konfiguration

Alarmsystem-Lizenz einspielen

Das Alarmsystem ist eine optionale Erweiterung des Standard-MyTISM-Systems. Um es aktivieren und nutzen zu können, müssen Sie zuerst eine gültige Alarmsystem-Lizenz erworben und auf dem Server eingespielt haben.

Alarmsystem aktivieren

Das Alarmsystem ist normalerweise deaktiviert, d.h. Sie können zwar beliebige Alarme anlegen, diese werden aber von MyTISM erst einmal in keiner Weise behandelt.

Um das Alarmsystem zu aktivieren müssen Sie in der Datei mytism.ini im Abschnitt [Alarme] die Einstellung activateAlarme auf if_possible oder mandatory setzen. Sowohl if_possible als auch mandatory starten das Alarmsystem; sie unterscheiden sich lediglich darin, dass bei mandatory eine auffälligere Fehlermeldung ausgegeben wird (ursprünglich sollte der Serverstart abgebrochen werden, was nach Diskussion dann aber deaktiviert wurde).

Sollte der entsprechende Abschnitt noch nicht existieren, fügen Sie ihn einfach ein.

[Alarme]
activateAlarme=if_possible

Wenn Ihre MyTISM-Installation mehrere synchronisierende Server umfasst, müssen - und dürfen - Sie das Alarmsystem aus technischen Gründen nur auf dem authoritativen Server aktivieren. Wenn Sie obigen Eintrag in der Datei mytism.ini eines nicht-authoritativen Servers eintragen, wird nur eine Warnmeldung im Log ausgegeben und das Alarmsystem dort nicht aktiviert.

Sync-Events behandeln

Sollten Sie in der Datei mytism.ini im Abschnitt [Alarme] noch einen Eintrag handleSyncEvents=1 oder handleSyncEvents=0 aufgeführt haben, können Sie diese Zeile löschen, da sie zu einer mittlerweile nicht mehr benötigten und nicht mehr unterstützten Konfigurationsmöglichkeit gehört. Falls die Zeile vorhanden ist wird sie ignoriert.

Benachrichtigungssystems aktivieren

Sollen Empfänger beim Auslösen eines Alarms benachrichtigt werden muss das Benachrichtigungssystem aktiviert und entsprechend konfiguriert sein. Wenn dies nicht der Fall ist, können keine Benachrichtigungen (per e-Mail, Fax, o.Ä) versandt werden und die ausgelösten Alarme sind nur über das AlarmAusloesungen-Lesezeichen bzw. den entsprechenden Reiter z.B. im Benutzerformular ersichtlich.

Anlegen und Verwalten von Alarmen

Alarme sind ganz normale Objekte und können mit den entsprechenden Schablonen, Lesezeichen und Formularen angelegt und verwaltet werden. Die automatisch generierten Schablonen, Lesezeichen und Formulare befinden sich im Ordner Admins → MyTISM → Alarme. Diese Strukturelemente sind normalerweise nur für MyTISM-Administratoren verfügbar.

Evtl. existieren auf Ihrer speziellen MyTISM-Installation auch noch weitere, angepasste Formulare und Lesezeichen oder Formulare und Lesezeichen für eigene Alarm-Untervarianten. Diese befinden sich dann möglicherweise in anderen Ordnern; weitere Informationen hierzu kann Ihnen Ihr MyTISM-Administrator geben.

Gruppe "Admins Alarmsystem"

Es gibt in MyTISM-Applikationen eine automatisch angelegte Gruppe "Admins Alarmsystem". Benutzer, die dieser Gruppe zugewiesen wurden, haben automatisch alle Rechte um Alarme und damit zusammenhängende Objekte zu erstellen und zu verwalten.

Außerdem steht ihnen im Gruppen-Ordner ein Lesezeichen "Alarme" zur Verfügung, mittels derer sie die im System vorhandenen Alarme auflisten, öffnen, editieren und über das Kontext-Menü neue Alarme anlegen können.

Ebenfalls im Gruppen-Ordner gibt es noch eine Schablone "AlarmLog", mittels derer Meldungen des Alarm- und Benachrichtigungssystems angezeigt werden können.

Alarme aktivieren und deaktivieren

Alle Alarme besitzen ein "Aktiv"-Flag. Ist dieses gesetzt, so ist der Alarm aktiviert und kann ausgelöst werden. Ist das Flag nicht gesetzt - der Standard bei neuen Alarmen - so ist der Alarm deaktiviert und löst nicht aus.

Sie können hiermit einen Alarm quasi "vorbereiten", in dem Sie alle benötigten Daten des Alarms eintragen, aber das "Aktiv"-Flag noch nicht setzen. Der Alarm ist dann bereits im System bekannt, wird aber noch nicht behandelt. Sie können in diesem Fall das "Aktiv"-Flag zu einem späteren Zeitpunkt setzen und den Alarm speichern; der Alarm wird dann ab diesem Zeitpunkt behandelt.

Caution
Für BO-basierte Termine und Wiedervorlagen erfolgt die Initialisierung der WiedervorlageStatus bzw. BO-basierter TerminStatus (siehe alarme_alarmAusloesungen) in jedem Fall beim ersten Speichern des Alarms, da diese Informationen zum Funktionieren dieser Alarme essentiell sind und immer benötigt werden.

Die Aktivierung von neu angelegten (und auf "Aktiv" gesetzten) Hinweisen und EinfachenTerminen geschieht sehr schnell, die Aktivierung von BO-basierten Terminen und Wiedervorlagen kann aus technischen Gründen, je nach der Anzahl der zu "überwachenden" Ojekte, etwas Zeit in Anspruch nehmen (siehe Wiedervorlagestatus).

Testmodus für Alarme

Caution
Der Testmodus für Alarme ist noch in Arbeit; ggf. ändert sich das Verhalten in diesem Bereich in Zukunft noch.

Alle Alarme können in einem Testmodus betrieben werden; hierzu muss das entsprechende "Testmodus"-Flag gesetzt werden. Ist dieses gesetzt, so löst der Alarm keine Benachrichtigungen aus und erzeugt auch keine AlarmAuslösungen-Objekte.

Es werden lediglich entsprechende Info-Meldungen im AlarmLog ausgegeben, mittels derer verfolgt werden kann, was bei der Auslösung passiert wäre.

Caution
Sonstige bei der Auslösung normalerweise erfolgende Dinge passieren jedoch weiterhin: So werden z.B. einfache Termine auch gelöscht, wenn sie im Testmodus "ausgelöst" wurden, etc. Für BO-basierte Termine und Wiedervorlagen erfolgt die Aktualisierung der WiedervorlageStatus bzw. BO-basierter TerminStatus (siehe alarme_alarmAusloesungen) in jedem Fall weiterhin, da diese Informationen zum Funktionieren dieser Alarme essentiell sind und immer benötigt werden.

Meldungen des Alarm- und Benachrichtigungssystems anzeigen

Über die Schablone "AlarmLog" die sich standardmäßig im im Gruppen-Ordner der Gruppe "Admins Alarmsystem" befindet, können Meldungen des Alarm- und Benachrichtigungssystems angezeigt werden.

Note
Es werden nur die Meldungen angezeigt, die eingetroffen sind, seit das Log-Fenster geöffnet wurde. Ältere Meldungen können nur direkt im Server-Log auf dem Server eingesehen werden.

Das AlarmLog zeigt Meldungen des Alarm- und Benachrichtigungssystems.

Die Checkboxen geben an, welche Typen von Meldungen angezeigt werden sollen. Meldungen sind nach ihrer Wichtigkeit in verschiedene Stufen eingeteilt, von "Trace" (die niedrigste, "geschwätzigste" Stufe), über "Debug", "Info", "Warn" und "Error" bis zu "Fatal", der höchsten und wichtigsten Stufe.

Meldungen der Stufen "Fatal", "Error" und "Warn" werden immer angezeigt; Anzeige von Meldungen der anderen Stufen kann durch aktivieren oder deaktivieren der entsprechenden Checkboxen aktiviert oder deaktiviert werden.

Note
Es werden nur die Meldungen der entsprechenden Stufe (de)aktiviert; Meldungen höherer oder niedrigerer Stufen sind nicht betroffen.
Note
Es werden nur Meldungen angezeigt, die überhaupt vom Logsystem ausgegeben werden; wenn also die Konfiguration des Logsystems z.B. für eine Klasse die Ausgabe von Debug-Meldungen gar nicht aktiviert (weil der LogLevel des entsprechenden Loggers so eingestellt ist), werden entsprechende Meldungen gar nicht erst ausgegeben und daher hier natürlich auch nicht angezeigt.

Außerdem kann mittels der entsprechenden Checkbox angegeben werden, ob die sog. StackTraces von Exceptions (Fehlern) hier angezeigt werden sollen. Diese sind - entsprechende Fachkenntnisse vorausgesetzt - zur Fehlerbehebung oft sehr nützlich.

Schlussendlich kann mittels der entsprechenden Checkbox auch noch angegeben werden, ob die Texte, die für die Benachrichtigungen generiert würden, angezeigt werden sollen.

Gemeinsame Eigenschaften aller Alarme

Alle Alarme haben bestimmte Eigenschaften gemeinsam:

Erster Reiter

Name

Pflichtfeld - Der Name oder Titel eines Alarms sollte den Alarm kurz und prägnant benennen. Der Name kann frei gewählt werden und kann z.B. bei der Anzeige der Alarme im zugehörigen Lesezeichen oder bei den Benachrichtigungen bei der Alarm-Auslösung benutzt werden. Es ist sehr sinnvoll, jedoch keineswegs zwingend, dass unterschiedliche Alarme unterschiedliche Namen haben :-)

Beschreibung

Optional - Die Beschreibung kann einen längeren Kommentar bzw. eine längere Beschreibung des Alarms beinhalten. Dieser Text kann z.B. bei den Benachrichtigungen benutzt werden.

Empfänger "Sende Benachrichtigungen an …​ diese(n) Empfänger (CC) …​ (und) diese(n) Empfänger (BCC)"

Mindestens eines von "Empfänger (CC)", "Empfänger (BCC)" oder "Benachrichtigungsskript" muss gegeben sein - Wie bereits erwähnt können Empfänger definiert werden, die bei der Auslösung eines Alarms benachrichtigt werden sollen.
Alle "Empfänger (CC)" sind bei der Benachrichtigung für alle anderen Empfänger einsehbar; Benachrichtigungen für "Empfänger (BCC)" werden dagegen einzeln versendet, so dass kein Empfänger über die anderen Bescheid weiß.
Ein Empfänger, z.B. ein Benutzer, erhält für eine Alarm-Auslösung immer nur eine Benachrichtigung, auch wenn z.B. für einen Alarm mehrere Gruppen als Empfänger eingetragen wurden und der Benutzer Mitglied in mehreren dieser Gruppen ist oder der Benutzer selbst ebenfalls für den Alarm eingetragen wurde.

Benachrichtigungsvorlage "Erstelle die Benachrichtigung mit …​"

Pflichtfeld - Die BenachrichtigungsVorlage wird beim Versand von Benachrichtigungen verwendet und gibt die Texte für Betreff und Nachrichtentext vor. Es handelt sich hierbei zwar um ein eigenständiges Objekt, dessen Daten können jedoch direkt im Alarm-Formular bearbeitet werden.

"Alte Alarme nur auslösen wenn nicht älter als", Reiter "Erweitert"

Optional - Es kann passieren, dass Alarme zu einem bestimmten Zeitpunkt hätten ausgelöst werden sollen, dies jedoch nicht passiert ist, z.B. weil zu dieser Zeit das Alarmsystem deaktiviert war. Die entsprechenden Auslösungen werden dann normalerweise später (also z.B. sobald das Alarmsystem wieder aktiviert wird) "nachgeholt".
Wenn Sie möchten, dass dabei nur Alarme ausgelöst werden, bei denen der eigentliche Auslösezeitpunkt nicht zu weit in der Vergangenheit liegt, können Sie hier angeben, wie weit die eigentliche Auslösung maximal zurück liegen darf.

Benachrichtigungsskript "Sende Benachrichtigungen mittels dieses Skripts", Reiter "Erweitert"

Mindestens eines von Benutzer, Gruppe oder Benachrichtigungsskript muss gegeben sein - Diese Eigenschaft dient dazu, bei der Auslösung von Alarmen eigene Aktionen ausführen zu können und wird nur benötigt, wenn die Standardmöglichkeiten zur Benachrichtigung von Benutzern bzw. Gruppen einmal nicht ausreichen. Ausführlichere Informationen hierzu finden Sie im Abschnitt

Reiter "Erweitert"

"Verantwortlicher"

Optional - Hier kann ein Benutzer angegeben werden, der in irgendeiner Weise "verantwortlich" für diesen Alarm ist. Diese Angabe ist optional und wird zur Zeit lediglich dazu verwendet, bei Benachrichtigungen den "Absender" der Benachrichtigung zu setzen
Wenn hier nichts angegeben ist, wird als Absender der interne Benutzer des Alarmsystems gesetzt. Bei Benachrichtigungen, die per e-Mail verschickt werden, wird die erste e-Mail-Adresse dieses Benutzers als Absender der Mails gesetzt (wenn mehrere Adressen für den Benutzer verfügbar sind, ist nicht definiert, welche davon "die erste" ist).

"Überwachung starten ab"

Optional - Normalerweise werden Alarme sofort aktiv, sobald sie erstellt und als "Alarm ist aktiv" definiert wurden. Falls Sie hier ein Datum und ggf. eine Zeit eintragen wird der Alarm erst zu diesem Zeitpunkt aktiv und wird nicht vor diesem Zeitpunkt ausgelöst.

"Will verschlüsselte Benachrichtigungen" und "Will signierte Benachrichtigungen"

Optional - Alarme können die Standardeinstellungen des Systems für verschlüsselte und/oder signierte Benachrichtigungen gezielt überschreiben. Eingestellte Werte hier übersteuern die Standardeinstellungen aber werden wiederum selbst von ggf. vorhandenen Einstellungen der Benutzer übersteuert.

"Hänge statt dem auslösenden Objekt an die Benachrichtigungen an …​"

Wird unter Anhängen von (weiteren) Objekten genauer erklärt.

Einfacher Termin

Wie oben bereits erwähnt, handelt es sich bei einfachen Terminen um Alarme, die, ohne dass weitere Bedingungen erfüllt sein müssen, einfach zu einem festgelegten Zeitpunkt ausgelöst werden.

Beispiel: Am 22. Juli 2011 um 14:00 Uhr ist eine Projektbesprechung angesetzt. Alle Projektteilnehmer sollen eine Viertelstunde vorher eine Benachrichtigung erhalten.

Auf dem ersten Reiter des vorgebauten Formulars für einfache Termine sind die Eingabefelder für die normalerweise benutzten und die unbedingt erforderlichen Angaben zusammengefasst.

Allgemeine Eigenschaften festlegen

Geben Sie dem einfachen Termin einen kurzen aber aussagekräftigen Namen, und ggf. wenn sinnvoll eine längere Beschreibung.

"Alte Alarme nur auslösen wenn nicht älter als" und "Verantwortlicher" können Sie, bei Bedarf, auf dem Reiter "Erweitert" angeben.

Wann soll der einfache Termin stattfinden?

Einfache Termine können entweder einmalig, zu einem fest eingetragenen Zeitpunkt, oder wiederholt, mit einer konfigurierbaren Frequenz, ausgelöst weden.

An einem festen Zeitpunkt

Geben Sie bei "Alarm auslösen" → "…​ am/um" das Datum und die Zeit an, wann der einfache Termin stattfindet bzw. beginnt.
Der Alarm löst zu diesem Zeitpunkt einmal aus und wird danach automatisch gelöscht.

Wiederholt

Geben Sie bei "Alarm auslösen" → "…​ (oder stattdessen) wiederholen nach Muster" die Definition an, die festlegt, mit welcher Frequenz der Termin auslösen soll.
Der Alarm löst immer wieder aus, zu Zeitpunkten die anhand der angegebenen Definition bestimmt werden. Hilfe zur Definition (im sog. "Cron-Format") finden Sie z.B. unter http://www.nncron.ru/help/EN/working/cron-format.htm

Vorwarnzeit

Normalerweise wird der einfache Termin erst zum angegebenen bzw. ermittelten Zeitpunkt ausgelöst und eventuelle Benachrichtigungen werden also auch erst dann versendet.

Wenn die Auslösung bereits statt finden soll, bevor der einfache Termin eigentlich "startet", können Sie unter "…​ aber sende Benachrichtigungen …​ früher (Vorwarnzeit)" optional eine Zeitspanne angeben, um wieviel früher dem angegebenen bzw. ermittelten Zeitpunkt dies erfolgen soll.

Wer soll Benachrichtigungen erhalten und wie sollen diese aussehen?

Geben Sie bei "Sende Benachrichtigungen an …​ diese(n) Benutzer …​ (und) diese Gruppe(n)" ein oder mehrere Benutzer und/oder Gruppen an, der oder die bei der Auslösung des einfachen Termins benachrichtigt werden soll(en).

Ein Benutzer erhält für eine Alarm-Auslösung immer nur eine Benachrichtigung, auch wenn z.B. für einen Alarm mehrere Gruppen eingetragen wurden und der Benutzer Mitglied in mehreren dieser Gruppen ist oder der Benutzer selbst ebenfalls für den Alarm eingetragen wurde.

Bei "Erstelle die Benachrichtigung mit …​" wählen Sie eine Textvorlage aus, mittels derer der Betreff und der Text der zu versendenden Benachrichtigungen festgelegt werden. Alternativ können Sie mittels des Schreibstift-Icons auch eine neue, eigene Vorlage direkt erstellen.

Note
Wenn der Text für die Benachrichtigung (von Leerzeichen abgesehen) mit <html> beginnt, werden die daraus generierten e-Mails als HTML-Mails verschickt. Wenn der Text nicht auf diese Weise beginnt werden die e-Mails als ganz normale Textmails verschickt.

Alice, die Projektleiterin, möchte Bob und Claire, die beiden anderen Projektmitarbeiter, am 22. Juli um 14:00 Uhr zu einer Besprechung einladen.

Sie erstellt also einen neuen EinfachenTermin mit Namen "Projektbesprechung". Unter "Alarm auslösen …​ am/um" trägt sie "22.07.2011 14:00" ein.

Damit jeder auch noch Zeit hat, seine Sachen zusammenzusuchen und von seinem Büro in den Besprechungsraum am anderen Ende des Gebäudes zu gelangen, setzt sie eine "…​ aber sende Benachrichtigungen …​ früher (Vorwarnzeit)" von 15 Minuten (d.h. sie trägt "15m" ein), so dass die Benachrichtigungen um 13:45 Uhr bei den Teilnehmern ankommen.

Zu benachrichtigende Benutzer sind natürlich Bob und Claire und sie trägt sich selbst ebenfalls nochmal für eine Erinnerung ein (da sie leider notorisch vergesslich ist :-).

Als Benachrichtigungsvorlage wählt sie die bereits in der Datenbank vorhandenen Vorlage "Projektbesprechung".

Nachdem sie den EinfachenTermin gespeichert hat, wird dieser von der MyTISM-Anwendung in eine interne Liste eingetragen. Am 22. Juli um 13:45 Uhr werden dann automatisch entsprechende Benachrichtigungen an Alice, Bob und Claire verschickt und der einfache Termin wird automatisch gelöscht.

BO-basierter Termin

BO-basierte Termine werden einer Menge von Objekten zugeordnet, von denen entweder jedes einen eigenen, festen Auslösungszeitpunkt bereits selbst definiert oder für welche der BO-basierte Termin jeweils einen eigenen, festen Auslösezeitpunkt berechnet.

Beispiel: Für alle Mitarbeiter ist der jeweilige Geburtstag eingetragen. Die Mitarbeiter sollen jedes Jahr eine automatische Gratulation erhalten (ob das wirklich so eine tolle Idee ist, sei mal dahingestellt …​).

Auf dem ersten Reiter des vorgebauten Formulars für BO-basierte Termine sind die Eingabefelder für die normalerweise benutzten und die unbedingt erforderlichen Angaben zusammengefasst. Da die Benachrichtigung jeweils an den Mitarbeiter gehen soll

Allgemeine Eigenschaften festlegen

Geben Sie dem BO-basierten Termin einen kurzen aber aussagekräftigen Namen, und ggf. wenn sinnvoll eine längere Beschreibung.

"Alte Alarme nur auslösen wenn nicht älter als" und "Verantwortlicher" können Sie, bei Bedarf, auf dem Reiter "Erweitert" angeben.

Welche Objekte sollen "überwacht" werden?

Legen Sie fest, für welche Objekte der BO-basierte Termin auslösen soll. Dazu muss dem BO-basierten Termin unter "Überwache die Objekte …​" eine sog. BOMaske zugewiesen werden, die die Menge der zu "beobachtenden" Objekte definiert.

Sie können hier entweder eine bereits vorhandene BOMaske auswählen oder alternativ mittels des Schreibstift-Icons auch eine neue, eigene Maske direkt erstellen. Für die meisten Anwendungszwecke ist es ausreichend, unter "…​ vom Typ" einfach einen Objekt-Typ (Entität) auszuwählen, womit dann alle Objekte dieses gewählten Typs vom BO-basierten Termin "beobachtet" werden.

Die Erstellung von BOMasken wird im Abschnitt "BOMasken" im Kapitel "Rechteverwaltung" im Administrator-Handbuch ausführlich erklärt, für weitere Informationen sehen Sie bitte dort nach. Anzumerken ist hier noch, dass die Eigenschaft Attribut von BOMaske nur für die Rechteverwaltung notwendig ist und für das Alarmsystem nicht benutzt wird; evtl. hier eingetragene Werte werden vom Alarmsystem einfach ignoriert.

Wann soll der BO-basierte Termin (für ein Objekt) ausgelöst werden?

Es gibt zwei Möglichkeiten, zu bestimmen, wann der BO-basierte Termin für ein bestimmtes Objekt ausgelöst wird: Durch Angabe eines Attributes, das einfach ausgelesen werden soll, oder durch ein Skript, welches ausgewertet wird und für jedes Objekt das Auslösedatum berechnet.

Auslösedatum aus Objekt-Attribut auslesen

Unter "…​ das Datum aus Attribut" können Sie den Namen eines Attributes der Objekte auswählen, aus dem der Auslösezeitpunkt gelesen werden soll.

Es werden nur Attribute angezeigt, die einen Datumswert beinhalten, also im Schema mit Typ Datetime (oder einem davon abgeleiteten Typ) definiert sind.

Wenn möglich sollten Sie diese Variante der Variante mit Skript (s.u.) vorziehen, da sie

  1. weniger Schreibarbeit und keine Kenntnisse in Skriptprogrammierung erfordert

  2. die Anforderungen an das System geringer sind und

  3. direkt bei der Definition des Alarms überprüft werden kann, ob alle Angaben korrekt sind - bei einem Skript kann das normalerweise erst festgestellt werden, wenn zur Laufzeit bei der Auswertung des Skripts ein Fehler auftritt.

Auslösedatum mit Skript berechnen

Falls das einfache Auslesen eines Attributwertes für Ihre Zwecke nicht ausreicht, können Sie alternativ unter "…​ das Datum, das dieses Skript liefer, erreicht ist" ein Skript angeben, welches das Datum für die Auslösung berechnet. Damit das entsprechende Eingabefeld angezeigt wird, darf in der Auswahlbox kein Attribut angewählt sein (Eintrag "(kein Attribut, benutze Skript)" muss ausgewählt sein).

Das so definierte Skript führt der BO-basierte Termin dann für jedes seiner zu überwachenden Objekte aus. Dieses Skript muss dann einen Wert vom Typ java.util.Date zurückliefern, welcher angibt, wann der Alarm für das entsprechende Objekt ausgelöst werden soll.

Wie das Skript diesen Zeitpunkt bestimmt, ist im Prinzip vollkommen egal; es könnte z.B. theoretisch ebenfalls einfach nur den Wert eines Attributes des Objektes auslesen und diesen zurückgeben (wobei dann die Benutzung eines Skripts natürlich nicht wirklich Sinn macht) oder aber auch beliebig komplizierte Berechnungen ausführen, um das Auslösedatum für das aktuelle Objekt zu errechnen.

Skript zur Berechnung des nächsten Geburtstages:
kal = new GregorianCalendar()
kal.setTime( bo.getGeburtstag() ) // Auf Geburtstag initialisieren.
kal.set( Calendar.HOUR_OF_DAY, 10 ) // Auslösen um 10 Uhr morgens.
now = new Date()
while( kal.getTime().before( now ) ) { // Nächsten Termin finden.
  kal.roll( Calendar.YEAR, true ) }
return kal.getTime() // Als Date() zurückgeben.

Zu beachten ist, dass das Skript möglichst schnell ein Ergebnis zurückliefern sollte, um das Alarmsystem nicht unnötig zu verlangsamen.

Außerdem muss das Skript in jedem Fall ein Objekt vom Typ java.util.Date zurückliefern - also nicht etwa gar keinen Wert oder einen Wert von einem anderen Typ! Sollte das passieren, oder sollte irgendein Fehler im Skript auftreten, wird für das entsprechende Objekt kein Alarm ausgelöst.

Im Skript stehen folgende vordefinierte Variablen zur Verfügung:

bo

Das Objekt, für welches der Auslösezeitpunkt bestimmt werden soll.

bbt

Der BO-basierte Termin, zu dem das Skript gehört.

bbts

Das BOAlarmStatus-Objekt (wird normalerweise nicht benötigt).

log

Ein Logger-Objekt (Name "de.ipcon.db.core.BO-basierter Termin") mit dem Debug- und andere Meldungen ins Server-Log ausgegeben werden können.

Wie auch bei EinfachenTerminen kann auch bei BO-basierten Terminen Außerdem noch unter "…​ aber sende Benachrichtigungen …​ früher (Vorwarnzeit)" eine Vorwarnzeit angegeben werden, die die entsprechende Alarmauslösung dann noch früher stattfinden lässt.

Wer soll Benachrichtigungen erhalten und wie sollen diese aussehen?

Die Konfiguration für die Benachrichtigungen funktioniert hier genauso wie bereits für einfache Termine beschrieben.

Automatische Neuterminierung nach Auslösung

Normalerweise wird, analog zu den EinfachenTerminen, auch bei den BO-basierten Terminen für jedes überwachte Objekt nur ein einziges Mal ein Alarm ausgelöst. In gewissen Fällen kann es aber sinnvoll bzw. möglich sein, dass für ein Objekt der Alarm mehrfach zu verschiedenen Zeitpunkten ausgelöst werden kann und soll.

Durch Setzen von "Alarm bleibt auch nach Auslösung weiterhin aktiv" (auf dem Reiter "Erweitert") kann bestimmt werden, dass nach der Auslösung des Alarms für ein Objekt das Skript erneut aufgerufen bzw. das angegebene Attribut des Objekts erneut ausgelesen wird um sofort einen neuen Auslösezeitpunkt festzulegen, an dem dann der Alarm für dieses Objekt erneut ausgelöst werden soll.

Hierbei ist allerdings zu beachten, dass der Alarm für dieses Objekt nur dann wieder neu eingeplant wird, wenn hierbei dann ein Datum zurückliefert wird, welches in der Zukunft d.h. nach dem aktuellen Auslösezeitpunkt liegt. Ansonsten könnte es zu Problemen kommen, da der Alarm dann ohne Unterbrechung direkt hintereinander immer wieder ausgelöst würde.

Sollte das neue Datum ungültig sein (in der Vergangenheit liegen), so wird der Alarm für das aktuelle Objekt nicht mehr neu terminiert und in Zukunft nicht mehr ausgelöst.

Wenn Sie die Variante mit Attribut verwenden, macht diese Funktion normalerweise keinen Sinn, da ja immer nur ein Datum (welches dann nach der Auslösung garantiert in der Vergangenheit liegt) zurückgeliefert wird. Eine Ausnahme wäre, falls es sich um ein virtuelles Attribut handelt, da diese ja normalerweise ebenfalls berechnete Werte zurückliefern. Dies ist jedoch eher ein Thema für Fortgeschrittene und wird deshalb hier nicht weiter behandelt.

Auf dem zweiten Reiter des vorgebauten Formulars für BO-basierte Termine sind die Eingabefelder für die weniger benutzten Angaben zusammengefasst.

Anhängen von (weiteren) Objekten

Normalerweise wird an die Benachrichtigungen von Alarmen das Objekt, aufgrund welcher der Alarm ausgelöst wurde, angehängt. Es ist jedoch auch möglich, nicht das Objekt selbst, sondern ein von diesem Objekt referenziertes anderes Objekt oder gar beliebige Objekte stattdessen anzuhängen.

Im Feld "…​ das Objekt aus Attribut" können Sie ein Attribut des eigentlichen Objekts wählen, dessen Wert statt dem auslösenden Objekt angehängt werden soll. Es werden nur Relationen-Attribute angezeigt, d.h. keine Attribute die nur einfache Zahlen oder Zeichenketten, etc. als Werte beinhalten.

Wenn Sie mehrere oder andere Objekte anhängen möchten, wählen Sie hier "(kein Attribut, benutze Skript)" und können dann im darunter angezeigten Feld ein Skript eingeben, welches die anzuhängenden Objekte zusammenstellt und als eine Sammlung vom Java-Typ Map zurückliefert.

Skript das mehrere Werte an Benachrichtigungen anhängt:
// FIXME Vermutlich kürzere Alternative:
// [ 'Objekt selbst':bo, 'Mitarbeiter':bo.getMitarbeiter(), 'Personeneintrag des Mitarbeiters':bo.getMitarbeiter().getPerson()] as Map
map = new HashMap()
map.put( "Objekt selbst", bo )
map.put( "Mitarbeiter", bo.getMitarbeiter() )
map.put( "Personeneintrag des Mitarbeiters", bo.getMitarbeiter().getPerson() )
return map
Note
Wenn Sie ein Attribut auswählen, oder ein Skript angeben, wird das eigentliche auslösende Objekt nicht mehr angehängt.

Wenn Sie an eine Benachrichtigung gar keine Objekte anhängen wollen, dann benutzen Sie ein Skript, welches null oder eine leere Map zurückgibt.

Gar nichts anhängen:
return null

Es stehen folgende vordefinierte Variablen zur Verfügung:

alarm

Der Alarm, der ausgelöst wurde.

dateNow

Das Datum und die Zeit (als java.util.Date-Objekt) wann der Alarm ausgelöst wurde.

log

Ein Logger-Objekt (Name "de.ipcon.db.alarm.AlarmNotificationManager") mit dem Debug- und andere Meldungen ins Server-Log ausgegeben werden können.
Für BO-basierte Termine, Hinweise und Wiedervorlagen, die sich ja immer auf BOs beziehen, stehen noch zwei zusätzliche Variablen zur Verfügung:

bo

Das Objekt (BO), welches erstellt/geändert/gelöscht wurde (kann null sein).

bot

Der BOT des BOs, für welches der Alarm ausgelöst wurde (kann evtl. null sein).

Für Hinweise, die ja immer durch ein Ereignis ausgelöst werden, ist schlussendlich noch eine Variable definiert:

bt

Die BT, welche den Alarm ausgelöst hat.

Hinweise

Hinweise sind dazu gedacht, eine Menge von Objekten zu überwachen und Benachrichtigungen zu versenden, wenn an einem oder mehreren dieser Objekte bestimmte Änderungen durchgeführt, bzw. solche Objekte erzeugt oder gelöscht wurden.

Beispiel: Der Chef der Buchhaltung möchte benachrichtigt werden, sobald der Bestand eines Kontos unter 100,- EUR sinkt.

Auf dem ersten Reiter des vorgebauten Formulars für Hinweise sind die Eingabefelder für die normalerweise benutzten und die unbedingt erforderlichen Angaben zusammengefasst.

Allgemeine Eigenschaften festlegen

Geben Sie dem Hinweis einen kurzen aber aussagekräftigen Namen, und ggf. wenn sinnvoll eine längere Beschreibung.

"Alte Alarme nur auslösen wenn nicht älter als" und "Verantwortlicher" können Sie, bei Bedarf, auf dem Reiter "Erweitert" angeben.

Welche Objekte sollen "überwacht" werden?

Die Menge der zu "überwachenden" Objekte wird hier genau so wie für BO-basierte Termine definiert.

Wann soll der Hinweis ausgelöst werden?

Nachdem definiert wurde, welche Objekte überwacht werden sollen, muss festgelegt werden, welche Ereignisse, z.B. Änderungen an diesen Objekten, den Hinweis auslösen sollen.

Hierzu gibt es verschiedene Möglichkeiten, die im Folgenden beschrieben werden.

Auslösung bei beliebiger Änderung, Erstellen oder Löschen von Objekten (Unter-Reiter "Einfach")

Diese oft benutzten, einfachen Fälle können einfach durch Aktivieren der entsprechenden Checkbox definiert werden:

  • "…​ ein überwachtes Objekt erzeugt wurde" löst den Hinweis aus, sobald eines oder mehrer neue Objekte, auf die die für den Hinweis definierte Maske (s.o.) passt, erstellt wurden.

  • "…​ ein überwachtes Objekt geändert wurde" löst den Hinweis aus, sobald an einem oder mehreren der überwachten Objekte irgendeine Änderung durchgeführt wurde; sei es z.B., dass der Wert eines Attributes gesetzt, gelöscht oder geändert wurde oder ein Objekt in einer Relation hinzugefügt oder gelöscht wurde.

  • "…​ ein überwachtes Objekt gelöscht wurde" löst den Hinweis aus, sobald eines oder mehrere der überwachten Objekte gelöscht wurden.

  • "…​ ein überwachtes Objekt erschienen ist" löst den Hinweis aus, sobald ein existierendes Objekt so geändert wurde, dass es jetzt in die Menge der vom Alarm überwachten Objekte passt.

  • "…​ ein überwachtes Objekt verschwunden ist" löst den Hinweis aus, sobald ein existierendes und vom Alarm überwachtes Objekt so geändert wurde, dass es jetzt nicht mehr in die Menge der vom Alarm überwachten Objekte passt.

Auslösung mittels Auslösekriterien (Unter-Reiter "Erweitert")

Mit den sog. Auslösekriterien gibt es eine recht einfache aber sehr flexible Möglichkeit, festzulegen, welche Änderungen erfolgt sein müssen, damit der Hinweis ausgelöst wird.

Mittels der Auslösekriterien geben Sie an, für welche Attribute ("Felder" oder "Eigenschaften" der Objekte) welche Änderungen oder Ereignisse eingetreten sein müssen, damit der Hinweis für das Objekt ausgelöst wird. Sie können für jeden Hinweis beliebig viele Auslösekriterien festlegen.
Jedes dieser Auslösekriterien hat drei wichtige Eigenschaften:

Attribut (erstes Feld)

Hiermit definieren Sie, an welchem Attribut der überwachten Objekte eine Änderung erfolgt sein muss. Die Einträge in der Auswahlbox geben alle Attribute an, welche für die vom Alarm überwachten Objekte verfügbar sind - mit Ausnahme von virtuellen und nicht-persistenten (weil diese aus technischen Gründen hier nicht geprüft werden können) und System-Attributen.

Änderungstyp (zweites Feld)

Hiermit definieren Sie, wie sich das oben angegebene Attribut verändert haben muss, damit der Hinweis ausgelöst wird. Je nach Typ des ausgewählten Attributs werden hier nur passende Änderungstypen aufgeführt.

Wert (drittes Feld, ist ausgeblendet wenn nicht anwendbar)

Manche Änderungstypen, wie z.B. "wird gesetzt auf den Wert", erfordern einen Vergleichswert; diesen können sie hier angeben. Wenn ein Änderungstyp keinen Vergleichswert erfordert, wird dieses Feld automatisch ausgeblendet.
Die Werte können so eingegeben werden, wie Sie sie auch normalerweise in anderen MyTISM-Formularen angeben. Für Datums- und Wahrheitswerte wird ebenfalls ein passendes Eingabefeld angezeigt. Für Relationen (also Attribute die Verweise/Links auf ein oder mehrere andere Objekte abbilden) können Sie das gewünschte Vergleichsobjekt mittels Popup auswählen. Allerdings ist die Unterstützung für Relationen hier noch lückenhaft, so kann z.Zt. z.B. noch nicht geprüft werden, ob ein Objekt zu einer Mehrfach-Relation hinzugefügt oder entfernt wurde; dies ist z.Zt. nur mit einem Skript (s.u.) möglich.

Wenn Sie informiert werden wollen, wenn der Kontostand Ihres Kontos unter 100,- EUR gesunken ist, setzen Sie Attribut (Feld 1) auf Kontostand, Änderungstyp (Feld 2) auf "wird gesetzt auf Wert kleiner als" und Wert (Feld 3) auf 100.

Wenn Sie über jede Änderung Ihres Kontostandes informiert werden wollen, setzen sie Änderungstyp (Feld 2) auf "wird in irgendeiner Weise geändert"; in diesem Fall brauchen Sie keinen Vergleichswert anzugeben und das Wert-Feld wird automatisch ausgeblendet.

Wenn Ihnen die vordefinierten Möglichkeiten, z.B. die verfügbaren Vergleichsmöglichkeiten der Änderungstypen, nicht ausreichen, steht Ihnen noch die Möglichkeit zur Verfügung, mittels eines eigenen Skript praktisch jeden beliebigen Vergleich zu realisieren, auch wenn hierzu ein paar Kenntnisse in Skript-Programmierung und etwas Wissen über die internen Abläufe in einer MyTISM-Anwendung nötig sind.

Das Skript wird für jeden Transaktionsschritt (BP) der aktuellen Transaktion (BT), in der das angegebene Attribut gesetzt, gelöscht oder geändert wurde, einmal ausgeführt. Wenn das Skript für mindestens eine der BPs "true" zurückliefert, gilt das Auslösekriterium als erfüllt; wenn es für alle BPs nur "false" liefert als "nicht erfüllt".

Im Skript stehen folgende vordefinierte Variablen zur Verfügung:

bp

Das BP-Objekt, welches gerade überprüft wird.

bo

Das Objekt (BO), welches erstellt/geändert/gelöscht wurde (kann null sein).

valueNew

Der neue/gesetzte Wert, aus dem BP-Objekt (als Java-Objekt! Kann null sein).

valueOld

Der alte/vorher gesetzte Wert, aus dem BP-Objekt (als Java-Objekt! Kann null sein).

valueCompare

Der von Ihnen eingegebene (Vergleichs)Wert (bereits umgewandelt in Java-Objekt! Kann null sein wenn Sie keinen Wert eingegeben haben bzw. der gewählte Änderungstyp keinen Vergleichswert erfordert und das Feld ausgeblendet war).

schema

Das SchemaI für die aktuelle MyTISM-Installation.

attribute

Das beim Auslösekriterium angegebene AttributeI (nicht der Name, sondern das Java-Objekt! Kann null sein).

type

Der CBOType des beim Auslösekriterium angegebenen Attributes (kann null sein).

kriterium

Das AusloeseKriterium-Objekt (wird eher selten benötigt).

log

Ein Logger-Objekt (Name "de.ipcon.db.core.AusloeseKriterium") mit dem Debug- und andere Meldungen ins Server-Log ausgegeben werden können.

Note
Bitte verwechseln Sie dieses Skript nicht mit der unten erwähnten Möglichkeit eines "globalen" Auslöseskript für den gesamten Hinweis. Das oben beschriebene Skript stellt nur eine Option dar, weitere Vergleichsmöglichkeiten für Auslösekriterien zu realisieren. Es ist nur ein Teil dieses einzelnen Auslösekriteriums und bezieht sich immer nur auf Änderungen an einem einzelnen Attribut.

Auslösung mittels Auslöseskript (Unter-Reiter "Skript")

Ein Auslöseskript gibt Ihnen vollkommene Freiheit, um die Auslösung eines Hinweises zu bestimmen; um dieses Feature benutzen zu können, müssen sie allerdings über gewisse Kenntnisse in Skript-Programmierung und etwas Wissen über die interne Struktur und Abläufe in MyTISM-Anwendungen verfügen.

Mittels eines Auslöseskripts können Sie in jeder von Ihnen gewünschten Art und Weise überprüfen, ob der Hinweis ausgelöst werden soll, oder nicht. Wenn das Skript "true" zurückliefert, wird der Hinweis ausgelöst; bei "false" nicht.

if( bo.PreisInCentNN.intValue() > bo.MaxPreisInCentNN.intValue() )
   if( ! bo.PreisueberschreitungErlaubtNN.booleanValue() )
      return true
return false

Im Skript stehen folgende vordefinierte Variablen zur Verfügung:

bo

Das Objekt (BO) welches erstellt/geändert/gelöscht wurde.

schema

Das SchemaI für die aktuelle MyTISM-Installation.

bp

Das BP-Objekt, welches gerade überprüft wird.

kriterium

Das AusloeseKriterium-Objekt (eher uninteressant).

log

Ein Logger-Objekt (Name "de.ipcon.db.core.AusloeseKriterium") mit dem Debug- und andere Meldungen ins Server-Log ausgegeben werden können.

Das Skript wird für jeden Transaktionsschritt (BP) einmal ausgeführt, d.h. beim Speichern eines Formulars im Normalfall mehrmals, wenn sich mehrere Werte geändert haben. Es reicht in diesem Fall, wenn das Skript mindestens einmal "true" zurückliefert, um die Auslösung des Hinweises zu veranlassen.

Mindestens eines oder alle gleichzeitig?

Wenn Sie mehrere Kriterien für die Auslösung des Hinweises angeben - also "…​ ein überwachtes Objekt erzeugt wurde", "…​ ein überwachtes Objekt geändert wurde", "…​ ein überwachtes Objekt gelöscht wurde", ggf. Auslösekriterien, ggf. ein Auslöseskript - so wird der Hinweis normalerweise bereits ausgelöst, wenn mindestens eines dieser Kriterien zutrifft (die Kriterien sind mit "oder" verknüpft).

Möchten Sie, dass alle Kriterien gleichzeitig zutreffen müssen, damit die Auslösung erfolgt, so aktivieren sie die Checkbox "Alle Kriterien müssen zutreffen".

Sie haben zwei AuslöseKriterien definiert: * Kontostand, "wird gesetzt auf einen Wert kleiner als", 100 und * Kontostand, "wird gesetzt auf einen Wert größer als", 50 .

Im Normalfall würde der Hinweis immer auslösen, wenn sich der Kontostand ändert, da jede Zahl entweder kleiner als 100 oder größer als 50 ist. Wenn Sie aber "Alle Kriterien müssen zutreffen" setzen, müssen beide Kriterien zutreffen und der Hinweis wird nur ausgelöst, wenn der Kontostand auf einen Wert größer als 50 und kleiner als 100 - also z.B. auf 80 - gesetzt wird.

Wer soll Benachrichtigungen erhalten und wie sollen diese aussehen?

Die Konfiguration für die Benachrichtigungen funktioniert hier genauso wie bereits für einfache Termine beschrieben.

Von wem muss die Änderung stammen?

Wenn der Hinweis nur ausgelöst werden soll, wenn eine Änderung von einem bestimmten Benutzer bzw. einem Mitglied einer bestimmten Gruppe durchgeführt wurde, können Sie dies mit "…​ diesem Benutzer" und "…​ (oder) einem Mitglied dieser Gruppe" angeben.

Wenn Sie für beides einen Wert eintragen, reicht es aus, wenn die Änderung von dem Benutzer oder einem Mitglied der Gruppe gemacht wurde; es ist also nicht erforderlich, dass der angegebene Benutzer auch noch Mitglied der angegebenen Gruppe ist.

Wenn Sie für eines der beiden Kriterien keine Angabe machen, wird nur für das andere Kriterium überprüft, ob die Änderung von diesem Benutzer bzw. dieser Gruppe gemacht wurde (was dann zutreffen muss, damit die Änderung "gewertet" wird). Wenn Sie für beide Eigenschaften keinen Wert angegeben haben, ist vollkommen egal, von welchem Benutzer oder welcher Gruppe die Änderung stammt und die sonstigen Auslösekriterien werden immer überprüft.

Auf dem zweiten Reiter des vorgebauten Formulars für Hinweise sind die Eingabefelder für die weniger benutzten Angaben zusammengefasst.

Ab wann ist der Hinweis aktiv?

Wie bereits früher erwähnt werden Alarme normalerweise sofort aktiv, sobald sie das erste Mal gespeichert werden. Hinweise beginnen also direkt nachdem sie erzeugt wurden, die ihnen zugewiesene Menge von Objekten zu überwachen und bei Eintreten der durch ihre AenderungsKriterien definierten Änderungen Alarme auszulösen.

Es ist jedoch auch möglich anzugeben, dass der Hinweis erst zu einem späteren Zeitpunkt aktiv wird. Bei "Überwachung starten ab" (Reiter "Erweitert") können Sie ein Datum angeben, ab welchem der Hinweis aktiv werden soll. Liegt dieses Datum in der Zukunft, werden erst die ab diesem Datum erfolgenden Änderungen überprüft und für die Auslösung berücksichtigt. Wenn Sie hier keinen Wert angeben oder das angegebene Datum in der Vergangenheit liegt, wird der Hinweis ganz normal sofort aktiv.

Wiedervorlagen

Note
Evtl. sind Wiedervorlagen nicht genau das, was Sie benötigen bzw. was Sie sich darunter vorstellen. Wenn Sie z.B. in Ihrer Anwendung Dokumente mit einem Attribut WiedervorlageAm haben, und Sie möchten, dass jeweils an den dort eingetragenen Daten ein Alarm ausgelöst bzw. eine Benachrichtigung verschickt wird, dann können Sie das mit einem BoBasierterTermin realisieren.

Wiedervorlagen sind sozusagen das Gegenteil der Hinweise. Alarme werden hier ausgelöst, wenn innerhalb einer bestimmten, festgelegten Zeitspanne an einer Menge von überwachten Objekten bestimmte Änderungen nicht durchgeführt wurden.

Beispiel: Der Projektleiter möchte benachrichtigt werden, wenn sich der Status eines Projekts zwei Tage lang nicht geändert hat.

Auf dem ersten Reiter des vorgebauten Formulars für Wiedervorlagen sind die Eingabefelder für die normalerweise benutzten und die unbedingt erforderlichen Angaben zusammengefasst.

Allgemeine Eigenschaften festlegen

Geben Sie der Wiedervorlage einen kurzen aber aussagekräftigen Namen, und ggf. wenn sinnvoll eine längere Beschreibung.

"Alte Alarme nur auslösen wenn nicht älter als" und "Verantwortlicher" können Sie, bei Bedarf, auf dem Reiter "Erweitert" angeben.

Welche Objekte sollen "überwacht" werden?

Die Menge der zu "überwachenden" Objekte wird hier genau so wie für BO-basierte Termine definiert.

Wann soll die Wiedervorlage ausgelöst werden?

Nachdem definiert wurde, welche Objekte überwacht werden sollen, muss festgelegt werden, welche Ereignisse, z.B. Änderungen an diesen Objekten, verhindern sollen, dass die Wiedervorlage ausgelöst wird.

Die Definition der Kriterien, die geprüft werden, erfolgt hier genau so wie für die Hinweise.

Nachdem die Wiedervorlage angelegt wurde, wartet sie für jedes der von ihr "überwachten" Objekte eine festgelegte Zeit.

  • Tritt innerhalb dieser Zeitspanne das gewünschte (durch die Kriterien definierte) Ereignis ein oder wird die gewünschte (durch die Kriterien definierte) Änderung an einem Objekt durchgeführt, wurde damit die Auslösung der Wiedervorlage für das betreffende Objekt verhindert und die Überwachung für dieses Objekt wird beendet (außer wenn "Alarm bleibt auch nach Kriterienerfüllung weiterhin aktiv" gesetzt ist).

  • Bleibt jedoch das gewünschte Ereignis innerhalb dieser Zeitspanne aus oder tritt die gewünschte Änderung für ein Objekt nicht ein, so löst die Wiedervorlage am Ende der Zeitspanne für das betreffende Objekt Alarm aus. Dann beendet sie die Überwachung für das betreffende Objekt (außer wenn "Alarm bleibt auch nach Auslösung weiterhin aktiv" gesetzt ist).

Wie oben erwähnt, wird das genaue Verhalten der Wiedervorlage mittels dreier Einstellungen definiert:

Inaktivitätszeit "…​ die Auslösekriterien …​ NICHT zutrafen."

Dies gibt die Zeitspanne an, welche die Wiedervorlage auf das Eintreten der Ereignisse bzw. Änderungen warten soll.

Beispiel: Wollen Sie benachrichtigt werden, wenn sich an einem Projekt zwei Tage nichts getan hat, so geben Sie hier "2d" an.

Neuterminierung nach Aufschub "Alarm bleibt auch nach Kriterienerfüllung weiterhin aktiv", Reiter "Erweitert"

Normalerweise wird die Überwachung eines Objektes beendet, nachdem die definierte Änderung für dieses Objekt innerhalb der Inaktivitätszeit eingetreten ist und die Wiedervorlage für dieses Objekt damit verhindert wurde.
Wollen Sie jedoch, dass die Überwachung auch weiter fortgeführt wird, obwohl das gewünschte Ereignis oder die gewünschte Änderung einmal eingetreten ist, so setzen sie dieses Flag. Wenn das Flag gesetzt ist, heißt das im Endeffekt, dass das gewünschte Ereignis oder die gewünschte Änderung regelmäßig immer wieder eintreten muss, um zu verhindern, dass die Wiedervorlage letztendlich ausgelöst wird.

Beispiel: Der Fertigstellungsstand eines Projektes muss mindestens einmal jeden Tag aktualisiert werden.

Neuterminierung nach Auslösung "Alarm bleibt auch nach Auslösung weiterhin aktiv", Reiter "Erweitert"

Normalerweise wird die Überwachung eines Objektes ebenfalls beendet, nachdem die definierte Änderung für dieses Objekt innerhalb der Inaktivitätszeit nicht eingetreten ist und die Wiedervorlage für dieses Objekt ausgelöst wurde, d.h. für jedes überwachte Objekt löst die Wiedervorlage nur ein einziges Mal einen Alarm aus (es gibt gewisse Ausnahmen, s.u.).
Wollen Sie jedoch, dass die Überwachung auch danach weiter fortgeführt wird, so setzen sie dieses Flag. Wenn das Flag gesetzt ist, heißt das im Endeffekt, dass regelmäßig wieder nach erneutem Ablauf der Inaktivitätszeit ein Alarm für das betreffende Objekt ausgelöst wird.

Beispiel: Wenn eine Rechnung nicht innerhalb eines Tages bezahlt wurde, soll an jedem folgenden Tag eine Benachrichtigung darüber versandt werden, nicht nur einmal.

Auf dem zweiten Reiter des vorgebauten Formulars für Wiedervorlagen sind die Eingabefelder für die weniger benutzten Angaben zusammengefasst. Im aktuellen Beispiel sind die Flags zur Neuterminierung nach Auslösung und zur Neuterminierung nach Beschwichtigung/Aufschub ausnahmsweise gesetzt; für die meisten Anwendungsfälle macht es Sinn

Wer soll Benachrichtigungen erhalten und wie sollen diese aussehen?

Die Konfiguration für die Benachrichtigungen funktioniert hier genauso wie bereits für einfache Termine beschrieben.

Wiedervorlage-Status

Wenn eine neue Wiedervorlage angelegt wird, legt MyTISM automatisch für alle überwachten Objekte sogenannte Wiedervorlage-Status an, welche Daten beinhalten, die für die korrekte Überwachung und Auslösung des Alarms für die Objekte benötigt werden.

Je nach Anzahl der zu überwachenden Objekte, kann das Anlegen der Status einige Zeit in Anspruch nehmen. Die Wiedervorlage wird erst dann aktiv, wenn alle benötigten Status angelegt wurden.

Benachrichtigung bei Alarm-Auslösung

Wenn ein Alarm ausgelöst wird, werden nacheinander drei verschiedene Mechanismen in Gang gesetzt:

  1. Die hartkodierte notify()-Methode der Klasse, zu der der Alarm gehört, wird aufgerufen.

  2. Ein evtl. für den Alarm eingetragenes Benachrichtigungsskript wird ausgeführt.

  3. Die Standard-Benachrichtigungen werden ausgeführt.

Hartkodierte notify()-Methode

Jede Subklasse der Alarm-Basisklasse Alarm erbt deren notify()-Methode. Bei der Auslösung eines Alarms wird diese Methode automatisch vom Alarmsystem aufgerufen und kann beliebige Aktionen ausführen.

Wenn die Methode "true" zurückliefert, wird angenommen, dass alle bei der Auslösung erforderlichen bzw. gewünschten Aktionen vollständig durchgeführt wurden; in diesem Fall werden weder das Benachrichtigungsskript noch die Standard-Benachrichtigungen ausgeführt.

Bei allen standardmässig in MyTISM implementierten Alarm-Klassen (also den in dieser Dokumentation erwähnten) tut diese Methode nichts und liefert "false" zurück, so dass mit der Bearbeitung fortgefahren wird; sie brauchen sich in diesem Fall also keine weiteren Gedanken hierzu zu machen. Es kann allerdings sein, dass für Ihre MyTISM-Installation spezielle Subklassen von Alarm existieren, die eine "richtige" notify()-Methode besitzen; wenn dies der Fall ist, kann Ihnen Ihr MyTISM-Administrator weitere Informationen hierzu geben.

Benachrichtigungsskript "Sende Benachrichtigungen mittels dieses Skripts", Reiter "Erweitert"

Wenn Ihnen die Standard-Möglichkeiten (s.u.) für Benachrichtigungen nicht ausreichen, können Sie mit Hilfe der Benachrichtigungsskript-Eigenschaft der Alarme weitere Tätigkeiten ausführen lassen.

Sie können hier ein Stück Groovy-Code angeben, das in der von Ihnen gewünschten Art und Weise Benachrichtigungen auslöst oder auch andere Aktionen ausführt. Wenn das Groovy-Skript "true" zurückliefert, wird angenommen, dass alle erforderlichen Aktionen durchgeführt wurden und die Standard-Benachrichtigungen werden nicht mehr ausgelöst. Wenn das Skript "false" zurückliefert, werden die Standard-Benachrichtigungen zusätzlich zu allen evtl. bereits vom Skript gemachten Aktionen auch noch ganz normal ausgelöst.

Sollte im Skript ein Fehler auftreten (d.h. eine Exception geworfen werden) wird die Bearbeitung ebenfalls abgebrochen, d.h. auch in diesem Fall werden die Standard-Benachrichtigungen nicht mehr ausgelöst.

Beispiel, Mitarbeiter implementiert NotificationReceiverI:
api.getLogger().info( "Alarm " + alarm + " wurde um " + dateNow + " ausgeloest!" )
api.sendNotification( api.getBOById( idBO, de.beispielprojekt.bo.Mitarbeiter.class ) )
Beispiel, Empfänger hängt per Attribut "Benutzer" an auslösendem Objekt:
api.sendNotification( getTriggeringBO().getBenutzer() )
Beispiel, Mail an beliebige e-Mail-Adresse senden:
api.sendNotificationByEmail( "nobody@example.com" )

Es stehen folgende vordefinierte Variablen zur Verfügung:

alarm

Der Alarm, der ausgelöst wurde.

dateNow

Das Datum und die Zeit (als java.util.Date-Objekt) wann der Alarm ausgelöst wurde.

api

Ein Objekt vom Typ BedingterAlarmBenachrichtigungsScriptAPII (für Hinweise und Wiedervorlagen), BO-basierter TerminBenachrichtigungsScriptAPII (für BO-basierte Termine) oder EinfacherTerminBenachrichtigungsScriptAPII (für einfache Termine) welches nützliche Methoden zur Verfügung stellt.

log

Ein Logger-Objekt (Name "de.ipcon.db.alarm.BenachrichtigungsScriptAPI", das gleiche Objekt was auch api.getLogger() liefert) mit dem Debug- und andere Meldungen ins Server-Log ausgegeben werden können.

Für BO-basierte Termine, Hinweise und Wiedervorlagen, die sich ja immer auf BOs beziehen, stehen noch zwei zusätzliche Variablen zur Verfügung:

idBO

Der ID des BOs, für welches der Alarm ausgelöst wurde.

bot

Der BOT des BOs, für welches der Alarm ausgelöst wurde (kann evtl. null sein).

Für Hinweise und Wiedervorlagen, die ja immer durch ein Ereignis ausgelöst werden, ist schlussendlich noch eine Variable definiert:

bt

Die BT, welche den Alarm ausgelöst hat.

Die Benachrichtigung(en) im Geburtstags-Gratulation-Beispiel (siehe #alarme_bobasiertertermin) werden über ein Benachrichtigungsskript versandt.

Standard-Mechanismus

Wenn ein Alarm ausgelöst wird, werden alle dafür eingetragenen Benutzer benachrichtigt (sofern das Benachrichtigungssystem aktiviert und richtig konfiguriert ist).

Ein Benutzer erhält für eine Alarm-Auslösung immer nur eine Benachrichtigung, auch wenn z.B. für einen Alarm mehrere Gruppen eingetragen wurden und der Benutzer Mitglied in mehreren dieser Gruppen ist oder der Benutzer selbst ebenfalls für den Alarm eingetragen wurde.

Der Betreff und der Text der entsprechenden Benachrichtigungen können für jeden Alarm eigens definiert werden. Die Definition der Textvorlagen erfolgt im Format GSP (Groovy Server Pages); die eigentlichen Texte werden bei der Auslösung dynamisch aus diesen Vorlagen generiert.

Eine genaue Beschreibung von GSP würde hier zu weit führen; für Informationen dazu siehe http://groovy.codehaus.org/Groovy+Templates. Hier nur ein kleines

Beispiel:
Dies ist eine Benachrichtigung fuer ${benutzer.getName()}!
Der Alarm ${alarm.getName()} wurde am ${api.formatDate( dateNow, "dd.MM.yyyy" )} um ${api.formatDate( dateNow, "HH:mm:ss" )} Uhr ausgeloest.

Folgende vordefinierte Variablen stehen zur Verfügung:

benutzer

Der Benutzer, für den die Benachrichtigung gedacht ist; kann null sein, wenn die Benachrichtigung mittels des Benachrichtigungsskripts erzeugt wurde und nicht an einen Benutzer, sondern ein anderes Objekt verschickt wurde.

empfaenger

Das Empfaenger-Objekt, für das die Benachrichtigung gedacht ist; ist immer gesetzt. Wenn die Benachrichtigung an einen Benutzer ging, ist dieser Wert gleich dem Wert der Variable "benutzer".

alarm

Der Alarm, der ausgelöst wurde.

dateNow

Das Datum und die Zeit (als java.util.Date-Objekt) wann der Alarm ausgelöst wurde (oder genauer: Wann das MyTISMBenachrichtigungs-Objekt erstellt wurde; diese Zeiten können sich um einige Sekunden unterscheiden).

api

Ein Objekt vom Typ TemplateScriptAPII, welches nützliche Methoden zur Verfügung stellt.

Für BO-basierte Termine, Hinweise und Wiedervorlagen, die sich ja immer auf BOs beziehen, steht noch eine zusätzliche Variable zur Verfügung:

bo

Das Objekt (BO) für welches der Alarm ausgelöst wurde.

Für Hinweise und Wiedervorlagen, die ja immer durch ein Ereignis ausgelöst werden, ist schlussendlich noch eine Variable definiert:

bt

Die BT, welche den Alarm ausgelöst hat.

Schliesslich können spezielle Unterklassen von Alarmen evtl. auch noch weitere Variablen zur Verfügung stellen.

Note
Wenn mehrere von einem Hinweis oder einer Wiedervorlage überwachte BOs erstellt, geändert oder gelöscht wurden, wird für jedes dieser BOs überprüft, ob eine Auslösung erfolgt; wenn ja wird für jedes entsprechende Objekt (BO) eine Benachrichtigung versendet.

Logging/Historie und AlarmAusloesungen-Objekte

Jede Auslösung eines Alarms für einen Benutzer mittels der Standard-Benachrichtigungsmethode - nicht z.B. für via Benachrichtigungsskript benachrichtigte Benutzer - wird automatisch "mitgeloggt". Für jede Auslösung wird ein Objekt vom Typ AlarmAusloesung angelegt, mit den Informationen, welcher Alarm wann und für welchen Benutzer ausgelöst wurde; für BO-basierte Termine, Hinweise und Wiedervorlagen Außerdem noch, für welches Objekt die Auslösung verursacht hat.

Die AlarmAusloesungen können z.B. über das Lesezeichen "Alarme → AlarmAusloesungen" eingesehen werden; ebenso kann jeder Benutzer die für ihn erfolgten AlarmAusloesungen im Reiter "Alarmsystem → Alarmauslösungen" im (automatisch generierten) Benutzerformular einsehen.

Die Eigenschaft "Bestaetigt" der AlarmAusloesungen wird z.Zt. noch nicht benutzt.

Sonstige Infos

"Verpasste" bzw. "Verspätete" Auslösung

Es kann passieren, das ein Alarm eigentlich zu einem bestimmten Zeitpunkt hätte ausgelöst werden sollen, dies jedoch nicht passiert ist, weil zu diesem Zeitpunkt das Alarmsystem deaktiviert war.

In solchen Fällen wird der Alarm dann normalerweise sofort ausgelöst, sobald das Alarmsystem wieder aktiviert wird.

Dieser Fall kann z.B. auch dann eintreten, wenn bei einer MyTISM-Installation mit synchronisierenden Instanzen eine Änderung, die z.B. einen Hinweis auslöst, auf einer der "Slave"-Instanzen (ohne Alarmsystem) passiert ist. Wenn nun diese Änderung z.B. durch Netzwerkprobleme oder falsch konfigurierte Synchronisationseinstellungen erst nach einer längeren Zeit auf die "Master"-Instanz (mit aktiviertem Alarmsystem) übertragen wird, wird auch hier der Hinweis erst mit dieser Verspätung ausgelöst.

Durch die Angabe von "Alte Alarme nur auslösen wenn nicht älter als" (siehe Abschnitt #alarme_eigenschaften) können Sie festlegen, ob bzw. mit wie viel Verspätung solche Alarme trotzdem noch ausgelöst werden.

Neuinitialisierung der Objekt-Status für BO-basierten Terminen und Wiedervorlagen

Bestimmte Änderungen an bereits bestehenden BO-basierten Terminen oder Wiedervorlagen können dazu führen, dass die für die interne Verarbeitung gespeicherten Informationen zur Auslösung des Alarms für die überwachten Objekte neu initialisiert werden (müssen).

Dies geschieht z.B. bei einem Wechseln der BOMaske ("Überwache die Objekte …​") oder auch wenn nur "innerhalb" der Maske die Entitaet-Eigenschaft geändert wurde.

Bei solchen Änderungen werden die zum Alarm zugehörigen *AlarmStatus neu initialisiert, genauso, als wenn der Alarm neu angelegt worden wäre.

Folgende Änderungen führen zur Neuinitialisierung:

  • Für BO-basierter Termin: Jede Änderung an Attribut, Script, Maske sowie das Aktivieren von NeuterminierungNachAusloesung.

  • Für Wiedervorlagen: Jede Änderung an AusloeseKriterien, AchtetAufBOAendern, AchtetAufBOErstellen, AchtetAufBOLoeschen, Script, AenderungVonBenutzer, AenderungVonGruppe, AKsMitUndVerknuepfen, UeberwachungStartenAb, Inaktivitaetszeit und Maske sowie das Aktivieren von NeuterminierungNachAusloesung.

CBOFormat

Diese relativ kleine Klasse hat mittlerweile einen derart hohen Stellenwert im Umgang mit MyTISM erlangt, dass ich ihm hiermit ein eigenes Kapitel widme - ohne ein fundiertes Verständnis der Leistungen dieses Mechanismus macht man sonst viele Sachen um Magnituden komplizierter als nötig - ob es Felder im Report oder einfach "schöne" Lesezeichen sind. Außerdem kann das CBOFormat im Export-Fall sehr nützlich sein.

Was ist CBOFormat?

Ursprünglich wurde CBOFormat entwickelt, um Variablen in Texten auszutauschen und dabei jeglichen "echten" Programmcode zu vermeiden. Dabei ging es um die Abbildung von Regeln wie "Wenn der Vorname leer ist, darf das Komma nach dem Nachnamen nicht gedruckt werden", "drucke die Emailadresse nur bis zum @ und den Rest in die nächste Zeile", oder "wenn das Feld nicht leer ist, dann kommt da noch folgender Text hin".

Alles Geschichten, für die man normalerweise ein Stückchen Programmcode braucht, aber wer schon einmal versucht hat, geschweifte Klammern und diverse andere Sonderzeichen vor dem Rest des Textes zu maskieren - man denke einmal nur an nötige Zeilenumbrüche innerhalb eines etwas komplizierteren Scripts, von Einrückungen ganz zu schweigen - wird ein Lied davon singen können, wie lesbar dann der Programmtext noch ist, ganz zu schweigen von der leicht "zerscripteten" Umgebung.

Es mußte also eine Art Pseudocode her, der mit wenig Ballast diese Aufgaben bewerkstelligen kann und trotzdem den Funktionsumfang möglichst komplett abdeckt. Hier ist er:

Zunächst sei erwähnt, daß CBOFormat immer einen Satz Variablen und ein sogenanntes Root-Objekt zur Auswertung übergeben bekommt, und außerdem kompletten Zugriff auf das MyTISM-Schema hat und somit alle Entitäten deren Attribute kennt.

Nun zum ersten Beispiel:

Einen Ansprechpartner mit Familienname und Rufname soll konsistent formatiert werden. Gehen wir mal von einem Ansprechpartner-Objekt mit Familienname, Vorname, Titel, Geburtstag und AnzahlKinder aus. Der Ansprechpartner soll in der Form "Familienname, Rufname, Titel" gedruckt werden; falls aber der Rufname nicht angegeben ist, soll das Komma nicht mit angedruckt werden; ebenso soll beim Titel verfahren werden. Das sieht so aus:

Familienname(', 'Rufname)(', 'Titel)

Die runde Klammer bewirkt, dass wenn ein Feld darin leer ist, der ganze Konstrukt verschwindet. Mit ? ' ? Die ' um das Komma leiten statischen Text ein. Die Klammer bindet sozusagen einen Auswertungsversuch zusammen - geht er schief, dann verschwindet er komplett.

Das ganze kann man auch etwas weiter ausbauen:

(Familienname):('Kein Familienname angegeben!')(', 'Rufname)(', 'Titel)

Wie man sieht, kann man hinter einer Klammer einfach einen Doppelpunkt und eine weitere Klammer angeben, die dann benutzt wird, wenn die erste Klammer weg fällt. Das ist fast so wie die if-then-Makros in Word zum Beispiel, nur dass man in unserem Fall so viele Klammern mit Doppelpunkten verketten kann, wie man will (im Fall einer polymorphen Relation kann das sehr nützlich sein).

Mann stelle sich jetzt vor, daß man ein Korrespondenz-Objekt an die Hand bekommt und nun diesen Ansprechpartner in einer CBOFormat-Klausel formatieren soll: (wie nehmen an, daß der Ansprechpartner im Korrespondenz-Objekt über das Attribut "Adressat" definiert ist)

(Adressat.Familienname):('-')(', 'Adressat.Rufname)(', 'Adressat.Titel)

Sieht umständlich aus, weil das Adressat sich bei vermehrter Nutzung sehr oft angegeben werden muß. Dafür gibts einen einfacheren Weg: Vorklammern über [:

Adressat[(Familienname):('-')(', 'Rufname)(', 'Titel)]

Eine besonderes Verhalten zeigt sich bei der Adressierung von Entitäten. Nehmen wir das Beispiel von vorhin, und notieren einfach folgendes:

Adressat

Da nun das Ergebnis der Evaluierung ein BO ist, wird dessen Schema-Description zur Formatierung herangezogen, wenn also im Schema ein

... description="(Familienname):('-')(', 'Rufname)(', 'Titel)"

steht, dann ist die Ausgabe identisch mit dem oberen Beispiel.

Den gleichen Effekt hat die Verwendung der Variable '.', welche für die Root-Variable steht; das ist recht nützlich, um eine bestimmte Information vor oder nach die schon vorhandene (und gegebenenfalls komplexe) Beschreibung hinzuzufügen:

Adressat['['Id'] '.]

Obiges Beispiel gibt zum Beispiel den Adressaten wie im Beispiel davor aus, allerdings mit seiner Id in eckigen Klammern.

Datum und Zeitwert-Formatierung

Eine weitere interessante Möglichkeit ist das Formatieren von Datum und Zeitwerten. Das geschieht einfach über das Anhängen einer geschweiften Klammer direkt an den Wert:

"Geburtstag{dd.MM.yyyy, HH:mm:ss 'Uhr'}"

Das würde den Geburtstag in der Form "24.04.1971, 15:35:00 Uhr" ausgeben.

Die verwendbaren Zeichen finden sich in der nachstehenden Tabelle:

Table 2. Die Bezeichner des SimpleDateFormat

Symbol

Datums- oder Zeitkomponente

Beispiel

G

Zeitalter

v.Chr, n.Chr

y

Jahr

2004, 04

Y

Wochenjahr

2004, 04

M

Monat im Jahr

Juli, Jul, 07

w

Woche im Jahr

27

W

Woche im Monat

2

D

Tag im Jahr

189

d

Tag im Monat

10

F

Wochentag im Monat

2 (also der 2. Dienstag im aktuellen Monat)

E

Wochentag textuell

Dienstag, Di

a

AM/PM

PM

H

Stunde im Tag (0-23)

0

k

Stunde im Tag (1-24)

24

K

Stunde in AM/PM (0-11)

0

h

Stunde in AM/PM (1-12)

12

m

Minute in der Stunde

30

s

Sekunde in der Minute

55

S

Millisekunden

978

z

Zeitzone Generisch

Pazifische Standardzeit; PST; GMT-08:00

Z

Zeitzone nach RFC822

-0800

'

Textbegrenzer

'Uhr'

Note
Sollte in der Formatierung die Woche im Jahr benutzt werden (w), sollte für das Jahr das Wochenjahr benutzt werden (Y statt y). Sonst könnte es zu Verwirrungen bei Daten am Anfang des Jahres kommen, da die ersten Tage oft noch in die letzte Woche des Vorjahres fallen.

Zahlen-Formatierung

Zahlen lassen sich ebenso wie formatieren:

"AnzahlKinder{#,##0.000}"

würde zum Beispiel die Anzahl Kinder auf 3 Stellen nach dem Komma, die Tausender dreistellig gruppiert ausgeben.. :-)

Ein Zahlenformat beinhaltet optional ein negatives Format, abgetrennt durch ? ; ?, z.B. #,##0.00+;#,\##0.00-.

Die Definition der Symbole in nachstehender Tabelle:

Table 3. Die Bezeichner des DateFormat

Symbol

Ort

Landesabhängig

Bedeutung

0

Nummer

ja

Ziffer

#

Nummer

ja

Ziffer, 0 wird nicht gedruckt

.

Nummer

ja

Dezimaltrenner

-

Nummer

ja

Minus-Symbol

,

Nummer

ja

Gruppierungs-Symbol

E

Nummer

ja

Mantissen/Exponent-Separator.

;

Formattrenner

ja

Trennt positives von negativem Format.

%

Pre/Suffix

ja

Multipliziere mit 100 und zeige als Prozent.

\u2030

Pre/Suffix

ja

Multipliziere mit 1000 und zeige als Promille.

\u00A4

Pre/Suffix

ja

Platzhalter für das Währungssymbol, wird ersetzt durch das aktuelle Währungssymbol. Doppelt zeigt es das internationale Währungssymbol. Wenn es innerhalb eines Formates benutzt wird, tauscht es den Dezimaltrenner gegen den Währungsdezimaltrenner aus (ist in manchen Ländern üblich).

'

Pre/Suffix

nein

Textbegrenzer für spezielle Zeichen.

Funktionsaufrufe

Ein weiteres, allerdings selten benutztes Feature ist die Verwendung von Funktionen im CBOFormat. Das liegt nicht zuletzt daran, daß durch die Verwendung eines reinen Forward-Parsers eigentlich immer nur der aktuelle Wert zur Verfügung steht und somit nur reine String-Modifikationen möglich sind. Nichts desto trotz seien sie hier kurz vorgestellt. Eingeleitet werden die Funktionen mit | (Pipe), die Parameterübergabe erfolgt in Klammern. Die Klammern nach dem Funktionsnamen sind obligatorisch. Meist werden die Funktionen erst dann wirklich nützlich, wenn man sie zusammen mit der runden Klammer einsetzt.

equals(s)

Vergleicht den gerade aktiven String mit dem angegebenen String. Fällt der Vergleich positiv aus, bleibt der aktive String unverändert, wenn nicht, wird der aktive String geleert.

notEqual(s)

Vergleicht den gerade aktiven String mit dem angegebenen String. Fällt der Vergleich negativ aus, bleibt der String unverändert, wenn nicht, wird der aktive String geleert.

(Ansprechpartner.Familienname|notEqual('bla')):('blablabla')
Das Beispiel gibt im Falle eine Familiennamens "bla" statt dessen ein
"blablabla" aus
reverse()

Dreht den aktuellen String rückwärts.

cutLeftFrom(s)

Schneidet den aktuellen String an der Kante des übergebenen Strings links ab.

Ansprechpartner.Emailadresse|cutLeftFrom('@')
Ergibt im Falle von "foo@bar.com" ein "foo".
cutRightFrom(s)

Schneidet den aktuellen String an der Kante des übergebenen Strings rechts ab.

Ansprechpartner.Emailadresse|cutRightFrom('@')
Ergibt im Falle von "foo@bar.com" ein "bar.com".
ifTrue()

Falls der aktuelle String nicht leer ist, ist er es danach.

(Ansprechpartner.EmailAdresse|ifTrue()):('ja')
Gibt für den Fall, daß der Ansprechpartner eine Mailadresse hat, ein
"ja" zurück.
ifFalse()

Falls der aktuelle String leer ist, ist er danach nicht mehr leer.

(Ansprechpartner.EmailAdresse|ifFalse()):('nein')
Gibt für den Fall, daß der Ansprechpartner keine Mailadresse hat, ein
"nein" zurück.
stripLF()

Diese Operation entfernt alle Zeilenschaltungen aus dem aktuellen String.

left(count)

Gibt die ersten count Zeichen vom aktuellen String zurück.

right(count)

Gibt die letzten count Zeichen vom aktuellen String zurück.

strip()

Entfernt alle Leerzeichen um den aktuellen String herum.

Script-Verwendung

Nun gibt es immer noch Situationen, da geht’s ohne Script einfach nicht. Dafür kann man auch ein Beanshell-Script in doppelter geschweiften Klammern angeben und dann den passenden String zusammenbasteln.

Um an die erforderlichen Daten heranzukommen wird die Root-Referenz als "bo" und alle im Variablenhash definierten Variablen unter ihrem dort hinterlegten Namen eingeblendet.

'vorgestern war'{{new SimpleDateFormat("EEE").format(Calendar.getInstance().roll(Calendar.DAY_OF_YEAR,-2).getTime())}}

Dabei kommt die Eigenschaft der Beanshell, das letzte Ergebnis als Rückgabewert zu liefern zu Hilfe, sonst wäre bei dem Beispiel noch ein return …​; notwendig gewesen.

Wo kann man das CBOFormat nun überhaupt einsetzen?

Das CBOFormat findet seine Anwendung zunächst einmal in der Schema-Definition, und zwar in Form des "description" Attributs. Es soll helfen, die Entitäten mit einer Art textueller Beschreibung auszustatten (Programmierern als toString() Methode bekannt). Ich wollte aber aus verschiedenen Gründen nicht die toString() Methode überladen, weil eine für das Debugging wichtige Information, der hashCode, mit angegeben wird, der aber für den Benutzer völlig nichtssagend ist; zudem sind mehrere solcher descriptions denkbar (wenn auch (noch) nicht implementiert) oder können auch ad hoc angefordert werden.

Dafür hat jede vom System generierte Entität eine describe-Methode, die einen optionalen Stringparameter bekommt und auf diesem Weg einen String der gewünschten Form ausgibt. Das kann vom Reportgenerator über direkte Objektreferenzen direkt benutzt werden (zum Beispiel $F{THIS}.describe()), oder innerhalb einer BO-Methode für Debugging Ausgaben, Export-Formate…​

In Solstice begegnet man dem CBOFormat ständig. Überall, wo ein "displayFormat" angegeben werden kann, ist das CBOFormat am Werk; in Lesezeichen die Spaltendefinition, in Formularen für Labels, TablePopups, in den Fenstertiteln etc.

MEX - Makros und erweiterte Query-Funktionen

Leider hat der bislang im MyTISM verwendete OQL-Parser nicht alle Funktionen, die man sich wünschen könnte. Die bislang fehlenden Funktionen sind unter anderem Subqueries, Subclass-Casting, explizite Joins, Unions und Fetch-Strategien. Leider können wir nicht alle Problem auf einmal lösen, aber dennoch gibt es zumindest für die Union und die dadurch substituierbaren Subclass-Castings eine Lösung: MEX.

Diese Geschichte soll aber kein Notnagel sein, um Probleme zu kaschieren, um später wieder ausgebaut zu werden. Insgesamt ist es eine gute Möglichkeit, in einigen Queries Roundtrips zu vermeiden oder einfach in der GUI komplexe Queries überhaupt handhabbar zu machen. Ein Präprozessor eben, der ohnehin verfügbare Möglichkeiten der API dem Benutzer zugänglich macht.

Definition MEX

Die Auswertung der einzelnen Konstrukte erfolgt an verschiedenen Stellen im Kernel, je nachdem wer sich dafür zuständig fühlt. MEX, so der Name der Sprache, besteht im wesentlichen aus verschachtelten geschweiften Klammer-Blocks. Die Funktionsweise ist die, daß ein bestehender Text mit den Tags versehen wird und auf der Serverseite ausgewertet wird, wobei in mehreren Schritten die Klammer-Blocks entfernt werden. Bleiben nach der Auswertung noch unbehandelte Klammer-Blocks übrig, kommt es zu einer Fehlermeldung, in der der unausgewertete Klammer-Block erwähnt wird, so daß man den Fehler hoffentlich direkt sehen kann.

Der Core, der MEXTransformer, beherrscht nur 3 Konstrukte:

Sichtbare Variablendefinition

Definiert eine Variable und ersetzt die Definition an dieser Stelle mit dem definierten Wert.

{hausnr=4711}

wird ersetzt durch

4711

und die Variable hausnr hat im Anschluß den Wert 4711.

Unsichtbare Variablendefinition

Definiert eine Variable und entfernt die Definition aus dem Quelltext.

{!hausnr=4711}

verschwindet komplett und die Variable hausnr hat im Anschluß den Wert 4711.

Variablenexpansion

Ersetzt die angegebene Variable durch ihren Wert.

{=hausnr}

wird ersetzt durch

4711

(vorausgesetzt, die Variable wurde mit diesem Wert definiert).

Wichtig zu wissen ist, daß die Variablendefinition und die Auswertung in verschiedenen Stufen passieren, die Variable hat also den Wert, den sie zuletzt im Text hatte im Anschluß in jeder Variablen, wo auch immer sie erwähnt wurde. Ein Beispiel:

{a=1}{=a}+{=a}={a=2}{=a}

ergibt

2+2=2

Auf diese Weise kann man allerdings Werte schon benutzen bevor die Variablen ihren Wert erhalten haben. Wir können innerhalb der GUI später nicht explizit etwas früher oder später definieren wollen. Die Variablen werden genau einmal definiert und benutzt, ändern aber ihre Werte nicht während der Auswertung.

Das sind bereits alle Konstrukte, die MEX (im Moment) selbst beherrscht. Alle anderen kommen von anderen "Schichten" dazu. Mehr dazu im nächsten Kapitel.

Support auf Queryseite

MEX wird momentan auf Seiten des Backend ausschließlich für Queries benutzt. Der zuständige CastorPersistenceHandler, der die Queries im Backend an den ORM Castor abliefert, implementiert folgenden Konstrukt:

          select a from de.ipcon.db.core.Formular a where a.BOTyp.Name="Beleg"
{UnionAll select a from de.ipcon.db.core.Schablone a where a.BOTyp.Name="Beleg"}

(Man sollte erwähnen, daß BOTyp in beiden Fällen eine Eigenschaft ist, die nicht von der Oberklasse Struktur kommt und daher nicht über Struktur her fragbar ist). Dieses Beispiel gibt in einem einzigen Roundtrip zum Backend die Ergebnisse in einer einzigen Antwort zurück. Man kann diese Konstruktion auch beliebig oft wiederholen, ganz, wie die Situation es erfordert. Dabei sind auch Projektionen und andere Tricks kein Problem:

          select a.Benutzer from de.ipcon.db.core.Gruppe a where not Ldel and a.Formulare.BOTyp.Name="Beleg"
{UnionAll select a from de.ipcon.db.core.Benutzer a where not Ldel and a.Gruppen.Name="Admins"}
Note
Im Moment sind Union und UnionAll funktionsgleich, weil die Dopplerfindung im entsprechenden QueryIterator noch nicht durchgebaut ist

Support in Solstice

Schließlich und endlich unterstützt die Solstice-Oberfläche das ganze dadurch, daß die CBOTextQuery, also diejenige Query, die durch <Query type="Text"/> gewählt wird, Teile der Query als Variablen bereits deklariert. So wird folgende typische Query nach Benutzern:

select a from de.ipcon.db.core.Benutzer where not Ldel

wie folgt umgeschrieben:

{!select=select a from}
{!where=a where}
{!constraints=not Ldel}
{=select} de.ipcon.db.core.Benutzer {=where} {=constraints}

Sieht jetzt sehr merkwürdig aus, ist aber absolut funktionsidentisch mit der ersten Version. Zwei Mechanismen sind jetzt interessant: Zum einen kann die letzte Zeile der Query mit dem Tagnamen <template> ausgetauscht werden. Man kann also so etwas schreiben:

<Query type="Text">
  <template>{=select} de.ipcon.db.core.Benutzer {=where} {=constraints} and Name like = "A%"</template>
</Query>

Jetzt wirds interessant: Jeder Filter, jede Volltextsuche und weitere direkt eingegebene OQL-Schnipsel werden allesamt in der Variablen constraint mit eingearbeitet, so daß der einfache Query oben schnell an Komplexität gewinnt. Jetzt ist es natürlich leicht, den oben erwähnten Union-Konstrukt zu verwenden, um sämtliche Constraints auch schön durch alle Union-Teile mit durchzuschleifen:

<template>
            {=select} de.venice.bo.Rechnung {=where} {=constraints} and (Not Bezahlt or is_undefined(Bezahlt))
  {UnionAll {=select} de.venice.bo.EingangsRechnung {=where} {=constraints} and FreigabeFiBu}
</template>

Es wird noch netter. Es kann ja sein, daß man verschiedene Attribute nicht gleichlautend und/oder gar nicht in bestimmten Subklassen zu finden sind, aber dennoch über die netten grafischen Filter zu wählen sein sollen. Für diesen Zweck kann man die Filter nun gruppieren. Man gibt einem Filter nun einfach ein group="R" mit, und damit landen seine Query-Constraints in einer Variablen namens "constraintsR". Ein Beispiel:

<Query type="Text">
  <template>
              {=select} de.venice.bo.Rechnung {=where} {=constraints} and {=constraintsAR} and (Not Bezahlt or is_undefined(Bezahlt))
    {UnionAll {=select} de.venice.bo.EingangsRechnung {=where} {=constraints} and {=constraintsER} and FreigabeFiBu}
  </template>
  <filter type="bool" group="AR" title="Ausgangs-Rechnung hat Skonto">
    <ifTrue>is_defined(Skonto)</ifTrue>
  </filter>
  <filter type="string" group="AR" title="Kunden-Nr">
    <clause>Kunde.DebitorenNr like "%{}%"</clause>
  </filter>
  <filter type="string" group="ER" title="Lieferanten-Nr">
    <clause>Lieferant.KreditorenNr like "%{}%"</clause>
  </filter>
</Query>

Dieses Beispiel verdeutlicht ganz gut die möglichen Szenarien solcher Queries.

Weitere geplante Features sind die Verwendung von Skalaren und Listen als Parameter für und von Subqueries und Fetch-Strategien zur Verbesserung des Unlazy-Verhaltens der GUI (prefetching von Relationen z.B.).

Volltextsuche

Die Volltextsuche erlaubt die einfache und schnelle Suche nach gegebenen Suchbegriffen über alle in der MyTISM-Datenbank gespeicherten Objekte.

Vorbereitung und Konfiguration

Volltextsuche aktivieren

Die Volltextsuche ist normalerweise deaktiviert, d.h. Sie können in Abfragen keine auf der Volltextsuche basierenden Klauseln verwenden. Abfragen mit solchen Klauseln führen bei nicht aktiver Volltextsuche zu einer Fehlermeldung.

Um die Volltextsuche zu aktivieren müssen Sie in der Datei mytism.ini im Abschnitt "Fulltextsearch" das Flag "activateFts" setzen (sollte auch der entsprechende Abschnitt noch nicht existieren, fügen Sie ihn einfach ebenfalls ein):

[Fulltextsearch]
activateFts=1

Einstellungen

Für alle Einstellungen existieren Standardwerte; im Normalfall ist also keine weitere Konfiguration für die Volltextsuche notwendig und meist auch nicht sinnvoll. Lediglich der Parameter max_locks_per_transaction der PostgreSQL-Instanz sollte angepasst werden, da aufgrund der besonderen Gegebenheiten der Volltextsuche die Standardeinstellung dafür nicht ausreichend zu sein scheint.

PostgreSQL: max_locks_per_transaction

Bei der (initialen) Indexierung für die Volltextsuche werden u.U. viele gleichzeitige und relativ langlaufende Anfragen an die PostgreSQL-Datenbank gestellt. Aus diesem Grund kann es nötig sein, den Parameter max_locks_per_transaction in der Datei /etc/postgresql/8.4/main/postgresql.conf (der Pfad kann je nach benutzter Version und Konfiguration ggf. abweichen) zu erhöhen.

Im Normalfall sollte es keine Probleme machen, diesen Wert einfach auf z.B. 1024 zu setzen, was auch für die Indexierung vollkommen ausreichend sein sollte. Nachdem der Wert in obiger Konfigurationsdatei geändert wurde, muss die PostgreSQL-Instanz durchgestartet werden.

Betriebssystem: Mögliche Anzahl gleichzeitig offener Dateien

Je nach Betriebssystem und Konfiguration kann es sein, dass der Wert für die mögliche Anzahl gleichzeitig offener Dateien erhöht werden muss. Ein Symptom dafür sind entsprechende Fehlermeldungen während des Betriebs der Applikation. Da der zu setzende Wert je nach System und Konfiguration verschieden sein kann, können wir hierzu allerdings keine allgemeingültigen Anweisungen oder Standardwerte geben.

indexAllByDefault

Im Gegensatz zu früheren Versionen der Volltextsuche werden jetzt standardmäßig keine Entitäten in den Index aufgenommen; nur Entitäten, die im Schema explizit mittels <fulltext indexed="yes"/> markiert wurden, werden für die Volltextsuche indexiert.

Um diesen Standard zu ändern, so dass erst einmal alle (abgesehen von einer Handvoll systeminterner) Entitäten in den Index aufgenommen werden, können Sie folgende Einstellung verwenden:

Standardmäßig (fast) alle Entitäten indexieren:
[Fulltextsearch]
activateFts=1
indexAllByDefault=1

indexDeletedBOs

Standardmäßig werden auch als gelöscht markierte Objekte für die Volltextsuche indexiert. Wenn Sie dies nicht möchten oder benötigen können Sie die Indexierung von gelöschten Objekten wie folgt deaktivieren:

Gelöschte Objekte nicht indexieren:
[Fulltextsearch]
activateFts=1
indexDeletedBOs=0

spellcheck

Wenn Sie die Spellcheck/"Meinten sie vielleicht…​"-Funktionalität zum Vorschlagen von alternativen Suchwörtern nutzen wollen, müssen Sie diese explizit aktivieren:

Spellcheck/"Meinten sie vielleicht…​"-Funktionalität aktivieren:
[Fulltextsearch]
activateFts=1
spellcheck=1
Important
Um diese Funktionalität nutzen zu können muss sich die JAR-Datei lucene-spellchecker.jar im Classpath befinden.

fetchSize

Mit dieser Einstellung kann bestimmt werden, in welchen "Packen" Objekte bei der Indexierung aus der Datenbank geladen werden. Abfragen, die sehr lange laufen, werden irgendwann automatisch abgebrochen; mit diesem Parameter kann verhindert werden, dass Abfragen zu viel Zeit in Anspruch nehmen, indem die Anzahl der pro Abfrage zu ladenden Objekte limitiert wird.

fetchSize auf 100.000 erhöhen:
[Fulltextsearch]
activateFts=1
fetchSize=100000

Im Normalfall werden Sie diese Einstellung jedoch selten benötigen; der Standardwert 50.000 wird normalerweise ok sein.

maxFieldLength und unlimitedFieldLength

Wenn indexierte Objekte Felder mit sehr großen/langen (Text)werten enthalten, werden von diesen standardmäßig nur die ersten 10.000 Zeichen indexiert. In gewissen Fällen kann dies nicht ausreichend sein, oder alternativ zu viel und unnötig sein, so dass sie diese Grenze verändern können.

Die ersten 50.000 Zeichen von Feldinhalten indexieren:
[Fulltextsearch]
activateFts=1
maxFieldLength=50000

Um (praktisch) beliebig lange Feldinhalte zu indexieren, können sie die Einstellung unlimitedFieldLength aktivieren:

Gesamten Feldinhalt von (praktisch) beliebiger Länge indexieren:
[Fulltextsearch]
activateFts=1
unlimitedFieldLength=1

indexPath

Important
Es hat sich herausgestellt, dass auf manchen Systemen ein Eintrag indexPath=niofs:///<DURCH KORREKTES PROJEKTVERZEICHNIS ERSETZEN>/index notwendig ist, um einen Fehler ("Setting type of FS directory is a JVM level setting, you can not set different values within the same JVM") beim Serverstart zu vermeiden.

Diese Einstellung für die Volltextsuche betrifft das Verzeichnis, in dem die Dateien des Index gespeichert werden. Über "indexPath=ein/pfad/im/dateisystem" können sie bestimmen, wo diese Dateien abgelegt werden.

Ein Beispiel, unter Linux:
[Fulltextsearch]
activateFts=1
indexPath=/var/lib/mytism/ftsindex
Ein Beispiel, unter Windows:
[Fulltextsearch]
activateFts=1
indexPath=C:\Daten\MyTISM\FTS-Index

Im Normalfall werden Sie diese Einstellung jedoch selten benötigen; wenn kein Eintrag für "indexPath" vorhanden ist, wird der Standardpfad benutzt. In diesem Fall werden die Index-Dateien unterhalb eines Verzeichnisses namens index im Projekt-Verzeichnis der MyTISM-Installation abgelegt, also z.B. unter /.is/index.

Interessant in diesem Zusammenhang könnte evtl. sein, dass über diesen Parameter auch noch Einstellungen für den Dateisystemzugriff gemacht werden können, die sich evtl. auf die Performance auswirken können. Siehe hierzu auch 4.1. File System Store.

Java 1.4 NIO zum Zugriff benutzen:
[Fulltextsearch]
activateFts=1
indexPath=niofs:///.is/index

maxThreads

Um die Leistung des Servers optimal zu nutzen, werden bei der Indexierung parallel mehrere Abfragen abgesetzt, um die in den Suchindex aufzunehmenden Objekte zu laden.

Je nach Leistungsfähigkeit des Servers können mehr oder weniger Abfragen gleichzeitig bearbeitet werden. Ausserdem wird die Anzahl der gleichzeitig möglichen Abfragen durch die erlaubte Anzahl von Verbindungen zur Datenbank, etc. begrenzt. Mit dieser Einstellung kann die maximale Anzahl der gleichzeitig für die Indexierung laufenden Threads begrenzt werden.

Eine geringe Angabe für maxThreads führt lediglich dazu, dass die ggf. vorhandenen Ressourcen des Systems nicht optimal genutzt werden und die Indexierung länger dauern kann, als eigentlich erforderlich. Eine zu hohe Angabe kann dagegen dazu führen, dass die Indexierung aufgrund zu grosser Anforderungen an das System fehlschlägt, was normalerweise zur Folge hat, dass die Indexierung wieder komplett neu gestartet werden muss.

Der Standardwert für maxThreads ist die Anzahl der initial für die Java Virtual Machine verfügbaren Prozessoren und sollte im Normalfall ok sein. Wenn Sie wichtige Gründe dafür haben, können Sie diesen Wert verringern oder erhöhen. Wenn Sie hier `-1' angeben, sind beliebig viele parallel laufende Threads erlaubt; konkret heisst das, dass für jede im Schema definierte (und für die Indexierung vorgesehene) Entity ein eigener Thread gestartet wird.

Caution
Wenn Sie sehr viele gleichzeitige Abfragen benutzen wollen müssen Sie ggf. auch den Wert für max_connections in der PostgreSQL-Konfiguration (s.o.) erhöhen - und zwar mindestens auf den Wert, den Sie für maxThreads angegeben haben - da es sonst während der Indexierung zu Fehlern kommen kann.
maxThreads auf 15 setzen:
[Fulltextsearch]
activateFts=1
maxThreads=15

directoryWrapper

Über diese Einstellung kann der Zugriff auf den Index über einen Wrapper gekapselt werden, was ggf. Verbesserungen bei der Performance bringen kann. Siehe hierzu auch 4.6. Lucene Directory Wrapper.

[Fulltextsearch]
activateFts=1
directoryWrapper=org.compass.core.lucene.engine.store.wrapper.AsyncMemoryMirrorDirectoryWrapperProvider

compassConfig

Mittels dieser Einstellung können Sie den Pfad zu einer Konfigurationsdatei angeben, mit der Sie praktisch beliebige Einstellungen direkt zum verwendeten Compass-Suchframework durchreichen können.

Weitere Infos dazu siehe Compass-Dokumentation.

Der Index

Grundlage der Volltextsuche ist der sogenannte Index; grob gesagt handelt es sich dabei um eine Struktur, die die Daten der in der MyTISM-Datenbank gespeicherten Objekte in einer Form enthält, welche eine einfaches Auffinden nach zu eingegebenen Suchbegriffen passenden Objekten ermöglicht.

Initiale Erstellung

Dieser Index wird normalerweise einmal erstellt und im Weiteren dann automatisch aktualisiert, wenn Änderungen an den Objekten in der Datenbank vorgenommen werden. Ist die Volltextsuche aktiviert und noch kein Index vorhanden, wird beim Starten der MyTISM-Instanz automatisch ein Index erzeugt. Die Volltextsuche ist erst verfügbar, wenn der Index fertig komplett wurde.

Für die Erstellung müssen alle Objekte, die mittels der Volltextsuche gefunden werden können sollen, geladen und ihre Daten in den Index eingespeichert werden. Je nach Grösse der MyTISM-Datenbank und der Anzahl der dort gespeicherten Objekte sowie der Leistungsfähigkeit der Server-Machine, auf der die MyTISM-Instanz läuft, kann dieser Vorgang von einigen Minuten bis hin zu vielen Stunden (oder noch länger) in Anspruch nehmen.

Während dieser Zeit sind der Server und die MyTISM-Instanz aufgrund der vielen und umfangreichen Abfragen normalerweise stark ausgelastet, was ggf. natürlich Beeinträchtigungen für die normale Benutzung mit sich bringen kann.

Ausserdem sollte darauf geachtet werden, dass die Erstellung des Index nicht unterbrochen wird (z.B. durch Herunterfahren der MyTISM-Instanz oder des gesamten Servers), da es wahrscheinlich ist, dass sich bei einer Unterbrechung der Index in einem halbfertigen, nicht benutzbaren Zustand befindet und daher die Indexerstellung später noch einmal komplett neu angestossen werden muss.

Wie bereits oben erwähnt muss die Indexerstellung jedoch im Normalfall nur ein einziges Mal gemacht werden, so dass es sich hierbei um eine einmalige Einschränkung handelt.

Erneute Erstellung / Re-Indexierung

Soll der Index aus irgendeinem Grund vollständig neu erstellt werden, gibt es zwei Möglichkeiten, dass zu erreichen:

  1. Anlegen einer Datei namens ".force-fts-index-rebuild" im MyTISM-Projektverzeichnis. Dies ist die normale, bevorzugte Methode. Der Inhalt der Datei ist unwichtig, sie kann leer sein.
    Die Datei wird nach der Indexerstellung automatisch gelöscht.

  2. Händiges Löschen des Index-Verzeichnisses inkl. aller darin enthaltenen Unterverzeichnisse und Dateien. Diese Methode muss ggf. angewandt werden, wenn die Indexerstellung unterbrochen wurde (s.o.).

In beiden Fällen wird der Index von Grund auf neu erstellt, wie im vorherigen Abschnitt beschrieben, mit allen dort erwähnten Einschränkungen für die Benutzung der Anwendung währenddessen.

Verteilen des Index für synchronisierende Server

Da der Index keinerlei Instanz-spezifische Informationen enthält, kann ein einmal erstellter Index auch für eventuell vorhandenen, synchronisierende Server verwendet werden. Dies ist natürlich insb. bei grossen Datenbanken sinnvoll, damit die aufwändige Indexerstellung nicht mehrmals erfolgen muss.

Das Verteilen des Index ist z.Zt. jedoch noch nicht automatisch möglich und daher muss der Index händig auf die entsprechende Server-Machine kopiert werden. Dies geschieht einfach durch Kopieren des gesamten Index-Verzeichnisses, inklusive aller darin enthaltenen Unterverzeichnisse und Dateien, in das MyTISM-Projektverzeichniss (bzw. das in der Konfiguration angegeben Verzeichnis) auf dem synchronisierenden Server.

Caution
Die MyTISM-Instanz, deren Index kopiert werden soll, sollte entweder ganz gestoppt oder die Volltextsuche sollte nicht aktiviert sein. Ist dies nicht der Fall, kann es sein, dass der Index gerade aktualisiert wird, was ggf. dazu führt, dass er nach dem Kopieren nicht benutzbar ist. Auch auf dem Zielserver sollte die Volltextsuche nicht aktiv sein, wenn die Index-Dateien dorthin kopiert werden; die MyTISM-Instanz an sich kann aber laufen.

Um nach dem Kopieren der Index-Dateien die Volltextsuche auf dem Zielserver zur Verfügung zu stellen, muss diese in der Konfiguration aktiviert werden und die MyTISM-Instanz auf dem Zielserver danach neu gestartet werden.

Konfiguration für die in den Index aufzunehmenden Daten

Im Normalfall werden alle textuellen Daten aller Objekte in der MyTISM-Datenbank für die Volltextsuche aufbereitet und im Index eingespeichert. Als Entwickler einer MyTISM-Anwendung können Sie die Indexierung allerdings weitergehend konfigurieren und z.B. bestimmen, dass bestimmte Objekte oder bestimmte Daten von Objekten nicht indexiert werden sollen.

Da diese Möglichkeit jedoch nur beim Bauen einer MyTISM-Anwendung besteht und im fertigen Produkt nicht mehr weiter konfigurierbar ist finden sich ausführliche Informationen hierzu in der MyTISM-Entwicklerdokumentation.

Benutzung der Volltextsuche

Wenn die Volltextsuche in der MyTISM-Konfiguration aktiviert und der Index vollständig erstellt wurde, kann die entsprechende Funktionalität genutzt werden.

Standard-Abfragen

Volltextsuche-Kriterien können in Abfragen (Queries) als MEX-Ausdrücke eingefügt werden. Die entsprechende Syntax lautet: Fulltext [from <Entitätsname>] matches <Volltext-Suchklausel(n)>

Beispiel: Eingabe von [\{Fulltext matches Schmitt} in der Suchzeile eines Kunden-Lesezeichen findet alle Kunden(-Objekte) die in irgendeinem ihrer (indexierten) Attribute die Zeichenkette "Schmitt" enthalten.

Normalerweise wird über alle Attribute der Objekte gesucht; es können aber auch nur bestimmte Attribute in die Suche einbezogen werden.

Beispiel: Eingabe von [\{Fulltext matches Name:Schmitt} in der Suchzeile eines Kunden-Lesezeichen findet alle Kunden(-Objekte) die in ihrem Attribut "Name" die Zeichenkette "Schmitt" enthalten.

Die Namen der Attribute entsprechen dabei genau den Namen, die im MyTISM-Schema angegeben sind; Gross- und Kleinschreibung sind dabei zu beachten.

Weitere Angaben zur Abfrage-Syntax finden sich auch noch in der Compass-Dokumentation.

Einschränkungen der Entität

FIXME Im Normalfall keine Angabe nötig, Default ist Entität der Abfrage.

Grooql (Groovy Object Query Language)

Eine alternative Möglichkeit, Objektmengen abzufragen. Hat Ähnlichkeiten/Überschneidungen mit OQL und BOMasken. Besteht aus Filterskripten, in einer eingeschränkten Groovy-Version geschrieben.

Im Moment nur direkt aus Programmcode heraus zu benutzen, noch nicht unterstützt z.B. in Lesezeichen (ist aber geplant).

Paket de.ipcon.db.grooql, "Hauptklasse" GrooqlFilter.

Sprachumfang

Unterstützt werden z.Zt.:

  • Logische Verknüpfungen: && (und), || (oder), ! (nicht)

  • Operatoren für Skalare: ==, !=, <, >, , >=

  • Methoden für Zeichenketten: .startsWith( ‘foo’ ), .endsWith( ‘foo’ ), .contains( ‘foo’ ), matchesSimple( 'bla*' ), .lower(), .upper(), .trim()

  • Arithmetik/Zahlen: +, -, *, /, % (Modulo)

  • Methoden für Datums- und Zeitwerte:

    • Vergleich: .after( someDate ), .before( someDate )

    • Extrahieren von Datums-“Teilen”: .day, .month, .year bzw. alternativ .getDay(), .getMonth(), .getYear()

    • Genauigkeit/Granularität vergröbern: .thatDay() (setzt h/min/sec auf 0), .thatMonth() (setzt d/h/min/sec auf 0), .thatWeek() (setzt h/min/sec auf 0 und Tag auf Anfangstag (Montag) der entsprechenden Woche), .thatMonth() (setzt d auf 1, h/min/sec auf 0), .thatYear() (setzt M/d auf 1, h/min/sec auf 0)

    • Zukunft: .addDay( 1 ), .addMonth( -1 ), .addYear( 3 )

    • Vergangenheit: .subDay( 2 ), .subMonth( -3 ), .subYear( 1 )

  • BOs: Zugriff auf Attributketten.

Beispiele für Filterskripte

Vorraussetzung: GrooqlFilter der Objekte von Entität (GrooqlFilter.Entity) "Dokument" sucht; diese hat Attribute "Name" (String), "ErstellungsDatum" (Date), "Summe1" (Integer), "Summe2" (Integer). Beispiele für GrooqlFilter.FilterSource:

Alle Dokumente mit bestimmtem Namen:
Name = "Bilanz 1"
Alle Dokumente mit Namen der mit "Bilanz" beginnt:
Name.startsWith( "Bilanz 1" )
Alle Dokumente aus dem Jahr 2011:
ErstellungsDatum.year = 2011

oder

ErstellungsDatum.getYear() = 2011
Alle Dokumente neuer als 2011:
ErstellungsDatum.year > 2011

oder

ErstellungsDatum.getYear() > 2011
Alle Bilanzen von 2011:
Name.startsWith( "Bilanz" ) && ErstellungsDatum.getYear() = 2011
Alle Dokumente ohne Namen:
Name == null || Name.trim() = ""
Alle Dokumente mit Summe1 + Summe2 > 1000:
Summe1 + Summe2 > 1000

Codebausteine

Codebausteine dienen dazu, oft benötigte Teile des XML-Quelltextes von anderen Strukturelementen zu verwalten. Diese Code-Teile können dann auf einfache Weise in den Quelltext von Strukturelementen eingebunden werden ohne das der Code immer wieder kopiert werden muss.

Einbinden von Codebausteinen

Reiter des vorgebauten Codebaustein-Formulars mit Angaben u.A. zum Namen.

Codebausteine können einfach durch Einfügen eines Elementes <Include name="codebausteinName/pfad"/> im Quelltext (Attribut "Parameter" bzw. zusätzlich Attribute "AnkerDefinition" und "ReportDefinition" bei Reports) eines Strukturelementes eingebunden werden. Dabei gibt das Attribut name den Namen (ggf. mit Pfad) an, unter dem der Codebaustein im Navigationsbaum abgelegt ist.

Caution
Bitte beachten: Damit der Codebaustein richtig gefunden wird, müssen Sie sowohl für den Codebaustein als auch für die Ordner im ggf. angegebenen Pfad den Wert aus dem "Name"-Attribut des Codebausteins bzw. Ordners verwenden! Der im Baum angezeigte Name ist der sog. "L10nName", der automatisch (soweit verfügbar) in der für den Client angezeigten Sprache gehalten ist. Dieser "L10nName" wird sich in vielen Fällen vom eigentlichen Namen des Elements in "Name" unterscheiden!

Beispiel eines Codebausteins und seiner Einbindung in Formularen.

Codebaustein "Allgemein.elem", abgelegt im Ordner "/Admins/$R{MyTISM}/$R{Alarme}/$R{X}":
<Element>
  <Border etched="true" title="Allgemein">
    <View>
      <Element label="$R{Name}">
        <Text displayProperty="Name" columns="25"/>
      </Element>
      <!-- ...noch mehr Quelltext... -->
    </View>
  </Border>
</Element>
Formular "$R{_BOBasierterTermin} (Vorgebaut)":
  <Tab title="Allgemein" scrollable="true">
    <View>
      <!-- Einbindung von Codebausteinen: -->
      <Include name="/Admins/$R{MyTISM}/$R{Alarme}/$R{X}/Allgemein.elem"/>
      <Include name="/Admins/$R{MyTISM}/$R{Alarme}/$R{X}/Maske.elem" parentClass="de.ipcon.db.core.BOBasierterTermin"/>
      <Element>
        <Border etched="true" title="Auslösung">
          <View>
            <Element label="$R{Attribut}">
              <Text displayProperty="Attribut" columns="25"/>
            </Element>
  ...
Formular "$R{_Hinweis} (Vorgebaut)":
  ...
    </View>
  </Tab>
  <Tab title="Allgemein" scrollable="true">
    <View>
      <!-- Einbindung von Codebausteinen: -->
      <Include name="/Admins/$R{MyTISM}/$R{Alarme}/$R{X}/Allgemein.elem"/>
      <Include name="/Admins/$R{MyTISM}/$R{Alarme}/$R{X}/Maske.elem" parentClass="de.ipcon.db.core.Hinweis"/>
      <Include name="/Admins/$R{MyTISM}/$R{Alarme}/$R{X}/Sonstiges.elem"/>
    </View>
  </Tab>
  <Tab title="Auslösekriterien" scrollable="true">
    <View>
      <Element>
  ...

Reiter "CookedParameter" und "Codebausteine"

Die vorgebauten Formulare für Lesezeichen, Formulare, Schablonen und Reports beinhalten zwei Reiter namens "CookedParameter" und "Codebausteine".

Unter "CookedParameter" kann man sich ansehen, wie der Quellcode des Strukturelements (aus dem Attribut "Parameter") letztendlich aussieht, nachdem der Inhalt aller Codebausteine eingefügt und alle L10n-Einträgen durch den entsprechenden sprachspezifischen Text ersetzt wurden.

Bei Reports existiert außerdem noch "CookedReportDefinition" die dasselbe für den Inhalt des Attributs "ReportDefinition" anzeigt.

Unter "Codebausteine" kann man sehen, welche Codebausteine vom aktuellen Strukturelement verwendet werden und diese direkt öffnen. Hier ist zu beachten, dass diese Liste erst gefüllt wird (technische Gründe…​), wenn der Reiter "CookedParameter" des Strukturelements mindestens einmal angesehen wurde.

Pfadangaben für Codebausteinen

Absolute Pfade mit "/" am Anfang (wie in obigen Beispielen) werden immer von der Wurzel des Navigationsbaumes (genauer eigentlich: Der Struktur-Hierarchie) aus aufgelöst.

Relative Pfade mit ".", ".." oder direkt einem Namen am Anfang werden vom "Standort" des aufrufenden Strukturelements aus aufgelöst. Dabei bezeichnet "." den aktuellen Standort (wird wohl eher selten benötigt), ".." den Elter des aktuellen bzw. des vorher im Pfad genannten Strukturelements.

Normalerweise greift bei relativen Pfaden automatisch ein Fallback-Mechanismus; dieser funktioniert indem der Codebaustein (mit dem gegebenen relativen Pfad) erst vom Standort des aufrufenden Strukturelements, wenn er dort nicht gefunden wird von dessen Elter aus, dann ggf. von dessen Elter, etc. gesucht wird.

Durch Angabe von useFallback="false" beim Include-Aufruf wird der Fallback-Mechanismus deaktiviert; in diesem Fall wird nur einmal, ausgehend vom aufrufenden Strukturelement aus, gesucht.

Beispiel-Baum:
 (- ROOT)
   - Codebaustein0
   - Alias0a (to Codebaustein1b, DUMMYNAME)
   - Alias0b (to Codebaustein1b, own name)
   - Formular0a
   - Ordner1
     - Formular1a
     - Codebaustein1b
     - Codebaustein1c
     - Ordner2
Absoluter Pfad:
<Include name="/Codebaustein0"/>

findet Codebaustein0, egal von welchem Strukturelement aus aufgerufen.

Absoluter Pfad:
<Include name="/Ordner1/Codebaustein1b"/>

findet Codebaustein1b, egal von welchem Strukturelement aus aufgerufen.

Relativer Pfad:
<Include name="Codebaustein1b"/>

findet Codebaustein1b wenn von Formular1a aufgerufen. Findet nichts wenn z.B. von Formular0a aufgerufen.

Relativer Pfad:
<Include name="./Codebaustein1b"/>

findet Codebaustein1b wenn von Formular1a aufgerufen. Findet nichts wenn z.B. von Formular0a aufgerufen.

Relativer Pfad:
<Include name="../Codebaustein0"/>

findet Codebaustein0 wenn von Formular1a aufgerufen. Findet nichts wenn z.B. von Formular0a aufgerufen.

Relativer Pfad (mit Fallback, automatisch aktiv):
<Include name="Codebaustein0"/>

findet Codebaustein0 wenn von Formular1a oder auch Formular0a aufgerufen.

Relativer Pfad (mit Fallback deaktiviert):
<Include name="Codebaustein0" useFallback="false"/>

findet Codebaustein0 wenn von Formular0a aufgerufen; von Formular1a aufgerufen wird nichts gefunden.

Relativer Pfad (mit Fallback, automatisch aktiv):
<Include name="Codebaustein1b"/>

findet Codebaustein1b wenn von Formular1a aufgerufen; findet Codebaustein1b auch wenn von Formular0a aufgerufen (über Alias0a).

Benamsung von Codebausteinen

Der Name des Codebausteins sollte einen Hinweis darauf geben, um was es sich bei dem Inhalt handelt. Hierzu wird er mit einer Endung versehen.

Oft verwendete Endungen sind:

"button"

Inhalt besteht aus einem "button"-Element, ggf. mit zugehöriger Action.

"elements"

Inhalt besteht aus mehreren, beliebigen XML-Elementen.

"filter"

Inhalt definiert einen Filter für ein Lesezeichen.

"script"

Inhalt ist ein Skript.

"tab"

Inhalt ist ein ganzer Reiter ("Tab") eines Formulars.

"table"

Inhalt ist eine Tabellendefinition.

"view"

Inhalt ist ein "view"-Element für ein Formular.

Die Verwendung dieser (und ggf. weiterer Endungen) ist allerdings nur eine Konvention und zur Nutzung von Codebausteinen nicht unbedingt notwendig.

Example 1. Namensbeispiele:

EinstellungenNavigationsbaum.tab
Benutzer.table

Inhalt von Codebausteinen

Parameter-Reiter des vorgebauten Codebaustein-Formulars; wie man sieht ist es auch möglich innerhalb von Codebausteinen weitere Codebausteine zu benutzen.

Codebausteine können einen beliebigen Inhalt haben, angefangen von einem kurzen oder längeren normalem Text bis hin zu großen XML-Stücken.

Beispiele für mögliche Codebausteine:

text
text text text viel text
und noch mehr text
und noch weiterer text

dann ausserdem noch text
<element>text</element>
<element>
  <kindelement>text</kindelement>
</element>
<element attribut1="wert">
  <kindelement>text1</kindelement>
  <kindelement attribut="wert">text2</kindelement>
</element>

Zu beachten ist allerdings, dass es sich - aus technischen Gründen - bei dem Inhalt eines Codebausteins (mehr oder weniger…​) um ein wohlgeformtes XML-Dokument handeln muss.

Dies bedeutet insb. dass es genau ein "Root"- bzw. "Wurzel"-Element geben muss; will man mehrere, in der gleichen "Hierarchie-Stufe" befindliche (XML-)Elemente in einem Codebaustein abspeichern, muss man in diesem Fall ein "künstliches" Wurzel-Element einfügen. Dieses trägt den Namen Include und wird beim Einfügen in den Quellcode anderer Struktruelemente einfach entfernt (d.h. es wird nur der Inhalt dieses Elements eingefügt.

Beispiel:
<element attribut1="wert"/>
<element>text</element>
<andereselement/>
muss geschrieben werden als:
<Include>
  <element attribut1="wert"/>
  <element>text</element>
  <andereselement/>
</Include>

Dieses künstliche "Include"-Element kann immer - also auch wenn sowieso eigentlich schon nur ein Wurzel-Element existiert - angegeben werden.

Beispiel:
<element attribut1="wert">text</element>
kann auch geschrieben werden als:
<Include>
  <element attribut1="wert">text</element>
</Include>

hideComment beim Einbinden eines Codebausteines

Beim Einbinden von Codebausteinen werden vor dem Code des eigentlichen Codebausteines standardmässig Kommentare eingesetzt, die Anfang und Ende des Codebausteines im Quelltext kennzeichnen. Der Mechanismus, der diese Kommentare erzeugt, fügt zwischen den Kommentartags und dem eigentlichen Inhalt des Codebausteines eine Anzahl von Leerzeichen ein. Dieses Verhalten ist offensichtlich eine Eigenart der genutzten XML-Bibliothek.

Für Programmquelltext ist dieses Verhalten nicht weiter störend.
Wenn der Codebaustein jedoch z.B. für eine mehrzeilige Kundenadresse verwendet wird, so kann es passieren, daß die Leerzeichen, die hinter dem Ende des Kommentares automatisch eingefügt wurden, eine Verschiebung in der ersten Zeile der Adresse verursachen. Die erste Zeile, die in dem Fall einen Namen enthielt und in einem Report verwendet wurde, war im generierten Report nach rechts verschoben.

Um diesen Effekt zu vermeiden läßt sich das Einfügen von Kommentaren beim Einbinden des Codebausteines pro Verwendung individuell deaktivieren. Dafür existiert das vordefinierte Argument hideComment. Es wird, analog zu den bereits beschriebenen Argumenten, als Attribut im Include-Statement wie im folgenden Beispiel eingegeben.

<Include name="codebaustein" hideComment="true"/>

Einziger - bekannter - Nachteil dieses Argumentes: Die Referenzpunkte, die man in den Cooked-Parameters etc. hat, um diesen Codebaustein zu finden - die XML-Kommentarzeilen - existieren nicht mehr.

Argumente für Codebausteine

Argumente-Reiter des vorgebauten Codebaustein-Formulars.

Teilweise kann es vorkommen, dass ein Stück Quellcode in verschiedenen Strukturelementen fast gleich vorkommt, sich aber in einem oder mehreren kleinen Punkten unterscheidet:

Quellcode 1:
<element>
   Ein bisschen Text.
   <-- Fast gleich: -->
   <element attribut="eins"/>
   <-- Ende -->
   <weiteresElement/>
</element>
Quellcode 2:
<element>
   <einElement>inhalt</einElement>
   <nochEinElement/>
   <-- Fast gleich: -->
   <element attribut="zwei"/>
   <-- Ende -->
   <wiederumEinElement attr="wert"/>
</element>

Um diese fast gleichen Teile trotz dieser geringfügigen Unterschiede trotzdem als Codebausteine "auslagern" zu können, kann man für Codebausteine sog. Argumente definieren, die man dann bei der Einbindung des Codebausteins mit Werten bestückt.

Im Quellcode-Stück des Codebausteins setzt man für diese Argumente dann einen Platzhalter $IP{nameDesArguments} ein; bei der Einbindung des Codebausteins im Quelltext des Strukturelements wird dieser Platzhalter dann durch den beim "Aufruf" mitgegebenen Wert ersetzt.

Beispiel (wobei der Einsatz eines Codebausteins hier, aufgrund der sehr kleinen Codestelle, zugegebenermaßen keinen großen Sinn macht :-):

Codebaustein mit def. Argument "attrWert":
<element attribut="$IP{attrWert}"/>
Quellcode 1:
<element>
   Ein bisschen Text.
   <!-- War: <element attribut="eins"/> -->
   <Include name="codebaustein" attrWert="eins"/>
   <weiteresElement/>
</element>
Quellcode 2:
<element>
   <einElement>inhalt</einElement>
   <nochEinElement/>
   <!-- War: <element attribut="zwei"/> -->
   <Include name="codebaustein" attrWert="zwei"/>
   <wiederumEinElement attr="wert"/>
</element>

Für diese Argumente kann man auch Standardwerte definieren (siehe u.A. obiger Screenshot), die automatisch genommen werden, wenn beim "Aufruf" des Codebausteins kein Wert für das Argument mit übergeben wurde. Dies ist insb. dann sinnvoll, wenn der Codebaustein oft verwendet wird, der Wert aber in den meisten Fällen gleich ist und nur ein- oder wenige Male ein anderer Wert benötigt wird:

Quelltext eines Codebausteins der außerdem (über das Codebaustein-Formular) noch ein CodebausteinArgument "attrWert" mit Standardwert "eins" definiert hat:
<element attribut="$IP{attrWert}"/>
Quellcode 1:
<element>
   Ein bisschen Text.
   <!-- War: <element attribut="eins"/> -->
   <Include name="codebaustein"/>  <!-- attrWert="eins" braucht nicht angegeben zu werden. -->
   <weiteresElement/>
</element>
Quellcode 2:
<element>
   <einElement>inhalt</einElement>
   <nochEinElement/>
   <!-- War: <element attribut="zwei"/> -->
   <Include name="codebaustein" attrWert="zwei"/>
   <wiederumEinElement attr="wert"/>
</element>

Problembehebung

IllegalArgumentException: Invalid parameter "xyz" given…​

Diese Fehlermeldung bedeutet, dass beim "Aufruf" eines Codebausteins ein Argument angegeben wurde, das für diesen Codebaustein nicht definiert wurde. Wenn nicht wirklich einfach vergessen wurde, das CodebausteinArgument am Codebaustein zu definieren (s.o.) kann das auch passieren, falls der Benutzer keine ausreichenden Rechte hat, CodebausteinArgumente zu lesen.

In diesem Fall wird dann zwar der Codebaustein (für den Leserechte gesetzt sind) geladen, aber die eigentlich dafür definierten CodebausteinArgumente können nicht geladen werden (was aufgrund des Designs des Rechtesystems aber nicht zu einer Fehlermeldung führen kann und soll) und deswegen sieht es so aus, als wären für den Codebaustein keine Argumente vorhanden, was wiederum diesen Fehler zur Folge hat.

Einstellungen-Variablen

Einstellungen-Variablen dienen, wie der Name schon sagt, dazu Werte für bestimmte Einstellungen zu setzen, welche dann z.B. in Skripten abgefragt und benutzt werden können.

Definition der vorhandenen/verfügbaren Variablen

Normalerweise werden Einstellungen-Variablen vom Administrator oder von Entwicklern, je nach dem Bedarf der spezifischen MyTISM-Anwendung, definiert. Eine Variable(ndefinition) hat folgende Eigenschaften:

Name

Pflichtfeld - Der Name oder Titel einer Variable sollte diese kurz und prägnant benennen. Der Name kann frei gewählt werden; ein wirkliches einheitliches Schema für die Benamsung existiert (bisher) noch nicht.

Beschreibung

Optional - Die Beschreibung kann einen längeren Kommentar bzw. eine längere Beschreibung der Variable beinhalten und ggf. erklären wo bzw. wofür sie benutzt wird.

Standardwert

Optional - Dies ist der Wert, den die Variable normalerweise hat und der bei der Abfrage z.B. in Skripten zurückgeliefert wird, wenn kein spezieller Wert für bestimmte Benutzer oder Gruppen gesetzt wurde (s.u.). Variablenwerte hier sind immer Zeichenketten, eine weiter Typisierung (z.B. für Nummern oder Wahrheitswerte) gibt es nicht. Wenn kein Wert gesetzt wird, ist der Standardwert einfach null.

Ueberschreibbar

Optional - Wenn dieses Flag gesetzt ist, können für einzelne Benutzer oder Gruppen vom Standardwert abweichende Werte für diese Variable definiert werden (oder genauer gesagt: Wenn solche Werte definiert wurden, werden sie auch berücksichtigt; s.u.). Wenn das Flag nicht gesetzt ist, gilt für alle Benutzer oder Gruppen immer nur der Standardwert der Variable.

Abfrage von Einstellungen-Variablen in Skripten

Variablenwerte können wie folgt abgefragt werden (Beispiel aus dem vorgebauten JahrMonatTag-Filter-Codebaustein):

val = de.ipcon.db.core.EinstellungenVariable.getWertForBenutzer( ctx.getBOLoader(), "jahrMonatTagFilter.Jahr", ctx.getCurrentUser() );

Die Abfrage erfolgt über den Aufruf der statischen Methode getWertForBenutzer der Klasse EinstellungenVariable. Diese bekommt einen BOLoaderI zum Zugriff auf Datenbankobjekte als erstes Argument, den Namen der Variablen als zweites Argument und den Benutzer, für den der Wert abgefragt werden soll als drittes Argument.

FIXME Die API hier hat sich geändert

Setzen von abweichenden Werten für Benutzer oder Gruppen

Wenn für bestimmte Benutzer oder Gruppen vom Standardwert abweichende, spezielle Werte für eine Variable gesetzt werden sollen, geschieht das durch Anlegen von EinstellungenVarWertBenutzer- oder EinstellungenVarWertGruppe-Objekten.

In diesen Objekten gibt man an, für welche Variable der Wert "überschrieben" werden soll, für welchen Benutzer oder Gruppe der abweichende Wert gelten soll und natürlich den Wert selbst.

Die Auswertung bzw. Bestimmung welcher Wert für einen spezifischen Benutzer letztendlich zurückgeliefert wird erfolgt so:

  1. Wenn eine Variable mit dem gewünschten Namen nicht existiert, wird null zurückgegeben.

  2. Wenn die Variable existiert und Ueberschreibbar NBSP nicht gesetzt ist, wird immer der Standardwert der Variable zurückgegeben.

  3. Wenn Ueberschreibbar gesetzt ist und eine EinstellungenVarWertBenutzer-Instanz für den Benutzer und die Variable existiert, wird der dort angegebene Wert zurückgegeben.

  4. Wenn Ueberschreibbar gesetzt ist, keine passende EinstellungenVarWertBenutzer-Instanz existiert, aber eine EinstellungenVarWertGruppe-Instanz für die Variable und eine Gruppe, in der der Benutzer Mitglied ist, existiert, wird der dort angegebene Wert zurückgegeben. Wenn mehrere passende Instanzen für die Variable und unterschiedliche Gruppen, in denen der Benutzer Mitglied ist, existieren, so wird der Wert zurückgegeben, der für die Gruppe mit der kleinsten Id definiert wurde.

Lesezeichen und Anzeige in Benutzer- und Gruppen-Formularen

Im Ordner der Gruppe "Benutzer" gibt es ein vorgebautes Lesezeichen, in dem alle für den angemeldeten Benutzer geltende EinstellungenVarWerte (sowohl für Benutzer als auch Gruppe) angezeigt werden; allerdings nur solche, für deren zugehörige Variable das Flag Ueberschreibbar gesetzt ist!

In den vorgebauten Formularen für Benutzer und Gruppe gibt es ebenfalls einen Reiter Variablen; in der dortigen Tabelle werden alle für den jeweiligen Benutzer bzw. die jeweilige Gruppe definierten EinstellungenVarWertBenutzer bzw. EinstellungenVarWertGruppe angezeigt.

VirtualProperties

Bei den VirtualProperties handelt es sich um Attribute, die zur Laufzeit (KEIN Server-Restart oder Client-Neuanmeldung nötig!) an ein BO hinzugefügt werden können - sei es in einem Lesezeichen, einem Formular oder im Report. Als Programmiersprache der VirtualProperties kommt Groovy zum Einsatz, welche 100% kompatibel zu Java ist.

Der Tag heisst addVirtualProperty und kennt folgende Parameter:

Table 4. addVirtualProperty-Parameter
Parameter Beschreibung Pflichtfeld

entity

Name der Entität, an die das virtuelle Attribut "angebaut" werden soll

ja

name

Wie das zu bauende virtuelle Attribut heissen soll

ja

type

Von welchem Datentyp das virtuelle Attribut ist (Default: "String"); mögliche Typen: String, Integer, Long, Decimal, Date, MyTISM-Objekte (z.B. BO, Artikel, Rechnung, …​)

ja, sofern abweichend vom Default

relation

Handelt es sich um eine Relation des Typs "n-1" oder "1-n"

ja, sofern es sich beim Typ um ein MyTISM-Objekt handelt

readonly

Ist das virtuelle Attribut beschreibbar? (Default: "false") - Wird eine setter-Methode explizit definiert impliziert dies ein readonly="true"

siehe Beschreibung

Beispiele für VirtualProperties

VirtualProperty in einem Lesezeichen:

<Table entity="Rechnung">
  <addVirtualProperty entity="Rechnung" name="PostenAnzahl">
    <getter>bo.getPosten().size()</getter>
  </addVirtualProperty>
  <Query type="Text"/>
  <View>
    <Column property="BelegNr" sort="DESC" sortLevel="2"/>
    <Column property="Wartend"/>
    <Column property="Adressat.AbstraktePerson" title="Kunde"/>
    <Column property="Belegdatum"/>
    <Column property="GesamtSumme"/>
    <Column property="Waehrung"/>
    <Column property="PostenAnzahl"/>
  </View>
</Table>

VirtualProperties in einem Formular

<View>
  <script>
    bo=ftx.Root.BO;
    bo.setTransientProperty("_ftx",ftx);
    bo.setTransientProperty("_ctx",ctx);
    bo.setTransientProperty("_mainftx",main.ftx);
    bo.setTransientProperty("_log",log);
    bo.setTransientProperty("_messageView",messageView);
  </script>
  <addVirtualProperty entity="BX" name="Scanzeile">
    <setter>
if ( value == null ) return
command = value.substring( 0, 1 )
param = value.substring( 1 )
switch ( command.toUpperCase() ) {
  case 'M':
    println 'M-Nr'
    bo.LogInfo = "Kommando $command"
    // hier ggfs. Code zur Verarbeitung der M-Nr
    break
  case 'S':
    println 'S-Nr'
    bo.LogInfo = "Kommando $command"
    // hier ggfs. Code zur Verarbeitung der S-Nr
    break
  default:
    bo.LogInfo = "Error: unbekanntes Kommando \"$command\""
}
    </setter>
  </addVirtualProperty>
  <addVirtualProperty entity="BX" name="LogInfo">
    <getter></getter>
  </addVirtualProperty>
  <!-- Formular-Definition -->
  <Element>
    <Text property="ScanZeile" align="CENTER" fontStyle="bold">
      <Action cmd="beep" accKey="ENTER" shortDescription="keep focus after enter key here">
        <onAction>ftx.sync()</onAction>
      </Action>
    </Text>
  </Element>
  <Element label="LogInfo">
    <Text property="LogInfo"/>
  </Element>
</View>

Quellcode 2:

<element>
   <einElement>inhalt</einElement>
   <nochEinElement/>
   <!-- War: <element attribut="zwei"/> -->
   <Include name="codebaustein" attrWert="zwei"/>
   <wiederumEinElement attr="wert"/>
</element>

Troubleshooting - Probleme und (hoffentlich) deren Lösungen

FIXME: wie man Fehler meldet (Weg, Inhalt (was wir wissen muessen), …​

Probleme beim Start des Clients

de.ipcon.tools.IRuntimeException: Vergroesserung des Pools fehlgeschlagen, IOException aufgetreten: Malformed reply from SOCKS server

Beim SOCKS Server handelt es sich um einen Proxy-Server. Stellen Sie sicher, dass im JavaWebstart die Option "Direktverbindung" eingestellt ist anstelle einen Proxy-Server zu verwenden. Desweiteren öffnen Sie "/Start/Einstellungen/Systemsteuerung/Internetoptionen" und wechseln dort auf den Reiter "Verbindungen". Im unteren Drittel befinden sich die "LAN-Einstellungen". Dort deaktivieren Sie bitte ALLE Checkboxen und schliessen den Dialog mit OK.

FAQ - Immer wiederkehrende Fragen und deren Beantwortung

Benutzer-Passwort ändern / Change user password / Changer mot de passe

Benutzer-Passwort ändern

Nach der Anmeldung finden Sie im linken Menü-Baum ganz oben einen Eintrag mit Ihrem Login-Namen. Wenn Sie auf diesen Eintrag mit der rechten Maustaste klicken, öffnet sich ein Kontext-Menü, aus dem Sie den Eintrag Information auswählen. Es öffnet sich ein Formular, in dem Sie ein neues Passwort setzen können.

Change user password

After the login you will find in menu tree (left side) at the top an entry with your login name. Right-clicking on this entry will open a context menu where you can choose the entry Information. A form will open where you can change your password.

Changer mot de passe

Après la connexion vous trouvez dans la navigation (côté gauche) en haut l’entrée de votre nom d’utilisateur. Faites un clique droit sur votre nom d’utilisateur pour ouvrir un menu où vous choississez l’entrée Informations. Un formulaire vous permet de changer votre mot de passe.

JavaWebstart-Cache löschen unter Windows

Geben Sie unter START / AUSFÜHREN folgenden Befehl ein und drücken RETURN

javaws -viewer

Es öffnet sich das “Java Control Panel” und evtl. sogar direkt schon “Java Cache Viewer”

Wenn sich der Cache Viewer nicht öffnet, dann klicken Sie im Control Panel bei “Temporäre Internetdateien” auf “Anzeigen”

Markieren Sie im “Java Cache Viewer” die jeweilige Anwendung (einmal klicken) und löschen Sie diese dann durch Anklicken des grossen roten “X” in der Menüzeile des Cache Viewers.

Schliessen Sie den "Java Cache Viewer" und das "Java Control Panel" und versuchen Sie sich erneut anzumelden, indem Sie die Anwendung erneut herunterladen.