Compare commits

...

69 Commits

Author SHA1 Message Date
Developer 02
46b8bde162 chore(Web): Aktualisiert auf 2.7.0. 2024-12-02 10:12:10 +01:00
Developer 02
a6468c2ff1 feat(HomeController): Funktionalität zur Überprüfung des SMS-Codes hinzugefügt 2024-11-30 04:23:24 +01:00
Developer 02
40a21a0b89 feat(EnvelopeReceiverCache): zum Abrufen und Setzen von Caches über Envelope Receiver unter Verwendung von Standard-Schlüsselwörtern als Schnittstellenimplementierung erstellt.
- Erstellte Optionen.
 - Zu DI hinzugefügt.
2024-11-30 03:46:40 +01:00
Developer 02
fa44b82493 feat(EnvelopeLocked): Timer mit CSS-Konfiguration und Javascript-Ereignis hinzugefügt.
- Ablauf über Home-Controller-Ansichtsdaten hinzugefügt
2024-11-30 01:56:02 +01:00
Developer 02
cdec5485c6 feat(GtxMessagingService): Zwischenspeicherung für SMS-Code und Ablauf des SMS-Codes mittels Envelope-Receiver-ID hinzugefügt
- Erweiterungsmethode für Zeitcaching hinzugefügt.
2024-11-29 16:25:20 +01:00
Developer 02
2a963a1861 feat(Web): Verteilter Sql Server-Cache hinzugefügt.
- Bat-Datei erstellt, um Tabelle für Cache zu erstellen.
 - Sql-Datei zum Erstellen einer Tabelle für den Cache erstellt
2024-11-29 14:08:07 +01:00
Developer 02
9d1a2e7254 refactor(HomeController): SMS-Code zum Senden hinzugefügt 2024-11-29 12:05:07 +01:00
Developer 02
b779ef6f0b feat(GtxMessagingService): Konfigurierte Codelänge über ioptions.
- Standardmäßig ist sie 5
2024-11-29 11:16:08 +01:00
Developer 02
0c81a86610 feat(GtxMessagingService): SendSmsCodeAsync mit Basisfunktionalität als Schnittstellenimplementierung hinzugefügt 2024-11-29 11:13:59 +01:00
Developer 02
b11f32bd3c feat: CodeGenerator-Service mit Konfigurationsunterstützung implementiert
- CodeGenerator-Service erstellt, der zufällige Codes basierend auf einem konfigurierbaren Zeichensatz generiert.
- IOptions<CodeGeneratorConfig> für DI-Injektion der Konfigurationseinstellungen integriert.
- Lazy-Initialisierung für statische Instanz des CodeGenerators hinzugefügt.
- Validierung hinzugefügt, um sicherzustellen, dass die Code-Länge größer als null ist.
- Geplante zukünftige Verbesserung: Random als Singleton injizieren, um die Multithreading-Performance zu verbessern.
2024-11-29 11:08:01 +01:00
Developer 02
b8d9963fac refactor(HomeController): ReadWithSecretByUuidSignatureAsync implementiert, um alle Informationen in einer einzigen Sql-Transaktion zu erhalten.
- Methode hinzugefügt, um geheimes dto in dto zu konvertieren
2024-11-29 10:22:11 +01:00
Developer 02
e77532ebfd feat(EnvelopeReceiverService): ReadWithSecretByUuidSignatureAsync zum Lesen mit Zugangscode und Telefonnummer hinzugefügt 2024-11-29 10:11:33 +01:00
Developer 02
ec37518245 feat(EnvelopeReceiverSecretDto): Erstellt als Erbe von EnvelopeReceiverDto, hinzugefügt AccessCode und PhoneNumber. 2024-11-29 10:07:09 +01:00
Developer 02
a1618fc8d0 refactor(HomeController): EnvelopeReceiverSecretDto zur Vereinfachung entfernt und direkt String verwendet 2024-11-29 10:01:28 +01:00
Developer 02
6b65fc28fd refactor(HomeController): log message format more appropriately written 2024-11-29 09:29:27 +01:00
Developer 02
a763fd6a24 feat(EnvelopeLocked): Textkörper und Fußzeile für SMS-Ansicht hinzugefügt. 2024-11-29 01:10:08 +01:00
Developer 02
28a8e20b63 feat(WebKey): Lokalisierungstasten sms tfa in EnvelopeLocked Ansicht hinzugefügt. 2024-11-29 00:45:21 +01:00
Developer 02
155f80e8b3 feat(EnvelopeLocked): Angepasste Icon-Farbe für sms TFA 2024-11-29 00:38:18 +01:00
Developer 02
d8f74971f3 feat(EnvelopeLocked): Der Parameter viaSms wurde hinzugefügt, um die Seite sowohl für die Überprüfung des Zugangscodes als auch des SMS-Codes zu verwenden.
- accessCodeName und accessCodeLabel wurden aktualisiert, um bedingt zugewiesen zu werden.
2024-11-29 00:26:29 +01:00
Developer 02
44dc7185c6 feat(Auth): Getter-Methoden zur Werteprüfung hinzugefügt 2024-11-28 23:57:18 +01:00
Developer 02
551ba595b6 refactor(EnvelopeLocked): envelopeRecevier-Modell aus der Ansicht entfernt. 2024-11-28 23:56:18 +01:00
Developer 02
4b77713df4 Merge branch 'master' into feat/two-factor-auth 2024-11-28 23:39:12 +01:00
Developer 02
f1ca1e9067 feat(Auth): Erstellung eines Authentifizierungsmodells anstelle der direkten Verwendung des Zugriffscodes. 2024-11-28 23:38:51 +01:00
Developer 02
0469f057c9 refactor(HomeController): Aktualisiert, um den Envelope-Empfänger als Modell zur EnvelopeLocked-Ansicht hinzuzufügen 2024-11-28 21:50:05 +01:00
Developer 02
b4a97abe6b feat(EnvelopeReceiverBase): HasPhoneNumber-Eigenschaft sowohl zur Entität als auch zum DTO hinzugefügt 2024-11-28 20:46:51 +01:00
Developer 02
423b293197 feat(MessagingService): Möglichkeit hinzugefügt, den Anbieter des Messaging-Servers zu benachrichtigen. 2024-11-27 17:46:32 +01:00
Developer 02
27618a343e feat(EnvelopeReceiverService): SendSmsAsync hinzugefügt, um SMS an den Benutzer über die Umschlag-Empfänger-ID mithilfe des Messaging-Services zu senden. 2024-11-27 17:35:38 +01:00
Developer 02
fe106c5a8c feat(EnvelopeReceiverBase): Eigenschaft „Telefonnummer“ hinzugefügt. 2024-11-27 17:09:17 +01:00
Developer 02
941b98b1a4 feat(SmsResponse): Erstellung eines Standardantwort-DTOs für SMS-Anfragen.
- GtxMessagingResponse für rohe dynamische Antwort erstellt.
 - Mapping-Profil hinzufügen
2024-11-27 15:13:41 +01:00
Developer 02
168c33bfea chore(Application): Core.Client auf 2.0.3 hochgerüstet 2024-11-26 23:58:07 +01:00
Developer 02
40c25ee111 fix(appsettings): Leerzeichen aus SmsConfig.QueryParams.from entfernt, da vom SMS-Dienst nicht erlaubt 2024-11-26 23:47:42 +01:00
Developer 02
608d79d35b chore(Web): upgraded to 2.6.0 2024-11-25 15:54:15 +01:00
Developer 02
62d396932d refactor(EnvelopeDocument): removed Filename, Filepath and FilenameOriginal properties. 2024-11-25 15:42:51 +01:00
Developer 02
62dcb41526 Refactor: Unnötige alte Fußzeilen aus den Ansichten „Umschlag abgelaufen“, „Abgelehnt“ und „Signiert“ entfernt. 2024-11-25 15:37:28 +01:00
Developer 02
360bb9b3d8 refactor(_layout.cshtml): Datenschutz-Link in der Fußzeile aktualisiert, unterstützt mehrere Sprachen 2024-11-25 15:24:18 +01:00
Developer 02
1f57914f9e refactor(_Layout.cshtml): aktualisiert, um Datenschutz und die Website der Digital Data GmbH bei Klick in einem neuen Tab zu öffnen. 2024-11-25 15:12:48 +01:00
Developer 02
9c431ddf56 refactor(Config): DocumentPathDmz, ExportPathDmz und DocumentPathMoveAftsend sowohl in der Entität als auch im DTO entfernt. 2024-11-25 14:58:32 +01:00
Developer 02
61ff2f8cde Merge branch 'master' of http://git.dd:3000/AppStd/EnvelopeGenerator 2024-11-25 14:42:56 +01:00
Developer 02
f2ee509727 chore: Upgrade von Core.Abstractions 2.2.1 und Core.Client auf 2.0.2 2024-11-25 14:40:52 +01:00
Developer 02
da06daf776 refactor(GtxMessagingService): Optionen für generische und dynamische Antworttypen zur SendSmsAsync-Methode hinzugefügt. 2024-11-25 14:13:34 +01:00
Developer 02
d3104500d4 refactor(Application): Aufrüstung von Core.Client auf 2.0.1 2024-11-25 14:08:47 +01:00
Developer 02
d23b8b9187 feat(TestMessagingController): initialized 2024-11-25 13:32:50 +01:00
Developer 02
ec206ab33a feat(DIExtensions): Gtx-Nachrichtendienst hinzugefügt 2024-11-25 13:27:09 +01:00
Developer 02
de6d4b9dd8 feat(DIExtensions): HTTP-Client-Dienst hinzugefügt 2024-11-25 13:25:22 +01:00
Developer 02
2943fe0e2d refactor(DIExtensions): Aktualisiert, um TryAddScoped anstelle von AddScoped für sicherere DI-Injektion zu verwenden. 2024-11-25 13:10:43 +01:00
Developer 02
33e99f584a feat(IMessagingService): Initialisiert und implementiert in GtxMessagingService 2024-11-25 13:07:39 +01:00
Developer 02
4a62ab0c56 feat(GtxMessagingService): Umbenennung von SendSms in SendSmsAsync 2024-11-25 13:01:43 +01:00
Developer 02
132acd35cc feat(GtxMessagingService): Empfänger- und Nachrichteneingaben zur SendSms-Methode über SMS-Parameter hinzugefügt 2024-11-25 13:01:12 +01:00
Developer 02
ed80839777 feat(SmsParams): Aktualisiert, um IHttpClientOptions zu implementieren 2024-11-25 12:47:30 +01:00
Developer01
2114615584 MS Removed PathColumns 2024-11-25 12:38:02 +01:00
Developer 02
6e6f3fd2ed chore: Aktualisierung von Core.Abstraction auf 2.2.0 2024-11-25 11:55:09 +01:00
Developer 02
5da306acd3 feat(GtxMessagingService): SendSms-Methode initialisiert und Authentifizierungspfad hinzugefügt. 2024-11-22 15:37:59 +01:00
Developer 02
18ef1d19b5 feat(GtxMessagingService): Eingespritzter Client.IHttpClientService 2024-11-22 15:20:19 +01:00
Developer 02
b76ebd2abc chore: Aktualisierung von Core.Abstraction auf 2.1.0
- Core.Client zur Anwendungsschicht hinzufügen
2024-11-22 14:57:20 +01:00
Developer 02
d55233061d feat: erweitere SmsParams um zusätzliche Eigenschaften für die SMS-API-Integration
- Neue erforderliche Eigenschaft `Endpoint` hinzugefügt, mit einem Standardwert für den GTX Messaging REST-API-Endpunkt.
- Optionale Eigenschaften wie `Format`, `Accept`, `ContentType` und SMS-spezifische Parameter (`From`, `Texts`, `DlrMask` usw.) hinzugefügt, um API-Anforderungen zu erfüllen.
- XML-Dokumentation mit API-Referenzlink aktualisiert, um besseren Kontext zu bieten.
- TODO hinzugefügt, um Regex-Validierung in `init`-Methoden zu implementieren und Eingabefehler zu reduzieren.
2024-11-21 18:25:14 +01:00
Developer 02
949001791c refactor: Umbenennung von GTX in Gtx und GTXMessagingConfig in SmsParams 2024-11-21 17:22:21 +01:00
Developer 02
30f93f2439 feat(GTXMessagingService): initialisiert mit Konfigurationsoption 2024-11-21 11:46:15 +01:00
Developer01
36ffb9511c Merge branch 'master' of http://git.dd:3000/AppStd/EnvelopeGenerator 2024-11-12 08:30:32 +01:00
Developer01
bf84d5c63a MS Common v1.5.9 email Valdiation, email Resending, Clientintegration 2024-11-12 08:30:14 +01:00
Developer01
57f8d0e398 MS Multiple Files Dialog, Qualifizierte Signatur weg 2024-11-08 11:48:58 +01:00
Developer 02
bb8bd8ed40 chore: Aktualisiert auf 2.5.0. 2024-11-06 00:37:57 +01:00
Developer 02
ba832acad3 feat(Main.cshtml): Bootstrap-Warnung hinzugefügt. 2024-11-06 00:31:25 +01:00
Developer 02
6490a3cb82 feat(home-description): gestaltetes CSS 2024-11-05 23:48:30 +01:00
Developer 02
4c077c90db feat(Main.cshtml): Typed.js für die Beschreibung der Startseite hinzugefügt. 2024-11-05 23:05:30 +01:00
Developer 02
9bd5e63128 feat(Typed.js): hinzugefügt 2024-11-04 17:21:47 +01:00
Developer 02
15ce7c9384 feat(localizer): Wert für HomePageDescription in Deutsch und Englisch hinzugefügt 2024-11-04 17:02:21 +01:00
Developer 02
8707a5cdb5 feat(HomeController): Kultur-Cookie zur Hauptseite hinzugefügt. 2024-11-04 16:28:37 +01:00
Developer 02
47c7070700 feat(Main.cshtml): sign_flow_min.svg hinzugefügt 2024-11-04 15:03:00 +01:00
Developer 02
fcc3223eb1 feat(HomeController): Endpunkt für Stammverzeichnis als Startseite hinzugefügt. 2024-11-04 14:30:09 +01:00
96 changed files with 2276 additions and 392 deletions

View File

@@ -0,0 +1,7 @@
namespace EnvelopeGenerator.Application.Configurations
{
public class CodeGeneratorConfig
{
public string CharPool { get; init; } = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789";
}
}

View File

@@ -0,0 +1,19 @@
namespace EnvelopeGenerator.Application.Configurations
{
public class EnvelopeReceiverCacheParams
{
/// <summary>
/// Gets the cache key format for SMS codes.
/// The placeholder {0} represents the envelopeReceiverId.
/// </summary>
public string CodeCacheKeyFormat { get; init; } = "sms-code-{0}";
/// <summary>
/// Gets the cache expiration key format for SMS codes.
/// The placeholder {0} represents the envelopeReceiverId.
/// </summary>
public string CodeExpirationCacheKeyFormat { get; init; } = "sms-code-expiration-{0}";
public TimeSpan CodeCacheValidityPeriod { get; init; } = new(0, 5, 0);
}
}

View File

@@ -0,0 +1,25 @@
using DigitalData.Core.Abstractions.Client;
using Microsoft.Extensions.Caching.Distributed;
namespace EnvelopeGenerator.Application.Configurations.GtxMessaging
{
/// <summary>
/// https://www.gtx-messaging.com/en/api-docs/sms-rest-api/
/// </summary>
public class SmsParams : IHttpClientOptions
{
public required string Uri { get; init; }
public string? Path { get; init; }
public Dictionary<string, object>? Headers { get; init; }
public Dictionary<string, object?>? QueryParams { get; init; }
public string RecipientQueryParamName { get; init; } = "to";
public string MessageQueryParamName { get; init; } = "text";
public int CodeLength { get; init; } = 5;
}
}

View File

@@ -0,0 +1,7 @@
namespace EnvelopeGenerator.Application.Contracts
{
public interface ICodeGenerator
{
string GenerateCode(int length);
}
}

View File

@@ -0,0 +1,17 @@
namespace EnvelopeGenerator.Application.Contracts
{
public interface IEnvelopeReceiverCache
{
Task<string?> GetSmsCodeAsync(string envelopeReceiverId);
/// <summary>
/// Asynchronously stores an SMS verification code in the cache and returns the expiration date of the code.
/// </summary>
/// <param name="envelopeReceiverId">The unique identifier for the recipient of the envelope to associate with the SMS code.</param>
/// <param name="code">The SMS verification code to be stored.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the expiration date and time of the stored SMS code.</returns>
Task<DateTime> SetSmsCodeAsync(string envelopeReceiverId, string code);
Task<DateTime?> GetSmsCodeExpirationAsync(string envelopeReceiverId);
}
}

View File

@@ -1,7 +1,7 @@
using DigitalData.Core.Abstractions.Application; using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.DTO; using DigitalData.Core.DTO;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver; using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Application.DTOs.Receiver; using EnvelopeGenerator.Application.DTOs.Messaging;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Contracts namespace EnvelopeGenerator.Application.Contracts
@@ -11,12 +11,14 @@ namespace EnvelopeGenerator.Application.Contracts
Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false); Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false);
Task<DataResult<IEnumerable<EnvelopeReceiverSecretDto>>> ReadSecretByUuidAsync(string uuid, bool withEnvelope = false, bool withReceiver = true); Task<DataResult<IEnumerable<string?>>> ReadAccessCodeByUuidAsync(string uuid, bool withEnvelope = false, bool withReceiver = true);
Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true); Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true);
Task<DataResult<EnvelopeReceiverDto>> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true); Task<DataResult<EnvelopeReceiverDto>> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true);
Task<DataResult<EnvelopeReceiverSecretDto>> ReadWithSecretByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true);
Task<DataResult<EnvelopeReceiverDto>> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true); Task<DataResult<EnvelopeReceiverDto>> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true);
Task<DataResult<string>> ReadAccessCodeByIdAsync(int envelopeId, int receiverId); Task<DataResult<string>> ReadAccessCodeByIdAsync(int envelopeId, int receiverId);
@@ -30,5 +32,7 @@ namespace EnvelopeGenerator.Application.Contracts
Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, params int[] ignore_statuses); Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, params int[] ignore_statuses);
Task<DataResult<string?>> ReadLastUsedReceiverNameByMail(string mail); Task<DataResult<string?>> ReadLastUsedReceiverNameByMail(string mail);
Task<DataResult<SmsResponse>> SendSmsAsync(string envelopeReceiverId, string message);
} }
} }

View File

@@ -0,0 +1,13 @@
using EnvelopeGenerator.Application.DTOs.Messaging;
namespace EnvelopeGenerator.Application.Contracts
{
public interface IMessagingService
{
string ServiceProvider { get; }
Task<SmsResponse> SendSmsAsync(string recipient, string message);
Task<SmsResponse> SendSmsCodeAsync(string recipient, string envelopeReceiverId);
}
}

View File

@@ -1,63 +0,0 @@
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;
namespace EnvelopeGenerator.Application
{
public static class DIExtensions
{
public static IServiceCollection AddEnvelopeGenerator(this IServiceCollection services, IConfiguration dispatcherConfigSection, IConfiguration mailConfigSection)
{
//Inject CRUD Service and repositoriesad
services.AddScoped<IConfigRepository, ConfigRepository>();
services.AddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
services.AddScoped<IEnvelopeDocumentRepository, EnvelopeDocumentRepository>();
services.AddScoped<IConfigRepository, ConfigRepository>();
services.AddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
services.AddScoped<IDocumentStatusRepository, DocumentStatusRepository>();
services.AddScoped<IEmailTemplateRepository, EmailTemplateRepository>();
services.AddScoped<IEnvelopeRepository, EnvelopeRepository>();
services.AddScoped<IEnvelopeCertificateRepository, EnvelopeCertificateRepository>();
services.AddScoped<IEnvelopeDocumentRepository, EnvelopeDocumentRepository>();
services.AddScoped<IEnvelopeHistoryRepository, EnvelopeHistoryRepository>();
services.AddScoped<IEnvelopeReceiverRepository, EnvelopeReceiverRepository>();
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>();
services.AddScoped<IEnvelopeHistoryService, EnvelopeHistoryService>();
services.AddScoped<IDocumentStatusService, DocumentStatusService>();
services.AddScoped<IEmailTemplateService, EmailTemplateService>();
services.AddScoped<IEnvelopeService, EnvelopeService>();
services.AddScoped<IEnvelopeCertificateService, EnvelopeCertificateService>();
services.AddScoped<IEnvelopeDocumentService, EnvelopeDocumentService>();
services.AddScoped<IEnvelopeReceiverService, EnvelopeReceiverService>();
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

@@ -1,4 +1,5 @@
using DigitalData.Core.Abstractions; using DigitalData.Core.Abstractions;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Application.DTOs namespace EnvelopeGenerator.Application.DTOs
@@ -8,11 +9,9 @@ namespace EnvelopeGenerator.Application.DTOs
int SendingProfile, int SendingProfile,
string SignatureHost, string SignatureHost,
string ExternalProgramName, string ExternalProgramName,
string ExportPath, string ExportPath) : IUnique<int>
string DocumentPathDmz,
string ExportPathDmz,
string DocumentPathMoveAftsend) : IUnique<int>
{ {
[NotMapped]
[JsonIgnore] [JsonIgnore]
[Obsolete("Configuration does not have an ID; it represents a single table in the database.")] [Obsolete("Configuration does not have an ID; it represents a single table in the database.")]
public int Id => throw new InvalidOperationException("This configuration does not support an ID as it represents a single row in the database."); public int Id => throw new InvalidOperationException("This configuration does not support an ID as it represents a single row in the database.");

View File

@@ -25,5 +25,7 @@ namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiver
public DateTime AddedWhen { get; init; } public DateTime AddedWhen { get; init; }
public DateTime? ChangedWhen { get; init; } public DateTime? ChangedWhen { get; init; }
public bool HasPhoneNumber { get; init; }
} }
} }

View File

@@ -1,4 +1,9 @@
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiver namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiver
{ {
public record EnvelopeReceiverSecretDto(string? AccessCode) : EnvelopeReceiverDto; public record EnvelopeReceiverSecretDto() : EnvelopeReceiverDto()
{
public string? AccessCode { get; init; }
public string? PhoneNumber { get; init; }
}
} }

View File

@@ -0,0 +1,19 @@
namespace EnvelopeGenerator.Application.DTOs.Messaging
{
public record SmsResponse
{
public required bool Ok { get; init; }
public DateTime? Expiration { get; set; }
public DateTime? AllowedAt { get; set; }
public TimeSpan AllowedAfter => Allowed ? TimeSpan.Zero : AllowedAt!.Value - DateTime.Now;
public bool Allowed => AllowedAt is null || DateTime.Now >= AllowedAt;
public bool Error => !Ok && Allowed;
public dynamic? Errors { get; init; }
}
}

View File

@@ -12,8 +12,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" /> <PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Application" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.Application" Version="2.0.0" />
<PackageReference Include="DigitalData.Core.Client" Version="2.0.3" />
<PackageReference Include="DigitalData.Core.DTO" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.DTO" Version="2.0.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="2.0.0" /> <PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.18" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.18" />

View File

