31-10-2023
This commit is contained in:
parent
b5e0908149
commit
dc24ae3631
@ -3,17 +3,14 @@
|
||||
Public Class EnvelopeReceiver
|
||||
Public Property Id As Integer
|
||||
Public Property UserId As Integer
|
||||
Public Property Signature As String
|
||||
|
||||
Public Property Name As String
|
||||
Public Property Company As String = ""
|
||||
Public Property JobTitle As String = ""
|
||||
|
||||
Public Property Email As String
|
||||
Public ReadOnly Property Signature As String
|
||||
Get
|
||||
Return StringEx.GetChecksum(Email.ToUpper)
|
||||
End Get
|
||||
End Property
|
||||
|
||||
Public ReadOnly Property HasId As Boolean
|
||||
Get
|
||||
Return Id > 0
|
||||
@ -23,4 +20,8 @@ Public Class EnvelopeReceiver
|
||||
Public Property Sequence As Integer = 0
|
||||
Public Property PrivateMessage As String = ""
|
||||
Public Property AccessCode As String = ""
|
||||
|
||||
Public Function GetSignature() As String
|
||||
Return StringEx.GetChecksum(Email.ToUpper)
|
||||
End Function
|
||||
End Class
|
||||
|
||||
@ -8,8 +8,10 @@
|
||||
''' <returns>The EnvelopeKey</returns>
|
||||
Public Shared Function EncodeEnvelopeReceiverId(pEnvelopeUuid As String, pReceiverSignature As String) As String
|
||||
Dim oString = $"{pEnvelopeUuid}::{pReceiverSignature}"
|
||||
'TODO: Verschlüsseln
|
||||
Return oString
|
||||
Dim oBytes = Text.Encoding.UTF8.GetBytes(oString)
|
||||
Dim oBase64String = Convert.ToBase64String(oBytes)
|
||||
|
||||
Return oBase64String
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
@ -18,13 +20,14 @@
|
||||
''' <param name="pEnvelopeReceiverId">The EnvelopeKey</param>
|
||||
''' <returns>A tuple containing EnvelopeUUID and Receiver Signature</returns>
|
||||
Public Shared Function DecodeEnvelopeReceiverId(pEnvelopeReceiverId As String) As Tuple(Of String, String)
|
||||
Dim oSplit = pEnvelopeReceiverId.Split(New String() {"::"}, StringSplitOptions.None)
|
||||
'TODO: Entschlüsseln
|
||||
Dim oBytes = Convert.FromBase64String(pEnvelopeReceiverId)
|
||||
Dim oString = Text.Encoding.UTF8.GetString(oBytes)
|
||||
Dim oSplit = oString.Split(New String() {"::"}, StringSplitOptions.None)
|
||||
|
||||
Return New Tuple(Of String, String)(oSplit(0), oSplit(1))
|
||||
End Function
|
||||
|
||||
Public Shared Function GetEnvelopeURL(pHost As String, pEnvelopeUuid As String, pReceiverSignature As String) As String
|
||||
|
||||
Dim oEnvelopeUserReference As String = EncodeEnvelopeReceiverId(pEnvelopeUuid, pReceiverSignature)
|
||||
Dim oURL As String = String.Format("{0}/EnvelopeKey/{1}", pHost.Trim(), oEnvelopeUserReference)
|
||||
Return oURL
|
||||
|
||||
@ -6,12 +6,14 @@ Imports DigitalData.Modules.Logging
|
||||
Public Class EnvelopeModel
|
||||
Inherits BaseModel
|
||||
|
||||
Private UserModel As UserModel
|
||||
Private ReadOnly UserModel As UserModel
|
||||
Private ReadOnly ReceiverModel As ReceiverModel
|
||||
|
||||
Public Sub New(pState As State)
|
||||
MyBase.New(pState)
|
||||
|
||||
UserModel = New UserModel(pState)
|
||||
ReceiverModel = New ReceiverModel(pState)
|
||||
End Sub
|
||||
|
||||
Private Function ToEnvelope(pRow As DataRow) As Envelope
|
||||
@ -29,6 +31,7 @@ Public Class EnvelopeModel
|
||||
}
|
||||
|
||||
oEnvelope.User = UserModel.SelectUser(oEnvelope.UserId)
|
||||
oEnvelope.Receivers = ReceiverModel.ListEnvelopeReceivers(oEnvelope.Id)
|
||||
|
||||
Return oEnvelope
|
||||
End Function
|
||||
|
||||
@ -14,7 +14,8 @@ Public Class ReceiverModel
|
||||
.Id = pRow.ItemEx("GUID", 0),
|
||||
.Email = pRow.ItemEx("EMAIL_ADDRESS", ""),
|
||||
.Name = pRow.ItemEx("NAME", ""),
|
||||
.Sequence = pRow.ItemEx("SEQUENCE", 0)
|
||||
.Sequence = pRow.ItemEx("SEQUENCE", 0),
|
||||
.Signature = pRow.ItemEx("SIGNATURE", "")
|
||||
}
|
||||
End Function
|
||||
|
||||
@ -40,7 +41,7 @@ Public Class ReceiverModel
|
||||
|
||||
Dim oCommand = New SqlCommand(oSql)
|
||||
oCommand.Parameters.Add("EMAIL", SqlDbType.NVarChar).Value = pReceiver.Email
|
||||
oCommand.Parameters.Add("SIGNATURE", SqlDbType.NVarChar).Value = pReceiver.Signature
|
||||
oCommand.Parameters.Add("SIGNATURE", SqlDbType.NVarChar).Value = pReceiver.GetSignature()
|
||||
|
||||
Dim oResult = Database.ExecuteNonQuery(oCommand)
|
||||
If oResult = True Then
|
||||
|
||||
@ -12,6 +12,31 @@ namespace EnvelopeGenerator.Web.Handler
|
||||
{
|
||||
public class FileHandler
|
||||
{
|
||||
public class PostResult
|
||||
{
|
||||
readonly bool Ok = true;
|
||||
readonly string ErrorMessage = "";
|
||||
readonly ErrorType ErrorType = ErrorType.None;
|
||||
|
||||
public PostResult()
|
||||
{
|
||||
Ok = true;
|
||||
}
|
||||
|
||||
public PostResult(ErrorType errorType, string errorMessage)
|
||||
{
|
||||
Ok = false;
|
||||
ErrorType = errorType;
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ErrorType
|
||||
{
|
||||
None,
|
||||
ServerError
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URL: GET /api/envelope/{envelopeKey}
|
||||
///
|
||||
@ -76,7 +101,10 @@ namespace EnvelopeGenerator.Web.Handler
|
||||
{
|
||||
// Better error handling & reporting
|
||||
logger.Error(e);
|
||||
return Results.Problem();
|
||||
return Results.Problem(
|
||||
statusCode: 500,
|
||||
detail: e.Message,
|
||||
type: ErrorType.ServerError.ToString());
|
||||
|
||||
}
|
||||
}
|
||||
@ -91,11 +119,11 @@ namespace EnvelopeGenerator.Web.Handler
|
||||
|
||||
// Load Envelope from EnvelopeKey
|
||||
string envelopeKey = EnsureValidEnvelopeKey(logger, ctx.Request);
|
||||
EnvelopeResponse r = database.LoadEnvelope(envelopeKey);
|
||||
EnvelopeResponse response = database.LoadEnvelope(envelopeKey);
|
||||
|
||||
// Get the document Index
|
||||
int documentId = EnsureValidDocumentIndex(logger, ctx.Request);
|
||||
var document = GetDocument(r.Envelope, documentId);
|
||||
var document = GetDocument(response.Envelope, documentId);
|
||||
|
||||
using FileStream fs = new(document.Filepath, FileMode.Open);
|
||||
await ctx.Request.Body.CopyToAsync(fs);
|
||||
@ -107,7 +135,10 @@ namespace EnvelopeGenerator.Web.Handler
|
||||
{
|
||||
// Better error handling & reporting
|
||||
logger.Error(e);
|
||||
return Results.Problem();
|
||||
return Results.Problem(
|
||||
statusCode: 500,
|
||||
detail: e.Message,
|
||||
type: ErrorType.ServerError.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,10 +182,17 @@ namespace EnvelopeGenerator.Web.Handler
|
||||
{
|
||||
// Better error handling & reporting
|
||||
logger.Error(e);
|
||||
return Results.Problem();
|
||||
return Results.Problem(
|
||||
statusCode: 500,
|
||||
detail: e.Message,
|
||||
type: ErrorType.ServerError.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region Private
|
||||
|
||||
|
||||
private State GetState(LogConfig LogConfig, MSSQLServer Database)
|
||||
{
|
||||
return new State
|
||||
@ -235,5 +273,8 @@ namespace EnvelopeGenerator.Web.Handler
|
||||
|
||||
return document;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -8,17 +8,31 @@
|
||||
<ul>
|
||||
@foreach (var envelope in envelopes)
|
||||
{
|
||||
<li><a href="/EnvelopeKey/@Helpers.EncodeEnvelopeReceiverId(envelope.Uuid, "DDA3F6FA6605965DAC6FCF5AC518C78CDA11086584FED872BE146E987976F829")">Envelope @envelope.Id</a></li>
|
||||
<li><a href="/EnvelopeKey/@getEnvelopeKey(envelope)">@envelope.Title</a></li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
@code {
|
||||
public List<Envelope> envelopes = new();
|
||||
|
||||
// List envelopes delivered to j.jenne@digitaldata.works
|
||||
public int receiverId = 11;
|
||||
|
||||
string? getReceiverSignature(Envelope envelope)
|
||||
{
|
||||
var receiver = envelope.Receivers.Where(r => r.Id == receiverId).SingleOrDefault();
|
||||
return receiver?.Signature;
|
||||
}
|
||||
|
||||
string getEnvelopeKey(Envelope envelope)
|
||||
{
|
||||
return Helpers.EncodeEnvelopeReceiverId(envelope.Uuid, getReceiverSignature(envelope));
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
// Test
|
||||
envelopes = Database.LoadEnvelopes(11);
|
||||
envelopes = Database.LoadEnvelopes(receiverId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -59,42 +59,82 @@ export class App {
|
||||
}
|
||||
|
||||
public static async handleClick(eventType: string) {
|
||||
let result = false;
|
||||
|
||||
switch (eventType) {
|
||||
case "RESET":
|
||||
await App.handleReset(null)
|
||||
result = await App.handleReset(null)
|
||||
|
||||
if (result == true) {
|
||||
alert("Dokument zurückgesetzt!");
|
||||
} else {
|
||||
alert("Fehler beim Zurücksetzen des Dokuments!")
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "FINISH":
|
||||
await App.handleFinish(null)
|
||||
result = await App.handleFinish(null)
|
||||
|
||||
if (result == true) {
|
||||
// TODO: Redirect to success page
|
||||
alert("Dokument erfolgreich signiert!")
|
||||
} else {
|
||||
alert("Fehler beim Abschließen des Dokuments!")
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static async handleFinish(event: any) {
|
||||
public static async handleFinish(event: any): Promise<boolean> {
|
||||
|
||||
// Save changes before doing anything
|
||||
try {
|
||||
await App.Instance.save();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Export annotation data and save to database
|
||||
try {
|
||||
const json = await App.Instance.exportInstantJSON()
|
||||
console.log(json);
|
||||
console.log(JSON.stringify(json));
|
||||
const result: boolean = await App.Network.postEnvelope(App.envelopeKey, App.currentDocument.id, JSON.stringify(json))
|
||||
const postEnvelopeResult: boolean = await App.Network.postEnvelope(App.envelopeKey, App.currentDocument.id, JSON.stringify(json))
|
||||
|
||||
if (result == true) {
|
||||
alert("Dokument erfolgreich signiert!")
|
||||
if (postEnvelopeResult === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Flatten the annotations and save the document to disk
|
||||
/*
|
||||
try {
|
||||
const buffer = await App.Instance.exportPDF({ flatten: true });
|
||||
const result = await App.Network.postDocument(App.envelopeKey, App.currentDocument.id, buffer);
|
||||
console.log(result)
|
||||
*/
|
||||
const postDocumentResult: boolean = await App.Network.postDocument(App.envelopeKey, App.currentDocument.id, buffer);
|
||||
|
||||
if (postDocumentResult === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static async handleReset(event: any) {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public static async handleReset(event: any): Promise<boolean> {
|
||||
if (confirm("Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?")) {
|
||||
const result = App.Annotation.deleteAnnotations(App.Instance)
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,18 +242,34 @@ class Network {
|
||||
}
|
||||
|
||||
public postDocument(envelopeKey: string, documentId: number, buffer: ArrayBuffer): Promise<any> {
|
||||
return fetch(`/api/document/${envelopeKey}/${documentId}`, { credentials: "include", method: "POST", body: buffer })
|
||||
.then(res => res.json());
|
||||
const url = `/api/document/${envelopeKey}/${documentId}`;
|
||||
const options: RequestInit = {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
body: buffer
|
||||
}
|
||||
|
||||
console.debug("PostDocument/Calling url: " + url)
|
||||
return fetch(url, options)
|
||||
.then(this.handleResponse)
|
||||
.then((res: Response) => {
|
||||
if (!res.ok) {
|
||||
return false;
|
||||
};
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public postEnvelope(envelopeKey: string, documentId: number, jsonString: string): Promise<boolean> {
|
||||
const url = `/api/envelope/${envelopeKey}?index=${documentId}`;
|
||||
const options: RequestInit = {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
body: jsonString
|
||||
}
|
||||
|
||||
return fetch(`/api/envelope/${envelopeKey}?index=${documentId}`, options)
|
||||
console.debug("PostEnvelope/Calling url: " + url)
|
||||
return fetch(url, options)
|
||||
.then(this.handleResponse)
|
||||
.then((res: Response) => {
|
||||
if (!res.ok) {
|
||||
@ -297,6 +353,7 @@ class UI {
|
||||
{
|
||||
type: "custom",
|
||||
id: "button-reset",
|
||||
className: "button-reset",
|
||||
title: "Zurücksetzen",
|
||||
onPress() {
|
||||
callback("RESET")
|
||||
@ -309,6 +366,7 @@ class UI {
|
||||
{
|
||||
type: "custom",
|
||||
id: "button-finish",
|
||||
className: "button-finish",
|
||||
title: "Abschließen",
|
||||
onPress() {
|
||||
callback("FINISH")
|
||||
|
||||
382
EnvelopeGenerator.Web/Scripts/index.d.ts
vendored
382
EnvelopeGenerator.Web/Scripts/index.d.ts
vendored
@ -5103,6 +5103,7 @@ interface IImageAnnotation extends AnnotationProperties {
|
||||
imageAttachmentId: string | null;
|
||||
isSignature: boolean;
|
||||
xfdfAppearanceStream: string | null;
|
||||
xfdfAppearanceStreamOriginalPageRotation: number | null;
|
||||
}
|
||||
declare class ImageAnnotation<T extends IImageAnnotation = IImageAnnotation> extends Annotation<T> {
|
||||
description: null | string;
|
||||
@ -5111,6 +5112,7 @@ declare class ImageAnnotation<T extends IImageAnnotation = IImageAnnotation> ext
|
||||
imageAttachmentId: string;
|
||||
isSignature: boolean;
|
||||
xfdfAppearanceStream: null | string;
|
||||
xfdfAppearanceStreamOriginalPageRotation: null | number;
|
||||
static defaultValues: IObject;
|
||||
static readableName: string;
|
||||
}
|
||||
@ -6067,197 +6069,6 @@ type SerializedAdditionalActionsType = {
|
||||
};
|
||||
};
|
||||
|
||||
type Glyph = {
|
||||
c: string;
|
||||
rect: Rect;
|
||||
};
|
||||
|
||||
declare const SearchType: {
|
||||
readonly TEXT: "text";
|
||||
readonly PRESET: "preset";
|
||||
readonly REGEX: "regex";
|
||||
};
|
||||
type ISearchType = (typeof SearchType)[keyof typeof SearchType];
|
||||
|
||||
declare function toJSON(bookmark: Bookmark): BookmarkJSON;
|
||||
|
||||
type ID$1 = string;
|
||||
type BookmarkProps = {
|
||||
id: ID$1 | null;
|
||||
pdfBookmarkId: ID$1 | null;
|
||||
name: string | null;
|
||||
sortKey: number | null;
|
||||
action: Action | null;
|
||||
};
|
||||
declare const Bookmark_base: Record$1.Factory<BookmarkProps>;
|
||||
declare class Bookmark extends Bookmark_base {
|
||||
id: ID$1;
|
||||
action: Action;
|
||||
static toSerializableObject: typeof toJSON;
|
||||
static fromSerializableObject: (bookmark: BookmarkJSON) => Bookmark;
|
||||
}
|
||||
|
||||
type Rotation$1 = 0 | 90 | 180 | 270;
|
||||
type AddPageConfiguration = {
|
||||
backgroundColor: Color;
|
||||
pageWidth: number;
|
||||
pageHeight: number;
|
||||
rotateBy: Rotation$1;
|
||||
insets?: Rect;
|
||||
};
|
||||
type OperationAttachment = string | File | Blob;
|
||||
type min = number;
|
||||
type max = number;
|
||||
type Range = [min, max];
|
||||
type ImportPageIndex = Array<number | Range>;
|
||||
type DocumentMetadata = {
|
||||
title?: string;
|
||||
author?: string;
|
||||
};
|
||||
type NonSerializableDocumentOperations = {
|
||||
type: 'removePages';
|
||||
pageIndexes: Array<number>;
|
||||
} | {
|
||||
type: 'duplicatePages';
|
||||
pageIndexes: Array<number>;
|
||||
} | {
|
||||
type: 'movePages';
|
||||
pageIndexes: Array<number>;
|
||||
afterPageIndex: number;
|
||||
} | {
|
||||
type: 'movePages';
|
||||
pageIndexes: Array<number>;
|
||||
beforePageIndex: number;
|
||||
} | {
|
||||
type: 'rotatePages';
|
||||
pageIndexes: Array<number>;
|
||||
rotateBy: Rotation$1;
|
||||
} | {
|
||||
type: 'keepPages';
|
||||
pageIndexes: Array<number>;
|
||||
} | {
|
||||
type: 'importDocument';
|
||||
afterPageIndex: number;
|
||||
treatImportedDocumentAsOnePage?: boolean;
|
||||
document: OperationAttachment;
|
||||
importedPageIndexes?: ImportPageIndex;
|
||||
} | {
|
||||
type: 'importDocument';
|
||||
beforePageIndex: number;
|
||||
treatImportedDocumentAsOnePage?: boolean;
|
||||
document: OperationAttachment;
|
||||
importedPageIndexes?: ImportPageIndex;
|
||||
} | {
|
||||
type: 'applyInstantJson';
|
||||
instantJson: Record<string, any>;
|
||||
dataFilePath: OperationAttachment;
|
||||
} | {
|
||||
type: 'applyXfdf';
|
||||
xfdf: string;
|
||||
ignorePageRotation?: boolean;
|
||||
dataFilePath: OperationAttachment;
|
||||
} | {
|
||||
type: 'flattenAnnotations';
|
||||
pageIndexes?: Array<number>;
|
||||
annotationIds?: string[];
|
||||
} | {
|
||||
type: 'setPageLabel';
|
||||
pageIndexes?: Array<number>;
|
||||
pageLabel?: string;
|
||||
} | {
|
||||
type: 'performOcr';
|
||||
pageIndexes?: Array<number> | 'all';
|
||||
language: string;
|
||||
} | {
|
||||
type: 'applyRedactions';
|
||||
} | {
|
||||
type: 'updateMetadata';
|
||||
metadata: DocumentMetadata;
|
||||
};
|
||||
type DocumentOperation = (AddPageConfiguration & {
|
||||
type: 'addPage';
|
||||
afterPageIndex: number;
|
||||
}) | (AddPageConfiguration & {
|
||||
type: 'addPage';
|
||||
beforePageIndex: number;
|
||||
}) | {
|
||||
type: 'cropPages';
|
||||
pageIndexes?: Array<number>;
|
||||
cropBox: Rect;
|
||||
} | NonSerializableDocumentOperations;
|
||||
|
||||
type BaseFormFieldJSON = {
|
||||
v: 1;
|
||||
pdfObjectId?: number | null;
|
||||
annotationIds: Array<string>;
|
||||
name: string;
|
||||
label: string;
|
||||
flags?: FormFieldFlags;
|
||||
id: string;
|
||||
additionalActions?: SerializedAdditionalActionsType;
|
||||
group?: IGroup;
|
||||
permissions?: IPermissions;
|
||||
};
|
||||
type ChoiceFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/listbox' | 'pspdfkit/form-field/combobox';
|
||||
options: Array<FormOptionJSON>;
|
||||
multiSelect: boolean;
|
||||
commitOnChange: boolean;
|
||||
defaultValues: Array<string>;
|
||||
};
|
||||
type ListBoxFormFieldJSON = ChoiceFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/listbox';
|
||||
};
|
||||
type DoNotSpellCheckPropertyPair = XOR<Record<'doNotSpellCheck', boolean>, Record<'doNotSpellcheck', boolean>>;
|
||||
type ComboBoxFormFieldJSON = ChoiceFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/combobox';
|
||||
edit: boolean;
|
||||
} & DoNotSpellCheckPropertyPair;
|
||||
type CheckBoxFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/checkbox';
|
||||
options: Array<FormOptionJSON>;
|
||||
defaultValues: Array<string>;
|
||||
};
|
||||
type RadioButtonFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/radio';
|
||||
options: Array<FormOptionJSON>;
|
||||
noToggleToOff: boolean;
|
||||
radiosInUnison: boolean;
|
||||
defaultValue: string;
|
||||
};
|
||||
type TextFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/text';
|
||||
password: boolean;
|
||||
maxLength?: number | null;
|
||||
doNotScroll: boolean;
|
||||
multiLine: boolean;
|
||||
defaultValue: string;
|
||||
comb: boolean;
|
||||
} & DoNotSpellCheckPropertyPair;
|
||||
type ButtonFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/button';
|
||||
buttonLabel: string | null;
|
||||
};
|
||||
type SignatureFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/signature';
|
||||
};
|
||||
type FormFieldJSON = ListBoxFormFieldJSON | ComboBoxFormFieldJSON | RadioButtonFormFieldJSON | CheckBoxFormFieldJSON | TextFormFieldJSON | ButtonFormFieldJSON | SignatureFormFieldJSON;
|
||||
|
||||
type OCGLayer = {
|
||||
name: string;
|
||||
ocgId: number;
|
||||
radioGroup?: number;
|
||||
};
|
||||
type OCGCollection = {
|
||||
name?: string;
|
||||
ocgId?: number;
|
||||
layers: OCGLayer[];
|
||||
};
|
||||
type OCG = OCGLayer | OCGCollection;
|
||||
type OCGVisibilityState = {
|
||||
visibleOCGIds: number[];
|
||||
};
|
||||
|
||||
type IRectJSON = [left: number, top: number, width: number, height: number];
|
||||
|
||||
type BaseAnnotationJSON = {
|
||||
@ -6294,6 +6105,7 @@ type ImageAnnotationJSON = Omit<BaseAnnotationJSON, 'type'> & {
|
||||
rotation: number;
|
||||
isSignature?: boolean;
|
||||
xfdfAppearanceStream?: string;
|
||||
xfdfAppearanceStreamOriginalPageRotation?: number;
|
||||
};
|
||||
type ShapeAnnotationJSON = Omit<BaseAnnotationJSON, 'type'> & {
|
||||
strokeWidth: number;
|
||||
@ -6403,6 +6215,7 @@ type StampAnnotationJSON = Omit<BaseAnnotationJSON, 'type'> & {
|
||||
subtitle: string | null;
|
||||
rotation: number | null;
|
||||
xfdfAppearanceStream?: string;
|
||||
xfdfAppearanceStreamOriginalPageRotation?: number;
|
||||
kind?: StampKind;
|
||||
};
|
||||
type TextAnnotationJSON = Omit<BaseAnnotationJSON, 'type'> & {
|
||||
@ -6458,6 +6271,63 @@ type CommentMarkerAnnotationJSON = Omit<BaseAnnotationJSON, 'type'> & {
|
||||
};
|
||||
type AnnotationJSONUnion = TextMarkupAnnotationJSON | TextAnnotationJSON | WidgetAnnotationJSON | RedactionAnnotationJSON | StampAnnotationJSON | NoteAnnotationJSON | LinkAnnotationJSON | InkAnnotationJSON | RectangleAnnotationJSON | PolylineAnnotationJSON | PolygonAnnotationJSON | LineAnnotationJSON | EllipseAnnotationJSON | ImageAnnotationJSON | UnknownAnnotationJSON | MediaAnnotationJSON | CommentMarkerAnnotationJSON;
|
||||
|
||||
type BaseFormFieldJSON = {
|
||||
v: 1;
|
||||
pdfObjectId?: number | null;
|
||||
annotationIds: Array<string>;
|
||||
name: string;
|
||||
label: string;
|
||||
flags?: FormFieldFlags;
|
||||
id: string;
|
||||
additionalActions?: SerializedAdditionalActionsType;
|
||||
group?: IGroup;
|
||||
permissions?: IPermissions;
|
||||
};
|
||||
type ChoiceFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/listbox' | 'pspdfkit/form-field/combobox';
|
||||
options: Array<FormOptionJSON>;
|
||||
multiSelect: boolean;
|
||||
commitOnChange: boolean;
|
||||
defaultValues: Array<string>;
|
||||
};
|
||||
type ListBoxFormFieldJSON = ChoiceFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/listbox';
|
||||
};
|
||||
type DoNotSpellCheckPropertyPair = XOR<Record<'doNotSpellCheck', boolean>, Record<'doNotSpellcheck', boolean>>;
|
||||
type ComboBoxFormFieldJSON = ChoiceFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/combobox';
|
||||
edit: boolean;
|
||||
} & DoNotSpellCheckPropertyPair;
|
||||
type CheckBoxFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/checkbox';
|
||||
options: Array<FormOptionJSON>;
|
||||
defaultValues: Array<string>;
|
||||
};
|
||||
type RadioButtonFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/radio';
|
||||
options: Array<FormOptionJSON>;
|
||||
noToggleToOff: boolean;
|
||||
radiosInUnison: boolean;
|
||||
defaultValue: string;
|
||||
};
|
||||
type TextFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/text';
|
||||
password: boolean;
|
||||
maxLength?: number | null;
|
||||
doNotScroll: boolean;
|
||||
multiLine: boolean;
|
||||
defaultValue: string;
|
||||
comb: boolean;
|
||||
} & DoNotSpellCheckPropertyPair;
|
||||
type ButtonFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/button';
|
||||
buttonLabel: string | null;
|
||||
};
|
||||
type SignatureFormFieldJSON = BaseFormFieldJSON & {
|
||||
type: 'pspdfkit/form-field/signature';
|
||||
};
|
||||
type FormFieldJSON = ListBoxFormFieldJSON | ComboBoxFormFieldJSON | RadioButtonFormFieldJSON | CheckBoxFormFieldJSON | TextFormFieldJSON | ButtonFormFieldJSON | SignatureFormFieldJSON;
|
||||
|
||||
type SerializedJSON = {
|
||||
skippedPdfObjectIds?: number[];
|
||||
annotations?: AnnotationJSONUnion[];
|
||||
@ -6479,6 +6349,113 @@ type InstantJSON = SerializedJSON & {
|
||||
};
|
||||
};
|
||||
|
||||
type Rotation$1 = 0 | 90 | 180 | 270;
|
||||
type AddPageConfiguration = {
|
||||
backgroundColor: Color;
|
||||
pageWidth: number;
|
||||
pageHeight: number;
|
||||
rotateBy: Rotation$1;
|
||||
insets?: Rect;
|
||||
};
|
||||
type OperationAttachment = string | File | Blob;
|
||||
type min = number;
|
||||
type max = number;
|
||||
type Range = [min, max];
|
||||
type ImportPageIndex = Array<number | Range>;
|
||||
type DocumentMetadata = {
|
||||
title?: string;
|
||||
author?: string;
|
||||
};
|
||||
type NonSerializableDocumentOperations = {
|
||||
type: 'removePages';
|
||||
pageIndexes: Array<number>;
|
||||
} | {
|
||||
type: 'duplicatePages';
|
||||
pageIndexes: Array<number>;
|
||||
} | {
|
||||
type: 'movePages';
|
||||
pageIndexes: Array<number>;
|
||||
afterPageIndex: number;
|
||||
} | {
|
||||
type: 'movePages';
|
||||
pageIndexes: Array<number>;
|
||||
beforePageIndex: number;
|
||||
} | {
|
||||
type: 'rotatePages';
|
||||
pageIndexes: Array<number>;
|
||||
rotateBy: Rotation$1;
|
||||
} | {
|
||||
type: 'keepPages';
|
||||
pageIndexes: Array<number>;
|
||||
} | {
|
||||
type: 'importDocument';
|
||||
afterPageIndex: number;
|
||||
treatImportedDocumentAsOnePage?: boolean;
|
||||
document: OperationAttachment;
|
||||
importedPageIndexes?: ImportPageIndex;
|
||||
} | {
|
||||
type: 'importDocument';
|
||||
beforePageIndex: number;
|
||||
treatImportedDocumentAsOnePage?: boolean;
|
||||
document: OperationAttachment;
|
||||
importedPageIndexes?: ImportPageIndex;
|
||||
} | {
|
||||
type: 'applyInstantJson';
|
||||
instantJson: Record<string, any>;
|
||||
dataFilePath: OperationAttachment;
|
||||
} | {
|
||||
type: 'applyXfdf';
|
||||
xfdf: string;
|
||||
ignorePageRotation?: boolean;
|
||||
dataFilePath: OperationAttachment;
|
||||
} | {
|
||||
type: 'flattenAnnotations';
|
||||
pageIndexes?: Array<number>;
|
||||
annotationIds?: string[];
|
||||
} | {
|
||||
type: 'setPageLabel';
|
||||
pageIndexes?: Array<number>;
|
||||
pageLabel?: string;
|
||||
} | {
|
||||
type: 'performOcr';
|
||||
pageIndexes?: Array<number> | 'all';
|
||||
language: string;
|
||||
} | {
|
||||
type: 'applyRedactions';
|
||||
} | {
|
||||
type: 'updateMetadata';
|
||||
metadata: DocumentMetadata;
|
||||
};
|
||||
type DocumentOperation = (AddPageConfiguration & {
|
||||
type: 'addPage';
|
||||
afterPageIndex: number;
|
||||
}) | (AddPageConfiguration & {
|
||||
type: 'addPage';
|
||||
beforePageIndex: number;
|
||||
}) | {
|
||||
type: 'cropPages';
|
||||
pageIndexes?: Array<number>;
|
||||
cropBox: Rect;
|
||||
} | NonSerializableDocumentOperations;
|
||||
|
||||
declare function toJSON(bookmark: Bookmark): BookmarkJSON;
|
||||
|
||||
type ID$1 = string;
|
||||
type BookmarkProps = {
|
||||
id: ID$1 | null;
|
||||
pdfBookmarkId: ID$1 | null;
|
||||
name: string | null;
|
||||
sortKey: number | null;
|
||||
action: Action | null;
|
||||
};
|
||||
declare const Bookmark_base: Record$1.Factory<BookmarkProps>;
|
||||
declare class Bookmark extends Bookmark_base {
|
||||
id: ID$1;
|
||||
action: Action;
|
||||
static toSerializableObject: typeof toJSON;
|
||||
static fromSerializableObject: (bookmark: BookmarkJSON) => Bookmark;
|
||||
}
|
||||
|
||||
declare const SearchPattern: {
|
||||
readonly CREDIT_CARD_NUMBER: "credit_card_number";
|
||||
readonly DATE: "date";
|
||||
@ -6496,6 +6473,13 @@ declare const SearchPattern: {
|
||||
};
|
||||
type ISearchPattern = (typeof SearchPattern)[keyof typeof SearchPattern];
|
||||
|
||||
declare const SearchType: {
|
||||
readonly TEXT: "text";
|
||||
readonly PRESET: "preset";
|
||||
readonly REGEX: "regex";
|
||||
};
|
||||
type ISearchType = (typeof SearchType)[keyof typeof SearchType];
|
||||
|
||||
declare const ProductId: {
|
||||
SharePoint: string;
|
||||
Salesforce: string;
|
||||
@ -6546,6 +6530,11 @@ type SignatureAppearance = {
|
||||
watermarkImage?: Blob | File;
|
||||
};
|
||||
|
||||
type Glyph = {
|
||||
c: string;
|
||||
rect: Rect;
|
||||
};
|
||||
|
||||
declare const TextLineElementKind: {
|
||||
P: string;
|
||||
TH: string;
|
||||
@ -7114,7 +7103,7 @@ declare class InstantClient {
|
||||
userId: string | null | undefined;
|
||||
}
|
||||
|
||||
declare const allowedToolbarTypes: ("distance" | "note" | "comment" | "text" | "zoom-in" | "zoom-out" | "link" | "search" | "ellipse" | "image" | "line" | "polygon" | "polyline" | "spacer" | "arrow" | "highlighter" | "undo" | "redo" | "callout" | "debug" | "signature" | "custom" | "print" | "rectangle" | "ink" | "stamp" | "cloudy-rectangle" | "dashed-rectangle" | "cloudy-ellipse" | "dashed-ellipse" | "cloudy-polygon" | "dashed-polygon" | "text-highlighter" | "perimeter" | "ellipse-area" | "rectangle-area" | "polygon-area" | "sidebar-thumbnails" | "sidebar-document-outline" | "sidebar-annotations" | "sidebar-bookmarks" | "pager" | "multi-annotations-selection" | "pan" | "zoom-mode" | "annotate" | "ink-eraser" | "document-editor" | "document-crop" | "export-pdf" | "layout-config" | "marquee-zoom" | "responsive-group" | "redact-text-highlighter" | "redact-rectangle" | "document-comparison" | "measure" | "form-creator" | "content-editor")[];
|
||||
declare const allowedToolbarTypes: ("distance" | "note" | "comment" | "text" | "zoom-in" | "zoom-out" | "link" | "search" | "ellipse" | "image" | "line" | "polygon" | "polyline" | "spacer" | "arrow" | "highlighter" | "undo" | "redo" | "callout" | "custom" | "print" | "rectangle" | "ink" | "stamp" | "cloudy-rectangle" | "dashed-rectangle" | "cloudy-ellipse" | "dashed-ellipse" | "cloudy-polygon" | "dashed-polygon" | "text-highlighter" | "perimeter" | "ellipse-area" | "rectangle-area" | "polygon-area" | "sidebar-thumbnails" | "sidebar-document-outline" | "sidebar-annotations" | "sidebar-bookmarks" | "pager" | "multi-annotations-selection" | "pan" | "zoom-mode" | "annotate" | "ink-eraser" | "signature" | "document-editor" | "document-crop" | "export-pdf" | "debug" | "layout-config" | "marquee-zoom" | "responsive-group" | "redact-text-highlighter" | "redact-rectangle" | "document-comparison" | "measure" | "form-creator" | "content-editor")[];
|
||||
|
||||
type ToolbarItemType = ToolItemType | (typeof allowedToolbarTypes)[number];
|
||||
type ToolbarItem = Omit<ToolItem, 'type'> & {
|
||||
@ -7494,7 +7483,6 @@ declare class Instance {
|
||||
textLinesForPageIndex: (pageIndex: number) => Promise<List<TextLine>>;
|
||||
getMarkupAnnotationText: (annotation: TextMarkupAnnotationsUnion) => Promise<string>;
|
||||
getTextFromRects: (pageIndex: number, rects: List<Rect>) => Promise<string>;
|
||||
getDocumentPermissions: () => Promise<Record<IDocumentPermissions, boolean>>;
|
||||
currentZoomLevel: number;
|
||||
maximumZoomLevel: number;
|
||||
minimumZoomLevel: number;
|
||||
@ -7535,9 +7523,6 @@ declare class Instance {
|
||||
setAnnotationCreatorName: (annotationCreatorName?: string | null) => void;
|
||||
setOnWidgetAnnotationCreationStart: (callback: OnWidgetAnnotationCreationStartCallback) => void;
|
||||
setOnCommentCreationStart: (callback: OnCommentCreationStartCallback) => void;
|
||||
getOCGs: () => Promise<OCG[]>;
|
||||
getOCGVisibilityState: () => Promise<OCGVisibilityState>;
|
||||
setOCGVisibilityState: (visibilityState: OCGVisibilityState) => Promise<void>;
|
||||
contentWindow: Window;
|
||||
contentDocument: Document;
|
||||
readonly viewState: ViewState;
|
||||
@ -7603,7 +7588,7 @@ declare class Instance {
|
||||
setDocumentEditorFooterItems: (stateOrFunction: DocumentEditorFooterItem[] | SetDocumentEditorFooterFunction) => void;
|
||||
setDocumentEditorToolbarItems: (stateOrFunction: DocumentEditorToolbarItem[] | SetDocumentEditorToolbarFunction) => void;
|
||||
getSignaturesInfo: () => Promise<SignaturesInfo>;
|
||||
signDocument: (arg0: SignatureCreationData | null, arg1?: TwoStepSignatureCallback | SigningServiceData) => Promise<void>;
|
||||
signDocument: (arg0: SignatureCreationData | null | undefined, arg1: TwoStepSignatureCallback | SigningServiceData | undefined) => Promise<void>;
|
||||
applyOperations: (operations: Array<DocumentOperation>) => Promise<void>;
|
||||
exportPDFWithOperations: (arg0: Array<DocumentOperation>) => Promise<ArrayBuffer>;
|
||||
applyRedactions: () => Promise<void>;
|
||||
@ -7787,7 +7772,6 @@ type StandaloneConfiguration = SharedConfiguration & {
|
||||
isSalesforce?: boolean;
|
||||
productId?: IProductId;
|
||||
processorEngine?: IProcessorEngine;
|
||||
dynamicFonts?: string;
|
||||
};
|
||||
type Configuration = ServerConfiguration | StandaloneConfiguration;
|
||||
|
||||
@ -8079,6 +8063,7 @@ interface IStampAnnotation extends AnnotationProperties {
|
||||
subtitle: string | null;
|
||||
color: Color | null;
|
||||
xfdfAppearanceStream: string | null;
|
||||
xfdfAppearanceStreamOriginalPageRotation: number | null;
|
||||
}
|
||||
declare class StampAnnotation<T extends IStampAnnotation = IStampAnnotation> extends Annotation<T> {
|
||||
stampType: StampKind;
|
||||
@ -8086,6 +8071,7 @@ declare class StampAnnotation<T extends IStampAnnotation = IStampAnnotation> ext
|
||||
subtitle: null | string;
|
||||
color: null | Color;
|
||||
xfdfAppearanceStream: null | string;
|
||||
xfdfAppearanceStreamOriginalPageRotation: null | number;
|
||||
static defaultValues: IObject;
|
||||
static readableName: string;
|
||||
}
|
||||
|
||||
@ -97,10 +97,11 @@ var App = /** @class */ (function () {
|
||||
};
|
||||
App.handleClick = function (eventType) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var _a;
|
||||
var result, _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
result = false;
|
||||
_a = eventType;
|
||||
switch (_a) {
|
||||
case "RESET": return [3 /*break*/, 1];
|
||||
@ -109,11 +110,24 @@ var App = /** @class */ (function () {
|
||||
return [3 /*break*/, 5];
|
||||
case 1: return [4 /*yield*/, App.handleReset(null)];
|
||||
case 2:
|
||||
_b.sent();
|
||||
result = _b.sent();
|
||||
if (result == true) {
|
||||
alert("Dokument zurückgesetzt!");
|
||||
}
|
||||
else {
|
||||
alert("Fehler beim Zurücksetzen des Dokuments!");
|
||||
}
|
||||
return [3 /*break*/, 5];
|
||||
case 3: return [4 /*yield*/, App.handleFinish(null)];
|
||||
case 4:
|
||||
_b.sent();
|
||||
result = _b.sent();
|
||||
if (result == true) {
|
||||
// TODO: Redirect to success page
|
||||
alert("Dokument erfolgreich signiert!");
|
||||
}
|
||||
else {
|
||||
alert("Fehler beim Abschließen des Dokuments!");
|
||||
}
|
||||
return [3 /*break*/, 5];
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
@ -122,24 +136,52 @@ var App = /** @class */ (function () {
|
||||
};
|
||||
App.handleFinish = function (event) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var json, result;
|
||||
var e_2, json, postEnvelopeResult, e_3, buffer, postDocumentResult, e_4;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, App.Instance.save()];
|
||||
case 0:
|
||||
_a.trys.push([0, 2, , 3]);
|
||||
return [4 /*yield*/, App.Instance.save()];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, App.Instance.exportInstantJSON()];
|
||||
return [3 /*break*/, 3];
|
||||
case 2:
|
||||
json = _a.sent();
|
||||
console.log(json);
|
||||
console.log(JSON.stringify(json));
|
||||
return [4 /*yield*/, App.Network.postEnvelope(App.envelopeKey, App.currentDocument.id, JSON.stringify(json))];
|
||||
e_2 = _a.sent();
|
||||
console.error(e_2);
|
||||
return [2 /*return*/, false];
|
||||
case 3:
|
||||
result = _a.sent();
|
||||
if (result == true) {
|
||||
alert("Dokument erfolgreich signiert!");
|
||||
_a.trys.push([3, 6, , 7]);
|
||||
return [4 /*yield*/, App.Instance.exportInstantJSON()];
|
||||
case 4:
|
||||
json = _a.sent();
|
||||
return [4 /*yield*/, App.Network.postEnvelope(App.envelopeKey, App.currentDocument.id, JSON.stringify(json))];
|
||||
case 5:
|
||||
postEnvelopeResult = _a.sent();
|
||||
if (postEnvelopeResult === false) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/];
|
||||
return [3 /*break*/, 7];
|
||||
case 6:
|
||||
e_3 = _a.sent();
|
||||
console.error(e_3);
|
||||
return [2 /*return*/, false];
|
||||
case 7:
|
||||
_a.trys.push([7, 10, , 11]);
|
||||
return [4 /*yield*/, App.Instance.exportPDF({ flatten: true })];
|
||||
case 8:
|
||||
buffer = _a.sent();
|
||||
return [4 /*yield*/, App.Network.postDocument(App.envelopeKey, App.currentDocument.id, buffer)];
|
||||
case 9:
|
||||
postDocumentResult = _a.sent();
|
||||
if (postDocumentResult === false) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [3 /*break*/, 11];
|
||||
case 10:
|
||||
e_4 = _a.sent();
|
||||
console.error(e_4);
|
||||
return [2 /*return*/, false];
|
||||
case 11: return [2 /*return*/, true];
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -148,8 +190,12 @@ var App = /** @class */ (function () {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var result;
|
||||
return __generator(this, function (_a) {
|
||||
if (confirm("Wollen Sie das Dokument und alle erstellten Signaturen zur<EFBFBD>cksetzen?")) {
|
||||
if (confirm("Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?")) {
|
||||
result = App.Annotation.deleteAnnotations(App.Instance);
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
else {
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
return [2 /*return*/];
|
||||
});
|
||||
@ -272,16 +318,32 @@ var Network = /** @class */ (function () {
|
||||
.then(function (res) { return res.arrayBuffer(); });
|
||||
};
|
||||
Network.prototype.postDocument = function (envelopeKey, documentId, buffer) {
|
||||
return fetch("/api/document/".concat(envelopeKey, "/").concat(documentId), { credentials: "include", method: "POST", body: buffer })
|
||||
.then(function (res) { return res.json(); });
|
||||
var url = "/api/document/".concat(envelopeKey, "/").concat(documentId);
|
||||
var options = {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
body: buffer
|
||||
};
|
||||
console.debug("PostDocument/Calling url: " + url);
|
||||
return fetch(url, options)
|
||||
.then(this.handleResponse)
|
||||
.then(function (res) {
|
||||
if (!res.ok) {
|
||||
return false;
|
||||
}
|
||||
;
|
||||
return true;
|
||||
});
|
||||
};
|
||||
Network.prototype.postEnvelope = function (envelopeKey, documentId, jsonString) {
|
||||
var url = "/api/envelope/".concat(envelopeKey, "?index=").concat(documentId);
|
||||
var options = {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
body: jsonString
|
||||
};
|
||||
return fetch("/api/envelope/".concat(envelopeKey, "?index=").concat(documentId), options)
|
||||
console.debug("PostEnvelope/Calling url: " + url);
|
||||
return fetch(url, options)
|
||||
.then(this.handleResponse)
|
||||
.then(function (res) {
|
||||
if (!res.ok) {
|
||||
@ -321,7 +383,8 @@ var UI = /** @class */ (function () {
|
||||
{
|
||||
type: "custom",
|
||||
id: "button-reset",
|
||||
title: "Zur<75>cksetzen",
|
||||
className: "button-reset",
|
||||
title: "Zurücksetzen",
|
||||
onPress: function () {
|
||||
callback("RESET");
|
||||
},
|
||||
@ -330,7 +393,8 @@ var UI = /** @class */ (function () {
|
||||
{
|
||||
type: "custom",
|
||||
id: "button-finish",
|
||||
title: "Abschlie<69>en",
|
||||
className: "button-finish",
|
||||
title: "Abschließen",
|
||||
onPress: function () {
|
||||
callback("FINISH");
|
||||
},
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user