Compare commits

...

115 Commits

Author SHA1 Message Date
Developer 02
46f1633f79 feat(geoloc): Anfangsort hinzugefügt 2024-10-22 23:26:07 +02:00
Developer 02
1af07f0df8 feat: location-picker.css initialisiert.
- Minified CSS hinzugefügt
2024-10-22 21:40:38 +02:00
Developer 02
55c9dfb9c2 refactor(ShowEnvelope.cshtml): Interne Skripte, die sich auf die Ereignisse von readonly-send und geoloc Elementen beziehen, wurden in event-binder.js verschoben. 2024-10-22 20:38:44 +02:00
Developer 02
1a230306a3 refactor(ShowEnvelope.cshtml): collapseNav-Skript entfernt, das nach der Aktualisierung der Navigationsleiste nicht mehr benötigt wird 2024-10-22 20:32:14 +02:00
Developer 02
28022bc669 feat(_Layout.cshtml): LeafletLocationPicker in Pop-up hinzugefügt. 2024-10-22 20:28:54 +02:00
Developer 02
ad5843b7c9 feat(_Layout): leaflet-locationpicker-Abhängigkeiten hinzugefügt. 2024-10-22 16:52:46 +02:00
Developer 02
173b691e56 feat: leaflet und leaflet-locationpicker hinzufügen 2024-10-21 18:10:45 +02:00
Developer 02
8a0fe70a88 feat: Popup-Fenster für den Speicherort initialisiert.
- Schaltfläche im pspdf-kit-Symbolleistenmenü hinzugefügt.
2024-10-21 14:47:58 +02:00
Developer 02
c4114a3800 feat(HomeController): UserCulture zu EnvelopeSigned hinzugefügt 2024-10-18 13:27:42 +02:00
Developer 02
977486bb7d chore: Aktualisiert auf Version 2.4.0.0. 2024-10-18 10:40:24 +02:00
Developer 02
6ccc0d2e0a refactor(HomeController): Aktualisiert, um ein Dokument aus der Datenbank über EnvelopeDocumentDto zu lesen, anstatt das Dokument aus dem Dateipfad mit envelopeOldService zu lesen 2024-10-18 10:34:51 +02:00
Developer 02
084a9b7db4 refactor(EnvelopeDocumentDto): ByteDta-Eigenschaft hinzugefügt 2024-10-18 10:21:01 +02:00
Developer 02
826844cf46 refactor(EnvelopeDocument): ByteDta-Eigenschaft hinzugefügt 2024-10-18 10:17:28 +02:00
Developer 02
39cff26f2d feat(site.css): Die Schriftgröße der Fußzeile und die Anordnung der Elemente wurden angepasst, um eine bessere Reaktionsfähigkeit zu gewährleisten. 2024-10-18 10:05:03 +02:00
Developer 02
1619801526 refactor(appsettings) verschiebt DispatcherConfig in die Nähe von Mail config 2024-10-18 09:55:24 +02:00
Developer 02
5a1263ee3a refactor(CookieConsentSettings ): Entfernen Sie es und es ist DI Injection 2024-10-18 09:50:53 +02:00
Developer 02
bc91baa4fa refactor(appsettings): verschiebe appsettings about developmentement nach appsettings.Dev 2024-10-18 09:46:34 +02:00
Developer 02
a4882a7bfa refactor(appsettings): Der Pfad zu den Protokolldateien wurde auf einen zentralen Speicherort aktualisiert. 2024-10-18 09:43:39 +02:00
Developer 02
c254b5b8df refactor(Envelope): Entfernte DmzMoved-Eigenschaft 2024-10-18 09:32:49 +02:00
Developer 02
66718a3fd8 chore: das Projekt auf 2.3 aktualisiert 2024-10-16 15:06:01 +02:00
Developer 02
99fc2aecd9 refactor(app.js) : Entfernen von /ReadOnly beim Kopieren der Url 2024-10-16 14:56:54 +02:00
Developer 02
a41d03aed5 feat(HomeController): zentralisierte Standard-Kultur-Cookie-Zuweisung. 2024-10-16 14:42:58 +02:00
Developer 02
6d14b79c43 refactor(flag-dropdown): in die footer verschoben 2024-10-16 13:48:06 +02:00
Developer 02
faeac8f290 refactor(EnvelopeRejected): Unnötige _CookieConsentPartial entfernen 2024-10-16 11:58:23 +02:00
Developer 02
d172faacf3 refactor(ShowEnvelope): Unnötige _CookieConsentPartial entfernen 2024-10-16 11:56:54 +02:00
Developer 02
35d6beb3cb feat(ShowEnvelope): Wenn ReadOnly, machen Sie die Kopfzeile ViewDoc anstelle von SignDoc.
- ViewDoc-Schlüssel zu resx in beiden Sprachen hinzugefügt
2024-10-16 11:55:48 +02:00
Developer 02
7ff787ec28 refactor(_layout): City-Regex-Prüfung ignorieren, wenn IS_MOBILE_DEVICE
- IS_MOBILE_DEVICE als globalen konstanten Wert hinzugefügt
 - DEVICE_TYPE geändert in DEVICE_SCREEN_TYPE
 - IS_DESKTOP zu IS_DESKTOP_SIZE geändert
2024-10-16 11:32:25 +02:00
Developer 02
f6fc850a20 fix: Pull-Konflikte in Constant.vb gelöst. 2024-10-16 10:00:20 +02:00
Developer 02
04b8d0ef5d fix: Pull-Konflikte in Constant.vb gelöst. 2024-10-16 09:43:41 +02:00
Developer 02
891f6368f1 refactor(card.css): CSS der footer für Reaktionsfähigkeit bearbeitet 2024-10-16 02:11:07 +02:00
Developer 02
e6011b6201 refactor(card.css): CSS des Kopfbereichs für Reaktionsfähigkeit bearbeitet 2024-10-16 01:54:02 +02:00
Developer 02
8b86114998 refactor (card.css): unnötiges CSS entfernt 2024-10-16 00:31:32 +02:00
Developer 02
c20b115faf feat(logo): minimiertes Logo hinzugefügt 2024-10-16 00:11:14 +02:00
Developer 02
2c8ccd3e7c feat: signFlow-Logo hinzufügen 2024-10-14 19:18:40 +02:00
Developer 02
b1f771c320 feat: Kopierschaltfläche von der linken Navigationsleiste in die pspdf-kit Symbolleiste verschoben. 2024-10-14 18:54:29 +02:00
Developer 02
425645a610 feat: neue Klasse für Desktop-Schaltflächen mit dem Namen 'btn-desktop' erstellt
- CSS hinzugefügt, um die Klasse btn-desktop auszublenden, wenn die Breite weniger als 1024px beträgt.
2024-10-14 17:41:51 +02:00
Developer 02
24e6ffc5ef feat: CSS mit Bootstrap für mobil beschreibbare Toolbar-Elemente anordnen 2024-10-14 17:24:49 +02:00
Developer 02
1dd9ce6bbc feat: Aktivieren Sie die Schaltfläche für die mobile Ablehnung.
- Ereignis hinzugefügt.
 - In die Mitte der Schaltflächenleiste verschoben
2024-10-14 17:06:01 +02:00
Developer 02
e528fa6409 feat: getMobileItems umbenennen in getMobileWritableItems 2024-10-14 16:05:27 +02:00
Developer 02
6440dd09d1 feat: Erstellung der konstanten Variablen DEVICE_TYPE und IS_DESKTOP.
- Aufteilung der benutzerdefinierten Symbolleistenelemente in writableItems und mobileItems
 - beide werden abhängig von der jeweiligen Bedingung hinzugefügt
2024-10-14 15:44:21 +02:00
Developer 02
869493bd97 feat: Globale Konstante isReadOnly-Wert erstellt.
- Constraint für ReadOnly-Schaltfläche mit isReadOnly-Wert hinzugefügt
2024-10-14 14:02:02 +02:00
Developer 02
1cb9042736 feat(ui.js): Freigabe-Schaltfläche als Symbolleisten-Schaltfläche hinzugefügt.
- Share-Button auf der linken Seite der Kopfzeile entfernt
2024-10-14 11:27:41 +02:00
Developer 02
c9410a1e2e feat(ReadOnlyController): Try-Catch zur 'CreateAsync'-Methode hinzugefügt.
- Unnötige Testmethoden wurden aus dem Controller entfernt.
2024-10-14 09:38:16 +02:00
Developer01
683ff03a0f Merge branch 'master' of http://git.dd:3000/AppStd/EnvelopeGenerator 2024-10-13 17:38:48 +02:00
Developer01
c9ba7eeaf9 MS 2024-10-13 17:38:31 +02:00
Developer 02
c4f0ce7d4b feat: bsNotify-Methode und Benachrichtigung für Clipboard-Kopie hinzufügen
- Implementierung der bsNotify-Methode für benutzerdefinierte Benachrichtigungen mit alertify.
- Hinzufügen von benutzerdefinierten Styles für den benutzerdefinierten Benachrichtigungstyp von alertify.
- Hinzufügen der Clipboard-Kopie-Funktionalität mit Erfolgs- und Fehlerbenachrichtigungen.
2024-10-11 14:15:06 +02:00
Developer 02
dc83486032 feat(ShowEnvelope): Nachricht auf Popup-Seite zum Teilen hinzugefügt 2024-10-11 09:51:02 +02:00
Developer 02
2a64091c87 feat: Funktionalität des Datumsauswählers zum Eingabefeld hinzufügen
- Einen Datumsauswähler implementiert, der beim Fokussieren des Eingabefeldes und beim Klicken auf das Kalendersymbol geöffnet wird.
- Sicherstellt, dass das Eingabefeld keine manuelle Dateneingabe zulässt, während der Kalender weiterhin angezeigt werden kann.
- Das `onkeydown`-Ereignis verwendet, um manuelle Eingaben zu verhindern und die Datenauswahl ausschließlich über den Datumsauswähler zu ermöglichen.
2024-10-11 00:59:00 +02:00
Developer 02
f65f749208 feat(ShowEnvelope): Der Rücksetzwert des Verfallsdatums der Briefumschlag-Aktie wird auf das Datum eine Woche später statt auf den leeren String gesetzt. 2024-10-11 00:45:37 +02:00
Developer 02
0b6ed00062 feat(EnvelopeReceiverReadOnlyCreateDto): EmailAddress und Required Attribute zu ReceiverMail hinzugefügt. 2024-10-11 00:36:18 +02:00
Developer 02
e87c976e19 fix(ShowEnvelope): Logik der E-Mail-Formatprüfung korrigiert 2024-10-10 22:00:52 +02:00
Developer 02
f31ece3a59 feat(ShowEnvelope): Logik zur Überprüfung des E-Mail-Formats hinzugefügt. Zeigt eine Fehlermeldung, wenn das Format falsch ist. 2024-10-10 21:56:32 +02:00
Developer 02
cfd08602ab feat(ShowEnvelope): Logik zur Überprüfung des Datums hinzugefügt. Wenn es weniger als einen Tag beträgt, wird eine Fehlermeldung angezeigt. 2024-10-10 21:46:06 +02:00
Developer 02
4b7152b272 feat(ShowEnvelope): Die Attribute min, max und value wurden zu readonly-date-valid hinzugefügt. 2024-10-10 20:47:55 +02:00
Developer 02
5117a66c81 feat(ShowEnvelope): Standarddatum nach 3 Tagen setzen 2024-10-10 19:30:19 +02:00
Developer 02
83794d4bbc feat: add email validation and toggle 'is-invalid' class for inputs with 'email-input' class 2024-10-10 19:21:15 +02:00
Developer 02
76f74778b4 feat(ShowEnvelope): Swal-Nachrichten zur Umschlagfreigabe-Anforderung hinzugefügt 2024-10-10 18:53:35 +02:00
Developer 02
ded3425e31 feat: Funktion hinzugefügt, um aktuelle URL beim Klicken auf den Button in die Zwischenablage zu kopieren 2024-10-09 16:53:44 +02:00
Developer 02
738b379fe5 feat(UI.js): PDF-Export-Symbolleiste zur pspdf-Instanz hinzugefügt 2024-10-09 16:50:01 +02:00
Developer 02
cf8b28441f feat(ShowEnvelope): Bedingung hinzugefügt, die auf Nur-Lesen basiert, um den Share-Button zu sehen.
- Wenn es schreibgeschützt ist, erscheint die Schaltfläche Kopie zur Zwischenablage hinzufügen.
2024-10-09 16:49:07 +02:00
Developer 02
0eb5897185 feat(share pop-up): Post-Request-Prozess hinzugefügt. 2024-10-09 15:03:54 +02:00
Developer 02
25cd1601a6 feat(share pop-up): Datumswähler hinzugefügt. 2024-10-09 13:54:16 +02:00
Developer 02
42e4d110ad refactor(wwwroot\lib): MDB und bootstrap-cookie-consent-settings-main entfernt 2024-10-09 12:43:18 +02:00
Developer 02
2538f34892 feat(ShowEnvelope). erstellt Basis-Popup-Menü, um das Dokument zu senden 2024-10-09 12:37:36 +02:00
Developer 02
3ce11f4cc7 feat(api-service): Methode für Share-Request erstellt. 2024-10-09 11:00:21 +02:00
Developer 02
f2cd34a79e feat(share-button): Basisstruktur der Klick-Aktion hinzugefügt 2024-10-09 10:50:26 +02:00
Developer 02
5f923ad485 feat(ShowEnvelope): Share-Button hinzugefügt
- CSS einstellen.
2024-10-09 10:46:18 +02:00
Developer 02
8f70f085d3 feat(HomeController): Historischer Prozess zum Speichern von Umschlägen hinzugefügt, die unter dem Endpunkt EnvelopeReceiverReadOnly angezeigt werden. 2024-10-09 09:58:20 +02:00
Developer 02
d6c09ed31a feat(ReadOnlyController): Speichern der EnvelopeShared-History zur CreateAsync-Methode hinzugefügt. 2024-10-09 09:37:02 +02:00
Developer 02
7d3ee1331d feat(EnvelopeStatus): Status „EnvelopeViewed“ hinzugefügt 2024-10-09 02:11:51 +02:00
Developer 02
cd5b90a1e2 feat(ReadOnlyControler): History-Dienst hinzugefügt, um envelope-shared-process zu speichern 2024-10-09 02:10:08 +02:00
Developer 02
ac861f5fa0 fix(DIExtensions): arrangierte Mail- und Dispatcher-Konfigurationen 2024-10-09 02:05:21 +02:00
Developer 02
c1d8f817bb feat(ReadOnlyController): envelope-id von create DTO automatisch aus envelope-id claim hinzugefügt. 2024-10-09 01:16:06 +02:00
Developer 02
da28a7332b feat(EnvelopeClaimTypes): „envelope ID“ als Auth-Claim hinzugefügt. 2024-10-09 00:51:59 +02:00
Developer 02
bfd4e6a8ed feat(MailConfig): Schnittstelle zur Mail-Konfiguration über appsettings erstellt. 2024-10-09 00:22:23 +02:00
Developer 02
b4e0e4b6b2 feat(EnvelopeMailService): Arrangiert den Mailservice für read-only Umschläge zum Versenden als E-Mail 2024-10-08 15:56:34 +02:00
Developer 02
e37caf5c8f refactor(ShowEnvelopeView): Kurzzeitanteil der Verfallszeit für schreibgeschützten Umschlag entfernen 2024-10-07 10:08:10 +02:00
Developer 02
e95cf24af7 feat(EnvelopeExpired-View): Erstellt eine Ansicht, die anzeigt, wenn der Umschlag abgelaufen ist 2024-10-05 04:19:52 +02:00
Developer 02
524a72caa0 feat(ShowEnvelope-View): Flex-Action-Panel ausblenden, wenn schreibgeschützt 2024-10-05 03:22:37 +02:00
Developer 02
1919c562cc feat(ShowEnvelope-View): ReadOnlyMessage wurde für verschiedene Kulturen erstellt. Zu View hinzugefügt.
- View-Benutzernachrichten sind so organisiert, dass sie davon abhängen, ob der Umschlag schreibgeschützt ist oder nicht.
2024-10-05 03:20:39 +02:00
Developer 02
62b54d6e75 feat(HomeController): Sanizer hinzugefügt 2024-10-05 02:16:14 +02:00
Developer 02
efa9160c04 feat(EnvelopeReceiverReadOnly): Modelldaten und Dokument im Endpunkt sind so eingestellt, dass sie als Bytes geladen werden. 2024-10-05 02:04:57 +02:00
Developer 02
bc6955055a feat(EnvelopeReceiverReadOnly): Created endpoint for ShowEnvelope view 2024-10-04 11:28:52 +02:00
Developer 02
dc997d5ff2 refactor(.Extensions): Extensions nach Gruppen aufgeteilt.
- erstellt dekodierende Erweiterungen.
 - Kodierungserweiterungen erstellt.
 - XSS-Erweiterungen in das Extensions-Paket verschoben.
 - EncodeTypes vom Paket Common in das Paket Constants verschoben.
2024-10-02 13:33:11 +02:00
Developer 02
a32f495038 refactor(HomeController): EnvelopeReceiverReadOnly-Endpunkt initialisiert.
- injiziert EnvelopeReceiverReadOnlyService
2024-10-01 18:14:59 +02:00
Developer 02
210466883c refactor(SendAccessCode): Prüfung hinzugefügt, ob die envelope-id schreibgeschützt ist. Wenn ja, Umleitung zu /ReadOnly
- Umbenannt in MainAsync
2024-10-01 17:51:31 +02:00
Developer 02
728385b70a feat(extensions): created extensions project.
- Moved encoding extensions from application to extensions project to increase the abstraction
2024-10-01 17:25:50 +02:00
Developer 02
792aa0b922 feat(decode): Methoden zur Konvertierung des dekodierten Arrays in eine Umschlag-Empfänger-ID und eine Nur-Lese-ID mit Ausnahmewirkung hinzugefügt. 2024-10-01 16:44:46 +02:00
Developer 02
6847b74095 feat(EncodeType): Enum zur Klassifizierung von Kodierungstypen erstellt.
- Erweiterungsmethoden hinzugefügt, um zu versuchen, String mit out-keyworld zu dekodieren.

- Erweiterungsmethode hinzugefügt, um den Typ des Kodierungstyps des dekodierten String-Arrays zu finden.
2024-10-01 16:04:44 +02:00
Developer 02
54e86b421c feat(EnvelopeReceiverReadOnly): Erweiterungsmethode zum Kodieren und Dekodieren von Envelope-Receiver ReadOnly Id hinzugefügt 2024-10-01 15:26:06 +02:00
Developer 02
370666cb0e refactor: EnvelopeReceiverReadOnlyRepository so angepasst, dass die Einbindung von Envelope bis zur Klärung des ID-Typs verschoben wird
- Konstruktor von EnvelopeReceiverReadOnlyRepository angepasst, um IEnvelopeRepository zu akzeptieren.
 - Die Zeile Include(erro => erro.Envelope) in der Methode ReadOnly auskommentiert, bis der EnvelopeId-Typ standardisiert ist.
 - Methoden IncludeEnvelope hinzugefügt, um Envelope manuell für jedes EnvelopeReceiverReadOnly einzubinden.
 - Markierte manuelle Einbindungs-Methoden als veraltet, um durch IQueryable.Include ersetzt zu werden, sobald der EnvelopeId-Typ geklärt ist.
2024-10-01 12:54:27 +02:00
Developer 02
e17f7df930 feat(EnvelopeReceiverReadOnly): Controller initialisieren
- Join mit Receiver und Read
 - DI-Konfiguration hinzufügen
 - Auslöser hinzufügen (TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD)
2024-10-01 11:17:30 +02:00
Developer 02
0e91df7acc feat (Constants.EmailTemplateType): Einen neuen Typ namens „DocumentShared“ erstellt 2024-09-30 15:14:01 +02:00
Developer 02
ad26230da5 feat (EnvelopeReceiverReadOnly): Erstellt „EnvelopeReceiverReadOnlyService“ als Implementierung von „CRUDService“ und „IEnvelopeReceiverReadOnlyService-interface“ 2024-09-30 15:02:52 +02:00
Developer 02
a103f34230 feat (EnvelopeReceiverReadOnly): Erstellt „mapping-profiles“ für Auto-Mapper.
- EnvelopeReceiverReadOnly zu EnvelopeReceiverReadOnlyDto
 - EnvelopeReceiverReadOnlyCreateDto zu EnvelopeReceiverReadOnly
 - EnvelopeReceiverReadOnlyUpdateDto zu EnvelopeReceiverReadOnly
2024-09-30 14:43:30 +02:00
Developer 02
806bd3b248 feat (EnvelopeReceiverReadOnly): Erstellt „data-transfer-objects“.
- Erstellt „Read-DTO“
 - Erstellt „Create-DTO
 - Erstellt „Update-DTO“ um nur „DateValid“ zu aktualisieren.
2024-09-30 14:39:22 +02:00
Developer 02
c69c39fa44 refactor(EnvelopeReceiverReadOnly): ChangedWho Eigenschaft „nullable“ gemacht 2024-09-30 14:00:40 +02:00
Developer 02
b6badb44af feat(EnvelopeReceiverReadOnly): Erstellte Repository-Klasse mit der Interface-Implementierung 2024-09-30 13:54:39 +02:00
Developer 02
b2195ce13f feat(EnvelopeReceiverReadOnly): Entität erstellt 2024-09-30 13:43:57 +02:00
Developer 02
f3cb9b8510 feat(EnvelopeStatus): Neuer Status für die gemeinsame Nutzung von Umschlägen mit Code 2008 hinzugefügt. 2024-09-30 11:49:08 +02:00
Developer 02
8edfecb9dc refactor (privacy-policy): Fehlende Informationen und CSS aktualisieren 2024-09-30 11:32:14 +02:00
Developer 02
c123d103bb chore Upgrade auf 2.1.1.0 2024-09-25 16:09:01 +02:00
Developer 02
b92d9da387 refactor(envelope-locked): konvertiere submit-button bootstrap Klasse von btn-outline-primary zu btn-primary 2024-09-25 16:07:54 +02:00
Developer 02
8841698aab chore(web): Version auf 2.0.0.0 aktualisieren und Copyrights schreiben 2024-09-25 15:52:09 +02:00
Developer 02
6f140f16cd fix(footer): Entferne den Ursprung der Datenschutzrichtlinien-URL, um auf den aktuellen Ursprung umzuleiten 2024-09-25 15:49:38 +02:00
Developer 02
8bfd31997b Merge branch 'master' of http://git.dd:3000/AppStd/EnvelopeGenerator 2024-09-25 14:46:14 +02:00
Developer 02
4a1459d708 feat: add smooth width transition to progress bar with 1s ease animation 2024-09-25 14:46:08 +02:00
Developer 02
361bdeb2b2 feat(event-binder.js): Es wurde der Effekt hinzugefügt, dass die Breite des Fortschrittsbalkens nach dem Signaturprozess um einen bestimmten Anteil erhöht wird. 2024-09-25 14:41:30 +02:00
Developer 02
9ce5af7cd0 feat(event-binder.js): Signaturkomponente hinzugefügt, um die Animationen in der Fortschrittsleiste der Signatur zu behandeln
- Befehl zum Erhöhen der Anzahl von Signaturen unter der createAnnotationFrameBlob-Methode in annotation.js hinzugefügt