@@ -0,0 +1,43 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EnvelopeGenerator.Application.Extensions
{
public static class CacheExtensions
{
public static Task SetLongAsync(this IDistributedCache cache, string key, long value, DistributedCacheEntryOptions? options = null)
=> options is null
? cache.SetAsync(key, BitConverter.GetBytes(value))
: cache.SetAsync(key, BitConverter.GetBytes(value), options: options);
public static async Task<long?> GetLongAsync(this IDistributedCache cache, string key)
{
var value = await cache.GetAsync(key);
return value is null ? null : BitConverter.ToInt64(value, 0);
}
public static Task SetDateTimeAsync(this IDistributedCache cache, string key, DateTime value, DistributedCacheEntryOptions? options = null)
=> cache.SetLongAsync(key: key, value: value.Ticks, options: options);
public static async Task<DateTime?> GetDateTimeAsync(this IDistributedCache cache, string key)
{
var value = await cache.GetAsync(key);
return value is null ? null : new(BitConverter.ToInt64(value, 0));
}
public static Task SetTimeSpanAsync(this IDistributedCache cache, string key, TimeSpan value, DistributedCacheEntryOptions? options = null)
=> cache.SetLongAsync(key: key, value: value.Ticks, options: options);
public static async Task<TimeSpan?> GetTimeSpanAsync(this IDistributedCache cache, string key)
{
var value = await cache.GetAsync(key);
return value is null ? null : new(BitConverter.ToInt64(value, 0));
}
}
}

View File

@@ -0,0 +1,76 @@
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 Microsoft.Extensions.DependencyInjection.Extensions;
using DigitalData.Core.Client;
using EnvelopeGenerator.Application.Configurations.GtxMessaging;
namespace EnvelopeGenerator.Application.Extensions
{
public static class DIExtensions
{
public static IServiceCollection AddEnvelopeGenerator(this IServiceCollection services, IConfigurationSection dispatcherConfigSection, IConfigurationSection mailConfigSection, IConfigurationSection smsConfigSection, IConfigurationSection codeGeneratorConfigSection, IConfigurationSection envelopeReceiverCacheParamsSection)
{
//Inject CRUD Service and repositoriesad
services.TryAddScoped<IConfigRepository, ConfigRepository>();
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
services.TryAddScoped<IEnvelopeDocumentRepository, EnvelopeDocumentRepository>();
services.TryAddScoped<IConfigRepository, ConfigRepository>();
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
services.TryAddScoped<IDocumentStatusRepository, DocumentStatusRepository>();
services.TryAddScoped<IEmailTemplateRepository, EmailTemplateRepository>();
services.TryAddScoped<IEnvelopeRepository, EnvelopeRepository>();
services.TryAddScoped<IEnvelopeCertificateRepository, EnvelopeCertificateRepository>();
services.TryAddScoped<IEnvelopeDocumentRepository, EnvelopeDocumentRepository>();
services.TryAddScoped<IEnvelopeHistoryRepository, EnvelopeHistoryRepository>();
services.TryAddScoped<IEnvelopeReceiverRepository, EnvelopeReceiverRepository>();
services.TryAddScoped<IEnvelopeTypeRepository, EnvelopeTypeRepository>();
services.TryAddScoped<IReceiverRepository, ReceiverRepository>();
services.TryAddScoped<IUserReceiverRepository, UserReceiverRepository>();
services.TryAddScoped<IEnvelopeReceiverReadOnlyRepository, EnvelopeReceiverReadOnlyRepository>();
services.TryAddScoped<IConfigService, ConfigService>();
services.TryAddScoped<IDocumentReceiverElementService, DocumentReceiverElementService>();
services.TryAddScoped<IEnvelopeDocumentService, EnvelopeDocumentService>();
services.TryAddScoped<IEnvelopeHistoryService, EnvelopeHistoryService>();
services.TryAddScoped<IDocumentStatusService, DocumentStatusService>();
services.TryAddScoped<IEmailTemplateService, EmailTemplateService>();
services.TryAddScoped<IEnvelopeService, EnvelopeService>();
services.TryAddScoped<IEnvelopeCertificateService, EnvelopeCertificateService>();
services.TryAddScoped<IEnvelopeDocumentService, EnvelopeDocumentService>();
services.TryAddScoped<IEnvelopeReceiverService, EnvelopeReceiverService>();
services.TryAddScoped<IEnvelopeTypeService, EnvelopeTypeService>();
services.TryAddScoped<IReceiverService, ReceiverService>();
services.TryAddScoped<IUserReceiverService, UserReceiverService>();
services.TryAddScoped<IEnvelopeReceiverReadOnlyService, EnvelopeReceiverReadOnlyService>();
//Auto mapping profiles
services.AddAutoMapper(typeof(BasicDtoMappingProfile).Assembly);
services.AddAutoMapper(typeof(UserMappingProfile).Assembly);
services.Configure<DispatcherConfig>(dispatcherConfigSection);
services.Configure<MailConfig>(mailConfigSection);
services.Configure<CodeGeneratorConfig>(codeGeneratorConfigSection);
services.Configure<EnvelopeReceiverCacheParams>(envelopeReceiverCacheParamsSection);
services.AddHttpClientService<SmsParams>(smsConfigSection);
services.TryAddSingleton<IMessagingService, GtxMessagingService>();
services.TryAddSingleton<ICodeGenerator, CodeGenerator>();
services.TryAddSingleton<IEnvelopeReceiverCache, EnvelopeReceiverCache>();
return services;
}
public static IServiceCollection AddEnvelopeGenerator(this IServiceCollection services, IConfiguration config) => services.AddEnvelopeGenerator(
dispatcherConfigSection: config.GetSection("DispatcherConfig"),
mailConfigSection: config.GetSection("MailConfig"),
smsConfigSection: config.GetSection("SmsConfig"),
codeGeneratorConfigSection: config.GetSection("CodeGeneratorConfig"),
envelopeReceiverCacheParamsSection: config.GetSection("EnvelopeReceiverCacheParams"));
}
}

View File

@@ -0,0 +1,12 @@
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Domain.HttpResponse;
namespace EnvelopeGenerator.Application.Extensions
{
public static class MappingExtensions
{
public static bool Ok(this GtxMessagingResponse gtxMessagingResponse)
=> gtxMessagingResponse.TryGetValue("message-status", out var status)
&& status?.ToString()?.ToLower() == "ok";
}
}

View File

@@ -14,6 +14,7 @@
public static readonly string PossibleSecurityBreach = nameof(PossibleSecurityBreach); public static readonly string PossibleSecurityBreach = nameof(PossibleSecurityBreach);
public static readonly string WrongEnvelopeReceiverId = nameof(WrongEnvelopeReceiverId); public static readonly string WrongEnvelopeReceiverId = nameof(WrongEnvelopeReceiverId);
public static readonly string EnvelopeOrReceiverNonexists = nameof(EnvelopeOrReceiverNonexists); public static readonly string EnvelopeOrReceiverNonexists = nameof(EnvelopeOrReceiverNonexists);
public static readonly string PhoneNumberNonexists = nameof(PhoneNumberNonexists);
public static readonly string Default = nameof(Default); public static readonly string Default = nameof(Default);
} }
} }

View File

@@ -3,8 +3,11 @@ using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Application.DTOs.EnvelopeHistory; using EnvelopeGenerator.Application.DTOs.EnvelopeHistory;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver; using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly; using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Application.DTOs.Messaging;
using EnvelopeGenerator.Application.DTOs.Receiver; using EnvelopeGenerator.Application.DTOs.Receiver;
using EnvelopeGenerator.Application.Extensions;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Domain.HttpResponse;
namespace EnvelopeGenerator.Application.MappingProfiles namespace EnvelopeGenerator.Application.MappingProfiles
{ {
@@ -50,6 +53,13 @@ namespace EnvelopeGenerator.Application.MappingProfiles
CreateMap<EnvelopeReceiverBase, EnvelopeReceiverBasicDto>(); CreateMap<EnvelopeReceiverBase, EnvelopeReceiverBasicDto>();
CreateMap<EnvelopeReceiverReadOnlyCreateDto, EnvelopeReceiverReadOnly>(); CreateMap<EnvelopeReceiverReadOnlyCreateDto, EnvelopeReceiverReadOnly>();
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, EnvelopeReceiverReadOnly>(); CreateMap<EnvelopeReceiverReadOnlyUpdateDto, EnvelopeReceiverReadOnly>();
// Messaging mappings
// for GTX messaging
CreateMap<GtxMessagingResponse, SmsResponse>()
.ConstructUsing(gtxRes => gtxRes.Ok()
? new SmsResponse() { Ok = true }
: new SmsResponse() { Ok = false, Errors = gtxRes });
} }
} }
} }

View File

@@ -156,6 +156,9 @@
<data name="Hello" xml:space="preserve"> <data name="Hello" xml:space="preserve">
<value>Hallo</value> <value>Hallo</value>
</data> </data>
<data name="HomePageDescription" xml:space="preserve">
<value>Das digitale Unterschriftenportal ist eine Plattform, die entwickelt wurde, um Ihre Dokumente sicher zu unterschreiben und zu verwalten. Mit seiner benutzerfreundlichen Oberfläche können Sie Ihre Dokumente schnell hochladen, die Unterschriftsprozesse verfolgen und Ihre digitalen Unterschriftenanwendungen einfach durchführen. Dieses Portal beschleunigt Ihren Arbeitsablauf mit rechtlich gültigen Unterschriften und erhöht gleichzeitig die Sicherheit Ihrer Dokumente.</value>
</data>
<data name="LocakedOpen" xml:space="preserve"> <data name="LocakedOpen" xml:space="preserve">
<value>Öffnen</value> <value>Öffnen</value>
</data> </data>
@@ -174,9 +177,27 @@
<data name="LockedFooterTitle" xml:space="preserve"> <data name="LockedFooterTitle" xml:space="preserve">
<value>Sie haben keinen Zugriffscode erhalten?</value> <value>Sie haben keinen Zugriffscode erhalten?</value>
</data> </data>
<data name="LockedSmsAccessCode" xml:space="preserve">
<value>SMS-Code</value>
</data>
<data name="LockedSmsTfaBody" xml:space="preserve">
<value>Wir haben soeben den Zugangscode als SMS an die von Ihnen angegebene Telefonnummer gesendet.</value>
</data>
<data name="LockedSmsTfaFooterBody" xml:space="preserve">
<value>Sie können den Absender bitten, Ihre Rufnummer zu überprüfen. Die Telefonnummer muss mit der Ortsvorwahl eingegeben werden. Andernfalls können Sie beantragen, den Zwei-Faktor-Schutz zu entfernen.</value>
</data>
<data name="LockedSmsTfaFooterTitle" xml:space="preserve">
<value>Sie haben keine SMS erhalten?</value>
</data>
<data name="LockedSmsTfaTitle" xml:space="preserve">
<value>2-Faktor-Authentifizierung</value>
</data>
<data name="LockedTitle" xml:space="preserve"> <data name="LockedTitle" xml:space="preserve">
<value>Dokument erfordert einen Zugriffscode</value> <value>Dokument erfordert einen Zugriffscode</value>
</data> </data>
<data name="Privacy" xml:space="preserve">
<value>Datenschutz</value>
</data>
<data name="ReadOnlyMessage" xml:space="preserve"> <data name="ReadOnlyMessage" xml:space="preserve">
<value>Weitergeleitet von {0}. Gültig bis {1}.</value> <value>Weitergeleitet von {0}. Gültig bis {1}.</value>
</data> </data>

View File

@@ -156,6 +156,9 @@
<data name="Hello" xml:space="preserve"> <data name="Hello" xml:space="preserve">
<value>Hello</value> <value>Hello</value>
</data> </data>
<data name="HomePageDescription" xml:space="preserve">
<value>The Digital Signature Portal is a platform developed for securely signing and managing your documents. With its user-friendly interface, you can quickly upload your documents, track the signing processes, and easily carry out your digital signature applications. This portal accelerates your workflow with legally valid signatures while enhancing the security of your documents.</value>
</data>
<data name="LocakedOpen" xml:space="preserve"> <data name="LocakedOpen" xml:space="preserve">
<value>Open</value> <value>Open</value>
</data> </data>
@@ -174,9 +177,27 @@
<data name="LockedFooterTitle" xml:space="preserve"> <data name="LockedFooterTitle" xml:space="preserve">
<value>You have not received an access code?</value> <value>You have not received an access code?</value>
</data> </data>
<data name="LockedSmsAccessCode" xml:space="preserve">
<value>SMS Code</value>
</data>
<data name="LockedSmsTfaBody" xml:space="preserve">
<value>We have just sent the access code as an SMS to the phone number you provided.</value>
</data>
<data name="LockedSmsTfaFooterBody" xml:space="preserve">
<value>You can ask the sender to check your phone number. The phone number must be entered with the area code. Otherwise you can request to remove the two-factor protection.</value>
</data>
<data name="LockedSmsTfaFooterTitle" xml:space="preserve">
<value>You have not received an SMS?</value>
</data>
<data name="LockedSmsTfaTitle" xml:space="preserve">
<value>2-Factor Authentication</value>
</data>
<data name="LockedTitle" xml:space="preserve"> <data name="LockedTitle" xml:space="preserve">
<value>Document requires an access code</value> <value>Document requires an access code</value>
</data> </data>
<data name="Privacy" xml:space="preserve">
<value>Privacy</value>
</data>
<data name="ReadOnlyMessage" xml:space="preserve"> <data name="ReadOnlyMessage" xml:space="preserve">
<value>Forwarded by {0}. Valid until {1}.</value> <value>Forwarded by {0}. Valid until {1}.</value>
</data> </data>

View File

@@ -0,0 +1,37 @@
using EnvelopeGenerator.Application.Configurations;
using EnvelopeGenerator.Application.Contracts;
using Microsoft.Extensions.Options;
using System.Text;
namespace EnvelopeGenerator.Application.Services
{
public class CodeGenerator : ICodeGenerator
{
public static Lazy<CodeGenerator> LazyStatic => new(() => new CodeGenerator(Options.Create<CodeGeneratorConfig>(new())));
public static CodeGenerator Static => LazyStatic.Value;
private readonly string _charPool;
public CodeGenerator(IOptions<CodeGeneratorConfig> options)
{
_charPool = options.Value.CharPool;
}
public string GenerateCode(int length)
{
//TODO: Inject Random as a singleton to support multithreading to improve performance.
Random random = new();
if (length <= 0)
throw new ArgumentException("Password length must be greater than 0.");
var passwordBuilder = new StringBuilder(length);
for (int i = 0; i < length; i++)
passwordBuilder.Append(_charPool[random.Next(_charPool.Length)]);
return passwordBuilder.ToString();
}
}
}

View File

@@ -0,0 +1,50 @@
using AngleSharp.Dom;
using EnvelopeGenerator.Application.Configurations;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.Extensions;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
namespace EnvelopeGenerator.Application.Services
{
public class EnvelopeReceiverCache : IEnvelopeReceiverCache
{
private readonly EnvelopeReceiverCacheParams _cacheParams;
private readonly DistributedCacheEntryOptions _codeCacheOptions;
private readonly IDistributedCache _cache;
public EnvelopeReceiverCache(IOptions<EnvelopeReceiverCacheParams> cacheParamOptions, IDistributedCache cache)
{
_cacheParams = cacheParamOptions.Value;
_codeCacheOptions = new() { AbsoluteExpirationRelativeToNow = cacheParamOptions.Value.CodeCacheValidityPeriod };
_cache = cache;
}
public async Task<string?> GetSmsCodeAsync(string envelopeReceiverId)
{
var code_key = string.Format(_cacheParams.CodeCacheKeyFormat, envelopeReceiverId);
return await _cache.GetStringAsync(code_key);
}
public async Task<DateTime> SetSmsCodeAsync(string envelopeReceiverId, string code)
{
// set key
var code_key = string.Format(_cacheParams.CodeCacheKeyFormat, envelopeReceiverId);
await _cache.SetStringAsync(code_key, code, _codeCacheOptions);
// set expiration
var code_expiration_key = string.Format(_cacheParams.CodeExpirationCacheKeyFormat, envelopeReceiverId);
var expiration = DateTime.Now + _cacheParams.CodeCacheValidityPeriod;
await _cache.SetDateTimeAsync(code_expiration_key, expiration, _codeCacheOptions);
return expiration;
}
public async Task<DateTime?> GetSmsCodeExpirationAsync(string envelopeReceiverId)
{
var code_expiration_key = string.Format(_cacheParams.CodeExpirationCacheKeyFormat, envelopeReceiverId);
return await _cache.GetDateTimeAsync(code_expiration_key);
}
}
}

View File

@@ -9,6 +9,7 @@ using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using EnvelopeGenerator.Extensions; using EnvelopeGenerator.Extensions;
using EnvelopeGenerator.Application.DTOs.Messaging;
namespace EnvelopeGenerator.Application.Services namespace EnvelopeGenerator.Application.Services
{ {
@@ -16,10 +17,13 @@ namespace EnvelopeGenerator.Application.Services
{ {
private readonly IStringLocalizer<Resource> _localizer; private readonly IStringLocalizer<Resource> _localizer;
public EnvelopeReceiverService(IEnvelopeReceiverRepository repository, IStringLocalizer<Resource> localizer, IMapper mapper) private readonly IMessagingService _messagingService;
public EnvelopeReceiverService(IEnvelopeReceiverRepository repository, IStringLocalizer<Resource> localizer, IMapper mapper, IMessagingService messagingService)
: base(repository, mapper) : base(repository, mapper)
{ {
_localizer = localizer; _localizer = localizer;
_messagingService = messagingService;
} }
public async Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true) public async Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true)
@@ -34,10 +38,10 @@ namespace EnvelopeGenerator.Application.Services
return Result.Success(_mapper.Map<IEnumerable<EnvelopeReceiverDto>>(env_rcvs)); return Result.Success(_mapper.Map<IEnumerable<EnvelopeReceiverDto>>(env_rcvs));
} }
public async Task<DataResult<IEnumerable<EnvelopeReceiverSecretDto>>> ReadSecretByUuidAsync(string uuid, bool withEnvelope = false, bool withReceiver = true) public async Task<DataResult<IEnumerable<string?>>> ReadAccessCodeByUuidAsync(string uuid, bool withEnvelope = false, bool withReceiver = true)
{ {
var env_rcvs = await _repository.ReadByUuidAsync(uuid: uuid, withEnvelope: withEnvelope, withReceiver: withReceiver); var env_rcvs = await _repository.ReadByUuidAsync(uuid: uuid, withEnvelope: withEnvelope, withReceiver: withReceiver);
return Result.Success(_mapper.Map<IEnumerable<EnvelopeReceiverSecretDto>>(env_rcvs)); return Result.Success(env_rcvs.Select(er => er.AccessCode));
} }
public async Task<DataResult<EnvelopeReceiverDto>> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true) public async Task<DataResult<EnvelopeReceiverDto>> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true)
@@ -50,6 +54,16 @@ namespace EnvelopeGenerator.Application.Services
return Result.Success(_mapper.Map<EnvelopeReceiverDto>(env_rcv)); return Result.Success(_mapper.Map<EnvelopeReceiverDto>(env_rcv));
} }
public async Task<DataResult<EnvelopeReceiverSecretDto>> ReadWithSecretByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true)
{
var env_rcv = await _repository.ReadByUuidSignatureAsync(uuid: uuid, signature: signature, withEnvelope: withEnvelope, withReceiver: withReceiver);
if (env_rcv is null)
return Result.Fail<EnvelopeReceiverSecretDto>()
.Message(Key.EnvelopeReceiverNotFound);
return Result.Success(_mapper.Map<EnvelopeReceiverSecretDto>(env_rcv));
}
public async Task<DataResult<EnvelopeReceiverDto>> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true) public async Task<DataResult<EnvelopeReceiverDto>> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true)
{ {
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); (string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
@@ -135,5 +149,31 @@ namespace EnvelopeGenerator.Application.Services
var er = await _repository.ReadLastByReceiver(mail); var er = await _repository.ReadLastByReceiver(mail);
return er is null ? Result.Fail<string?>().Notice(LogLevel.None, Flag.NotFound) : Result.Success(er.Name); return er is null ? Result.Fail<string?>().Notice(LogLevel.None, Flag.NotFound) : Result.Success(er.Name);
} }
public async Task<DataResult<SmsResponse>> SendSmsAsync(string envelopeReceiverId, string message)
{
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
if (uuid is null || signature is null)
return Result.Fail<SmsResponse>()
.Message(_localizer[Key.WrongEnvelopeReceiverId])
.Notice(LogLevel.Warning, (uuid, signature).ToTitle())
.Notice(LogLevel.Warning, EnvelopeFlag.WrongEnvelopeReceiverId)
.Notice(LogLevel.Warning, Flag.PossibleSecurityBreach);
var env_rcv = await _repository.ReadByUuidSignatureAsync(uuid: uuid, signature: signature, withEnvelope: false, withReceiver: false);
if (env_rcv is null)
return Result.Fail<SmsResponse>()
.Message(Key.EnvelopeReceiverNotFound);
if (env_rcv.PhoneNumber is null)
return Result.Fail<SmsResponse>()
.Message(Key.PhoneNumberNonexists)
.Notice(LogLevel.Error, Flag.NotFound, $"An attempt was made to send sms to the user whose phone number is null. Envelope recipient ID is {envelopeReceiverId}, UUID is {uuid} and signature is {signature}.");
var res = await _messagingService.SendSmsAsync(recipient: env_rcv.PhoneNumber, message: message);
return Result.Success(res);
}
} }
} }

View File

@@ -0,0 +1,70 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Client;
using DigitalData.Core.Client;
using EnvelopeGenerator.Application.Configurations.GtxMessaging;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs.Messaging;
using EnvelopeGenerator.Application.Extensions;
using EnvelopeGenerator.Domain.HttpResponse;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
namespace EnvelopeGenerator.Application.Services
{
public class GtxMessagingService : IMessagingService
{
private readonly IHttpClientService<SmsParams> _smsClient;
private readonly SmsParams _smsParams;
private readonly IMapper _mapper;
private readonly ICodeGenerator _codeGen;
private readonly IEnvelopeReceiverCache _erCache;
public string ServiceProvider { get; }
public GtxMessagingService(IHttpClientService<SmsParams> smsClient, IOptions<SmsParams> smsParamsOptions, IMapper mapper, ICodeGenerator codeGenerator, IEnvelopeReceiverCache envelopeReceiverCache)
{
_smsClient = smsClient;
_smsParams = smsParamsOptions.Value;
_mapper = mapper;
ServiceProvider = GetType().Name.Replace("Service", string.Empty);
_codeGen = codeGenerator;
_erCache = envelopeReceiverCache;
}
public async Task<SmsResponse> SendSmsAsync(string recipient, string message)
{
return await _smsClient.FetchAsync(queryParams: new Dictionary<string, object?>()
{
{ _smsParams.RecipientQueryParamName, recipient },
{ _smsParams.MessageQueryParamName, message }
})
.ThenAsync(res => res.Json<GtxMessagingResponse>())
.ThenAsync(_mapper.Map<SmsResponse>);
}
public async Task<SmsResponse> SendSmsCodeAsync(string recipient, string envelopeReceiverId)
{
var code = await _erCache.GetSmsCodeAsync(envelopeReceiverId);
if (code is null)
{
code = _codeGen.GenerateCode(_smsParams.CodeLength);
var expiration = await _erCache.SetSmsCodeAsync(envelopeReceiverId, code);
var res = await SendSmsAsync(recipient: recipient, message: code);
res.Expiration = expiration;
return res;
}
else
{
var code_expiration = await _erCache.GetSmsCodeExpirationAsync(envelopeReceiverId);
return code_expiration is null
? new() { Ok = false }
: new() { Ok = false, AllowedAt = code_expiration };
}
}
}
}

