djgentelella 0.3.29__py3-none-any.whl → 0.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. djgentelella/__init__.py +4 -1
  2. djgentelella/admin.py +4 -3
  3. djgentelella/fields/files.py +27 -1
  4. djgentelella/firmador_digital/__init__.py +0 -0
  5. djgentelella/firmador_digital/config/__init__.py +0 -0
  6. djgentelella/firmador_digital/config/asgi_config.py +32 -0
  7. djgentelella/firmador_digital/config/asgi_worker.py +5 -0
  8. djgentelella/firmador_digital/config/websocket_urls.py +4 -0
  9. djgentelella/firmador_digital/consumers/__init__.py +0 -0
  10. djgentelella/firmador_digital/consumers/pdf_render.py +8 -0
  11. djgentelella/firmador_digital/consumers/sign.py +148 -0
  12. djgentelella/firmador_digital/forms.py +28 -0
  13. djgentelella/firmador_digital/gunicorn/config_asgi.py +12 -0
  14. djgentelella/firmador_digital/gunicorn/config_wsgi.py +13 -0
  15. djgentelella/firmador_digital/models.py +33 -0
  16. djgentelella/firmador_digital/signvalue_utils.py +41 -0
  17. djgentelella/firmador_digital/utils.py +154 -0
  18. djgentelella/firmador_digital/viewsets.py +77 -0
  19. djgentelella/forms/forms.py +10 -2
  20. djgentelella/locale/es/LC_MESSAGES/django.mo +0 -0
  21. djgentelella/locale/es/LC_MESSAGES/django.po +103 -6
  22. djgentelella/locale/es/LC_MESSAGES/djangojs.mo +0 -0
  23. djgentelella/locale/es/LC_MESSAGES/djangojs.po +107 -8
  24. djgentelella/management/commands/createbasejs.py +3 -0
  25. djgentelella/management/commands/loaddevstatic.py +18 -0
  26. djgentelella/migrations/0013_usersignatureconfig.py +25 -0
  27. djgentelella/serializers/firmador_digital.py +103 -0
  28. djgentelella/settings.py +4 -2
  29. djgentelella/static/djgentelella.readonly.vendors.min.css +1 -1
  30. djgentelella/static/djgentelella.readonly.vendors.min.js +1 -1
  31. djgentelella/static/djgentelella.vendors.header.min.js +1 -1
  32. djgentelella/static/djgentelella.vendors.min.css +2 -2
  33. djgentelella/static/gentelella/css/pdfviewer.css +147 -0
  34. djgentelella/static/gentelella/images/firmador.ico +0 -0
  35. djgentelella/static/gentelella/js/base/digital_signature.js +945 -0
  36. djgentelella/static/gentelella/js/base.js +947 -0
  37. djgentelella/static/gentelella/js/datatables.js +2 -2
  38. djgentelella/static/gentelella/js/widgets.js +72 -59
  39. djgentelella/static/vendors/friconix/friconix.js +1 -1
  40. djgentelella/static/vendors/pdfjs/images/altText_add.svg +3 -0
  41. djgentelella/static/vendors/pdfjs/images/altText_disclaimer.svg +3 -0
  42. djgentelella/static/vendors/pdfjs/images/altText_done.svg +3 -0
  43. djgentelella/static/vendors/pdfjs/images/altText_spinner.svg +30 -0
  44. djgentelella/static/vendors/pdfjs/images/altText_warning.svg +3 -0
  45. djgentelella/static/vendors/pdfjs/images/cursor-editorFreeHighlight.svg +6 -0
  46. djgentelella/static/vendors/pdfjs/images/cursor-editorFreeText.svg +3 -0
  47. djgentelella/static/vendors/pdfjs/images/cursor-editorInk.svg +4 -0
  48. djgentelella/static/vendors/pdfjs/images/cursor-editorTextHighlight.svg +8 -0
  49. djgentelella/static/vendors/pdfjs/images/editor-toolbar-delete.svg +5 -0
  50. djgentelella/static/vendors/pdfjs/images/loading-icon.gif +0 -0
  51. djgentelella/static/vendors/pdfjs/images/messageBar_closingButton.svg +3 -0
  52. djgentelella/static/vendors/pdfjs/images/messageBar_warning.svg +3 -0
  53. djgentelella/static/vendors/pdfjs/images/toolbarButton-editorHighlight.svg +6 -0
  54. djgentelella/static/vendors/pdfjs/images/toolbarButton-menuArrow.svg +3 -0
  55. djgentelella/static/vendors/timeline/css/timeline.css +1 -1
  56. djgentelella/static/vendors/timeline/js/timeline.js +1 -1
  57. djgentelella/templates/forms/as_grid.html +2 -2
  58. djgentelella/templates/forms/as_inline.html +3 -5
  59. djgentelella/templates/gentelella/registration/login.html +38 -42
  60. djgentelella/templates/gentelella/registration/reset_done.html +24 -0
  61. djgentelella/templates/gentelella/statics/javascript.html +75 -61
  62. djgentelella/templates/gentelella/statics/stylesheets.html +11 -10
  63. djgentelella/templates/gentelella/widgets/addtreeselect.html +1 -1
  64. djgentelella/templates/gentelella/widgets/chunkedupload.html +2 -2
  65. djgentelella/templates/gentelella/widgets/digital_signature.html +208 -0
  66. djgentelella/templates/gentelella/widgets/file.html +15 -16
  67. djgentelella/templatetags/gtsettings.py +4 -0
  68. djgentelella/urls.py +2 -1
  69. djgentelella/widgets/core.py +5 -2
  70. djgentelella/widgets/digital_signature.py +116 -0
  71. {djgentelella-0.3.29.dist-info → djgentelella-0.4.1.dist-info}/METADATA +18 -20
  72. {djgentelella-0.3.29.dist-info → djgentelella-0.4.1.dist-info}/RECORD +76 -38
  73. {djgentelella-0.3.29.dist-info → djgentelella-0.4.1.dist-info}/WHEEL +1 -1
  74. {djgentelella-0.3.29.dist-info → djgentelella-0.4.1.dist-info}/AUTHORS +0 -0
  75. {djgentelella-0.3.29.dist-info → djgentelella-0.4.1.dist-info}/LICENSE.txt +0 -0
  76. {djgentelella-0.3.29.dist-info → djgentelella-0.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,945 @@
1
+ ///////////////////////////////////////////////
2
+ // Init widgets digital signature
3
+ ///////////////////////////////////////////////
4
+ build_digital_signature = function (instance) {
5
+
6
+ const widgetId = instance.getAttribute("id");
7
+ const url_ws = instance.getAttribute("data-ws-url");
8
+ const container = instance.closest(".widget-digital-signature");
9
+ const container_tag = `container-${widgetId}`;
10
+ const doc_instance = {
11
+ "pk": instance.getAttribute("data-pk"),
12
+ "cc": instance.getAttribute("data-cc"),
13
+ "value": instance.getAttribute("data-value")
14
+ }
15
+ const urls = {
16
+ "logo": instance.getAttribute("data-logo"),
17
+ "sign_doc": instance.getAttribute("data-renderurl"),
18
+ "renderattr": instance.getAttribute("data-renderattr")
19
+ }
20
+ container.setAttribute("data-widget-id", container_tag);
21
+
22
+ // pdfviewer
23
+ const defaultPage = instance.getAttribute("data-default-page") || "first";
24
+
25
+ // Create a new instance of the PDF viewer with the appropriate settings
26
+ const pdfInstance = new PdfSignatureComponent(container, defaultPage, urls, doc_instance);
27
+
28
+ if (!doc_instance) {
29
+ console.error("You must define the doc_instance variable.");
30
+ return;
31
+ }
32
+
33
+ // Signature
34
+ let signatureManager = new SignatureManager(widgetId, container, url_ws, pdfInstance);
35
+ signatureManager.startSign(doc_instance, urls['logo']);
36
+
37
+ // Store the instance in a global object with key per widget ID
38
+ if (!window.pdfSignatureComponents) {
39
+ window.pdfSignatureComponents = {};
40
+ }
41
+
42
+ // Add the instance to the global object if it does not exist
43
+ if (!window.pdfSignatureComponents[container_tag]) {
44
+ window.pdfSignatureComponents[container_tag] = pdfInstance;
45
+ }
46
+
47
+ }
48
+
49
+ ///////////////////////////////////////////////
50
+ // PDF preview Digtal Signature
51
+ ///////////////////////////////////////////////
52
+ class PdfSignatureComponent {
53
+ constructor(container, defaultPage, urls, doc_instance) {
54
+ this.container = container;
55
+ this.defaultPage = defaultPage;
56
+ this.widgetId = container.getAttribute("data-widget-id");
57
+ this.urls=urls;
58
+ this.doc_instance=doc_instance;
59
+
60
+ // Internal elements
61
+ this.signature = container.querySelector('.signature');
62
+ this.canvas = container.querySelector('.pdfviewer');
63
+ this.btn_prev = container.querySelector('.prev');
64
+ this.btn_next = container.querySelector('.next');
65
+ this.page_num = container.querySelector('.page_num');
66
+ this.page_number = container.querySelector('.page_number');
67
+ this.page_count = container.querySelector('.page_count');
68
+ this.sub_canvas_container = container.querySelector('.sub_canvas_container');
69
+
70
+ // Verify that all required elements are present
71
+ if (!this.signature || !this.canvas || !this.btn_prev || !this.btn_next || !this.page_num || !this.page_number || !this.page_count || !this.sub_canvas_container) {
72
+ console.warn("Falta alguno de los elementos requeridos en este componente. Se omite su inicialización.");
73
+ return;
74
+ }
75
+
76
+ // Variables specific to the component
77
+ this.pdfDoc = null;
78
+ this.pageNum = 1;
79
+ this.pageRendering = false;
80
+ this.pageNumPending = null;
81
+ this.scale = 1.2;
82
+ this.signX = 0;
83
+ this.signY = 198;
84
+ this.signWidth = 133;
85
+ this.signHeight = 133;
86
+
87
+ // Initializes the processes
88
+ this.initEvents();
89
+ this.initPDFViewer();
90
+ this.initInteract();
91
+ this.initSignatureSettings();
92
+
93
+ }
94
+
95
+ initEvents() {
96
+ this.btn_prev.addEventListener('click', () => this.onPrevPage());
97
+ this.btn_next.addEventListener('click', () => this.onNextPage());
98
+ this.page_number.addEventListener('change', (e) => this.renderPage(e.target.value));
99
+ this.page_number.addEventListener('keyup', (e) => this.renderPage(e.target.value));
100
+ }
101
+
102
+ initPDFViewer() {
103
+
104
+ if (typeof this.urls['sign_doc'] === 'undefined') {
105
+ console.warn("The variable 'sign_doc' is not defined.");
106
+ return;
107
+ }
108
+ pdfjsLib.getDocument(this.urls['sign_doc']+"?"+this.urls['renderattr']).promise.then((pdfDoc_) => {
109
+ this.pdfDoc = pdfDoc_;
110
+ this.page_count.textContent = pdfDoc_.numPages;
111
+
112
+ // define page number
113
+ if (this.defaultPage === "last") {
114
+ this.pageNum = this.pdfDoc.numPages;
115
+ } else if (this.defaultPage === "first") {
116
+ this.pageNum = 1;
117
+ } else {
118
+ let numPage = parseInt(this.defaultPage, 10);
119
+ if (!isNaN(numPage) && numPage > 0 && numPage <= this.pdfDoc.numPages) {
120
+ this.pageNum = numPage;
121
+ } else {
122
+ console.warn("Invalid page number, starting on the first page.");
123
+ this.pageNum = 1;
124
+ }
125
+ }
126
+
127
+
128
+ this.renderPage(this.pageNum);
129
+ });
130
+ }
131
+
132
+ onPrevPage() {
133
+ if (this.pageNum <= 1) return;
134
+ this.pageNum--;
135
+ this.queueRenderPage(this.pageNum);
136
+ }
137
+
138
+ onNextPage() {
139
+ if (this.pageNum >= this.pdfDoc.numPages) return;
140
+ this.pageNum++;
141
+ this.queueRenderPage(this.pageNum);
142
+ }
143
+
144
+ queueRenderPage(num) {
145
+ if (this.pageRendering) {
146
+ this.pageNumPending = num;
147
+ } else {
148
+ this.renderPage(num);
149
+ }
150
+ }
151
+
152
+ renderPage(num) {
153
+ this.pageRendering = true;
154
+ this.pdfDoc.getPage(num).then((page) => {
155
+ const viewport = page.getViewport({scale: this.scale});
156
+ this.canvas.height = viewport.height;
157
+ this.canvas.width = viewport.width;
158
+
159
+ const renderContext = {
160
+ canvasContext: this.canvas.getContext('2d'), viewport: viewport
161
+ };
162
+ const renderTask = page.render(renderContext);
163
+
164
+ renderTask.promise.then(() => {
165
+ this.pageRendering = false;
166
+ if (this.pageNumPending !== null) {
167
+ this.renderPage(this.pageNumPending);
168
+ this.pageNumPending = null;
169
+ }
170
+ });
171
+ });
172
+ this.page_num.textContent = num;
173
+ this.page_number.value = num;
174
+ }
175
+
176
+ initInteract() {
177
+ // First instance of draggable and resizable with interact.js
178
+ interact(this.signature)
179
+ .draggable({
180
+ inertia: true, modifiers: [interact.modifiers.restrictRect({
181
+ restriction: this.canvas, endOnly: false
182
+ })], autoScroll: true, listeners: {
183
+ move: (event) => this.dragMoveListener(event)
184
+ }
185
+ })
186
+ .resizable({
187
+ edges: {left: true, right: true, bottom: true, top: true}, listeners: {
188
+ move: (event) => {
189
+ let target = event.target;
190
+ let x = (parseFloat(target.getAttribute('data-x')) || 0);
191
+ let y = (parseFloat(target.getAttribute('data-y')) || 0);
192
+
193
+ target.style.width = event.rect.width + 'px';
194
+ target.style.height = event.rect.height + 'px';
195
+
196
+ x += event.deltaRect.left;
197
+ y += event.deltaRect.top;
198
+
199
+ target.style.transform = `translate(${x}px, ${y}px)`;
200
+ target.setAttribute('data-x', x);
201
+ target.setAttribute('data-y', y);
202
+ target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height);
203
+
204
+ this.signWidth = event.rect.width;
205
+ this.signHeight = event.rect.height;
206
+ this.signX = x;
207
+ this.signY = y;
208
+ }
209
+ }, modifiers: [interact.modifiers.restrictEdges({outer: 'parent'}), interact.modifiers.restrictSize({
210
+ min: {
211
+ width: 100, height: 50
212
+ }
213
+ })], inertia: true
214
+ });
215
+
216
+ // Second instance of draggable with autoScroll over the container
217
+ interact(this.signature)
218
+ .draggable({
219
+ inertia: true, modifiers: [interact.modifiers.restrictRect({
220
+ restriction: this.canvas, endOnly: false
221
+ })], autoScroll: {
222
+ container: this.sub_canvas_container, margin: 50, distance: 5, interval: 50
223
+ }, listeners: {
224
+ move: (event) => this.dragMoveListener(event)
225
+ }
226
+ });
227
+
228
+ this.canvas.addEventListener('dblclick', (event) => this.moveSignatureToClick(event));
229
+ }
230
+
231
+ moveSignatureToClick(event) {
232
+ const rect = this.canvas.getBoundingClientRect();
233
+ const x = event.clientX - rect.left;
234
+ const y = event.clientY - rect.top;
235
+
236
+ const signatureWidth = this.signature.offsetWidth;
237
+ const signatureHeight = this.signature.offsetHeight;
238
+
239
+ const centerX = x - signatureWidth / 2;
240
+ const centerY = y - signatureHeight / 2;
241
+
242
+ this.updatePosition(this.signature, centerX, centerY);
243
+ }
244
+
245
+
246
+ dragMoveListener(event) {
247
+ const target = event.target;
248
+ const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
249
+ const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
250
+ this.updatePosition(target, x, y);
251
+ }
252
+
253
+ updatePosition(target, x, y) {
254
+ target.style.transform = `translate(${x}px, ${y}px)`;
255
+ const {x: xAdjusted, y: yAdjusted} = this.adjustPositionToFitWithinCanvas(target, x, y);
256
+ target.style.transform = `translate(${xAdjusted}px, ${yAdjusted}px)`;
257
+ target.setAttribute('data-x', xAdjusted);
258
+ target.setAttribute('data-y', yAdjusted);
259
+ this.signX = Math.round(xAdjusted);
260
+ this.signY = Math.round(yAdjusted);
261
+ }
262
+
263
+ adjustPositionToFitWithinCanvas(target, x, y) {
264
+ const canvasRect = this.canvas.getBoundingClientRect();
265
+ const targetRect = target.getBoundingClientRect();
266
+ if (targetRect.right > canvasRect.right) {
267
+ x -= targetRect.right - canvasRect.right;
268
+ }
269
+ if (targetRect.bottom > canvasRect.bottom) {
270
+ y -= targetRect.bottom - canvasRect.bottom;
271
+ }
272
+ return {x, y};
273
+ }
274
+
275
+ initSignatureSettings() {
276
+ // A base configuration is applied by cloning the signature element
277
+ const tempSignature = this.signature.cloneNode(true);
278
+ tempSignature.style = '';
279
+ tempSignature.classList.remove("right", "left", "top", "bottom", "full", "none");
280
+ tempSignature.style.visibility = 'visible';
281
+ tempSignature.style.width = 'auto';
282
+ tempSignature.style.height = 'auto';
283
+ tempSignature.style.overflow = 'visible';
284
+ const textElem = tempSignature.querySelector('.text');
285
+ if (textElem) textElem.style.wordBreak = 'break-word';
286
+
287
+ this.formatAndLoadContent(tempSignature)
288
+ .then(() => {
289
+ this.signature.className = tempSignature.className;
290
+ this.signature.style.cssText = tempSignature.style.cssText;
291
+ this.signature.innerHTML = tempSignature.innerHTML;
292
+ this.updatePosition(this.signature, 198, 0);
293
+ })
294
+ .catch(error => {
295
+ console.error(gettext("Error when applying setting to signature: "), error);
296
+ });
297
+ }
298
+
299
+ async formatAndLoadContent(element, content) {
300
+ const imageContainer = element.querySelector('.image');
301
+ const textContainer = element.querySelector('.text');
302
+ if (imageContainer) imageContainer.innerHTML = '';
303
+ if (textContainer) textContainer.innerHTML = '';
304
+ try {
305
+ // Here you can load the image or update the text of the signature
306
+ // await this.loadSignatureImage(signatureImageURL, imageContainer);
307
+ } catch (error) {
308
+ console.error(gettext("Error loading content: "), error);
309
+ }
310
+ }
311
+
312
+ async createTemporarySignature(content) {
313
+ let tempSignature = window.app.signature.cloneNode(true);
314
+ tempSignature.id = 'temp_signature';
315
+ tempSignature.style.position = 'absolute';
316
+ tempSignature.style.visibility = 'hidden';
317
+ let textElem = tempSignature.querySelector('.text');
318
+ if (textElem) textElem.style.wordBreak = 'break-word';
319
+ this.sub_canvas_container.appendChild(tempSignature);
320
+ await this.formatAndLoadContent(tempSignature, content);
321
+ return tempSignature;
322
+ }
323
+
324
+ // If required, you can define a method for loading an image
325
+ loadSignatureImage(signatureImage, imageContainer) {
326
+ return new Promise((resolve, reject) => {
327
+ if (!signatureImage) {
328
+ resolve();
329
+ return;
330
+ }
331
+ const img = new Image();
332
+ img.src = signatureImage;
333
+ img.alt = 'signature-image';
334
+ img.onload = () => {
335
+ if (imageContainer) imageContainer.appendChild(img);
336
+ resolve();
337
+ };
338
+ img.onerror = () => {
339
+ reject(new Error(gettext("Error loading image")));
340
+ };
341
+ });
342
+ }
343
+
344
+ getDocumentSettings() {
345
+ const displayScale = this.canvas.getBoundingClientRect().width / this.canvas.width;
346
+ const xReal = this.signX / displayScale;
347
+ const yReal = this.signY / displayScale;
348
+ const wReal = this.signWidth / displayScale;
349
+ const hReal = this.signHeight / displayScale;
350
+
351
+ const xPdf = xReal / this.scale;
352
+ const yPdf = yReal / this.scale;
353
+ const wPdf = wReal / this.scale;
354
+ const hPdf = hReal / this.scale;
355
+
356
+ return {
357
+ pageNumber: this.pageNum,
358
+ signWidth: Math.round(wPdf),
359
+ signHeight: Math.round(hPdf),
360
+ signX: Math.round(xPdf),
361
+ signY: Math.round(yPdf),
362
+ };
363
+ }
364
+ }
365
+
366
+ ///////////////////////////////////////////////
367
+ // Signature manager Digital Signature
368
+ ///////////////////////////////////////////////
369
+ class SignatureManager {
370
+ constructor(input_id, container, url_ws, pdfvisor) {
371
+ this.input_id=input_id;
372
+ this.container = container;
373
+ this.modal = new bootstrap.Modal(container.querySelector("#loading_sign"));
374
+ this.firmador = new DocumentClient(container, container.getAttribute("data-widget-id"), this, url_ws, this.doc_instance);
375
+ this.signerBtn = container.querySelector(".btn_signer");
376
+ this.errorsContainer = container.querySelector(".errors_signer");
377
+ this.refreshBtn = container.querySelector(".btn_signer_refresh");
378
+ this.socketError = false;
379
+ this.pdfvisor = pdfvisor;
380
+
381
+ this.initEvents();
382
+ }
383
+
384
+ initEvents() {
385
+ if (this.signerBtn) {
386
+ this.signerBtn.addEventListener('click', () => this.sign());
387
+ }
388
+ if (this.refreshBtn) {
389
+ this.refreshBtn.addEventListener('click', () => this.refresh());
390
+ }
391
+ }
392
+
393
+ startSign(doc_instance, logo_url = null) {
394
+ if (this.socketError) {
395
+ alertSimple(errorInterpreter(3), gettext("Error"), "error");
396
+ return;
397
+ }
398
+
399
+ this.doc_instance = doc_instance;
400
+ this.logo_url = logo_url;
401
+
402
+ this.clearErrors();
403
+
404
+ this.firmador.start_sign(doc_instance, logo_url)
405
+ }
406
+
407
+ refresh() {
408
+ this.socketError = false;
409
+ this.firmador.remotesigner.inicialize();
410
+
411
+ this.clearErrors();
412
+ this.firmador.start_sign(this.doc_instance, this.logo_url);
413
+ }
414
+
415
+ sign() {
416
+ this.clearErrors();
417
+ this.firmador.do_sign_remote();
418
+ }
419
+
420
+ addError(errorCode) {
421
+ let title = document.createElement('p');
422
+ title.classList.add('mt-2', 'text-danger', 'mb-0');
423
+ title.innerHTML = `<i class="fa fa-times-circle" aria-hidden="true"></i> <span class="fw-bold"> ${gettext("Errors")} </span>`;
424
+ let errorElement = document.createElement('p');
425
+ errorElement.classList.add('text-danger', 'mx-3', 'small');
426
+ errorElement.innerHTML = errorInterpreter(errorCode);
427
+ this.errorsContainer.appendChild(title);
428
+ this.errorsContainer.appendChild(errorElement);
429
+ }
430
+
431
+ clearErrors() {
432
+ this.errorsContainer.innerHTML = '';
433
+ }
434
+
435
+ reloadPage() {
436
+ window.location.reload();
437
+ }
438
+
439
+ showLoading() {
440
+ this.modal.show();
441
+ }
442
+
443
+ hideLoading() {
444
+ setTimeout(() => {
445
+ this.modal.hide();
446
+ }, 500);
447
+ }
448
+ }
449
+
450
+ ///////////////////////////////////////////////
451
+ // Socket Digital Signature
452
+ ///////////////////////////////////////////////
453
+ function responseManageTypeData(instance, err_json_fn, error_text_fn) {
454
+ return function (response) {
455
+ const contentType = response.headers.get("content-type");
456
+ if (response.ok) {
457
+ if (contentType && contentType.indexOf("application/json") !== -1) {
458
+ return response.json();
459
+ } else {
460
+ return response.text();
461
+ }
462
+ } else {
463
+ if (contentType && contentType.indexOf("application/json") !== -1) {
464
+ response.json().then(data => err_json_fn(data));
465
+ } else {
466
+ if (response.status === 406) {
467
+ // cierre de ventana para ingresar el PIN
468
+ error_text_fn(errorInterpreter(4));
469
+ } else {
470
+ response.text().then(data => error_text_fn(data));
471
+ }
472
+ }
473
+ }
474
+ return Promise.reject(response);
475
+ }
476
+ }
477
+
478
+ function SocketManager(socket, signatureManager) {
479
+ // If an error occurs during the connection
480
+ socket.onerror = (event) => {
481
+ // console.error("WebSocket error");
482
+ signatureManager.hideLoading();
483
+ alertSimple(errorInterpreter(3), gettext("Error"), "error");
484
+ signatureManager.socketError = true;
485
+ };
486
+
487
+ // If the connection is closed
488
+ socket.onclose = (event) => {
489
+ // console.warn("WebSocket cerrado");
490
+ };
491
+
492
+ // If the connection is opened
493
+ socket.onopen = (event) => {
494
+ // console.log("WebSocket conectado");
495
+ signatureManager.socket_error = false;
496
+ };
497
+ }
498
+
499
+ function callFetch(instance) {
500
+ fetch(instance.url, {
501
+ method: instance.type,
502
+ body: instance.data,
503
+ headers: {
504
+ 'X-CSRFToken': getCookie('csrftoken'),
505
+ 'Content-Type': 'application/json'
506
+ },
507
+ cache: 'no-cache', // do not use cache
508
+ }).then(responseManageTypeData(instance, instance.error_json, instance.error_text))
509
+ .then(data => instance.success(data))
510
+ .catch(error => {
511
+ instance.error(error);
512
+ });
513
+ }
514
+
515
+ function FirmadorLibreLocal(docmanager, signatureManager) {
516
+ return {
517
+ "cert_url": "http://localhost:3516/certificates",
518
+ "sign_url": "http://localhost:3516/sign",
519
+ "success_get_certificates": function (data) {
520
+ docmanager.success_certificates(data);
521
+ },
522
+ "get_certificates": function () {
523
+ let parent = this;
524
+
525
+ const instance = {
526
+ url: this.cert_url,
527
+ type: 'GET',
528
+ data: null,
529
+ success: function (data) {
530
+ parent.success_get_certificates(data);
531
+ },
532
+ error_text: function (message) {
533
+ // console.log(message);
534
+ },
535
+ error_json: function (error) {
536
+ // console.log(error);
537
+ },
538
+ error: function (error) {
539
+ // Unrecognized Firmador Libre
540
+ if (String(error) === "TypeError: NetworkError when attempting to fetch resource.") {
541
+ signatureManager.addError(1);
542
+ }
543
+ }
544
+ }
545
+ callFetch(instance);
546
+ },
547
+ "sign": function (data) {
548
+ let json = JSON.stringify(data);
549
+ let manager = docmanager;
550
+
551
+ const fetch_instance = {
552
+ 'url': this.sign_url,
553
+ 'type': 'POST',
554
+ 'data': json,
555
+ "success": function (data) {
556
+ // if the result is different from a string, it is possible that there is an error.
557
+ // console.log(data)
558
+ if (typeof data !== 'string') { //prevent option call
559
+ manager.local_done(data);
560
+ }
561
+
562
+ },
563
+ "error": function (error) {
564
+ // console.log(error);
565
+ signatureManager.hideLoading();
566
+ if (typeof error === "object") {
567
+ // close window for PIN entry
568
+ if (error.status === 406 && error.statusText === "Not Acceptable") {
569
+ alertSimple(errorInterpreter(4), gettext("Warning"), "warning");
570
+ }
571
+ }
572
+ },
573
+ "error_text": function (message) {
574
+ console.error("error_text", message);
575
+ signatureManager.hideLoading();
576
+ },
577
+ "error_json": function (error) {
578
+ console.error("error_json", error);
579
+ signatureManager.hideLoading();
580
+ },
581
+ };
582
+
583
+ callFetch(fetch_instance);
584
+ }
585
+ }
586
+ }
587
+
588
+ function FirmadorLibreWS(docmanager, url, signatureManager) {
589
+ var firmador = {
590
+ "url": url,
591
+ "websocket": null,
592
+ "firmador_url": "http://localhost:3516",
593
+ "trans_received": function (instance) {
594
+ return function (event) {
595
+ try {
596
+ const data = JSON.parse(event.data);
597
+ instance.receive_json(data);
598
+ } catch (err) {
599
+ console.error("Error al parsear mensaje WS:", err);
600
+ }
601
+ };
602
+ },
603
+ "receive_json": function (data) {
604
+ // validate socket errors
605
+ if (data.result === false && data.error) {
606
+ signatureManager.hideLoading();
607
+ if (typeof data.details === "string") {
608
+ // connection issues with the Firmador Libre API
609
+ if (data.details.includes("Connection refused")) {
610
+ signatureManager.addError(3);
611
+ } else {
612
+ alertSimple(errorInterpreter(999), gettext("Error"), "error");
613
+ }
614
+
615
+ } else if (data.code) {
616
+ switch (data.code) {
617
+ case 0:
618
+ // when an unknown error occurs on the server
619
+ alertSimple(errorInterpreter(0), gettext("Error"), "error");
620
+ break;
621
+ case 6:
622
+ // when the new signature field position overlaps with an existing signature
623
+ alertSimple(errorInterpreter(6), gettext("Error"), "error");
624
+ break;
625
+ case 7:
626
+ // when the signature field is outside the page boundaries
627
+ alertSimple(errorInterpreter(7), gettext("Error"), "error");
628
+ break;
629
+ case 8:
630
+ // when an error occurs due to an incompatible library
631
+ alertSimple(errorInterpreter(8), gettext("Error"), "error");
632
+ break;
633
+ case 9:
634
+ // when an error occurs due to an unavailable cryptographic provider
635
+ alertSimple(errorInterpreter(9), gettext("Error"), "error");
636
+ break;
637
+ case 10:
638
+ // when an error occurs due to the signing algorithm
639
+ alertSimple(errorInterpreter(10), gettext("Error"), "error");
640
+ break;
641
+ case 11:
642
+ // when there are errors serializing data
643
+ alertSimple(errorInterpreter(11), gettext("Error"), "error");
644
+ break;
645
+ case 12:
646
+ // when an error occurs due to a signing service timeout
647
+ alertSimple(errorInterpreter(12), gettext("Error"), "error");
648
+ break;
649
+ // when an unknown error occurs
650
+ alertSimple(errorInterpreter(999), gettext("Error"), "error");
651
+ break;
652
+ }
653
+ }
654
+ }else{
655
+
656
+ if(data.hasOwnProperty('tobesigned')){
657
+ docmanager.do_sign_local(data);
658
+ }else{
659
+ docmanager.remote_done(data)
660
+ }
661
+
662
+ }
663
+ },
664
+
665
+ "inicialize": function () {
666
+ this.websocket = new WebSocket(url);
667
+ SocketManager(this.websocket, signatureManager);
668
+ this.websocket.onmessage = this.trans_received(this);
669
+ },
670
+ "local_done": function (data) {
671
+ // console.log("local_done", data);
672
+ },
673
+ "sign": function (data) {
674
+ data["action"] = "initial_signature";
675
+ if (data.card !== undefined) {
676
+ this.websocket.send(JSON.stringify(data));
677
+ signatureManager.showLoading();
678
+ } else {
679
+ alertSimple(errorInterpreter(2), gettext("Error"), "error");
680
+ signatureManager.addError(2);
681
+ }
682
+ },
683
+ "complete_sign": function (data) {
684
+ data["action"] = "complete_signature";
685
+
686
+ try {
687
+ this.websocket.send(JSON.stringify(data));
688
+ } catch (e) {
689
+ // console.error("Error de comunicación WS");
690
+ signatureManager.hideLoading();
691
+ alertFunction(errorInterpreter(3), gettext("Error"), "error", false, closeModalSignature);
692
+ }
693
+ },
694
+ };
695
+ firmador.inicialize();
696
+ return firmador;
697
+ }
698
+
699
+ function DocumentClient(container, widgetId, signatureManager, url_ws) {
700
+ const docmanager = {
701
+ "widgetId": widgetId,
702
+ "container": container,
703
+ "signatureManager": signatureManager,
704
+ "remotesigner": null,
705
+ "localsigner": null,
706
+ "certificates": null,
707
+ "doc_instance": null,
708
+ "logo_url": null,
709
+
710
+ "start_sign": function (doc_instance, logo_url = null) {
711
+ this.doc_instance = doc_instance;
712
+ this.logo_url = logo_url;
713
+ this.localsigner.get_certificates();
714
+ this.signatureManager.clearErrors();
715
+ },
716
+ "success_certificates": function (data) {
717
+
718
+ if (data.length > 0) {
719
+ container.querySelector("#container_select_card").classList.remove("d-none");
720
+ container.querySelector("#container_select_card_tem").classList.add("d-none");
721
+
722
+ let select_card = container.querySelector(".select_card");
723
+
724
+ if (!select_card) {
725
+ console.error(`Select not found for widget ${widgetId}`);
726
+ return;
727
+ }
728
+ select_card.innerHTML = "";
729
+ this.certificates = {};
730
+
731
+ data.forEach((element) => {
732
+ this.certificates[element.tokenSerialNumber] = element;
733
+ let start_token = element.tokenSerialNumber.substring(0, 4);
734
+ let newOption = new Option(
735
+ `${start_token} ${element.commonName}`,
736
+ element.tokenSerialNumber,
737
+ false, false
738
+ );
739
+ select_card.appendChild(newOption);
740
+ });
741
+ } else {
742
+ container.querySelector("#container_select_card").classList.add("d-none");
743
+ container.querySelector("#container_select_card_tem").classList.remove("d-none");
744
+ this.signatureManager.addError(2);
745
+ }
746
+ },
747
+
748
+ "do_sign_remote": function () {
749
+ let select = container.querySelector(".select_card");
750
+ let selected_card = select ? select.value : null;
751
+
752
+ if (selected_card && this.certificates) {
753
+ let data = {
754
+ 'logo_url': this.logo_url,
755
+ 'instance': this.doc_instance,
756
+ 'card': this.certificates[selected_card],
757
+ "docsettings": window.pdfSignatureComponents[widgetId].getDocumentSettings()
758
+ };
759
+ this.remotesigner.sign(data);
760
+ } else if (!selected_card && !this.certificates) {
761
+ signatureManager.hideLoading();
762
+ alertSimple(errorInterpreter(1), gettext("Error"), "error");
763
+ this.signatureManager.addError(1);
764
+ } else if (!selected_card && this.certificates) {
765
+ signatureManager.hideLoading();
766
+ alertSimple(errorInterpreter(2), gettext("Error"), "error");
767
+ this.signatureManager.addError(2);
768
+ }
769
+
770
+ },
771
+ "do_sign_local": function (data) {
772
+ this.localsigner.sign(data);
773
+ },
774
+ "local_done": function (data) {
775
+ data['instance'] = this.doc_instance;
776
+ data['logo_url'] = this.logo_url;
777
+ this.remotesigner.complete_sign(data);
778
+ },
779
+ "remote_done": function (data) {
780
+ if(data.result !== null ){
781
+ const l = btoa(JSON.stringify({'token': data.result}));
782
+ this.signatureManager.doc_instance['value'] =l;
783
+ this.signatureManager.pdfvisor.urls['renderattr']="value="+l;
784
+ document.getElementById(this.signatureManager.input_id).value=l;
785
+ this.signatureManager.pdfvisor.initPDFViewer();
786
+ signatureManager.hideLoading();
787
+ alertFunction(
788
+ gettext("The signing was successfully completed."),
789
+ gettext("Success"),
790
+ "success", false, function(){}
791
+ );
792
+ }
793
+ },
794
+
795
+ };
796
+
797
+ docmanager["remotesigner"] = new FirmadorLibreWS(docmanager, url_ws, signatureManager);
798
+ docmanager["localsigner"] = new FirmadorLibreLocal(docmanager, signatureManager);
799
+
800
+ return docmanager;
801
+ }
802
+
803
+ ///////////////////////////////////////////////
804
+ // Manage Errors Digital Signature
805
+ ///////////////////////////////////////////////
806
+ function errorInterpreter(error) {
807
+
808
+ let textError = "";
809
+
810
+ switch (error) {
811
+ case 0:
812
+ // when an uncontrolled error occurs in the signing server
813
+ textError = gettext("An error has occurred in the internal server of the uncontrolled 'Firmador Libre'.");
814
+ break;
815
+ case 1:
816
+ // when the 'Firmador Libre' application fails to open
817
+ textError = gettext("Make sure to start the 'Firmador Libre' application. If it is already running, please press the reload button.");
818
+ break;
819
+ case 2:
820
+ // when there is no card connected
821
+ textError = gettext("There is no card connected to the device. Please press the reload button and connect your card.");
822
+ break;
823
+ case 3:
824
+ // when the signing service is not functioning
825
+ textError = gettext("The internal signature service does not work. Please contact the support.");
826
+ break;
827
+ case 4:
828
+ // when the modal is closed but the PIN entry window remains open
829
+ textError = gettext("Authentication failed because the PIN entry window was detected closed, please try again.");
830
+ break;
831
+ case 5:
832
+ // when the card is disconnected
833
+ //! This error should be resolved within Firmador Libre
834
+ textError = gettext("The device was disconnected, possibly the window was closed for signature. Please close the window for the authentication PIN.");
835
+ break;
836
+ case 6:
837
+ // when the new signature field position overlaps with an existing signature
838
+ textError = gettext("The new signature field position overlaps with an existing signature.");
839
+ break;
840
+ case 7:
841
+ // when the signature is positioned outside the page boundaries
842
+ textError = gettext("The new signature field position is outside the page dimensions.");
843
+ break;
844
+ case 8:
845
+ // when an error occurs due to an incompatible library
846
+ textError = gettext("The version of one or more libraries is incompatible.");
847
+ break;
848
+ case 9:
849
+ // when an error occurs due to an unavailable cryptographic provider
850
+ textError = gettext("The cryptographic provider is not available.");
851
+ break;
852
+ case 10:
853
+ // when an error occurs due to the signing algorithm
854
+ textError = gettext("The signing algorithm is not available.");
855
+ break;
856
+ case 11:
857
+ // when errors occur while serializing data
858
+ textError = gettext("Errors have been encountered in the data to be sent to the 'Free Signer'. Please press the reload button and try again.");
859
+ break;
860
+ case 12:
861
+ // when an error occurs due to a signing service timeout
862
+ textError = gettext("The request to the signing service timed out. Please, press the reload button and try again.");
863
+ break;
864
+ default:
865
+ // when an unknown error occurs
866
+ textError = gettext("We're sorry, an unexpected error occurred. Please, press the reload button and try again.");
867
+ }
868
+
869
+ return textError;
870
+ }
871
+
872
+
873
+ ///////////////////////////////////////////////
874
+ // Alerts Digital Signature
875
+ ///////////////////////////////////////////////
876
+ function alertSimple(text, title = "Error", icon = "error") {
877
+ Swal.fire({
878
+ icon: icon,
879
+ title: title,
880
+ text: text,
881
+ confirmButtonText: gettext("Accept")
882
+ });
883
+ }
884
+
885
+ function alertFunction(text, title = "Error", icon = "error", cancelButton = false,
886
+ callback = () => {
887
+ }) {
888
+ Swal.fire({
889
+ icon: icon,
890
+ title: title,
891
+ text: text,
892
+ confirmButtonText: gettext("Accept"),
893
+ showCancelButton: cancelButton,
894
+ cancelButtonText: gettext("Cancel"),
895
+ confirmButtonColor: '#3085d6',
896
+ cancelButtonColor: '#d33',
897
+ }).then((result) => {
898
+ if (result.isConfirmed) {
899
+ callback();
900
+ }
901
+ });
902
+ }
903
+
904
+ ///////////////////////////////////////////////
905
+ // End widgets digital signature
906
+ ///////////////////////////////////////////////
907
+
908
+
909
+ ////////////////////////////////////////////////////////////////
910
+ // copy action
911
+ ////////////////////////////////////////////////////////////////
912
+ document.addEventListener("DOMContentLoaded", function () {
913
+
914
+ if (document.getElementById("copy-command-line")) {
915
+ document.getElementById("copy-command-line").addEventListener("click", function () {
916
+ let commandText = document.getElementById("command-line").innerText.trim();
917
+
918
+ navigator.clipboard.writeText(commandText)
919
+ .then(() => {
920
+ let text = document.getElementById("text-copy")
921
+ text.classList.remove("d-none")
922
+ setTimeout(() => {
923
+ text.classList.add("d-none")
924
+ }, 1500)
925
+ })
926
+ .catch(err => {
927
+ console.error("Error al copiar el texto: ", err);
928
+ });
929
+ });
930
+
931
+ document.getElementById("show-command-line").addEventListener("click", () => {
932
+ let container = document.getElementById("container-command-line")
933
+
934
+ if (container.classList.contains("d-none")) {
935
+ container.classList.remove("d-none");
936
+ } else {
937
+ container.classList.add("d-none");
938
+ }
939
+ })
940
+ }
941
+ });
942
+
943
+ ////////////////////////////////////////////////////////////////
944
+ // End copy action
945
+ ////////////////////////////////////////////////////////////////