Anwendungen

Eine Anwendung (Fachschale, Modul, ...) in ein System zu integrieren, bedeutet mehr als das Verlinken von Webseiten untereinander. Eine Anwendung in cardo integriert sich im Idealfall vollständig in das Gesamtsystem:

  • Nutzung der zentralen Berechtigungsverwaltung,
  • vollständig transparente Integration in die Oberfläche,
  • Nutzung der administrativen Schnittstellen für eine zentrale Verwaltung,
  • Nutzung der vorhandenen Daten,
  • Integration in die systemweite Suche,
  • Nachnutzung bestehender Systemkomponenten,
  • Anbieten von Funktionen für andere Systemkomponenten

Für jeden dieser Zwecke stehen entsprechende Programmschnittstellen, sowohl auf Clientseite, als auch auf Serverseite zur Verfügung. Nicht alle Schnittstellen müssen dabei implementiert werden.

Im einfachsten Fall genügt die Schnittstelle zur Registrierung im System und die Anwendung kann weitestgehend ihr Eigenleben führen.

Bis auf die Verwendung der Schnittstelle ICardoManagedApplication sind alle beschriebenen Vorgehensweisen als Empfehlung zu verstehen. Alternative Vorgehensweisen sind möglich.

Voraussetzungen

Die Anwendungserstellung setzt eine lauffähige Installation von cardo4 auf dem Entwicklungsrechner voraus. Zudem empfehlen wir den Einsatz von Visual Studio als Entwicklungsumgebung.

Die aktuellen Komponentenversionen finden Sie hier

Für Visual Studio muss die TypeScript Erweiterung in o.g. Version installiert sein.

Für das Anlegen einer Anwendung mit Standardfunktionen ist eine Projektvorlage für Visual Studio 2022 verfügbar. Ebenso stellen wir eine Visual Studio Extension als VSIX bereit, die verschiedene Routineaufgaben vereinfacht.

Grundkenntnisse in der Sprache TypeScript sind von Vorteil, für .Net werden Kenntnisse vorausgesetzt.

Anwendungen haben eine serverseitige und eine clientseitige Implementierung.

Downloads