View File

@@ -78,7 +78,7 @@
Public Enum CertificationType Public Enum CertificationType
ElectronicSignature = 1 ElectronicSignature = 1
QualifiedSignature = 2 'QualifiedSignature = 2
End Enum End Enum
Public Enum FinalEmailType Public Enum FinalEmailType

View File

@@ -3,13 +3,6 @@
Public Property DocumentPathOrigin As String = "" Public Property DocumentPathOrigin As String = ""
Public Property DocumentPath As String = "" Public Property DocumentPath As String = ""
Public Property ExportPath As String = "" Public Property ExportPath As String = ""
Public Property DocumentPath_DMZ As String = ""
Public Property ExportPath_DMZ As String = ""
Public Property DOCUMENT_PATH_MOVE_AFTSEND As String = ""
Public Property FINISHED_PATH_EX_DMZ As String = ""
Public Property EML_PATH_EX_DMZ As String = ""
Public Property SendingProfile As Integer = 0 Public Property SendingProfile As Integer = 0
Public Property SignatureHost As String = "" Public Property SignatureHost As String = ""
Public Property NetUse_necessary As Boolean = False
Public Property NetUse_Finish As Boolean = False
End Class End Class

View File

@@ -260,11 +260,13 @@
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Model.en.Designer.vb</LastGenOutput> <LastGenOutput>Model.en.Designer.vb</LastGenOutput>
<CustomToolNamespace>My.Resources</CustomToolNamespace> <CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Strings\Model.resx"> <EmbeddedResource Include="Strings\Model.resx">
<CustomToolNamespace>My.Resources</CustomToolNamespace> <CustomToolNamespace>My.Resources</CustomToolNamespace>
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Model.Designer.vb</LastGenOutput> <LastGenOutput>Model.Designer.vb</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -82,49 +82,14 @@ Namespace Jobs
Logger.Debug("Loading ReportCreator..") Logger.Debug("Loading ReportCreator..")
ReportCreator = New ReportCreator(LogConfig, oState) ReportCreator = New ReportCreator(LogConfig, oState)
Logger.Debug("My.Settings.RuninDMZ: [{0}]", My.Settings.RuninDMZ.ToString)
Logger.Debug("My.Settings.NetUse_Usr: [{0}]", My.Settings.NetUse_Usr)
Config.DocumentPath = Config.DocumentPath
If My.Settings.RuninDMZ = True Then
If Config.DocumentPath_DMZ <> String.Empty Then
Logger.Debug("RuninDMZ - Using DocumentPath_DMZ: [{0}] - Overwrite Document-Path", Config.DocumentPath_DMZ)
Config.DocumentPath = Config.DocumentPath_DMZ
Config.NetUse_necessary = True
Else
Config.DocumentPath = Config.DocumentPath
End If
Else
If Config.DOCUMENT_PATH_MOVE_AFTSEND <> String.Empty Then
Logger.Debug("Using DMZRemotePath: [{0}] - Overwrite Document-Path ...", Config.DOCUMENT_PATH_MOVE_AFTSEND)
Config.DocumentPath = Config.DOCUMENT_PATH_MOVE_AFTSEND
Config.NetUse_Finish = True
Else
Config.DocumentPath = Config.DocumentPath
End If
End If
Logger.Debug("DocumentPath: [{0}]", Config.DocumentPath) Logger.Debug("DocumentPath: [{0}]", Config.DocumentPath)
If My.Settings.RuninDMZ = True Then
If Config.FINISHED_PATH_EX_DMZ <> String.Empty Then
Logger.Debug("RuninDMZ - FINISHED_PATH_EX_DMZ configured: [{0}]", Config.FINISHED_PATH_EX_DMZ)
Config.NetUse_Finish = True
End If
If Config.ExportPath_DMZ <> String.Empty Then
Logger.Debug("RuninDMZ - Using ExportPath_DMZ: [{0}] - Overwrite ExportPath", Config.ExportPath_DMZ)
Config.ExportPath = Config.ExportPath_DMZ
End If
End If
Logger.Debug("ExportPath: [{0}]", Config.ExportPath) Logger.Debug("ExportPath: [{0}]", Config.ExportPath)
If Config.NetUse_Finish = True Then
If NetUse_Command(Config.DocumentPath, My.Settings.NetUse_Usr, My.Settings.NetUse_PW) = True Then
Logger.Debug("NetUse_Finish = successful!")
End If
End If
Dim oCompleteStatus As Integer = Constants.EnvelopeStatus.EnvelopeCompletelySigned Dim oCompleteStatus As Integer = Constants.EnvelopeStatus.EnvelopeCompletelySigned
Dim oSql = $"SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = {oCompleteStatus} AND DATEDIFF(minute, CHANGED_WHEN, GETDATE()) >= {CompleteWaitTime} ORDER BY GUID" Dim oSql = $"SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = {oCompleteStatus} AND DATEDIFF(minute, CHANGED_WHEN, GETDATE()) >= {CompleteWaitTime} ORDER BY GUID"
Dim oTable = Database.GetDatatable(oSql) Dim oTable = Database.GetDatatable(oSql)
@@ -157,10 +122,6 @@ Namespace Jobs
Logger.Warn("EnvelopeData could not be loaded for Id [{0}]!", oId) Logger.Warn("EnvelopeData could not be loaded for Id [{0}]!", oId)
Throw New ArgumentNullException("EnvelopeData") Throw New ArgumentNullException("EnvelopeData")
End If End If
If Config.DOCUMENT_PATH_MOVE_AFTSEND <> String.Empty Then
oEnvelopeData.DocumentPath.Replace(Config.DocumentPathOrigin, Config.DOCUMENT_PATH_MOVE_AFTSEND)
Logger.Debug("Replaced Path in oEnvelopeData.DocumentPath!")
End If
Logger.Debug("Burning Annotations to pdf ...") Logger.Debug("Burning Annotations to pdf ...")
Dim oBurnedDocument As Byte() = BurnAnnotationsToPdf(oEnvelopeData) Dim oBurnedDocument As Byte() = BurnAnnotationsToPdf(oEnvelopeData)
If oBurnedDocument Is Nothing Then If oBurnedDocument Is Nothing Then
@@ -196,13 +157,6 @@ Namespace Jobs
Throw New ExportDocumentException("Could not export final document to disk!", ex) Throw New ExportDocumentException("Could not export final document to disk!", ex)
End Try End Try
If Config.NetUse_Finish = True Then
If Config.FINISHED_PATH_EX_DMZ <> String.Empty Then
If My.Settings.NetUse_PW <> String.Empty And My.Settings.NetUse_Usr <> String.Empty Then
Clean_DNZ_PAth(Config.FINISHED_PATH_EX_DMZ)
End If
End If
End If
Logger.Info("Writing EB-bytes to database...") Logger.Info("Writing EB-bytes to database...")
Update_File_DB(oOutputFilePath, oEnvelope.Id) Update_File_DB(oOutputFilePath, oEnvelope.Id)
@@ -287,48 +241,48 @@ Namespace Jobs
data = br.ReadBytes(CInt(numBytes)) data = br.ReadBytes(CInt(numBytes))
Return data Return data
End Function End Function
Private Function NetUse_Command(pDestinationPath As String, pUsername As String, pPassword As String) 'Private Function NetUse_Command(pDestinationPath As String, pUsername As String, pPassword As String)
Dim oDectryptedPW = Helpers.Decrypt(My.Settings.NetUse_PW) ' Dim oDectryptedPW = Helpers.Decrypt(My.Settings.NetUse_PW)
Dim netUseCommand As String = $"net use {pDestinationPath} /user:{pUsername} {oDectryptedPW}" ' Dim netUseCommand As String = $"net use {pDestinationPath} /user:{pUsername} {oDectryptedPW}"
Logger.Debug("EXECUTING NetUse_Command for " & pDestinationPath) ' Logger.Debug("EXECUTING NetUse_Command for " & pDestinationPath)
Dim processInfo As New ProcessStartInfo("cmd.exe", $"/C {netUseCommand}") ' Dim processInfo As New ProcessStartInfo("cmd.exe", $"/C {netUseCommand}")
processInfo.RedirectStandardOutput = True ' processInfo.RedirectStandardOutput = True
processInfo.UseShellExecute = False ' processInfo.UseShellExecute = False
processInfo.CreateNoWindow = True ' processInfo.CreateNoWindow = True
Using process As Process = Process.Start(processInfo) ' Using process As Process = Process.Start(processInfo)
process.WaitForExit() ' process.WaitForExit()
' Prüfe den Rückgabewert des net use Befehls ' ' Prüfe den Rückgabewert des net use Befehls
If process.ExitCode = 0 Then ' If process.ExitCode = 0 Then
Return True ' Return True
Else ' Else
Return False ' Return False
End If ' End If
End Using ' End Using
End Function 'End Function
Private Function Clean_DNZ_PAth(pSourcePath As String) As Boolean 'Private Function Clean_DNZ_PAth(pSourcePath As String) As Boolean
Dim oFilename = System.IO.Path.GetFileName(pSourcePath) ' Dim oFilename = System.IO.Path.GetFileName(pSourcePath)
Logger.Debug("## Starting Clean_DNZ_PAth ...") ' Logger.Debug("## Starting Clean_DNZ_PAth ...")
Logger.Debug("## pSourcePath {0}", pSourcePath) ' Logger.Debug("## pSourcePath {0}", pSourcePath)
Dim oDirectorySource = Path.Combine(pSourcePath, ParentFolderUID) ' Dim oDirectorySource = Path.Combine(pSourcePath, ParentFolderUID)
Try ' Try
Logger.Debug($"Deleting oDirectorySource {oDirectorySource} ...") ' Logger.Debug($"Deleting oDirectorySource {oDirectorySource} ...")
Directory.Delete(oDirectorySource, True) ' Directory.Delete(oDirectorySource, True)
Console.WriteLine($"Folder successfully deleted!") ' Console.WriteLine($"Folder successfully deleted!")
Logger.Debug($"...Deleted!") ' Logger.Debug($"...Deleted!")
Return True ' Return True
Catch ex As Exception ' Catch ex As Exception
Logger.Error(ex) ' Logger.Error(ex)
Return False ' Return False
End Try ' End Try
End Function 'End Function
Private Function SendFinalEmails(pEnvelope As Envelope) As Boolean ', pAttachment As String Private Function SendFinalEmails(pEnvelope As Envelope) As Boolean ', pAttachment As String
Dim oMailToCreator = pEnvelope.FinalEmailToCreator Dim oMailToCreator = pEnvelope.FinalEmailToCreator
Dim oMailToReceivers = pEnvelope.FinalEmailToReceivers Dim oMailToReceivers = pEnvelope.FinalEmailToReceivers
@@ -398,15 +352,7 @@ Namespace Jobs
Dim oAnnotations = pEnvelopeData.AnnotationData Dim oAnnotations = pEnvelopeData.AnnotationData
Dim oInputPath = "" Dim oInputPath = ""
If IsNothing(pEnvelopeData.DocAsByte) Then If IsNothing(pEnvelopeData.DocAsByte) Then
If My.Settings.RuninDMZ Then oInputPath = pEnvelopeData.DocumentPath
Logger.Debug("Replacing Path in pData.DocumentPath ...")
oInputPath = pEnvelopeData.DocumentPath.Replace(Config.DocumentPathOrigin, Config.DocumentPath)
ElseIf Config.DOCUMENT_PATH_MOVE_AFTSEND <> String.Empty Then
Logger.Debug("Replacing Path in pData.DocumentPath ...")
oInputPath = pEnvelopeData.DocumentPath.Replace(Config.DocumentPathOrigin, Config.DOCUMENT_PATH_MOVE_AFTSEND)
Else
oInputPath = pEnvelopeData.DocumentPath
End If
Logger.Info($"Input path: [{oInputPath}]") Logger.Info($"Input path: [{oInputPath}]")
Else Else
Logger.Info($"we got bytes..") Logger.Info($"we got bytes..")

View File

@@ -16,17 +16,12 @@ Public Class ConfigModel
Dim oRow As DataRow = oTable.Rows.Item(0) Dim oRow As DataRow = oTable.Rows.Item(0)
Return New DbConfig() With { Return New DbConfig() With {
.DocumentPath = oRow.ItemEx("DOCUMENT_PATH", ""), .DocumentPath = oRow.ItemEx("DOCUMENT_PATH", ""),
.DocumentPathOrigin = oRow.ItemEx("DOCUMENT_PATH", ""), .DocumentPathOrigin = oRow.ItemEx("DOCUMENT_PATH", ""),
.DocumentPath_DMZ = oRow.ItemEx("DOCUMENT_PATH_DMZ", ""), .ExportPath = oRow.ItemEx("EXPORT_PATH", ""),
.ExportPath = oRow.ItemEx("EXPORT_PATH", ""), .SendingProfile = oRow.ItemEx("SENDING_PROFILE", 0),
.ExportPath_DMZ = oRow.ItemEx("EXPORT_PATH_DMZ", ""), .SignatureHost = oRow.ItemEx("SIGNATURE_HOST", ""),
.SendingProfile = oRow.ItemEx("SENDING_PROFILE", 0), .ExternalProgramName = oRow.ItemEx("EXTERNAL_PROGRAM_NAME", "")
.SignatureHost = oRow.ItemEx("SIGNATURE_HOST", ""),
.ExternalProgramName = oRow.ItemEx("EXTERNAL_PROGRAM_NAME", ""),
.DOCUMENT_PATH_MOVE_AFTSEND = oRow.ItemEx("DOCUMENT_PATH_MOVE_AFTSEND", ""),
.FINISHED_PATH_EX_DMZ = oRow.ItemEx("FINISHED_PATH_EX_DMZ", ""),
.EML_PATH_EX_DMZ = oRow.ItemEx("EML_PATH_EX_DMZ", "")
} }
Catch ex As Exception Catch ex As Exception
Logger.Error(ex) Logger.Error(ex)

View File

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

View File

@@ -15,7 +15,7 @@ Option Explicit On
Namespace My Namespace My
<Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _ <Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0"), _ Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.9.0.0"), _
Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _ Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Partial Friend NotInheritable Class MySettings Partial Friend NotInheritable Class MySettings
Inherits Global.System.Configuration.ApplicationSettingsBase Inherits Global.System.Configuration.ApplicationSettingsBase
@@ -53,33 +53,6 @@ Namespace My
Return defaultInstance Return defaultInstance
End Get End Get
End Property End Property
<Global.System.Configuration.ApplicationScopedSettingAttribute(), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Configuration.DefaultSettingValueAttribute("False")> _
Public ReadOnly Property RuninDMZ() As Boolean
Get
Return CType(Me("RuninDMZ"),Boolean)
End Get
End Property
<Global.System.Configuration.ApplicationScopedSettingAttribute(), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Configuration.DefaultSettingValueAttribute("dd-gan\Administrator")> _
Public ReadOnly Property NetUse_Usr() As String
Get
Return CType(Me("NetUse_Usr"),String)
End Get
End Property
<Global.System.Configuration.ApplicationScopedSettingAttribute(), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Configuration.DefaultSettingValueAttribute("sY4vnATDXwosbTJGip6SqA==")> _
Public ReadOnly Property NetUse_PW() As String
Get
Return CType(Me("NetUse_PW"),String)
End Get
End Property
End Class End Class
End Namespace End Namespace

View File

@@ -1,15 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="My" GeneratedClassName="MySettings" UseMySettingsClassName="true"> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" UseMySettingsClassName="true">
<Profiles /> <Profiles />
<Settings> <Settings />
<Setting Name="RuninDMZ" Type="System.Boolean" Scope="Application">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="NetUse_Usr" Type="System.String" Scope="Application">
<Value Profile="(Default)">dd-gan\Administrator</Value>
</Setting>
<Setting Name="NetUse_PW" Type="System.String" Scope="Application">
<Value Profile="(Default)">sY4vnATDXwosbTJGip6SqA==</Value>
</Setting>
</Settings>
</SettingsFile> </SettingsFile>

View File

@@ -45,7 +45,7 @@ Public Class ActionService
Return oSendResult Return oSendResult
End Function End Function
Public Function ResendReceiver(pEnvelope As Envelope, pReceiver As EnvelopeReceiver) As Boolean Public Function ResendReceiver(pEnvelope As Envelope, pReceiver As EnvelopeReceiver) As Boolean
EmailService.SendDocumentReceivedEmail(pEnvelope, pReceiver) Return EmailService.SendDocumentReceivedEmail(pEnvelope, pReceiver)
End Function End Function

View File

@@ -168,6 +168,9 @@
<data name="Envelope-Editor" xml:space="preserve"> <data name="Envelope-Editor" xml:space="preserve">
<value>Envelope-Editor</value> <value>Envelope-Editor</value>
</data> </data>
<data name="Error email Validation" xml:space="preserve">
<value>The email [ @Mail ] could not be varified!</value>
</data>
<data name="Error sending the envelope" xml:space="preserve"> <data name="Error sending the envelope" xml:space="preserve">
<value>Error sending the envelope:</value> <value>Error sending the envelope:</value>
</data> </data>

View File

@@ -168,6 +168,9 @@
<data name="Envelope-Editor" xml:space="preserve"> <data name="Envelope-Editor" xml:space="preserve">
<value>Umschlag-Editor</value> <value>Umschlag-Editor</value>
</data> </data>
<data name="Error email Validation" xml:space="preserve">
<value>Die Email-Adresse [ @Mail ] konnte nicht validiert werden!</value>
</data>
<data name="Error sending the envelope" xml:space="preserve"> <data name="Error sending the envelope" xml:space="preserve">
<value>Fehler beim Senden des Umschlags:</value> <value>Fehler beim Senden des Umschlags:</value>
</data> </data>

View File