- Vorzeichenbehafteter Befehl zum Nullsetzen der Zählung unter RESET-case der handleClick-Methode in app.js hinzugefügt
2024-09-25 14:03:05 +02:00
Developer 02
84fa9e6e7c refactor(envelope-locked): Zugriffscode-Panel einrichten 2024-09-25 13:28:47 +02:00
Developer 02
36916ed5c8 refactor(envelope-lcoked): Konvertierung von 'access-code' Floating Input 2024-09-25 11:01:54 +02:00
Developer 02
fb366d3e0b refactor: Umschlag nav-menu bearbeiten. minfied card.css 2024-09-25 09:21:55 +02:00
Developer01
6e7670f667 Service GDPicture 2024-09-24 17:48:29 +02:00
Developer01
e82be8b6a5 Merge branch 'master' of http://git.dd:3000/AppStd/EnvelopeGenerator 2024-09-24 17:26:19 +02:00
Developer01
07320af4ee MS GPicture 2024-09-24 17:26:11 +02:00
513 changed files with 13429 additions and 59122 deletions

View File

@@ -0,0 +1,13 @@
namespace EnvelopeGenerator.Application.Configurations
{
public class DispatcherConfig
{
public int SendingProfile { get; init; } = 1;
public string AddedWho { get; init; } = "DDEnvelopGenerator";
public int ReminderTypeId { get; init; } = 202377;
public string EmailAttmt1 { get; init; } = string.Empty;
}
}

View File

@@ -0,0 +1,7 @@
namespace EnvelopeGenerator.Application.Configurations
{
public class MailConfig
{
public required Dictionary<string, string> Placeholders { get; init; }
}
}

View File

@@ -1,6 +1,7 @@
using DigitalData.Core.DTO;
using DigitalData.EmailProfilerDispatcher.Abstraction.Contracts;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Common;
namespace EnvelopeGenerator.Application.Contracts
@@ -8,6 +9,9 @@ namespace EnvelopeGenerator.Application.Contracts
public interface IEnvelopeMailService : IEmailOutService
{
Task<DataResult<int>> SendAsync(EnvelopeReceiverDto envelopeReceiverDto, Constants.EmailTemplateType tempType);
Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto);
Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto envelopeReceiverDto);
}
}
}

View File

@@ -0,0 +1,10 @@
using DigitalData.Core.Abstractions.Application;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Contracts
{
public interface IEnvelopeReceiverReadOnlyService : ICRUDService<EnvelopeReceiverReadOnlyCreateDto, EnvelopeReceiverReadOnlyDto, EnvelopeReceiverReadOnlyUpdateDto, EnvelopeReceiverReadOnly, long>
{
}
}

View File

@@ -1,21 +1,18 @@
using DigitalData.UserManager.Application.MappingProfiles;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.MappingProfiles;
using EnvelopeGenerator.Application.Configurations;
using EnvelopeGenerator.Application.Services;
using EnvelopeGenerator.Infrastructure.Contracts;
using EnvelopeGenerator.Infrastructure.Repositories;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EnvelopeGenerator.Application
{
public static class DIExtensions
{
public static IServiceCollection AddEnvelopeGenerator(this IServiceCollection services)
public static IServiceCollection AddEnvelopeGenerator(this IServiceCollection services, IConfiguration dispatcherConfigSection, IConfiguration mailConfigSection)
{
//Inject CRUD Service and repositoriesad
services.AddScoped<IConfigRepository, ConfigRepository>();
@@ -33,6 +30,7 @@ namespace EnvelopeGenerator.Application
services.AddScoped<IEnvelopeTypeRepository, EnvelopeTypeRepository>();
services.AddScoped<IReceiverRepository, ReceiverRepository>();
services.AddScoped<IUserReceiverRepository, UserReceiverRepository>();
services.AddScoped<IEnvelopeReceiverReadOnlyRepository, EnvelopeReceiverReadOnlyRepository>();
services.AddScoped<IConfigService, ConfigService>();
services.AddScoped<IDocumentReceiverElementService, DocumentReceiverElementService>();
services.AddScoped<IEnvelopeDocumentService, EnvelopeDocumentService>();
@@ -46,12 +44,20 @@ namespace EnvelopeGenerator.Application
services.AddScoped<IEnvelopeTypeService, EnvelopeTypeService>();
services.AddScoped<IReceiverService, ReceiverService>();
services.AddScoped<IUserReceiverService, UserReceiverService>();
services.AddScoped<IEnvelopeReceiverReadOnlyService, EnvelopeReceiverReadOnlyService>();
//Auto mapping profiles
services.AddAutoMapper(typeof(BasicDtoMappingProfile).Assembly);
services.AddAutoMapper(typeof(UserMappingProfile).Assembly);
services.Configure<DispatcherConfig>(dispatcherConfigSection);
services.Configure<MailConfig>(mailConfigSection);
return services;
}
public static IServiceCollection AddEnvelopeGenerator(this IServiceCollection services, IConfiguration config) => services.AddEnvelopeGenerator(
dispatcherConfigSection: config.GetSection("DispatcherConfig"),
mailConfigSection: config.GetSection("MailConfig"));
}
}
}

View File

@@ -7,6 +7,7 @@ namespace EnvelopeGenerator.Application.DTOs
int Id,
int EnvelopeId,
DateTime AddedWhen,
IEnumerable<DocumentReceiverElementDto>? Elements
byte[]? ByteData = null,
IEnumerable<DocumentReceiverElementDto>? Elements = null
) : IUnique<int>;
}

View File

@@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly
{
public record EnvelopeReceiverReadOnlyCreateDto(
DateTime DateValid)
{
[EmailAddress]
[Required]
public required string ReceiverMail { get; init; }
[JsonIgnore]
public long? EnvelopeId { get; set; } = null;
[JsonIgnore]
public string? AddedWho { get; set; }
[JsonIgnore]
public DateTime AddedWhen { get; } = DateTime.Now;
};
}

View File

@@ -0,0 +1,16 @@
using EnvelopeGenerator.Application.DTOs.Receiver;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly
{
public record EnvelopeReceiverReadOnlyDto(
long Id,
long EnvelopeId,
string ReceiverMail,
DateTime DateValid,
DateTime AddedWhen,
string AddedWho,
EnvelopeDto? Envelope = null,
string? ChangedWho = null,
DateTime? ChangedWhen = null,
ReceiverReadDto? Receiver = null);
}

View File

@@ -0,0 +1,12 @@
using DigitalData.Core.Abstractions;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly
{
public record EnvelopeReceiverReadOnlyUpdateDto(
long Id,
DateTime DateValid,
string ChangedWho) : IUnique<long>
{
public DateTime ChangedWhen { get; } = DateTime.Now;
};
}

View File

@@ -1,13 +0,0 @@
namespace EnvelopeGenerator.Application
{
public class DispatcherConfig
{
public int SendingProfile { get; init; } = 1;
public string AddedWho { get; init; } = "DDEnvelopGenerator";
public int ReminderTypeId { get; init; } = 202377;
public string EmailAttmt1 { get; init; } = string.Empty;
}
}

View File

@@ -22,6 +22,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Extensions\EnvelopeGenerator.Extensions.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
</ItemGroup>

View File

@@ -2,6 +2,7 @@
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Application.DTOs.EnvelopeHistory;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Application.DTOs.Receiver;
using EnvelopeGenerator.Domain.Entities;
@@ -28,6 +29,7 @@ namespace EnvelopeGenerator.Application.MappingProfiles
CreateMap<Receiver, ReceiverCreateDto>();
CreateMap<Receiver, ReceiverUpdateDto>();
CreateMap<UserReceiver, UserReceiverDto>();
CreateMap<EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
// DTO to Entity mappings
CreateMap<ConfigDto, Config>();
@@ -46,6 +48,8 @@ namespace EnvelopeGenerator.Application.MappingProfiles
CreateMap<ReceiverUpdateDto, Receiver>();
CreateMap<UserReceiverDto, UserReceiver>();
CreateMap<EnvelopeReceiverBase, EnvelopeReceiverBasicDto>();
CreateMap<EnvelopeReceiverReadOnlyCreateDto, EnvelopeReceiverReadOnly>();
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, EnvelopeReceiverReadOnly>();
}
}
}

View File

@@ -177,6 +177,9 @@
<data name="LockedTitle" xml:space="preserve">
<value>Dokument erfordert einen Zugriffscode</value>
</data>
<data name="ReadOnlyMessage" xml:space="preserve">
<value>Weitergeleitet von {0}. Gültig bis {1}.</value>
</data>
<data name="Reject" xml:space="preserve">
<value>Ablehnen</value>
</data>
@@ -210,6 +213,9 @@
<data name="UnexpectedError" xml:space="preserve">
<value>Ein unerwarteter Fehler ist aufgetreten.</value>
</data>
<data name="ViewDoc" xml:space="preserve">
<value>Dokument ansehen</value>
</data>
<data name="WelcomeToTheESignPortal" xml:space="preserve">
<value>Herzlich willkommen im eSign-Portal</value>
</data>

View File

@@ -177,6 +177,9 @@
<data name="LockedTitle" xml:space="preserve">
<value>Document requires an access code</value>
</data>
<data name="ReadOnlyMessage" xml:space="preserve">
<value>Forwarded by {0}. Valid until {1}.</value>
</data>
<data name="Reject" xml:space="preserve">
<value>Reject</value>
</data>
@@ -210,6 +213,9 @@
<data name="UnexpectedError" xml:space="preserve">
<value>An unexpected error has occurred.</value>
</data>
<data name="ViewDoc" xml:space="preserve">
<value>View document</value>
</data>
<data name="WelcomeToTheESignPortal" xml:space="preserve">
<value>Welcome to the eSign portal</value>
</data>

View File

@@ -3,14 +3,15 @@ using DigitalData.Core.DTO;
using DigitalData.EmailProfilerDispatcher.Abstraction.Contracts;
using DigitalData.EmailProfilerDispatcher.Abstraction.DTOs.EmailOut;
using DigitalData.EmailProfilerDispatcher.Abstraction.Services;
using DigitalData.UserManager.Application;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Common;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using static EnvelopeGenerator.Common.Constants;
using EnvelopeGenerator.Extensions;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Application.Configurations;
namespace EnvelopeGenerator.Application.Services
{
@@ -20,39 +21,50 @@ namespace EnvelopeGenerator.Application.Services
private readonly IEnvelopeReceiverService _envRcvService;
private readonly DispatcherConfig _dConfig;
private readonly IConfigService _configService;
private readonly Dictionary<string, string> _placeholders;
public EnvelopeMailService(IEmailOutRepository repository, IStringLocalizer<Resource> localizer, IMapper mapper, IEmailTemplateService tempService, IEnvelopeReceiverService envelopeReceiverService, IOptions<DispatcherConfig> dispatcherConfigOptions, IConfigService configService) : base(repository, mapper)
public EnvelopeMailService(IEmailOutRepository repository, IMapper mapper, IEmailTemplateService tempService, IEnvelopeReceiverService envelopeReceiverService, IOptions<DispatcherConfig> dispatcherConfigOptions, IConfigService configService, IOptions<MailConfig> mailConfig) : base(repository, mapper)
{
_tempService = tempService;
_envRcvService = envelopeReceiverService;
_dConfig = dispatcherConfigOptions.Value;
_configService = configService;
_placeholders = mailConfig.Value.Placeholders;
}
//TODO: create ioptions and implement TemplatePlaceHolderAttribute instead of this method
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null)
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null, EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
{
Dictionary<string, string> placeholders = new() {
{ "[NAME_PORTAL]", "signFlow" },
{ "[SIGNATURE_TYPE]" , "signieren"},
{ "[REASON]", string.Empty } };
if (accessCode is not null)
placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
_placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
if(envelopeReceiverDto is not null && envelopeReceiverDto.Envelope is not null && envelopeReceiverDto.Receiver is not null)
if(envelopeReceiverDto?.Envelope is not null && envelopeReceiverDto.Receiver is not null)
{
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).EncodeEnvelopeReceiverId();
var sigHost = await _configService.ReadDefaultSignatureHost();
var linkToDoc = $"{sigHost}/envelope/{erId}";
placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
placeholders["[LINK_TO_DOCUMENT_TEXT]"] = linkToDoc[..Math.Min(40, linkToDoc.Length)];
var linkToDoc = $"{sigHost}/EnvelopeKey/{erId}";
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
_placeholders["[LINK_TO_DOCUMENT_TEXT]"] = linkToDoc[..Math.Min(40, linkToDoc.Length)] + "..";
}
return placeholders;
return _placeholders;
}
public async Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto dto) => await SendAsync(dto: dto, tempType: Constants.EmailTemplateType.DocumentAccessCodeReceived);
private async Task<Dictionary<string, string>> CreatePlaceholders(EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
{
if (readOnlyDto?.Envelope is not null && readOnlyDto.Receiver is not null)
{
_placeholders["[NAME_RECEIVER]"] = await _envRcvService.ReadLastUsedReceiverNameByMail(readOnlyDto.AddedWho).ThenAsync(res => res, (msg, ntc) => string.Empty) ?? string.Empty;
var erReadOnlyId = (readOnlyDto.Id).EncodeEnvelopeReceiverId();
var sigHost = await _configService.ReadDefaultSignatureHost();
var linkToDoc = $"{sigHost}/EnvelopeKey/{erReadOnlyId}";
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
_placeholders["[LINK_TO_DOCUMENT_TEXT]"] = linkToDoc[..Math.Min(40, linkToDoc.Length)] + "..";
}
return _placeholders;
}
public async Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto dto) => await SendAsync(dto: dto, tempType: Constants.EmailTemplateType.DocumentAccessCodeReceived);
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverDto dto, Constants.EmailTemplateType tempType)
{
@@ -92,8 +104,43 @@ namespace EnvelopeGenerator.Application.Services
var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
//TODO: remove the requirement to add the models using reflections
return await CreateWithTemplateAsync(createDto: mail,placeholders: placeholders,
dto, dto.Envelope.User!, dto.Envelope);
}
}
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto)
{
var tempSerResult = await _tempService.ReadByNameAsync(Constants.EmailTemplateType.DocumentShared);
if (tempSerResult.IsFailed)
return tempSerResult.ToFail<int>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, $"The email cannot send because '{Constants.EmailTemplateType.DocumentShared}' template cannot found.");
var temp = tempSerResult.Data;
var mail = new EmailOutCreateDto()
{
EmailAddress = dto.ReceiverMail,
EmailSubj = temp.Subject,
EmailBody = temp.Body,
//TODO: remove int casting when all
ReferenceId = (int) dto.EnvelopeId, //REFERENCE_ID = ENVELOPE_ID
ReferenceString = dto.Envelope!.Uuid, //REFERENCE_STRING = ENVELOPE_UUID
//receiver_name = receiver.name,
//receiver_access_code = receiver.access_code,
//sender_adress = envelope.user.email,
//sender_name = envelope.user.full_name,
//envelope_title = envelope.title,
ReminderTypeId = _dConfig.ReminderTypeId,
SendingProfile = _dConfig.SendingProfile,
EntityId = null,
WfId = (int)EnvelopeStatus.EnvelopeShared,
WfReference = null,
AddedWho = _dConfig.AddedWho,
EmailAttmt1 = _dConfig.EmailAttmt1
};
var placeholders = await CreatePlaceholders(readOnlyDto: dto);
return await CreateWithTemplateAsync(createDto: mail, placeholders: placeholders, dto.Envelope);
}
}
}

View File

@@ -0,0 +1,16 @@
using AutoMapper;
using DigitalData.Core.Application;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
namespace EnvelopeGenerator.Application.Services
{
public class EnvelopeReceiverReadOnlyService : CRUDService<IEnvelopeReceiverReadOnlyRepository, EnvelopeReceiverReadOnlyCreateDto, EnvelopeReceiverReadOnlyDto, EnvelopeReceiverReadOnlyUpdateDto, EnvelopeReceiverReadOnly, long>, IEnvelopeReceiverReadOnlyService
{
public EnvelopeReceiverReadOnlyService(IEnvelopeReceiverReadOnlyRepository repository, IMapper mapper) : base(repository, mapper)
{
}
}
}

View File