Letzter Stand der Download-Version ist der 30.01.2022.

  • cardo4 - Projektvorlage - eine Projektvorlage für eine typische cardo4 Anwendung. Laden Sie das Zip herunter und kopieren Sie dieses in den Projektvorlagenordner Ihrer Installation:

    "%USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates\"
    

    Sie finden die Vorlage dann unter "File -> New -> Project" als "cardo4 Anwendung".

    ACHTUNG: Erst nach dem einmaligen Filtern per Suchfeld nach "cardo" ist das Template in der Liste zu finden (Stand VS 16.7.7)!

  • IduIT.DevExtension Version 2022 - eine Visual-Studio Extension, extrahieren Sie das Zip-Archiv und rufen Sie die Installation durch Doppelklick der Datei bzw. IduIT.DevExtension.vsix auf.

    Die Erweiterung ist für Visual Studio 2022 17.5 erstellt, dies ist damit die unterstütze Mindestversion.

    Wichtiger Hinweis:

    Mit cardo Version 4.0.8 gibt es eine neue Version, die das Ermitteln der TypeScript auf anderem Wege (bisher als Umgebungsvariable, jetzt aus einer Datei) umsetzt.

    Es ist scheinbar nicht möglich unter Verwendung der asynchronen Package-Extension einen Einstiegspunkt zu finden, der vor dem Laden der Solution aufgerufen wird. Daher mussten wir dieses Package als synchron belassen.

    Lange Rede: Damit die Extension funktioniert, muss in den Visual Studio Einstellungen (Tools => Options => die Option "Allow synchronous autoload of extensions..." aktiviert werden.

    Vs Ext Settings

Serverseitige Implementierung

Hintergrund

In cardo4 existiert eine Anwendung als ein CLI Typ, der die Schnittstelle ICardoManagedApplication implementiert. D.h. es gibt keinerlei Vorgabe für ein bestimmtes Dateilayout o.ä.

Das System ermittelt beim Starten alle Typen in der aktuellen App-Domain, die das entsprechende Interface implementieren und nicht abstrakt sind.

Dies bedeutet konkret, dass der Programmierer ein Assembly bereitstellen muss, in dem die entsprechenden Typen definiert sind.

Die Verwendung des App-Code Ordners ist nicht vorgesehen. Alle Anwendungen laufen in der einen App-Domain des IIS, die auch die cardo4 Webseite darstellt.

Die von der Anwendung benötigten Ressourcen sind typischerweise in der DLL enthalten. Damit reduziert sich die Installation auf das Bereitstellen des Assemblys. Für das Ausliefern der Ressourcen steht der Ressource-Collector bereit. Dieser sorgt auch für Minifizierung/Komprimierung, Caching und ggf. das Zusammenfassen von Ressourcen (z.b. JavaScript, CSS etc.) bei der Auslieferung.

Sollten dennoch Dateien verwendet werden, müssen diese außerhalb des Ordners "coreWeb4" abgelegt werden, der Updater des cardo Systems löscht alle nicht im Lieferumfang enthaltenen Dateien in diesem Pfad, ausgenommen die DLLs im Bin-Ordner. Für die Ablage eigener Dateien steht ein virtuelles Verzeichnis "Project" in jeder Installation zur Verfügung.

Das Interface ICardoManagedApplication

Dieses Interface stellt die Basis für eine "von cardo verwaltete Anwendung" dar. Dies bedeutet, dass hier eine Komponente bereitgestellt wird, die vom Systembetreuer in der Installation registriert werden kann.

Eine Anwendung muss nicht zwangsläufig eine UI haben (bzw. als "Fenster" auftauchen), daher ist die Schnittstelle IClientApplicationDefinition separat zu implementieren.

Bei der Implementierung muss beachtet werden, dass die Klasse über einen parameterlosen Konstruktor (der auch intern sein kann) verfügt.

//Namespace: IduIT.cardo.Core.Api.Applications

public interface ICardoManagedApplication : IDisposable, 
	Core.IInstallationDependentModul,
	IduIT.Core.IInstanceActivatorFactoryForAjaxRequired
{
	/// <summary>
	/// Anzeigetitel der Anwendung. 
	/// Kann u.U. auch geändert werden, nachdem die Anwendung registriert ist
	/// </summary>
	String InitOnlyTitle { get; }
	/// <summary>
	/// Verbale Beschreibung der Anwendung, Plain-Text
	/// Kann u.U. auch geändert werden, nachdem die Anwendung registriert ist
	/// </summary>
	String InitOnlyDescription
	{
		get;
	}
	/// <summary>
	/// Gibt an, ob mehrere Instanzen dieser Anwendung registriert werden dürfen.
	/// </summary>
	Boolean InitOnlyAllowMultipleRegistration();
	/// <summary>
	/// Gibt an, ob diese Anwendung in der aktuellen Umgebung sichtbar ist. Die ist die einzige Methode, wo vor der Verwendung nicht OnLoadRegisteredApplication aufgerufen wird.
	/// </summary>
	/// <returns></returns>
	new bool InitOnlyIsVisibleInThisInstallation();
	/// <summary>
	/// Wird bei jeder Instanziierung der Anwendung durch den ManagedApplicationManager aufgerufen.
	/// </summary>
	/// <param name="registeredInstanceId"></param>
	void OnLoadRegisteredApplication(String registeredInstanceId);
}

Die Dokumentationsdateien für IntelliSense werden mitgeliefert, ggf. ist die dort enthaltene Dokumentation zu den Schnittstellendetails aktueller.

Die Anwendungsinstanz wird von der Umgebung immer so erstellt, dass alle benutzerbezogen Angaben immer im Kontext des aktuell ausführenden in cardo angemeldeten Benutzers belegt sind. Dazu gehören anwendungsdefinierte Berechtigungen und Anwendungseinstellungen.

D.h. alle Ajax-Methoden müssen immer in dieser Klasse definiert werden, wenn eine Zugriffssteuerung oder der Zugriff auf Anwendungseinstellungen erforderlich ist.

Optionale serverseitige Interfaces

Diese Schnittstellen stehen optional zur Verfügung (beide leiten von ICardoManagedApplication ab) und können implementiert werden, wenn ...

  • ICardoManagedApplicationWithSQLUpdateSupport - ... die Anwendung den von cardo bereitgestellten Mechanismus zur Erstellung und Aktualisierung des Datenbankschemas nutzen möchte. Dabei handelt es sich um einen XML Dialekt (schema...) zur Beschreibung von Datenbankstrukturen. Das XML wird dabei für die Dialekte MSSql Server, Oracle, PostgreQSL in SQL übersetzt und ermöglicht die Erstellung und Aktualisierung der Schemas.

  • ICardoManagedApplicationWithSystemSettings<T> - ... die Anwendung Eigenschaften definiert, die der Betreuer in der jeweiligen Installation komfortabel über die Verwaltungsoberfläche einstellen kann.

Bereitstellung von Webservices

Einige Anwendungen stellen Teile der Funktionen als SOAP Dienst zur Verfügung. Da in cardo4 i.d.R. keine Dateien für die Ausführung vorhanden sind, muss ein entsprechender Handler in der web.config eingetragen werden (geplant ist, dass der Updater diesen Schritt mit durchführt).

Für die Erstellung eines Web-Services im "asmx"-Stil steht die Vorlagen - Basisklasse ApplicationWebServiceWrapper zur Verfügung. Dabei wird, wie bei den Ajax-Methoden, sichergestellt, dass auf die im Benutzerkontext initialisierte Anwendungsinstanz Zugriff besteht.

public sealed class MyAsmxWrapper : Api.Applications.ApplicationWebServiceWrapper<FileBrowserApplication>
{
  public override string HandlerEndpointUrlWithoutExtension
  {
    get
    {
      return "services/MyService";
    }
  }

  [System.Web.Services.WebMethod()]
  public String HelloWorld()
  {
    return "Hallo"+App.Settings.ToString();
  }
}

Bsp. Eintrag in der Web.config

<system.webServer>
 <handlers>
  <add name="c4asmx:MyAsmxWrapper" verb="*" path="services/MyService.asmx" type="MyAsmxWrapper,MyAssembly"/>
 </handlers>
</system.webServer>

An der Klasse kann das Attribut IDU.Core.Web.HttpHandlerRequestInfoAttribute definiert werden.

Wenn dies nicht angegeben ist, dann wird der Handler mit der Sicherheitsanforderung "LoginRequired" versehen. Dies führt in jedem Fall zu einer Authentifizierungsaufforderung, auch wenn kein Benutzerkontext erforderlich ist.

Dieses Verhalten wurde ab Februar 2018 eingeführt, es gab scheinbar eine .Net interne Änderung bzgl. der Fehlerbehandlung in ASMX, was dazu führte, dass keine Authentifizierung (Status code 401) an den Client übermittelt wurde.

Bereitstellung von allgemeinen Http-Handlern (ab Version 4.0.5)

Neben der Implementierung von ASMX Diensten steht analog die Klasse ApplicationHttpHandlerWrapper zur Verfügung, womit einfache Handler (entsprechend System.Web.IHttpHandler) bereitgestellt werden können, die Zugriff auf die cardo Anwendung benötigen.

Das Vorgehen ist analog. Die Methode RunHttpHandler muss implementiert werden.

internal sealed class MyHttpHandlerWrapper : Api.Applications.ApplicationHttpHandlerWrapper<FileBrowserApplication>
{
  public override string HandlerEndpointUrlWithoutExtension
  {
    get
    {
      return "services/MyHttpService";
    }
  }
  protected override void RunHttpHandler(HttpContext context)
  {
    context.Response.Write("Hello, World!");
  }
 
}

Bsp. Eintrag in der Web.config

<system.webServer>
 <handlers>
  <add name="c4asmx:MyHttpHandlerWrapper " verb="*" path="services/MyHttpService.ashx" type="MyAsmxWrapper,MyAssembly"/>
 </handlers>
</system.webServer>
Benutzerberechtigungen

Die Implementierung von anwendungsspezifischen Benutzerberechtigungen ist optional und ermöglicht einer Anwendung bestimmte Aspekte abhängig vom aufrufendem Benutzer zu steuern. Dabei steht cardo als Laufzeitumgebung nur als Vermittler zwischen Anwendung und Benutzer. Die Interpretation der Berechtigungen obliegt der Anwendung selber.

Die Berechtigungen "Anwendung starten" und "Anwendung verwalten" werden von cardo immer automatisch bereitgestellt und auch durchgesetzt.

Wenn die Anwendung eigene Berechtigungen definiert, dann übernimmt cardo folgende Aufgaben:

  • stellt die Verwaltungsoberfläche für den Betreuer bereit,
  • garantiert die korrekte Belegung der Berechtigungen bei allen Aufrufen an die Anwendungsinstanz

Anwendungsberechtigungen werden immer als eine Baumstruktur abgebildet. Pro Eintrag sind die Berechtigungen als ein Enumerationstyp (mit Flags-Attribut) zu definieren. Dieses System ist äußerst flexible und erlaubt eine sehr detaillierte Abbildung von Berechtigungen aller Art. Der Betreuer kann Benutzer und/oder Gruppen für diese Struktur festlegen, jeweils mit der Option "Erlauben"/"Verweigern". Zur Laufzeit werden diese Informationen entsprechend den Regeln in cardo im Kontext des Aufrufers belegt und stehen der Anwendung für die Auswertung zur Verfügung.

Definition

Eine Anwendung kann beliebig viele dieser Berechtigungsstrukturen definieren. Dies erfolgt durch die Bereitstellung einer get - Eigenschaft (internal oder public) die vom Typ einer von ApplicationSecurityRights<RightFlags> abgeleitet Klasse ist.

Der FlagsType muss ein Enumerationstyp sein, der folgenden Anforderungen genügt:

  • das Attribut "Flags" muss definiert sein
  • es muss ein Wert Undefined mit Zahlenwert 0 und
  • ein Wert Prohibited mit dem Zahlenwert 1 definiert sein.

Folgendes Beispiel verdeutlicht das Vorgehen:

  1. Bereitstellung des Berechtigungstyps (Enumeration) und der Ableitung von ApplicationSecurityRights

        /// <summary>
        /// Berechtigungsarten für diese Anwendung
        /// </summary>
        [Flags]
        public enum RightFlags
        {
             /// <summary>
             /// Der von der cardo3Api geforderte obligatorische Wert für undefiniert (0)
             /// </summary>
             Undefined = 0,
             /// <summary>
             /// Der von der cardo3Api geforderte obligatorische Wert für verboten (1)
             /// </summary>
             Prohibited = 1,
             /// <summary>
             /// Lesezugriff auf die Daten
             /// </summary>
             Read = 2,
             /// <summary>
             /// Schreibzugriff auf die Daten
             /// </summary>
             Write = 4
        }
        /// <summary>
        /// Klasse für Berechtigungen in der Anwendung. Wesentlich ist der bereitgestellt Member EffectiveSecurity.
        /// Zudem benötigt die Laufzeitumgebung CreateSecurityNodeForImport() für den Import der Datenstruktur.
        /// In der Anwendungsklasse können dann beliebig viele Properties eingefügt werden. Der Themenbaum Identifier wird aus dem Namen der Eigenschaft erstellt,
        /// der Typ wird von der entsprechenden Klasse abgeleitet.
        /// </summary>
        internal sealed class ApplicationRights : ApplicationSecurityRights<RightFlags>
        {
             public override ApplicationSecurityRightsImportNodes CreateSecurityNodeForImportOrUpdates(IduIT.cardo.Core.Api.Applications.ICardoManagedApplication registeredInstanceIfReload)
             {
     	        //registeredInstanceIfReload ist == null, wenn die Anwendung
     	        //gerade neu registriert wird
     	        //sonst ist die Anwendung bereits registriert, der Rechtebaum 
     	        //kann dann z.B. aus der Datenstruktur generiert werden
    
     	        return new ApplicationSecurityRightsImportNodes("Default", "Berechtigungen")
     			        //Knoten anfüge ... z.B. via 
     			        //.AddChildReturnThis
     			        //.CreateChild ...
     			        ;
             }
        }
    
    
  2. Verwendung in der Implementierungsklasse

    internal sealed class Cardo4Anwendung : ICardoManagedApplication
    {
        ..
        ..
        ..
         /// <summary>
         /// Berechtigungen des aktuellen Benutzers, wichtig ist, dass nie null zurückgegeben wird. 
         /// Die Laufzeit befüllt die Instanz (Recht im Benutzerkontext).
         /// Die Eigenschaft kann public oder internal sein.
         /// Wird von cardo bei der Erstellung der Instanz immer mit den eingestellten Werten belegt.
         /// </summary>
         [System.ComponentModel.Description("Berechtigungen")]
         internal ApplicationRights Permission { get; } = new ApplicationRights();
    
    }
    
    

System.ComponentModel.DescriptionAttribut kann sowohl für die Enumerations-Werte als auch für die Property definiert werden, diese werden als Klartexte dem Betreuer in der Anwendungsverwaltungsseite angezeigt.

  1. Verwendung in der Implementierungsklasse (fiktive Ajax-Methode)

    internal sealed class Cardo4Anwendung : ICardoManagedApplication
    {
        ..
        ..
        ..
        [IduIT.Core.Web.Ajax.AjaxMethod]
        int AxQuestion(void)
        {
            if(!Permission["Default"].HasFlag(RightFlags.Read))
                throw new Exception("Zugriff verweigert.");
            return 42;
        }
    }
    
    

Clientseitige Implementierung

Wenn eine Anwendung eine Oberfläche definiert (was i.d.R. der Fall ist) muss dem System dies zunächst mitgeteilt werden. Dies erfolgt serverseitig durch die Implementierung der Schnittstelle IClientApplicationDefinition. Diese Schnittstelle hat eine Schnittmenge mit ICardoManagedApplication, so dass die dort definierten Eigenschaften nicht erneut implementiert werden müssen.

Die Implementierung der eigentlichen Oberflächenelemente erfolgt dann, wie in jedem anderen Web-Framework, in JavaScript, bzw. hier, indirekt, bevorzugt in TypeScript.

Das Interface IClientApplicationDefinition

Für das Starten einer Anwendung benötigt cardo mindestens eine Anwendungsdefinition. D.h. Informationen zu Titel, Icon usw. Der eigentliche Code der Anwendung wird beim ersten Starten nachgeladen.

Dem entsprechend definiert die Schnittstelle im Wesentlichen folgende Methoden:

/// <summary>
/// Registriert Scripte in der Seite, die ggf. für die Anwendungsdefinition verfügbar sein müssen.
/// </summary>
/// <param name="resourceCollector"></param>
void RegisterDefinitionScripts(IDU.Core.Web.Res.ResourceCollector resourceCollector);
/// <summary>
/// Registriert Scripte in der Seite, die für die Anwendungsinstanz benötigt werden.
/// </summary>
/// <param name="resourceCollector"></param>
void RegisterApplicationScripts(IDU.Core.Web.Res.ResourceCollector resourceCollector);

In beiden Methoden muss die Anwendung die entsprechenden Ressourcen Dateien (Script, CSS) ausliefern. Dazu wird eine Instanz eines Ressource-Collector übergeben, der diese Auslieferung an den Browser übernimmt.

Client-Code

Clientseitig muss eine Klasse bereitgestellt werden, die den Start-Code der Anwendung darstellt. Diese Klasse muss folgenden Anforderungen genügen:

Das folgende Beispiel stellt eine minimale Anwendungsdefinition (TypeScript) dar, deren JavaScript Output per RegisterDefinitionScripts ausgeliefert wird:

namespace Sample.Application
{
	/**
	* Anwendungsklasse.
	*/
	export class Cardo4Anwendung extends IduIT.cardo.Core.Applications.ApplicationBase
	{
		/**
		* Prüft, ob die Anwendung mit der angegebenen Typ-Instanz umgehen kann.
		* Dies kann bspw. bei fehlenden Berechtigungen nicht der Fall sein.
		* Kann optional Optionen zum Starten der Anwendung mit dem Typ zurückgeben.
		* 
		* @param wkt Eine WellKnownType-Instanz.
		*/
		public static canHandleWellKnownType(wkt: WellKnownType.IWellKnownType): boolean | IduIT.cardo.Core.Applications.StartOptions.IWellKnownTypeStartOptions<string>
		{
			return false;
		}
		/**
		* Gibt die UI-Komponenten der Anwendung zurück.
		* 
		* Diese Methode wird 1x pro Anwendung aufgerufen.
		* Alle Komponenten, die in dieser Methode erstellt werden, können als Referenz (this.xxx) in der Anwendung abgelegt werden
		* und bleiben bestehen, solange die Anwendungsinstanz existiert.
		* 
		* Wie diese Komponenten arrangiert werden, bleibt allerdings dem Layout der Umgebung überlassen.
		*/
		public getUi(): IduIT.cardo.Core.Applications.IApplicationUi
		{
			this.__mainComponent = new Components.MainComponent();
			return {
				component: this.__mainComponent,
				width: 700,
				height: 400
			};
		}
		/**
		* Startet die Anwendung mit dem angegebenen WellKnownType.
		* 
		* Für diesen Typ wurde in canHandleWellKnownType true zurückgegeben.
		* 
		* @param wkt WellKnownType-Instanz.
		* @param option Eine Option, die zum Starten ausgewählt wurde. Optionen können über die statische
		* canHandleWellKnownType zurückgegeben werden. Ist dieser Parameter nicht angegeben, wurde keine Option
		* ausgewählt und es ist die Standard-Aktion auszuführen.
		*/
		public handleWellKnownType(wkt: WellKnownType.IWellKnownType, option?: IduIT.cardo.Core.Applications.StartOptions.IStartOptionEntry<string>): Promise.IPromise<any>
		{
			return Promise.Deferred.resolve(null);
		}
		/**
		* Disposing.
		*/
		public dispose(): void
		{
			super.dispose();
			Kiss.Util.Disposer.disposeMembers(this, '__mainComponent');
		}
		/**
		* Haupt-Komponente 
		*/
		private readonly __mainComponent: Components.MainComponent;
	}
}

Zuletzt geändert: 21.03.2024 09:46:21 (erstmals erstellt 07.03.2024)