diff --git a/EnvelopeGenerator.Common/Entities/EmailData.vb b/EnvelopeGenerator.Common/Entities/EmailData.vb index 368728b1..b94b69d7 100644 --- a/EnvelopeGenerator.Common/Entities/EmailData.vb +++ b/EnvelopeGenerator.Common/Entities/EmailData.vb @@ -19,4 +19,20 @@ Public Class EmailData End Get End Property + Public Sub New(pEnvelope As Envelope, pReceiver As EnvelopeReceiver) + EmailAdress = pReceiver.Email + EmailSubject = pEnvelope.Subject + Message = pEnvelope.Message + ReferenceID = pEnvelope.Id + ReferenceString = pEnvelope.Uuid + ReceiverName = pReceiver.Name + SenderAdress = pEnvelope.User.Email + SenderName = pEnvelope.User.FullName + End Sub + + Public Sub New() + End Sub + + + End Class diff --git a/EnvelopeGenerator.Common/Entities/EmailTemplate.vb b/EnvelopeGenerator.Common/Entities/EmailTemplate.vb index db310299..871e7631 100644 --- a/EnvelopeGenerator.Common/Entities/EmailTemplate.vb +++ b/EnvelopeGenerator.Common/Entities/EmailTemplate.vb @@ -28,7 +28,7 @@ Public Class EmailTemplate _DocumentSignedBodyTemplate = New List(Of String) From { "Guten Tag, ", "", - "Ihre Unterschrift auf dem Dokument wurde verarbeitet.", + "Ihre Unterschrift auf dem Dokument wurde gespeichert.", "", "Mit freundlichen Grüßen", "" diff --git a/EnvelopeGenerator.Common/Entities/Envelope.vb b/EnvelopeGenerator.Common/Entities/Envelope.vb index 8b8672cc..d7fd48af 100644 --- a/EnvelopeGenerator.Common/Entities/Envelope.vb +++ b/EnvelopeGenerator.Common/Entities/Envelope.vb @@ -6,8 +6,8 @@ Public Property Status As Constants.EnvelopeStatus Public Property Uuid As String = Guid.NewGuid.ToString() - Public Property Subject As String - Public Property Message As String + Public Property Subject As String = My.Resources.Envelope.You_received_a_document_to_sign_ + Public Property Message As String = My.Resources.Envelope.Please_read_and_sign_this_document Public Property AddedWhen As Date Public Property User As New User() diff --git a/EnvelopeGenerator.Common/Models/HistoryModel.vb b/EnvelopeGenerator.Common/Models/HistoryModel.vb index 733f4b70..7d25b9f5 100644 --- a/EnvelopeGenerator.Common/Models/HistoryModel.vb +++ b/EnvelopeGenerator.Common/Models/HistoryModel.vb @@ -11,6 +11,16 @@ Public Class HistoryModel Select Case pActionType Case Constants.EnvelopeHistoryActionType.Created Return "Umschlag erfolgreich erstellt" + + Case Constants.EnvelopeHistoryActionType.Sent + Return "Umschlag an Empfänger versendet" + + Case Constants.EnvelopeHistoryActionType.Seen + Return "Umschlag von Empfänger geöffnet" + + Case Constants.EnvelopeHistoryActionType.Signed + Return "Umschlag von Empfänger signiert" + Case Else Return pActionType.ToString() End Select diff --git a/EnvelopeGenerator.Common/Strings/Envelope.en.resx b/EnvelopeGenerator.Common/Strings/Envelope.en.resx index 3a158b28..c098c017 100644 --- a/EnvelopeGenerator.Common/Strings/Envelope.en.resx +++ b/EnvelopeGenerator.Common/Strings/Envelope.en.resx @@ -174,6 +174,9 @@ Only one file is allowed! + + Please read and sign this document. + Recipient could not be deleted! @@ -183,4 +186,7 @@ The envelope could not be deleted! + + You received a document to sign: + \ No newline at end of file diff --git a/EnvelopeGenerator.Common/Strings/Envelope.resx b/EnvelopeGenerator.Common/Strings/Envelope.resx index 898ccfde..4a8acedf 100644 --- a/EnvelopeGenerator.Common/Strings/Envelope.resx +++ b/EnvelopeGenerator.Common/Strings/Envelope.resx @@ -174,6 +174,9 @@ Es ist nur eine Datei zulässig! + + Bitte lesen und unterzeichnen Sie dieses Dokument. + Empfänger konnte nicht gelöscht werden! @@ -183,4 +186,7 @@ Der Umschlag konnte nicht gelöscht werden! + + Sie haben ein Dokument zu signieren erhalten: + \ No newline at end of file diff --git a/EnvelopeGenerator.Common/Strings/Envelope1.Designer.vb b/EnvelopeGenerator.Common/Strings/Envelope1.Designer.vb index 2854a778..0eabad97 100644 --- a/EnvelopeGenerator.Common/Strings/Envelope1.Designer.vb +++ b/EnvelopeGenerator.Common/Strings/Envelope1.Designer.vb @@ -235,6 +235,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Sucht eine lokalisierte Zeichenfolge, die Bitte lesen und unterzeichnen Sie dieses Dokument. ähnelt. + ''' + Public Shared ReadOnly Property Please_read_and_sign_this_document() As String + Get + Return ResourceManager.GetString("Please read and sign this document", resourceCulture) + End Get + End Property + ''' ''' Sucht eine lokalisierte Zeichenfolge, die Empfänger konnte nicht gelöscht werden! ähnelt. ''' @@ -261,5 +270,14 @@ Namespace My.Resources Return ResourceManager.GetString("The envelope could not be deleted", resourceCulture) End Get End Property + + ''' + ''' Sucht eine lokalisierte Zeichenfolge, die Sie haben ein Dokument zu signieren erhalten: ähnelt. + ''' + Public Shared ReadOnly Property You_received_a_document_to_sign_() As String + Get + Return ResourceManager.GetString("You received a document to sign:", resourceCulture) + End Get + End Property End Class End Namespace diff --git a/EnvelopeGenerator.Form/Controllers/EnvelopeEditorController.vb b/EnvelopeGenerator.Form/Controllers/EnvelopeEditorController.vb index 2f11939b..439ba44c 100644 --- a/EnvelopeGenerator.Form/Controllers/EnvelopeEditorController.vb +++ b/EnvelopeGenerator.Form/Controllers/EnvelopeEditorController.vb @@ -33,16 +33,8 @@ Public Class EnvelopeEditorController Public Function SendEnvelope() As Boolean For Each receiverItem As EnvelopeReceiver In Envelope.Receivers - Dim oEmailData As New EmailData With + Dim oEmailData As New EmailData(Envelope, receiverItem) With { - .EmailAdress = receiverItem.Email, - .EmailSubject = Envelope.Subject, - .Message = Envelope.Message, - .ReferenceID = Envelope.Id, - .ReferenceString = Envelope.Uuid, - .ReceiverName = receiverItem.Name, - .SenderAdress = Envelope.User.Email, - .SenderName = Envelope.User.FullName, .SignatureLink = Helpers.GetEnvelopeURL(State.DbConfig.SignatureHost, Envelope.Uuid, receiverItem.Signature) } @@ -58,9 +50,22 @@ Public Class EnvelopeEditorController If EnvelopeModel.Send(Envelope) Then - 'TODO: Send email to History - Return True + + Dim newHistoryEntry As New EnvelopeHistoryEntry With { + .EnvelopeId = Envelope.Id, + .ActionType = EnvelopeHistoryActionType.Sent, + .UserReference = Envelope.User.Email + } + + If HistoryModel.Insert(newHistoryEntry) Then + 'TODO: Send email to History + Return True + Else + Logger.Warn("History Entry could not be created!") + Return False + End If Else + Logger.Warn("Envelope could not be updated!") Return False End If End Function diff --git a/EnvelopeGenerator.Form/frmEnvelopeEditor.vb b/EnvelopeGenerator.Form/frmEnvelopeEditor.vb index ed15734f..a5865756 100644 --- a/EnvelopeGenerator.Form/frmEnvelopeEditor.vb +++ b/EnvelopeGenerator.Form/frmEnvelopeEditor.vb @@ -63,8 +63,6 @@ Partial Public Class frmEnvelopeEditor Controller = New EnvelopeEditorController(State, Envelope) Documents = New BindingList(Of EnvelopeDocument)(Controller.Envelope.Documents) Receivers = New BindingList(Of EnvelopeReceiver)(Controller.Envelope.Receivers) - txtMessage.EditValue = Controller.Envelope.Message - txtSubject.EditValue = Controller.Envelope.Subject For Each docItem As EnvelopeDocument In Documents If docItem.Thumbnail Is Nothing Then @@ -78,6 +76,9 @@ Partial Public Class frmEnvelopeEditor End If End If + txtMessage.EditValue = Controller.Envelope.Message + txtSubject.EditValue = Controller.Envelope.Subject + GridDocuments.DataSource = Documents GridReceivers.DataSource = Receivers End Sub diff --git a/EnvelopeGenerator.Form/frmHistory.resx b/EnvelopeGenerator.Form/frmHistory.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/EnvelopeGenerator.Form/frmHistory.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs b/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs index 445f4ff3..58753a5b 100644 --- a/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs @@ -7,10 +7,12 @@ namespace EnvelopeGenerator.Web.Controllers public class EnvelopeController : BaseController { private readonly EnvelopeService envelopeService; + private readonly EmailService emailService; - public EnvelopeController(DatabaseService database, LoggingService logging, EnvelopeService envelope) : base(database, logging) + public EnvelopeController(DatabaseService database, LoggingService logging, EnvelopeService envelope, EmailService email) : base(database, logging) { envelopeService = envelope; + emailService = email; } [HttpGet] @@ -58,6 +60,10 @@ namespace EnvelopeGenerator.Web.Controllers Status = Common.Constants.DocumentStatus.Signed }); + envelopeService.InsertHistoryEntrySigned(response); + + SendSignedEmail(response); + return Ok(); } catch (Exception e) @@ -65,5 +71,18 @@ namespace EnvelopeGenerator.Web.Controllers return ErrorResponse(e); } } + + public bool SendSignedEmail(EnvelopeResponse response) + { + EmailTemplate template = new(); + EmailData emailData = new(response.Envelope, response.Receiver) + { + SignatureLink = "", + }; + + template.FillDocumentSignedEmailBody(emailData); + + return emailService.SendEmail(emailData); + } } } diff --git a/EnvelopeGenerator.Web/Controllers/HistoryController.cs b/EnvelopeGenerator.Web/Controllers/HistoryController.cs index 2e06259b..c0a0d3f7 100644 --- a/EnvelopeGenerator.Web/Controllers/HistoryController.cs +++ b/EnvelopeGenerator.Web/Controllers/HistoryController.cs @@ -7,7 +7,7 @@ namespace EnvelopeGenerator.Web.Controllers { public class ActionObject { - public string? ActionType { get; set; } + public int ActionType { get; set; } } public class HistoryController : BaseController @@ -25,18 +25,12 @@ namespace EnvelopeGenerator.Web.Controllers { try { - logger.Info("EnvelopeController/Get"); + logger.Info("HistoryController/Post"); // Validate Envelope Key and load envelope envelopeService.EnsureValidEnvelopeKey(envelopeKey); EnvelopeResponse response = envelopeService.LoadEnvelope(envelopeKey); - - string actionTypeString = action.ActionType; - - if (!Enum.TryParse(actionTypeString, out var actionType)) - { - return BadRequest(); - }; + EnvelopeHistoryActionType actionType = (EnvelopeHistoryActionType)action.ActionType; envelopeService.InsertHistoryEntry(new EnvelopeHistoryEntry() { diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index 6c896479..6a7ccd53 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -11,6 +11,7 @@ namespace EnvelopeGenerator.Web // Add base services builder.Services.AddSingleton(); builder.Services.AddTransient(); + builder.Services.AddTransient(); // Add higher order services builder.Services.AddSingleton(); diff --git a/EnvelopeGenerator.Web/Services/DatabaseService.cs b/EnvelopeGenerator.Web/Services/DatabaseService.cs index 61fd73df..7f9bd32b 100644 --- a/EnvelopeGenerator.Web/Services/DatabaseService.cs +++ b/EnvelopeGenerator.Web/Services/DatabaseService.cs @@ -15,6 +15,8 @@ namespace EnvelopeGenerator.Web.Services public ElementModel elementModel; public HistoryModel historyModel; public DocumentStatusModel documentStatusModel; + public EmailModel emailModel; + public ConfigModel configModel; public ModelContainer(State state) { @@ -24,6 +26,8 @@ namespace EnvelopeGenerator.Web.Services elementModel = new(state); historyModel = new(state); documentStatusModel = new(state); + emailModel = new(state); + configModel = new(state); } } public readonly ModelContainer? Models; @@ -39,8 +43,17 @@ namespace EnvelopeGenerator.Web.Services { logger.Debug("MSSQL Connection: [{0}]", MSSQL.CurrentConnectionString); + // There is a circular dependency between state and models + // All models need a state object, including the config Model + // The state object needs to be filled with the DbConfig property, + // which is obtained by the config Model. + // So first, the config model is initialized with an incomplete state object, + // then all the other models with the DbConfig property filled. var state = GetState(); - Models = new(state); + var configModel = new ConfigModel(state); + state.DbConfig = configModel.LoadConfiguration(); + + Models = new(state); } else { diff --git a/EnvelopeGenerator.Web/Services/EmailService.cs b/EnvelopeGenerator.Web/Services/EmailService.cs new file mode 100644 index 00000000..2a9506d3 --- /dev/null +++ b/EnvelopeGenerator.Web/Services/EmailService.cs @@ -0,0 +1,38 @@ +using EnvelopeGenerator.Common; + +namespace EnvelopeGenerator.Web.Services +{ + public class EmailService : BaseService + { + private ReceiverModel receiverModel; + private EnvelopeModel envelopeModel; + private HistoryModel historyModel; + private DocumentModel documentModel; + private DocumentStatusModel documentStatusModel; + private EmailModel emailModel; + + public EmailService(IConfiguration Config, LoggingService Logging, DatabaseService database) : base(Config, Logging) + { + logger = Logging.LogConfig.GetLogger(); + + if (database.Models == null) + { + throw new ArgumentNullException("Models not loaded."); + } + + receiverModel = database.Models.receiverModel; + envelopeModel = database.Models.envelopeModel; + historyModel = database.Models.historyModel; + documentModel = database.Models.documentModel; + documentStatusModel = database.Models.documentStatusModel; + emailModel = database.Models.emailModel; + } + + public bool SendEmail(EmailData emailData) + { + return emailModel.Insert(emailData); + } + + +} +} diff --git a/EnvelopeGenerator.Web/wwwroot/js/annotation.js b/EnvelopeGenerator.Web/wwwroot/js/annotation.js index 7d696793..bfbc9f64 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/annotation.js +++ b/EnvelopeGenerator.Web/wwwroot/js/annotation.js @@ -101,23 +101,9 @@ return annotation } - async createFrameAnnotation(annotation, receiver) { - const left = annotation.boundingBox.left - 25 - const top = annotation.boundingBox.top - 25 - const width = 150 - const height = 75 - - const imageUrl = await this.Annotation.createAnnotationFrameBlob( - receiver.name, - width, - height - ) - const request = await fetch(imageUrl) - const blob = await request.blob() - - const imageAttachmentId = await this.Instance.createAttachment(blob) + createImageAnnotation(boundingBox, pageIndex, imageAttachmentId) { const frameAnnotation = new PSPDFKit.Annotations.ImageAnnotation({ - pageIndex: annotation.pageIndex, + pageIndex: pageIndex, isSignature: false, readOnly: true, locked: true, @@ -125,13 +111,9 @@ contentType: 'image/png', imageAttachmentId, description: 'FRAME', - boundingBox: new PSPDFKit.Geometry.Rect({ - left: left, - top: top, - width: width, - height: height, - }), - }) + boundingBox: boundingBox, + }); + return frameAnnotation } async createAnnotationFrameBlob(receiverName, width, height) { diff --git a/EnvelopeGenerator.Web/wwwroot/js/app.js b/EnvelopeGenerator.Web/wwwroot/js/app.js index 921f317e..4ea6b025 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/app.js +++ b/EnvelopeGenerator.Web/wwwroot/js/app.js @@ -93,10 +93,26 @@ class App { const isSignature = !!annotation.isSignature if (isFormField === false && isSignature === true) { - await this.Annotation.createFrameAnnotation( - annotation, - this.currentReceiver - ) + + const left = annotation.boundingBox.left - 25; + const top = annotation.boundingBox.top - 25; + const width = 150; + const height = 75; + + const imageUrl = await this.Annotation.createAnnotationFrameBlob(this.currentReceiver.name, width, height); + + const request = await fetch(imageUrl); + const blob = await request.blob(); + const imageAttachmentId = await this.Instance.createAttachment(blob); + + const frameAnnotation = this.Annotation.createImageAnnotation(new PSPDFKit.Geometry.Rect({ + left: left, + top: top, + width: width, + height: height, + }), annotation.pageIndex, imageAttachmentId) + + this.Instance.create(frameAnnotation); } } @@ -119,8 +135,8 @@ class App { result = await this.handleFinish(null) if (result == true) { - // TODO: Redirect to success page - alert('Dokument erfolgreich signiert!') + // Redirect to success page after saving to database + window.location.href = `/EnvelopeKey/${this.envelopeKey}/Success` } else { alert('Fehler beim Abschließen des Dokuments!') } @@ -147,12 +163,11 @@ class App { JSON.stringify(json) ) + console.log(postEnvelopeResult) + if (postEnvelopeResult === false) { return false } - - // Redirect to success page after saving to database - window.location.href = `/EnvelopeKey/${this.envelopeKey}/Success` } catch (e) { console.error(e) return false diff --git a/EnvelopeGenerator.Web/wwwroot/js/network.js b/EnvelopeGenerator.Web/wwwroot/js/network.js index aaf47479..1790d507 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/network.js +++ b/EnvelopeGenerator.Web/wwwroot/js/network.js @@ -39,6 +39,8 @@ actionType: actionType, } + console.log(data) + const options = { credentials: 'include', method: 'POST',