@@ -217,6 +217,15 @@ Namespace My.Resources
End Get End Get
End Property End Property
'''<summary>
''' Sucht eine lokalisierte Zeichenfolge, die Die Email-Adresse [ @Mail ] konnte nicht validiert werden! ähnelt.
'''</summary>
Public Shared ReadOnly Property Error_email_Validation() As String
Get
Return ResourceManager.GetString("Error email Validation", resourceCulture)
End Get
End Property
'''<summary> '''<summary>
''' Sucht eine lokalisierte Zeichenfolge, die Fehler beim Senden des Umschlags: ähnelt. ''' Sucht eine lokalisierte Zeichenfolge, die Fehler beim Senden des Umschlags: ähnelt.
'''</summary> '''</summary>

View File

@@ -306,16 +306,16 @@ Namespace My.Resources
Return ResourceManager.GetString("PartlySigned", resourceCulture) Return ResourceManager.GetString("PartlySigned", resourceCulture)
End Get End Get
End Property End Property
'''<summary> ''''<summary>
''' Sucht eine lokalisierte Zeichenfolge, die Qualifizierte Signatur ähnelt. '''' Sucht eine lokalisierte Zeichenfolge, die Qualifizierte Signatur ähnelt.
'''</summary> ''''</summary>
Public Shared ReadOnly Property QualifiedSignature() As String 'Public Shared ReadOnly Property QualifiedSignature() As String
Get ' Get
Return ResourceManager.GetString("QualifiedSignature", resourceCulture) ' Return ResourceManager.GetString("QualifiedSignature", resourceCulture)
End Get ' End Get
End Property 'End Property
'''<summary> '''<summary>
''' Sucht eine lokalisierte Zeichenfolge, die Arbeitsanweisung ähnelt. ''' Sucht eine lokalisierte Zeichenfolge, die Arbeitsanweisung ähnelt.
'''</summary> '''</summary>

View File

@@ -2,9 +2,6 @@
<configuration> <configuration>
<configSections> <configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="EnvelopeGenerator.Common.My.MySettings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections> </configSections>
<system.diagnostics> <system.diagnostics>
<sources> <sources>
@@ -29,17 +26,4 @@
</sharedListeners> </sharedListeners>
</system.diagnostics> </system.diagnostics>
<applicationSettings>
<EnvelopeGenerator.Common.My.MySettings>
<setting name="RuninDMZ" serializeAs="String">
<value>False</value>
</setting>
<setting name="NetUse_Usr" serializeAs="String">
<value>dd-gan\Administrator</value>
</setting>
<setting name="NetUse_PW" serializeAs="String">
<value>sY4vnATDXwosbTJGip6SqA==</value>
</setting>
</EnvelopeGenerator.Common.My.MySettings>
</applicationSettings>
</configuration> </configuration>

View File

@@ -1,6 +1,7 @@
using DigitalData.Core.Abstractions; using DigitalData.Core.Abstractions;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Domain.Entities namespace EnvelopeGenerator.Domain.Entities
{ {
@@ -23,19 +24,9 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("EXPORT_PATH", TypeName = "nvarchar(256)")] [Column("EXPORT_PATH", TypeName = "nvarchar(256)")]
public string? ExportPath { get; init; } public string? ExportPath { get; init; }
[Column("DOCUMENT_PATH_DMZ", TypeName = "nvarchar(512)")]
[Required]
public string? DocumentPathDmz { get; init; }
[Column("EXPORT_PATH_DMZ", TypeName = "nvarchar(512)")]
[Required]
public required string ExportPathDmz { get; init; }
[Column("DOCUMENT_PATH_MOVE_AFTSEND", TypeName = "nvarchar(512)")]
[Required]
public required string DocumentPathMoveAftsend { get; init; }
[Obsolete("Configuration does not have an ID; it represents a single table in the database.")] [Obsolete("Configuration does not have an ID; it represents a single table in the database.")]
[NotMapped]
[JsonIgnore]
public int Id => throw new InvalidOperationException("This configuration does not support an ID as it represents a single table in the database."); public int Id => throw new InvalidOperationException("This configuration does not support an ID as it represents a single table in the database.");
} }
} }

View File

@@ -16,21 +16,10 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("ENVELOPE_ID")] [Column("ENVELOPE_ID")]
public int EnvelopeId { get; set; } public int EnvelopeId { get; set; }
[Required]
[Column("FILENAME", TypeName = "nvarchar(256)")]
public required string Filename { get; set; }
[Required]
[Column("FILEPATH", TypeName = "nvarchar(256)")]
public required string Filepath { get; set; }
[Required] [Required]
[Column("ADDED_WHEN", TypeName = "datetime")] [Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; set; } public required DateTime AddedWhen { get; set; }
[Column("FILENAME_ORIGINAL", TypeName = "nvarchar(256)")]
public required string FilenameOriginal { get; set; }
[Column("BYTE_DATA", TypeName = "varbinary(max)")] [Column("BYTE_DATA", TypeName = "varbinary(max)")]
public byte[]? ByteData { get; init; } public byte[]? ByteData { get; init; }

View File

@@ -41,6 +41,15 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("CHANGED_WHEN", TypeName = "datetime")] [Column("CHANGED_WHEN", TypeName = "datetime")]
public DateTime? ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
[Column("PHONE_NUMBER")]
[StringLength(20)]
[RegularExpression(@"^\+[0-9]+$", ErrorMessage = "Phone number must start with '+' followed by digits.")]
public string? PhoneNumber { get; set; }
[NotMapped]
public (int Envelope, int Receiver) Id => (Envelope: EnvelopeId, Receiver: ReceiverId); public (int Envelope, int Receiver) Id => (Envelope: EnvelopeId, Receiver: ReceiverId);
[NotMapped]
public bool HasPhoneNumber => PhoneNumber is not null;
} }
} }

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Core.Abstractions" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="2.2.1" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" Version="1.0.0" /> <PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" Version="1.0.0" />
<PackageReference Include="UserManager.Domain" Version="1.0.0" /> <PackageReference Include="UserManager.Domain" Version="1.0.0" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,4 @@
namespace EnvelopeGenerator.Domain.HttpResponse
{
public class GtxMessagingResponse : Dictionary<string, object?> { }
}

View File

@@ -142,6 +142,12 @@
<Compile Include="Controllers\EnvelopeListController.vb" /> <Compile Include="Controllers\EnvelopeListController.vb" />
<Compile Include="Controllers\FieldEditorController.vb" /> <Compile Include="Controllers\FieldEditorController.vb" />
<Compile Include="Controllers\BaseController.vb" /> <Compile Include="Controllers\BaseController.vb" />
<Compile Include="frmChooseDocVariant.Designer.vb">
<DependentUpon>frmChooseDocVariant.vb</DependentUpon>
</Compile>
<Compile Include="frmChooseDocVariant.vb">
<SubType>Form</SubType>
</Compile>
<Compile Include="frmEnvelopeEditor.vb"> <Compile Include="frmEnvelopeEditor.vb">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -190,6 +196,9 @@
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
</Compile> </Compile>
<Compile Include="My Project\AssemblyInfo.vb" /> <Compile Include="My Project\AssemblyInfo.vb" />
<EmbeddedResource Include="frmChooseDocVariant.resx">
<DependentUpon>frmChooseDocVariant.vb</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="frmEnvelopeEditor.en.resx"> <EmbeddedResource Include="frmEnvelopeEditor.en.resx">
<DependentUpon>frmEnvelopeEditor.vb</DependentUpon> <DependentUpon>frmEnvelopeEditor.vb</DependentUpon>
<SubType>Designer</SubType> <SubType>Designer</SubType>

View File

@@ -2,7 +2,7 @@
Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Logging
Module ModuleSettings Module ModuleSettings
Public DOCUMENT_PATH_MOVE_AFTSEND As String = "" Public ENVELOPE_TEMP_DOCUMENT As String = ""
Public CurrLogConfig As LogConfig Public CurrLogConfig As LogConfig
Public Directory2Delete As String = "" Public Directory2Delete As String = ""
Public MS_GDPICTUREKEY As String = "" Public MS_GDPICTUREKEY As String = ""

View File

@@ -11,7 +11,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyCompany("Digital Data")> <Assembly: AssemblyCompany("Digital Data")>
<Assembly: AssemblyProduct("Envelope Generator")> <Assembly: AssemblyProduct("Envelope Generator")>
<Assembly: AssemblyCopyright("Copyright © 2024")> <Assembly: AssemblyCopyright("Copyright © 2024")>
<Assembly: AssemblyTrademark("2.4.5.0")> <Assembly: AssemblyTrademark("2.8.0.0")>
<Assembly: AssemblyCulture("")> <Assembly: AssemblyCulture("")>
' Setting ComVisible to false makes the types in this assembly not visible ' Setting ComVisible to false makes the types in this assembly not visible
@@ -32,5 +32,5 @@ Imports System.Runtime.InteropServices
' You can specify all the values or you can default the Build and Revision Numbers ' You can specify all the values or you can default the Build and Revision Numbers
' by using the '*' as shown below: ' by using the '*' as shown below:
' [assembly: AssemblyVersion("1.0.*")] ' [assembly: AssemblyVersion("1.0.*")]
<Assembly: AssemblyVersion("2.7.0.0")> <Assembly: AssemblyVersion("2.8.0.0")>
<Assembly: AssemblyFileVersion("1.0.0.0")> <Assembly: AssemblyFileVersion("1.0.0.0")>

View File

@@ -0,0 +1,75 @@
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class frmChooseDocVariant
Inherits DevExpress.XtraEditors.XtraForm
'Das Formular überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
'Wird vom Windows Form-Designer benötigt.
Private components As System.ComponentModel.IContainer
'Hinweis: Die folgende Prozedur ist für den Windows Form-Designer erforderlich.
'Das Bearbeiten ist mit dem Windows Form-Designer möglich.
'Das Bearbeiten mit dem Code-Editor ist nicht möglich.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(frmChooseDocVariant))
Me.LabelControl1 = New DevExpress.XtraEditors.LabelControl()
Me.SimpleButton2 = New DevExpress.XtraEditors.SimpleButton()
Me.OpenFileDialog1 = New System.Windows.Forms.OpenFileDialog()
Me.SuspendLayout()
'
'LabelControl1
'
Me.LabelControl1.Appearance.Font = New System.Drawing.Font("Segoe UI", 9.75!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
Me.LabelControl1.Appearance.Options.UseFont = True
Me.LabelControl1.Location = New System.Drawing.Point(12, 26)
Me.LabelControl1.Name = "LabelControl1"
Me.LabelControl1.Size = New System.Drawing.Size(625, 17)
Me.LabelControl1.TabIndex = 2
Me.LabelControl1.Text = "In diesem Dialog wählen Sie die Dokumente aus, welche dann zu einem Dokument verk" &
"ettet werden!"
'
'SimpleButton2
'
Me.SimpleButton2.Appearance.Font = New System.Drawing.Font("Segoe UI", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
Me.SimpleButton2.Appearance.Options.UseFont = True
Me.SimpleButton2.ImageOptions.SvgImage = CType(resources.GetObject("SimpleButton2.ImageOptions.SvgImage"), DevExpress.Utils.Svg.SvgImage)
Me.SimpleButton2.Location = New System.Drawing.Point(142, 85)
Me.SimpleButton2.Name = "SimpleButton2"
Me.SimpleButton2.Size = New System.Drawing.Size(343, 50)
Me.SimpleButton2.TabIndex = 3
Me.SimpleButton2.Text = "Mehrere PDF auswählen und automatisch verketten"
'
'OpenFileDialog1
'
Me.OpenFileDialog1.FileName = "OpenFileDialog1"
Me.OpenFileDialog1.Filter = "PDF Files|*.pdf"
'
'frmChooseDocVariant
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(644, 189)
Me.Controls.Add(Me.SimpleButton2)
Me.Controls.Add(Me.LabelControl1)
Me.IconOptions.SvgImage = CType(resources.GetObject("frmChooseDocVariant.IconOptions.SvgImage"), DevExpress.Utils.Svg.SvgImage)
Me.Name = "frmChooseDocVariant"
Me.Text = "Mehrere Dokumente"
Me.ResumeLayout(False)
Me.PerformLayout()
End Sub
Friend WithEvents LabelControl1 As DevExpress.XtraEditors.LabelControl
Friend WithEvents SimpleButton2 As DevExpress.XtraEditors.SimpleButton
Friend WithEvents OpenFileDialog1 As OpenFileDialog
End Class

View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="DevExpress.Data.v21.2" name="DevExpress.Data.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<data name="SimpleButton2.ImageOptions.SvgImage" type="DevExpress.Utils.Svg.SvgImage, DevExpress.Data.v21.2" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFlEZXZFeHByZXNzLkRhdGEudjIxLjIsIFZlcnNpb249MjEuMi40
LjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjg4ZDE3NTRkNzAwZTQ5YQUBAAAAHURl
dkV4cHJlc3MuVXRpbHMuU3ZnLlN2Z0ltYWdlAQAAAAREYXRhBwICAAAACQMAAAAPAwAAAJYEAAAC77u/
PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4NCjxzdmcgeD0iMHB4IiB5PSIwcHgi
IHZpZXdCb3g9IjAgMCAzMiAzMiIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv
MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3Bh
Y2U9InByZXNlcnZlIiBpZD0iTGF5ZXJfMSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAg
MzIgMzIiPg0KICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLlllbGxvd3tmaWxsOiNGRkIxMTU7fQoJ
LlJlZHtmaWxsOiNEMTFDMUM7fQoJLkJsdWV7ZmlsbDojMTE3N0Q3O30KCS5HcmVlbntmaWxsOiMwMzlD
MjM7fQoJLkJsYWNre2ZpbGw6IzcyNzI3Mjt9CgkuV2hpdGV7ZmlsbDojRkZGRkZGO30KCS5zdDB7b3Bh
Y2l0eTowLjc1O30KPC9zdHlsZT4NCiAgPGcgaWQ9IkRvY3VtZW50UERGIj4NCiAgICA8cGF0aCBkPSJN
MjIsMjR2NEgyVjJoMTR2NWMwLDAuNiwwLjQsMSwxLDFoNXY0aDJWN2wtNy03SDFDMC40LDAsMCwwLjQs
MCwxdjI4YzAsMC42LDAuNCwxLDEsMWgyMmMwLjYsMCwxLTAuNCwxLTEgICB2LTVIMjJ6IiBjbGFzcz0i
QmxhY2siIC8+DQogICAgPHBhdGggZD0iTTE5LjIsMTZjMC4zLDAuNSwwLjQsMS4xLDAuNCwxLjljMCww
LjktMC4yLDEuNS0wLjUsMmMtMC4zLDAuNS0wLjcsMC43LTEuMywwLjdoLTAuNnYtNS4zaDAuNiAgIEMx
OC40LDE1LjMsMTguOSwxNS42LDE5LjIsMTZ6IE0xMi4xLDE1LjNoLTAuNXYyLjZoMC41YzAuNywwLDEu
MS0wLjQsMS4xLTEuM2MwLTAuNC0wLjEtMC44LTAuMy0xQzEyLjYsMTUuNCwxMi40LDE1LjMsMTIuMSwx
NS4zeiAgICBNMzAsMTJ2MTJINlYxMkgzMHogTTE0LjgsMTYuNWMwLTAuOC0wLjItMS41LTAuNi0xLjlj
LTAuNC0wLjQtMS0wLjctMS44LTAuN0gxMHY4aDEuNnYtMi43aDAuNmMwLjgsMCwxLjQtMC4zLDEuOS0w
LjggICBDMTQuNSwxOCwxNC44LDE3LjMsMTQuOCwxNi41eiBNMjEuMiwxNy45YzAtMi42LTEuMS0zLjkt
My40LTMuOWgtMi4xdjhoMi4yYzEuMSwwLDEuOS0wLjQsMi41LTEuMUMyMC45LDIwLjIsMjEuMiwxOS4y
LDIxLjIsMTcuOXogICAgTTI2LDE0aC0zLjd2OGgxLjZ2LTMuMWgydi0xLjNoLTJ2LTIuMkgyNlYxNHoi
IGNsYXNzPSJSZWQiIC8+DQogIDwvZz4NCjwvc3ZnPgs=
</value>
</data>
<metadata name="OpenFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="frmChooseDocVariant.IconOptions.SvgImage" type="DevExpress.Utils.Svg.SvgImage, DevExpress.Data.v21.2" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFlEZXZFeHByZXNzLkRhdGEudjIxLjIsIFZlcnNpb249MjEuMi40
LjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjg4ZDE3NTRkNzAwZTQ5YQUBAAAAHURl
dkV4cHJlc3MuVXRpbHMuU3ZnLlN2Z0ltYWdlAQAAAAREYXRhBwICAAAACQMAAAAPAwAAANYEAAAC77u/
PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4NCjxzdmcgeD0iMHB4IiB5PSIwcHgi
IHZpZXdCb3g9IjAgMCAzMiAzMiIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv
MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3Bh
Y2U9InByZXNlcnZlIiBpZD0iTG9naWNhbCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAg
MzIgMzIiPg0KICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLkJsdWV7ZmlsbDojMTE3N0Q3O30KPC9z
dHlsZT4NCiAgPHBhdGggZD0iTTUsNGgyM1YzYzAtMC42LTAuNC0xLTEtMUg1QzMuMywyLDIsMy4zLDIs
NXYyMmMwLDEuNywxLjMsMywzLDNoMjJjMC42LDAsMS0wLjQsMS0xVjZINUM0LjQsNiw0LDUuNiw0LDUg
IEM0LDQuNCw0LjQsNCw1LDR6IE0xNiwyNGgtMnYtMmgyVjI0eiBNMTEuNiwxNC40YzAuMi0wLjUsMC40
LTAuOSwwLjctMS4zYzAuMy0wLjQsMC43LTAuNiwxLjEtMC44YzAuNC0wLjIsMC45LTAuMywxLjUtMC4z
ICBjMC43LDAsMS4zLDAuMSwxLjgsMC4zYzAuNSwwLjIsMC45LDAuNSwxLjIsMC44YzAuMywwLjMsMC41
LDAuNywwLjYsMWMwLjEsMC40LDAuMiwwLjcsMC4yLDFjMCwwLjQsMCwwLjctMC4xLDEgIGMtMC4xLDAu
My0wLjIsMC41LTAuMywwLjhjLTAuMSwwLjItMC4zLDAuNC0wLjQsMC42Yy0wLjIsMC4yLTAuMywwLjMt
MC41LDAuNGMtMC4yLDAuMS0wLjMsMC4zLTAuNSwwLjRjLTAuMiwwLjEtMC4zLDAuMy0wLjQsMC40ICBj
LTAuMSwwLjEtMC4zLDAuMy0wLjQsMC41Yy0wLjEsMC4yLTAuMiwwLjQtMC4yLDAuNnYwLjZoLTJ2LTAu
N2MwLTAuNCwwLjEtMC43LDAuMS0wLjljMC4xLTAuMywwLjItMC41LDAuMy0wLjcgIGMwLjEtMC4yLDAu
My0wLjQsMC40LTAuNWMwLjEtMC4xLDAuMy0wLjMsMC40LTAuNHMwLjMtMC4zLDAuNC0wLjRjMC4xLTAu
MSwwLjItMC4yLDAuMy0wLjRjMC4xLTAuMSwwLjItMC4zLDAuMi0wLjUgIGMwLjEtMC4yLDAuMS0wLjQs
MC4xLTAuNmMwLTAuNS0wLjEtMC45LTAuMy0xLjFjLTAuMi0wLjItMC41LTAuNC0wLjktMC40Yy0wLjMs
MC0wLjUsMC4xLTAuNywwLjJjLTAuMiwwLjEtMC40LDAuMy0wLjUsMC41ICBjLTAuMSwwLjItMC4yLDAu
NC0wLjMsMC43Yy0wLjEsMC4yLTAuMSwwLjUtMC4xLDAuOGgtMi4yQzExLjQsMTUuNCwxMS40LDE0Ljks
MTEuNiwxNC40eiIgY2xhc3M9IkJsdWUiIC8+DQo8L3N2Zz4L
</value>
</data>
</root>

View File

@@ -0,0 +1,96 @@
Imports DevExpress.XtraSplashScreen
Imports EnvelopeGenerator.Common
Imports DigitalData.Modules.Logging
Imports GdPicture14
Public Class frmChooseDocVariant
Private TempFiles As TempFiles
Public Property State As State
Private Logger As Logger
Private Sub frmChooseDocVariant_Load(sender As Object, e As EventArgs) Handles Me.Load
ENVELOPE_TEMP_DOCUMENT = ""
TempFiles = New TempFiles(State.LogConfig)
TempFiles.Create()
Logger = State.LogConfig.GetLogger()
End Sub
Private Sub SimpleButton1_Click(sender As Object, e As EventArgs)
OpenFileDialog1.Multiselect = False
Dim oHandle = SplashScreenManager.ShowOverlayForm(Me)
Try
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
ENVELOPE_TEMP_DOCUMENT = OpenFileDialog1.FileName
End If
Catch ex As Exception
Logger.Error(ex)
Finally
SplashScreenManager.CloseOverlayForm(oHandle)
End Try
' Else
' SplashScreenManager.CloseOverlayForm(oHandle)
' End If
End Sub
Private Sub SimpleButton2_Click(sender As Object, e As EventArgs) Handles SimpleButton2.Click
OpenFileDialog1.Multiselect = True
Dim oHandle = SplashScreenManager.ShowOverlayForm(Me)
Try
Dim oErr As Boolean = False
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
Dim oIDX As Integer = 0
For Each oFile As String In OpenFileDialog1.FileNames
oIDX += 1
Next
Dim arPDF As GdPicturePDF() = New GdPicturePDF(oIDX) {}
oIDX = 0
For Each oFile As String In OpenFileDialog1.FileNames
arPDF(oIDX) = New GdPicturePDF()
If arPDF(oIDX).LoadFromFile(oFile) <> GdPictureStatus.OK Then
MsgBox($"PDF Statsu of file {oFile} is not OK. Please check PDF-conformity!", MsgBoxStyle.Critical)
oErr = True
Exit For
End If
oIDX += 1
Next
If oErr = False Then
Dim dstPDF As GdPicturePDF = arPDF(0).MergeDocuments(arPDF)
Dim oStatus As GdPictureStatus = arPDF(0).GetStat()
If oStatus = GdPictureStatus.OK Then
MsgBox("All documents have been successfully merged.", MsgBoxStyle.Information)
Dim oTempFolder = TempFiles.TempPath
Dim oTempFilename = String.Concat(oTempFolder, "\", $"MergedDoc.pdf")
If System.IO.File.Exists(oTempFilename) Then
System.IO.File.Delete(oTempFilename)
End If
If dstPDF.SaveToFile(oTempFilename) = GdPictureStatus.OK Then
MessageBox.Show("Merged document has been successfully saved.", "Example: MergeDocuments")
ENVELOPE_TEMP_DOCUMENT = oTempFilename
dstPDF.CloseDocument()
End If
Else
MessageBox.Show("The MergeDocuments() method has failed with the status: " + oStatus.ToString(), "Example: MergeDocuments")
End If
dstPDF.Dispose()
oIDX = 0
For Each oFile As String In OpenFileDialog1.FileNames
arPDF(oIDX).CloseDocument()
oIDX += 1
Next
End If
End If
Catch ex As Exception
Logger.Error(ex)
Finally
SplashScreenManager.CloseOverlayForm(oHandle)
End Try
End Sub
Private Sub LabelControl1_Click(sender As Object, e As EventArgs) Handles LabelControl1.Click
End Sub
End Class

View File

@@ -58,12 +58,14 @@ Partial Public Class frmEnvelopeEditor
Me.BarButtonItem1 = New DevExpress.XtraBars.BarButtonItem() Me.BarButtonItem1 = New DevExpress.XtraBars.BarButtonItem()
Me.BarStaticItem1 = New DevExpress.XtraBars.BarStaticItem() Me.BarStaticItem1 = New DevExpress.XtraBars.BarStaticItem()
Me.BarButtonItem2 = New DevExpress.XtraBars.BarButtonItem() Me.BarButtonItem2 = New DevExpress.XtraBars.BarButtonItem()
Me.BarButtonItem3 = New DevExpress.XtraBars.BarButtonItem()
Me.RibbonPage1 = New DevExpress.XtraBars.Ribbon.RibbonPage() Me.RibbonPage1 = New DevExpress.XtraBars.Ribbon.RibbonPage()
Me.RibbonPageGroup1 = New DevExpress.XtraBars.Ribbon.RibbonPageGroup() Me.RibbonPageGroup1 = New DevExpress.XtraBars.Ribbon.RibbonPageGroup()
Me.RibbonPageGroupDocuments = New DevExpress.XtraBars.Ribbon.RibbonPageGroup() Me.RibbonPageGroupDocuments = New DevExpress.XtraBars.Ribbon.RibbonPageGroup()
Me.RibbonPageGroupInvitation = New DevExpress.XtraBars.Ribbon.RibbonPageGroup() Me.RibbonPageGroupInvitation = New DevExpress.XtraBars.Ribbon.RibbonPageGroup()
Me.RibbonPageGroupAddSignature = New DevExpress.XtraBars.Ribbon.RibbonPageGroup() Me.RibbonPageGroupAddSignature = New DevExpress.XtraBars.Ribbon.RibbonPageGroup()
Me.RibbonPageGroupReceiver = New DevExpress.XtraBars.Ribbon.RibbonPageGroup() Me.RibbonPageGroupReceiver = New DevExpress.XtraBars.Ribbon.RibbonPageGroup()
Me.RibbonPageGroup2 = New DevExpress.XtraBars.Ribbon.RibbonPageGroup()
Me.RibbonStatusBar1 = New DevExpress.XtraBars.Ribbon.RibbonStatusBar() Me.RibbonStatusBar1 = New DevExpress.XtraBars.Ribbon.RibbonStatusBar()
Me.LayoutControlGroup4 = New DevExpress.XtraLayout.LayoutControlGroup() Me.LayoutControlGroup4 = New DevExpress.XtraLayout.LayoutControlGroup()
Me.LayoutControlGroup5 = New DevExpress.XtraLayout.LayoutControlGroup() Me.LayoutControlGroup5 = New DevExpress.XtraLayout.LayoutControlGroup()
@@ -98,6 +100,7 @@ Partial Public Class frmEnvelopeEditor
Me.EnvelopeDocumentBindingSource = New System.Windows.Forms.BindingSource(Me.components) Me.EnvelopeDocumentBindingSource = New System.Windows.Forms.BindingSource(Me.components)
Me.OpenFileDialog1 = New System.Windows.Forms.OpenFileDialog() Me.OpenFileDialog1 = New System.Windows.Forms.OpenFileDialog()
Me.txtEnvelopeIdLabel = New DevExpress.XtraBars.BarStaticItem() Me.txtEnvelopeIdLabel = New DevExpress.XtraBars.BarStaticItem()
Me.BarButtonItem4 = New DevExpress.XtraBars.BarButtonItem()
CType(Me.SplitContainerControl1, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.SplitContainerControl1, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.SplitContainerControl1.Panel1, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.SplitContainerControl1.Panel1, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SplitContainerControl1.Panel1.SuspendLayout() Me.SplitContainerControl1.Panel1.SuspendLayout()
@@ -240,9 +243,9 @@ Partial Public Class frmEnvelopeEditor
'RibbonControl1 'RibbonControl1
' '
Me.RibbonControl1.ExpandCollapseItem.Id = 0 Me.RibbonControl1.ExpandCollapseItem.Id = 0
Me.RibbonControl1.Items.AddRange(New DevExpress.XtraBars.BarItem() {Me.RibbonControl1.ExpandCollapseItem, Me.RibbonControl1.SearchEditItem, Me.btnSave, Me.btnCancel, Me.btnNewFile, Me.btnDeleteFile, Me.btnSendEnvelope, Me.btnEditFields, Me.btnDeleteReceiver, Me.btnEditData, Me.txtCreatorEmailLabel, Me.txtEnvelopeIdLabel2, Me.BarButtonItem1, Me.BarStaticItem1, Me.BarButtonItem2}) Me.RibbonControl1.Items.AddRange(New DevExpress.XtraBars.BarItem() {Me.RibbonControl1.ExpandCollapseItem, Me.RibbonControl1.SearchEditItem, Me.btnSave, Me.btnCancel, Me.btnNewFile, Me.btnDeleteFile, Me.btnSendEnvelope, Me.btnEditFields, Me.btnDeleteReceiver, Me.btnEditData, Me.txtCreatorEmailLabel, Me.txtEnvelopeIdLabel2, Me.BarButtonItem1, Me.BarStaticItem1, Me.BarButtonItem2, Me.BarButtonItem3, Me.BarButtonItem4})
resources.ApplyResources(Me.RibbonControl1, "RibbonControl1") resources.ApplyResources(Me.RibbonControl1, "RibbonControl1")
Me.RibbonControl1.MaxItemId = 15 Me.RibbonControl1.MaxItemId = 17
Me.RibbonControl1.Name = "RibbonControl1" Me.RibbonControl1.Name = "RibbonControl1"
Me.RibbonControl1.Pages.AddRange(New DevExpress.XtraBars.Ribbon.RibbonPage() {Me.RibbonPage1}) Me.RibbonControl1.Pages.AddRange(New DevExpress.XtraBars.Ribbon.RibbonPage() {Me.RibbonPage1})
Me.RibbonControl1.ShowApplicationButton = DevExpress.Utils.DefaultBoolean.[False] Me.RibbonControl1.ShowApplicationButton = DevExpress.Utils.DefaultBoolean.[False]
@@ -342,9 +345,15 @@ Partial Public Class frmEnvelopeEditor
Me.BarButtonItem2.Id = 14 Me.BarButtonItem2.Id = 14
Me.BarButtonItem2.Name = "BarButtonItem2" Me.BarButtonItem2.Name = "BarButtonItem2"
' '
'BarButtonItem3
'
resources.ApplyResources(Me.BarButtonItem3, "BarButtonItem3")
Me.BarButtonItem3.Id = 15
Me.BarButtonItem3.Name = "BarButtonItem3"
'
'RibbonPage1 'RibbonPage1
' '
Me.RibbonPage1.Groups.AddRange(New DevExpress.XtraBars.Ribbon.RibbonPageGroup() {Me.RibbonPageGroup1, Me.RibbonPageGroupDocuments, Me.RibbonPageGroupInvitation, Me.RibbonPageGroupAddSignature, Me.RibbonPageGroupReceiver}) Me.RibbonPage1.Groups.AddRange(New DevExpress.XtraBars.Ribbon.RibbonPageGroup() {Me.RibbonPageGroup1, Me.RibbonPageGroupDocuments, Me.RibbonPageGroupInvitation, Me.RibbonPageGroupAddSignature, Me.RibbonPageGroupReceiver, Me.RibbonPageGroup2})
Me.RibbonPage1.Name = "RibbonPage1" Me.RibbonPage1.Name = "RibbonPage1"
resources.ApplyResources(Me.RibbonPage1, "RibbonPage1") resources.ApplyResources(Me.RibbonPage1, "RibbonPage1")
' '
@@ -359,6 +368,7 @@ Partial Public Class frmEnvelopeEditor
'RibbonPageGroupDocuments 'RibbonPageGroupDocuments
' '
Me.RibbonPageGroupDocuments.ItemLinks.Add(Me.btnNewFile) Me.RibbonPageGroupDocuments.ItemLinks.Add(Me.btnNewFile)
Me.RibbonPageGroupDocuments.ItemLinks.Add(Me.BarButtonItem4)
Me.RibbonPageGroupDocuments.ItemLinks.Add(Me.btnDeleteFile) Me.RibbonPageGroupDocuments.ItemLinks.Add(Me.btnDeleteFile)
Me.RibbonPageGroupDocuments.Name = "RibbonPageGroupDocuments" Me.RibbonPageGroupDocuments.Name = "RibbonPageGroupDocuments"
resources.ApplyResources(Me.RibbonPageGroupDocuments, "RibbonPageGroupDocuments") resources.ApplyResources(Me.RibbonPageGroupDocuments, "RibbonPageGroupDocuments")
@@ -382,6 +392,12 @@ Partial Public Class frmEnvelopeEditor
Me.RibbonPageGroupReceiver.Name = "RibbonPageGroupReceiver" Me.RibbonPageGroupReceiver.Name = "RibbonPageGroupReceiver"
resources.ApplyResources(Me.RibbonPageGroupReceiver, "RibbonPageGroupReceiver") resources.ApplyResources(Me.RibbonPageGroupReceiver, "RibbonPageGroupReceiver")
' '
'RibbonPageGroup2
'
Me.RibbonPageGroup2.ItemLinks.Add(Me.BarButtonItem3)
Me.RibbonPageGroup2.Name = "RibbonPageGroup2"
resources.ApplyResources(Me.RibbonPageGroup2, "RibbonPageGroup2")
'
'RibbonStatusBar1 'RibbonStatusBar1
' '
Me.RibbonStatusBar1.ItemLinks.Add(Me.txtCreatorEmailLabel) Me.RibbonStatusBar1.ItemLinks.Add(Me.txtCreatorEmailLabel)
@@ -659,6 +675,13 @@ Partial Public Class frmEnvelopeEditor
Me.txtEnvelopeIdLabel.PaintStyle = DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph Me.txtEnvelopeIdLabel.PaintStyle = DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph
Me.txtEnvelopeIdLabel.Tag = "Envelope-ID: {0}" Me.txtEnvelopeIdLabel.Tag = "Envelope-ID: {0}"
' '
'BarButtonItem4
'
resources.ApplyResources(Me.BarButtonItem4, "BarButtonItem4")
Me.BarButtonItem4.Id = 16
Me.BarButtonItem4.ImageOptions.SvgImage = CType(resources.GetObject("BarButtonItem4.ImageOptions.SvgImage"), DevExpress.Utils.Svg.SvgImage)
Me.BarButtonItem4.Name = "BarButtonItem4"
'
'frmEnvelopeEditor 'frmEnvelopeEditor
' '
resources.ApplyResources(Me, "$this") resources.ApplyResources(Me, "$this")
@@ -783,6 +806,9 @@ Partial Public Class frmEnvelopeEditor
Friend WithEvents BarButtonItem1 As DevExpress.XtraBars.BarButtonItem Friend WithEvents BarButtonItem1 As DevExpress.XtraBars.BarButtonItem
Friend WithEvents BarStaticItem1 As DevExpress.XtraBars.BarStaticItem Friend WithEvents BarStaticItem1 As DevExpress.XtraBars.BarStaticItem
Friend WithEvents BarButtonItem2 As DevExpress.XtraBars.BarButtonItem Friend WithEvents BarButtonItem2 As DevExpress.XtraBars.BarButtonItem
Friend WithEvents BarButtonItem3 As DevExpress.XtraBars.BarButtonItem
Friend WithEvents RibbonPageGroup2 As DevExpress.XtraBars.Ribbon.RibbonPageGroup
Friend WithEvents BarButtonItem4 As DevExpress.XtraBars.BarButtonItem
#End Region #End Region

View File

@@ -213,7 +213,7 @@
</value> </value>
</data> </data>
<data name="btnNewFile.Caption" xml:space="preserve"> <data name="btnNewFile.Caption" xml:space="preserve">
<value>Neues Dokument</value> <value>Ein Dokument hinzufügen</value>
</data> </data>
<data name="btnNewFile.ImageOptions.SvgImage" type="DevExpress.Utils.Svg.SvgImage, DevExpress.Data.v21.2" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btnNewFile.ImageOptions.SvgImage" type="DevExpress.Utils.Svg.SvgImage, DevExpress.Data.v21.2" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
@@ -414,6 +414,30 @@
<data name="BarButtonItem2.Caption" xml:space="preserve"> <data name="BarButtonItem2.Caption" xml:space="preserve">
<value>Öffnen</value> <value>Öffnen</value>
</data> </data>
<data name="BarButtonItem3.Caption" xml:space="preserve">
<value>BarButtonItem3</value>
</data>
<data name="BarButtonItem4.Caption" xml:space="preserve">
<value>Mehrere Dokumente hinzufügen</value>
</data>
<data name="BarButtonItem4.ImageOptions.SvgImage" type="DevExpress.Utils.Svg.SvgImage, DevExpress.Data.v21.2" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFlEZXZFeHByZXNzLkRhdGEudjIxLjIsIFZlcnNpb249MjEuMi40
LjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjg4ZDE3NTRkNzAwZTQ5YQUBAAAAHURl
dkV4cHJlc3MuVXRpbHMuU3ZnLlN2Z0ltYWdlAQAAAAREYXRhBwICAAAACQMAAAAPAwAAAHECAAAC77u/
PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4NCjxzdmcgeD0iMHB4IiB5PSIwcHgi
IHZpZXdCb3g9IjAgMCAzMiAzMiIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv
MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3Bh
Y2U9InByZXNlcnZlIiBpZD0iTXVsdGlwbGVfRG9jdW1lbnRzIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91
bmQ6bmV3IDAgMCAzMiAzMiI+DQogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgkuQmxhY2t7ZmlsbDoj
NzI3MjcyO30KPC9zdHlsZT4NCiAgPHBhdGggZD0iTTMxLDhoLTVWNWMwLTAuNS0wLjUtMS0xLTFoLTVW
MWMwLTAuNS0wLjUtMS0xLTFIMUMwLjUsMCwwLDAuNSwwLDF2MjJjMCwwLjUsMC41LDEsMSwxaDV2M2Mw
LDAuNSwwLjUsMSwxLDEgIGg1djNjMCwwLjUsMC41LDEsMSwxaDE4YzAuNSwwLDEtMC41LDEtMVY5QzMy
LDguNSwzMS41LDgsMzEsOHogTTYsNXYxN0gyVjJoMTZ2Mkg3QzYuNSw0LDYsNC41LDYsNXogTTEyLDl2
MTdIOFY2aDE2djJIMTMgIEMxMi41LDgsMTIsOC41LDEyLDl6IE0zMCwzMEgxNFYxMGgxNlYzMHoiIGNs
YXNzPSJCbGFjayIgLz4NCjwvc3ZnPgs=
</value>
</data>
<data name="RibbonControl1.Location" type="System.Drawing.Point, System.Drawing"> <data name="RibbonControl1.Location" type="System.Drawing.Point, System.Drawing">
<value>0, 0</value> <value>0, 0</value>
</data> </data>
@@ -432,6 +456,9 @@
<data name="RibbonPageGroupReceiver.Text" xml:space="preserve"> <data name="RibbonPageGroupReceiver.Text" xml:space="preserve">
<value>Empfänger</value> <value>Empfänger</value>
</data> </data>
<data name="RibbonPageGroup2.Text" xml:space="preserve">
<value>RibbonPageGroup2</value>
</data>
<data name="RibbonPage1.Text" xml:space="preserve"> <data name="RibbonPage1.Text" xml:space="preserve">
<value>Start</value> <value>Start</value>
</data> </data>
@@ -1058,6 +1085,18 @@
<data name="&gt;&gt;BarButtonItem2.Type" xml:space="preserve"> <data name="&gt;&gt;BarButtonItem2.Type" xml:space="preserve">
<value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value> <value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data> </data>
<data name="&gt;&gt;BarButtonItem3.Name" xml:space="preserve">
<value>BarButtonItem3</value>
</data>
<data name="&gt;&gt;BarButtonItem3.Type" xml:space="preserve">
<value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;BarButtonItem4.Name" xml:space="preserve">
<value>BarButtonItem4</value>
</data>
<data name="&gt;&gt;BarButtonItem4.Type" xml:space="preserve">
<value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;RibbonPage1.Name" xml:space="preserve"> <data name="&gt;&gt;RibbonPage1.Name" xml:space="preserve">
<value>RibbonPage1</value> <value>RibbonPage1</value>
</data> </data>
@@ -1094,6 +1133,12 @@
<data name="&gt;&gt;RibbonPageGroupReceiver.Type" xml:space="preserve"> <data name="&gt;&gt;RibbonPageGroupReceiver.Type" xml:space="preserve">
<value>DevExpress.XtraBars.Ribbon.RibbonPageGroup, DevExpress.XtraBars.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value> <value>DevExpress.XtraBars.Ribbon.RibbonPageGroup, DevExpress.XtraBars.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data> </data>
<data name="&gt;&gt;RibbonPageGroup2.Name" xml:space="preserve">
<value>RibbonPageGroup2</value>
</data>
<data name="&gt;&gt;RibbonPageGroup2.Type" xml:space="preserve">
<value>DevExpress.XtraBars.Ribbon.RibbonPageGroup, DevExpress.XtraBars.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;LayoutControlGroup4.Name" xml:space="preserve"> <data name="&gt;&gt;LayoutControlGroup4.Name" xml:space="preserve">
<value>LayoutControlGroup4</value> <value>LayoutControlGroup4</value>
</data> </data>

View File

@@ -4,9 +4,12 @@ Imports System.IO
Imports DevExpress.Export.Xl Imports DevExpress.Export.Xl
Imports DevExpress.Utils.CommonDialogs Imports DevExpress.Utils.CommonDialogs
Imports DevExpress.Utils.Drawing Imports DevExpress.Utils.Drawing
Imports DevExpress.Utils.Svg.CommonSvgImages
Imports DevExpress.XtraEditors Imports DevExpress.XtraEditors
Imports DevExpress.XtraExport.Helpers Imports DevExpress.XtraExport.Helpers
Imports DevExpress.XtraGrid Imports DevExpress.XtraGrid
Imports DevExpress.XtraGrid.Columns
Imports DevExpress.XtraGrid.Views.Base
Imports DevExpress.XtraGrid.Views.Grid Imports DevExpress.XtraGrid.Views.Grid
Imports DevExpress.XtraSplashScreen Imports DevExpress.XtraSplashScreen
Imports DigitalData.Modules.Base Imports DigitalData.Modules.Base
@@ -30,6 +33,7 @@ Partial Public Class frmEnvelopeEditor
Private Const COL_CODE = "AccessCode" Private Const COL_CODE = "AccessCode"
Public Property State As State Public Property State As State
Private TempFiles As TempFiles
Public Sub New() Public Sub New()
InitializeComponent() InitializeComponent()
@@ -40,30 +44,30 @@ Partial Public Class frmEnvelopeEditor
'SaveEnvelopeWithValidation() 'SaveEnvelopeWithValidation()
' If Not IsNothing(Envelope) Then ' If Not IsNothing(Envelope) Then
Try Try
' prüfen ob es schon eine Datei gibt ' prüfen ob es schon eine Datei gibt
If Documents.Count > 0 Then If Documents.Count > 0 Then
MsgBox(Resources.Envelope.Only_one_file_is_allowed, MsgBoxStyle.Information, Text) MsgBox(Resources.Envelope.Only_one_file_is_allowed, MsgBoxStyle.Information, Text)
Return Return
End If
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
Dim oDocument = Await Controller.CreateDocument(OpenFileDialog1.FileName)
If oDocument IsNot Nothing Then
Documents.Add(oDocument)
' Update_File_DB(OpenFileDialog1.FileName)
Else
MsgBox(Resources.Envelope.Document_Could_Not_Be_Saved, MsgBoxStyle.Critical, Text)
End If End If
End If
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then Catch ex As Exception
Dim oDocument = Await Controller.CreateDocument(OpenFileDialog1.FileName) Logger.Error(ex)
Finally
SplashScreenManager.CloseOverlayForm(oHandle)
If oDocument IsNot Nothing Then RibbonPageGroupAddSignature_Enabled()
Documents.Add(oDocument) End Try
' Update_File_DB(OpenFileDialog1.FileName)
Else
MsgBox(Resources.Envelope.Document_Could_Not_Be_Saved, MsgBoxStyle.Critical, Text)
End If
End If
Catch ex As Exception
Logger.Error(ex)
Finally
SplashScreenManager.CloseOverlayForm(oHandle)
RibbonPageGroupAddSignature_Enabled()
End Try
' Else ' Else
' SplashScreenManager.CloseOverlayForm(oHandle) ' SplashScreenManager.CloseOverlayForm(oHandle)
' End If ' End If
@@ -72,6 +76,8 @@ Partial Public Class frmEnvelopeEditor
Private Sub frmEditor_Load(sender As Object, e As EventArgs) Handles Me.Load Private Sub frmEditor_Load(sender As Object, e As EventArgs) Handles Me.Load
Logger = State.LogConfig.GetLogger() Logger = State.LogConfig.GetLogger()
Logger.Debug("Loading Configuration..") Logger.Debug("Loading Configuration..")
TempFiles = New TempFiles(State.LogConfig)
TempFiles.Create()
If Envelope Is Nothing Then If Envelope Is Nothing Then
Controller = New EnvelopeEditorController(State) Controller = New EnvelopeEditorController(State)
@@ -93,6 +99,24 @@ Partial Public Class frmEnvelopeEditor
Receivers = New BindingList(Of EnvelopeReceiver)(Controller.Envelope.Receivers) Receivers = New BindingList(Of EnvelopeReceiver)(Controller.Envelope.Receivers)
For Each docItem As EnvelopeDocument In Documents For Each docItem As EnvelopeDocument In Documents
If Not File.Exists(docItem.Filepath) Then
Dim oTempFolder = TempFiles.TempPath
Dim oTempFilename = String.Concat(oTempFolder, "\", $"ViewEnvDoc_{Envelope.Id}.pdf")
If File.Exists(oTempFilename) Then
Try
File.OpenWrite(oTempFilename)
Catch ex As Exception
MsgBox("File might already be open?", MsgBoxStyle.Exclamation)
Me.Cursor = Cursors.Default
Exit Sub
End Try
File.Delete(oTempFilename)
End If
WriteBytetoPath(oTempFilename, docItem.Byte_Data)
docItem.Filepath = oTempFilename
End If
If docItem.Thumbnail Is Nothing Then If docItem.Thumbnail Is Nothing Then
docItem.Thumbnail = Controller.CreateThumbnail(docItem.Filepath) docItem.Thumbnail = Controller.CreateThumbnail(docItem.Filepath)
docItem.PageCount = Controller.GetPageCount(docItem.Filepath) docItem.PageCount = Controller.GetPageCount(docItem.Filepath)
@@ -141,6 +165,8 @@ Partial Public Class frmEnvelopeEditor
Dim oDocument As EnvelopeDocument = DirectCast(ViewDocuments.GetFocusedRow(), EnvelopeDocument) Dim oDocument As EnvelopeDocument = DirectCast(ViewDocuments.GetFocusedRow(), EnvelopeDocument)
If Controller.DeleteDocument(oDocument) Then If Controller.DeleteDocument(oDocument) Then
Documents.Remove(oDocument) Documents.Remove(oDocument)
GridDocuments.DataSource = Nothing
End If End If
End If End If
End Sub End Sub
@@ -163,6 +189,19 @@ Partial Public Class frmEnvelopeEditor
If ViewDocuments.GetSelectedRows().Count > 0 Then If ViewDocuments.GetSelectedRows().Count > 0 Then
Dim oDocument As EnvelopeDocument = DirectCast(ViewDocuments.GetFocusedRow(), EnvelopeDocument) Dim oDocument As EnvelopeDocument = DirectCast(ViewDocuments.GetFocusedRow(), EnvelopeDocument)
Dim oEnvelope = Controller.Envelope
If Not IsNothing(oDocument.Byte_Data) Then
Dim oTempFolder = TempFiles.TempPath
Dim oTempFilename = String.Concat(oTempFolder, "\", $"ViewEnvDoc_{oEnvelope.Id}.pdf")
If Not File.Exists(oTempFilename) Then
WriteBytetoPath(oTempFilename, oDocument.Byte_Data)
oDocument.Filepath = oTempFilename
End If
End If
Dim oGDPictureKey As String = MS_GDPICTUREKEY Dim oGDPictureKey As String = MS_GDPICTUREKEY
Dim oForm As New frmFieldEditor(State) With { Dim oForm As New frmFieldEditor(State) With {
@@ -180,7 +219,23 @@ Partial Public Class frmEnvelopeEditor
SplashScreenManager.CloseOverlayForm(oHandle) SplashScreenManager.CloseOverlayForm(oHandle)
End Try End Try
End Sub End Sub
Private Sub WriteBytetoPath(ByVal sFileName As String, pByte As Byte())
'For Document
Try
If Not pByte Is Nothing Then
'Read image data into a file stream
Using fs As New FileStream(sFileName, FileMode.OpenOrCreate, FileAccess.Write)
fs.Write(pByte, 0, pByte.Length)
'Set image variable value using memory stream.
fs.Flush()
fs.Close()
End Using
End If
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Exclamation, "Error in downloadFile")
End Try
End Sub
Private Function SaveEnvelopeWithValidation() As Boolean Private Function SaveEnvelopeWithValidation() As Boolean
Return SaveEnvelope(True) Return SaveEnvelope(True)
End Function End Function
@@ -479,7 +534,12 @@ Partial Public Class frmEnvelopeEditor
RibbonPageGroupAddSignature_Enabled() RibbonPageGroupAddSignature_Enabled()
End Sub End Sub
Dim CellValueChanged As Boolean = False Dim CellValueChanged As Boolean = False
Private Sub ViewReceivers_ColumnPositionChanged(sender As Object, e As EventArgs) Handles ViewReceivers.ColumnPositionChanged
End Sub
Private Sub ViewReceivers_CellValueChanged(sender As Object, e As Views.Base.CellValueChangedEventArgs) Handles ViewReceivers.CellValueChanged Private Sub ViewReceivers_CellValueChanged(sender As Object, e As Views.Base.CellValueChangedEventArgs) Handles ViewReceivers.CellValueChanged
If e.Column.FieldName = COL_EMAIL And CellValueChanged = False Then If e.Column.FieldName = COL_EMAIL And CellValueChanged = False Then
If e.Value Is Nothing Then If e.Value Is Nothing Then
' Keine E-Mail-Adresse, also weg damit ' Keine E-Mail-Adresse, also weg damit
@@ -491,21 +551,57 @@ Partial Public Class frmEnvelopeEditor
Dim oNameCellValue = ViewReceivers.GetRowCellValue(e.RowHandle, COL_NAME) Dim oNameCellValue = ViewReceivers.GetRowCellValue(e.RowHandle, COL_NAME)
If oNameCellValue Is Nothing Then If oNameCellValue Is Nothing Then
Dim oEmailAdress As String = DirectCast(e.Value.ToString.ToLower, String) Dim oEmailAdress As String = DirectCast(e.Value.ToString.ToLower, String)
oEmailAdress = Trim(oEmailAdress)
If IsValidEmailAddress(oEmailAdress) = True Then
Dim oLastName As String = Controller.GetLastNameByEmailAdress(oEmailAdress)
Dim oAccessCode As String = Helpers.GetAccessCode()
ViewReceivers.SetRowCellValue(e.RowHandle, ViewReceivers.Columns.Item(COL_EMAIL), oEmailAdress)
ViewReceivers.SetRowCellValue(e.RowHandle, ViewReceivers.Columns.Item(COL_NAME), oLastName)
ViewReceivers.SetRowCellValue(e.RowHandle, ViewReceivers.Columns.Item(COL_CODE), oAccessCode)
CellValueChanged = False
Else
Dim oMsg = Resources.Envelope.Error_email_Validation
oMsg = oMsg.Replace("@Mail", oEmailAdress)
MsgBox(oMsg, MsgBoxStyle.Exclamation, Text)
ViewReceivers.DeleteRow(ViewReceivers.FocusedRowHandle)
End If
Dim oLastName As String = Controller.GetLastNameByEmailAdress(oEmailAdress)
Dim oAccessCode As String = Helpers.GetAccessCode()
ViewReceivers.SetRowCellValue(e.RowHandle, ViewReceivers.Columns.Item(COL_EMAIL), oEmailAdress)
ViewReceivers.SetRowCellValue(e.RowHandle, ViewReceivers.Columns.Item(COL_NAME), oLastName)
ViewReceivers.SetRowCellValue(e.RowHandle, ViewReceivers.Columns.Item(COL_CODE), oAccessCode)
CellValueChanged = False
End If End If
End If End If
End If End If
End Sub End Sub
Private Sub email_validation()
End Sub
Private Function IsValidEmailAddress(pEmailAddress As String) As Boolean
Try
If pEmailAddress.Contains("@") Then
Dim oAddress = New System.Net.Mail.MailAddress(pEmailAddress)
Return oAddress.Address = pEmailAddress
Else
Return False
End If
Catch ex As Exception
Logger.Error(ex)
Return False
End Try
End Function
Private Sub RibbonControl1_Click(sender As Object, e As EventArgs) Handles RibbonControl1.Click Private Sub RibbonControl1_Click(sender As Object, e As EventArgs) Handles RibbonControl1.Click
End Sub End Sub
Private Sub BarButtonItem3_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles BarButtonItem3.ItemClick
frmChooseDocVariant.ShowDialog()
End Sub
Private Sub BarButtonItem4_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles BarButtonItem4.ItemClick
End Sub
Private Sub ViewReceivers_CellValueChanging(sender As Object, e As CellValueChangedEventArgs) Handles ViewReceivers.CellValueChanging
End Sub
End Class End Class

View File

@@ -220,6 +220,9 @@
<data name="&gt;&gt;btCancel.ZOrder" xml:space="preserve"> <data name="&gt;&gt;btCancel.ZOrder" xml:space="preserve">
<value>7</value> <value>7</value>
</data> </data>
<data name="cmbCertificationType.Enabled" type="System.Boolean, mscorlib">
<value>False</value>
</data>
<data name="cmbCertificationType.Location" type="System.Drawing.Point, System.Drawing"> <data name="cmbCertificationType.Location" type="System.Drawing.Point, System.Drawing">
<value>207, 162</value> <value>207, 162</value>
</data> </data>

View File

@@ -566,6 +566,7 @@ Public Class frmMain
If oReceiver.Email = selReceiver.Email Then If oReceiver.Email = selReceiver.Email Then
If oController.ActionService.ResendReceiver(oEnvelope, oReceiver) = True Then If oController.ActionService.ResendReceiver(oEnvelope, oReceiver) = True Then
MsgBox(Resources.Envelope.Invitation_successfully_resend, MsgBoxStyle.Information, Text) MsgBox(Resources.Envelope.Invitation_successfully_resend, MsgBoxStyle.Information, Text)
End If End If
End If End If
Next Next

View File

@@ -62,7 +62,7 @@ Public Class frmSplashScreen
Private Sub Worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles Worker.DoWork Private Sub Worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles Worker.DoWork
Dim oState As State = DirectCast(e.Argument, State) Dim oState As State = DirectCast(e.Argument, State)
Worker.ReportProgress(20, "Initialize Database") Worker.ReportProgress(20, "Initializing Database")
Thread.Sleep(300) Thread.Sleep(300)
Dim oConnectionString = MSSQLServer.DecryptConnectionString(oState.Config.ConnectionString) Dim oConnectionString = MSSQLServer.DecryptConnectionString(oState.Config.ConnectionString)
@@ -74,13 +74,13 @@ Public Class frmSplashScreen
DB_DD_ECM = oState.Database DB_DD_ECM = oState.Database
End If End If
Worker.ReportProgress(40, "Initialize Confguration") Worker.ReportProgress(40, "Initializing Configuration")
Thread.Sleep(300) Thread.Sleep(300)
Dim ConfigModel = New ConfigModel(oState) Dim ConfigModel = New ConfigModel(oState)
oState.DbConfig = ConfigModel.LoadConfiguration() oState.DbConfig = ConfigModel.LoadConfiguration()
DOCUMENT_PATH_MOVE_AFTSEND = oState.DbConfig.DOCUMENT_PATH_MOVE_AFTSEND ' DOCUMENT_PATH_MOVE_AFTSEND = oState.DbConfig.DOCUMENT_PATH_MOVE_AFTSEND
Worker.ReportProgress(60, "Initialize User") Worker.ReportProgress(60, "Initializing User")
Dim oKey = oState.Database.GetScalarValue("SELECT LICENSE FROM TBDD_3RD_PARTY_MODULES WHERE NAME = 'GDPICTURE' and ACTIVE = 1") Dim oKey = oState.Database.GetScalarValue("SELECT LICENSE FROM TBDD_3RD_PARTY_MODULES WHERE NAME = 'GDPICTURE' and ACTIVE = 1")
Thread.Sleep(300) Thread.Sleep(300)
If oKey.ToString <> String.Empty Then If oKey.ToString <> String.Empty Then
@@ -91,7 +91,7 @@ Public Class frmSplashScreen
Dim oUser = oUserModel.SelectUser() Dim oUser = oUserModel.SelectUser()
Worker.ReportProgress(80, "Initialize Rights") Worker.ReportProgress(80, "Initializing Rights")
Thread.Sleep(300) Thread.Sleep(300)
' This checks for module access and admin rights ' This checks for module access and admin rights

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Core.Abstractions" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.API" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.API" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.15" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.15" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />

View File

@@ -1,7 +1,7 @@
using DigitalData.Core.API; using DigitalData.Core.API;
using DigitalData.Core.Application; using DigitalData.Core.Application;
using DigitalData.UserManager.Application; using DigitalData.UserManager.Application;
using EnvelopeGenerator.Application; using EnvelopeGenerator.Application.Extensions;
using EnvelopeGenerator.Infrastructure; using EnvelopeGenerator.Infrastructure;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Core.Abstractions" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.Infrastructure" Version="2.0.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="2.0.0" /> <PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" />

View File

@@ -1,5 +1,4 @@
using EnvelopeGenerator.Common; using EnvelopeGenerator.Web.Services;
using EnvelopeGenerator.Web.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Web.Controllers namespace EnvelopeGenerator.Web.Controllers

View File

@@ -9,6 +9,7 @@ using EnvelopeGenerator.Extensions;
namespace EnvelopeGenerator.Web.Controllers namespace EnvelopeGenerator.Web.Controllers
{ {
[Authorize] [Authorize]
[Route("api/[controller]")]
public class DocumentController : BaseController public class DocumentController : BaseController
{ {
private readonly EnvelopeOldService envelopeService; private readonly EnvelopeOldService envelopeService;
@@ -48,7 +49,7 @@ namespace EnvelopeGenerator.Web.Controllers
} }
[Authorize] [Authorize]
[HttpPost("api/document/{envelopeKey}")] [HttpPost("{envelopeKey}")]
public async Task<IActionResult> Open(string envelopeKey) public async Task<IActionResult> Open(string envelopeKey)
{ {
try try

View File

@@ -1,5 +1,4 @@
using DigitalData.Core.DTO; using DigitalData.Core.DTO;
using EnvelopeGenerator.Application;
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Common; using EnvelopeGenerator.Common;
using EnvelopeGenerator.Web.Services; using EnvelopeGenerator.Web.Services;
@@ -23,7 +22,6 @@ namespace EnvelopeGenerator.Web.Controllers
private readonly IReceiverService _receiverService; private readonly IReceiverService _receiverService;
private readonly IEnvelopeReceiverService _envRcvService; private readonly IEnvelopeReceiverService _envRcvService;
public EnvelopeController(DatabaseService database, public EnvelopeController(DatabaseService database,
EnvelopeOldService envelope, EnvelopeOldService envelope,
ILogger<EnvelopeController> logger, UrlEncoder urlEncoder, ILogger<EnvelopeController> logger, UrlEncoder urlEncoder,

View File

@@ -18,6 +18,8 @@ using static EnvelopeGenerator.Common.Constants;
using Ganss.Xss; using Ganss.Xss;
using Newtonsoft.Json; using Newtonsoft.Json;
using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Application.DTOs;
using DigitalData.Core.Client;
using DevExpress.Utils.About;
namespace EnvelopeGenerator.Web.Controllers namespace EnvelopeGenerator.Web.Controllers
{ {
@@ -33,8 +35,10 @@ namespace EnvelopeGenerator.Web.Controllers
private readonly Cultures _cultures; private readonly Cultures _cultures;
private readonly IEnvelopeMailService _mailService; private readonly IEnvelopeMailService _mailService;
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService; private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
private readonly IMessagingService _msgService;
private readonly IEnvelopeReceiverCache _erCache;
public HomeController(EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, HtmlSanitizer sanitizer, Cultures cultures, IEnvelopeMailService envelopeMailService, 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, IMessagingService messagingService, IEnvelopeReceiverCache envelopeReceiverCache)
{ {
this.envelopeOldService = envelopeOldService; this.envelopeOldService = envelopeOldService;
_envRcvService = envelopeReceiverService; _envRcvService = envelopeReceiverService;
@@ -46,6 +50,25 @@ namespace EnvelopeGenerator.Web.Controllers
_mailService = envelopeMailService; _mailService = envelopeMailService;
_logger = logger; _logger = logger;
_readOnlyService = readOnlyService; _readOnlyService = readOnlyService;
_msgService = messagingService;
_erCache = envelopeReceiverCache;
}
[HttpGet("/")]
public IActionResult Main([FromQuery] string? culture = null)
{
//TODO: add a middelware or use an asp.net functionality insead of this code-smell
culture = culture is not null ? _sanitizer.Sanitize(culture) : null;
if (UserLanguage is null && culture is null)
{
UserLanguage = _cultures.Default.Language;
return Redirect($"{Request.Headers["Referer"]}?culture={_cultures.Default.Language}");
}
ViewData["UserCulture"] = _cultures[UserLanguage];
return View();
} }
[HttpGet("EnvelopeKey/{envelopeReceiverId}")] [HttpGet("EnvelopeKey/{envelopeReceiverId}")]
@@ -116,9 +139,9 @@ namespace EnvelopeGenerator.Web.Controllers
{ {
ViewData["UserCulture"] = _cultures[UserLanguage]; ViewData["UserCulture"] = _cultures[UserLanguage];
return await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId).ThenAsync( return await _envRcvService.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId: envelopeReceiverId).ThenAsync(
Success: isExisting => isExisting ? View().WithData("EnvelopeKey", envelopeReceiverId) : this.ViewEnvelopeNotFound(), Success: er => View().WithData("EnvelopeKey", envelopeReceiverId),
Fail: IActionResult (messages,notices) => Fail: IActionResult (messages, notices) =>
{ {
_logger.LogNotice(notices); _logger.LogNotice(notices);
Response.StatusCode = StatusCodes.Status401Unauthorized; Response.StatusCode = StatusCodes.Status401Unauthorized;
@@ -133,7 +156,7 @@ namespace EnvelopeGenerator.Web.Controllers
} }
[HttpPost("EnvelopeKey/{envelopeReceiverId}/Locked")] [HttpPost("EnvelopeKey/{envelopeReceiverId}/Locked")]
public async Task<IActionResult> LogInEnvelope([FromRoute] string envelopeReceiverId, [FromForm] string access_code) public async Task<IActionResult> LogInEnvelope([FromRoute] string envelopeReceiverId, [FromForm] Auth auth)
{ {
try try
{ {
@@ -148,33 +171,77 @@ namespace EnvelopeGenerator.Web.Controllers
return Unauthorized(); return Unauthorized();
} }
_logger.LogInformation($"Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]"); _logger.LogInformation("Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]", uuid, signature);
//check access code //check access code
EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId);
var verification = await _envRcvService.VerifyAccessCodeAsync(uuid: uuid, signature: signature, accessCode: access_code);
if (verification.IsFailed)
{
_logger.LogNotice(verification.Notices);
Response.StatusCode = StatusCodes.Status401Unauthorized;
return View("EnvelopeLocked")
.WithData("ErrorMessage", _localizer[WebKey.WrongAccessCode].Value);
}
return await _envRcvService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature).ThenAsync<EnvelopeReceiverDto, IActionResult>( return await _envRcvService.ReadWithSecretByUuidSignatureAsync(uuid: uuid, signature: signature).ThenAsync<EnvelopeReceiverSecretDto, IActionResult>(
SuccessAsync: async er => SuccessAsync: async er_secret =>
{ {
//check the access code verification async Task<IActionResult> SendSmsView()
if (verification.IsWrong()) {
{ var res = await _msgService.SendSmsCodeAsync(er_secret.PhoneNumber!, envelopeReceiverId: envelopeReceiverId);
//Constants.EnvelopeStatus.AccessCodeIncorrect if (res.Ok)
await _historyService.RecordAsync(er.EnvelopeId, er.Receiver!.EmailAddress, Constants.EnvelopeStatus.AccessCodeIncorrect); return View("EnvelopeLocked").WithData("ViaSms", true).WithData("Expiration", res.Expiration);
Response.StatusCode = StatusCodes.Status401Unauthorized; else if (!res.Allowed)
return View("EnvelopeLocked") return View("EnvelopeLocked").WithData("ViaSms", true).WithData("Expiration", res.AllowedAt);
.WithData("ErrorMessage", _localizer[WebKey.WrongAccessCode].Value); else
} {
var res_json = JsonConvert.SerializeObject(res);
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: $"An unexpected error occurred while sending an SMS code. Response: ${res_json}");
return this.ViewInnerServiceError();
}
}
await _historyService.RecordAsync(er.EnvelopeId, er.Receiver!.EmailAddress, Constants.EnvelopeStatus.AccessCodeCorrect); if (auth.HasMulti)
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return View("EnvelopeLocked")
.WithData("ErrorMessage", _localizer[WebKey.WrongAccessCode].Value);
}
else if (auth.HasAccessCode)
{
//check the access code verification
if (er_secret.AccessCode != auth.AccessCode)
{
//Constants.EnvelopeStatus.AccessCodeIncorrect
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, Constants.EnvelopeStatus.AccessCodeIncorrect);
Response.StatusCode = StatusCodes.Status401Unauthorized;
return View("EnvelopeLocked")
.WithData("ErrorMessage", _localizer[WebKey.WrongAccessCode].Value);
}
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, Constants.EnvelopeStatus.AccessCodeCorrect);
//check if the user has phone is added
if (er_secret.HasPhoneNumber)
{
return await SendSmsView();
}
}
else if (auth.HasSmsCode)
{
var smsCode = await _erCache.GetSmsCodeAsync(envelopeReceiverId);
if (smsCode is null)
return RedirectToAction("EnvelopeLocked", new { envelopeReceiverId });
if(auth.SmsCode != smsCode)
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
ViewData["ErrorMessage"] = _localizer[WebKey.WrongAccessCode].Value;
return await SendSmsView();
}
}
else
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return View("EnvelopeLocked")
.WithData("ErrorMessage", _localizer[WebKey.WrongAccessCode].Value);
}
//continue the process without important data to minimize security errors.
EnvelopeReceiverDto er = er_secret;
ViewData["EnvelopeKey"] = envelopeReceiverId; ViewData["EnvelopeKey"] = envelopeReceiverId;
//check rejection //check rejection

View File

@@ -1,6 +1,5 @@
using EnvelopeGenerator.Application.Resources; using EnvelopeGenerator.Application.Resources;
using Ganss.Xss; using Ganss.Xss;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;

View File

@@ -1,7 +1,6 @@
using DigitalData.Core.DTO; using DigitalData.Core.DTO;
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly; using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Extensions;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json; using Newtonsoft.Json;

View File

@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
namespace EnvelopeGenerator.Web.Controllers.Test
{
[Route("api/[controller]")]
[ApiController]
public class TestCacheController : ControllerBase
{
private readonly IDistributedCache _cache;
public TestCacheController(IDistributedCache cache)
{
_cache = cache;
}
[HttpPost]
public async Task<IActionResult> SetCacheAsync(string key, string value)
{
var options = new DistributedCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
await _cache.SetStringAsync(key, value, options);
return Ok();
}
[HttpGet]
public async Task<IActionResult> GetCacheAsync(string key)
{
var value = await _cache.GetStringAsync(key);
return value is null ? BadRequest() : Ok(value);
}
}
}

View File

@@ -0,0 +1,24 @@
using EnvelopeGenerator.Application.Contracts;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Web.Controllers.Test
{
[Route("api/[controller]")]
[ApiController]
public class TestMessagingController : ControllerBase
{
private readonly IMessagingService _service;
public TestMessagingController(IMessagingService service)
{
_service = service;
}
[HttpPost]
public async Task<IActionResult> SendAsync(string recipient, string message, bool staticResponse = true)
{
var res = await _service.SendSmsAsync(recipient: recipient, message: message);
return res is null? StatusCode(StatusCodes.Status500InternalServerError) : Ok(res);
}
}
}

View File

@@ -1,10 +1,10 @@
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Common;
using EnvelopeGenerator.Common;
using EnvelopeGenerator.Web.Services; using EnvelopeGenerator.Web.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Web.Controllers.Test namespace EnvelopeGenerator.Web.Controllers.Test
{ {
[Route("api/test/[controller]")]
public class TestViewController : BaseController public class TestViewController : BaseController
{ {
private readonly EnvelopeOldService envelopeOldService; private readonly EnvelopeOldService envelopeOldService;
@@ -16,13 +16,13 @@ namespace EnvelopeGenerator.Web.Controllers.Test
_config = configuration; _config = configuration;
} }
[HttpGet("/")] [HttpGet]
public IActionResult Index() public IActionResult Index()
{ {
return View("Index"); return View("Index");
} }
[HttpPost("/")] [HttpPost]
public IActionResult DebugEnvelopes([FromForm] string? password) public IActionResult DebugEnvelopes([FromForm] string? password)
{ {
try try

View File

@@ -5,7 +5,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<PackageId>EnvelopeGenerator.Web</PackageId> <PackageId>EnvelopeGenerator.Web</PackageId>
<Version>2.4.0.0</Version> <Version>2.7.0</Version>
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company> <Company>Digital Data GmbH</Company>
<Product>EnvelopeGenerator.Web</Product> <Product>EnvelopeGenerator.Web</Product>
@@ -13,11 +13,18 @@
<PackageTags>digital data envelope generator web</PackageTags> <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> <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> <ApplicationIcon>Assets\icon.ico</ApplicationIcon>
<AssemblyVersion>2.4.0.0</AssemblyVersion> <AssemblyVersion>2.7.0</AssemblyVersion>
<FileVersion>2.4.0.0</FileVersion> <FileVersion>2.7.0</FileVersion>
<Copyright>Copyright © 2024 Digital Data GmbH. All rights reserved.</Copyright> <Copyright>Copyright © 2024 Digital Data GmbH. All rights reserved.</Copyright>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="wwwroot\lib\typed.js\**" />
<Content Remove="wwwroot\lib\typed.js\**" />
<EmbeddedResource Remove="wwwroot\lib\typed.js\**" />
<None Remove="wwwroot\lib\typed.js\**" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Remove="bundleconfig.json" /> <Content Remove="bundleconfig.json" />
</ItemGroup> </ItemGroup>
@@ -39,8 +46,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" /> <PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="BuildBundlerMinifier2022" Version="2.9.9" /> <PackageReference Include="BuildBundlerMinifier2022" Version="2.9.9" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.API" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.API" Version="2.0.1" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="2.0.0" /> <PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="2.0.0" />
<PackageReference Include="HtmlSanitizer" Version="8.0.865" /> <PackageReference Include="HtmlSanitizer" Version="8.0.865" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
@@ -50,6 +57,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" /> <PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
@@ -113,6 +121,12 @@
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath>\</PackagePath> <PackagePath>\</PackagePath>
</None> </None>
<None Update="Scripts\create-sql-cache.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Scripts\create-sql-cache.sql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,13 @@
namespace EnvelopeGenerator.Web.Models
{
public record Auth(string? AccessCode = null, string? SmsCode = null)
{
public bool HasAccessCode => AccessCode is not null;
public bool HasSmsCode => SmsCode is not null;
public bool HasMulti => HasAccessCode && HasSmsCode;
public bool HasNone => !(HasAccessCode || HasSmsCode);
}
}

View File

@@ -1,4 +1,3 @@
using DigitalData.UserManager.Infrastructure.Repositories;
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.Services; using EnvelopeGenerator.Application.Services;
using EnvelopeGenerator.Web.Services; using EnvelopeGenerator.Web.Services;
@@ -9,7 +8,6 @@ using NLog.Web;
using DigitalData.Core.API; using DigitalData.Core.API;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using EnvelopeGenerator.Web.Models; using EnvelopeGenerator.Web.Models;
using DigitalData.Core.DTO;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using Ganss.Xss; using Ganss.Xss;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -17,6 +15,7 @@ using EnvelopeGenerator.Application;
using DigitalData.EmailProfilerDispatcher; using DigitalData.EmailProfilerDispatcher;
using EnvelopeGenerator.Infrastructure; using EnvelopeGenerator.Infrastructure;
using EnvelopeGenerator.Web.Sanitizers; using EnvelopeGenerator.Web.Sanitizers;
using EnvelopeGenerator.Application.Extensions;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("Logging initialized!"); logger.Info("Logging initialized!");
@@ -81,6 +80,12 @@ try
//AddEF Core dbcontext //AddEF Core dbcontext
var connStr = config.GetConnectionString(Key.Default) ?? throw new InvalidOperationException("There is no default connection string in appsettings.json."); var connStr = config.GetConnectionString(Key.Default) ?? throw new InvalidOperationException("There is no default connection string in appsettings.json.");
builder.Services.AddDbContext<EGDbContext>(options => options.UseSqlServer(connStr)); builder.Services.AddDbContext<EGDbContext>(options => options.UseSqlServer(connStr));
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = connStr;
options.SchemaName = "dbo";
options.TableName = "TBDD_CACHE";
});
// Add envelope generator services // Add envelope generator services
builder.Services.AddEnvelopeGenerator(config); builder.Services.AddEnvelopeGenerator(config);

View File

@@ -0,0 +1,2 @@
dotnet sql-cache create "CONNECTION_STRING" dbo TBDD_CACHE
pause

View File

@@ -0,0 +1,23 @@
USE [DD_ECM]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TBDD_CACHE](
[Id] [nvarchar](449) NOT NULL,
[Value] [varbinary](max) NOT NULL,
[ExpiresAtTime] [datetimeoffset](7) NOT NULL,
[SlidingExpirationInSeconds] [bigint] NULL,
[AbsoluteExpiration] [datetimeoffset](7) NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

View File

@@ -98,13 +98,6 @@ namespace EnvelopeGenerator.Web.Services
//if documenet_path_dmz is existing in config, replace the path with it //if documenet_path_dmz is existing in config, replace the path with it
var config = await _configService.ReadDefaultAsync(); var config = await _configService.ReadDefaultAsync();
if (config.DocumentPathDmz is not null && config.DocumentPathDmz != string.Empty)
foreach (var doc in envelope.Documents)
{
doc.Filepath = doc.Filepath.Replace(config.DocumentPath, config.DocumentPathDmz);
}
return new() return new()
{ {
Receiver = receiver, Receiver = receiver,

View File

@@ -20,5 +20,4 @@
<section class="text-center"> <section class="text-center">
<p>Der Zeitraum für die gemeinsame Nutzung von Dokumenten ist abgelaufen.</p> <p>Der Zeitraum für die gemeinsame Nutzung von Dokumenten ist abgelaufen.</p>
</section> </section>
</div> </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,13 @@
@{ @using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
@using Newtonsoft.Json
@{
var nonce = _accessor.HttpContext?.Items["csp-nonce"] as string; var nonce = _accessor.HttpContext?.Items["csp-nonce"] as string;
var logo = _logoOpt.Value; var logo = _logoOpt.Value;
ViewData["Title"] = _localizer[WebKey.DocProtected]; ViewData["Title"] = _localizer[WebKey.DocProtected];
var userCulture = ViewData["UserCulture"] as Culture; var userCulture = ViewData["UserCulture"] as Culture;
bool viaSms = ViewData["ViaSms"] is bool _viaSms && _viaSms;
var accessCodeName = viaSms ? "smsCode" : "accessCode";
DateTime? expiration = ViewData["Expiration"] is DateTime _expiration ? _expiration : null;
} }
<div class="page container py-4 px-4"> <div class="page container py-4 px-4">
<header class="text-center"> <header class="text-center">
@@ -10,28 +15,32 @@
<h3 class="text">@_localizer[WebKey.WelcomeToTheESignPortal]</h3> <h3 class="text">@_localizer[WebKey.WelcomeToTheESignPortal]</h3>
<img class="@logo.LockedPageClass" src="@logo.Src" /> <img class="@logo.LockedPageClass" src="@logo.Src" />
</div> </div>
<div class="icon locked mt-4 mb-1"> <div class="icon locked @(viaSms ? "sms-tfa" : "") mt-4 mb-1">
<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" fill="currentColor" class="bi bi-shield-lock" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" fill="currentColor" class="bi bi-shield-lock" viewBox="0 0 16 16">
<path d="M5.338 1.59a61 61 0 0 0-2.837.856.48.48 0 0 0-.328.39c-.554 4.157.726 7.19 2.253 9.188a10.7 10.7 0 0 0 2.287 2.233c.346.244.652.42.893.533q.18.085.293.118a1 1 0 0 0 .101.025 1 1 0 0 0 .1-.025q.114-.034.294-.118c.24-.113.547-.29.893-.533a10.7 10.7 0 0 0 2.287-2.233c1.527-1.997 2.807-5.031 2.253-9.188a.48.48 0 0 0-.328-.39c-.651-.213-1.75-.56-2.837-.855C9.552 1.29 8.531 1.067 8 1.067c-.53 0-1.552.223-2.662.524zM5.072.56C6.157.265 7.31 0 8 0s1.843.265 2.928.56c1.11.3 2.229.655 2.887.87a1.54 1.54 0 0 1 1.044 1.262c.596 4.477-.787 7.795-2.465 9.99a11.8 11.8 0 0 1-2.517 2.453 7 7 0 0 1-1.048.625c-.28.132-.581.24-.829.24s-.548-.108-.829-.24a7 7 0 0 1-1.048-.625 11.8 11.8 0 0 1-2.517-2.453C1.928 10.487.545 7.169 1.141 2.692A1.54 1.54 0 0 1 2.185 1.43 63 63 0 0 1 5.072.56" /> <path d="M5.338 1.59a61 61 0 0 0-2.837.856.48.48 0 0 0-.328.39c-.554 4.157.726 7.19 2.253 9.188a10.7 10.7 0 0 0 2.287 2.233c.346.244.652.42.893.533q.18.085.293.118a1 1 0 0 0 .101.025 1 1 0 0 0 .1-.025q.114-.034.294-.118c.24-.113.547-.29.893-.533a10.7 10.7 0 0 0 2.287-2.233c1.527-1.997 2.807-5.031 2.253-9.188a.48.48 0 0 0-.328-.39c-.651-.213-1.75-.56-2.837-.855C9.552 1.29 8.531 1.067 8 1.067c-.53 0-1.552.223-2.662.524zM5.072.56C6.157.265 7.31 0 8 0s1.843.265 2.928.56c1.11.3 2.229.655 2.887.87a1.54 1.54 0 0 1 1.044 1.262c.596 4.477-.787 7.795-2.465 9.99a11.8 11.8 0 0 1-2.517 2.453 7 7 0 0 1-1.048.625c-.28.132-.581.24-.829.24s-.548-.108-.829-.24a7 7 0 0 1-1.048-.625 11.8 11.8 0 0 1-2.517-2.453C1.928 10.487.545 7.169 1.141 2.692A1.54 1.54 0 0 1 2.185 1.43 63 63 0 0 1 5.072.56" />
<path d="M9.5 6.5a1.5 1.5 0 0 1-1 1.415l.385 1.99a.5.5 0 0 1-.491.595h-.788a.5.5 0 0 1-.49-.595l.384-1.99a1.5 1.5 0 1 1 2-1.415" /> <path d="M9.5 6.5a1.5 1.5 0 0 1-1 1.415l.385 1.99a.5.5 0 0 1-.491.595h-.788a.5.5 0 0 1-.49-.595l.384-1.99a1.5 1.5 0 1 1 2-1.415" />
</svg> </svg>
</div> </div>
<h1>@_localizer[WebKey.LockedTitle]</h1> <h1>@_localizer[viaSms ? WebKey.LockedSmsTfaTitle : WebKey.LockedTitle]</h1>
</header> </header>
<section class="text-center"> <section class="text-center">
<p>@_localizer[WebKey.LockedBody]</p> <p>@_localizer[viaSms ? WebKey.LockedSmsTfaBody : WebKey.LockedBody]</p>
</section> </section>
<div class="row m-0 p-0"> <div class="row m-0 p-0">
<div class="access-code-panel justify-content-center align-items-center p-0 m-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"> <form id="form-access-code" class="form form-floating mb-0" method="post">
<div class="form-floating access-code-form-floating"> <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"> <input type="password" id="access_code" class="form-control" name="@accessCodeName" placeholder="@_localizer[viaSms ? WebKey.LockedSmsAccessCode : WebKey.LockedAccessCode]" required="required">
<label for="access_code">@_localizer[WebKey.LockedAccessCode]</label> <label for="access_code">@_localizer[viaSms ? WebKey.LockedSmsAccessCode : WebKey.LockedAccessCode]</label>
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
<span class="material-symbols-outlined"> <span class="material-symbols-outlined">
login login
</span> </span>
</button> </button>
@if (expiration is not null)
{
<div id="sms-timer" class="alert alert-primary" role="alert">00:00</div>
}
</div> </div>
</form> </form>
</div> </div>
@@ -45,8 +54,34 @@
} }
<section class="no-receiver-explanation text-center"> <section class="no-receiver-explanation text-center">
<details> <details>
<summary>@_localizer[WebKey.LockedFooterTitle]</summary> <summary>@_localizer[viaSms ? WebKey.LockedSmsTfaFooterTitle : WebKey.LockedFooterTitle]</summary>
<p>@_localizer[WebKey.LockedFooterBody]</p> <p>@_localizer[viaSms ? WebKey.LockedSmsTfaFooterBody : WebKey.LockedFooterBody]</p>
</details> </details>
</section> </section>
</div> </div>
<script nonce="@nonce">
var expiration = new Date(@Html.Raw(JsonConvert.SerializeObject(expiration)));
const element = document.getElementById("sms-timer");
const interval = setInterval(function () {
var now = new Date();
var diffInMillis = expiration - now;
if (diffInMillis <= 0) {
element.textContent = "00:00";
clearInterval(interval);
return;
}
var minutes = Math.floor(diffInMillis / 1000 / 60);
var seconds = Math.floor((diffInMillis / 1000) % 60);
var formattedMinutes = minutes.toString().padStart(2, '0');
var formattedSeconds = seconds.toString().padStart(2, '0');
var remainingTime = `${formattedMinutes}:${formattedSeconds}`;
element.textContent = remainingTime;
}, 1000);
</script>

View File

@@ -68,5 +68,4 @@
</p> </p>
</div> </div>
</section> </section>
</div> </div>
<footer class="container" id="page-footer">&copy; SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></footer>

View File

@@ -14,5 +14,4 @@
<section class="text-center"> <section class="text-center">
<p>Sie haben das Dokument signiert. Im Anschluss erhalten Sie eine schriftliche Bestätigung.</p> <p>Sie haben das Dokument signiert. Im Anschluss erhalten Sie eine schriftliche Bestätigung.</p>
</section> </section>
</div> </div>
<footer class="container" id="page-footer">&copy; SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></footer>

View File

@@ -0,0 +1,36 @@
@{
var nonce = _accessor.HttpContext?.Items["csp-nonce"] as string;
var logo = _logoOpt.Value;
ViewData["Title"] = _localizer["Home"];
var userCulture = ViewData["UserCulture"] as Culture;
}
<div class="page container py-4 px-4">
<header class="text-center">
<div class="header-1 alert alert-secondary" role="alert">
<h3 class="text">@_localizer[WebKey.WelcomeToTheESignPortal]</h3>
<img class="@logo.LockedPageClass" src="@logo.Src" />
</div>
<div class="icon mt-4 mb-1">
<img src="~/img/sign_flow_min.svg">
</div>
<h1>signFlow</h1>
</header>
<section class="text-center">
<div class="alert alert-light" role="alert">
<p class="home-description"><span id="home-description"></span></p>
</div>
</section>
@if (ViewData["ErrorMessage"] is string errMsg)
{
<div id="access-code-error-message" class="alert alert-danger row" role="alert">
@_sanitizer.Sanitize(errMsg)
</div>
}
</div>
<script nonce="@nonce">
const msg = "@_localizer[WebKey.HomePageDescription]";
var typed = new Typed('#home-description', {
strings: [msg],
typeSpeed: 15,
});
</script>

View File

@@ -4,6 +4,8 @@
@{ @{
var nonce = _accessor.HttpContext?.Items["csp-nonce"] as string; var nonce = _accessor.HttpContext?.Items["csp-nonce"] as string;
var userCulture = ViewData["UserCulture"] as Culture; var userCulture = ViewData["UserCulture"] as Culture;
//TODO: instead of default assignment add a middleware for culture
userCulture ??= _cultures.Default;
var isReadOnly = false; var isReadOnly = false;
if (ViewData["IsReadOnly"] is bool isReadOnly_bool) if (ViewData["IsReadOnly"] is bool isReadOnly_bool)
isReadOnly = isReadOnly_bool; isReadOnly = isReadOnly_bool;
@@ -58,6 +60,7 @@
<script src="~/lib/pspdfkit/dist-2024.3.2/pspdfkit.js"></script> <script src="~/lib/pspdfkit/dist-2024.3.2/pspdfkit.js"></script>
<script src="~/js/util.min.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="~/js/api-service.min.js" asp-append-version="true"></script>
<script src="~/lib/typed.js@2.1.0/dist/typed.umd.js"></script>
@await RenderSectionAsync("Scripts", required: false) @await RenderSectionAsync("Scripts", required: false)
@{ @{
var settings = new JsonSerializerSettings var settings = new JsonSerializerSettings
@@ -76,7 +79,7 @@
<script src="~/js/event-binder.min.js" asp-append-version="true"></script> <script src="~/js/event-binder.min.js" asp-append-version="true"></script>
@Html.AntiForgeryToken() @Html.AntiForgeryToken()
<footer> <footer>
<span>&copy; SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></span> <span>&copy; SignFlow 2023-2024 <a href="https://digitaldata.works" target="_blank">Digital Data GmbH</a></span>
<div class="dropup flag-dropdown"> <div class="dropup flag-dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="langDropdownMenuButton" data-bs-toggle="dropdown" aria-expanded="false"> <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> <span class="fi @userCulture?.FIClass.TrySanitize(_sanitizer) me-2" id="selectedFlag"></span><span id="selectedLanguage"></span>
@@ -94,7 +97,7 @@
} }
</ul> </ul>
</div> </div>
<a href="/privacy-policy.de-DE.html">Datenschutz</a> <a href="/privacy-policy.@(userCulture?.Language).html" target="_blank">@_localizer[WebKey.Privacy]</a>
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -11,11 +11,16 @@
public static readonly string de_DE = nameof(de_DE).Replace("_", "-"); public static readonly string de_DE = nameof(de_DE).Replace("_", "-");
public static readonly string en_US = nameof(en_US).Replace("_", "-"); public static readonly string en_US = nameof(en_US).Replace("_", "-");
public static readonly string LockedTitle = nameof(LockedTitle); public static readonly string LockedTitle = nameof(LockedTitle);
public static readonly string LockedSmsTfaTitle = nameof(LockedSmsTfaTitle);
public static readonly string LockedBody = nameof(LockedBody); public static readonly string LockedBody = nameof(LockedBody);
public static readonly string LockedSmsTfaBody = nameof(LockedSmsTfaBody);
public static readonly string LocakedOpen = nameof(LocakedOpen); public static readonly string LocakedOpen = nameof(LocakedOpen);
public static readonly string LockedAccessCode = nameof(LockedAccessCode); public static readonly string LockedAccessCode = nameof(LockedAccessCode);
public static readonly string LockedSmsAccessCode = nameof(LockedSmsAccessCode);
public static readonly string LockedFooterTitle = nameof(LockedFooterTitle); public static readonly string LockedFooterTitle = nameof(LockedFooterTitle);
public static readonly string LockedSmsTfaFooterTitle = nameof(LockedSmsTfaFooterTitle);
public static readonly string LockedFooterBody = nameof(LockedFooterBody); public static readonly string LockedFooterBody = nameof(LockedFooterBody);
public static readonly string LockedSmsTfaFooterBody = nameof(LockedSmsTfaFooterBody);
public static readonly string WrongAccessCode = nameof(WrongAccessCode); public static readonly string WrongAccessCode = nameof(WrongAccessCode);
public static readonly string SignDoc = nameof(SignDoc); public static readonly string SignDoc = nameof(SignDoc);
public static readonly string DocRejected = nameof(DocRejected); public static readonly string DocRejected = nameof(DocRejected);
@@ -35,5 +40,7 @@
public static readonly string SigningProcessTitle = nameof(SigningProcessTitle); public static readonly string SigningProcessTitle = nameof(SigningProcessTitle);
public static readonly string WelcomeToTheESignPortal = nameof(WelcomeToTheESignPortal); public static readonly string WelcomeToTheESignPortal = nameof(WelcomeToTheESignPortal);
public static readonly string ViewDoc = nameof(ViewDoc); public static readonly string ViewDoc = nameof(ViewDoc);
public static readonly string HomePageDescription = nameof(HomePageDescription);
public static readonly string Privacy = nameof(Privacy);
} }
} }

View File

@@ -125,5 +125,18 @@
"[SIGNATURE_TYPE]": "signieren", "[SIGNATURE_TYPE]": "signieren",
"[REASON]": "" "[REASON]": ""
} }
} },
"GTXMessagingConfig": {
"AuthKey": "ep$?A!Gs"
},
"SmsConfig": {
"Uri": "https://rest.gtx-messaging.net",
"Path": "smsc/sendsms/f566f7e5-bdf2-4a9a-bf52-ed88215a432e/json",
"Headers": {},
"QueryParams": {
"from": "signFlow"
},
"CodeCacheValidityPeriod": "00:10:00"
},
"EnvelopeReceiverCacheParams": {}
} }

View File

@@ -130,6 +130,15 @@ main {
margin: 0 0 0.5vh 0; margin: 0 0 0.5vh 0;
} }
.home-description {
text-align: justify;
font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New;
font-weight: 500;
font-size: .95em;
letter-spacing: -1px;
word-spacing: -2px;
}
.envelope-view { .envelope-view {
display: flex; /* d-flex */ display: flex; /* d-flex */
flex-direction: column; /* flex-column */ flex-direction: column; /* flex-column */
@@ -212,10 +221,15 @@ footer {
} }
.page header .icon.locked { .page header .icon.locked {
background-color: #ffc107; background-color: #ffa407;
color: #000; color: #000;
} }
.page header .icon.locked.sms-tfa {
background-color: #ff7207;
color: #000;
}
.page header .icon.signed { .page header .icon.signed {
background-color: #146c43; background-color: #146c43;
color: #fff; color: #fff;
@@ -388,14 +402,17 @@ footer#page-footer {
.access-code-form-floating { .access-code-form-floating {
display: flex; display: flex;
justify-content: start; justify-content: space-between;
flex-direction: row; flex-direction: row;
align-items: center;
} }
.access-code-form-floating button { .access-code-form-floating button {
align-content: center; align-content: center;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-top-left-radius: 0; border-top-left-radius: 0;
margin:0;
height: 100%;
} }
.access-code-form-floating input { .access-code-form-floating input {
@@ -413,6 +430,24 @@ footer#page-footer {
height: 2.5rem; height: 2.5rem;
} }
#sms-timer {
height: 3rem;
display: flex;
align-items: center;
font-family: 'Arial', sans-serif;
font-weight: bold;
color: #ffffff;
background-color: #007bff;
margin: 0 0 0 2rem;
border-radius: 8px;
text-align: center;
}
#sms-timer:hover {
background-color: #0056b3;
cursor: pointer;
}
/*.flag-dropdown button { /*.flag-dropdown button {
height: 100%; height: 100%;
}*/ }*/

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
<svg width="98" height="95" viewBox="0 0 98 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"/>
</svg>

After

Width:  |  Height:  |  Size: 700 B

View File

@@ -11,7 +11,7 @@ document.querySelectorAll('.email-input').forEach(input => {
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
var dropdownItems = document.querySelectorAll('.culture-dropdown-item'); var dropdownItems = document.querySelectorAll('.culture-dropdown-item');
dropdownItems.forEach(function (item) { dropdownItems.forEach(function (item) {
item.addEventListener('click', async function(event) { item.addEventListener('click', async function (event) {
event.preventDefault(); event.preventDefault();
var language = this.getAttribute('data-language'); var language = this.getAttribute('data-language');
var flagCode = this.getAttribute('data-flag'); var flagCode = this.getAttribute('data-flag');
@@ -21,6 +21,30 @@ document.addEventListener('DOMContentLoaded', function () {
}); });
}); });
const setTimer = (elementId, expirationTime) => {
const element = document.getElementById(elementId);
const interval = setInterval(function () {
var now = new Date();
var diffInMillis = expirationTime - now;
if (diffInMillis <= 0) {
element.textContent = "00:00";
clearInterval(interval);
}
var minutes = Math.floor(diffInMillis / 1000 / 60);
var seconds = Math.floor((diffInMillis / 1000) % 60);
var formattedMinutes = minutes.toString().padStart(2, '0');
var formattedSeconds = seconds.toString().padStart(2, '0');
var remainingTime = `${formattedMinutes}:${formattedSeconds}`;
element.textContent = remainingTime;
}, 1000);
}
const bsNotify = (message, options) => alertify.notify( 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>`, `<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', 'custom',

View File

@@ -1 +1 @@
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)})})});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}} 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)})})});const setTimer=(n,t)=>{const i=document.getElementById(n),r=setInterval(function(){var u=new Date,n=t-u;n<=0&&(i.textContent="00:00",clearInterval(r));var f=Math.floor(n/6e4),e=Math.floor(n/1e3%60),o=f.toString().padStart(2,"0"),s=e.toString().padStart(2,"0"),h=`${o}:${s}`;i.textContent=h},1e3)},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}}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2023 Matt Boldt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,378 @@
[![Code Climate](https://codeclimate.com/github/mattboldt/typed.js/badges/gpa.svg)](https://codeclimate.com/github/mattboldt/typed.js)
[![GitHub release](https://img.shields.io/github/release/mattboldt/typed.js.svg)]()
[![npm](https://img.shields.io/npm/dt/typed.js.svg)](https://img.shields.io/npm/dt/typed.js.svg)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/mattboldt/typed.js/master/LICENSE.txt)
<img src="https://raw.githubusercontent.com/mattboldt/typed.js/master/logo-cropped.png" width="450px" title="Typed.js" />
### [Live Demo](http://www.mattboldt.com/demos/typed-js/) | [View All Demos](http://mattboldt.github.io/typed.js/) | [View Full Docs](http://mattboldt.github.io/typed.js/docs) | [mattboldt.com](http://www.mattboldt.com)
Typed.js is a library that types. Enter in any string, and watch it type at the speed you've set, backspace what it's typed, and begin a new sentence for however many strings you've set.
---
## Installation
### CDN
```html
<script src="https://unpkg.com/typed.js@2.1.0/dist/typed.umd.js"></script>
```
For use directly in the browser via `<script>` tag:
```html
<!-- Element to contain animated typing -->
<span id="element"></span>
<!-- Load library from the CDN -->
<script src="https://unpkg.com/typed.js@2.1.0/dist/typed.umd.js"></script>
<!-- Setup and start animation! -->
<script>
var typed = new Typed('#element', {
strings: ['<i>First</i> sentence.', '&amp; a second sentence.'],
typeSpeed: 50,
});
</script>
</body>
```
### As an ESModule
For use with a build tool like [Vite](https://vitejs.dev/), and/or in a React application, install with NPM or Yarn.
#### NPM
```
npm install typed.js
```
#### Yarn
```
yarn add typed.js
```
#### General ESM Usage
```js
import Typed from 'typed.js';
const typed = new Typed('#element', {
strings: ['<i>First</i> sentence.', '&amp; a second sentence.'],
typeSpeed: 50,
});
```
### ReactJS Usage
```js
import React from 'react';
import Typed from 'typed.js';
function MyComponent() {
// Create reference to store the DOM element containing the animation
const el = React.useRef(null);
React.useEffect(() => {
const typed = new Typed(el.current, {
strings: ['<i>First</i> sentence.', '&amp; a second sentence.'],
typeSpeed: 50,
});
return () => {
// Destroy Typed instance during cleanup to stop animation
typed.destroy();
};
}, []);
return (
<div className="App">
<span ref={el} />
</div>
);
}
```
More complex hook-based function component: https://jsfiddle.net/mattboldt/60h9an7y/
Class component: https://jsfiddle.net/mattboldt/ovat9jmp/
### Use with Vue.js
Check out the Vue.js component: https://github.com/Orlandster/vue-typed-js
### Use it as WebComponent
Check out the WebComponent: https://github.com/Orlandster/wc-typed-js
## Wonderful sites that have used (or are using) Typed.js
https://github.com/features/package-registry
https://slack.com/
https://envato.com/
https://gorails.com/
https://productmap.co/
https://www.typed.com/
https://apeiron.io
https://git.market/
https://commando.io/
http://testdouble.com/agency.html
https://www.capitalfactory.com/
http://www.maxcdn.com/
https://www.powerauth.com/
---
### Strings from static HTML (SEO Friendly)
Rather than using the `strings` array to insert strings, you can place an HTML `div` on the page and read from it.
This allows bots and search engines, as well as users with JavaScript disabled, to see your text on the page.
```javascript
<script>
var typed = new Typed('#typed', {
stringsElement: '#typed-strings'
});
</script>
```
```html
<div id="typed-strings">
<p>Typed.js is a <strong>JavaScript</strong> library.</p>
<p>It <em>types</em> out sentences.</p>
</div>
<span id="typed"></span>
```
### Type Pausing
You can pause in the middle of a string for a given amount of time by including an escape character.
```javascript
var typed = new Typed('#element', {
// Waits 1000ms after typing "First"
strings: ['First ^1000 sentence.', 'Second sentence.'],
});
```
### Smart Backspacing
In the following example, this would only backspace the words after "This is a"
```javascript
var typed = new Typed('#element', {
strings: ['This is a JavaScript library', 'This is an ES6 module'],
smartBackspace: true, // Default value
});
```
### Bulk Typing
The following example would emulate how a terminal acts when typing a command and seeing its result.
```javascript
var typed = new Typed('#element', {
strings: ['git push --force ^1000\n `pushed to origin with option force`'],
});
```
### CSS
CSS animations are built upon initialization in JavaScript. But, you can customize them at your will! These classes are:
```css
/* Cursor */
.typed-cursor {
}
/* If fade out option is set */
.typed-fade-out {
}
```
## Customization
```javascript
var typed = new Typed('#element', {
/**
* @property {array} strings strings to be typed
* @property {string} stringsElement ID of element containing string children
*/
strings: [
'These are the default values...',
'You know what you should do?',
'Use your own!',
'Have a great day!',
],
stringsElement: null,
/**
* @property {number} typeSpeed type speed in milliseconds
*/
typeSpeed: 0,
/**
* @property {number} startDelay time before typing starts in milliseconds
*/
startDelay: 0,
/**
* @property {number} backSpeed backspacing speed in milliseconds
*/
backSpeed: 0,
/**
* @property {boolean} smartBackspace only backspace what doesn't match the previous string
*/
smartBackspace: true,
/**
* @property {boolean} shuffle shuffle the strings
*/
shuffle: false,
/**
* @property {number} backDelay time before backspacing in milliseconds
*/
backDelay: 700,
/**
* @property {boolean} fadeOut Fade out instead of backspace
* @property {string} fadeOutClass css class for fade animation
* @property {boolean} fadeOutDelay Fade out delay in milliseconds
*/
fadeOut: false,
fadeOutClass: 'typed-fade-out',
fadeOutDelay: 500,
/**
* @property {boolean} loop loop strings
* @property {number} loopCount amount of loops
*/
loop: false,
loopCount: Infinity,
/**
* @property {boolean} showCursor show cursor
* @property {string} cursorChar character for cursor
* @property {boolean} autoInsertCss insert CSS for cursor and fadeOut into HTML <head>
*/
showCursor: true,
cursorChar: '|',
autoInsertCss: true,
/**
* @property {string} attr attribute for typing
* Ex: input placeholder, value, or just HTML text
*/
attr: null,
/**
* @property {boolean} bindInputFocusEvents bind to focus and blur if el is text input
*/
bindInputFocusEvents: false,
/**
* @property {string} contentType 'html' or 'null' for plaintext
*/
contentType: 'html',
/**
* Before it begins typing
* @param {Typed} self
*/
onBegin: (self) => {},
/**
* All typing is complete
* @param {Typed} self
*/
onComplete: (self) => {},
/**
* Before each string is typed
* @param {number} arrayPos
* @param {Typed} self
*/
preStringTyped: (arrayPos, self) => {},
/**
* After each string is typed
* @param {number} arrayPos
* @param {Typed} self
*/
onStringTyped: (arrayPos, self) => {},
/**
* During looping, after last string is typed
* @param {Typed} self
*/
onLastStringBackspaced: (self) => {},
/**
* Typing has been stopped
* @param {number} arrayPos
* @param {Typed} self
*/
onTypingPaused: (arrayPos, self) => {},
/**
* Typing has been started after being stopped
* @param {number} arrayPos
* @param {Typed} self
*/
onTypingResumed: (arrayPos, self) => {},
/**
* After reset
* @param {Typed} self
*/
onReset: (self) => {},
/**
* After stop
* @param {number} arrayPos
* @param {Typed} self
*/
onStop: (arrayPos, self) => {},
/**
* After start
* @param {number} arrayPos
* @param {Typed} self
*/
onStart: (arrayPos, self) => {},
/**
* After destroy
* @param {Typed} self
*/
onDestroy: (self) => {},
});
```
## Contributing
### [View Contribution Guidelines](./.github/CONTRIBUTING.md)
## end
Thanks for checking this out. If you have any questions, I'll be on [Twitter](https://twitter.com/atmattb).
If you're using this, let me know! I'd love to see it.
It would also be great if you mentioned me or my website somewhere. [www.mattboldt.com](http://www.mattboldt.com)

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

View File

@@ -0,0 +1,3 @@
!function(t,s){"object"==typeof exports&&"undefined"!=typeof module?module.exports=s():"function"==typeof define&&define.amd?define(s):(t||self).Typed=s()}(this,function(){function t(){return t=Object.assign?Object.assign.bind():function(t){for(var s=1;s<arguments.length;s++){var e=arguments[s];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])}return t},t.apply(this,arguments)}var s={strings:["These are the default values...","You know what you should do?","Use your own!","Have a great day!"],stringsElement:null,typeSpeed:0,startDelay:0,backSpeed:0,smartBackspace:!0,shuffle:!1,backDelay:700,fadeOut:!1,fadeOutClass:"typed-fade-out",fadeOutDelay:500,loop:!1,loopCount:Infinity,showCursor:!0,cursorChar:"|",autoInsertCss:!0,attr:null,bindInputFocusEvents:!1,contentType:"html",onBegin:function(t){},onComplete:function(t){},preStringTyped:function(t,s){},onStringTyped:function(t,s){},onLastStringBackspaced:function(t){},onTypingPaused:function(t,s){},onTypingResumed:function(t,s){},onReset:function(t){},onStop:function(t,s){},onStart:function(t,s){},onDestroy:function(t){}},e=new(/*#__PURE__*/function(){function e(){}var n=e.prototype;return n.load=function(e,n,i){if(e.el="string"==typeof i?document.querySelector(i):i,e.options=t({},s,n),e.isInput="input"===e.el.tagName.toLowerCase(),e.attr=e.options.attr,e.bindInputFocusEvents=e.options.bindInputFocusEvents,e.showCursor=!e.isInput&&e.options.showCursor,e.cursorChar=e.options.cursorChar,e.cursorBlinking=!0,e.elContent=e.attr?e.el.getAttribute(e.attr):e.el.textContent,e.contentType=e.options.contentType,e.typeSpeed=e.options.typeSpeed,e.startDelay=e.options.startDelay,e.backSpeed=e.options.backSpeed,e.smartBackspace=e.options.smartBackspace,e.backDelay=e.options.backDelay,e.fadeOut=e.options.fadeOut,e.fadeOutClass=e.options.fadeOutClass,e.fadeOutDelay=e.options.fadeOutDelay,e.isPaused=!1,e.strings=e.options.strings.map(function(t){return t.trim()}),e.stringsElement="string"==typeof e.options.stringsElement?document.querySelector(e.options.stringsElement):e.options.stringsElement,e.stringsElement){e.strings=[],e.stringsElement.style.cssText="clip: rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px;";var r=Array.prototype.slice.apply(e.stringsElement.children),o=r.length;if(o)for(var a=0;a<o;a+=1)e.strings.push(r[a].innerHTML.trim())}for(var u in e.strPos=0,e.currentElContent=this.getCurrentElContent(e),e.currentElContent&&e.currentElContent.length>0&&(e.strPos=e.currentElContent.length-1,e.strings.unshift(e.currentElContent)),e.sequence=[],e.strings)e.sequence[u]=u;e.arrayPos=0,e.stopNum=0,e.loop=e.options.loop,e.loopCount=e.options.loopCount,e.curLoop=0,e.shuffle=e.options.shuffle,e.pause={status:!1,typewrite:!0,curString:"",curStrPos:0},e.typingComplete=!1,e.autoInsertCss=e.options.autoInsertCss,e.autoInsertCss&&(this.appendCursorAnimationCss(e),this.appendFadeOutAnimationCss(e))},n.getCurrentElContent=function(t){return t.attr?t.el.getAttribute(t.attr):t.isInput?t.el.value:"html"===t.contentType?t.el.innerHTML:t.el.textContent},n.appendCursorAnimationCss=function(t){var s="data-typed-js-cursor-css";if(t.showCursor&&!document.querySelector("["+s+"]")){var e=document.createElement("style");e.setAttribute(s,"true"),e.innerHTML="\n .typed-cursor{\n opacity: 1;\n }\n .typed-cursor.typed-cursor--blink{\n animation: typedjsBlink 0.7s infinite;\n -webkit-animation: typedjsBlink 0.7s infinite;\n animation: typedjsBlink 0.7s infinite;\n }\n @keyframes typedjsBlink{\n 50% { opacity: 0.0; }\n }\n @-webkit-keyframes typedjsBlink{\n 0% { opacity: 1; }\n 50% { opacity: 0.0; }\n 100% { opacity: 1; }\n }\n ",document.body.appendChild(e)}},n.appendFadeOutAnimationCss=function(t){var s="data-typed-fadeout-js-css";if(t.fadeOut&&!document.querySelector("["+s+"]")){var e=document.createElement("style");e.setAttribute(s,"true"),e.innerHTML="\n .typed-fade-out{\n opacity: 0;\n transition: opacity .25s;\n }\n .typed-cursor.typed-cursor--blink.typed-fade-out{\n -webkit-animation: 0;\n animation: 0;\n }\n ",document.body.appendChild(e)}},e}()),n=new(/*#__PURE__*/function(){function t(){}var s=t.prototype;return s.typeHtmlChars=function(t,s,e){if("html"!==e.contentType)return s;var n=t.substring(s).charAt(0);if("<"===n||"&"===n){var i;for(i="<"===n?">":";";t.substring(s+1).charAt(0)!==i&&!(1+ ++s>t.length););s++}return s},s.backSpaceHtmlChars=function(t,s,e){if("html"!==e.contentType)return s;var n=t.substring(s).charAt(0);if(">"===n||";"===n){var i;for(i=">"===n?"<":"&";t.substring(s-1).charAt(0)!==i&&!(--s<0););s--}return s},t}());/*#__PURE__*/
return function(){function t(t,s){e.load(this,s,t),this.begin()}var s=t.prototype;return s.toggle=function(){this.pause.status?this.start():this.stop()},s.stop=function(){this.typingComplete||this.pause.status||(this.toggleBlinking(!0),this.pause.status=!0,this.options.onStop(this.arrayPos,this))},s.start=function(){this.typingComplete||this.pause.status&&(this.pause.status=!1,this.pause.typewrite?this.typewrite(this.pause.curString,this.pause.curStrPos):this.backspace(this.pause.curString,this.pause.curStrPos),this.options.onStart(this.arrayPos,this))},s.destroy=function(){this.reset(!1),this.options.onDestroy(this)},s.reset=function(t){void 0===t&&(t=!0),clearInterval(this.timeout),this.replaceText(""),this.cursor&&this.cursor.parentNode&&(this.cursor.parentNode.removeChild(this.cursor),this.cursor=null),this.strPos=0,this.arrayPos=0,this.curLoop=0,t&&(this.insertCursor(),this.options.onReset(this),this.begin())},s.begin=function(){var t=this;this.options.onBegin(this),this.typingComplete=!1,this.shuffleStringsIfNeeded(this),this.insertCursor(),this.bindInputFocusEvents&&this.bindFocusEvents(),this.timeout=setTimeout(function(){0===t.strPos?t.typewrite(t.strings[t.sequence[t.arrayPos]],t.strPos):t.backspace(t.strings[t.sequence[t.arrayPos]],t.strPos)},this.startDelay)},s.typewrite=function(t,s){var e=this;this.fadeOut&&this.el.classList.contains(this.fadeOutClass)&&(this.el.classList.remove(this.fadeOutClass),this.cursor&&this.cursor.classList.remove(this.fadeOutClass));var i=this.humanizer(this.typeSpeed),r=1;!0!==this.pause.status?this.timeout=setTimeout(function(){s=n.typeHtmlChars(t,s,e);var i=0,o=t.substring(s);if("^"===o.charAt(0)&&/^\^\d+/.test(o)){var a=1;a+=(o=/\d+/.exec(o)[0]).length,i=parseInt(o),e.temporaryPause=!0,e.options.onTypingPaused(e.arrayPos,e),t=t.substring(0,s)+t.substring(s+a),e.toggleBlinking(!0)}if("`"===o.charAt(0)){for(;"`"!==t.substring(s+r).charAt(0)&&(r++,!(s+r>t.length)););var u=t.substring(0,s),p=t.substring(u.length+1,s+r),c=t.substring(s+r+1);t=u+p+c,r--}e.timeout=setTimeout(function(){e.toggleBlinking(!1),s>=t.length?e.doneTyping(t,s):e.keepTyping(t,s,r),e.temporaryPause&&(e.temporaryPause=!1,e.options.onTypingResumed(e.arrayPos,e))},i)},i):this.setPauseStatus(t,s,!0)},s.keepTyping=function(t,s,e){0===s&&(this.toggleBlinking(!1),this.options.preStringTyped(this.arrayPos,this));var n=t.substring(0,s+=e);this.replaceText(n),this.typewrite(t,s)},s.doneTyping=function(t,s){var e=this;this.options.onStringTyped(this.arrayPos,this),this.toggleBlinking(!0),this.arrayPos===this.strings.length-1&&(this.complete(),!1===this.loop||this.curLoop===this.loopCount)||(this.timeout=setTimeout(function(){e.backspace(t,s)},this.backDelay))},s.backspace=function(t,s){var e=this;if(!0!==this.pause.status){if(this.fadeOut)return this.initFadeOut();this.toggleBlinking(!1);var i=this.humanizer(this.backSpeed);this.timeout=setTimeout(function(){s=n.backSpaceHtmlChars(t,s,e);var i=t.substring(0,s);if(e.replaceText(i),e.smartBackspace){var r=e.strings[e.arrayPos+1];e.stopNum=r&&i===r.substring(0,s)?s:0}s>e.stopNum?(s--,e.backspace(t,s)):s<=e.stopNum&&(e.arrayPos++,e.arrayPos===e.strings.length?(e.arrayPos=0,e.options.onLastStringBackspaced(),e.shuffleStringsIfNeeded(),e.begin()):e.typewrite(e.strings[e.sequence[e.arrayPos]],s))},i)}else this.setPauseStatus(t,s,!1)},s.complete=function(){this.options.onComplete(this),this.loop?this.curLoop++:this.typingComplete=!0},s.setPauseStatus=function(t,s,e){this.pause.typewrite=e,this.pause.curString=t,this.pause.curStrPos=s},s.toggleBlinking=function(t){this.cursor&&(this.pause.status||this.cursorBlinking!==t&&(this.cursorBlinking=t,t?this.cursor.classList.add("typed-cursor--blink"):this.cursor.classList.remove("typed-cursor--blink")))},s.humanizer=function(t){return Math.round(Math.random()*t/2)+t},s.shuffleStringsIfNeeded=function(){this.shuffle&&(this.sequence=this.sequence.sort(function(){return Math.random()-.5}))},s.initFadeOut=function(){var t=this;return this.el.className+=" "+this.fadeOutClass,this.cursor&&(this.cursor.className+=" "+this.fadeOutClass),setTimeout(function(){t.arrayPos++,t.replaceText(""),t.strings.length>t.arrayPos?t.typewrite(t.strings[t.sequence[t.arrayPos]],0):(t.typewrite(t.strings[0],0),t.arrayPos=0)},this.fadeOutDelay)},s.replaceText=function(t){this.attr?this.el.setAttribute(this.attr,t):this.isInput?this.el.value=t:"html"===this.contentType?this.el.innerHTML=t:this.el.textContent=t},s.bindFocusEvents=function(){var t=this;this.isInput&&(this.el.addEventListener("focus",function(s){t.stop()}),this.el.addEventListener("blur",function(s){t.el.value&&0!==t.el.value.length||t.start()}))},s.insertCursor=function(){this.showCursor&&(this.cursor||(this.cursor=document.createElement("span"),this.cursor.className="typed-cursor",this.cursor.setAttribute("aria-hidden",!0),this.cursor.innerHTML=this.cursorChar,this.el.parentNode&&this.el.parentNode.insertBefore(this.cursor,this.el.nextSibling)))},t}()});
//# sourceMappingURL=typed.umd.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,257 @@
/**
* Welcome to Typed.js!
* @param {string} elementId HTML element ID _OR_ HTML element
* @param {object} options options object
* @returns {object} a new Typed object
*/
declare module 'typed.js' {
export interface TypedOptions {
/**
* strings to be typed
*/
strings?: string[];
/**
* ID or instance of HTML element of element containing string children
*/
stringsElement?: string | Element;
/**
* type speed in milliseconds
*/
typeSpeed?: number;
/**
* time before typing starts in milliseconds
*/
startDelay?: number;
/**
* backspacing speed in milliseconds
*/
backSpeed?: number;
/**
* only backspace what doesn't match the previous string
*/
smartBackspace?: boolean;
/**
* shuffle the strings
*/
shuffle?: boolean;
/**
* time before backspacing in milliseconds
*/
backDelay?: number;
/**
* Fade out instead of backspace
*/
fadeOut?: boolean;
/**
* css class for fade animation
*/
fadeOutClass?: string;
/**
* Fade out delay in milliseconds
*/
fadeOutDelay?: number;
/**
* loop strings
*/
loop?: boolean;
/**
* amount of loops
*/
loopCount?: number;
/**
* show cursor
*/
showCursor?: boolean;
/**
* character for cursor
*/
cursorChar?: string;
/**
* insert CSS for cursor and fadeOut into HTML
*/
autoInsertCss?: boolean;
/**
* attribute for typing Ex: input placeholder, value, or just HTML text
*/
attr?: string;
/**
* bind to focus and blur if el is text input
*/
bindInputFocusEvents?: boolean;
/**
* 'html' or 'null' for plaintext
*/
contentType?: string;
/**
* Before it begins typing the first string
*/
onBegin?(self: Typed): void;
/**
* All typing is complete
*/
onComplete?(self: Typed): void;
/**
* Before each string is typed
*/
preStringTyped?(arrayPos: number, self: Typed): void;
/**
* After each string is typed
*/
onStringTyped?(arrayPos: number, self: Typed): void;
/**
* During looping, after last string is typed
*/
onLastStringBackspaced?(self: Typed): void;
/**
* Typing has been stopped
*/
onTypingPaused?(arrayPos: number, self: Typed): void;
/**
* Typing has been started after being stopped
*/
onTypingResumed?(arrayPos: number, self: Typed): void;
/**
* After reset
*/
onReset?(self: Typed): void;
/**
* After stop
*/
onStop?(arrayPos: number, self: Typed): void;
/**
* After start
*/
onStart?(arrayPos: number, self: Typed): void;
/**
* After destroy
*/
onDestroy?(self: Typed): void;
}
export default class Typed {
constructor(elementId: any, options: TypedOptions);
/**
* Toggle start() and stop() of the Typed instance
* @public
*/
public toggle(): void;
/**
* Stop typing / backspacing and enable cursor blinking
* @public
*/
public stop(): void;
/**
* Start typing / backspacing after being stopped
* @public
*/
public start(): void;
/**
* Destroy this instance of Typed
* @public
*/
public destroy(): void;
/**
* Reset Typed and optionally restarts
* @param {boolean} restart
* @public
*/
public reset(restart?: boolean): void;
cursor: HTMLSpanElement;
strPos: number;
arrayPos: number;
curLoop: number;
/**
* Begins the typing animation
* @private
*/
private begin;
typingComplete: boolean;
timeout: any;
/**
* Called for each character typed
* @param {string} curString the current string in the strings array
* @param {number} curStrPos the current position in the curString
* @private
*/
private typewrite;
temporaryPause: boolean;
/**
* Continue to the next string & begin typing
* @param {string} curString the current string in the strings array
* @param {number} curStrPos the current position in the curString
* @private
*/
private keepTyping;
/**
* We're done typing the current string
* @param {string} curString the current string in the strings array
* @param {number} curStrPos the current position in the curString
* @private
*/
private doneTyping;
/**
* Backspaces 1 character at a time
* @param {string} curString the current string in the strings array
* @param {number} curStrPos the current position in the curString
* @private
*/
private backspace;
stopNum: number;
/**
* Full animation is complete
* @private
*/
private complete;
/**
* Has the typing been stopped
* @param {string} curString the current string in the strings array
* @param {number} curStrPos the current position in the curString
* @param {boolean} isTyping
* @private
*/
private setPauseStatus;
/**
* Toggle the blinking cursor
* @param {boolean} isBlinking
* @private
*/
private toggleBlinking;
cursorBlinking: any;
/**
* Speed in MS to type
* @param {number} speed
* @private
*/
private humanizer;
/**
* Shuffle the sequence of the strings array
* @private
*/
private shuffleStringsIfNeeded;
sequence: any;
/**
* Adds a CSS class to fade out current string
* @private
*/
private initFadeOut;
/**
* Replaces current text in the HTML element
* depending on element type
* @param {string} str
* @private
*/
private replaceText;
/**
* If using input elements, bind focus in order to
* start and stop the animation
* @private
*/
private bindFocusEvents;
/**
* On init, insert the cursor element
* @private
*/
private insertCursor;
}
}

View File

@@ -0,0 +1,36 @@
{
"name": "typed.js",
"version": "2.1.0",
"homepage": "https://github.com/mattboldt/typed.js",
"repository": "https://github.com/mattboldt/typed.js",
"license": "MIT",
"author": "Matt Boldt",
"description": "A JavaScript Typing Animation Library",
"type": "module",
"source": "src/typed.js",
"types": "./index.d.ts",
"files": [
"dist",
"index.d.ts"
],
"exports": {
"require": "./dist/typed.cjs",
"import": "./dist/typed.module.js",
"types": "./index.d.ts"
},
"main": "./dist/typed.cjs",
"module": "./dist/typed.module.js",
"unpkg": "./dist/typed.umd.js",
"keywords": [
"typed",
"animation"
],
"devDependencies": {
"microbundle": "^0.15.1"
},
"scripts": {
"build": "microbundle --name=Typed",
"dev": "microbundle --name=Typed watch",
"diff": "git diff -- ':^docs'"
}
}