Zum Inhalt

Änderungsprotokoll

Änderungen, die in Apps auf Basis von Coworkee Handlungsbedarf auslösen. Neueste zuerst.

Unveröffentlicht

Hinzugefügt

  • OData: Coworkee:OData:CaseInsensitive (CoworkeeODataOptions.CaseInsensitive, Standard true) legt fest, ob Namen in OData-URIs unabhängig von der Schreibweise passen ($filter=name eq 'x'); bisher war das immer an. Siehe OData.
  • OData: AddODataEntity<T>(..., o => o.Key(c => c.IsoCode)) registriert ein Set mit einer anderen Schlüsseleigenschaft beliebigen primitiven Typs; Einzelabfragen sind /odata/Countries('DE'). Datensatz-Anhänge, Social, Workflows, Entity-Historie und Webhook-Entity-Ereignisse weisen Entities ohne Guid-Id bei der Registrierung ab; Client-Entities überspringen solche Sets. Siehe OData.
  • OData: berechnete Eigenschaften (ODataComputedProperty) lassen sich auswählen ($select=ReferenceNumberDisplay) und kommen dann mit ihrem Wert zurück; ein Ausdruck über die eigenen Spalten der Zeile funktioniert auch auf Sets externer Datenbanken. Siehe OData.
  • Die tägliche Benachrichtigungs-Zusammenfassung lässt Benachrichtigungen aus, die schon als eigene Mail hinausgingen.
  • Benachrichtigungen: Typen mit Kategorien und Einstellungen pro Benutzer (AddNotificationTypes, Umfang Off/All/Followed, in der App und per E-Mail, cw.NotificationPreferences), Entitäten verfolgen (Followable<T>, cw.NotificationFollows, CoworkeeFollowButton), INotifier.PublishAsync(NotificationMessage) für Empfänger, Verfolger und ein Publikum, E-Mails pro Benachrichtigung (Vorlage Notifications.Notification) mit Verzögerung und Entprellung (cw.PendingNotificationMails, Job coworkee-notification-mails), Erledigt-Status, Zahlen pro Kategorie und Suche im Eingang (Reiter Eingang, Einstellungen, Gefolgt auf /notifications), neue ICoworkeeApi-Member. Apps brauchen eine Migration. Siehe Realtime.
  • Kommentare: Status (None, Open, Closed) und App-Properties, PUT /api/v1/comments/{id}/status mit der neuen Berechtigung Social.Comments.SetStatus, SocialOptions.ValidateComments (eine Prüfung vor jeder Änderung), das Domain-Event CommentChanged (nach dem Commit). CoworkeeComments zeigt den Status, bietet ein Status-Menü und nimmt NewStatus für neue Kommentare; ISocialApi.SetCommentStatusAsync und AddCommentAsync(..., CommentRequest). Comment ist jetzt ein Aggregate Root; Apps brauchen eine Migration. Siehe Social.
  • Exporte: GET /api/v1/data/{set}/export/stream streamt Fortschrittszeilen (NDJSON) und endet mit einem einmaligen anonymen Download-Link (fünf Minuten); ein Client, der vorher geht, bekommt die Datei per Mail. POST /api/v1/data/{set}/export/mail mailt einen Export an den Nutzer und weitere Empfänger. Geplante Exporte (cw.ExportSchedules, Berechtigung DataTransfer.ExportSchedules, Job coworkee-export-schedules): Export planen im Export-Menü der Tabelle, Seite Geplante Exporte, IDataTransferApi-Member, Mail-Vorlage Data.Export, IExportMailer. ReportSchedule hat Every und Week (monatlich am letzten Freitag), auch für Berichtsabos (neue Spalten). Apps brauchen eine Migration. Siehe Import und Export.
  • Temporäre Dateilinks: POST /api/v1/files/{id}/token und POST /api/v1/attachments/{type}/{id}/{slot}/{attachmentId}/token liefern ein Token, das die Datei fünf Minuten lang ohne Anmeldung öffnet (/api/v1/files/token/{token}/{name}, inline oder ?download=true); Temporären Link kopieren auf der Seite Dateien, IFilesApi.CreateTokenAsync, IAttachmentsApi.CreateTokenAsync. IAccessTokens (Tabelle cw.AccessTokens, einmalig oder bis zum Ablauf, nur Hashes gespeichert) für eigene anonyme Links. Apps brauchen eine Migration. Siehe Dateien.
  • CodeEditor.Highlight: ein regulärer Ausdruck, dessen Treffer hervorgehoben werden; der Mail-Inhaltseditor hebt {{ ... }} hervor.
  • Mail-Inhaltseditor: Platzhalter sind Chips mit ihrem Namen, die visuelle Werkzeugleiste hat die Mail-Formatierungen und Platzhalter einfügen, Strg+Leertaste öffnet die Platzhalterliste in beiden Modi, {{ wirkt im Visuell-Modus wie {. Siehe Mailing.

Geändert

  • CodeCompletion.InsertText ist jetzt der ganze Text samt Auslöser ({{ name }} statt name }}): Die Auswahl ersetzt, was vom CompletionTrigger getippt ist, und die schließenden Klammern, die Monaco ergänzt hat, und Strg+Leertaste fügt überall einen vollständigen Text ein. Neues Flag Snippet für Monaco-Snippets ({{ if $1 }}). Eigene Vorschläge für CodeEditor brauchen den Auslöser im Text.
  • Mail-Inhaltseditor: Inhalte, deren Markup der visuelle Editor ändern würde (andere Styles als color, background-color und text-align, Tags über Textformatierung hinaus, Kommentare), öffnen sich im Quelltext, eine gestaltete Schaltfläche geht beim Speichern also nicht mehr verloren. Die mitgelieferten Vorlagen mit Schaltfläche öffnen sich im Quelltext.

Behoben

  • Der visuelle Mail-Editor zeigte eine leere Werkzeugleiste: RichTextEditor übergab Tools="null", was MudExRichTextEditor 9.5.5 als „keine Werkzeuge“ liest. Er übergibt Tools jetzt nur, wenn gesetzt.
  • Quelltext-Modus des Mail-Editors: {{ tippen und einen Platzhalter wählen ergab {{ name }}}}, weil Monaco die Klammern schon geschlossen hatte.
  • Sicherheit, OData: Zeilenfilter und Set-Berechtigungen gelten jetzt auch für Navigationen. $expand zeigt nur die Zeilen, die das Ziel-Set dem Benutzer auflistet (eine ausgeblendete einzelne Navigation ist null), $filter mit any/all/$count und $orderby über eine Navigation sehen nur sichtbare Zeilen, auch in Exporten, query_data, Facettenfiltern, ODataEntityAccess.FilteredAsync und der globalen Suche. $expand in ein Set ohne dessen Berechtigung antwortet 403, in einen Typ, dessen IODataEntityFilter<T> mehr als Where tut (OrderBy, Take, eine neue Abfrage), 400. Solche Filter sollten einfache Where-Aufrufe werden, wenn ihr Typ über Navigationen erreicht wird. ODataNavigationScope wendet die Regeln auf eigene Abfragen an. Siehe OData.
  • Sicherheit, Berichte: Felder über eine Navigation (Customer/Name) gruppieren und aggregieren nur über die Zeilen, die das Ziel-Set dem Benutzer zeigt; ein ausgeblendetes Ziel zählt als leer. Bisher galt nur die Berechtigung des Sets. Tabellen-Widgets und Abo-Tabellen lesen sie genauso.
  • OData: Hide und HideUnless wirken für Member, die eine Basisklasse deklariert, auch wenn das EDM den Basistyp zwischen Sets teilt (eine Navigation zeigt auf die Basis): der Member bleibt im Modell und wird nur auf dem ausblendenden Set abgewiesen (403), mit einer Warnung beim Start. Geschützte Eigenschaften werden nach dem eigenen Typ der Zeile geprüft, so dass ein HideUnless auf einer geerbten Eigenschaft in Listen und expandierten Zeilen hält. Siehe OData.
  • OData-Antworten mit 400 tragen ODatas eigene Meldung in detail; andere Abfragefehler (EF kann die Abfrage nicht übersetzen, was Zeilenfilter zitieren kann) kommen ohne Detail und werden protokolliert.
  • Sicherheit, OData: die Prüfungen geschützter Eigenschaften, der $expand-Berechtigungen und der Navigationsfilter lesen die Optionen so, wie OData sie parst; ein Schlüssel mit Leerzeichen (?+$filter=, %20$expand=) umging sie alle. Parameter-Aliase (@a=Size) und $apply über Navigationen antworten 400. [AutoExpand]-Navigationen folgen ihrem Ziel (ein ausgeblendetes oder verbotenes Ziel ist null oder leer).
  • Sicherheit, OData: Facetten zählen auf den eingeschränkten Zeilen (ein $filter über eine Navigation zählte ausgeblendete Zeilen mit), und eine Facettenbeschriftung über eine Navigation (LabelPath = "it.Customer.Name") zeigt den Schlüssel für Zeilen, die der Benutzer nicht sehen darf.
  • Sicherheit, OData: berechnete Eigenschaften mit Permission werden auch hinter Navigationen abgewiesen ($expand=Bins($select=Volume), $filter=Featured/Volume gt 0) und in expandierten Zeilen weggelassen; Hide auf einer Navigation einer geteilten Basis hält auf Zeilen, die über die Basis erreicht werden.
  • OData: zwei gefilterte Referenzebenen (Invoice/Shipment/Customer) und eine Sammlung unter einer ausgeblendeten Referenz lassen sich übersetzen; ein Zeilenfilter, der auf der In-Memory-Abfrage scheitert, aus der der Scope ihn liest, blendet nur seinen eigenen Typ aus, und geprüft werden nur die Navigationsziele, die eine Abfrage erreicht. ODataNavigationScope.For(services, type) mit ApplyAsync ersetzt ForAsync und Apply; Berichte prüfen jedes Ziel einmal pro Abfrage.
  • OData: /odata/<Set>/$count antwortete 500; die Seitengröße des Modells ([Page]) gilt auf Entity-Sets wieder.
  • Globale Suche: Felder über eine Navigation (t => t.Bin!.Name) werden in der Abfrage mit den Regeln der Navigation gelesen; ein ausgeblendetes Ziel lässt das Feld leer. Bisher las der Titel sie aus der geladenen Zeile, in der Navigationen nicht geladen sind.
  • Berichte auf SQLite gruppieren DateTimeOffset-Felder eines Coworkee-DbContext nach Zeiträumen (Jahr, Quartal, Monat, Woche, Tag, in UTC); bisher wurden sie abgelehnt. Der DbContext speichert sie als Zahlen; die Zeiträume werden daraus berechnet.
  • Sicherheit, OData: die Prüfungen geschützter, ausgeblendeter und berechneter Eigenschaften laufen auf der gebundenen Abfrage statt auf ihrem Text: Featured/ Size (ein Leerzeichen nach /) und $compute=Owner/ Code as C umgingen sie, und Literale mit einem Eigenschaftsnamen ('a/Note') wurden abgewiesen. Export, Berichtsfilter, Facettenfilter und query_data nutzen dieselbe Prüfung. $search (Listen und Exporte) sucht nur in Eigenschaften, die der Benutzer lesen darf. Facetten, deren Wert über eine Navigation gelesen wird, fallen weg. $apply über Navigationen wird nur zu Zeilen mit Zeilenfiltern oder Berechtigungen abgewiesen.
  • Sicherheit, OData: Aggregate über Entity-Sets in $apply (aggregate(Employees(Salary with max as M))) lasen geschützte Eigenschaften an den Prüfungen vorbei; jetzt wird jeder Member geprüft, den ein geparster Knoten hält, und eine Abfrage, die die Prüfung nicht parsen kann, antwortet 400. ISearchable-Suchfelder über eine Navigation folgen den Regeln des Ziels. Facetten über eine Navigation fallen nur weg, wenn das Ziel einschränkt oder der Wert gesperrt ist; Schlüssel und Ziele ohne Regeln bleiben. Ein Member einer geteilten Basis, der auf einem Set ausgeblendet ist, bleibt auf den anderen lesbar.
  • Sicherheit, OData: ISearchable-Suchfelder werden an jedem Member geprüft, den sie lesen (Verkettungen, Methodenaufrufe), und über Navigationen zu gefilterten Zielen weggelassen; eine Facette über den Schlüssel des Ziels braucht den eigenen, gemappten und lesbaren Fremdschlüssel der Zeile auf den EF-Schlüssel, sonst folgt sie den Regeln des Ziels; Facetten über eine Sammlung werden beim Start abgewiesen; ein Abfrageknoten, den die Prüfung nicht lesen kann, antwortet 400. Berichte auf SQLite weisen Zeiträume des DateTimeOffset eines externen Kontexts sauber ab.
  • AddODataImport mit Id (Aktualisieren, Upsert) weist ein Set, dessen Schlüssel keine Guid ist, bei der Registrierung ab und aktualisiert Zeilen von Sets mit einem Guid-Schlüssel außer Id. AddExtendedAttributes weist eine Entity ohne Guid-Id ab.
  • Sicherheit, OData: Abfrageoptionen ohne $ (filter=, orderby=) werden nicht mehr gelesen; sie umgingen die Prüfung geschützter Eigenschaften. Clients senden $filter.

Sicherheit

  • Die Exportgrenze pro Benutzer gilt für gestreamte, Hintergrund- und gemailte Exporte und wird unter einer Sperre pro Benutzer geprüft. Liegengebliebene Exporte zählen ab StartedAt (neue Spalte von cw.DataTransferJobs, deren Status jetzt ein Concurrency-Token ist) plus einem Puffer; ein Export-Job führt nur wartende Jobs aus und behält einen Fehlschlag, den die Aufräumung inzwischen schrieb. Benachrichtigungsmails gehen nur an aktive Adressen; verzögerte behalten den neuesten Text, wenn die Outbox eine ältere wiederholt. Apps brauchen eine Migration.
  • Anonyme Token-Links (temporäre Datei- und Anhang-Links, Export-Downloads) sind pro Adresse in der Rate begrenzt (Policy tokens, 120 pro Minute), und ihre Tokens sind in den Logs von ASP.NET Core, HttpClient und YARP (samt Scopes) und in den Activities ein- und ausgehender sowie weitergeleiteter Anfragen maskiert (***), in API und BFF; Coworkee.Core.Diagnostics.SecretPaths für eigene Hosts und Logs. Coworkee.Bff verweist jetzt auf Coworkee.Core. Abgelaufene Tokens löscht das Aufbewahrungsziel access-tokens.
  • Exporte: gestreamte Exporte haben eine Grenze pro Benutzer (Coworkee:OData:MaxConcurrentExports, 429), übergeben Exporte über BackgroundExportRows an den Export-Job und brechen nach Coworkee:OData:ExportTimeout ab; liegengebliebene laufende Exporte schlagen fehl. Geplante Exporte senden nichts mehr, sobald der Besitzer deaktiviert ist oder DataTransfer.ExportSchedules oder die Berechtigung des Sets verloren hat, und Exporte ausgewählter Gruppen schlagen fehl, wenn keine davon mehr zugänglich ist, statt alle zu listen.
  • Benachrichtigungen erreichen Verfolger nur, solange sie die Entität noch sehen (jeder Verfolger wird gegen das Followable<TEntity>-Set geprüft); Verfolger von Followable(string)-Typen nur über ein Audience. Wartende Benachrichtigungsmails werden über die Outbox geschrieben, nachdem der sendende Befehl festgeschrieben ist, sind pro Benutzer, Typ und Entität eindeutig, werden nach Version übernommen und lassen den Befehl nie scheitern. Der Job coworkee-stale-exports lässt Exporte scheitern, die länger als ExportTimeout hängen; ohne Retention-Modul werden abgelaufene Zugriffstokens gelöscht, sobald ein anderes Token eingelöst wird.
  • Kommentare: Einen Status zu ändern, auch als Autor oder beim Wiederöffnen, braucht Social.Comments.SetStatus; Autoren geben einen Status nur beim Schreiben an.

0.0.1.41 (2026-10-27)

Hinzugefügt

  • Gruppenbaum und Geltungsbereich: Gruppen haben eine übergeordnete Gruppe und einen Code-Pfad (anlegen unter einer übergeordneten, verschieben mit PUT /api/v1/identity/groups/{id}/parent, Löschen entfernt den Teilbaum), Mitglieder lassen sich hinzufügen, entfernen und verschieben, IGroupScope (Coworkee.Application.Groups) liefert Filtern die zugänglichen, ausgewählten und aktiven Gruppen, <GroupScopePicker /> lässt Benutzer Listen auf einzelne Gruppen einschränken, AddGroupDetailTab ergänzt Reiter auf der Gruppenseite, Gruppenimport über den Pfad der übergeordneten Gruppe. Neue Berechtigungen Identity.Groups.ManageMembers (in Manage enthalten) und Identity.Groups.ScopeAll; siehe Identität.
  • Externe Datenbanken: mit AddCoworkeeExternalDbContext<TContext>() lesen OData-Sets, Zeilenfilter, Suche, Export, Berichte und Client-Entities Entities eines zweiten, einfachen DbContext, und die Unit of Work speichert ihn; Client-Entities nehmen ChangedAt-Datumsspalten für Entities ohne IAuditable. Siehe Persistenz.
  • Users-OData-Set: Groups-Mitgliedschaften, Filter Groups/any(m: m/GroupId eq {id}).
  • Gruppenseite: Schaltflächen für Import (Groups.Manage) und Export. Das Set Groups hat ein berechnetes ParentPath („Kunde A/Hamburg“), und der Export schreibt Name, Description und ParentPath in Baumreihenfolge, so dass sich die Datei wieder importieren lässt, auch in einen anderen Mandanten.
  • Berechnete OData-Eigenschaften mit Exported = true sind Spalten des Exports; ComputedQueryRoot (aus Workflows verschoben) und ODataComputedProperty.Inline helfen, Unterabfragen darin zu schreiben.
  • Als anderer Benutzer anmelden: Berechtigung Identity.Users.Impersonate, Anmelden als in der Benutzerliste und auf der Benutzerseite, Zurück zu meinem Konto im Benutzermenü. POST /api/v1/identity/users/{id}/impersonation stellt ein Ticket für eine Minute aus, /bff/impersonate und /bff/stop-impersonation wechseln die Anmeldung über die Auth-Server-Seiten /Account/Impersonate und /Account/StopImpersonation. Tokens tragen impersonator und impersonator_name, /bff/user liefert ImpersonatorId und ImpersonatorName. [NotWhileImpersonating] lehnt einen Request während einer Übernahme ab; siehe Identität.
  • AuthClientOptions.AllowImpersonation (Coworkee:Auth:Clients:<n>:AllowImpersonation, standardmäßig aus): Nur solche Clients bedienen jemanden, der als anderer Benutzer angemeldet ist, die anderen antworten mit access_denied. AddWeb setzt es für den Web-Client; setze es für eigene BFF-Clients, die Anmelden als unterstützen sollen.
  • AuthServerOptions.MaxImpersonationDuration (Coworkee:Auth:MaxImpersonationDuration, 8 Stunden): Eine Übernahme endet bei der nächsten Autorisierung oder dem nächsten Refresh danach. Sie endet auch, wenn sich der Aufrufer überall abmeldet, sein Passwort ändert, gesperrt wird oder die Regeln nicht mehr gelten.
  • AuditQuery.ImpersonatorId (GET /api/v1/audit?impersonatorId=...); ImpersonatedUser, JobEnvelope und IntegrationMessage haben eine optionale ImpersonatorId.

Geändert

  • Apps brauchen eine Migration (dotnet ef migrations add GroupTree): cw.Groups bekommt ParentId und Code, der Name ist pro übergeordneter Gruppe statt pro Mandant eindeutig, neue Tabelle cw.GroupSelections. Der Migrationshost vergibt den vorhandenen Gruppen ihre Codes.
  • SetGroupMembers braucht Identity.Groups.ManageMembers statt Manage; Rollen mit Manage funktionieren weiter, es enthält die neue Berechtigung.
  • ICoworkeeApi hat neue Gruppen-Mitglieder (GetGroupChildrenAsync, SearchGroupTreeAsync, MoveGroupAsync, AddGroupMembersAsync, RemoveGroupMembersAsync, MoveGroupMembersAsync, GetMyGroupsAsync, SelectGroupsAsync): eigene Implementierungen und Test-Fakes kompilieren erst wieder, wenn sie sie ergänzen.
  • PUT /api/v1/identity/groups/{id} mit einer anderen ParentId als der aktuellen übergeordneten Gruppe antwortet mit 400; verschoben wird eine Gruppe mit PUT /api/v1/identity/groups/{id}/parent. Fehlt ParentId, bleibt die übergeordnete Gruppe.
  • IUnitOfWork liefert eine Hülle, die die externen Contexts und dann den Context der App speichert, nicht mehr den DbContext selbst; Code, der es in einen DbContext umgewandelt hat, lässt sich stattdessen den Context geben.
  • Aspire 13.6.4 (App-Host-SDK, Aspire.Hosting.*, Aspire.Hosting.Testing), CommunityToolkit-Aspire-Pakete 13.6.1, Nextended 10.1.48; coworkee update hebt die festgelegten Pakete an.
  • Apps brauchen eine Migration (dotnet ef migrations add Impersonation): cw.AuditEntries und cw.OutboxMessages bekommen die Spalte ImpersonatorId (nullable).
  • ICoworkeeApi hat das neue Mitglied StartImpersonationAsync, ICurrentUser das Standardmitglied ImpersonatorId (null): Eigene Implementierungen von ICoworkeeApi und Test-Fakes kompilieren erst wieder, wenn sie es ergänzen; eigene ICurrentUser-Implementierungen, die Claims lesen, sollten den Claim impersonator liefern.
  • Ist jemand als anderer Benutzer angemeldet, antworten die Konto- und die Zustimmungsseite des Auth-Servers mit 403, und das Ändern der eigenen E-Mail-Adresse, das Anlegen von API-Schlüsseln, der Export der persönlichen Daten, das Löschen des eigenen Kontos, die Wahl der aktiven Rolle sowie das Anlegen und Ändern von Webhooks und Bericht-Abonnements werden abgelehnt (auth.impersonating).
  • IsLocalPath des BFF (/bff/login, /bff/impersonate, /bff/stop-impersonation) lehnt Rücksprungadressen mit Steuerzeichen oder Backslashes ab.
  • [Realtime] sendet weiterhin an alle mit der Ansichts-Berechtigung; sind Gruppen Kunden in einem Mandanten, sehen Clients anderer Einheiten Datensatz-IDs und geänderte Eigenschaftsnamen. Themen pro Gruppe folgen.

0.0.1.40 (2026-10-25)

Behoben

  • Kibana: Die Anmeldung endete mit 500 auf /oauth2/callback („id token issued by a different provider"); oauth2-proxy erwartet den Issuer mit dem abschließenden Schrägstrich, den der Auth-Server in iss schreibt.

0.0.1.39 (2026-10-25)

Hinzugefügt

  • Open WebUI: app.AddOpenWebUI(ollama) fügt einen Chat über die Modelle von Ollama hinzu, mit Anmeldung am Auth-Server wie die anderen Tools; Template-Option openWebUI (--open-web-ui, mit --ai-provider ollama), eine Karte im Studio und Teil der Vorlage Everything; Deploy-Tool openwebui.

Geändert

  • Grafana, WebDataStudio und AspireUI melden sich am Auth-Server der App an, mit oder ohne Keycloak, wie der Proxy von Kibana: je ein Client {app}-{tool}, nur Administratoren der Systemorganisation, als Admins des Tools. Die lokale Administrator-Anmeldung bleibt. Clients im Keycloak-Realm bekommen sie nicht mehr.
  • Auth-Server: Benutzerinfo-Endpunkt /connect/userinfo (sub, email, name, role, tenant). Coworkee:Auth:DynamicBackchannel nennt An- und Abmeldung nach dem Issuer und Token, Benutzerinfo und Schlüssel nach der gefragten Adresse; so erreichen Tools in Containern ihn über das Container-Netz, der Browser über die Host-Adresse. Der App-Host setzt es mit dem Issuer in einem lokalen Lauf.
  • Grafana 13.2.4 (Nextended 10.1.47); Datenquellen und Volumes von 11.5 funktionieren weiter. Handlungsbedarf: Ein App-Host, der Nextended.Aspire.Hosting.AspireUI pinnt (Option aspireUI), hebt den Pin auf 10.1.47, sonst scheitert das Restore mit NU1605.
  • Studio: Karten von Optionen, die gerade keine Rolle spielen, bleiben sichtbar, deaktiviert, mit dem Grund als Tooltip; Kibana steht direkt nach der Suche und gehört zur Vorlage Everything.

Behoben

  • Auth-Server: Clients mit response_mode=form_post (der OpenID-Connect-Handler von ASP.NET Core, WebDataStudio) bekamen nach der Anmeldung eine leere Seite; die Content Security Policy blockierte das Script, das die Antwort weiterschickt. Sie erlaubt genau dieses Script jetzt über seinen Hash.
  • Kibana: Die Anmeldung eines lokalen Laufs schickt den Browser an die Host-Adressen von Auth-Server und Proxy; sie ging an aspire.dev.internal, das nur Container kennen. Das Dashboard verlinkt kibana-server nicht mehr, darüber kam jeder ohne Proxy herein.
  • AspireUI: Ein lokaler Lauf gibt ihm wie ein Deployment das Volume {app}-aspireui-data. Über das geteilte aspireui-data-aspireui setzte die zuletzt gestartete App die Keycloak-Anmeldung aller anderen (404 auf /realms/<andere App>/.well-known/openid-configuration). Das alte Volume bleibt; löschen mit docker volume rm aspireui-data-aspireui, sobald es nichts mehr nutzt.

0.0.1.38 (2026-10-25)

Neu

  • Themes: sechs neue eingebaute Themes, 21 insgesamt, die sich auch darin unterscheiden, wo das Logo sitzt: GitHub (dunkler Header mit Logo, kein Logo im Menü), LinkHub (Pillen-Schaltflächen per eigenem CSS, Logo nur im Menü), Terminal (gar kein Logo), Slate (dunkle Seitenleiste mit Logo, schlichter Header), Ember und Mint (Logo nur im Header). Eingebaute Themes können jetzt eigenes CSS mitbringen, App-Themes übernehmen es von ihrer Basis. CLI/Template --theme, Studio-Karten. Siehe Eingebaute Themes.
  • Kibana: options.UseKibana() stellt Kibana neben den Elasticsearch-Container (Template- und CLI-Option kibana, Studio-Karte Kibana, Deploy-Tool kibana). Davor meldet oauth2-proxy am Auth-Server der App an, mit Keycloak, wenn es an ist; Kibanas eigenes Single Sign-on bräuchte eine kostenpflichtige Elastic-Lizenz. Neu ist AuthClientOptions.SystemAdministratorsOnly: Bei so einem Client lässt der Auth-Server nur Administratoren der System-Organisation herein. Siehe Kibana.
  • Backups: ein heruntergeladenes Zip wieder hochladen (Backup hochladen auf /admin/backups und in der AirWorks-Backup-App, POST /api/v1/backups/upload?name=, UploadBackupCommand); es steht in der Liste wie ein hier erstelltes Backup und wird genauso wiederhergestellt. Siehe Datenbank-Backups.

Korrekturen

  • Berichtsliste: Favoritenstern und Menü einer Berichtskachel stehen auf einer Linie.

0.0.1.37 (2026-10-20)

Breaking Changes

  • Migration nötig in Apps mit Coworkee.Reporting (das Template hat es standardmäßig): die neuen Tabellen cw.ReportSubscriptions (Berichtsabos) und cw.OutgoingMailAttachments (Mailing, Dateien eingereihter Mails). CoworkeeReportingModule hängt jetzt von CoworkeeMailingModule ab.
  • DateRanges ist von Coworkee.Client.Blazor.Reporting.State nach Coworkee.Contracts.Reporting gezogen (der Server nutzt es auch); wo es direkt aufgerufen wird, das using ergänzen.
  • MudBlazor.Extensions 9.11.6: Apps, die MudBlazor.Extensions selbst festlegen, gehen auf 9.11.6.

Neu

  • Berichte: Abos per Mail. Per Mail abonnieren an einem Bericht schickt ihn täglich, wöchentlich oder monatlich zu einer Uhrzeit einer Zeitzone, auf Wunsch mit eigenem Zeitraum und eigenen Facetten; die Mail (Reports.Subscription, alle Sprachen) in der Sprache des Abonnenten (sonst der Standardsprache der Organisation) zeigt Kennzahlen mit ihren Schwellenwert-Farben und die ersten Zeilen der übrigen Widgets (bei Zeitreihen die neuesten Zeiträume), eine Excel-Arbeitsmappe enthält alle Zeilen, ein Link beendet das Abo. Der Job coworkee-report-subscriptions führt jedes als der Abonnent aus, seine Berechtigungen und Zeilenfilter gelten. Berichte › Abos listet sie; die neue Berechtigung Reports.Subscriptions zeigt die der Organisation. Mailing: Anhänge (MailAttachment, IMailSender.QueueWithAttachmentsAsync, MailEnvelope.Files) für alle vier Anbieter; eigene IMailTransports, die Files nicht beachten, funktionieren weiter ohne sie. Siehe Abos per Mail.
  • Berichte: Berichte mit KI erstellen und ändern. Mit KI erstellen in der Berichtsliste und KI bitten, diesen Bericht zu ändern im Designer schicken einen Prompt an POST /api/v1/reports/generate (GenerateReportDefinitionQuery, Reports.Manage, braucht ein KI-Modell): der Server baut die Anweisungen aus den Quellen, die der Benutzer lesen darf, prüft die Antwort wie beim Speichern und fragt mit den Fehlern erneut (höchstens zweimal); die Definition öffnet sich ungespeichert im Designer (reports/new für einen neuen, Rückgängig für eine Änderung). Neues Assistenten-/MCP-Werkzeug generate_report; create_report und update_report werden dem Assistenten jetzt angeboten (sie laufen nach der Bestätigung des Benutzers). Siehe Einen Bericht mit KI erstellen.
  • Berichte: Schwellenwerte. WidgetOptions.Thresholds (ThresholdStop: Startwert, Theme-Farbe success/warning/error/info oder #rrggbb, Bezeichnung) färben Werte und Tachos von Kennzahlen, Pivot-Zellen, die Tabellenspalte ThresholdColumn und mit ColorBarsByThreshold Balken; jedes Band zeigt zusätzlich ein Symbol und seine Bezeichnung. Der Designer hat einen Bänder-Editor mit Bänder aus dem Ziel. Siehe Schwellenwerte.
  • Berichte: Umschalter für Betrachter. WidgetOptions.Metrics (MetricView: Bezeichnung, Aggregate, Zahlenformat, zusätzlicher Filter) und WidgetOptions.Buckets (BucketView: Bezeichnung, Gruppierungsfeld, Datumsstufe, Datumsfeld) bieten Alternativen, die Betrachter im Kopf von Kennzahlen, Diagrammen, Pivots und Tabellen wählen; der Bericht bleibt unverändert, Exporte nutzen die Wahl, und die Wahl bleibt je Benutzer in der neuen Benutzereinstellung Reports.Views erhalten. Beim Speichern wird jede Alternative geprüft. Siehe Umschalter für Betrachter.
  • AirWorks: Vollbild-App-Starter. Coworkee:AirWorks:Launcher des AirWorks-Hosts (menu, Standard, oder overlay) erreicht den Desktop als launcher von /bff/user; das Template schreibt es in die appsettings.json des Hosts (Option airworksLauncher, CLI --airworks-launcher, Studio-Karte AirWorks-App-Starter). Der Starter listet die Apps der Administration unter Administration und andere Apps unter ihrer launcherGroup (neu an CoworkeeApp, entityApp und embedApp, das auch shortcut nimmt; group bleibt der Bereich der Administration), mit Kontextmenü (Öffnen, Zum Desktop hinzufügen) und Fokusfalle. Bestehende AirWorks-Clients setzen @fgilde/airworks in ClientApp/package.json auf Commit 9657ff3 (AirWorks 0.6.0), um ihn zu nutzen. Siehe App-Starter.

0.0.1.36 (2026-10-20)

Breaking Changes

  • MudBlazor.Extensions 9.11.5: Apps, die MudBlazor.Extensions selbst festlegen, gehen auf 9.11.5.

Korrekturen

  • Navigation: der hinter den Filter-Chips getippte Filtertext bleibt beim Öffnen einer Seite im Filterfeld stehen; bisher wurde das Feld geleert, während der Filter weiter galt. Die Korrektur kommt mit MudBlazor.Extensions 9.11.5.

0.0.1.35 (2026-10-20)

Breaking Changes

  • Anhänge an Datensätzen: IRecordAttachments.PutAsync, DeleteAsync und DeleteAllAsync speichern nicht mehr selbst, sie gehören zur Unit of Work des Aufrufers (die Pipeline speichert nach einem Command, anderer Code ruft IUnitOfWork.SaveChangesAsync). Blobs zu nie committeten Zeilen werden am Ende des Scopes gelöscht, Blobs entfernter Zeilen nach dem Commit. App-Code, der sie außerhalb eines Commands aufrief, speichert jetzt selbst; lesende Aufrufe (OpenAsync, ListAsync) sehen nur gespeicherte Zeilen. Die HTTP-Endpunkte speichern wie bisher. Siehe Anhänge an Datensätzen.
  • Migration nötig in Apps mit Coworkee.Social: die neue Tabelle cw.Reactions (Emoji-Reaktionen).
  • Error.Validation(field, message).Message ist die übergebene Meldung statt One or more validation errors occurred.; ebenso der title der 400-Problem-Details. code (validation) und errors bleiben gleich; die Überladung mit dem Dictionary behält den allgemeinen Text.
  • Migration nötig in Apps mit Coworkee.Workflows: die neue Spalte RequestedBy von cw.WorkflowApprovals (wer den Übergang in die Genehmigung ausgelöst hat). Die Eskalation einer Genehmigung mit NotByRequester() nimmt die antragstellende Person nicht mehr als Genehmigende auf, wenn sie in der Eskalationsrolle ist; vor der Migration gestartete Genehmigungen haben keine gespeicherte antragstellende Person und werden nicht gefiltert.

Korrekturen

  • coworkee entity add/module add: generierte Listenseiten zeigen bool-Spalten als übersetztes Ja/Nein, Enum-Spalten mit ihrer übersetzten Bezeichnung (EnumLabels), DateTimeOffset-Spalten in Ortszeit und DateOnly-Spalten als kurzes Datum in der Kultur des Benutzers, statt True, Membernamen und +00:00-Offsets. Bestehende generierte Seiten können dieselben CellTemplates übernehmen.
  • Lokalisierung: 57 Texte der Bibliothek, die in jeder Sprache englisch erschienen, gibt es jetzt in allen 22 Sprachen: der Einrichtungsassistent (Beschriftungen, Schritte, Hinweise, Meldungen), die Beschreibungen der Seiten Berichte, Chat und Berichtsdaten, Noch keine Berichte, Chat mit {0}, die Berechtigungsmatrix und die Ressourcenberechtigungen (Berechtigungen durchsuchen, Benutzer oder Gruppe, ...), Versionsverlauf, Audit-Zeitleiste, Theme-Auswahl, Meine Einstellungen und die Schalter der Navigation. Der Test der literalen Schlüssel prüft auch l[...], beide Texte von L[a ? "…" : "…"] und die Texte, die an PageHeader und CoworkeeDataTable gehen.
  • Reporting: Berichtsnamen und -beschreibungen, Widget-Titel und Wertbeschriftungen zeigen ihre Übersetzung, wenn die App einen Text dafür hat (geseedete Berichte lassen sich lokalisieren; der gespeicherte Text bleibt der Schlüssel, CoworkeeLocalizer.Stored); ein Widget aus einer Vorlage speichert den Schlüssel der Vorlage statt des übersetzten Titels. Die Summe im Donut und die Ziellinie sind übersetzt, und Kategorien der x-Achse drehen sich um 45°, statt abgeschnitten zu werden ("2…", "Back…"). Siehe Reporting.
  • CoworkeeDataTable: Spaltenköpfe behalten ihre Beschriftung auf einer Zeile, eine breite Tabelle scrollt also seitlich, statt ihre letzten Spalten ("Kostenstell", "Stat") unter die fixierte Aktionsspalte zu quetschen.
  • Layout: der App-Titel in der App-Leiste bleibt einzeilig und endet auf schmalen Bildschirmen mit Auslassungspunkten, statt umzubrechen.
  • CoworkeeWorkflow: das Fälligkeitsdatum einer Genehmigung behält seinen Abstand zum Wartetext ("…Personen. Fällig 25.10.").
  • Aspire: AddMigrations verdrahtet die Module, die das Migrationsprojekt referenziert, wie bei jedem Dienst (Speicher, Mail, Redis, Suche, Übersetzung, Einträge in app.Modules); Dateien eines Seeds landeten vorher im lokalen Ordner des Migrations-Hosts und öffneten sich in der App mit 404. App-Hosts, die CoworkeeModules.Storage von Hand in die Migrationen verdrahtet haben, können diese Zeile streichen.
  • Dialoge: ein Seitenblatt oder Dialog der Bibliothek, geöffnet aus einem Dialog, den die App mit dem einfachen MudBlazor-ShowAsync zeigt (etwa die Dateivorschau von RecordAttachment), blieb unsichtbar; MudBlazor.Extensions wandte seine Optionen auf den äußeren Dialog an. Das Layout markiert jetzt die schon offenen Dialoge, wenn ein neuer hinzukommt.
  • Reporting: Diagramme über eine berechnete Quelle (AddReportSource) mit mehreren Zeilen pro Kategorie und Serie schlagen nicht mehr fehl (die Gruppierung im Speicher machte aus jeder Zeile eine eigene Gruppe, das Diagramm warf dann wegen doppelter Schlüssel); Diagrammserien addieren doppelte Kategorien.
  • Datensatz-Anhänge: Mit CreateScope() erzeugte Scopes (Startcode, das Elsa-Modul, ältere Dienste) lassen sich mit eingeschaltetem Anhangsmodul wieder freigeben (AttachmentBlobs ist auch IDisposable).
  • Sicherheit, Social: Kommentare, Tags, Bewertungen, Reaktionen und ihre Echtzeit-Topics eines mit Entitätsklasse registrierten Typs (Comments<T>, ...) verlangen jetzt, dass der Benutzer die Zeile über das OData-Set des Typs sieht (Berechtigung des Sets, Organisation, Soft Delete, Zeilenfilter IODataEntityFilter<T>); vorher reichten die Berechtigung und eine vorhandene Id. Tag-Zähler und GET /api/v1/tags/{entityType}/entities zählen nur sichtbare Zeilen. Typen ohne OData-Set behalten die Id-Prüfung; Authorize(...) ergänzt weiterhin Prüfungen, Hooks, die nur den Zeilenfilter wiederholten, können entfallen. Siehe Social.
  • Workflows: Für eine Entität, die der Command in derselben Unit of Work hinzugefügt hat, lädt WorkflowEngine.LoadAsync die Include(...)-Referenzen über ihren Fremdschlüssel, wenn nur die ID gesetzt war; Describe, Guards und Aktionen sehen dort kein null mehr.
  • CoworkeeDataTable: die fixierte Aktionsspalte bleibt auf Zeilen unter dem Mauszeiger und gestreiften Zeilen deckend (die durchscheinende Zeilenfarbe liegt über der Oberfläche), langer Zellentext scheint nicht mehr unter den Knöpfen durch, im hellen wie im dunklen Modus.
  • coworkee entity add/module add: Navigationseinträge, Seitentitel und Berechtigungsgruppen zeigen Namen aus mehreren Wörtern als Wörter („Benefit enrollments“, „Time off: Benefit enrollments“) statt des PascalCase-Plurals; Dialogtitel und Berechtigungsnamen taten das schon.
  • coworkee entity add/module add: erzeugte Seiten geben PageHeader und CreateText Textschlüssel statt übersetzter Texte, die ein zweites Mal übersetzt wurden („People“ → „Personal“ → „Persönlich“). Regel: PageHeader und CoworkeeDataTable.CreateText übersetzen, Aufrufer übergeben Schlüssel. Erzeugte Seiten bestehender Apps entfernen das @L[...] um diese Werte.
  • coworkee entity add/module add: Referenz-Facetten bekommen LabelPath = "it.{Navigation}.{Anzeige}", damit eine Navigation mit dem Namen eines Dynamic-LINQ-Schlüsselworts (Parent, It, Root, ...) die OData-Menge nicht mehr mit 500 antworten lässt, wenn Facetten angefragt werden. Handgeschriebene Facetten auf so einer Navigation ergänzen das it. selbst. Siehe OData.
  • ResourceCalendar: ein Monat passt in die verfügbare Breite (schmalere Tagesspalten und Ressourcenspalte) und blendet am rechten Rand aus, wenn er scrollt; Zeilen wachsen mit einer zweizeiligen Ressourcenzelle, statt sie abzuschneiden; eine Zeile mit UserId zeigt Avatar mit Anwesenheit plus Title und Subtitle statt der Benutzerkarte ohne Untertitel.
  • Sicherheit, Workflows: Zustand, Historie, Kommentare, Übergänge, Entscheidungen und das Echtzeit-Topic eines Workflows verlangen jetzt, dass der Benutzer die Zeile über ihr OData-Set sieht (Berechtigung des Sets, Organisation, Zeilenfilter IODataEntityFilter<T>); vorher reichten Workflows.View und eine Id. Genehmigende der offenen Genehmigung der Zeile sehen und entscheiden sie weiterhin. WorkflowEngine.LoadAsync nimmt ein optionales WorkflowAccess (Standard See). Siehe Workflows.
  • Vorlage: App.razor bindet das Scoped-CSS-Bundle ein, damit .razor.css-Dateien des Web-Clients ohne Weiteres greifen (das Logo der Startseite nutzt eine). Bestehende Apps, deren Web-Client .razor.css-Dateien hat, ergänzen <link href="@Assets["MyApp.Web.styles.css"]" rel="stylesheet" /> (mit dem Namen ihres Web-Projekts) nach dem Stylesheet von MudBlazor.Extensions.
  • API-Tests einer App mit --event-bus rabbitMq starten ohne Broker: neue Einstellung Coworkee:EventBus:RabbitMq:Enabled (Standard true); die Fixture der Vorlage setzt sie auf false, die Events erreichen die Handler im Prozess. Bestehende Apps ergänzen builder.UseSetting("Coworkee:EventBus:RabbitMq:Enabled", "false"); in ihrer ApiFactory. Siehe Testen.
  • coworkee spec validate lehnt eine Entität ab, deren Plural dem Namen gleicht (der erzeugte Feature-Namespace verdeckte den Entitätstyp, die App baute nicht).

Neu

  • Anhänge an Datensätzen: Echtzeit-Topic attachments:{entityType}:{entityId} (AttachmentTopics.Record) bei jedem hinzugefügten, ersetzten oder gelöschten Anhang, für Benutzer, die einen Slot des Datensatzes lesen dürfen; RecordAttachment folgt ihm und lädt neu. Abschnitt attachments im Export personenbezogener Daten (die Uploads des Benutzers, nur Metadaten; das Löschen behält die Dateien wie beim Abschnitt files). Siehe Anhänge an Datensätzen.
  • Workflows: State(...).Approval(ctx => ..., rejected, a => a.ApprovedTo(...)) wählt den genehmigten (und mit einer zweiten Funktion den abgelehnten) Zustand bei der Entscheidung aus der Entität, etwa eine zweite Genehmigung für große Beträge hinter einem einzigen Einreichen-Übergang; die möglichen Zustände stehen in ApprovedTo/RejectedTo und werden beim Start und bei jeder Entscheidung geprüft. ApprovalStep.ApprovedState/RejectedState sind bei solchen Genehmigungen null; ApprovedTargets/RejectedTargets listen die Zustände. Siehe Workflows.
  • Blazor: CoworkeeDetailPanel<TKey>, die Details eines Board- oder Listeneintrags in einer Schublade rechts (auf Smartphones bildschirmfüllend), gebunden an einen Query-Parameter (?item={id}, QueryParameter): Links, Neuladen und die Zurück-Taste öffnen und schließen ihn; Escape und die Schließen-Schaltfläche schließen ihn; die Fragmente Title, Actions und ChildContent bekommen den Schlüssel, dazu Width, KeyChanged, Open(key). Siehe Detailbereich.
  • Anhänge an Datensätzen (Coworkee.Files, Modul CoworkeeAttachmentsModule, Migration nötig für die Tabelle cw.RecordAttachments): services.AddRecordAttachments<LeaveRequest>("LeaveRequests", a => a.Slot("certificate", maxBytes, "application/pdf", "image/*").MultiSlot("documents").WriteRequires(permission)); Endpunkte /api/v1/attachments/{type}/{id}/{slot} (Liste, inline oder Download, Upload, Löschen), deren Lesen der Zeilensichtbarkeit des OData-Sets des Typs folgt, Schreiben zusätzlich der Schreibberechtigung und den Authorize-Prüfungen; IRecordAttachments (PutAsync/OpenAsync/DeleteAsync/DeleteAllAsync/ListAsync) für App-Code, Hook IAttachmentScanner (Virenscan), Aufbewahrungsziel orphaned-attachments. Client: IAttachmentsApi und die Komponente RecordAttachment (ein Slot: Upload mit MudExUploadEdit, Ansicht mit MudExFileDisplay, Löschen). ODataEntityAccess.VisibleIdsAsync<T>: die Ids, die der aktuelle Benutzer sieht. Siehe Anhänge an Datensätzen.
  • Anonyme Datensätze: IAnonymous (oder die Basis AnonymousEntity mit zufälligen IDs per Guid.NewGuid()) für Zeilen, die nicht auf ihren Autor zeigen dürfen: kein CreatedBy/ModifiedBy/DeletedBy, kein Audit-Eintrag, Snapshots ohne CreatedBy, Outbox-, Webhook- und Elsa-Trigger-Nachrichten ohne Benutzer und Trace-ID, CreatedAt/ModifiedAt/DeletedAt auf den UTC-Tag gekürzt (IAnonymous.TimestampPrecision, record.Truncate(time) für eigene Zeitstempel). Siehe Audit.
  • OData: services.AddODataChildFilter<TalkItem, Talk>(i => i.Talk) lässt Kind-Zeilen den Zeilenfiltern ihres Elternteils folgen (sichtbar, wenn ODataEntityAccess.VisibleAsync<Talk> das Elternteil enthält; Unterabfrage auf den Fremdschlüssel), statt eines eigenen Filters, der den des Elternteils wiederholt. Siehe OData.
  • Blazor: kleine Diagramme ohne das Reporting-Modul in Coworkee.Client.Blazor.Components.Charts: CoworkeeBarChart (horizontal oder Vertical, Werte, Farben, ShowPercent, Max), CoworkeeDonut (Segmente, Label in der Mitte, Legende) und CoworkeeSparkline; reines HTML/SVG in den Theme-Farben (Dark Mode), Texte für Screenreader lesbar. Siehe Kleine Diagramme.
  • iCalendar-Dateien: IcsCalendar/IcsEvent/IcsTime in Coworkee.Application.Calendar (ganztägig, UTC- oder TZID-Zeiten, Ort, URL, Status, Kategorien, Organisator, Teilnehmende; Maskierung nach RFC 5545 und UTF-8-sicherer Umbruch nach 75 Oktetts) und Results.Ics(calendar, fileName?) in Coworkee.AspNetCore.Http (text/calendar; charset=utf-8). Siehe Kalender-Feeds für Abo-Adressen mit Token und Caching-Header.
  • Berichte: eigene Datenquellen. services.AddReportSource<HeadcountRow>("HeadcountByMonth", "Mitarbeitende pro Monat", permission, async (context, ct) => rows) registriert Zeilen eines einfachen Typs (ein IQueryable der Datenbank oder eine Liste, im Speicher aggregiert), die Berichte nach den Entitätsmengen listen (ReportSourceDto.Computed, Summen-Symbol im Designer) und wie eine Menge aggregieren: OData-Filter gegen den Zeilentyp geparst, Gruppierung mit Datums-Buckets, alle Funktionen; ReportSourceContext liefert die Dienste der Anfrage und den Zeitraum aus dem Filter des Widgets (DateFrom/DateTo). Tabellen lesen ihre Zeilen über POST /api/v1/reports/rows (IReportsApi.RowsAsync, neues Interface-Mitglied); Datentabellen-Widget und Facetten gibt es dafür nicht, Drill-down nur, wenn ReportingOptions.DrillDown eine Seite nennt. Siehe Berichte.
  • AirWorks (@coworkee/airworks): Berichtsansicht und Designer können berechnete Berichtsquellen: Tabellen darauf lesen POST /api/v1/reports/rows, Datentabellen lassen sie aus (der Designer bietet dort nur Entitätsmengen an), die Quellenauswahl markiert sie mit ∑, der Zeitraum eines reinen Datumsfelds filtert nach Tagen. @coworkee/client hat ReportSourceDto.computed, ReportRowsRequest und ReportRowsResult.
  • Social: Emoji-Reaktionen. AddCoworkeeSocial(s => s.Reactions<Product>("Products").CommentReactions()), Berechtigungen Social.Reactions.View/.Create, GET/POST /api/v1/reactions/{entityType}/{entityId} (umschalten mit { "emoji" }), Realtime-Topic reactions:{entityType}:{entityId}, Komponente <CoworkeeReactions EntityType="Products" EntityId="id" /> (Anzahl, eigene gefüllt, beim Überfahren, wer reagiert hat, Auswahl aus SocialReactions.Defaults); mit CommentReactions() bekommt jeder Kommentar von CoworkeeComments dieselbe Zeile (CommentDto.Reactions, CommentThreadDto.ShowReactions/CanReact). Export und Löschung personenbezogener Daten umfassen Reaktionen. ISocialApi hat GetReactionsAsync/ToggleReactionAsync. Siehe Kommentare, Tags, Bewertungen und Reaktionen.
  • Workflows: WorkflowEngine.LoadAsync findet eine Entität, die die aktuelle Unit of Work hinzugefügt und noch nicht gespeichert hat, im Change Tracker; so legt ein Command einen Datensatz an und führt seinen ersten Übergang in einem SaveChanges aus (kein vorzeitiges Speichern; ein abgelehnter Übergang speichert nichts). Siehe Workflows.
  • Hintergrundjobs: TenantJobRunner.ForEachTenantAsync((services, tenantId, ct) => ..., ct) führt Arbeit einmal pro aktiver Organisation als deren System und in einem eigenen Scope aus, protokolliert und liefert fehlgeschlagene Organisationen und macht mit den anderen weiter; ForTenantsAsync(ids, ...) für die Organisationen, die eine Abfrage gefunden hat. ITenantDirectory.GetActiveTenantIdsAsync. Der Job workflow-deadlines nutzt ihn (ein Fehler in einer Organisation stoppt den Lauf nicht mehr). Siehe Hintergrundjobs.
  • Client: ClientUser.From(principal) (IsAuthenticated, UserId, TenantId, Email, DisplayName) und das scoped CurrentUserInfo (GetAsync(), Changed), registriert von AddCoworkeeClient; die Komponenten der Bibliothek nutzen es statt sub selbst zu parsen, und der Client-Principal trägt den Claim tenant_id. Siehe Client.
  • Berechtigungen: berechnete Berechtigungen vergeben eine definierte Berechtigung aus Daten (services.AddComputedPermission(HrPermissions.LeadsPeople, (c, ct) => db.Employees.AnyAsync(e => e.LeaderUserId == c.UserId, ct)) oder IComputedPermission), einmal pro Request und Benutzer nach den Rollenrechten geprüft; sie zählen für [RequiresPermission], IPermissionChecker, OData, MCP und die Berechtigungsliste des Clients (Navigation, PermissionGate, Policies). CoworkeeNavItem(..., Visible: sp => ...) blendet einen Eintrag über einen Callback im Client aus. Siehe Berechtigungen.
  • ODataPicker/OptionalODataPicker: TextProperty="FirstName,LastName" zeigt mehrere Eigenschaften mit Leerzeichen verbunden, fragt sie per $select ab, sortiert nach ihnen und findet eine Zeile, wenn jedes Wort der Suche in einer davon vorkommt; mit einer Eigenschaft trifft eine Suche aus mehreren Wörtern jetzt auch, wenn die Wörter nicht nebeneinander stehen. Siehe Datentabellen.
  • ResourceCalendar: CalendarResource.WorkDays ersetzt die Arbeitstage für eine Zeile (Teilzeit), und weitere Attribute (id, data-testid, ...) landen auf dem Wurzelelement. Siehe Ressourcenkalender.
  • Workflows: EntityWorkflow<T>.Include(e => e.Employee) lädt eine Navigation mit der Entität, sodass Describe, Guards und Aktionen sie nutzen können (Describe(l => $"Urlaub von {l.Employee.Name}")). Siehe Workflows.
  • OData-Sets: AddODataEntity<T>(set, permission, o => o.Hide(...).HideUnless(e => e.Salary, permission)) zeigt eine Eigenschaft nur Benutzern mit der Berechtigung; Listen, Einzelabrufe, $filter/$orderby/$select/$expand, Facetten, Exporte, Berichte, die globale Suche, die Daten-Tools des Assistenten, die Entity-Historie und Webhooks halten sich daran. ODataEntityRegistration.ProtectedProperties, ODataEntityAccess.DeniedPropertiesAsync. Siehe OData.
  • ISystemDispatcher.SendAsync(request, permissions, ct) führt einen Request als System in der Organisation des Aufrufers aus, mit genau den genannten Berechtigungen und in einem eigenen Scope (Audit: system); darunter CurrentUserScope.BeginSystem(tenantId, permissions) und SystemActor. AnonymousCurrentUser und TestCurrentUser reichen jetzt ClientId/ClientPermissions des Umgebungs-Benutzers weiter. Siehe Berechtigungen.
  • CoworkeeDataTable: RowHref (Func<T, string?>) und OnRowClick (EventCallback<T>) öffnen eine Zeile per Klick (nicht auf ihre Buttons oder Checkbox) oder über ihren Öffnen-Button in der Aktionsspalte (Tastatur, echter Link bei RowHref). Siehe Datentabellen.
  • ResourceCalendar (Blazor): Ressourcen über Tagen mit Balken, halben Tagen, Spuren, Feiertagen und Ziehen zur Tagesauswahl. Siehe Ressourcenkalender.
  • Workflows: ApprovalStep.UsersFrom(context => ...) ermittelt Genehmigende beim Start der Genehmigung aus der Entität, etwa die Führungskraft der antragstellenden Person.

Korrekturen

  • Eine gespeicherte geheime Einstellung, die der aktuelle Data-Protection-Schlüsselring nicht entschlüsseln kann (etwa nach einem Wechsel des Schlüsselrings), gilt mit einer Warnung im Log als nicht gesetzt, statt jedes Lesen der Einstellungen scheitern zu lassen (/api/v1/ai/status antwortete auf jeder Seite mit 500). Das Secret einfach neu speichern. SettingProtector bekommt einen ILogger<SettingProtector>.

0.0.1.34 (2026-10-18)

Breaking Changes

  • Migration nötig: Passkeys liegen in der neuen Tabelle cw.UserPasskeys (Schema-Version 3 von ASP.NET Core Identity: Schlüssel CredentialId, Data als JSON, Cascade von cw.Users). Eigene ICoworkeeApi-Implementierungen ergänzen ResetUserPasskeysAsync.
  • Migration nötig in Apps, die das neue Modul Coworkee.Retention aufnehmen: die Tabelle cw.RetentionRuns. Apps ohne das Modul brauchen nichts.
  • SettingWriter und SettingProtector aus Coworkee.Settings sind jetzt öffentlich (für Module, die eigene Einstellungen schreiben).

Neu

  • Passkeys (WebAuthn) am Auth-Server: Mit Passkey anmelden und das Autofill des Browsers auf der Anmeldeseite, die Kontoseite Passkeys zum Hinzufügen (höchstens 20), Umbenennen und Entfernen, Passkeys (n) im Sicherheitsreiter des Profils, Passkeys: n mit Alle entfernen im Anmeldebereich der Benutzerverwaltung (POST /api/v1/identity/users/{id}/passkeys/reset), PasskeyCount in ProfileDto und UserDetailDto, Passkeys im Export der persönlichen Daten. Eine Anmeldung per Passkey überspringt die Bestätigung in zwei Schritten. Die Einstellung Account.Passkeys.Enabled (Standard an) und der Login-Modus External schalten sie ab; Coworkee:Auth:Passkeys bindet IdentityPasskeyOptions (ServerDomain hinter einem Proxy ohne Forwarded Headers). Siehe Passkeys.
  • Der Audit-Trail schreibt binäre Schlüssel als base64url in EntityId statt System.Byte[].
  • Coworkee.Retention: Tage pro Datenart (Audit-Trail, Snapshots, verarbeitete Outbox und Inbox, weich gelöschte Datensätze, KI-Werkzeugaufrufe und Unterhaltungen, Webhook-Zustellungen, Mail-Log, Benachrichtigungen, Import- und Export-Jobs, gelöschte Dateien mit ihren Blobs, Backups, OpenIddict-Tokens), der nächtliche Job coworkee-retention (Coworkee:Retention:Cron, Standard 41 2 * * *), die Admin-Seite Aufbewahrung (/admin/retention, Permission Retention.Manage, nur in der Systemorganisation) mit Live-Zählung und Jetzt löschen, Endpoints /api/v1/retention und ein Abschnitt im Systemstatus. Jede Datenart, die bisher für immer blieb, bleibt so, bis jemand Tage einstellt; nur das Webhook-Protokoll behält seine 30 Tage. Module und Apps melden eigene Datenarten mit IRetentionTarget und services.AddRetentionTarget<T>() an; ExecuteDeleteInBatchesAsync löscht in Batches zu 1000. Template-, CLI- und Studio-Option retention (an); neue Apps behalten verarbeitete Nachrichten und Tokens 14 Tage, Import- und Export-Jobs 30. Siehe Aufbewahrung.
  • Webhooks: WebhookOptions.LogRetentionDays ist der Standard von Retention.webhook-deliveries.Days; mit dem Retention-Modul tut die alte tägliche Bereinigung des Webhook-Protokolls nichts.
  • CoworkeeMigrationsOptions.AfterSeed führt die eigenen Datenschritte der App nach Migration und Seed aus, bevor Keycloak abgeglichen wird.

0.0.1.33 (2026-10-17)

Breaking Changes

  • Migration nötig: ein gefilterter eindeutiger Index IX_Themes_Name auf cw.Themes (eingebaute Themes, TenantId null) und die Spalte cw.ChatAttachments.ThumbnailKey. Der Index verhindert, dass zwei App-Instanzen die eingebauten Themes auf SQL Server und SQLite doppelt anlegen (Postgres lehnte das schon ab); der Seed räumt solche Doppel nicht mehr auf. Meldet die Migration einen doppelten Schlüssel, lösch zuerst die überzählige eingebaute Zeile.
  • Wiederherstellen aus dem Verlauf prüft seine Vorschau: EntityHistoryPreviewDto.Version und EntityHistoryRestoreRequest.Version sind neu; POST /api/v1/history/{set}/{id}/restore antwortet 409, wenn die Version fehlt oder sich der Datensatz seit der Vorschau geändert hat. Verlaufsdialog, Rückgängig im Audit-Log und das AirWorks-Datensatzfenster zeigen dann die neue Vorschau. Eigene IEntityHistoryApi-Implementierungen und direkte API-Aufrufe geben die Version weiter; OpenAPI-Snapshots und SDKs neu erzeugen. Siehe Auditing.
  • Coworkee.Chat hängt von Coworkee.BackgroundJobs ab und bringt SkiaSharp (MIT) mit seinen nativen Linux-Bibliotheken für die Vorschaubilder.

Neu

  • coworkee update fügt einzelne Zeilen selbst ein (Regelart insertions in upgrade-rules.json: nach der ersten Zeile, die zu einem Muster passt, mit deren Einrückung, übersprungen, wo die Zeile schon steht; --dry-run listet sie nur). Der Schritt von 0.0.1.32 (builder.ConfigureContainer(new DefaultServiceProviderFactory()); in src/*.Migrations/Program.cs) passiert jetzt automatisch.
  • Neue Apps haben eine AGENTS.md mit den Regeln für Coding-Agenten (Aufbau, Pflichtmuster, Fallstricke, Migrationen, Tests, CLI und MCP) und eine CLAUDE.md, die sie einbindet. Bestehende Apps übernehmen sie aus einer neuen App mit denselben Optionen.
  • AirWorks: Der Theme- und der Mail-Vorlagen-Editor der Verwaltung sind die Seiten der Web-App in einem Fenster, wenn die BFF die Web-App kennt (embedOr), die TypeScript-Editoren bleiben der Ersatz; Workflow-Designer öffnet Elsa Studio in einem Browser-Tab (Workflows.Elsa.Manage); das Datensatzfenster hat In der Web-App öffnen (die Seite des Treffers der globalen Suche). Neuer Textschlüssel Open in the web app.
  • Coworkee:Auth:FrameAncestors: Origins, die die Kontoseiten des Auth-Servers einrahmen dürfen (Standard: keine), damit ein AirWorks-Fenster die Anmeldeseite einer eingebetteten Webseite zeigen kann. Nur auf derselben Site; siehe AirWorks.
  • app.UseCoworkeeSecurityHeaders() (Coworkee.Bff) schickt eine Content-Security-Policy für den Blazor-WebAssembly-Client (frame-ancestors aus Coworkee:Bff:FrameAncestors, MudEx-Dateivorschauen aus blob- und data-URLs, Hot Reload in Development), nosniff und eine Referrer-Policy; weitere Quellen pro Direktive oder eine eigene Policy über Coworkee:Bff:SecurityHeaders. Der Web-Host der Vorlage nutzt es; bestehende Apps ergänzen es vor UseStaticFiles und entfernen eine selbst geschriebene Policy. Apps mit der Cloud-Auswahl des Files-Moduls ergänzen deren Hosts. Siehe Sicherheits-Header der Web-App.
  • Coworkee:Bff:MaxRequestBodySize begrenzt, was der Web-Host an die API weiterleitet; ohne den Wert haben seine weitergeleiteten Routen keine Grenze und die API prüft ihre eigene. Ein selbst geschriebenes ConfigureKestrel(k => k.Limits.MaxRequestBodySize = ...) im Web- oder AirWorks-Host ist unnötig und kann weg.
  • builder.AddCoworkeeMigrations<TDbContext>(...) und host.RunCoworkeeMigrationsAsync() (Coworkee.Identity.Setup) sind der Migrations-Host: migrieren mit Wiederholungen (Migrations:Attempts), die Entwicklungsbenutzer oder den ersten Administrator eines Deployments seeden, die Entwicklungsbenutzer im Stand halten, Keycloak abgleichen, dazu die Container-Korrektur von 0.0.1.32. Die Vorlage nutzt es; bestehende Apps ersetzen damit ihre selbst geschriebene src/*.Migrations/Program.cs, damit spätere Korrekturen mit dem Paket kommen (coworkee update weist darauf hin). Siehe Migrationen und Seed.
  • Chat-Bilder bekommen ein kleines WebP, das der Server erzeugt (höchstens 480 px, aufrecht gedreht, GET /api/v1/chat/attachments/{id}/thumbnail, ChatAttachmentDto.HasThumbnail); die Nachrichtenliste zeigt es, ein Klick öffnet die ganze Datei.
  • Jede Chat-Datei öffnet sich im MudEx-Datei-Viewer (MudExFileDisplay), auch ein Upload vor dem Senden. Die Regel für eigene Seiten steht unter Dateien.
  • Die Policy von UseCoworkeeSecurityHeaders erlaubt die CDNs, von denen der Datei-Viewer seine Viewer lädt (Coworkee:Bff:SecurityHeaders:FileViewerCdns: jsDelivr, esm.sh, unpkg), in script-src, style-src und connect-src, damit Word, Excel, Karten und die anderen Viewer ohne weiteres Zutun gehen; leere die Liste für eine Policy ohne Fremd-Skript. Siehe Sicherheits-Header der Web-App.
  • In einen Chat hochgeladene, aber nie gesendete Dateien werden nach Coworkee:Chat:UnsentAttachmentLifetime gelöscht (Standard 24 Stunden, ein stündlicher Job).

Korrekturen

  • Die SDK-Seite beschrieb ein sdk/openapi.json mit Snapshot-Test und einen Client in sdk/typescript, die das Template nie hatte; sie zeigt beides jetzt als Rezept für Apps, die es brauchen.
  • npm run build von @coworkee/client und @coworkee/airworks leert zuerst dist, damit Pakete keine Dateien entfernter Quellen mehr mitnehmen.
  • Die eingebauten Themes Coworkee, Forest und Sunset erreichen WCAG AA beim Button-Text im hellen Modus (etwas dunklere Primär- oder Sekundärfarbe); der Kontrasttest prüft jedes eingebaute Theme.
  • Der Datei-Viewer gibt eine private Datei nie an den Online-Office-Viewer von Microsoft.
  • Dateien, die der Web-Host weiterleitet, behalten ihre eigene Sandbox-Policy: UseCoworkeeSecurityHeaders setzt die App-Policy nur bei Antworten ohne eigene (vorher trugen weitergeleitete Dateien beide).
  • Die Inhalts-Endpunkte von Dateien, öffentlichen Dateien, Chat-Dateien und Vorschaubildern beantworten HEAD, das der Bild-Viewer zuerst schickt (bisher 405).

0.0.1.32 (2026-10-17)

Korrekturen

  • Die Migrationen eines lokalen Laufs stürzten seit 0.0.1.31 beim Start ab: Sie laufen als Development, und dessen Prüfung der Dienste scheitert an den MVC-Diensten von OData in einem Host ohne Web-Umgebung. Die Vorlage baut den Container der Migrationen mit den Produktions-Standards; bestehende Apps ergänzen builder.ConfigureContainer(new DefaultServiceProviderFactory()); nach Host.CreateApplicationBuilder(args) in src/*.Migrations/Program.cs (coworkee update weist darauf hin).

0.0.1.31 (2026-10-17)

Breaking Changes

  • Migration nötig (Apps mit Chat): Direkt- und Gruppenchats bringen die Tabellen cw.ChatConversations, cw.ChatMembers, cw.ChatReactions, cw.ChatAttachments und neue Spalten von cw.ChatMessages. Frühere Direktnachrichten wandern beim nächsten Start in Unterhaltungen; Gelesenes bleibt gelesen.
  • Coworkee.Chat hängt von Coworkee.Storage ab (Anhänge) und referenziert Coworkee.Notifications (Benachrichtigungen bei Erwähnungen, wenn das Modul da ist).
  • Chat-Verträge: ChatMessageDto.ToUserId ist nullable (leer in Gruppen), das DTO hat neue Eigenschaften (ConversationId, Html, Source, EditedAt, Deleted, ReplyTo, Reactions, Attachments, HasEntityMentions); ChatReadDto hat ConversationId. IChatApi hat die Mitglieder für Unterhaltungen dazubekommen; eigene Implementierungen ergänzen sie. Neue Endpunkte unter /api/v1/chat/chats, /messages und /attachments: OpenAPI-Snapshots und SDKs neu erzeugen. Die Klartext-Endpunkte pro Person bleiben.
  • UserAvatar öffnet ein Menü mit Chat und Profil für andere Personen, wenn der Benutzer chatten darf (OfferChat, Standard true); setze OfferChat="false", wo der Avatar in etwas sitzt, das auf Klicks reagiert. Neue Bibliotheksseite person unter /people/{id}.

Neu

  • Acht weitere eingebaute Themes, jeweils mit heller und dunkler Palette, eigener Schrift, Radius und Optionen: Octo, Code, Arctic, Vampire, Solar, Atom, Paper und Aubergine (CLI/Spec theme, Template, Studio-Galerie). Ihr Text erfüllt den Kontrast nach WCAG AA. Siehe Theming.
  • Das Setup-Token ist bei einem lokalen Lauf leicht zu finden: Der AppHost gibt allen Diensten dasselbe Token und zeigt es, solange das System nicht eingerichtet ist, in seiner Konsole (coworkee run), im Log der API und im Aspire-Dashboard (Link Setup mit Token an der Web-Ressource, Eigenschaft Setup token an der API). Coworkee Studio zeigt es mit Kopier-Knopf in der Lauf-Karte und unter Meine Apps; coworkee deploy nennt <APP>_SETUP_TOKEN, wenn kein Administrator gesetzt ist. /setup?token=... füllt das Token ein. Siehe Setup-Assistent oder Seed.
  • @coworkee/airworks: Eingang (coworkee.inbox, Genehmigungen und Benachrichtigungen) ersetzt notificationsApp; Datensätze mit einem Datensatzfenster (coworkee.record: Felder, Verlauf mit Rückgängig und Wiederherstellen, Workflow); das Profil ist der erste Tab der Einstellungen (profileApp entfällt, renderProfile bleibt); die API-Dokumentation liegt in der Administration (Überwachung). Apps, die notificationsApp, profileApp oder apiDocsApp einzeln registriert haben, nehmen defaultApps oder die neuen Namen.
  • Seiten der Web-App in AirWorks-Fenstern: embedApp, openWebPage; CoworkeeLayout zeigt mit ?embed=1 nur die Seite. BffOptions.WebAppUrl (als appUrl von /bff/user) und BffOptions.FrameAncestors (diese Origins dürfen die Seiten einrahmen; das X-Frame-Options des Antiforgery entfällt dann); AddAirWorks setzt beides unter Aspire.
  • Badges auf den Kacheln von Eingang und Chat mit setBadge von AirWorks (AirWorks 0.5; ältere Versionen zeigen keine).
  • Chat: Gruppenchats (Name, Personen hinzufügen und entfernen, austreten), formatierte Nachrichten mit dem Rich-Text-Editor (Enter sendet), @-Erwähnungen von Personen (Link auf die Person, Benachrichtigung für Mitglieder des Chats) und #-Erwähnungen von Datensätzen aus der globalen Suche (nur für Leser sichtbar, deren Suche sie findet, sonst eingeschränkt), Antworten, Emoji-Reaktionen, eigene Nachrichten bearbeiten und löschen, Dateien und Bilder per Button, Drag-and-drop oder Einfügen mit Upload-Fortschritt (nur für Mitglieder, Einstellung Chat.MaxAttachmentMb, Erweiterung IChatAttachmentScanner), Lesebestätigungen pro Leser, Tipp-Anzeige und Suche im Chat. Der Server bereinigt jede Nachricht. Siehe Chat.
  • Mini-Chats: ein Dock unten rechts mit bis zu drei einklappbaren Chatfenstern (ungelesene Nachrichten im minimierten Zustand, Im Chat öffnen, pro Benutzer über ein Neuladen hinweg gemerkt, auf dem Handy bildschirmfüllend). Sie öffnen sich von der Chatseite, der Befehlspalette (Chatten mit…, @name), jedem Avatar, der Personenseite, der Snackbar einer neuen Nachricht und Erwähnungs-Benachrichtigungen. ChatDock öffnet sie aus eigenen Komponenten.
  • RichTextEditor nimmt Tools (Werkzeugleiste) und Placeholder.

Korrekturen

  • Eine neue App zeigte lokal ihre Setup-Seite, statt mit den Entwicklungsbenutzern anzumelden: Das Migrationsprojekt hat kein Launch-Profil und lief als Production, also fiel DemoSeed weg. AddMigrations gibt den Migrationen eines lokalen Laufs die Umgebung des AppHosts mit (DOTNET_ENVIRONMENT).
  • Die Setup-Seite eines bereits eingerichteten Systems führt zur App; ein offener Setup-Tab wechselt zur App, sobald er wieder den Fokus bekommt oder ein anderer Tab das Setup abschließt.
  • Eingebaute Themes standen doppelt in der Liste, wenn Anfragen sie gleichzeitig anlegten (SQL Server und SQLite lassen im eindeutigen Index leere Mandanten mehrfach zu). Das Anlegen läuft pro Prozess nur einmal gleichzeitig und entfernt solche Kopien (die Kopie, die eine Organisation nutzt, bleibt).

0.0.1.30 (2026-10-16)

Neu

  • Das Audit-Log macht Änderungen von Entity-Sets mit AddEntityHistory rückgängig: pro Eintrag Diese Änderung rückgängig machen, Stand vor dieser Änderung wiederherstellen, Datensatz wiederherstellen (Soft Delete) und Verlauf des Datensatzes öffnen, über die Vorschau und das Wiederherstellen des Verlaufs. EntityHistoryRestoreRequest hat einen Mode (After, Before, Undo), die Vorschau markiert Felder, die ein späterer Eintrag geändert hat (ChangedLater), und GET /api/v1/history listet die lesbaren Sets. IEntityHistoryApi hat GetSetsAsync (eigene Implementierungen ergänzen es).

0.0.1.29 (2026-10-16)

Neu

  • Workflow-Zustand als Filter auf dem Server: Jede OData-Menge, deren Entität einen Workflow hat, bekommt die virtuellen Eigenschaften WorkflowState und WorkflowAwaitsMe für $filter und $orderby (eine nach Mandant gefilterte Unterabfrage oder die MirrorTo-Spalte) und eine Facette WorkflowState mit den Zuständen der Definition und Wartet auf meine Genehmigung; beide brauchen Workflows.View. CoworkeeWorkflowColumn für CoworkeeDataTable filtert, sortiert und gruppiert danach; WorkflowState<T>.WithColor(...) färbt einen Zustand. Ersetze TemplateColumn + CoworkeeWorkflowActions durch <CoworkeeWorkflowColumn T="..." EntityType="..." />.
  • OData-Mengen können berechnete Eigenschaften anderer Module tragen (IODataComputedPropertySource, ODataComputedProperty): in $filter/$orderby gebunden, nie serialisiert, optional mit Facette. Die OData-Route konfiguriert jetzt die Bibliothek selbst statt Nextendeds AddODataAuto.

Korrekturen

  • SyncDevelopmentUsersAsync() läuft für die Migrationen eines lokalen Aspire-Laufs: Das Migrationsprojekt hat kein Startprofil und lief als Production, dort fand der Abgleich nie statt. AddMigrations setzt in lokalen Entwicklungsläufen Coworkee:Identity:SyncDevelopmentUsers; der Abgleich läuft in Development oder mit dieser Einstellung.
  • SyncDevelopmentUsersAsync() gibt Seed-Benutzern auch die seitdem im Seed ergänzten Rollen und Seed-Rollen die ergänzten Berechtigungen (nur in Development, entfernt wird nichts); so bekommt eine früher eingerichtete Datenbank z. B. eine neue Rolle eines Demo-Benutzers und neue Workflow-Berechtigungen einer Demo-Rolle.

0.0.1.28 (2026-10-16)

Breaking Changes

  • Migration nötig (Apps mit Reporting): neue Tabelle cw.DashboardReports.
  • IReportsApi hat GetDashboardAsync und SetDashboardAsync bekommen; eigene Implementierungen ergänzen sie. Neue Endpunkte unter /api/v1/reports/dashboard: OpenAPI-Snapshots und SDKs neu erzeugen.
  • AddCommandPaletteEntitySet und CommandPaletteEntitySet<T> sind entfernt: Die Befehlspalette findet Datensätze über die neue globale Suche des Servers. Registriere das Set neben seinem OData-Set, z. B. services.AddGlobalSearch<Product>(s => s.Fields(p => p.Name, p => p.Barcode).Page("/catalog/products")) (coworkee update zeigt auf die alten Aufrufe).
  • Neue Endpunkte /api/v1/search, /api/v1/search/stream und /api/v1/search/providers: OpenAPI-Snapshots und SDKs neu erzeugen.
  • ICoworkeeApi hat GetMailTemplateHistoryAsync bekommen; eigene Implementierungen ergänzen es.
  • MudBlazor.Extensions 9.11.4 und MudExRichTextEditor 9.5.5: Apps, die MudBlazor.Extensions selbst festlegen, gehen auf 9.11.4.

Neu

  • Ein Bericht als Dashboard: jeder Benutzer kann einen Bericht zu seinem Dashboard machen, Inhaber der neuen Berechtigung Reports.Dashboard zum Standard der Organisation; die Wahl des Benutzers gewinnt. ReportDashboard zeigt es live an und fällt auf das eigene Dashboard der App zurück. Die Dashboard-Seite des Templates nutzt es, führt Dashboard vor Berichte auf, und die Einrichtung mit Beispielen macht Catalog overview zum Dashboard der Organisation. Siehe Ein Bericht als Dashboard.
  • Globale Suche: eine Suche über Benutzer, Rollen, Gruppen, Dateien, Berichte, Chats und die eigenen Sets der App (AddGlobalSearch<T> für OData-Sets, IGlobalSearchProvider für alles andere, AddGlobalSearchIndex für einen Elasticsearch-Alias). Anfang vor Teil, "exakt", Typfilter wie users:max und Ids; Bewertungen über Provider hinweg vergleichbar, ein Timeout pro Provider, dieselben Berechtigungen, Organisation und Zeilenregeln wie die OData-Listen. Die Befehlspalette zeigt die Treffer Gruppe für Gruppe, sobald sie da sind, /search?q= listet alle mit einem Filter pro Typ, GET /api/v1/search/stream streamt JSON-Zeilen durch das BFF, und das MCP-Tool global_search gibt sie an KI-Clients. Siehe Globale Suche.
  • Die Dateiseite öffnet die Vorschau von ?file={id}.
  • Beispiele der Vorlage: Marken, Produkte und Dokumente sind in der globalen Suche.
  • coworkee module add und coworkee entity add tragen erzeugte Entitäten mit Texteigenschaften in die globale Suche ein (AddGlobalSearch<T> mit diesen Feldern und der Listenseite).
  • Einstellung Rich-Text-Editor: Editors.RichTextEditor (TipTap, der Standard, oder Quill) wählt den Editor für jeden Rich-Text-Editor der Bibliothek; RichTextEditor bringt dieselbe Wahl auf eigene Seiten. Mail-Platzhalter funktionieren in beiden als Erwähnungen. Siehe Mailing.
  • Mail-Vorlageneditor: Der Editor füllt das Fenster, ein Vorschau-Panel folgt jeder Änderung (Desktop- oder Telefonbreite, helles oder dunkles Mailprogramm, Zeile im Posteingang), Versionsverlauf mit Zeilenvergleich und Wiederherstellen (GET /api/v1/mail/templates/{name}/{culture}/history), Schutz vor dem Verlassen mit ungespeicherten Änderungen, Sprach-Chips neben dem Titel und eine durchsuchbare Platzhalterliste.
  • Workflow-Designer: Coworkee.Workflows.Elsa.Studio hostet Elsa Studio als eigene App, die sich am Auth-Server anmeldet (Berechtigung Workflows.Elsa.Manage) und mit der Elsa-API der API arbeitet; app.AddElsaStudio<T>() (oder AddProjects() für *_ElsaStudio) verdrahtet Client, API-Adresse und den Link zum Designer, coworkee deploy --without elsastudio lässt ihn weg. Template-Option elsaStudio (--elsa-studio). Siehe Elsa Studio.

Korrekturen

  • Dialoge und Seitenleisten der Bibliothek (Import-Assistent, Kampagnen-Assistent, Theme, Logo, Code-Editor, API-Schlüssel, Workflow-Kommentar) tragen die Startklasse von MudBlazor.Extensions und erscheinen damit erst an ihrer endgültigen Position, statt zuerst kurz in der Mitte aufzublitzen. Eigene Dialoge, die du mit ShowExAsync oder ShowSideSheetAsync öffnest, setzen Class="@MudExCss.Classes.Dialog.Initial" an ihrem MudDialog.
  • Der Eingang für Genehmigungen zeigt Titel, Details und Frist einer Genehmigung in eigenen Zeilen.
  • Apps mit Coworkee.Workflows.Elsa behalten die Eigenschaftsnamen ihrer Validierungsfehler (Brand.Name); die Elsa-API (FastEndpoints) hatte FluentValidation für jeden Validator auf camelCase umgestellt (brand.Name).
  • Die Endpunkte von Elsa (/api/elsa/...) landen nicht mehr im OpenAPI-Dokument der App: OpenAPI-Snapshots und SDKs von Apps mit Coworkee.Workflows.Elsa neu erzeugen.

0.0.1.27 (2026-10-16)

Korrekturen

  • Links auf Adressen des Servers (z. B. Anmelden am Ende der Einrichtung, /bff/login) laden wieder vom Server; seit die Seiten der Bibliothek eine Sammelroute haben, zeigte die App dafür Not found.. In Apps ist nichts zu ändern.

0.0.1.26 (2026-10-16)

Breaking Changes

  • ICoworkeeApi hat GetSystemStatusAsync dazubekommen; eigene Implementierungen ergänzen es.
  • Seiten der Bibliothek unter eigenem Präfix: die Admin-Seiten der Bibliothek liegen jetzt unter /cw/admin/... statt /admin/... (CoworkeeClientOptions.PagePrefix, "" behält die alten Adressen); alte Adressen leiten weiter. Die Seiten der Bibliothek haben keine @page-Routen mehr, eine Sammelseite (/{*Path}) zeigt sie: eine App mit eigener Sammelseite bekommt eine mehrdeutige Route. Apps ersetzen oder schalten Seiten per Schlüssel ab (options.Pages), siehe Komponenten ersetzen.
  • Deployments nehmen die Tools des App-Hosts mit: Ein veröffentlichter App-Host deployt jetzt auch Keycloak, Grafana, WebDataStudio, LibreTranslate und AspireUI. Apps aus einem älteren Template übernehmen drei Dinge aus einem neuen: in Migrations/Program.cs den Seed aus InitialAdmin.From(configuration) (mit DemoSeed.ConfigureDeployment) und ReconcileKeycloakRealmAsync(), in ServiceDefaults/Extensions.cs den Grafana-Exporter pro Signal mit GRAFANA_OTLP_PROTOCOL und in AppHost.cs app.Deploys(...) für Ollama und aspireUi.WithCoworkee(app) für AspireUI. KeycloakRealm liegt jetzt in Coworkee.Contracts.Identity.
  • Migration nötig: neue Tabellen cw.PushSubscriptions (Notifications), cw.DataTransferJobs und cw.ImportProfiles (OData, also jede App). Workflows und Webhooks bringen eigene Tabellen mit, wenn eine App sie aufnimmt.
  • ICoworkeeApi hat AddMailTemplateLanguageAsync, GetPushSettingsAsync, SavePushSubscriptionAsync und DeletePushSubscriptionAsync dazubekommen; eigene Implementierungen ergänzen sie.
  • Notification erbt jetzt von AggregateRoot (sie löst NotificationCreated aus, solange Push eingerichtet ist). Das Schema ändert sich dadurch nicht.
  • IODataClient.ExportAsync und ImportAsync entfernt: Nutze IDataTransferApi (Export der aktuellen Ansicht in drei Formaten, der Import-Assistent). Eigene IODataClient-Fakes streichen die beiden Methoden.
  • ODataImportRegistration hat Create, Update und Delete statt Send; AddODataImport nimmt für Ändern und Rückgängig (Guid? id, row) => command und optional einen Lösch-Command.
  • CoworkeeODataModule hängt von CoworkeeBackgroundJobsModule ab: Importe und große Exporte laufen als Jobs. Apps aus dem Template haben es bereits (Mailing); Test-Hosts setzen Coworkee:Jobs:ConnectionStringName und legen ihre Tabellen vor UseCoworkee() an.
  • Der Import-Button von CoworkeeDataTable öffnet den Import-Assistenten, statt sofort eine Excel-Datei hochzuladen; Excel-, CSV- und JSON-Exporte von OData-Sets kommen mit den sichtbaren Spalten vom Server.
  • Smartphone-Layout: Apps ergänzen viewport-fit=cover im Viewport-Tag von App.razor (coworkee update erledigt das).
  • Neue Endpunkte (Push, Sprachen von Mail-Vorlagen, Import und Export, Verlauf, Workflows, Webhooks): OpenAPI-Snapshots und SDKs neu erzeugen.

coworkee update kennt all das (Upgrade-Regeln von 0.0.1.26).

Neu

  • Template-Beispiele: die Befehlspalette bietet Neues Produkt (öffnet den Dialog über /catalog/products?new=true) und findet Produkte nach Name oder Barcode.
  • Webhooks: Secret erneuern: ein neues Geheimnis, einmal angezeigt; das alte signiert für Coworkee:Webhooks:SecretGracePeriod (24 h) mit, siehe Webhooks. Zwei neue Spalten in cw.WebhookSubscriptions (Apps mit Webhooks brauchen eine Migration).
  • Workflow-Chips in Tabellen laden die Zustände einer Seite in einer Anfrage (POST /api/v1/workflows/{entityType}/states, IWorkflowApi.GetStatesAsync) statt einer pro Zeile; eigene IWorkflowApi-Implementierungen ergänzen die Methode.
  • Schieberegler in Spaltenfiltern: zwischen auf Zahlen- und Datumsspalten regelt zwischen kleinstem und größtem Wert, den der Server meldet; eine Spalte mit Datums- oder Zahlenbereich-Facette (etwa das Bestelldatum der Demo) zeigt deren Histogramm und Regler statt einer Checkbox pro Bucket.
  • Systemstatus-Seite für die Systemorganisation (/cw/admin/system-status): Versionen, Datenbank und ausstehende Migrationen, Outbox, Hintergrundjobs mit den letzten Fehlern, Mail, Echtzeit, Redis, Dateien, Übersetzung und KI; Module und Apps steuern Karten über ISystemStatusContributor bei, siehe Admin-Oberfläche.
  • Workflows und Genehmigungen (Coworkee.Workflows, optional): Zustände und abgesicherte Übergänge pro Entitätstyp, Genehmigungen durch Rollen oder Benutzer mit Frist, Erinnerung und Eskalation, eine Historie, die Komponenten CoworkeeWorkflow, CoworkeeWorkflowActions und CoworkeeWorkflowTimeline und die Seite Genehmigungen, siehe Workflows und Genehmigungen.
  • Elsa-Workflows (Coworkee.Workflows.Elsa, optional): Elsa Workflows 3 in der API mit Coworkee-Aktivitäten und -Triggern, siehe Elsa-Workflows.
  • Webhooks (Coworkee.Webhooks, optional): Endpunkte pro Organisation bekommen signierte Ereignisse (im Stil von Standard Webhooks) zu Änderungen freigeschalteter Entity-Sets, Workflow-Übergängen und eigenen Ereignissen der App, beschränkt auf das, was der Besitzer des Endpunkts lesen darf; Wiederholungen mit Backoff, Abschalten nach wiederholten Fehlern, Zustellprotokoll, Testereignisse und erneute Zustellung unter /admin/webhooks, Schutz vor SSRF. Siehe Webhooks.
  • Import-Assistent pro Entity-Set: CSV, Excel und JSON, erkannte Kodierung, Trennzeichen und Kopfzeile, Spaltenzuordnung mit Vorschlägen und gespeicherten Zuordnungen, Umwandlung in der Sprache der Datei, Vorschau mit der Validierung der Commands, Anlegen, Ändern per Schlüssel oder beides, Probelauf, Hintergrundjob mit Live-Fortschritt, Datei der fehlerhaften Zeilen und Rückgängig für den letzten Import, siehe Import und Export. Admin-Seite Import und Export (/admin/data-transfer, Berechtigung DataTransfer.Use; gib sie den Rollen, die sie sehen sollen). Exporte der aktuellen Ansicht als Excel, CSV oder JSON (format, columns); mehr als Coworkee:OData:BackgroundExportRows Zeilen (10 000) laufen als Job mit Download-Hinweis.
  • Verlauf eines Datensatzes und Wiederherstellen (AddEntityHistory, History="true" an CoworkeeDataTable, CoworkeeEntityHistory): Zeitleiste mit Wort-Diff, Wiederherstellen eines Stands oder eines einzelnen Felds mit Vorschau, gelöschte Datensätze von ISoftDelete-Sets kommen zurück, siehe Audit.
  • Befehlspalette (Strg+K): Seiten, Datensätze, Aktionen und der Assistent als Rückfall, erweiterbar mit AddCommandPaletteProvider, siehe Befehlspalette.
  • Smartphone-Layout der Blazor-Shell und der Anmeldeseiten: Überlaufmenü in der App-Leiste, Navigation als Overlay, Bearbeitungsblätter im Vollbild, Facetten-Menüs als Sheets von unten, fixierte Zeilenaktionen, 44-px-Touchziele und Safe Areas, siehe Smartphones, installierbare App und Push.
  • Installierbare App: <Coworkee.Bff.Pwa.CoworkeePwa /> im Head von App.razor verlinkt das Web-Manifest in den Farben des Themes; Coworkee.Bff liefert Manifest, Service Worker (er speichert nur die Offline-Seite), Icon und Offline-Seite; Update- und Installationshinweise in der App.
  • Web-Push für die Benachrichtigungen: options.UsePush() in Aspire oder Coworkee:Notifications:Push:PublicKey/PrivateKey; Benutzer schalten es pro Gerät auf der Benachrichtigungsseite ein.
  • Sprachen von Mail-Vorlagen: Sprache hinzufügen unter Administration > Mail-Vorlagen legt eine Variante für eine später eingeschaltete Sprache an, kopiert oder maschinell übersetzt (nur Texte, Tags und Platzhalter bleiben), und entfernt sie wieder, siehe Mailing. Eine Mail in einer Sprache ohne Variante nutzt vor Englisch die Standardsprache (Organisation, System, Sprachen-Seite; IDefaultLanguage); die maschinelle Übersetzung steht anderen Modulen als IMachineTranslator zur Verfügung.
  • Deployment: Der App-Host veröffentlicht für Docker Compose, Azure Container Apps oder Kubernetes (öffentliche Adressen, Token-Zertifikate, Setup-Token, der erste Administrator und der Mailserver als Parameter, stabile Volumes, feste Ports); die Tools des App-Hosts (Keycloak mit seinem Realm, Grafana mit den Dashboards der App, WebDataStudio, LibreTranslate, Ollama, AspireUI) kommen mit Volumes und öffentlichen Adressen mit, jedes lässt sich weglassen (DeployTool, coworkee deploy --without), Mailpit nur mit --with mailpit; coworkee deploy compose|azure|k8s fragt nach Administrator und Mailserver und listet am Ende jede Adresse und die Anmeldung; Coworkee Studio bietet nach dem Erstellen einer App Deployen an; Token-Zertifikate können als Base64 aus einem Secret kommen. Nur Dienste, die Jobs verarbeiten, legen das Hangfire-Schema an. Siehe Deployment.
  • CLI: coworkee add/remove schalten Template-Optionen in einer bestehenden App ein oder aus (in deine Dateien zusammengeführt, Konflikte als .rej, Optionen in .coworkee/app.json), coworkee update wendet die Upgrade-Regeln der Versionen dazwischen an, coworkee db reset|seed|backup|restore, coworkee entity import --from db|openapi|csv, sechs Startvorlagen (coworkee specs list|show, coworkee new --template-spec), Optionen --workflows, --elsa, --webhooks und --pwa (standardmäßig an). MCP-Werkzeuge add_option und import_entities. Siehe Kommandozeile und Startvorlagen.
  • Studio: eine bestehende App öffnen und ändern, Meine Apps (starten, stoppen, Links), Mit einer Vorlage starten, Entity-Diagramm (ziehen, Referenzen zeichnen, automatisch anordnen, PNG-Export) und Entity-Import mit Vorschau.
  • Template: Die Beispielprodukte werden weich gelöscht und zeigen mit Audit ihren Verlauf und kommen zurück; das Dashboard funktioniert mit SQLite (dort kam 500); Demo-Benutzer werden nur in Development angelegt, Migrationen warten auf die Datenbank.
  • coworkee doctor vergleicht in einer App Versionen: dotnet-ef mit EF Core, Aspire-CLI mit dem Aspire-SDK des App-Hosts, coworkee mit CoworkeeVersion, und findet belegte Ports der Startprofile, jeweils mit Lösung.
  • Entwicklungsbenutzer bleiben aktuell: SyncDevelopmentUsersAsync() (nur in Development) legt Seed-Benutzer und -Rollen an, die einer früher eingerichteten Datenbank fehlen, und setzt das Passwort der Seed-Benutzer wieder auf das geseedete; der Migrations-Host des Templates ruft es nach SeedCoworkeeIdentityAsync() auf, so kommen Änderungen an DemoUsers.cs in einer bestehenden Datenbank an.

0.0.1.25 (2026-10-15)

Nur Korrekturen: Das Skript des Ladebildschirms ist eine eigene Datei, damit eine strikte CSP es ausführt, und App-Seiten gehen mit Cache-Control: no-cache, no-store raus. In Apps ist nichts zu ändern.

0.0.1.24 (2026-10-15)

Breaking Changes

  • ClientEntityIndicator entfernt: Jede Datentabelle einer gespiegelten Menge zeigt den ClientEntitySwitch in ihrer Werkzeugleiste (hinzugefügt von AddCoworkeeClientEntities). Entferne die Zeilen <ClientEntityIndicator> und einen eigenen Vergleichen-Knopf aus deinen Seiten.
  • ODataPicker.Margin (und OptionalODataPicker.Margin) ist jetzt Margin?: Ohne Wert folgt der Picker der Dichte des Themes. Code, der den Wert als Margin liest, ergänzt ?? Margin.Dense.
  • CliOption (Coworkee.Cli.Spec) hat den Konstruktorparameter DependsOn dazubekommen, OptionDependency die Eigenschaft Or: quellkompatibel, aber binär inkompatibel für Werkzeuge, die gegen ein älteres Coworkee.Cli.Spec kompiliert sind.
  • LoadingScreenDto hat den positionalen Parameter SignedIn dazubekommen; OpenAPI-Snapshots und SDKs neu erzeugen.
  • Enums in jedem MudExObjectEdit werden mit dem EnumEditor dargestellt (lokalisierte Beschriftungen aus EnumLabels, [Flags] als Mehrfachauswahl der Einzelbit-Mitglieder), nullbare Enums mit dem leerbaren NullableEnumEditor. Ein RenderWith der Meta gewinnt weiterhin.
  • --sign-in-media mit dem Layout Card wird ignoriert (nicht übergeben, nicht kopiert), außer --loading-screen SignInMedia zeigt das Medium hinter dem Ladebildschirm.

Neu

  • Ladebildschirm in den Farben des Themes mit dem echten Download-Fortschritt, einer freundlichen Fehlermeldung, wenn der Start scheitert, sowie Farbmodus, Theme und Sprache des Benutzers, siehe Theming. Jede App ergänzt eine Zeile als erste Zeile von <body> in MyApp.Web/App.razor: <Coworkee.Bff.CoworkeeLoadingScreen Logo="coworkee-icon.svg" />. Der Hintergrund (Animated, SignInMedia, Plain) wird auf der Karte Anmeldeseite gewählt, in Aspire mit options.SignIn.LoadingScreen, in der CLI mit --loading-screen. Die Seite geht jetzt mit Cache-Control: no-cache, no-store hinaus; das Logo kommt von /bff/loading-screen/logo, das Skript von /bff/loading-screen/script.js (kein Inline-Skript, Apps mit strenger Content-Security-Policy laufen weiter).
  • Öffentliche Dateien: Manager machen eine Datei öffentlich (PUT /api/v1/files/{id}/public, Leiste des Dateimanagers, Vorschau); jeder lädt sie über GET /api/v1/files/public/{id}/{name}, etwa als Medium der Anmeldeseite oder des Ladebildschirms, gewählt mit Aus Dateien wählen. Das Modell hat StoredFile.IsPublic dazubekommen: Apps mit dem Files-Modul fügen eine Migration hinzu.
  • Seitenmodus von Datentabellen: CoworkeeDataTable.PageMode (Pager, EndlessScroll) und die Einstellung DataTable.DefaultPageMode (global und pro Benutzer), siehe Datentabelle.
  • Client-Entity-Schalter in der Werkzeugleiste gespiegelter Tabellen: Suche im Browser oder auf dem Server, Synchronisierungsstand als Ring, Vergleichen und das Schnellere wählen nimmt die schnellere Seite; die Wahl bleibt pro Benutzer erhalten.
  • ImageDataUrlEdit lädt mit MudExUploadEdit hoch; Enums erhalten überall den EnumEditor (siehe Breaking Changes).
  • Die Texte der MudBlazor.Extensions-Komponenten, die die Bibliothek zeigt (Upload-Feld, Stil- und Cron-Editor, Dateimanager, Dateivorschau), werden in allen Sprachen mitgeliefert; ihre gespeicherten maschinellen Übersetzungen werden beim nächsten Start ersetzt.
  • CLI und Studio: Optionen, die eine andere brauchen (--airworks-packages ohne --airworks, --ai-provider, --ai-model und --ai-endpoint ohne --ai, --backup außerhalb von Postgres, --sign-in-media mit dem Layout Card, außer der Ladebildschirm zeigt es, --sign-in-media-side ohne Medium), gehen nicht mehr an das Template; die CLI warnt (coworkee spec validate ebenso) und das Studio blendet ihre Karten aus. Anmeldemedien eines Card-Layouts werden deshalb nicht mehr in die App kopiert. Neu: coworkee spec show und eine Spezifikation aus dem Studio, die jedes Bibliotheksmodul mit true/false nennt; coworkee options gibt Needs: und die Webseiten der mitgebrachten Produkte aus, die Studio-Karten zeigen Symbole und Öffnen-Knöpfe, siehe CLI.

0.0.1.23 und älter

Breaking Changes

  • IMailSender: Die Schnittstelle verlangt jetzt QueueAsync(to, template, model, culture, campaignId, cancellationToken) mit sechs Parametern. Die Überladung mit fünf Parametern ist eine Default-Methode, die null als Kampagne übergibt. Eigene Implementierungen (Testdoubles, Wrapper) implementieren die Methode mit sechs Parametern; Aufrufer bleiben unverändert.
  • IMailTemplateRenderer: hat RenderContentAsync(content, layout, model, cancellationToken) dazubekommen, das mit einem bereits aufgelösten Layout rendert (der Kampagnen-Job löst damit Vorlage und Layout einmal pro Lauf auf). Eigene Implementierungen ergänzen die Methode; Aufrufer bleiben unverändert.
  • Positionale Records haben Parameter dazubekommen: MailTemplateSummaryDto (IsCustom, Description), MailTemplateDto (IsCustom) und OutgoingMailDto (CampaignId). Code, der sie positional erzeugt oder dekonstruiert, und generierte Clients brauchen die neuen Parameter. OpenAPI-Snapshots und SDKs neu erzeugen.
  • Entfernt mit der Übernahme von MudEx 9.11.3: ChipListEditor, CronEditor, CronSchedule und SettingsFormRules.WrapInColumns. String-Listen werden als MudExTagField<string> dargestellt, [Cron] als MudExCronEdit; verwende diese direkt.

Neu

  • AirWorks-Client: Chat, Berichte mit Designer, Assistent, die Administrations-Apps (adminApps) und Realtime, siehe AirWorks-Client. Erzeugte Apps nehmen @coworkee/client und @coworkee/airworks aus den Tarballs im Template; jedes Release veröffentlicht sie zusätzlich auf npm (--airworks-packages npm). coworkee new --template-source trägt den lokalen Paketordner in die nuget.config der App ein. Apps aus der ersten AirWorks-Version: ...adminApps in ClientApp/src/main.ts ergänzen und für die Dateivorschau das MudEx-Hosting des Templates übernehmen (mudex-webcomponents, vite.config.ts, .dat-Content-Type in Program.cs).
  • Mail-Kampagnen mit Empfänger-Voreinstellungen, gespeicherten Abfragen und manueller Auswahl, geplantem Versand und einer Bestätigungsseite für die Abmeldung, siehe Mailing.
  • Eigene Mail-Vorlagen mit visuellem und Quelltext-Editor sowie eine CodeEditor-Komponente auf Basis von Monaco.
  • Änderungen am Entitätsmodell für Kampagnen, Empfängerlisten und eigene Vorlagen: Füge deiner App eine Migration hinzu.