@@ -8,6 +8,7 @@ using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using EnvelopeGenerator.Extensions;
namespace EnvelopeGenerator.Application.Services
{

View File

@@ -1,6 +1,7 @@
Public Class Constants
#Region "Status Fields"
'http://wiki.dd/xwiki13/bin/view/Anwendungen/Produkt-Handbuch/Sonstiges/SignFlow/Envelope%20Status/
Public Enum EnvelopeStatus
Invalid = 0
EnvelopeCreated = 1001
@@ -17,8 +18,11 @@
AccessCodeIncorrect = 2003
DocumentOpened = 2004
DocumentSigned = 2005
DocumentForwarded = 4001
SignatureConfirmed = 2006
DocumentRejected = 2007
EnvelopeShared = 2008
EnvelopeViewed = 2009
MessageInvitationSent = 3001 ' Wird von Trigger verwendet
MessageAccessCodeSent = 3002
MessageConfirmationSent = 3003
@@ -94,6 +98,15 @@
DocumentDeleted
DocumentCompleted
DocumentAccessCodeReceived
DocumentShared
End Enum
Public Enum EncodeType
EnvelopeReceiver
EnvelopeReceiverReadOnly
Undefined
DocumentForwarded
DocumentShared
End Enum
#End Region
@@ -109,4 +122,4 @@
Public Const RED_300 = "#fecaca"
Public Const ORANGE_300 = "#fed7aa"
#End Region
End Class
End Class

View File

@@ -1,5 +1,5 @@
Public Class DbConfig
Public Property ExternalProgramName As String = "Sign Flow"
Public Property ExternalProgramName As String = "signFLOW"
Public Property DocumentPathOrigin As String = ""
Public Property DocumentPath As String = ""
Public Property ExportPath As String = ""

View File

@@ -13,6 +13,8 @@
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -75,8 +77,8 @@
<Reference Include="DigitalData.Modules.Logging">
<HintPath>..\..\2_DLL Projekte\DDModules\Logging\bin\Debug\DigitalData.Modules.Logging.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14">
<HintPath>D:\ProgramFiles\GdPicture.NET 14\Redist\GdPicture.NET (.NET Framework 4.5)\GdPicture.NET.14.dll</HintPath>
<Reference Include="GdPicture.NET.14, Version=14.2.89.0, Culture=neutral, PublicKeyToken=f52a2e60ad468dbb, processorArchitecture=MSIL">
<HintPath>..\packages\GdPicture.14.2.89\lib\net462\GdPicture.NET.14.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
@@ -287,4 +289,11 @@
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Import Project="..\packages\GdPicture.runtimes.windows.14.2.89\build\net462\GdPicture.runtimes.windows.targets" Condition="Exists('..\packages\GdPicture.runtimes.windows.14.2.89\build\net462\GdPicture.runtimes.windows.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\GdPicture.runtimes.windows.14.2.89\build\net462\GdPicture.runtimes.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GdPicture.runtimes.windows.14.2.89\build\net462\GdPicture.runtimes.windows.targets'))" />
</Target>
</Project>

View File

@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' indem Sie "*" wie unten gezeigt eingeben:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("1.9.2.0")>
<Assembly: AssemblyFileVersion("1.9.2.0")>
<Assembly: AssemblyVersion("1.9.3.0")>
<Assembly: AssemblyFileVersion("1.9.3.0")>

View File

@@ -138,6 +138,9 @@
<data name="Document Could Not Be Saved" xml:space="preserve">
<value>Document could not be saved!</value>
</data>
<data name="Document forwarded" xml:space="preserve">
<value>Document forwarded to receiver: {0}</value>
</data>
<data name="Edit Envelope" xml:space="preserve">
<value>Edit Envelope</value>
</data>

View File

@@ -138,6 +138,9 @@
<data name="Document Could Not Be Saved" xml:space="preserve">
<value>Dokument konnte nicht gespeichert werden!</value>
</data>
<data name="Document forwarded" xml:space="preserve">
<value>Umschlag an Empfänger {0} weitergeleitet.</value>
</data>
<data name="Edit Envelope" xml:space="preserve">
<value>Bearbeite Umschlag</value>
</data>

View File

@@ -127,6 +127,15 @@ Namespace My.Resources
End Get
End Property
'''<summary>
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag an Empfänger {0} weitergeleitet. ähnelt.
'''</summary>
Public Shared ReadOnly Property Document_forwarded() As String
Get
Return ResourceManager.GetString("Document forwarded", resourceCulture)
End Get
End Property
'''<summary>
''' Sucht eine lokalisierte Zeichenfolge, die Bearbeite Umschlag ähnelt.
'''</summary>

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="GdPicture" version="14.2.89" targetFramework="net462" />
<package id="GdPicture.runtimes.windows" version="14.2.89" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" targetFramework="net462" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net462" />
<package id="Quartz" version="3.8.0" targetFramework="net462" />

View File

@@ -84,10 +84,6 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("EXPIRES_WARNING_WHEN_DAYS")]
public int? ExpiresWarningWhenDays { get; set; }
[Required]
[Column("DMZ_MOVED")]
public bool DmzMoved { get; set; }
/// <summary>
/// The sender of envelope
/// </summary>

View File

@@ -31,6 +31,9 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("FILENAME_ORIGINAL", TypeName = "nvarchar(256)")]
public required string FilenameOriginal { get; set; }
[Column("BYTE_DATA", TypeName = "varbinary(max)")]
public byte[]? ByteData { get; init; }
public IEnumerable<DocumentReceiverElement>? Elements { get; set; }
}
}

View File

@@ -0,0 +1,52 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using DigitalData.Core.Abstractions;
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
namespace EnvelopeGenerator.Domain.Entities
{
[Table("TBSIG_ENVELOPE_RECEIVER_READ_ONLY")]
public class EnvelopeReceiverReadOnly : IUnique<long>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("GUID")]
public long Id { get; init; }
[Column("ENVELOPE_ID")]
[Required]
public long EnvelopeId { get; init; }
//TODO: remove NotMapped attribute when EnvelopeId data type is standardized
[NotMapped]
public Envelope? Envelope { get; set; }
[Column("RECEIVER_MAIL")]
[Required]
[StringLength(250)]
[TemplatePlaceholder("NAME_RECEIVER")]
public required string ReceiverMail { get; init; }
[Column("DATE_VALID")]
[Required]
public DateTime DateValid { get; init; }
[Column("ADDED_WHO")]
[Required]
[StringLength(100)]
public required string AddedWho { get; init; }
public Receiver? Receiver { get; init; }
[Column("ADDED_WHEN")]
[Required]
public DateTime AddedWhen { get; init; }
[Column("CHANGED_WHO")]
[StringLength(100)]
public string? ChangedWho { get; init; }
[Column("CHANGED_WHEN")]
public DateTime? ChangedWhen { get; init; }
}
}

View File

@@ -1,12 +1,9 @@
using Microsoft.Extensions.Logging;
using System.Text;
using System.Text;
using static EnvelopeGenerator.Common.Constants;
namespace EnvelopeGenerator.Application
namespace EnvelopeGenerator.Extensions
{
/// <summary>
/// Provides extension methods for decoding and extracting information from an envelope receiver ID.
/// </summary>
public static class EnvelopeGeneratorExtensions
public static class DecodingExtensions
{
/// <summary>
/// Validates whether a given string is a correctly formatted Base-64 encoded string.
@@ -67,6 +64,40 @@ namespace EnvelopeGenerator.Application
return input.IndexOf('=') == -1; // No padding allowed except at the end
}
public static bool TryDecode(this string encodedKey, out string[] decodedKeys)
{
try
{
byte[] bytes = Convert.FromBase64String(encodedKey);
string decodedString = Encoding.UTF8.GetString(bytes);
decodedKeys = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
return true;
}
catch(ArgumentNullException) { }
catch (FormatException) { }
catch(ArgumentException) { }
decodedKeys = Array.Empty<string>();
return false;
}
public static EncodeType GetEncodeType(this string[] decodedKeys) => decodedKeys.Length switch
{
2 => EncodeType.EnvelopeReceiver,
3 => long.TryParse(decodedKeys[1], out var _) ? EncodeType.EnvelopeReceiverReadOnly : EncodeType.Undefined,
_ => EncodeType.Undefined,
};
public static (string? EnvelopeUuid, string? ReceiverSignature) ParseEnvelopeReceiverId(this string[] decodedKeys)
=> decodedKeys.GetEncodeType() == EncodeType.EnvelopeReceiver
? (EnvelopeUuid: decodedKeys[0], ReceiverSignature: decodedKeys[1])
: throw new InvalidOperationException("Attempted to convert a decoded other than type EnvelopeReceiver to EnvelopeReceiver.");
public static long ParseReadOnlyId(this string[] decodedKeys)
=> decodedKeys.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly
? long.Parse(decodedKeys[1])
: throw new InvalidOperationException("Attempted to convert a decoded other than type EnvelopeReceiver to EnvelopeReceiver. ");
/// <summary>
/// Decodes the envelope receiver ID and extracts the envelope UUID and receiver signature.
/// </summary>
@@ -79,7 +110,7 @@ namespace EnvelopeGenerator.Application
return (null, null);
}
byte[] bytes = Convert.FromBase64String(envelopeReceiverId);
string decodedString = System.Text.Encoding.UTF8.GetString(bytes);
string decodedString = Encoding.UTF8.GetString(bytes);
string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
if (parts.Length > 1)
@@ -88,6 +119,22 @@ namespace EnvelopeGenerator.Application
return (string.Empty, string.Empty);
}
public static long? DecodeEnvelopeReceiverReadOnlyId(this string envelopeReceiverReadOnlyId)
{
if (!envelopeReceiverReadOnlyId.IsBase64String())
{
return null;
}
byte[] bytes = Convert.FromBase64String(envelopeReceiverReadOnlyId);
string decodedString = System.Text.Encoding.UTF8.GetString(bytes);
string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
if (parts.Length > 2)
return long.TryParse(parts[1], out long readOnlyId) ? readOnlyId : null;
else
return null;
}
/// <summary>
/// Gets the envelope UUID from the decoded envelope receiver ID.
/// </summary>
@@ -101,48 +148,5 @@ namespace EnvelopeGenerator.Application
/// <param name="envelopeReceiverId">The base64 encoded string to decode.</param>
/// <returns>The receiver signature.</returns>
public static string? GetReceiverSignature(this string envelopeReceiverId) => envelopeReceiverId.DecodeEnvelopeReceiverId().ReceiverSignature;
public static string EncodeEnvelopeReceiverId(this (string envelopeUuid, string receiverSignature) input)
{
string combinedString = $"{input.envelopeUuid}::{input.receiverSignature}";
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
string base64String = Convert.ToBase64String(bytes);
return base64String;
}
public static void LogEnvelopeError(this ILogger logger, string envelopeReceiverId, Exception? exception = null, string? message = null, params object?[] args)
{
var sb = new StringBuilder().AppendLine(envelopeReceiverId.DecodeEnvelopeReceiverId().ToTitle());
if (message is not null)
sb.AppendLine(message);
if(exception is null)
logger.Log(LogLevel.Error, sb.ToString(), args);
else
logger.Log(LogLevel.Error, exception, sb.AppendLine(exception.Message).ToString(), args);
}
public static void LogEnvelopeError(this ILogger logger, string? uuid, string? signature = null, Exception? exception = null, string? message = null, params object?[] args)
{
var sb = new StringBuilder($"Envelope Uuid: {uuid}");
if(signature is not null)
sb.AppendLine().Append($"Receiver Signature: {signature}");
if (message is not null)
sb.AppendLine().Append(message);
if (exception is null)
logger.Log(LogLevel.Error, sb.ToString(), args);
else
logger.Log(LogLevel.Error, exception, sb.ToString(), args);
}
public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple)
{
return $"UUID is {envelopeReceiverTuple.UUID} and signature is {envelopeReceiverTuple.Signature}";
}
}
}

View File

@@ -0,0 +1,30 @@
using Microsoft.Extensions.Logging;
using System.Text;
namespace EnvelopeGenerator.Extensions
{
/// <summary>
/// Provides extension methods for decoding and extracting information from an envelope receiver ID.
/// </summary>
public static class EncodingExtensions
{
public static string EncodeEnvelopeReceiverId(this long readOnlyId)
{
//The random number is used as a salt to increase security but it is not saved in the database.
string combinedString = $"{Random.Shared.Next()}::{readOnlyId}::{Random.Shared.Next()}";
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
string base64String = Convert.ToBase64String(bytes);
return base64String;
}
public static string EncodeEnvelopeReceiverId(this (string envelopeUuid, string receiverSignature) input)
{
string combinedString = $"{input.envelopeUuid}::{input.receiverSignature}";
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
string base64String = Convert.ToBase64String(bytes);
return base64String;
}
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.19" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Common\EnvelopeGenerator.Common.vbproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,42 @@
using Microsoft.Extensions.Logging;
using System.Text;
namespace EnvelopeGenerator.Extensions
{
public static class LoggerExtensions
{
public static void LogEnvelopeError(this ILogger logger, string envelopeReceiverId, Exception? exception = null, string? message = null, params object?[] args)
{
var sb = new StringBuilder().AppendLine(envelopeReceiverId.DecodeEnvelopeReceiverId().ToTitle());
if (message is not null)
sb.AppendLine(message);
if (exception is null)
logger.Log(LogLevel.Error, sb.ToString(), args);
else
logger.Log(LogLevel.Error, exception, sb.AppendLine(exception.Message).ToString(), args);
}
public static void LogEnvelopeError(this ILogger logger, string? uuid, string? signature = null, Exception? exception = null, string? message = null, params object?[] args)
{
var sb = new StringBuilder($"Envelope Uuid: {uuid}");
if (signature is not null)
sb.AppendLine().Append($"Receiver Signature: {signature}");
if (message is not null)
sb.AppendLine().Append(message);
if (exception is null)
logger.Log(LogLevel.Error, sb.ToString(), args);
else
logger.Log(LogLevel.Error, exception, sb.ToString(), args);
}
public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple)
{
return $"UUID is {envelopeReceiverTuple.UUID} and signature is {envelopeReceiverTuple.Signature}";
}
}
}

View File

@@ -2,7 +2,7 @@
using Microsoft.Extensions.Localization;
using System.Text.Encodings.Web;
namespace EnvelopeGenerator.Web
namespace EnvelopeGenerator.Extensions
{
public static class XSSExtensions
{

View File

@@ -158,6 +158,7 @@ Public Class EnvelopeEditorController
Dim oTempFilePath = Path.Combine(oTempFiles.TempPath, Guid.NewGuid().ToString + oFileInfo.Extension)
Await Helpers.CopyFileAsync(oFileInfo.FullName, oTempFilePath)
'File.Copy(oFileInfo.FullName, oTempFilePath, True)
Dim oFileInfoTemp = New FileInfo(oTempFilePath)
@@ -175,6 +176,7 @@ Public Class EnvelopeEditorController
Catch ex As Exception
Logger.Error(ex)
Logger.Warn($"error in CreateDocument: {ex.Message}")
Return Nothing
End Try
End Function
@@ -288,7 +290,9 @@ Public Class EnvelopeEditorController
#End Region
Private Function GetEnvelopePath(pEnvelope As Envelope) As String
Try
Dim oEnvelopePath As String = Path.Combine(State.DbConfig.DocumentPath, pEnvelope.Uuid)
Dim oTempFiles As New TempFiles(State.LogConfig)
Dim oTempFolderPath = oTempFiles.TempPath
Dim oEnvelopePath As String = Path.Combine(oTempFolderPath, pEnvelope.Uuid)
If Not Directory.Exists(oEnvelopePath) Then
Directory.CreateDirectory(oEnvelopePath)

View File

@@ -14,6 +14,8 @@
<OptionCompare>Binary</OptionCompare>
<OptionStrict>Off</OptionStrict>
<OptionInfer>On</OptionInfer>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -69,8 +71,9 @@
<Reference Include="DevExpress.XtraNavBar.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a, processorArchitecture=MSIL" />
<Reference Include="DevExpress.XtraPrinting.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraTreeList.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DigitalData.Controls.DocumentViewer">
<HintPath>..\..\DDMonorepo\Controls.DocumentViewer\bin\Debug\DigitalData.Controls.DocumentViewer.dll</HintPath>
<Reference Include="DigitalData.Controls.DocumentViewer, Version=1.9.4.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\2_DLL Projekte\DDMonorepo\Controls.DocumentViewer\bin\Debug\DigitalData.Controls.DocumentViewer.dll</HintPath>
</Reference>
<Reference Include="DigitalData.GUIs.Common">
<HintPath>..\..\DDMonorepo\GUIs.Common\bin\Debug\DigitalData.GUIs.Common.dll</HintPath>
@@ -97,8 +100,8 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\EnvelopeGenerator.Common\bin\Debug\EnvelopeGenerator.Common.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14">
<HintPath>D:\ProgramFiles\GdPicture.NET 14\Redist\GdPicture.NET (.NET Framework 4.5)\GdPicture.NET.14.dll</HintPath>
<Reference Include="GdPicture.NET.14, Version=14.2.90.0, Culture=neutral, PublicKeyToken=f52a2e60ad468dbb, processorArchitecture=MSIL">
<HintPath>..\packages\GdPicture.14.2.90\lib\net462\GdPicture.NET.14.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.5.0.5\lib\net46\NLog.dll</HintPath>
@@ -269,6 +272,13 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Import Project="..\packages\GdPicture.runtimes.windows.14.2.90\build\net462\GdPicture.runtimes.windows.targets" Condition="Exists('..\packages\GdPicture.runtimes.windows.14.2.90\build\net462\GdPicture.runtimes.windows.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\GdPicture.runtimes.windows.14.2.90\build\net462\GdPicture.runtimes.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GdPicture.runtimes.windows.14.2.90\build\net462\GdPicture.runtimes.windows.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -68,4 +68,12 @@
<!--<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="APPLICATION_NAME"/> -->
</sharedListeners>
</system.diagnostics>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="GdPicture.NET.14" publicKeyToken="f52a2e60ad468dbb" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-14.2.90.0" newVersion="14.2.90.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -878,7 +878,7 @@
<value>0</value>
</data>
<metadata name="FrmEditorBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 54</value>
<value>792, 17</value>
</metadata>
<metadata name="EnvelopeDocumentBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>557, 17</value>

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="GdPicture" version="14.2.90" targetFramework="net462" />
<package id="GdPicture.runtimes.windows" version="14.2.90" targetFramework="net462" />
<package id="NLog" version="5.0.5" targetFramework="net462" />
</packages>

View File

@@ -60,7 +60,7 @@ builder.Services.AddDirectorySearchService();
builder.Services.AddCookieBasedLocalizer() ;
// Envelope generator serives
builder.Services.AddEnvelopeGenerator();
builder.Services.AddEnvelopeGenerator(config);
var app = builder.Build();

View File

@@ -0,0 +1,9 @@
using DigitalData.Core.Abstractions.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Infrastructure.Contracts
{
public interface IEnvelopeReceiverReadOnlyRepository : ICRUDRepository<EnvelopeReceiverReadOnly, long>
{
}
}

View File

@@ -51,6 +51,8 @@ namespace EnvelopeGenerator.Infrastructure
public DbSet<EmailOut> EMailOuts { get; set; }
public DbSet<EnvelopeReceiverReadOnly> EnvelopeReceiverReadOnlys { get; set; }
public EGDbContext(DbContextOptions<EGDbContext> options) : base(options)
{
UserReceivers = Set<UserReceiver>();
@@ -72,6 +74,7 @@ namespace EnvelopeGenerator.Infrastructure
Users = Set<User>();
UserReps = Set<UserRep>();
EMailOuts = Set<EmailOut>();
EnvelopeReceiverReadOnlys = Set<EnvelopeReceiverReadOnly>();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
@@ -126,12 +129,19 @@ namespace EnvelopeGenerator.Infrastructure
.HasForeignKey(eh => eh.UserReference)
.HasPrincipalKey(e => e.Email);
modelBuilder.Entity<EnvelopeReceiverReadOnly>()
.HasOne(erro => erro.Receiver)
.WithMany()
.HasForeignKey(erro => erro.AddedWho)
.HasPrincipalKey(r => r.EmailAddress);
// Configure entities to handle database triggers
modelBuilder.Entity<Envelope>().ToTable(tb => tb.HasTrigger("TBSIG_ENVELOPE_HISTORY_AFT_INS"));
modelBuilder.Entity<EnvelopeHistory>().ToTable(tb => tb.HasTrigger("TBSIG_ENVELOPE_HISTORY_AFT_INS"));
modelBuilder.Entity<EmailOut>().ToTable(tb => tb.HasTrigger("TBEMLP_EMAIL_OUT_AFT_INS"));
modelBuilder.Entity<EmailOut>().ToTable(tb => tb.HasTrigger("TBEMLP_EMAIL_OUT_AFT_UPD"));
modelBuilder.Entity<EnvelopeReceiverReadOnly>().ToTable(tb => tb.HasTrigger("TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD"));
//configure model builder for user manager tables
modelBuilder.ConfigureUserManager();

View File

@@ -0,0 +1,70 @@
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Infrastructure.Repositories
{
public class EnvelopeReceiverReadOnlyRepository : CRUDRepository<EnvelopeReceiverReadOnly, long, EGDbContext>, IEnvelopeReceiverReadOnlyRepository
{
private readonly IEnvelopeRepository _envRepo;
public EnvelopeReceiverReadOnlyRepository(EGDbContext dbContext, IEnvelopeRepository envelopeRepository) : base(dbContext, dbContext.EnvelopeReceiverReadOnlys)
{
_envRepo = envelopeRepository;
}
protected override IQueryable<EnvelopeReceiverReadOnly> ReadOnly()
{
return base.ReadOnly()
//TODO: add again when EnvelopeId data type is standardized
//.Include(erro => erro.Envelope)
.Include(erro => erro.Receiver);
}
public async override Task<IEnumerable<EnvelopeReceiverReadOnly>> ReadAllAsync()
{
var erros = await base.ReadAllAsync();
return await IncludeEnvelope(erros);
}
public override async Task<EnvelopeReceiverReadOnly?> ReadByIdAsync(long id)
{
var erro = await _dbSet.AsNoTracking()
.Include(erro => erro.Receiver)
.Where(erro => erro.Id == id)
.FirstOrDefaultAsync();
return await IncludeEnvelope(erro);
}
//TODO: Use IQueryable.Include instead of this when ID type is clarified.
[Obsolete("Use IQueryable.Include instead of this when ID type is clarified.")]
private async Task<EnvelopeReceiverReadOnly> IncludeEnvelope(EnvelopeReceiverReadOnly erro)
{
erro.Envelope = await _envRepo.ReadByIdAsync((int)erro.EnvelopeId);
return erro;
}
//TODO: Use IQueryable.Include instead of this when ID type is clarified.
[Obsolete("Use IQueryable.Include instead of this when ID type is clarified.")]
private async Task<IEnumerable<EnvelopeReceiverReadOnly>> IncludeEnvelope(params EnvelopeReceiverReadOnly[] erros)
{
foreach (var erro in erros)
erro.Envelope = await _envRepo.ReadByIdAsync((int) erro.EnvelopeId);
return erros;
}
//TODO: Use IQueryable.Include instead of this when ID type is clarified.
[Obsolete("Use IQueryable.Include instead of this when ID type is clarified.")]
private async Task<T> IncludeEnvelope<T>(T erros)
where T : IEnumerable<EnvelopeReceiverReadOnly>
{
foreach (var erro in erros)
erro.Envelope = await _envRepo.ReadByIdAsync((int)erro.EnvelopeId);
return erros;
}
}
}

View File

@@ -14,6 +14,8 @@
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -66,9 +68,8 @@
<Reference Include="DigitalData.Modules.Messaging">
<HintPath>..\..\2_DLL Projekte\DDModules\Messaging\bin\Debug\DigitalData.Modules.Messaging.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14, Version=14.1.0.152, Culture=neutral, PublicKeyToken=f52a2e60ad468dbb, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>D:\ProgramFiles\GdPicture.NET 14\Redist\GdPicture.NET (.NET Framework 4.5)\GdPicture.NET.14.dll</HintPath>
<Reference Include="GdPicture.NET.14, Version=14.2.89.0, Culture=neutral, PublicKeyToken=f52a2e60ad468dbb, processorArchitecture=MSIL">
<HintPath>..\packages\GdPicture.14.2.89\lib\net462\GdPicture.NET.14.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
@@ -177,4 +178,11 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Import Project="..\packages\GdPicture.runtimes.windows.14.2.89\build\net462\GdPicture.runtimes.windows.targets" Condition="Exists('..\packages\GdPicture.runtimes.windows.14.2.89\build\net462\GdPicture.runtimes.windows.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\GdPicture.runtimes.windows.14.2.89\build\net462\GdPicture.runtimes.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GdPicture.runtimes.windows.14.2.89\build\net462\GdPicture.runtimes.windows.targets'))" />
</Target>
</Project>

View File

@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' übernehmen, indem Sie "*" eingeben:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("1.7.0.0")>
<Assembly: AssemblyFileVersion("1.7.0.0")>
<Assembly: AssemblyVersion("1.7.1.0")>
<Assembly: AssemblyFileVersion("1.7.1.0")>

View File

@@ -26,47 +26,52 @@ Public Class Scheduler
End Sub
Public Async Function Start(pInterval As Integer) As Task
Logger.Debug("Starting Scheduler..")
Try
Logger.Debug("Starting Scheduler..")
Dim oProperties As New NameValueCollection()
Dim oProperties As New NameValueCollection()
Scheduler = Await SchedulerBuilder.Create(oProperties).
UseDefaultThreadPool(Sub(x) x.MaxConcurrency = 5).
BuildScheduler()
Scheduler = Await SchedulerBuilder.Create(oProperties).
UseDefaultThreadPool(Sub(x) x.MaxConcurrency = 5).
BuildScheduler()
Dim oJobKey = New JobKey(JobName)
Dim oJobData = New JobDataMap() From {
{Common.Constants.GDPICTURE, LicenseKey},
{Common.Constants.LOGCONFIG, LogConfig},
{Common.Constants.DATABASE, ConnectionString},
{Common.Constants.IGNORED_LABELS, _ignoredLabels}
}
Dim oJobKey = New JobKey(JobName)
Dim oJobData = New JobDataMap() From {
{Common.Constants.GDPICTURE, LicenseKey},
{Common.Constants.LOGCONFIG, LogConfig},
{Common.Constants.DATABASE, ConnectionString},
{Common.Constants.IGNORED_LABELS, _ignoredLabels}
}
Logger.Debug("Initialized Job [{0}]", JobName)
Logger.Debug("Initialized Job [{0}]", JobName)
Dim oJob As IJobDetail = JobBuilder.Create(Of FinalizeDocumentJob).
UsingJobData(oJobData).
WithIdentity(oJobKey).
Build()
Dim oJob As IJobDetail = JobBuilder.Create(Of FinalizeDocumentJob).
UsingJobData(oJobData).
WithIdentity(oJobKey).
Build()
Dim oTrigger As ITrigger = TriggerBuilder.Create().
ForJob(oJobKey).
WithIdentity($"{JobName}-trigger").
WithSimpleSchedule(Sub(s) s.
RepeatForever().
WithIntervalInMinutes(pInterval)).
StartNow().
Build()
Dim oTrigger As ITrigger = TriggerBuilder.Create().
ForJob(oJobKey).
WithIdentity($"{JobName}-trigger").
WithSimpleSchedule(Sub(s) s.
RepeatForever().
WithIntervalInMinutes(pInterval)).
StartNow().
Build()
Logger.Debug("Initialized Trigger")
Logger.Debug("Initialized Trigger")
Await Scheduler.ScheduleJob(oJob, oTrigger)
Await Scheduler.ScheduleJob(oJob, oTrigger)
Logger.Debug("Job scheduled.")
Logger.Debug("Job scheduled.")
Await Scheduler.Start()
Await Scheduler.Start()
Logger.Info("Scheduler started!")
Catch ex As Exception
Logger.Error(ex)
End Try
Logger.Info("Scheduler started!")
End Function
Public Async Function [Stop]() As Task

View File

@@ -37,13 +37,14 @@ Public Class Service
TempFiles.Create()
' === Initialize Databases ===
Logger.Info("Inititalize Databases")
Logger.Info("Inititalize Database ...")
If Config.ConnectionString = String.Empty Then
Throw New ApplicationException("Connection String is empty!")
End If
Database = New MSSQLServer(LogConfig, Config.ConnectionString)
Logger.Debug("Database initialized")
If Database.DBInitialized = False Then
Throw New ApplicationException("Database connection could not be established!")

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="GdPicture" version="14.2.89" targetFramework="net48" />
<package id="GdPicture.runtimes.windows" version="14.2.89" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" targetFramework="net462" />
<package id="NLog" version="5.0.5" targetFramework="net461" />
<package id="Quartz" version="3.8.0" targetFramework="net462" />

View File

@@ -18,6 +18,12 @@ namespace EnvelopeGenerator.Web.Controllers
public static string? GetAuthEnvelopeTitle(this ControllerBase controller) => controller.User.FindFirstValue(EnvelopeClaimTypes.Title);
public static int? GetAuthEnvelopeId(this ControllerBase controller)
{
var env_id_str = controller.User.FindFirstValue(EnvelopeClaimTypes.Id);
return int.TryParse(env_id_str, out int env_id) ? env_id : null;
}
//TODO: integrate localizer for ready-to-use views
public static ViewResult ViewError(this Controller controller, ErrorViewModel errorViewModel) => controller.View("_Error", errorViewModel);

View File

@@ -4,6 +4,7 @@ using EnvelopeGenerator.Web.Services;
using EnvelopeGenerator.Application.Contracts;
using Microsoft.AspNetCore.Authorization;
using EnvelopeGenerator.Application;
using EnvelopeGenerator.Extensions;
namespace EnvelopeGenerator.Web.Controllers
{

View File

@@ -6,8 +6,8 @@ using EnvelopeGenerator.Web.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;
using EnvelopeGenerator.Application.DTOs.EnvelopeHistory;
using static EnvelopeGenerator.Common.Constants;
using EnvelopeGenerator.Extensions;
namespace EnvelopeGenerator.Web.Controllers
{

View File

@@ -7,14 +7,17 @@ using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using DigitalData.Core.API;
using EnvelopeGenerator.Application;
using EnvelopeGenerator.Extensions;
using Microsoft.Extensions.Localization;
using DigitalData.Core.DTO;
using Microsoft.AspNetCore.Localization;
using System.Text.Encodings.Web;
using EnvelopeGenerator.Web.Models;
using EnvelopeGenerator.Application.Resources;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using static EnvelopeGenerator.Common.Constants;
using Ganss.Xss;
using Newtonsoft.Json;
using EnvelopeGenerator.Application.DTOs;
namespace EnvelopeGenerator.Web.Controllers
{
@@ -26,29 +29,51 @@ namespace EnvelopeGenerator.Web.Controllers
private readonly IEnvelopeHistoryService _historyService;
private readonly IStringLocalizer<Resource> _localizer;
private readonly IConfiguration _configuration;
private readonly UrlEncoder _urlEncoder;
private readonly HtmlSanitizer _sanitizer;
private readonly Cultures _cultures;
private readonly IEnvelopeMailService _mailService;
public HomeController(EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, UrlEncoder urlEncoder, Cultures cultures, IEnvelopeMailService envelopeMailService)
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
public HomeController(EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, HtmlSanitizer sanitizer, Cultures cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService)
{
this.envelopeOldService = envelopeOldService;
_envRcvService = envelopeReceiverService;
_historyService = historyService;
_localizer = localizer;
_configuration = configuration;
_urlEncoder = urlEncoder;
_sanitizer = sanitizer;
_cultures = cultures;
_mailService = envelopeMailService;
_logger = logger;
}
_readOnlyService = readOnlyService;
}
[HttpGet("EnvelopeKey/{envelopeReceiverId}")]
public async Task<IActionResult> SendAccessCode([FromRoute] string envelopeReceiverId)
public async Task<IActionResult> MainAsync([FromRoute] string envelopeReceiverId, [FromQuery] string? culture = null)
{
try
{
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
//TODO: add a middelware or use an asp.net functionality insead of this code-smell
culture = culture is not null ? _sanitizer.Sanitize(culture) : null;
envelopeReceiverId = _sanitizer.Sanitize(envelopeReceiverId);
if (UserLanguage is null && culture is null)
{
UserLanguage = _cultures.Default.Language;
return Redirect($"{Request.Headers["Referer"]}?culture={_cultures.Default.Language}");
}
envelopeReceiverId = _sanitizer.Sanitize(envelopeReceiverId);
if (!envelopeReceiverId.TryDecode(out var decoded))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return this.ViewDocumentNotFound();
}
if(decoded.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly)
return Redirect($"{envelopeReceiverId}/ReadOnly");
ViewData["EnvelopeKey"] = envelopeReceiverId;
return await _envRcvService.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId: envelopeReceiverId).ThenAsync<EnvelopeReceiverDto, IActionResult>(
@@ -85,22 +110,11 @@ namespace EnvelopeGenerator.Web.Controllers
}
[HttpGet("EnvelopeKey/{envelopeReceiverId}/Locked")]
public async Task<IActionResult> EnvelopeLocked([FromRoute] string envelopeReceiverId, [FromQuery] string? culture = null)
public async Task<IActionResult> EnvelopeLocked([FromRoute] string envelopeReceiverId)
{
try
{
culture = culture is not null ? _urlEncoder.Encode(culture) : null;
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
if (UserLanguage is null && culture is null)
{
UserLanguage = _cultures.Default.Language;
return Redirect($"{Request.Headers["Referer"]}?culture={_cultures.Default.Language}");
}
else if (UserLanguage is not null && culture is not null)
return Redirect($"Locked");
ViewData["UserCulture"] = _cultures[UserLanguage ?? culture];
ViewData["UserCulture"] = _cultures[UserLanguage];
return await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId).ThenAsync(
Success: isExisting => isExisting ? View().WithData("EnvelopeKey", envelopeReceiverId) : this.ViewEnvelopeNotFound(),
@@ -125,7 +139,7 @@ namespace EnvelopeGenerator.Web.Controllers
{
ViewData["UserCulture"] = _cultures[UserLanguage];
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
envelopeReceiverId = _sanitizer.Sanitize(envelopeReceiverId);
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
if (uuid is null || signature is null)
@@ -175,24 +189,23 @@ namespace EnvelopeGenerator.Web.Controllers
if (await _historyService.IsSigned(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress))
return View("EnvelopeSigned");
if (response.Envelope.Documents.Count > 0)
if (er.Envelope.Documents?.FirstOrDefault() is EnvelopeDocumentDto doc && doc.ByteData is not null)
{
var document = await envelopeOldService.GetDocument(response.Envelope.Documents[0].Id, envelopeReceiverId);
byte[] bytes = await envelopeOldService.GetDocumentContents(document);
ViewData["DocumentBytes"] = bytes;
ViewData["DocumentBytes"] = doc.ByteData;
}
else
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: "No document was found.");
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
return this.ViewDocumentNotFound();
}
}
var claims = new List<Claim> {
new(ClaimTypes.NameIdentifier, uuid),
new(ClaimTypes.Hash, signature),
new(ClaimTypes.Name, er.Name ?? string.Empty),
new(ClaimTypes.Email, er.Receiver.EmailAddress),
new(EnvelopeClaimTypes.Title, er.Envelope.Title)
new(EnvelopeClaimTypes.Title, er.Envelope.Title),
new(EnvelopeClaimTypes.Id, er.Envelope.Id.ToString())
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
@@ -232,7 +245,7 @@ namespace EnvelopeGenerator.Web.Controllers
{
try
{
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
envelopeReceiverId = _sanitizer.Sanitize(envelopeReceiverId);
return await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId).ThenAsync(
SuccessAsync: async isExisting =>
{
@@ -244,6 +257,7 @@ namespace EnvelopeGenerator.Web.Controllers
return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked");
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
ViewData["UserCulture"] = _cultures[UserLanguage];
ViewData["EnvelopeKey"] = envelopeReceiverId;
return View();
},
@@ -266,12 +280,12 @@ namespace EnvelopeGenerator.Web.Controllers
{
try
{
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
envelopeReceiverId = _sanitizer.Sanitize(envelopeReceiverId);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return await _envRcvService.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId).ThenAsync(
SuccessAsync: async (er) =>
{
{ViewData["UserCulture"] = _cultures[UserLanguage];
ViewData["UserCulture"] = _cultures[UserLanguage];
return await _historyService.IsRejected(envelopeId: er.EnvelopeId)
? View(er)
@@ -290,7 +304,81 @@ namespace EnvelopeGenerator.Web.Controllers
return this.ViewInnerServiceError();
}
}
[HttpGet("EnvelopeKey/{readOnlyKey}/ReadOnly")]
public async Task<IActionResult> EnvelopeReceiverReadOnly([FromRoute] string readOnlyKey)
{
try
{
ViewData["UserCulture"] = _cultures[UserLanguage];
readOnlyKey = _sanitizer.Sanitize(readOnlyKey);
// check if the readOnlyId is valid
if (!readOnlyKey.TryDecode(out var decodedKeys) || decodedKeys.GetEncodeType() != EncodeType.EnvelopeReceiverReadOnly)
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return this.ViewDocumentNotFound();
}
var readOnlyId = decodedKeys.ParseReadOnlyId();
var erro_res = await _readOnlyService.ReadByIdAsync(readOnlyId);
if (erro_res.IsFailed)
{
_logger.LogNotice(erro_res.Notices);
return this.ViewInnerServiceError();
}
var erro = erro_res.Data;
if (DateTime.Now > erro.DateValid)
return View("EnvelopeExpired");
return await _envRcvService.ReadByUuidSignatureAsync(uuid: erro.Envelope!.Uuid, erro.Receiver!.Signature).ThenAsync(
SuccessAsync: async er =>
{
var envelopeKey = (er.Envelope!.Uuid, er.Receiver!.Signature).EncodeEnvelopeReceiverId();
EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeKey);
//TODO: implement multi-threading to history process (Task)
var hist_res = await _historyService.RecordAsync((int)erro.EnvelopeId, erro.AddedWho, EnvelopeStatus.EnvelopeViewed);
if (hist_res.IsFailed)
{
_logger.LogError(
"Although the envelope was sent as read-only, the EnvelopeShared hisotry could not be saved. ReadOnly-key: {readOnlyKey}\nEnvelope Receiver:\n{envelopeReceiver}",
readOnlyKey, JsonConvert.SerializeObject(er));
_logger.LogNotice(hist_res.Notices);
}
if (er.Envelope.Documents?.FirstOrDefault() is EnvelopeDocumentDto doc && doc.ByteData is not null)
{
ViewData["DocumentBytes"] = doc.ByteData;
ViewData["EnvelopeKey"] = envelopeKey;
ViewData["IsReadOnly"] = true;
ViewData["ReadOnly"] = erro;
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
return View("ShowEnvelope", er);
}
else
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeKey, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
return this.ViewDocumentNotFound();
}
},
Fail: (messages, notices) =>
{
_logger.LogNotice(notices);
return this.ViewEnvelopeNotFound();
});
}
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred while displaying a read-only envelope. Read-only key is {readOnlyKey}. {message}", readOnlyKey, ex.Message);
return this.ViewInnerServiceError();
}
}
[Authorize]
[HttpGet("IsAuthenticated")]
public IActionResult IsAuthenticated()
@@ -305,7 +393,7 @@ namespace EnvelopeGenerator.Web.Controllers
{
try
{
language = _urlEncoder.Encode(language);
language = _sanitizer.Sanitize(language);
if (!_cultures.Languages.Contains(language))
return BadRequest();

View File

@@ -0,0 +1,102 @@
using DigitalData.Core.DTO;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Extensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace EnvelopeGenerator.Web.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ReadOnlyController : ControllerBase
{
private readonly ILogger<ReadOnlyController> _logger;
private readonly IEnvelopeReceiverReadOnlyService _erroService;
private readonly IEnvelopeMailService _mailService;
private readonly IEnvelopeHistoryService _histService;
public ReadOnlyController(ILogger<ReadOnlyController> logger, IEnvelopeReceiverReadOnlyService erroService, IEnvelopeMailService mailService, IEnvelopeHistoryService histService)
{
_logger = logger;
_erroService = erroService;
_mailService = mailService;
_histService = histService;
}
[HttpPost]
[Authorize]
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
{
try
{
//set AddedWho
var authReceiverMail = this.GetAuthReceiverMail();
if (authReceiverMail is null)
{
_logger.LogError("Email clam is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
return Unauthorized();
}
var envelopeId = this.GetAuthEnvelopeId();
if (envelopeId is null)
{
_logger.LogError("Envelope Id clam is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
return Unauthorized();
}
createDto.AddedWho = authReceiverMail;
createDto.EnvelopeId = envelopeId;
// create entity
var creation_res = await _erroService.CreateAsync(createDto: createDto);
if (creation_res.IsFailed)
{
_logger.LogNotice(creation_res);
return StatusCode(StatusCodes.Status500InternalServerError);
}
//read new entity
var read_res = await _erroService.ReadByIdAsync(creation_res.Data);
if (read_res.IsFailed)
{
_logger.LogNotice(creation_res);
return StatusCode(StatusCodes.Status500InternalServerError);
}
var new_erro = read_res.Data;
//send email two receiver
return await _mailService.SendAsync(new_erro).ThenAsync<int, IActionResult>(SuccessAsync: async res =>
{
//TODO: implement multi-threading to history process (Task)
//TODO: remove casting after change the id type
var hist_res = await _histService.RecordAsync((int)createDto.EnvelopeId, createDto.AddedWho, Common.Constants.EnvelopeStatus.EnvelopeShared);
if (hist_res.IsFailed)
{
_logger.LogError("Although the envelope was sent as read-only, the EnvelopeShared hisotry could not be saved. Create DTO:\n{createDto}", JsonConvert.SerializeObject(createDto));
_logger.LogNotice(hist_res.Notices);
}
return Ok();
},
Fail: (msg, ntc) =>
{
_logger.LogNotice(ntc);
return StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
}

View File

@@ -1,8 +1,8 @@
using EnvelopeGenerator.Application;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.AspNetCore.Mvc;
using EnvelopeGenerator.Extensions;
namespace EnvelopeGenerator.Web.Controllers.Test
{

View File

@@ -1,7 +1,7 @@
using DigitalData.Core.API;
using DigitalData.Core.DTO;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application;
using EnvelopeGenerator.Extensions;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.AspNetCore.Mvc;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
@@ -40,13 +40,27 @@ namespace EnvelopeGenerator.Web.Controllers.Test
}
[HttpGet("decode")]
public IActionResult DecodeEnvelopeReceiverId(string envelopeReceiverId)
public IActionResult DecodeEnvelopeReceiverId(string envelopeReceiverId, bool isReadOnly = false)
{
var decoded = envelopeReceiverId.DecodeEnvelopeReceiverId();
return Ok(new { uuid = decoded.EnvelopeUuid, signature = decoded.ReceiverSignature });
if (isReadOnly)
{
var readOnlyId = envelopeReceiverId.DecodeEnvelopeReceiverReadOnlyId();
return Ok(new { readOnlyId });
}
else
{
var (EnvelopeUuid, ReceiverSignature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
return Ok(new { uuid = EnvelopeUuid, signature = ReceiverSignature });
}
}
[HttpGet("encode")]
public IActionResult EncodeEnvelopeReceiverId(string uuid, string signature) => Ok((uuid, signature).EncodeEnvelopeReceiverId());
public IActionResult EncodeEnvelopeReceiverId(string? uuid = null, string? signature = null, long? readOnlyId = null)
{
if(readOnlyId is long readOnlyId_long)
return Ok(readOnlyId_long.EncodeEnvelopeReceiverId());
else
return Ok((uuid ?? string.Empty, signature ?? string.Empty).EncodeEnvelopeReceiverId());
}
}
}

View File

@@ -9,5 +9,10 @@
/// Claim type for the title of an envelope.
/// </summary>
public static readonly string Title = $"Envelope{nameof(Title)}";
/// <summary>
/// Claim type for the ID of an envelope.
/// </summary>
public static readonly string Id = $"Envelope{nameof(Id)}";
}
}

View File

@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PackageId>EnvelopeGenerator.Web</PackageId>
<Version>2.0.0.0</Version>
<Version>2.4.0.0</Version>
<Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company>
<Product>EnvelopeGenerator.Web</Product>
@@ -13,10 +13,30 @@
<PackageTags>digital data envelope generator web</PackageTags>
<Description>EnvelopeGenerator.Web is an ASP.NET MVC application developed to manage signing processes. It uses Entity Framework Core (EF Core) for database operations. The user interface for signing processes is developed with Razor View Engine (.cshtml files) and JavaScript under wwwroot, integrated with PSPDFKit. This integration allows users to view and sign documents seamlessly.</Description>
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>2.0.0.0</FileVersion>
<AssemblyVersion>2.4.0.0</AssemblyVersion>
<FileVersion>2.4.0.0</FileVersion>
<Copyright>Copyright © 2024 Digital Data GmbH. All rights reserved.</Copyright>
</PropertyGroup>
<ItemGroup>
<Compile Remove="wwwroot\lib\leaflet\build\**" />
<Compile Remove="wwwroot\lib\leaflet\debug\**" />
<Compile Remove="wwwroot\lib\leaflet\spec\**" />
<Compile Remove="wwwroot\lib\leaflet\src\**" />
<Content Remove="wwwroot\lib\leaflet\build\**" />
<Content Remove="wwwroot\lib\leaflet\debug\**" />
<Content Remove="wwwroot\lib\leaflet\spec\**" />
<Content Remove="wwwroot\lib\leaflet\src\**" />
<EmbeddedResource Remove="wwwroot\lib\leaflet\build\**" />
<EmbeddedResource Remove="wwwroot\lib\leaflet\debug\**" />
<EmbeddedResource Remove="wwwroot\lib\leaflet\spec\**" />
<EmbeddedResource Remove="wwwroot\lib\leaflet\src\**" />
<None Remove="wwwroot\lib\leaflet\build\**" />
<None Remove="wwwroot\lib\leaflet\debug\**" />
<None Remove="wwwroot\lib\leaflet\spec\**" />
<None Remove="wwwroot\lib\leaflet\src\**" />
</ItemGroup>
<ItemGroup>
<Content Remove="bundleconfig.json" />
</ItemGroup>
@@ -33,6 +53,17 @@
<ItemGroup>
<None Include="bundleconfig.json" />
<None Include="wwwroot\lib\leaflet-locationpicker\dist\leaflet-locationpicker.min.js" />
<None Include="wwwroot\lib\leaflet-locationpicker\dist\leaflet-locationpicker.src.js" />
<None Include="wwwroot\lib\leaflet-locationpicker\Gruntfile.js" />
<None Include="wwwroot\lib\leaflet-locationpicker\index.tmpl" />
<None Include="wwwroot\lib\leaflet-locationpicker\LICENSE" />
<None Include="wwwroot\lib\leaflet-locationpicker\README.md" />
<None Include="wwwroot\lib\leaflet\dist\leaflet-src.js" />
<None Include="wwwroot\lib\leaflet\dist\leaflet.js" />
<None Include="wwwroot\lib\leaflet\LICENSE" />
<None Include="wwwroot\lib\leaflet\PLUGIN-GUIDE.md" />
<None Include="wwwroot\lib\leaflet\README.md" />
</ItemGroup>
<ItemGroup>
@@ -70,6 +101,7 @@
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Common\EnvelopeGenerator.Common.vbproj" />
<ProjectReference Include="..\EnvelopeGenerator.Extensions\EnvelopeGenerator.Extensions.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
</ItemGroup>

View File

@@ -83,7 +83,7 @@ try
builder.Services.AddDbContext<EGDbContext>(options => options.UseSqlServer(connStr));
// Add envelope generator services
builder.Services.AddEnvelopeGenerator();
builder.Services.AddEnvelopeGenerator(config);
builder.Services.Configure<CookiePolicyOptions>(options =>
{
@@ -130,8 +130,6 @@ try
builder.Services.AddSingleton(config.GetSection("ContactLink").Get<ContactLink>() ?? new());
builder.Services.AddCookieConsentSettings();
builder.Services.AddCookieBasedLocalizer();
builder.Services.AddSingleton(HtmlEncoder.Default);
@@ -157,8 +155,6 @@ try
builder.Services.AddMemoryCache();
builder.ConfigureBySection<DispatcherConfig>();
builder.ConfigureBySection<Logo>();
var app = builder.Build();

View File

@@ -0,0 +1,24 @@
@{
ViewData["Title"] = "Abgelaufen";
}
<div class="page container p-5">
<header class="text-center">
<div class="icon expired">
<svg width="72" height="72" viewBox="0 0 48 48" version="1" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 48 48">
<circle fill="#00ACC1" cx="17" cy="17" r="14" />
<circle fill="#eee" cx="17" cy="17" r="11" />
<rect x="16" y="8" width="2" height="9" />
<rect x="18.2" y="16" transform="matrix(-.707 .707 -.707 -.707 46.834 19.399)" width="2.4" height="6.8" />
<circle cx="17" cy="17" r="2" />
<circle fill="#00ACC1" cx="17" cy="17" r="1" />
<path fill="#FFC107" d="M11.9,42l14.4-24.1c0.8-1.3,2.7-1.3,3.4,0L44.1,42c0.8,1.3-0.2,3-1.7,3H13.6C12.1,45,11.1,43.3,11.9,42z" />
<path fill="#263238" d="M26.4,39.9c0-0.2,0-0.4,0.1-0.6s0.2-0.3,0.3-0.5s0.3-0.2,0.5-0.3s0.4-0.1,0.6-0.1s0.5,0,0.7,0.1 s0.4,0.2,0.5,0.3s0.2,0.3,0.3,0.5s0.1,0.4,0.1,0.6s0,0.4-0.1,0.6s-0.2,0.3-0.3,0.5s-0.3,0.2-0.5,0.3s-0.4,0.1-0.7,0.1 s-0.5,0-0.6-0.1s-0.4-0.2-0.5-0.3s-0.2-0.3-0.3-0.5S26.4,40.1,26.4,39.9z M29.2,36.8h-2.3L26.5,27h3L29.2,36.8z" />
</svg>
</div>
<h1>Abgelaufen!</h1>
</header>
<section class="text-center">
<p>Der Zeitraum für die gemeinsame Nutzung von Dokumenten ist abgelaufen.</p>
</section>
</div>
<footer class="container" id="page-footer">&copy; SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></footer>

View File

@@ -1,8 +1,6 @@
@{
var nonce = _accessor.HttpContext?.Items["csp-nonce"] as string;
var logo = _logoOpt.Value;
}
@{
ViewData["Title"] = _localizer[WebKey.DocProtected];
var userCulture = ViewData["UserCulture"] as Culture;
}
@@ -23,62 +21,32 @@
<section class="text-center">
<p>@_localizer[WebKey.LockedBody]</p>
</section>
<div class="row m-0 p-0 justify-content-center">
<div class="col-8 pe-0">
<form id="form-access-code" class="form" method="post">
<div class="input">
<label class="visually-hidden" for="access_code">@_localizer[WebKey.LockedTitle]</label>
<div class="row m-0 p-0">
<div class="access-code-panel justify-content-center align-items-center p-0 m-0">
<form id="form-access-code" class="form form-floating mb-0" method="post">
<div class="form-floating access-code-form-floating">
<input type="password" id="access_code" class="form-control" name="access_code" placeholder="@_localizer[WebKey.LockedAccessCode]" required="required">
<div id="access-code-error-message" class="text-danger" style="height: 20px;">
@if (ViewData["ErrorMessage"] is string errMsg)
{
@_sanitizer.Sanitize(errMsg)
}
</div>
</div>
<div class="button">
<button type="submit" class="btn btn-primary">@_localizer[WebKey.LocakedOpen]</button>
<label for="access_code">@_localizer[WebKey.LockedAccessCode]</label>
<button type="submit" class="btn btn-primary">
<span class="material-symbols-outlined">
login
</span>
</button>
</div>
</form>
</div>
<div class="col-4 mb-3 d-flex justify-content-center align-items-center">
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="langDropdownMenuButton" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fi @userCulture?.FIClass.TrySanitize(_sanitizer) me-2" id="selectedFlag"></span><span id="selectedLanguage"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="langDropdownMenuButton">
@foreach(var culture in _cultures)
{
var lang = culture.Language;
var info = culture.Info;
<li>
<a class="dropdown-item" data-language="@lang.TrySanitize(_sanitizer)" data-flag="@_cultures[lang]?.FIClass.TrySanitize(_sanitizer)">
<span class="fi @_cultures[lang]?.FIClass.TrySanitize(_sanitizer) me-2"></span>@info?.Parent.NativeName
</a>
</li>
}
</ul>
</div>
</div>
</div>
<section class="text-center">
@if (ViewData["ErrorMessage"] is string errMsg)
{
<div id="access-code-error-message" class="alert alert-danger row" role="alert">
@_sanitizer.Sanitize(errMsg)
</div>
}
<section class="no-receiver-explanation text-center">
<details>
<summary>@_localizer[WebKey.LockedFooterTitle]</summary>
<p>@_localizer[WebKey.LockedFooterBody]</p>
</details>
</section>
</div>
<script nonce="@nonce">
document.addEventListener('DOMContentLoaded', function () {
var dropdownItems = document.querySelectorAll('.dropdown-item');
dropdownItems.forEach(function (item) {
item.addEventListener('click', async function(event) {
event.preventDefault();
var language = this.getAttribute('data-language');
var flagCode = this.getAttribute('data-flag');
document.getElementById('selectedFlag').className = 'fi ' + flagCode + ' me-2';
await setLanguage(language);
});
});
});
</script>
</div>

View File

@@ -9,7 +9,6 @@
@using Newtonsoft.Json
@using Newtonsoft.Json.Serialization
@model EnvelopeReceiverDto;
<partial name="_CookieConsentPartial" />
@{
var userCulture = ViewData["UserCulture"] as Culture;
var envelope = Model.Envelope;

View File

@@ -5,101 +5,147 @@
@using DigitalData.Core.DTO;
@using EnvelopeGenerator.Application.DTOs;
@using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver
@using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly
@using Newtonsoft.Json
@using Newtonsoft.Json.Serialization
@model EnvelopeReceiverDto;
@{
ViewData["Title"] = _localizer[WebKey.SignDoc];
}
<partial name="_CookieConsentPartial" />
@{
var userCulture = ViewData["UserCulture"] as Culture;
var envelope = Model.Envelope;
var receiver_name = Model.Name;
var document = Model.Envelope?.Documents?.FirstOrDefault();
var sender = Model.Envelope?.User;
var pages = document?.Elements?.Select(e => e.Page) ?? Array.Empty<int>();
int? signatureCount = document?.Elements?.Count();
var stPageIndexes = string.Join(pages.Count() > 1 ? ", " : "", pages.Take(pages.Count() - 1))
+ (pages.Count() > 1 ? $" {_localizer[WebKey.and].TrySanitize(_sanitizer)} " : "") + pages.LastOrDefault();
var isReadOnly = false;
if (ViewData["IsReadOnly"] is bool isReadOnly_bool)
isReadOnly = isReadOnly_bool;
ViewData["Title"] = isReadOnly ? _localizer[WebKey.ViewDoc] : _localizer[WebKey.SignDoc];
}
<div class="d-flex flex-column min-vh-100">
<nav class="navbar navbar-light bg-light">
<div class="container">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggleExternalContent" aria-controls="navbarToggleExternalContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="material-symbols-outlined">
more_vert
</span>
</button>
<div class="envelope-message">
<span class="icon material-symbols-outlined">history_edu</span>
<span class="message navbar-brand">@($"{_localizer[WebKey.Hello]} {Model.Name}, {@envelope?.Message}".TrySanitize(_sanitizer))</span>
</div>
<div class="logo">
<img class="@logo.ShowPageClass" src="@logo.Src" alt="logo">
</div>
</div>
</nav>
<div class="collapse show bg-light " id="navbarToggleExternalContent" data-bs-theme="light">
<div class="card sender-card p-1 mb-3">
<div class="row g-0">
<div class="col p-0 m-0">
<div class="card-body p-0 m-0 ms-4">
<h5 class="card-title p-0 m-0">
<span class="signature-process-title">@($"{_localizer[WebKey.SigningProcessTitle]}: ".TrySanitize(_sanitizer))</span>
<span class="signature-process-name">@($"{envelope?.Title}".TrySanitize(_sanitizer))</span>
</h5>
<p class="card-text p-0 m-0">@Html.Raw(string.Format(_localizer[WebKey.EnvelopeInfo1], pages.Count(), stPageIndexes).TrySanitize(_hlSanitizer))</p>
<p class="card-text p-0 m-0">
<small class="text-body-secondary">
@Html.Raw(string.Format(_localizer[WebKey.EnvelopeInfo2], /* sanitize separately but don't sanitize the URI */
envelope?.AddedWhen.ToString(userCulture?.Info?.DateTimeFormat).TrySanitize(_sanitizer),
$"{sender?.Prename} {sender?.Name}".TrySanitize(_sanitizer),
sender?.Email.TrySanitize(_sanitizer),
envelope?.Title.TrySanitize(_sanitizer),
sender?.Prename.TrySanitize(_sanitizer),
sender?.Name.TrySanitize(_sanitizer),
sender?.Email.TrySanitize(_sanitizer)))
</small>
</p>
</div>
</div>
</div>
</div>
</div>
<div class="envelope-view">
@if (!isReadOnly)
{
<div id="flex-action-panel" class="btn-group btn_group position-fixed bottom-0 end-0 d-flex align-items-center" role="group" aria-label="Basic mixed styles example">
<button class="btn_complete btn btn-primary" type="button">
<button class="btn_complete btn btn-primary btn-desktop" type="button">
<svg class="icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 16">
<path d="m10.036 8.278 9.258-7.79A1.979 1.979 0 0 0 18 0H2A1.987 1.987 0 0 0 .641.541l9.395 7.737Z" />
<path d="M11.241 9.817c-.36.275-.801.425-1.255.427-.428 0-.845-.138-1.187-.395L0 2.6V14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2.5l-8.759 7.317Z" />
</svg>
<span>@_localizer[WebKey.Complete]</span>
</button>
<button class="btn_reject btn btn-danger" type="button">
<button class="btn_reject btn btn-danger btn-desktop" type="button">
<svg width="25px" height="25px" viewBox="43.5 43.5 512 512" version="1.1" fill="currentColor" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path class="st0" d="M263.24,43.5c-117.36,0-212.5,95.14-212.5,212.5s95.14,212.5,212.5,212.5s212.5-95.14,212.5-212.5 S380.6,43.5,263.24,43.5z M367.83,298.36c17.18,17.18,17.18,45.04,0,62.23v0c-17.18,17.18-45.04,17.18-62.23,0l-42.36-42.36 l-42.36,42.36c-17.18,17.18-45.04,17.18-62.23,0v0c-17.18-17.18-17.18-45.04,0-62.23L201.01,256l-42.36-42.36 c-17.18-17.18-17.18-45.04,0-62.23v0c17.18-17.18,45.04-17.18,62.23,0l42.36,42.36l42.36-42.36c17.18-17.18,45.04-17.18,62.23,0v0 c17.18,17.18,17.18,45.04,0,62.23L325.46,256L367.83,298.36z" />
</svg>
<span>@_localizer[WebKey.Reject]</span>
</button>
<button class="btn_refresh btn btn-secondary" type="button">
<button class="btn_refresh btn btn-secondary btn-desktop" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
</svg>
</button>
</div>
<div id='app' class="flex-grow-1"></div>
}
<div class="dd-cards-container">
<div class="dd-card">
<div class="dd-card-preview">
<img src="~/img/sign_flow_horizontal.svg" class="app-logo">
@if (!isReadOnly)
{
<div class="progress-container">
<div id="signed-count-bar" class="progress"></div>
<span class="progress-text">
<span id="signed-count">0</span>/<span id="signature-count">@signatureCount</span> Unterschriften
</span>
</div>
}
</div>
<div class="dd-card-info">
<div class="logo">
<img class="@logo.ShowPageClass" src="@logo.Src" alt="logo">
</div>
<h2>@($"{envelope?.Title}".TrySanitize(_sanitizer))</h2>
@if (isReadOnly)
{
var dateTimeSt = string.Empty;
if (ViewData["ReadOnly"] is EnvelopeReceiverReadOnlyDto readOnly)
dateTimeSt = readOnly.DateValid.ToLongDateString();
<h6>@string.Format(_localizer["ReadOnlyMessage"], receiver_name, dateTimeSt)</h6>
}
else
{
<h6>@($"{@envelope?.Message}".TrySanitize(_sanitizer))</h6>
}
<p>
<small class="text-body-secondary">
@Html.Raw(string.Format(_localizer[WebKey.EnvelopeInfo2], /* sanitize separately but don't sanitize the URI */
envelope?.AddedWhen.ToString(userCulture?.Info?.DateTimeFormat).TrySanitize(_sanitizer),
$"{sender?.Prename} {sender?.Name}".TrySanitize(_sanitizer),
sender?.Email.TrySanitize(_sanitizer),
envelope?.Title.TrySanitize(_sanitizer),
sender?.Prename.TrySanitize(_sanitizer),
sender?.Name.TrySanitize(_sanitizer),
sender?.Email.TrySanitize(_sanitizer)))
</small>
</p>
</div>
</div>
</div>
@if (!isReadOnly)
{
<div class="modal fade" id="shareBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="shareBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<small class="modal-title text-body-secondary" id="shareBackdropLabel">Geben Sie hier den Empfänger ein, mit welchem Sie das Dokument teilen wollen:</small>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="input-group mb-3">
<span class="input-group-text">E-Mail</span>
<input type="text" class="form-control email-input" placeholder="user@mail.com" id="readonly-receiver-mail" aria-label="">
</div>
<div class="input-group">
<span class="input-group-text">Gültig bis</span>
<input type="date" name="expiration" class="form-control" lang="de" id="readonly-date-valid" onkeydown="return false;" onclick="this.showPicker()"
min="@DateTime.Today.AddDays(1).ToString("yyyy-MM-dd")"
max="@DateTime.Today.AddDays(90).ToString("yyyy-MM-dd")"
value="@DateTime.Today.AddDays(7).ToString("yyyy-MM-dd")">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="readonly-send">
<span class="material-symbols-outlined">
send
</span>
</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="locationBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="locationBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input id="geoloc" type="text" value="" size="20" />
<div id="fixedMapCont"></div>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
}
<div id='app'></div>
</div>
<script nonce="@nonce">
const collapseNav = () => {
document.addEventListener('click', function (event) {
var navbarToggle = document.getElementById('navbarToggleExternalContent');
var navbarButton = document.querySelector('[data-bs-target="#navbarToggleExternalContent"]');
var isCollapsed = new bootstrap.Collapse(navbarToggle)._isTransitioning;
if (!navbarToggle.contains(event.target) && !navbarButton.contains(event.target) && !isCollapsed) {
new bootstrap.Collapse(navbarToggle).hide();
}
});
}
@if (ViewData["DocumentBytes"] is byte[] documentBytes)
{
var settings = new JsonSerializerSettings

View File

@@ -3,9 +3,13 @@
@using Newtonsoft.Json.Serialization
@{
var nonce = _accessor.HttpContext?.Items["csp-nonce"] as string;
var userCulture = ViewData["UserCulture"] as Culture;
var isReadOnly = false;
if (ViewData["IsReadOnly"] is bool isReadOnly_bool)
isReadOnly = isReadOnly_bool;
}
<!DOCTYPE html>
<html lang="en">
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
@@ -14,12 +18,15 @@
<link rel="stylesheet" href="~/lib/sweetalert2/sweetalert2.min.css" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/logo.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/card.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/EnvelopeGenerator.Web.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="~/lib/flag-icons-main/css/flag-icons.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/location-picker.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/lib/alertifyjs/css/alertify.min.css" />
<link rel="stylesheet" href="~/lib/alertifyjs/css/themes/default.min.css" />
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
<link rel="stylesheet" href="~/lib/leaflet/dist/leaflet.css" />
<link rel="stylesheet" href="~/lib/leaflet-locationpicker/dist/leaflet-locationpicker.src.css" />
</head>
<body>
<style>
@@ -27,23 +34,35 @@
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24
}
</style>
<script nonce="@nonce">
@if (ViewData["EnvelopeKey"] is string envelopeKey)
{
<script nonce="@nonce">const ENV_KEY = "@envelopeKey.TrySanitize(_sanitizer)"</script>
@: const ENV_KEY = "@envelopeKey.TrySanitize(_sanitizer)"
}
const IS_READONLY = @isReadOnly.ToString().ToLower();
const DEVICE_SCREEN_TYPE = window.innerWidth <= 768 ? 'mobile' : window.innerWidth <= 1024 ? 'tablet' : 'desktop';
const IS_DESKTOP_SIZE = DEVICE_SCREEN_TYPE == 'desktop'
const IS_MOBILE_DEVICE = /Mobi|Android/i.test(window.navigator.userAgent);
</script>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/popper/dist/umd/popper.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="~/lib/sweetalert2/sweetalert2.min.js"></script>
<script src="~/lib/alertifyjs/alertify.min.js"></script>
<script src="~/js/util.min.js" asp-append-version="true"></script>
<script src="~/js/ui.min.js" asp-append-version="true"></script>
<script src="~/js/annotation.js" asp-append-version="true"></script>
<script src="~/js/network.min.js" asp-append-version="true"></script>
<script src="~/js/app.min.js" asp-append-version="true"></script>
<script src="~/lib/pspdfkit/dist-2024.3.2/pspdfkit.js"></script>
<script src="~/lib/bootstrap-cookie-consent-settings-main/bootstrap-cookie-consent-settings.js" asp-append-version="true"></script>
<script src="~/js/util.min.js" asp-append-version="true"></script>
<script src="~/js/api-service.min.js" asp-append-version="true"></script>
<script src="~/lib/leaflet/dist/leaflet.js"></script>
<script src="~/lib/leaflet-locationpicker/dist/leaflet-locationpicker.min.js"></script>
@await RenderSectionAsync("Scripts", required: false)
@{
var settings = new JsonSerializerSettings
@@ -61,6 +80,26 @@
</main>
<script src="~/js/event-binder.min.js" asp-append-version="true"></script>
@Html.AntiForgeryToken()
<footer>&copy; SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></footer>
<footer>
<span>&copy; SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></span>
<div class="dropup flag-dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="langDropdownMenuButton" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fi @userCulture?.FIClass.TrySanitize(_sanitizer) me-2" id="selectedFlag"></span><span id="selectedLanguage"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="langDropdownMenuButton">
@foreach (var culture in _cultures)
{
var lang = culture.Language;
var info = culture.Info;
<li>
<a class="dropdown-item culture-dropdown-item" data-language="@lang.TrySanitize(_sanitizer)" data-flag="@_cultures[lang]?.FIClass.TrySanitize(_sanitizer)">
<span class="fi @_cultures[lang]?.FIClass.TrySanitize(_sanitizer) me-2"></span>@info?.Parent.NativeName
</a>
</li>
}
</ul>
</div>
<a href="/privacy-policy.de-DE.html">Datenschutz</a>
</footer>
</body>
</html>

View File

@@ -1,6 +1,7 @@
@using EnvelopeGenerator.Web
@using EnvelopeGenerator.Web.Models
@using EnvelopeGenerator.Web.Sanitizers
@using EnvelopeGenerator.Extensions
@using Microsoft.Extensions.Localization
@using EnvelopeGenerator.Application.Resources
@using Microsoft.Extensions.Options

View File

@@ -34,5 +34,6 @@
public static readonly string RejectionInfo2_ext = nameof(RejectionInfo2_ext);
public static readonly string SigningProcessTitle = nameof(SigningProcessTitle);
public static readonly string WelcomeToTheESignPortal = nameof(WelcomeToTheESignPortal);
public static readonly string ViewDoc = nameof(ViewDoc);
}
}

View File

@@ -5,5 +5,7 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
},
"AdminPassword": "dd",
"UseCSPInDev": false
}

View File

@@ -14,9 +14,7 @@
"ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
},
"AdminPassword": "dd",
"PSPDFKitLicenseKey": "SXCtGGY9XA-31OGUXQK-r7c6AkdLGPm2ljuyDr1qu0kkhLvydg-Do-fxpNUF4Rq3fS_xAnZRNFRHbXpE6sQ2BMcCSVTcXVJO6tPviexjpiT-HnrDEySlUERJnnvh-tmeOWprxS6BySPnSILkmaVQtUfOIUS-cUbvvEYHTvQBKbSF8di4XHQFyfv49ihr51axm3NVV3AXwh2EiKL5C5XdqBZ4sQ4O7vXBjM2zvxdPxlxdcNYmiU83uAzw7B83O_jubPzya4CdUHh_YH7Nlp2gP56MeG1Sw2JhMtfG3Rj14Sg4ctaeL9p6AEWca5dDjJ2li5tFIV2fQSsw6A_cowLu0gtMm5i8IfJXeIcQbMC2-0wGv1oe9hZYJvFMdzhTM_FiejM0agemxt3lJyzuyP8zbBSOgp7Si6A85krLWPZptyZBTG7pp7IHboUHfPMxCXqi-zMsqewOJtQBE2mjntU-lPryKnssOpMPfswwQX7QSkJYV5EMqNmEhQX6mEkp2wcqFzMC7bJQew1aO4pOpvChUaMvb1vgRek0HxLag0nwQYX2YrYGh7F_xXJs-8HNwJe8H0-eW4x4faayCgM5rB5772CCCsD9ThZcvXFrjNHHLGJ8WuBUFm6LArvSfFQdii_7j-_sqHMpeKZt26NFgivj1A==",
"UseCSPInDev": true,
"Content-Security-Policy": [ // The first format parameter {0} will be replaced by the nonce value.
"default-src 'self'",
"script-src 'self' 'nonce-{0}' 'unsafe-eval'",
@@ -32,7 +30,7 @@
"NLog": {
"throwConfigExceptions": true,
"variables": {
"logDirectory": "E:\\EnvelopeGenerator\\Logs",
"logDirectory": "E:\\LogFiles\\Digital Data\\signFlow",
"logFileNamePrefix": "${shortdate}-ECM.EnvelopeGenerator.Web"
},
"targets": {
@@ -77,21 +75,6 @@
"Audience": null,
"Key": "8RGnd7x0G2TYLOIW4m_qlIls7MfbAIGNrpQJzMAUIvULHOLiG723znRa_MG-Z4yw3SErusOU4hTui2rVBMcCaQ"
},
"CookieConsentSettings": {
"PrivacyPolicyUrl": "./privacy-policy.en.html",
"LegalNoticeUrl": "./cookies-policy.en.html",
"ContentURL": "/cookie-consent-content",
"ButtonAgreeClass": "btn btn-primary",
"ButtonDontAgreeClass": "btn btn-link text-decoration-none none-display",
"ButtonSaveClass": "btn btn-secondary none-display",
"Lang": "de",
"DefaultLang": "en",
"CookieName": "cookie-consent-settings",
"CookieStorageDays": 1,
"ModalId": "bootstrapCookieConsentSettingsModal",
"AlsoUseLocalStorage": false,
"Categories": [ "necessary" ]
},
"ContactLink": {
"Label": "Kontakt",
"Href": "https://digitaldata.works/",
@@ -113,12 +96,6 @@
}
],
"DisableMultiLanguage": false,
"DispatcherConfig": {
"SendingProfile": 1,
"AddedWho": "DDEnvelopGenerator",
"ReminderTypeId": 202377,
"EmailAttmt1": ""
},
"Regexes": [
{
"Pattern": "/^\\p{L}+(?:([\\ \\-\\']|(\\.\\ ))\\p{L}+)*$/u",
@@ -135,5 +112,18 @@
"Src": "/img/digital_data.svg",
"ShowPageClass": "dd-show-logo",
"LockedPageClass": "dd-locked-logo"
},
"DispatcherConfig": {
"SendingProfile": 1,
"AddedWho": "DDEnvelopGenerator",
"ReminderTypeId": 202377,
"EmailAttmt1": ""
},
"MailConfig": {
"Placeholders": {
"[NAME_PORTAL]": "signFlow",
"[SIGNATURE_TYPE]": "signieren",
"[REASON]": ""
}
}
}

View File

@@ -76,5 +76,17 @@
"inputFiles": [
"wwwroot/css/privacy-policy.css"
]
},
{
"outputFileName": "wwwroot/css/card.min.css",
"inputFiles": [
"wwwroot/css/card.css"
]
},
{
"outputFileName": "wwwroot/css/location-picker.min.css",
"inputFiles": [
"wwwroot/css/location-picker.css"
]
}
]

View File

@@ -0,0 +1,126 @@
@import url('https://fonts.googleapis.com/css?family=Muli&display=swap');
* {
box-sizing: border-box;
}
.dd-cards-container {
font-family: 'Muli', sans-serif;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.dd-card {
background-color: #fff;
display: flex;
max-width: 100%;
margin: 0;
overflow: hidden;
width: 100%;
}
.dd-card h6 {
opacity: 0.6;
margin: 0;
letter-spacing: 0.0625rem;
font-size: clamp(0.6rem, 1.33vw, 0.8rem);
}
.dd-card h2 {
letter-spacing: 0.0625rem;
margin: 0;
font-size: clamp(1rem, 2.67vw, 1.67rem);
}
.uppercase {
text-transform: uppercase;
}
.dd-card-preview {
background-color: #2A265F;
color: #fff;
padding: 0.1rem 1rem 1rem 1rem;
margin:0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.dd-card-preview a {
color: #fff;
opacity: 0.6;
}
.dd-card-preview button {
border-width: 0;
align-items: center;
background-color: transparent;
color: white;
margin: 15% 0 0 0;
transition: color 0.25s ease;
}
.dd-card-preview button:hover {
color: #bebebe;
}
.dd-card-preview span {
}
.dd-card-info {
padding: clamp(0.55rem, 2vw, .875rem) 0 0 clamp(0.55rem, 2.5vw, 1.7rem);
margin: 0;
position: relative;
width: 100%;
}
.dd-card-info p, .dd-card-info a, .dd-card-info small, .dd-card-info span {
opacity: 0.65;
font-size: clamp(0.55rem, 1.23vw, 0.75rem);
margin: clamp(0.55rem, 2vw, .875rem) 0 clamp(0.55rem, 2vw, .875rem) 0;
text-decoration: none;
padding: 0;
}
.app-logo {
width: clamp(4rem, 10vw, 5rem);
margin: 1rem 0 0 0;
}
.progress-container {
text-align: right;
width: 100%;
margin: clamp(0.8rem, 2vw, 1rem) 0 0 0;
}
.progress {
background-color: #ddd;
border-radius: 0.1875rem;
height: clamp(0.25rem, 1vw, 0.3125rem);
width: clamp(6rem, 20vw, 10rem);
position: relative;
}
.progress::after {
background-color: #2A265F;
content: '';
position: absolute;
top: 0;
left: 0;
height: clamp(0.25rem, 1vw, 0.3125rem);
width: var(--progress-width, 1%);
transition: width 1s ease;
opacity: 0.85;
}
.progress-text {
font-size: clamp(0.5rem, 1.5vw, 0.625rem);
opacity: 0.6;
letter-spacing: clamp(0.05rem, 0.5vw, 0.0625rem);
text-align: left;
}

View File

@@ -0,0 +1 @@
@import url('https://fonts.googleapis.com/css?family=Muli&display=swap');*{box-sizing:border-box}.dd-cards-container{font-family:'Muli',sans-serif;display:flex;align-items:center;justify-content:center;flex-direction:column}.dd-card{background-color:#fff;display:flex;max-width:100%;margin:0;overflow:hidden;width:100%}.dd-card h6{opacity:.6;margin:0;letter-spacing:.0625rem;font-size:clamp(.6rem,1.33vw,.8rem)}.dd-card h2{letter-spacing:.0625rem;margin:0;font-size:clamp(1rem,2.67vw,1.67rem)}.uppercase{text-transform:uppercase}.dd-card-preview{background-color:#2a265f;color:#fff;padding:.1rem 1rem 1rem 1rem;margin:0;display:flex;flex-direction:column;justify-content:center;align-items:center}.dd-card-preview a{color:#fff;opacity:.6}.dd-card-preview button{border-width:0;align-items:center;background-color:transparent;color:#fff;margin:15% 0 0 0;transition:color .25s ease}.dd-card-preview button:hover{color:#bebebe}.dd-card-info{padding:clamp(.55rem,2vw,.875rem) 0 0 clamp(.55rem,2.5vw,1.7rem);margin:0;position:relative;width:100%}.dd-card-info p,.dd-card-info a,.dd-card-info small,.dd-card-info span{opacity:.65;font-size:clamp(.55rem,1.23vw,.75rem);margin:clamp(.55rem,2vw,.875rem) 0 clamp(.55rem,2vw,.875rem) 0;text-decoration:none;padding:0}.app-logo{width:clamp(4rem,10vw,5rem);margin:1rem 0 0 0}.progress-container{text-align:right;width:100%;margin:clamp(.8rem,2vw,1rem) 0 0 0}.progress{background-color:#ddd;border-radius:.1875rem;height:clamp(.25rem,1vw,.3125rem);width:clamp(6rem,20vw,10rem);position:relative}.progress::after{background-color:#2a265f;content:'';position:absolute;top:0;left:0;height:clamp(.25rem,1vw,.3125rem);width:var(--progress-width,1%);transition:width 1s ease;opacity:.85}.progress-text{font-size:clamp(.5rem,1.5vw,.625rem);opacity:.6;letter-spacing:clamp(.05rem,.5vw,.0625rem);text-align:left}

View File

@@ -0,0 +1,9 @@
#fixedMapCont {
height: 100%;
width: 100%;
}
#locationBackdrop .modal-body {
padding: 0;
margin: 0;
}

View File

@@ -0,0 +1 @@
#fixedMapCont{height:100%;width:100%}#locationBackdrop .modal-body{padding:0;margin:0}

View File

@@ -5,6 +5,11 @@
.dd-show-logo {
width: 9rem;
position: absolute;
right: 0;
margin: 0 2rem 0 0;
padding: 0;
top:0;
}
.cursor-locked-logo {
@@ -19,6 +24,7 @@
@media (max-width: 767px) {
.dd-show-logo {
width: 5rem;
margin-right: 0rem;
}
.cursor-show-logo {

View File

@@ -1 +1 @@
.dd-locked-logo{width:13rem;padding-top:1rem}.dd-show-logo{width:9rem}.cursor-locked-logo{width:9rem;padding-top:1rem}.cursor-show-logo{width:6rem}@media(max-width:767px){.dd-show-logo{width:5rem}.cursor-show-logo{width:3rem}}
.dd-locked-logo{width:13rem;padding-top:1rem}.dd-show-logo{width:9rem;position:absolute;right:0;margin:0 2rem 0 0;padding:0;top:0}.cursor-locked-logo{width:9rem;padding-top:1rem}.cursor-show-logo{width:6rem}@media(max-width:767px){.dd-show-logo{width:5rem;margin-right:0}.cursor-show-logo{width:3rem}}

View File

@@ -1,5 +1,5 @@
body {
font-family: Arial, sans-serif;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.6;
margin: 1.25rem;
background-color: #f4f4f4;

View File

@@ -1 +1 @@
body{font-family:Arial,sans-serif;line-height:1.6;margin:1.25rem;background-color:#f4f4f4}header{text-align:center;margin:0 10rem 3rem 10rem;box-shadow:0 .25rem .5rem rgba(0,0,0,.1)}h1{color:#333}h2{color:#0056b3}section{background-color:#fff;padding:1.25rem;border-radius:.5rem;box-shadow:0 .25rem .5rem rgba(0,0,0,.1);margin:0 10rem 3rem 10rem}ul{list-style:disc inside}a{color:#0056b3;text-decoration:none}a:hover{text-decoration:underline}
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.6;margin:1.25rem;background-color:#f4f4f4}header{text-align:center;margin:0 10rem 3rem 10rem;box-shadow:0 .25rem .5rem rgba(0,0,0,.1)}h1{color:#333}h2{color:#0056b3}section{background-color:#fff;padding:1.25rem;border-radius:.5rem;box-shadow:0 .25rem .5rem rgba(0,0,0,.1);margin:0 10rem 3rem 10rem}ul{list-style:disc inside}a{color:#0056b3;text-decoration:none}a:hover{text-decoration:underline}

View File

@@ -4,22 +4,20 @@
*/
/* Toolbar Buttons */
#app {
background: gray;
width: 100vw;
height: 80vh;
}
.navbar-toggler {
border: 0;
}
.material-symbols-outlined {
align-content: center;
}
.btn-group {
margin-right: 10vw;
margin-bottom: 10vh;
}
.btn_refresh, .btn_reject, .btn_complete {
height:2.5rem;
}
@@ -33,44 +31,94 @@
}
.button-finish {
transition: background-color linear 300ms;
background-color: #059669; /* emerald-600 */
color: white;
border-left: none;
color: #fff;
background-color: #0d6efd;
border-color: #0d6efd;
}
.button-finish:hover, .button-finish:focus, .button-finish:active {
background-color: #10b981; /* emerald-500 */
color: white;
.button-finish:hover {
color: #fff;
background-color: #0b5ed7;
border-color: #0a58ca;
}
.button-finish:focus {
box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5);
}
.button-finish:active {
color: #fff;
background-color: #0a58ca;
border-color: #0a53be;
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.button-finish:disabled {
color: #fff;
background-color: #0d6efd;
border-color: #0d6efd;
}
.button-reject {
transition: background-color linear 300ms;
background-color: #d97706; /* amber-600 */
color: white;
border-left: none;
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.button-reject:hover, .button-reject:focus, .button-reject:active {
background-color: #f59e0b; /* amber-500 */
color: white;
.button-reject:hover {
color: #fff;
background-color: #bb2d3b;
border-color: #b02a37;
}
.button-reject:focus {
box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5);
}
.button-reject:active {
color: #fff;
background-color: #b02a37;
border-color: #a52834;
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.button-reject:disabled {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.button-reset {
transition: background-color linear 300ms;
background-color: #2563eb; /* blue-600 */
color: white;
border-left: none;
color: #fff;
background-color: #6c757d;
border-color: #6c757d;
}
.button-reset:hover, .button-reset:focus, .button-reset:active {
background-color: #3b82f6; /* blue-500 */
color: white;
.button-reset:hover {
color: #fff;
background-color: #5c636a;
border-color: #565e64;
}
.button-reset:focus {
box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5);
}
.button-reset:active {
color: #fff;
background-color: #565e64;
border-color: #51585e;
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.button-reset:disabled {
color: #fff;
background-color: #6c757d;
border-color: #6c757d;
}
body {
background-color: #bbb;
background: #f8fcfc;
display: flex;
flex-direction: column;
height: 100vh;
@@ -78,16 +126,65 @@ body {
}
main {
flex: 1;
display:flex;
margin: 0 0 0.5vh 0;
}
.envelope-view {
display: flex; /* d-flex */
flex-direction: column; /* flex-column */
width: 100vw;
height: 95.9vh;
}
#app {
background: gray;
width: 100vw;
height: 100%;
flex-grow: 1;
border-width: 0;
}
footer {
background: #333;
color: white;
text-align: center;
padding: 10px 0;
height: 4vh;
min-height: 1.5rem;
background-color: #001F61;
border-radius: 10px 10px 0 0;
color: #fff;
font-family: 'Muli';
padding: 0.5vh 0;
position: fixed;
bottom: 0;
width: 100%;
z-index: 998;
border-width: 0;
font-size: clamp(0.58rem, 1.5vw, 1rem);
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
footer * {
margin-left: clamp(0.5rem, 2vw, 1rem);
}
footer a {
color: #FF7500;
text-decoration: none;
}
footer .dropdown-toggle, footer .flag-dropdown, footer li {
margin: 0;
padding: 0;
border-width: 0;
}
footer .dropdown-menu a {
padding: 0.25rem 1rem 0.25rem 1rem;
margin-left: 0;
user-select: none;
}
.page {
margin-top: 3rem;
@@ -129,6 +226,11 @@ footer {
color: #fff;
}
.page header .icon.expired {
background-color: rgba(228, 216, 213, 0.5);
color: #fff;
}
.page .form {
max-width: 30rem;
margin: 2rem auto;
@@ -281,13 +383,44 @@ footer#page-footer {
}
#form-access-code {
margin-left: 5rem;
justify-content: space-evenly;
}
.access-code-form-floating {
display: flex;
justify-content: start;
flex-direction: row;
}
.access-code-form-floating button {
align-content: center;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
.access-code-form-floating input {
align-content: center;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
border-right-width: 0;
width: 7rem;
}
#access-code-error-message {
justify-content: center;
align-content: center;
margin: 1.5rem 7rem 0 7rem;
height: 2.5rem;
}
/*.flag-dropdown button {
height: 100%;
}*/
.header-1 {
align-items: center;
justify-content: space-between;
margin-top:0;
margin-top: 0;
padding-top: 0;
}
@@ -298,6 +431,30 @@ footer#page-footer {
padding-top: 0;
}
.no-receiver-explanation {
padding: 2.5rem;
}
.ajs-message.ajs-custom {
margin: 0rem 0rem 0rem 0rem;
padding: 0rem 0rem 0rem 0rem;
width:50rem;
}
.ajs-message.ajs-custom .alert {
display: flex;
flex-direction: row;
}
.ajs-message.ajs-custom span {
margin: 0 1rem 0 0;
}
.ajs-message.ajs-custom p {
margin: 0;
padding: 0;
}
/* styles for mobile responsiveness */
@media (max-height: 850px) {
.navbar .container {
@@ -406,6 +563,12 @@ footer#page-footer {
}
}
@media (max-width: 1024px) {
#flex-action-panel, .btn-desktop {
display: none;
}
}
@media (max-height: 600px) {
.collapse {
height: 4rem;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256">
<defs>
<style>
.cls-1 {
fill: #ffd631;
}
.cls-1, .cls-2, .cls-3, .cls-4 {
stroke-width: 0px;
}
.cls-2 {
fill: #a52431;
}
.cls-3 {
fill: none;
}
.cls-4 {
fill: #FFFFFF;
}
.cls-5 {
clip-path: url(#clippath);
opacity: 0;
}
</style>
<clipPath id="clippath">
<rect class="cls-3" x="0" width="256" height="256"/>
</clipPath>
</defs>
<g class="cls-5">
<rect class="cls-2" x="0" width="256" height="256" rx="48.67" ry="48.67"/>
</g>
<path class="cls-1" d="M153.29,63.22l-20.08,28.03h-10.42l-20.08-28.03,8.74-5.57h33.09l8.74,5.57ZM89.25,71.8l19.69,27.49-11.88,14.56-14.5-37.78,6.69-4.26ZM104.33,128.96l17.33-21.24h12.67l17.33,21.24-23.67,19.17-23.67-19.17ZM158.94,113.85l-11.88-14.56,19.69-27.49,6.69,4.26-14.5,37.78ZM149.16,42.03v-.03h-42.32v.03s-43.57,27.76-43.57,27.76l.28.44,25.76,67.11,38.62,30.89.06.07h.01s.01,0,.01,0l.05-.07,38.62-30.89,25.76-67.11.28-.44-43.57-27.76Z"/>
<path class="cls-1" d="M191.6,222.14h5.32l4.9-17.08,4.83,17.08h5.35l5.39-24.5h-5.04l-3.33,16.56-4.76-16.56h-4.79l-4.76,16.56-3.33-16.56h-5.21l5.43,24.5ZM171.79,217.87c-4.38,0-7.74-3.47-7.74-7.98s3.36-7.98,7.74-7.98,7.77,3.43,7.77,7.98-3.36,7.98-7.77,7.98M171.79,222.52c7.28,0,12.95-5.56,12.95-12.64s-5.67-12.63-12.95-12.63-12.95,5.56-12.95,12.63,5.71,12.64,12.95,12.64M139.3,222.14h18.48v-4.48h-13.4v-20.02h-5.07v24.5ZM115.9,222.14h5.07v-10.04h9.21v-4.27h-9.21v-5.78h13.86v-4.41h-18.93v24.5Z"/>
<path class="cls-4" d="M92.79,222.14h4.8v-12.63c.84-1.16,2.13-1.82,3.74-1.82,2.31,0,3.78,1.51,3.78,3.85v10.6h4.79v-11.31c0-4.31-2.97-7.28-7.21-7.28-2,0-3.75.63-5.11,1.82v-1.47h-4.8v18.24ZM78.07,218.18c-2.97,0-5.25-2.31-5.25-5.29s2.31-5.18,5.28-5.18c1.68,0,3.19.63,4.24,1.65v7.14c-1.05,1.08-2.52,1.68-4.27,1.68M78.1,229.63c5.85,0,9.03-2.69,9.03-7.52v-18.2h-4.72v1.19c-1.44-.94-3.15-1.47-4.94-1.47-5.25,0-9.38,4.09-9.38,9.27s4.1,9.35,9.24,9.35c1.82,0,3.54-.59,5-1.65v1.44c0,2.41-1.47,3.64-4.34,3.64-1.93,0-3.82-.45-5.81-1.44l-1.68,3.71c2.13,1.08,4.79,1.68,7.59,1.68M58.75,222.14h4.8v-18.24h-4.8v18.24ZM61.13,201.73c1.51,0,2.73-1.26,2.73-2.73s-1.22-2.73-2.73-2.73-2.73,1.23-2.73,2.73,1.22,2.73,2.73,2.73M46.42,222.49c4.58,0,7.98-2.55,7.98-6.02,0-2.84-2-4.69-5.57-5.22l-3.5-.53c-1.78-.24-2.49-.74-2.49-1.75s1.16-1.78,2.87-1.78c1.85,0,3.64.56,5.63,1.78l2.31-3.12c-2.28-1.5-4.83-2.27-7.49-2.27-4.65,0-7.59,2.27-7.59,5.77,0,2.94,1.92,4.8,5.53,5.32l3.5.52c1.58.24,2.27.77,2.27,1.72,0,1.19-1.4,1.96-3.53,1.96-1.92,0-3.78-.66-5.91-2.1l-2.38,3.19c2.06,1.64,5.04,2.52,8.37,2.52"/>
<rect class="cls-3" x="0" width="256" height="256"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,5 @@
<svg width="199" height="95" viewBox="0 0 199 95" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M67.515 15.8772L52.455 36.8498H44.64L29.58 15.8772L36.135 11.7096H60.9525L67.5075 15.8772H67.515ZM19.485 22.2969L34.2525 42.8654L25.3425 53.7595L14.4675 25.4918L19.485 22.3044V22.2969ZM30.795 65.0651L43.7925 49.1729H53.295L66.2925 65.0651L48.54 79.4084L30.7875 65.0651H30.795ZM71.7525 53.7595L62.8425 42.8654L77.61 22.2969L82.6275 25.4843L71.7525 53.752V53.7595ZM64.4175 0.0224456V0H32.6775V0.0224456L0 20.793L0.209999 21.1222L19.53 71.3352L48.495 94.4476L48.54 94.5H48.5475H48.555L48.5925 94.4476L77.5575 71.3352L96.8775 21.1222L97.0875 20.793L64.41 0.0224456H64.4175Z" fill="#FFD631"/>
<path d="M180.74 34.5908H185.54V22.0971C186.38 20.9496 187.67 20.2968 189.28 20.2968C191.59 20.2968 193.06 21.7905 193.06 24.1052V34.5908H197.85V23.4029C197.85 19.1394 194.88 16.2014 190.64 16.2014C188.64 16.2014 186.89 16.8246 185.53 18.0018V16.5477H180.73V34.5908H180.74ZM166.02 30.6735C163.05 30.6735 160.77 28.3885 160.77 25.4406C160.77 22.4928 163.08 20.3165 166.05 20.3165C167.73 20.3165 169.24 20.9397 170.29 21.9487V29.0117C169.24 30.08 167.77 30.6735 166.02 30.6735ZM166.05 42C171.9 42 175.08 39.339 175.08 34.5611V16.5576H170.36V17.7347C168.92 16.8049 167.21 16.2806 165.42 16.2806C160.17 16.2806 156.04 20.3264 156.04 25.4505C156.04 30.5746 160.14 34.6996 165.28 34.6996C167.1 34.6996 168.82 34.116 170.28 33.0674V34.4919C170.28 36.8759 168.81 38.0926 165.94 38.0926C164.01 38.0926 162.12 37.6475 160.13 36.6682L158.45 40.3381C160.58 41.4065 163.24 42 166.04 42M146.7 34.5908H151.5V16.5477H146.7V34.5908ZM149.08 14.4011C150.59 14.4011 151.81 13.1547 151.81 11.7005C151.81 10.2464 150.59 9 149.08 9C147.57 9 146.35 10.2167 146.35 11.7005C146.35 13.1843 147.57 14.4011 149.08 14.4011ZM134.37 34.9371C138.95 34.9371 142.35 32.4146 142.35 28.982C142.35 26.1727 140.35 24.3426 136.78 23.8183L133.28 23.2941C131.5 23.0567 130.79 22.562 130.79 21.5629C130.79 20.5638 131.95 19.8022 133.66 19.8022C135.51 19.8022 137.3 20.3561 139.29 21.5629L141.6 18.4766C139.32 16.9928 136.77 16.2311 134.11 16.2311C129.46 16.2311 126.52 18.4766 126.52 21.9389C126.52 24.8471 128.44 26.687 132.05 27.2014L135.55 27.7158C137.13 27.9532 137.82 28.4775 137.82 29.4173C137.82 30.5944 136.42 31.3561 134.29 31.3561C132.37 31.3561 130.51 30.7032 128.38 29.2788L126 32.4344C128.06 34.0567 131.04 34.9271 134.37 34.9271" fill="white"/>
<path d="M172.7 84.6241H178.02L182.92 67.7265L187.75 84.6241H193.1L198.49 60.3858H193.45L190.12 76.7689L185.36 60.3858H180.57L175.81 76.7689L172.48 60.3858H167.27L172.7 84.6241ZM152.89 80.3997C148.51 80.3997 145.15 76.9668 145.15 72.5049C145.15 68.0431 148.51 64.6102 152.89 64.6102C157.27 64.6102 160.66 68.0036 160.66 72.5049C160.66 77.0063 157.3 80.3997 152.89 80.3997ZM152.89 85C160.17 85 165.84 79.4994 165.84 72.4951C165.84 65.4907 160.17 60 152.89 60C145.61 60 139.94 65.5006 139.94 72.4951C139.94 79.4895 145.65 85 152.89 85ZM120.4 84.6241H138.88V80.1919H125.48V60.3858H120.41V84.6241H120.4ZM97 84.6241H102.07V74.6913H111.28V70.467H102.07V64.7487H115.93V60.3858H97V84.6241Z" fill="#FFD631"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -102,8 +102,8 @@
const formFieldCity = new PSPDFKit.FormFields.TextFormField({
name: id_city,
annotationIds: PSPDFKit.Immutable.List([annotation_city.id]),
value: isMobile() ? location.city : "",
readOnly: isMobile()
value: IS_MOBILE_DEVICE ? location.city : "",
readOnly: IS_MOBILE_DEVICE
})
this.markFieldAsRequired(formFieldCity);
@@ -253,6 +253,7 @@
}
static async createAnnotationFrameBlob(receiverName, receiverSignature, timestamp, width, height) {
Comp.SignatureProgress.SignedCount += 1;
const canvas = document.createElement('canvas')
const scale = 4
const fontSize = 10

View File

@@ -1,25 +1,29 @@
class Content {
static get JSON () {
static get JSON() {
return 'application/json';
}
}
class API {
static get REJECT_URL () {
static get REJECT_URL() {
return `/api/envelope/reject`;
}
static get REJECT_REDIR_URL(){
static get REJECT_REDIR_URL() {
return `/envelopekey/${API.ENV_KEY}/rejected`;
}
static get SHARE_URL() {
return `/api/readonly`
}
static __XSRF_TOKEN
static get XSRF_TOKEN() {
API.__XSRF_TOKEN ??= document.getElementsByName('__RequestVerificationToken')[0].value;
return API.__XSRF_TOKEN;
}
static get ENV_KEY(){
static get ENV_KEY() {
return ENV_KEY ?? document.querySelector('meta[name="env-key"]').getAttribute('content');
}
}
@@ -28,20 +32,20 @@ const submitForm = async form => await fetch(form.action, {
method: form.method,
body: new FormData(form),
headers: {
"X-Requested-With": "XMLHttpRequest"
"X-Requested-With": "XMLHttpRequest"
}
})
const createRequest = async (method, url, body, contentType) => {
return fetch(url, {
credentials: 'include',
method: method,
headers: {
'Content-Type': contentType,
'X-XSRF-TOKEN': API.XSRF_TOKEN
},
body: JSON.stringify(body)
})
return fetch(url, {
credentials: 'include',
method: method,
headers: {
'Content-Type': contentType,
'X-XSRF-TOKEN': API.XSRF_TOKEN
},
body: JSON.stringify(body)
})
}
const createPost = (url, body, contentType) => createRequest('POST', url, body, contentType);
@@ -50,4 +54,6 @@ const rejectEnvelope = (reason) => createPost(API.REJECT_URL, reason, Content.JS
const redirect = (url) => window.location.href = url;
const redirRejected = () => redirect(API.REJECT_REDIR_URL);
const redirRejected = () => redirect(API.REJECT_REDIR_URL);
const shareEnvelope = (receiverMail, dateValid) => createPost(API.SHARE_URL, { receiverMail: receiverMail, dateValid: dateValid }, Content.JSON);

View File

@@ -1,2 +1,2 @@
class Content{static get JSON(){return"application/json"}}class API{static get REJECT_URL(){return`/api/envelope/reject`}static get REJECT_REDIR_URL(){return`/envelopekey/${API.ENV_KEY}/rejected`}static __XSRF_TOKEN
static get XSRF_TOKEN(){return API.__XSRF_TOKEN??=document.getElementsByName("__RequestVerificationToken")[0].value,API.__XSRF_TOKEN}static get ENV_KEY(){return ENV_KEY??document.querySelector('meta[name="env-key"]').getAttribute("content")}}const submitForm=async n=>await fetch(n.action,{method:n.method,body:new FormData(n),headers:{"X-Requested-With":"XMLHttpRequest"}}),createRequest=async(n,t,i,r)=>fetch(t,{credentials:"include",method:n,headers:{"Content-Type":r,"X-XSRF-TOKEN":API.XSRF_TOKEN},body:JSON.stringify(i)}),createPost=(n,t,i)=>createRequest("POST",n,t,i),rejectEnvelope=n=>createPost(API.REJECT_URL,n,Content.JSON),redirect=n=>window.location.href=n,redirRejected=()=>redirect(API.REJECT_REDIR_URL);
class Content{static get JSON(){return"application/json"}}class API{static get REJECT_URL(){return`/api/envelope/reject`}static get REJECT_REDIR_URL(){return`/envelopekey/${API.ENV_KEY}/rejected`}static get SHARE_URL(){return`/api/readonly`}static __XSRF_TOKEN
static get XSRF_TOKEN(){return API.__XSRF_TOKEN??=document.getElementsByName("__RequestVerificationToken")[0].value,API.__XSRF_TOKEN}static get ENV_KEY(){return ENV_KEY??document.querySelector('meta[name="env-key"]').getAttribute("content")}}const submitForm=async n=>await fetch(n.action,{method:n.method,body:new FormData(n),headers:{"X-Requested-With":"XMLHttpRequest"}}),createRequest=async(n,t,i,r)=>fetch(t,{credentials:"include",method:n,headers:{"Content-Type":r,"X-XSRF-TOKEN":API.XSRF_TOKEN},body:JSON.stringify(i)}),createPost=(n,t,i)=>createRequest("POST",n,t,i),rejectEnvelope=n=>createPost(API.REJECT_URL,n,Content.JSON),redirect=n=>window.location.href=n,redirRejected=()=>redirect(API.REJECT_REDIR_URL),shareEnvelope=(n,t)=>createPost(API.SHARE_URL,{receiverMail:n,dateValid:t},Content.JSON);

View File

@@ -48,7 +48,7 @@ class App {
// Load PSPDFKit
this.Instance = await UI.loadPSPDFKit(arrayBuffer, this.container, this.licenseKey, this.locale)
UI.configurePSPDFKit(this.Instance, this.handleClick.bind(this))
UI.addToolbarItems(this.Instance, this.handleClick.bind(this))
this.Instance.addEventListener(
'annotations.load',
@@ -87,6 +87,7 @@ class App {
//add click events of external buttons
[...document.getElementsByClassName('btn_refresh')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('RESET')));
[...document.getElementsByClassName('btn_complete')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('FINISH')));
[...document.getElementsByClassName('btn_reject')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('REJECT')));
}
handleAnnotationsLoad(loadedAnnotations) {
@@ -141,7 +142,7 @@ class App {
switch (eventType) {
case 'RESET':
result = await this.handleReset(null)
Comp.SignatureProgress.SignedCount = 0;
if (result.isConfirmed) {
Swal.fire({
title: 'Erfolg',
@@ -149,8 +150,7 @@ class App {
icon: 'info',
})
}
break
break;
case 'FINISH':
result = await this.handleFinish(null)
@@ -159,23 +159,74 @@ class App {
// Redirect to success page after saving to database
window.location.href = `/EnvelopeKey/${this.envelopeKey}/Success`
}
break
break;
case 'REJECT':
alert('Dokument abgelent!')
Swal.fire({
title: localized.rejection,
html: `<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,
icon: "question",
input: "text",
inputAttributes: {
autocapitalize: "off"
},
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: localized.complete,
cancelButtonText: localized.back,
showLoaderOnConfirm: true,
preConfirm: async (reason) => {
try {
var res = await rejectEnvelope(reason);
return res;
} catch (error) {
Swal.showValidationMessage(`
Request failed: ${error}
`);
}
},
allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
if (!result.isConfirmed)
return;
const res = result.value;
if (res.ok) {
redirRejected()
}
else
Swal.showValidationMessage(`Request failed: ${res.message}`);
});
break;
case 'COPY_URL':
const url = window.location.href.replace(/\/readonly/gi, '');
navigator.clipboard.writeText(url).then(function () {
bsNotify('Kopiert', { alert_type: 'success', delay: 4, icon_name: 'check_circle' });
}).catch(function (err) {
bsNotify('Unerwarteter Fehler', { alert_type: 'danger', delay: 4, icon_name: 'error' });
});
break;
case 'SHARE':
// Show the modal
Comp.ShareBackdrop.show();
break;
case 'LOCATION':
// Show the modal
Comp.LocationBackdrop.show();
break;
}
}
async handleFinish(event) {
const iJSON = await this.Instance.exportInstantJSON()
const iFormFieldValues = await iJSON.formFieldValues;
//check required
const iReqFields = iFormFieldValues.filter(f => Annotation.isFieldRequired(f))
const hasEmptyReq = iReqFields.some(f => (f.value === undefined || f.value === null || f.value === ""))
if (hasEmptyReq){
if (hasEmptyReq) {
Swal.fire({
title: 'Warnung',
text: 'Bitte füllen Sie alle Standortinformationen vollständig aus!',
@@ -187,8 +238,8 @@ class App {
//check city
const city_regex = new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$")
const iCityFields = iFormFieldValues.filter(f => Annotation.isCityField(f))
for(var f of iCityFields)
if(!city_regex.test(f.value)){
for (var f of iCityFields)
if (!IS_MOBILE_DEVICE && !city_regex.test(f.value)) {
Swal.fire({
title: 'Warnung',
text: `Bitte überprüfen Sie die eingegebene Ortsangabe "${f.value}" auf korrekte Formatierung. Beispiele für richtige Formate sind: München, Île-de-France, Sauðárkrókur, San Francisco, St. Catharines usw.`,

View File

@@ -1 +1,3 @@
const ActionType={Created:0,Saved:1,Sent:2,EmailSent:3,Delivered:4,Seen:5,Signed:6,Rejected:7};class App{constructor(n,t,i,r,u,f){this.container=f??`#${this.constructor.name.toLowerCase()}`;this.envelopeKey=n;this.Network=new Network;this.Instance=null;this.currentDocument=null;this.currentReceiver=null;this.signatureCount=0;this.envelopeReceiver=t;this.documentBytes=i;this.licenseKey=r;this.locale=u}async init(){this.currentDocument=this.envelopeReceiver.envelope.documents[0];this.currentReceiver=this.envelopeReceiver.receiver;const n=this.documentBytes;if(n.fatal||n.error)return Swal.fire({title:"Fehler",text:"Dokument konnte nicht geladen werden!",icon:"error"});const t=this.documentBytes;this.Instance=await UI.loadPSPDFKit(t,this.container,this.licenseKey,this.locale);UI.configurePSPDFKit(this.Instance,this.handleClick.bind(this));this.Instance.addEventListener("annotations.load",this.handleAnnotationsLoad.bind(this));this.Instance.addEventListener("annotations.change",this.handleAnnotationsChange.bind(this));this.Instance.addEventListener("annotations.create",this.handleAnnotationsCreate.bind(this));this.Instance.addEventListener("annotations.willChange",()=>{Comp.ActPanel.Toggle()});try{this.signatureCount=this.currentDocument.elements.length;await Annotation.createAnnotations(this.currentDocument,this.Instance);const n=await this.Network.openDocument(this.envelopeKey);if(n.fatal||n.error)return Swal.fire({title:"Fehler",text:"Umschlag konnte nicht geöffnet werden!",icon:"error"})}catch(i){}[...document.getElementsByClassName("btn_refresh")].forEach(n=>n.addEventListener("click",()=>this.handleClick("RESET")));[...document.getElementsByClassName("btn_complete")].forEach(n=>n.addEventListener("click",()=>this.handleClick("FINISH")))}handleAnnotationsLoad(n){n.toJS()}handleAnnotationsChange(){}async handleAnnotationsCreate(n){const t=n.toJS()[0],i=!!t.formFieldName,r=!!t.isSignature;if(i===!1&&r===!0){const r=t.boundingBox.left-20,u=t.boundingBox.top-20,n=150,i=75,f=new Date,e=await Annotation.createAnnotationFrameBlob(this.envelopeReceiver.name,this.currentReceiver.signature,f,n,i),o=await fetch(e),s=await o.blob(),h=await this.Instance.createAttachment(s),c=Annotation.createImageAnnotation(new PSPDFKit.Geometry.Rect({left:r,top:u,width:n,height:i}),t.pageIndex,h);this.Instance.create(c)}}async handleClick(n){let t=!1;switch(n){case"RESET":t=await this.handleReset(null);t.isConfirmed&&Swal.fire({title:"Erfolg",text:"Dokument wurde zurückgesetzt",icon:"info"});break;case"FINISH":t=await this.handleFinish(null);t==!0&&(window.location.href=`/EnvelopeKey/${this.envelopeKey}/Success`);break;case"REJECT":alert("Dokument abgelent!")}}async handleFinish(){const n=await this.Instance.exportInstantJSON(),t=await n.formFieldValues,r=t.filter(n=>Annotation.isFieldRequired(n)),u=r.some(n=>n.value===undefined||n.value===null||n.value==="");if(u)return Swal.fire({title:"Warnung",text:"Bitte füllen Sie alle Standortinformationen vollständig aus!",icon:"warning"}),!1;const f=new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$"),e=t.filter(n=>Annotation.isCityField(n));for(var i of e)if(!f.test(i.value))return Swal.fire({title:"Warnung",text:`Bitte überprüfen Sie die eingegebene Ortsangabe "${i.value}" auf korrekte Formatierung. Beispiele für richtige Formate sind: München, Île-de-France, Sauðárkrókur, San Francisco, St. Catharines usw.`,icon:"warning"}),!1;const o=await this.validateAnnotations(this.signatureCount);return o===!1?(Swal.fire({title:"Warnung",text:"Es wurden nicht alle Signaturfelder ausgefüllt!",icon:"warning"}),!1):Swal.fire({title:localized.confirmation,html:`<div class="text-start fs-6 p-0 m-0">${localized.sigAgree}</div>`,icon:"question",showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.finalize,cancelButtonText:localized.back}).then(async t=>{if(t.isConfirmed){try{await this.Instance.save()}catch(i){return Swal.fire({title:"Fehler",text:"Umschlag konnte nicht signiert werden!",icon:"error"}),!1}try{const i=await n,t=await this.Network.postEnvelope(this.envelopeKey,this.currentDocument.id,i);return t.fatal?(Swal.fire({title:"Fehler",text:"Umschlag konnte nicht signiert werden!",icon:"error"}),!1):t.error?(Swal.fire({title:"Warnung",text:"Umschlag ist nicht mehr verfügbar.",icon:"warning"}),!1):!0}catch(i){return!1}}else return!1})}async validateAnnotations(n){const t=await Annotation.getAnnotations(this.Instance),i=t.map(n=>n.toJS()).filter(n=>n.isSignature);return n>i.length?!1:!0}async handleReset(){const n=await Swal.fire({title:"Sind sie sicher?",text:"Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?",icon:"question",showCancelButton:!0});if(n.isConfirmed){const n=await Annotation.deleteAnnotations(this.Instance)}return n}}
const ActionType={Created:0,Saved:1,Sent:2,EmailSent:3,Delivered:4,Seen:5,Signed:6,Rejected:7};class App{constructor(n,t,i,r,u,f){this.container=f??`#${this.constructor.name.toLowerCase()}`;this.envelopeKey=n;this.Network=new Network;this.Instance=null;this.currentDocument=null;this.currentReceiver=null;this.signatureCount=0;this.envelopeReceiver=t;this.documentBytes=i;this.licenseKey=r;this.locale=u}async init(){this.currentDocument=this.envelopeReceiver.envelope.documents[0];this.currentReceiver=this.envelopeReceiver.receiver;const n=this.documentBytes;if(n.fatal||n.error)return Swal.fire({title:"Fehler",text:"Dokument konnte nicht geladen werden!",icon:"error"});const t=this.documentBytes;this.Instance=await UI.loadPSPDFKit(t,this.container,this.licenseKey,this.locale);UI.addToolbarItems(this.Instance,this.handleClick.bind(this));this.Instance.addEventListener("annotations.load",this.handleAnnotationsLoad.bind(this));this.Instance.addEventListener("annotations.change",this.handleAnnotationsChange.bind(this));this.Instance.addEventListener("annotations.create",this.handleAnnotationsCreate.bind(this));this.Instance.addEventListener("annotations.willChange",()=>{Comp.ActPanel.Toggle()});try{this.signatureCount=this.currentDocument.elements.length;await Annotation.createAnnotations(this.currentDocument,this.Instance);const n=await this.Network.openDocument(this.envelopeKey);if(n.fatal||n.error)return Swal.fire({title:"Fehler",text:"Umschlag konnte nicht geöffnet werden!",icon:"error"})}catch(i){}[...document.getElementsByClassName("btn_refresh")].forEach(n=>n.addEventListener("click",()=>this.handleClick("RESET")));[...document.getElementsByClassName("btn_complete")].forEach(n=>n.addEventListener("click",()=>this.handleClick("FINISH")));[...document.getElementsByClassName("btn_reject")].forEach(n=>n.addEventListener("click",()=>this.handleClick("REJECT")))}handleAnnotationsLoad(n){n.toJS()}handleAnnotationsChange(){}async handleAnnotationsCreate(n){const t=n.toJS()[0],i=!!t.formFieldName,r=!!t.isSignature;if(i===!1&&r===!0){const r=t.boundingBox.left-20,u=t.boundingBox.top-20,n=150,i=75,f=new Date,e=await Annotation.createAnnotationFrameBlob(this.envelopeReceiver.name,this.currentReceiver.signature,f,n,i),o=await fetch(e),s=await o.blob(),h=await this.Instance.createAttachment(s),c=Annotation.createImageAnnotation(new PSPDFKit.Geometry.Rect({left:r,top:u,width:n,height:i}),t.pageIndex,h);this.Instance.create(c)}}async handleClick(n){let t=!1;switch(n){case"RESET":t=await this.handleReset(null);Comp.SignatureProgress.SignedCount=0;t.isConfirmed&&Swal.fire({title:"Erfolg",text:"Dokument wurde zurückgesetzt",icon:"info"});break;case"FINISH":t=await this.handleFinish(null);t==!0&&(window.location.href=`/EnvelopeKey/${this.envelopeKey}/Success`);break;case"REJECT":Swal.fire({title:localized.rejection,html:`<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,icon:"question",input:"text",inputAttributes:{autocapitalize:"off"},showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.complete,cancelButtonText:localized.back,showLoaderOnConfirm:!0,preConfirm:async n=>{try{return await rejectEnvelope(n)}catch(t){Swal.showValidationMessage(`
Request failed: ${t}
`)}},allowOutsideClick:()=>!Swal.isLoading()}).then(n=>{if(n.isConfirmed){const t=n.value;t.ok?redirRejected():Swal.showValidationMessage(`Request failed: ${t.message}`)}});break;case"COPY_URL":const n=window.location.href.replace(/\/readonly/gi,"");navigator.clipboard.writeText(n).then(function(){bsNotify("Kopiert",{alert_type:"success",delay:4,icon_name:"check_circle"})}).catch(function(){bsNotify("Unerwarteter Fehler",{alert_type:"danger",delay:4,icon_name:"error"})});break;case"SHARE":Comp.ShareBackdrop.show();break;case"LOCATION":Comp.LocationBackdrop.show()}}async handleFinish(){const n=await this.Instance.exportInstantJSON(),t=await n.formFieldValues,r=t.filter(n=>Annotation.isFieldRequired(n)),u=r.some(n=>n.value===undefined||n.value===null||n.value==="");if(u)return Swal.fire({title:"Warnung",text:"Bitte füllen Sie alle Standortinformationen vollständig aus!",icon:"warning"}),!1;const f=new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$"),e=t.filter(n=>Annotation.isCityField(n));for(var i of e)if(!IS_MOBILE_DEVICE&&!f.test(i.value))return Swal.fire({title:"Warnung",text:`Bitte überprüfen Sie die eingegebene Ortsangabe "${i.value}" auf korrekte Formatierung. Beispiele für richtige Formate sind: München, Île-de-France, Sauðárkrókur, San Francisco, St. Catharines usw.`,icon:"warning"}),!1;const o=await this.validateAnnotations(this.signatureCount);return o===!1?(Swal.fire({title:"Warnung",text:"Es wurden nicht alle Signaturfelder ausgefüllt!",icon:"warning"}),!1):Swal.fire({title:localized.confirmation,html:`<div class="text-start fs-6 p-0 m-0">${localized.sigAgree}</div>`,icon:"question",showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.finalize,cancelButtonText:localized.back}).then(async t=>{if(t.isConfirmed){try{await this.Instance.save()}catch(i){return Swal.fire({title:"Fehler",text:"Umschlag konnte nicht signiert werden!",icon:"error"}),!1}try{const i=await n,t=await this.Network.postEnvelope(this.envelopeKey,this.currentDocument.id,i);return t.fatal?(Swal.fire({title:"Fehler",text:"Umschlag konnte nicht signiert werden!",icon:"error"}),!1):t.error?(Swal.fire({title:"Warnung",text:"Umschlag ist nicht mehr verfügbar.",icon:"warning"}),!1):!0}catch(i){return!1}}else return!1})}async validateAnnotations(n){const t=await Annotation.getAnnotations(this.Instance),i=t.map(n=>n.toJS()).filter(n=>n.isSignature);return n>i.length?!1:!0}async handleReset(){const n=await Swal.fire({title:"Sind sie sicher?",text:"Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?",icon:"question",showCancelButton:!0});if(n.isConfirmed){const n=await Annotation.deleteAnnotations(this.Instance)}return n}}

View File

@@ -1,40 +1,105 @@
$('.btn_reject').click(_ =>
Swal.fire({
title: localized.rejection,
html: `<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,
icon: "question",
input: "text",
inputAttributes: {
autocapitalize: "off"
},
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: localized.complete,
cancelButtonText: localized.back,
showLoaderOnConfirm: true,
preConfirm: async (reason) => {
try {
var res = await rejectEnvelope(reason);
return res;
} catch (error) {
Swal.showValidationMessage(`
Request failed: ${error}
`);
}
},
allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
if (!result.isConfirmed)
return;
const res = result.value;
if (res.ok) {
redirRejected()
document.querySelectorAll('.email-input').forEach(input => {
input.addEventListener('input', function () {
if (/^\S+@\S+\.\S+$/.test(this.value)) {
this.classList.remove('is-invalid');
} else {
this.classList.add('is-invalid');
}
else
Swal.showValidationMessage(`Request failed: ${res.message}`);
}));
});
});
document.addEventListener('DOMContentLoaded', function () {
var dropdownItems = document.querySelectorAll('.culture-dropdown-item');
dropdownItems.forEach(function (item) {
item.addEventListener('click', async function (event) {
event.preventDefault();
var language = this.getAttribute('data-language');
var flagCode = this.getAttribute('data-flag');
document.getElementById('selectedFlag').className = 'fi ' + flagCode + ' me-2';
await setLanguage(language);
});
});
});
document.getElementById('readonly-send').addEventListener('click', async () => {
const receiverMail = document.getElementById('readonly-receiver-mail');
const dateValid = document.getElementById('readonly-date-valid');
const receiverMail_value = receiverMail.value;
const dateValid_value = dateValid.value;
//check email
if (!receiverMail_value || receiverMail.classList.contains('is-invalid')) {
Swal.fire({
icon: "error",
title: "Falsche Email",
text: "Die E-Mail-Adresse ist ungültig. Bitte verwenden Sie das richtige Format, z. B.: user@mail.com."
});
return;
}
//check the date
const tomorrow = new Date(Date.now() + 86400000);
if (new Date(dateValid_value) < tomorrow) {
Swal.fire({
icon: "error",
title: "Falsches Datum",
text: "Die Verteilung der Umschläge sollte mindestens einen Tag dauern."
});
return;
}
shareEnvelope(receiverMail_value, dateValid_value)
.then(res => {
if (res.ok) {
Swal.fire({
title: "Gesendet",
icon: "success"
});
}
else {
Swal.fire({
icon: "error",
title: `Fehler ${res.status}`,
text: "Der Vorgang ist fehlgeschlagen. Bitte wenden Sie sich an das IT-Team."
});
}
})
.catch(err => {
Swal.fire({
icon: "error",
title: "Unerwarteter Fehler",
text: "Der Vorgang ist fehlgeschlagen. Bitte wenden Sie sich an das IT-Team."
});
})
receiverMail.value = '';
dateValid.valueAsDate = new Date(new Date().setDate(new Date().getDate() + 8));
});
document.addEventListener('DOMContentLoaded', async () => {
var coords;
try {
coords = await getCoordinates();
}
catch (err) {
coords = {
latitude: 0,
longitude: 0,
};
}
$('#geoloc').leafletLocationPicker({
alwaysOpen: true,
mapContainer: "#fixedMapCont",
location: { latitude: coords.latitude, longitude: coords.longitude }
});
});
const bsNotify = (message, options) => alertify.notify(
`<div class="alert ${options.alert_type ? 'alert-' + options.alert_type : ''}" role="alert"><span class="material-symbols-outlined">${options?.icon_name ?? ''}</span><p>${message}</p></div>`,
'custom',
options?.delay ?? 5);
class Comp {
static ActPanel = class {
@@ -64,4 +129,48 @@ class Comp {
Comp.ActPanel.Display = Comp.ActPanel.IsHided ? '' : 'none'
}
}
static SignatureProgress = class {
static __SignatureCount;
static get SignatureCount() {
this.__SignatureCount = parseInt(document.getElementById("signature-count").innerText);
return this.__SignatureCount;
}
static __SignedCountSpan;
static get SignedCountSpan() {
this.__SignedCountSpan ??= document.getElementById("signed-count");
return Comp.SignatureProgress.__SignedCountSpan;
}
static __signedCount = 0;
static get SignedCount() {
return this.__signedCount;
}
static set SignedCount(value) {
this.__signedCount = value;
const width = (value / this.SignatureCount) * 100;
this.SignedCountBar.style.setProperty('--progress-width', width + '%');
this.SignedCountSpan.innerText = value.toString();
}
static __SignedCountBar;
static get SignedCountBar() {
this.__SignedCountBar ??= document.getElementById("signed-count-bar");
return this.__SignedCountBar;
}
}
static __ShareBackdrop;
static get ShareBackdrop() {
Comp.__ShareBackdrop ??= new bootstrap.Modal(document.getElementById('shareBackdrop'));
return this.__ShareBackdrop;
}
static __LocationBackdrop;
static get LocationBackdrop() {
Comp.__LocationBackdrop ??= new bootstrap.Modal(document.getElementById('locationBackdrop'));
return this.__LocationBackdrop;
}
}

View File

@@ -1,3 +1 @@
$(".btn_reject").click(()=>Swal.fire({title:localized.rejection,html:`<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,icon:"question",input:"text",inputAttributes:{autocapitalize:"off"},showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.complete,cancelButtonText:localized.back,showLoaderOnConfirm:!0,preConfirm:async n=>{try{return await rejectEnvelope(n)}catch(t){Swal.showValidationMessage(`
Request failed: ${t}
`)}},allowOutsideClick:()=>!Swal.isLoading()}).then(n=>{if(n.isConfirmed){const t=n.value;t.ok?redirRejected():Swal.showValidationMessage(`Request failed: ${t.message}`)}}));class Comp{static ActPanel=class{static __Root;static get Root(){Comp.ActPanel.__Root??=document.getElementById("flex-action-panel");return Comp.ActPanel.__Root}static get Elements(){return[...Comp.ActPanel.Root.children]}static get IsHided(){return Comp.ActPanel.Root.style.display=="none"}static set Display(n){Comp.ActPanel.Root.style.display=n;Comp.ActPanel.Elements.forEach(t=>t.style.display=n)}static Toggle(){Comp.ActPanel.Display=Comp.ActPanel.IsHided?"":"none"}};}
document.querySelectorAll(".email-input").forEach(n=>{n.addEventListener("input",function(){/^\S+@\S+\.\S+$/.test(this.value)?this.classList.remove("is-invalid"):this.classList.add("is-invalid")})});document.addEventListener("DOMContentLoaded",function(){var n=document.querySelectorAll(".culture-dropdown-item");n.forEach(function(n){n.addEventListener("click",async function(n){n.preventDefault();var t=this.getAttribute("data-language"),i=this.getAttribute("data-flag");document.getElementById("selectedFlag").className="fi "+i+" me-2";await setLanguage(t)})})});document.getElementById("readonly-send").addEventListener("click",async()=>{const n=document.getElementById("readonly-receiver-mail"),t=document.getElementById("readonly-date-valid"),i=n.value,r=t.value;if(!i||n.classList.contains("is-invalid")){Swal.fire({icon:"error",title:"Falsche Email",text:"Die E-Mail-Adresse ist ungültig. Bitte verwenden Sie das richtige Format, z. B.: user@mail.com."});return}const u=new Date(Date.now()+864e5);if(new Date(r)<u){Swal.fire({icon:"error",title:"Falsches Datum",text:"Die Verteilung der Umschläge sollte mindestens einen Tag dauern."});return}shareEnvelope(i,r).then(n=>{n.ok?Swal.fire({title:"Gesendet",icon:"success"}):Swal.fire({icon:"error",title:`Fehler ${n.status}`,text:"Der Vorgang ist fehlgeschlagen. Bitte wenden Sie sich an das IT-Team."})}).catch(()=>{Swal.fire({icon:"error",title:"Unerwarteter Fehler",text:"Der Vorgang ist fehlgeschlagen. Bitte wenden Sie sich an das IT-Team."})});n.value="";t.valueAsDate=new Date((new Date).setDate((new Date).getDate()+8))});document.addEventListener("DOMContentLoaded",async()=>{var n;try{n=await getCoordinates()}catch(t){n={latitude:0,longitude:0}}$("#geoloc").leafletLocationPicker({alwaysOpen:!0,mapContainer:"#fixedMapCont",location:{latitude:n.latitude,longitude:n.longitude}})});const bsNotify=(n,t)=>alertify.notify(`<div class="alert ${t.alert_type?"alert-"+t.alert_type:""}" role="alert"><span class="material-symbols-outlined">${t?.icon_name??""}</span><p>${n}</p></div>`,"custom",t?.delay??5);class Comp{static ActPanel=class{static __Root;static get Root(){Comp.ActPanel.__Root??=document.getElementById("flex-action-panel");return Comp.ActPanel.__Root}static get Elements(){return[...Comp.ActPanel.Root.children]}static get IsHided(){return Comp.ActPanel.Root.style.display=="none"}static set Display(n){Comp.ActPanel.Root.style.display=n;Comp.ActPanel.Elements.forEach(t=>t.style.display=n)}static Toggle(){Comp.ActPanel.Display=Comp.ActPanel.IsHided?"":"none"}};static SignatureProgress=class{static __SignatureCount;static get SignatureCount(){this.__SignatureCount=parseInt(document.getElementById("signature-count").innerText);return this.__SignatureCount}static __SignedCountSpan;static get SignedCountSpan(){this.__SignedCountSpan??=document.getElementById("signed-count");return Comp.SignatureProgress.__SignedCountSpan}static __signedCount=0;static get SignedCount(){return this.__signedCount}static set SignedCount(n){this.__signedCount=n;const t=(n/this.SignatureCount)*100;this.SignedCountBar.style.setProperty("--progress-width",t+"%");this.SignedCountSpan.innerText=n.toString()}static __SignedCountBar;static get SignedCountBar(){this.__SignedCountBar??=document.getElementById("signed-count-bar");return this.__SignedCountBar}};static __ShareBackdrop;static get ShareBackdrop(){return Comp.__ShareBackdrop??=new bootstrap.Modal(document.getElementById("shareBackdrop")),this.__ShareBackdrop}static __LocationBackdrop;static get LocationBackdrop(){return Comp.__LocationBackdrop??=new bootstrap.Modal(document.getElementById("locationBackdrop")),this.__LocationBackdrop}}

View File

@@ -10,6 +10,7 @@
'zoom-mode',
'spacer',
'search',
'export-pdf'
]
// Load the PSPDFKit UI by setting a target element as the container to render in
@@ -49,8 +50,17 @@
return UI.Instance;
}
static configurePSPDFKit(instance, handler) {
const toolbarItems = UI.getToolbarItems(instance, handler)
static addToolbarItems(instance, handler) {
var toolbarItems = instance.toolbarItems.filter((item) => UI.allowedToolbarItems.includes(item.type));
if (IS_READONLY)
toolbarItems = toolbarItems.concat(UI.getReadOnlyItems(handler));
else
toolbarItems = toolbarItems.concat(UI.getWritableItems(handler));
if (!IS_DESKTOP_SIZE && !IS_READONLY)
toolbarItems = toolbarItems.concat(UI.getMobileWritableItems(handler));
instance.setToolbarItems(toolbarItems)
}
@@ -59,12 +69,6 @@
return null
}
static getToolbarItems(instance, handler) {
const customItems = UI.getCustomItems(handler)
const defaultItems = UI.getDefaultItems(instance.toolbarItems)
return defaultItems.concat(customItems)
}
static createElementFromHTML(html) {
const el = document.createElement('div')
el.innerHTML = html.trim()
@@ -72,21 +76,67 @@
return el.firstChild
}
static getCustomItems = function (callback) {
return []
static getWritableItems = function (callback) {
return [
{
type: 'custom',
id: 'button-reset',
className: 'button-reset',
title: 'Zurücksetzen',
id: 'button-share',
className: 'button-share',
title: 'Teilen',
onPress() {
callback('RESET')
callback('SHARE')
},
icon: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
</svg>`,
icon: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 13V17.5C20 20.5577 16 20.5 12 20.5C8 20.5 4 20.5577 4 17.5V13M12 3L12 15M12 3L16 7M12 3L8 7" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`,
},
{
type: 'custom',
id: 'button-location',
className: 'button-location',
title: 'Location',
onPress() {
callback('LOCATION')
},
icon: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 21C15.5 17.4 19 14.1764 19 10.2C19 6.22355 15.866 3 12 3C8.13401 3 5 6.22355 5 10.2C5 14.1764 8.5 17.4 12 21Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 13C13.6569 13 15 11.6569 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 11.6569 10.3431 13 12 13Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`,
}
];
}
static getReadOnlyItems = function (callback) {
return [
{
type: 'custom',
id: 'button-copy-url',
className: 'button-copy-url',
title: 'Teilen',
onPress() {
callback('COPY_URL')
},
icon: `<svg viewBox="4 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 3H9C6.79086 3 5 4.79086 5 7V15" stroke="#222222"/>
<path d="M8.5 11.5C8.5 10.3156 8.50074 9.46912 8.57435 8.81625C8.64681 8.17346 8.78457 7.78051 9.01662 7.4781C9.14962 7.30477 9.30477 7.14962 9.4781 7.01662C9.78051 6.78457 10.1735 6.64681 10.8163 6.57435C11.4691 6.50074 12.3156 6.5 13.5 6.5C14.6844 6.5 15.5309 6.50074 16.1837 6.57435C16.8265 6.64681 17.2195 6.78457 17.5219 7.01662C17.6952 7.14962 17.8504 7.30477 17.9834 7.4781C18.2154 7.78051 18.3532 8.17346 18.4257 8.81625C18.4993 9.46912 18.5 10.3156 18.5 11.5V15.5C18.5 16.6844 18.4993 17.5309 18.4257 18.1837C18.3532 18.8265 18.2154 19.2195 17.9834 19.5219C17.8504 19.6952 17.6952 19.8504 17.5219 19.9834C17.2195 20.2154 16.8265 20.3532 16.1837 20.4257C15.5309 20.4993 14.6844 20.5 13.5 20.5C12.3156 20.5 11.4691 20.4993 10.8163 20.4257C10.1735 20.3532 9.78051 20.2154 9.4781 19.9834C9.30477 19.8504 9.14962 19.6952 9.01662 19.5219C8.78457 19.2195 8.64681 18.8265 8.57435 18.1837C8.50074 17.5309 8.5 16.6844 8.5 15.5V11.5Z" stroke="#222222"/>
</svg>`,
}
];
}
static getMobileWritableItems = function (callback) {
return [
{
type: 'custom',
id: 'button-finish',
className: 'button-finish',
onPress() {
callback('FINISH')
},
icon: `<svg class="icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="-4 -4 26 26">
<path d="m10.036 8.278 9.258-7.79A1.979 1.979 0 0 0 18 0H2A1.987 1.987 0 0 0 .641.541l9.395 7.737Z" />
<path d="M11.241 9.817c-.36.275-.801.425-1.255.427-.428 0-.845-.138-1.187-.395L0 2.6V14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2.5l-8.759 7.317Z" />
</svg>`
},
{
type: 'custom',
@@ -96,24 +146,24 @@
onPress() {
callback('REJECT')
},
icon: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-hand-thumbs-down" viewBox="0 0 16 16">
<path d="M8.864 15.674c-.956.24-1.843-.484-1.908-1.42-.072-1.05-.23-2.015-.428-2.59-.125-.36-.479-1.012-1.04-1.638-.557-.624-1.282-1.179-2.131-1.41C2.685 8.432 2 7.85 2 7V3c0-.845.682-1.464 1.448-1.546 1.07-.113 1.564-.415 2.068-.723l.048-.029c.272-.166.578-.349.97-.484C6.931.08 7.395 0 8 0h3.5c.937 0 1.599.478 1.934 1.064.164.287.254.607.254.913 0 .152-.023.312-.077.464.201.262.38.577.488.9.11.33.172.762.004 1.15.069.13.12.268.159.403.077.27.113.567.113.856 0 .289-.036.586-.113.856-.035.12-.08.244-.138.363.394.571.418 1.2.234 1.733-.206.592-.682 1.1-1.2 1.272-.847.283-1.803.276-2.516.211a9.877 9.877 0 0 1-.443-.05 9.364 9.364 0 0 1-.062 4.51c-.138.508-.55.848-1.012.964zM11.5 1H8c-.51 0-.863.068-1.14.163-.281.097-.506.229-.776.393l-.04.025c-.555.338-1.198.73-2.49.868-.333.035-.554.29-.554.55V7c0 .255.226.543.62.65 1.095.3 1.977.997 2.614 1.709.635.71 1.064 1.475 1.238 1.977.243.7.407 1.768.482 2.85.025.362.36.595.667.518l.262-.065c.16-.04.258-.144.288-.255a8.34 8.34 0 0 0-.145-4.726.5.5 0 0 1 .595-.643h.003l.014.004.058.013a8.912 8.912 0 0 0 1.036.157c.663.06 1.457.054 2.11-.163.175-.059.45-.301.57-.651.107-.308.087-.67-.266-1.021L12.793 7l.353-.354c.043-.042.105-.14.154-.315.048-.167.075-.37.075-.581 0-.211-.027-.414-.075-.581-.05-.174-.111-.273-.154-.315l-.353-.354.353-.354c.047-.047.109-.176.005-.488a2.224 2.224 0 0 0-.505-.804l-.353-.354.353-.354c.006-.005.041-.05.041-.17a.866.866 0 0 0-.121-.415C12.4 1.272 12.063 1 11.5 1"/>
</svg>`,
icon: `<svg width="25px" height="25px" viewBox="43.5 43.5 512 512" version="1.1" fill="currentColor" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path class="st0" d="M263.24,43.5c-117.36,0-212.5,95.14-212.5,212.5s95.14,212.5,212.5,212.5s212.5-95.14,212.5-212.5 S380.6,43.5,263.24,43.5z M367.83,298.36c17.18,17.18,17.18,45.04,0,62.23v0c-17.18,17.18-45.04,17.18-62.23,0l-42.36-42.36 l-42.36,42.36c-17.18,17.18-45.04,17.18-62.23,0v0c-17.18-17.18-17.18-45.04,0-62.23L201.01,256l-42.36-42.36 c-17.18-17.18-17.18-45.04,0-62.23v0c17.18-17.18,45.04-17.18,62.23,0l42.36,42.36l42.36-42.36c17.18-17.18,45.04-17.18,62.23,0v0 c17.18,17.18,17.18,45.04,0,62.23L325.46,256L367.83,298.36z" />
</svg>`,
},
{
type: 'custom',
id: 'button-finish',
className: 'button-finish',
title: 'Abschließen',
id: 'button-reset',
className: 'button-reset',
title: 'Zurücksetzen',
onPress() {
callback('FINISH')
callback('RESET')
},
},
]
}
static getDefaultItems(items) {
return items.filter((item) => UI.allowedToolbarItems.includes(item.type))
icon: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="-1 -1 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
</svg>`,
}
];
}
static getPresets() {

View File

@@ -1,7 +1,18 @@
class UI{static allowedToolbarItems=["sidebar-thumbnails","sidebar-document-ouline","sidebar-bookmarks","pager","pan","zoom-out","zoom-in","zoom-mode","spacer","search",];static Instance
static loadPSPDFKit(n,t,i,r){return UI.Instance=PSPDFKit.load({inlineWorkers:!1,locale:r,licenseKey:i,styleSheets:["/css/site.css"],container:t,document:n,annotationPresets:UI.getPresets(),electronicSignatures:{creationModes:["DRAW","TYPE","IMAGE"]},initialViewState:new PSPDFKit.ViewState({sidebarMode:PSPDFKit.SidebarMode.THUMBNAILS}),isEditableAnnotation:function(n){return n.isSignature||n.description=="FRAME"?!1:!0},customRenderers:{Annotation:UI.annotationRenderer}}),UI.Instance}static configurePSPDFKit(n,t){const i=UI.getToolbarItems(n,t);n.setToolbarItems(i)}static annotationRenderer(){return null}static getToolbarItems(n,t){const i=UI.getCustomItems(t),r=UI.getDefaultItems(n.toolbarItems);return r.concat(i)}static createElementFromHTML(n){const t=document.createElement("div");return t.innerHTML=n.trim(),t.firstChild}static getCustomItems=function(n){return[];return[{type:"custom",id:"button-reset",className:"button-reset",title:"Zurücksetzen",onPress(){n("RESET")},icon:`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
</svg>`},{type:"custom",id:"button-reject",className:"button-reject",title:"Ablehnen",onPress(){n("REJECT")},icon:`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-hand-thumbs-down" viewBox="0 0 16 16">
<path d="M8.864 15.674c-.956.24-1.843-.484-1.908-1.42-.072-1.05-.23-2.015-.428-2.59-.125-.36-.479-1.012-1.04-1.638-.557-.624-1.282-1.179-2.131-1.41C2.685 8.432 2 7.85 2 7V3c0-.845.682-1.464 1.448-1.546 1.07-.113 1.564-.415 2.068-.723l.048-.029c.272-.166.578-.349.97-.484C6.931.08 7.395 0 8 0h3.5c.937 0 1.599.478 1.934 1.064.164.287.254.607.254.913 0 .152-.023.312-.077.464.201.262.38.577.488.9.11.33.172.762.004 1.15.069.13.12.268.159.403.077.27.113.567.113.856 0 .289-.036.586-.113.856-.035.12-.08.244-.138.363.394.571.418 1.2.234 1.733-.206.592-.682 1.1-1.2 1.272-.847.283-1.803.276-2.516.211a9.877 9.877 0 0 1-.443-.05 9.364 9.364 0 0 1-.062 4.51c-.138.508-.55.848-1.012.964zM11.5 1H8c-.51 0-.863.068-1.14.163-.281.097-.506.229-.776.393l-.04.025c-.555.338-1.198.73-2.49.868-.333.035-.554.29-.554.55V7c0 .255.226.543.62.65 1.095.3 1.977.997 2.614 1.709.635.71 1.064 1.475 1.238 1.977.243.7.407 1.768.482 2.85.025.362.36.595.667.518l.262-.065c.16-.04.258-.144.288-.255a8.34 8.34 0 0 0-.145-4.726.5.5 0 0 1 .595-.643h.003l.014.004.058.013a8.912 8.912 0 0 0 1.036.157c.663.06 1.457.054 2.11-.163.175-.059.45-.301.57-.651.107-.308.087-.67-.266-1.021L12.793 7l.353-.354c.043-.042.105-.14.154-.315.048-.167.075-.37.075-.581 0-.211-.027-.414-.075-.581-.05-.174-.111-.273-.154-.315l-.353-.354.353-.354c.047-.047.109-.176.005-.488a2.224 2.224 0 0 0-.505-.804l-.353-.354.353-.354c.006-.005.041-.05.041-.17a.866.866 0 0 0-.121-.415C12.4 1.272 12.063 1 11.5 1"/>
</svg>`},{type:"custom",id:"button-finish",className:"button-finish",title:"Abschließen",onPress(){n("FINISH")}},]};static getDefaultItems(n){return n.filter(n=>UI.allowedToolbarItems.includes(n.type))}static getPresets(){const n=PSPDFKit.defaultAnnotationPresets;return n.ink={lineWidth:10},n.widget={readOnly:!0},n}}
class UI{static allowedToolbarItems=["sidebar-thumbnails","sidebar-document-ouline","sidebar-bookmarks","pager","pan","zoom-out","zoom-in","zoom-mode","spacer","search","export-pdf"];static Instance
static loadPSPDFKit(n,t,i,r){return UI.Instance=PSPDFKit.load({inlineWorkers:!1,locale:r,licenseKey:i,styleSheets:["/css/site.css"],container:t,document:n,annotationPresets:UI.getPresets(),electronicSignatures:{creationModes:["DRAW","TYPE","IMAGE"]},initialViewState:new PSPDFKit.ViewState({sidebarMode:PSPDFKit.SidebarMode.THUMBNAILS}),isEditableAnnotation:function(n){return n.isSignature||n.description=="FRAME"?!1:!0},customRenderers:{Annotation:UI.annotationRenderer}}),UI.Instance}static addToolbarItems(n,t){var i=n.toolbarItems.filter(n=>UI.allowedToolbarItems.includes(n.type));i=IS_READONLY?i.concat(UI.getReadOnlyItems(t)):i.concat(UI.getWritableItems(t));IS_DESKTOP_SIZE||IS_READONLY||(i=i.concat(UI.getMobileWritableItems(t)));n.setToolbarItems(i)}static annotationRenderer(){return null}static createElementFromHTML(n){const t=document.createElement("div");return t.innerHTML=n.trim(),t.firstChild}static getWritableItems=function(n){return[{type:"custom",id:"button-share",className:"button-share",title:"Teilen",onPress(){n("SHARE")},icon:`<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 13V17.5C20 20.5577 16 20.5 12 20.5C8 20.5 4 20.5577 4 17.5V13M12 3L12 15M12 3L16 7M12 3L8 7" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`},{type:"custom",id:"button-location",className:"button-location",title:"Location",onPress(){n("LOCATION")},icon:`<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 21C15.5 17.4 19 14.1764 19 10.2C19 6.22355 15.866 3 12 3C8.13401 3 5 6.22355 5 10.2C5 14.1764 8.5 17.4 12 21Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 13C13.6569 13 15 11.6569 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 11.6569 10.3431 13 12 13Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`}]};static getReadOnlyItems=function(n){return[{type:"custom",id:"button-copy-url",className:"button-copy-url",title:"Teilen",onPress(){n("COPY_URL")},icon:`<svg viewBox="4 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 3H9C6.79086 3 5 4.79086 5 7V15" stroke="#222222"/>
<path d="M8.5 11.5C8.5 10.3156 8.50074 9.46912 8.57435 8.81625C8.64681 8.17346 8.78457 7.78051 9.01662 7.4781C9.14962 7.30477 9.30477 7.14962 9.4781 7.01662C9.78051 6.78457 10.1735 6.64681 10.8163 6.57435C11.4691 6.50074 12.3156 6.5 13.5 6.5C14.6844 6.5 15.5309 6.50074 16.1837 6.57435C16.8265 6.64681 17.2195 6.78457 17.5219 7.01662C17.6952 7.14962 17.8504 7.30477 17.9834 7.4781C18.2154 7.78051 18.3532 8.17346 18.4257 8.81625C18.4993 9.46912 18.5 10.3156 18.5 11.5V15.5C18.5 16.6844 18.4993 17.5309 18.4257 18.1837C18.3532 18.8265 18.2154 19.2195 17.9834 19.5219C17.8504 19.6952 17.6952 19.8504 17.5219 19.9834C17.2195 20.2154 16.8265 20.3532 16.1837 20.4257C15.5309 20.4993 14.6844 20.5 13.5 20.5C12.3156 20.5 11.4691 20.4993 10.8163 20.4257C10.1735 20.3532 9.78051 20.2154 9.4781 19.9834C9.30477 19.8504 9.14962 19.6952 9.01662 19.5219C8.78457 19.2195 8.64681 18.8265 8.57435 18.1837C8.50074 17.5309 8.5 16.6844 8.5 15.5V11.5Z" stroke="#222222"/>
</svg>`}]};static getMobileWritableItems=function(n){return[{type:"custom",id:"button-finish",className:"button-finish",onPress(){n("FINISH")},icon:`<svg class="icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="-4 -4 26 26">
<path d="m10.036 8.278 9.258-7.79A1.979 1.979 0 0 0 18 0H2A1.987 1.987 0 0 0 .641.541l9.395 7.737Z" />
<path d="M11.241 9.817c-.36.275-.801.425-1.255.427-.428 0-.845-.138-1.187-.395L0 2.6V14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2.5l-8.759 7.317Z" />
</svg>`},{type:"custom",id:"button-reject",className:"button-reject",title:"Ablehnen",onPress(){n("REJECT")},icon:`<svg width="25px" height="25px" viewBox="43.5 43.5 512 512" version="1.1" fill="currentColor" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path class="st0" d="M263.24,43.5c-117.36,0-212.5,95.14-212.5,212.5s95.14,212.5,212.5,212.5s212.5-95.14,212.5-212.5 S380.6,43.5,263.24,43.5z M367.83,298.36c17.18,17.18,17.18,45.04,0,62.23v0c-17.18,17.18-45.04,17.18-62.23,0l-42.36-42.36 l-42.36,42.36c-17.18,17.18-45.04,17.18-62.23,0v0c-17.18-17.18-17.18-45.04,0-62.23L201.01,256l-42.36-42.36 c-17.18-17.18-17.18-45.04,0-62.23v0c17.18-17.18,45.04-17.18,62.23,0l42.36,42.36l42.36-42.36c17.18-17.18,45.04-17.18,62.23,0v0 c17.18,17.18,17.18,45.04,0,62.23L325.46,256L367.83,298.36z" />
</svg>`},{type:"custom",id:"button-reset",className:"button-reset",title:"Zurücksetzen",onPress(){n("RESET")},icon:`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="-1 -1 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
</svg>`}]};static getPresets(){const n=PSPDFKit.defaultAnnotationPresets;return n.ink={lineWidth:10},n.widget={readOnly:!0},n}}

View File

@@ -57,12 +57,4 @@ function detailedCurrentDate() {
second: '2-digit',
timeZoneName: 'shortOffset'
}).format();
}
let __is_mobile = null;
function isMobile() {
if (__is_mobile === null) {
__is_mobile = /Mobi|Android/i.test(window.navigator.userAgent);
}
return __is_mobile;
}

View File

@@ -1 +1 @@
function getCoordinates(){return new Promise((n,t)=>{navigator.geolocation?navigator.geolocation.getCurrentPosition(t=>n(t.coords),n=>t(n)):t(new Error("Geolocation is not supported by this browser."))})}async function getCity(){try{const t=await getCoordinates(),i=await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${t.latitude}&lon=${t.longitude}`),n=await i.json();if(n&&n.address){const t=n.address.city||n.address.town||n.address.village||n.address.hamlet,i=n.address.postcode;return i+" "+t||""}}catch{return""}}async function getLocation(){try{const t=await getCoordinates(),i=await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${t.latitude}&lon=${t.longitude}`),n=await i.json();if(n&&n.address){const t=n.address.city||n.address.town||n.address.village||n.address.hamlet,i=n.address.postcode;return{postalCode:i,city:t}}}catch{return{postalCode:"",city:""}}}function detailedCurrentDate(){return new Intl.DateTimeFormat("de-DE",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:"shortOffset"}).format()}function isMobile(){return __is_mobile===null&&(__is_mobile=/Mobi|Android/i.test(window.navigator.userAgent)),__is_mobile}const B64ToBuff=n=>new Uint8Array(Array.from(atob(n),n=>n.charCodeAt(0))).buffer;const getLocaleDateString=()=>(new Date).toLocaleDateString("de-DE");let __is_mobile=null;
function getCoordinates(){return new Promise((n,t)=>{navigator.geolocation?navigator.geolocation.getCurrentPosition(t=>n(t.coords),n=>t(n)):t(new Error("Geolocation is not supported by this browser."))})}async function getCity(){try{const t=await getCoordinates(),i=await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${t.latitude}&lon=${t.longitude}`),n=await i.json();if(n&&n.address){const t=n.address.city||n.address.town||n.address.village||n.address.hamlet,i=n.address.postcode;return i+" "+t||""}}catch{return""}}async function getLocation(){try{const t=await getCoordinates(),i=await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${t.latitude}&lon=${t.longitude}`),n=await i.json();if(n&&n.address){const t=n.address.city||n.address.town||n.address.village||n.address.hamlet,i=n.address.postcode;return{postalCode:i,city:t}}}catch{return{postalCode:"",city:""}}}function detailedCurrentDate(){return new Intl.DateTimeFormat("de-DE",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:"shortOffset"}).format()}const B64ToBuff=n=>new Uint8Array(Array.from(atob(n),n=>n.charCodeAt(0))).buffer;const getLocaleDateString=()=>(new Date).toLocaleDateString("de-DE");

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More