djgentelella 0.3.28__py3-none-any.whl → 0.4.0__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 (74) 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/groute.py +6 -1
  20. djgentelella/gtselects.py +5 -9
  21. djgentelella/locale/es/LC_MESSAGES/django.mo +0 -0
  22. djgentelella/locale/es/LC_MESSAGES/django.po +103 -6
  23. djgentelella/locale/es/LC_MESSAGES/djangojs.mo +0 -0
  24. djgentelella/locale/es/LC_MESSAGES/djangojs.po +107 -8
  25. djgentelella/management/commands/createbasejs.py +3 -0
  26. djgentelella/management/commands/loaddevstatic.py +18 -0
  27. djgentelella/migrations/0013_usersignatureconfig.py +25 -0
  28. djgentelella/serializers/firmador_digital.py +103 -0
  29. djgentelella/settings.py +5 -4
  30. djgentelella/static/djgentelella.readonly.vendors.min.css +1 -1
  31. djgentelella/static/djgentelella.readonly.vendors.min.js +1 -1
  32. djgentelella/static/djgentelella.vendors.header.min.js +1 -1
  33. djgentelella/static/djgentelella.vendors.min.css +2 -2
  34. djgentelella/static/gentelella/css/pdfviewer.css +147 -0
  35. djgentelella/static/gentelella/images/firmador.ico +0 -0
  36. djgentelella/static/gentelella/js/base/digital_signature.js +945 -0
  37. djgentelella/static/gentelella/js/base.js +947 -0
  38. djgentelella/static/gentelella/js/datatables.js +2 -2
  39. djgentelella/static/gentelella/js/widgets.js +72 -59
  40. djgentelella/static/vendors/friconix/friconix.js +1 -1
  41. djgentelella/static/vendors/pdfjs/images/altText_add.svg +3 -0
  42. djgentelella/static/vendors/pdfjs/images/altText_disclaimer.svg +3 -0
  43. djgentelella/static/vendors/pdfjs/images/altText_done.svg +3 -0
  44. djgentelella/static/vendors/pdfjs/images/altText_spinner.svg +30 -0
  45. djgentelella/static/vendors/pdfjs/images/altText_warning.svg +3 -0
  46. djgentelella/static/vendors/pdfjs/images/cursor-editorFreeHighlight.svg +6 -0
  47. djgentelella/static/vendors/pdfjs/images/cursor-editorFreeText.svg +3 -0
  48. djgentelella/static/vendors/pdfjs/images/cursor-editorInk.svg +4 -0
  49. djgentelella/static/vendors/pdfjs/images/cursor-editorTextHighlight.svg +8 -0
  50. djgentelella/static/vendors/pdfjs/images/editor-toolbar-delete.svg +5 -0
  51. djgentelella/static/vendors/pdfjs/images/loading-icon.gif +0 -0
  52. djgentelella/static/vendors/pdfjs/images/messageBar_closingButton.svg +3 -0
  53. djgentelella/static/vendors/pdfjs/images/messageBar_warning.svg +3 -0
  54. djgentelella/static/vendors/pdfjs/images/toolbarButton-editorHighlight.svg +6 -0
  55. djgentelella/static/vendors/pdfjs/images/toolbarButton-menuArrow.svg +3 -0
  56. djgentelella/static/vendors/timeline/css/timeline.css +1 -1
  57. djgentelella/static/vendors/timeline/js/timeline.js +1 -1
  58. djgentelella/templates/gentelella/registration/login.html +38 -42
  59. djgentelella/templates/gentelella/statics/javascript.html +75 -61
  60. djgentelella/templates/gentelella/statics/stylesheets.html +11 -10
  61. djgentelella/templates/gentelella/widgets/addtreeselect.html +1 -1
  62. djgentelella/templates/gentelella/widgets/chunkedupload.html +2 -2
  63. djgentelella/templates/gentelella/widgets/digital_signature.html +208 -0
  64. djgentelella/templates/gentelella/widgets/file.html +15 -16
  65. djgentelella/templatetags/gtsettings.py +4 -0
  66. djgentelella/urls.py +1 -1
  67. djgentelella/widgets/core.py +5 -2
  68. djgentelella/widgets/digital_signature.py +116 -0
  69. {djgentelella-0.3.28.dist-info → djgentelella-0.4.0.dist-info}/METADATA +18 -20
  70. {djgentelella-0.3.28.dist-info → djgentelella-0.4.0.dist-info}/RECORD +74 -37
  71. {djgentelella-0.3.28.dist-info → djgentelella-0.4.0.dist-info}/WHEEL +1 -1
  72. {djgentelella-0.3.28.dist-info → djgentelella-0.4.0.dist-info}/AUTHORS +0 -0
  73. {djgentelella-0.3.28.dist-info → djgentelella-0.4.0.dist-info}/LICENSE.txt +0 -0
  74. {djgentelella-0.3.28.dist-info → djgentelella-0.4.0.dist-info}/top_level.txt +0 -0
@@ -2248,6 +2248,953 @@ function getMediaRecord(element, mediatype){
2248
2248
  }
2249
2249
 
2250
2250
 
2251
+ ///////////////////////////////////////////////
2252
+ // Init widgets digital signature
2253
+ ///////////////////////////////////////////////
2254
+ build_digital_signature = function (instance) {
2255
+
2256
+ const widgetId = instance.getAttribute("id");
2257
+ const url_ws = instance.getAttribute("data-ws-url");
2258
+ const container = instance.closest(".widget-digital-signature");
2259
+ const container_tag = `container-${widgetId}`;
2260
+ const doc_instance = {
2261
+ "pk": instance.getAttribute("data-pk"),
2262
+ "cc": instance.getAttribute("data-cc"),
2263
+ "value": instance.getAttribute("data-value")
2264
+ }
2265
+ const urls = {
2266
+ "logo": instance.getAttribute("data-logo"),
2267
+ "sign_doc": instance.getAttribute("data-renderurl"),
2268
+ "renderattr": instance.getAttribute("data-renderattr")
2269
+ }
2270
+ container.setAttribute("data-widget-id", container_tag);
2271
+
2272
+ // pdfviewer
2273
+ const defaultPage = instance.getAttribute("data-default-page") || "first";
2274
+
2275
+ // Create a new instance of the PDF viewer with the appropriate settings
2276
+ const pdfInstance = new PdfSignatureComponent(container, defaultPage, urls, doc_instance);
2277
+
2278
+ if (!doc_instance) {
2279
+ console.error("You must define the doc_instance variable.");
2280
+ return;
2281
+ }
2282
+
2283
+ // Signature
2284
+ let signatureManager = new SignatureManager(widgetId, container, url_ws, pdfInstance);
2285
+ signatureManager.startSign(doc_instance, urls['logo']);
2286
+
2287
+ // Store the instance in a global object with key per widget ID
2288
+ if (!window.pdfSignatureComponents) {
2289
+ window.pdfSignatureComponents = {};
2290
+ }
2291
+
2292
+ // Add the instance to the global object if it does not exist
2293
+ if (!window.pdfSignatureComponents[container_tag]) {
2294
+ window.pdfSignatureComponents[container_tag] = pdfInstance;
2295
+ }
2296
+
2297
+ }
2298
+
2299
+ ///////////////////////////////////////////////
2300
+ // PDF preview Digtal Signature
2301
+ ///////////////////////////////////////////////
2302
+ class PdfSignatureComponent {
2303
+ constructor(container, defaultPage, urls, doc_instance) {
2304
+ this.container = container;
2305
+ this.defaultPage = defaultPage;
2306
+ this.widgetId = container.getAttribute("data-widget-id");
2307
+ this.urls=urls;
2308
+ this.doc_instance=doc_instance;
2309
+
2310
+ // Internal elements
2311
+ this.signature = container.querySelector('.signature');
2312
+ this.canvas = container.querySelector('.pdfviewer');
2313
+ this.btn_prev = container.querySelector('.prev');
2314
+ this.btn_next = container.querySelector('.next');
2315
+ this.page_num = container.querySelector('.page_num');
2316
+ this.page_number = container.querySelector('.page_number');
2317
+ this.page_count = container.querySelector('.page_count');
2318
+ this.sub_canvas_container = container.querySelector('.sub_canvas_container');
2319
+
2320
+ // Verify that all required elements are present
2321
+ if (!this.signature || !this.canvas || !this.btn_prev || !this.btn_next || !this.page_num || !this.page_number || !this.page_count || !this.sub_canvas_container) {
2322
+ console.warn("Falta alguno de los elementos requeridos en este componente. Se omite su inicialización.");
2323
+ return;
2324
+ }
2325
+
2326
+ // Variables specific to the component
2327
+ this.pdfDoc = null;
2328
+ this.pageNum = 1;
2329
+ this.pageRendering = false;
2330
+ this.pageNumPending = null;
2331
+ this.scale = 1.2;
2332
+ this.signX = 0;
2333
+ this.signY = 198;
2334
+ this.signWidth = 133;
2335
+ this.signHeight = 133;
2336
+
2337
+ // Initializes the processes
2338
+ this.initEvents();
2339
+ this.initPDFViewer();
2340
+ this.initInteract();
2341
+ this.initSignatureSettings();
2342
+
2343
+ }
2344
+
2345
+ initEvents() {
2346
+ this.btn_prev.addEventListener('click', () => this.onPrevPage());
2347
+ this.btn_next.addEventListener('click', () => this.onNextPage());
2348
+ this.page_number.addEventListener('change', (e) => this.renderPage(e.target.value));
2349
+ this.page_number.addEventListener('keyup', (e) => this.renderPage(e.target.value));
2350
+ }
2351
+
2352
+ initPDFViewer() {
2353
+
2354
+ if (typeof this.urls['sign_doc'] === 'undefined') {
2355
+ console.warn("The variable 'sign_doc' is not defined.");
2356
+ return;
2357
+ }
2358
+ pdfjsLib.getDocument(this.urls['sign_doc']+"?"+this.urls['renderattr']).promise.then((pdfDoc_) => {
2359
+ this.pdfDoc = pdfDoc_;
2360
+ this.page_count.textContent = pdfDoc_.numPages;
2361
+
2362
+ // define page number
2363
+ if (this.defaultPage === "last") {
2364
+ this.pageNum = this.pdfDoc.numPages;
2365
+ } else if (this.defaultPage === "first") {
2366
+ this.pageNum = 1;
2367
+ } else {
2368
+ let numPage = parseInt(this.defaultPage, 10);
2369
+ if (!isNaN(numPage) && numPage > 0 && numPage <= this.pdfDoc.numPages) {
2370
+ this.pageNum = numPage;
2371
+ } else {
2372
+ console.warn("Invalid page number, starting on the first page.");
2373
+ this.pageNum = 1;
2374
+ }
2375
+ }
2376
+
2377
+
2378
+ this.renderPage(this.pageNum);
2379
+ });
2380
+ }
2381
+
2382
+ onPrevPage() {
2383
+ if (this.pageNum <= 1) return;
2384
+ this.pageNum--;
2385
+ this.queueRenderPage(this.pageNum);
2386
+ }
2387
+
2388
+ onNextPage() {
2389
+ if (this.pageNum >= this.pdfDoc.numPages) return;
2390
+ this.pageNum++;
2391
+ this.queueRenderPage(this.pageNum);
2392
+ }
2393
+
2394
+ queueRenderPage(num) {
2395
+ if (this.pageRendering) {
2396
+ this.pageNumPending = num;
2397
+ } else {
2398
+ this.renderPage(num);
2399
+ }
2400
+ }
2401
+
2402
+ renderPage(num) {
2403
+ this.pageRendering = true;
2404
+ this.pdfDoc.getPage(num).then((page) => {
2405
+ const viewport = page.getViewport({scale: this.scale});
2406
+ this.canvas.height = viewport.height;
2407
+ this.canvas.width = viewport.width;
2408
+
2409
+ const renderContext = {
2410
+ canvasContext: this.canvas.getContext('2d'), viewport: viewport
2411
+ };
2412
+ const renderTask = page.render(renderContext);
2413
+
2414
+ renderTask.promise.then(() => {
2415
+ this.pageRendering = false;
2416
+ if (this.pageNumPending !== null) {
2417
+ this.renderPage(this.pageNumPending);
2418
+ this.pageNumPending = null;
2419
+ }
2420
+ });
2421
+ });
2422
+ this.page_num.textContent = num;
2423
+ this.page_number.value = num;
2424
+ }
2425
+
2426
+ initInteract() {
2427
+ // First instance of draggable and resizable with interact.js
2428
+ interact(this.signature)
2429
+ .draggable({
2430
+ inertia: true, modifiers: [interact.modifiers.restrictRect({
2431
+ restriction: this.canvas, endOnly: false
2432
+ })], autoScroll: true, listeners: {
2433
+ move: (event) => this.dragMoveListener(event)
2434
+ }
2435
+ })
2436
+ .resizable({
2437
+ edges: {left: true, right: true, bottom: true, top: true}, listeners: {
2438
+ move: (event) => {
2439
+ let target = event.target;
2440
+ let x = (parseFloat(target.getAttribute('data-x')) || 0);
2441
+ let y = (parseFloat(target.getAttribute('data-y')) || 0);
2442
+
2443
+ target.style.width = event.rect.width + 'px';
2444
+ target.style.height = event.rect.height + 'px';
2445
+
2446
+ x += event.deltaRect.left;
2447
+ y += event.deltaRect.top;
2448
+
2449
+ target.style.transform = `translate(${x}px, ${y}px)`;
2450
+ target.setAttribute('data-x', x);
2451
+ target.setAttribute('data-y', y);
2452
+ target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height);
2453
+
2454
+ this.signWidth = event.rect.width;
2455
+ this.signHeight = event.rect.height;
2456
+ this.signX = x;
2457
+ this.signY = y;
2458
+ }
2459
+ }, modifiers: [interact.modifiers.restrictEdges({outer: 'parent'}), interact.modifiers.restrictSize({
2460
+ min: {
2461
+ width: 100, height: 50
2462
+ }
2463
+ })], inertia: true
2464
+ });
2465
+
2466
+ // Second instance of draggable with autoScroll over the container
2467
+ interact(this.signature)
2468
+ .draggable({
2469
+ inertia: true, modifiers: [interact.modifiers.restrictRect({
2470
+ restriction: this.canvas, endOnly: false
2471
+ })], autoScroll: {
2472
+ container: this.sub_canvas_container, margin: 50, distance: 5, interval: 50
2473
+ }, listeners: {
2474
+ move: (event) => this.dragMoveListener(event)
2475
+ }
2476
+ });
2477
+
2478
+ this.canvas.addEventListener('dblclick', (event) => this.moveSignatureToClick(event));
2479
+ }
2480
+
2481
+ moveSignatureToClick(event) {
2482
+ const rect = this.canvas.getBoundingClientRect();
2483
+ const x = event.clientX - rect.left;
2484
+ const y = event.clientY - rect.top;
2485
+
2486
+ const signatureWidth = this.signature.offsetWidth;
2487
+ const signatureHeight = this.signature.offsetHeight;
2488
+
2489
+ const centerX = x - signatureWidth / 2;
2490
+ const centerY = y - signatureHeight / 2;
2491
+
2492
+ this.updatePosition(this.signature, centerX, centerY);
2493
+ }
2494
+
2495
+
2496
+ dragMoveListener(event) {
2497
+ const target = event.target;
2498
+ const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
2499
+ const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
2500
+ this.updatePosition(target, x, y);
2501
+ }
2502
+
2503
+ updatePosition(target, x, y) {
2504
+ target.style.transform = `translate(${x}px, ${y}px)`;
2505
+ const {x: xAdjusted, y: yAdjusted} = this.adjustPositionToFitWithinCanvas(target, x, y);
2506
+ target.style.transform = `translate(${xAdjusted}px, ${yAdjusted}px)`;
2507
+ target.setAttribute('data-x', xAdjusted);
2508
+ target.setAttribute('data-y', yAdjusted);
2509
+ this.signX = Math.round(xAdjusted);
2510
+ this.signY = Math.round(yAdjusted);
2511
+ }
2512
+
2513
+ adjustPositionToFitWithinCanvas(target, x, y) {
2514
+ const canvasRect = this.canvas.getBoundingClientRect();
2515
+ const targetRect = target.getBoundingClientRect();
2516
+ if (targetRect.right > canvasRect.right) {
2517
+ x -= targetRect.right - canvasRect.right;
2518
+ }
2519
+ if (targetRect.bottom > canvasRect.bottom) {
2520
+ y -= targetRect.bottom - canvasRect.bottom;
2521
+ }
2522
+ return {x, y};
2523
+ }
2524
+
2525
+ initSignatureSettings() {
2526
+ // A base configuration is applied by cloning the signature element
2527
+ const tempSignature = this.signature.cloneNode(true);
2528
+ tempSignature.style = '';
2529
+ tempSignature.classList.remove("right", "left", "top", "bottom", "full", "none");
2530
+ tempSignature.style.visibility = 'visible';
2531
+ tempSignature.style.width = 'auto';
2532
+ tempSignature.style.height = 'auto';
2533
+ tempSignature.style.overflow = 'visible';
2534
+ const textElem = tempSignature.querySelector('.text');
2535
+ if (textElem) textElem.style.wordBreak = 'break-word';
2536
+
2537
+ this.formatAndLoadContent(tempSignature)
2538
+ .then(() => {
2539
+ this.signature.className = tempSignature.className;
2540
+ this.signature.style.cssText = tempSignature.style.cssText;
2541
+ this.signature.innerHTML = tempSignature.innerHTML;
2542
+ this.updatePosition(this.signature, 198, 0);
2543
+ })
2544
+ .catch(error => {
2545
+ console.error(gettext("Error when applying setting to signature: "), error);
2546
+ });
2547
+ }
2548
+
2549
+ async formatAndLoadContent(element, content) {
2550
+ const imageContainer = element.querySelector('.image');
2551
+ const textContainer = element.querySelector('.text');
2552
+ if (imageContainer) imageContainer.innerHTML = '';
2553
+ if (textContainer) textContainer.innerHTML = '';
2554
+ try {
2555
+ // Here you can load the image or update the text of the signature
2556
+ // await this.loadSignatureImage(signatureImageURL, imageContainer);
2557
+ } catch (error) {
2558
+ console.error(gettext("Error loading content: "), error);
2559
+ }
2560
+ }
2561
+
2562
+ async createTemporarySignature(content) {
2563
+ let tempSignature = window.app.signature.cloneNode(true);
2564
+ tempSignature.id = 'temp_signature';
2565
+ tempSignature.style.position = 'absolute';
2566
+ tempSignature.style.visibility = 'hidden';
2567
+ let textElem = tempSignature.querySelector('.text');
2568
+ if (textElem) textElem.style.wordBreak = 'break-word';
2569
+ this.sub_canvas_container.appendChild(tempSignature);
2570
+ await this.formatAndLoadContent(tempSignature, content);
2571
+ return tempSignature;
2572
+ }
2573
+
2574
+ // If required, you can define a method for loading an image
2575
+ loadSignatureImage(signatureImage, imageContainer) {
2576
+ return new Promise((resolve, reject) => {
2577
+ if (!signatureImage) {
2578
+ resolve();
2579
+ return;
2580
+ }
2581
+ const img = new Image();
2582
+ img.src = signatureImage;
2583
+ img.alt = 'signature-image';
2584
+ img.onload = () => {
2585
+ if (imageContainer) imageContainer.appendChild(img);
2586
+ resolve();
2587
+ };
2588
+ img.onerror = () => {
2589
+ reject(new Error(gettext("Error loading image")));
2590
+ };
2591
+ });
2592
+ }
2593
+
2594
+ getDocumentSettings() {
2595
+ const displayScale = this.canvas.getBoundingClientRect().width / this.canvas.width;
2596
+ const xReal = this.signX / displayScale;
2597
+ const yReal = this.signY / displayScale;
2598
+ const wReal = this.signWidth / displayScale;
2599
+ const hReal = this.signHeight / displayScale;
2600
+
2601
+ const xPdf = xReal / this.scale;
2602
+ const yPdf = yReal / this.scale;
2603
+ const wPdf = wReal / this.scale;
2604
+ const hPdf = hReal / this.scale;
2605
+
2606
+ return {
2607
+ pageNumber: this.pageNum,
2608
+ signWidth: Math.round(wPdf),
2609
+ signHeight: Math.round(hPdf),
2610
+ signX: Math.round(xPdf),
2611
+ signY: Math.round(yPdf),
2612
+ };
2613
+ }
2614
+ }
2615
+
2616
+ ///////////////////////////////////////////////
2617
+ // Signature manager Digital Signature
2618
+ ///////////////////////////////////////////////
2619
+ class SignatureManager {
2620
+ constructor(input_id, container, url_ws, pdfvisor) {
2621
+ this.input_id=input_id;
2622
+ this.container = container;
2623
+ this.modal = new bootstrap.Modal(container.querySelector("#loading_sign"));
2624
+ this.firmador = new DocumentClient(container, container.getAttribute("data-widget-id"), this, url_ws, this.doc_instance);
2625
+ this.signerBtn = container.querySelector(".btn_signer");
2626
+ this.errorsContainer = container.querySelector(".errors_signer");
2627
+ this.refreshBtn = container.querySelector(".btn_signer_refresh");
2628
+ this.socketError = false;
2629
+ this.pdfvisor = pdfvisor;
2630
+
2631
+ this.initEvents();
2632
+ }
2633
+
2634
+ initEvents() {
2635
+ if (this.signerBtn) {
2636
+ this.signerBtn.addEventListener('click', () => this.sign());
2637
+ }
2638
+ if (this.refreshBtn) {
2639
+ this.refreshBtn.addEventListener('click', () => this.refresh());
2640
+ }
2641
+ }
2642
+
2643
+ startSign(doc_instance, logo_url = null) {
2644
+ if (this.socketError) {
2645
+ alertSimple(errorInterpreter(3), gettext("Error"), "error");
2646
+ return;
2647
+ }
2648
+
2649
+ this.doc_instance = doc_instance;
2650
+ this.logo_url = logo_url;
2651
+
2652
+ this.clearErrors();
2653
+
2654
+ this.firmador.start_sign(doc_instance, logo_url)
2655
+ }
2656
+
2657
+ refresh() {
2658
+ this.socketError = false;
2659
+ this.firmador.remotesigner.inicialize();
2660
+
2661
+ this.clearErrors();
2662
+ this.firmador.start_sign(this.doc_instance, this.logo_url);
2663
+ }
2664
+
2665
+ sign() {
2666
+ this.clearErrors();
2667
+ this.firmador.do_sign_remote();
2668
+ }
2669
+
2670
+ addError(errorCode) {
2671
+ let title = document.createElement('p');
2672
+ title.classList.add('mt-2', 'text-danger', 'mb-0');
2673
+ title.innerHTML = `<i class="fa fa-times-circle" aria-hidden="true"></i> <span class="fw-bold"> ${gettext("Errors")} </span>`;
2674
+ let errorElement = document.createElement('p');
2675
+ errorElement.classList.add('text-danger', 'mx-3', 'small');
2676
+ errorElement.innerHTML = errorInterpreter(errorCode);
2677
+ this.errorsContainer.appendChild(title);
2678
+ this.errorsContainer.appendChild(errorElement);
2679
+ }
2680
+
2681
+ clearErrors() {
2682
+ this.errorsContainer.innerHTML = '';
2683
+ }
2684
+
2685
+ reloadPage() {
2686
+ window.location.reload();
2687
+ }
2688
+
2689
+ showLoading() {
2690
+ this.modal.show();
2691
+ }
2692
+
2693
+ hideLoading() {
2694
+ setTimeout(() => {
2695
+ this.modal.hide();
2696
+ }, 500);
2697
+ }
2698
+ }
2699
+
2700
+ ///////////////////////////////////////////////
2701
+ // Socket Digital Signature
2702
+ ///////////////////////////////////////////////
2703
+ function responseManageTypeData(instance, err_json_fn, error_text_fn) {
2704
+ return function (response) {
2705
+ const contentType = response.headers.get("content-type");
2706
+ if (response.ok) {
2707
+ if (contentType && contentType.indexOf("application/json") !== -1) {
2708
+ return response.json();
2709
+ } else {
2710
+ return response.text();
2711
+ }
2712
+ } else {
2713
+ if (contentType && contentType.indexOf("application/json") !== -1) {
2714
+ response.json().then(data => err_json_fn(data));
2715
+ } else {
2716
+ if (response.status === 406) {
2717
+ // cierre de ventana para ingresar el PIN
2718
+ error_text_fn(errorInterpreter(4));
2719
+ } else {
2720
+ response.text().then(data => error_text_fn(data));
2721
+ }
2722
+ }
2723
+ }
2724
+ return Promise.reject(response);
2725
+ }
2726
+ }
2727
+
2728
+ function SocketManager(socket, signatureManager) {
2729
+ // If an error occurs during the connection
2730
+ socket.onerror = (event) => {
2731
+ // console.error("WebSocket error");
2732
+ signatureManager.hideLoading();
2733
+ alertSimple(errorInterpreter(3), gettext("Error"), "error");
2734
+ signatureManager.socketError = true;
2735
+ };
2736
+
2737
+ // If the connection is closed
2738
+ socket.onclose = (event) => {
2739
+ // console.warn("WebSocket cerrado");
2740
+ };
2741
+
2742
+ // If the connection is opened
2743
+ socket.onopen = (event) => {
2744
+ // console.log("WebSocket conectado");
2745
+ signatureManager.socket_error = false;
2746
+ };
2747
+ }
2748
+
2749
+ function callFetch(instance) {
2750
+ fetch(instance.url, {
2751
+ method: instance.type,
2752
+ body: instance.data,
2753
+ headers: {
2754
+ 'X-CSRFToken': getCookie('csrftoken'),
2755
+ 'Content-Type': 'application/json'
2756
+ },
2757
+ cache: 'no-cache', // do not use cache
2758
+ }).then(responseManageTypeData(instance, instance.error_json, instance.error_text))
2759
+ .then(data => instance.success(data))
2760
+ .catch(error => {
2761
+ instance.error(error);
2762
+ });
2763
+ }
2764
+
2765
+ function FirmadorLibreLocal(docmanager, signatureManager) {
2766
+ return {
2767
+ "cert_url": "http://localhost:3516/certificates",
2768
+ "sign_url": "http://localhost:3516/sign",
2769
+ "success_get_certificates": function (data) {
2770
+ docmanager.success_certificates(data);
2771
+ },
2772
+ "get_certificates": function () {
2773
+ let parent = this;
2774
+
2775
+ const instance = {
2776
+ url: this.cert_url,
2777
+ type: 'GET',
2778
+ data: null,
2779
+ success: function (data) {
2780
+ parent.success_get_certificates(data);
2781
+ },
2782
+ error_text: function (message) {
2783
+ // console.log(message);
2784
+ },
2785
+ error_json: function (error) {
2786
+ // console.log(error);
2787
+ },
2788
+ error: function (error) {
2789
+ // Unrecognized Firmador Libre
2790
+ if (String(error) === "TypeError: NetworkError when attempting to fetch resource.") {
2791
+ signatureManager.addError(1);
2792
+ }
2793
+ }
2794
+ }
2795
+ callFetch(instance);
2796
+ },
2797
+ "sign": function (data) {
2798
+ let json = JSON.stringify(data);
2799
+ let manager = docmanager;
2800
+
2801
+ const fetch_instance = {
2802
+ 'url': this.sign_url,
2803
+ 'type': 'POST',
2804
+ 'data': json,
2805
+ "success": function (data) {
2806
+ // if the result is different from a string, it is possible that there is an error.
2807
+ // console.log(data)
2808
+ if (typeof data !== 'string') { //prevent option call
2809
+ manager.local_done(data);
2810
+ }
2811
+
2812
+ },
2813
+ "error": function (error) {
2814
+ // console.log(error);
2815
+ signatureManager.hideLoading();
2816
+ if (typeof error === "object") {
2817
+ // close window for PIN entry
2818
+ if (error.status === 406 && error.statusText === "Not Acceptable") {
2819
+ alertSimple(errorInterpreter(4), gettext("Warning"), "warning");
2820
+ }
2821
+ }
2822
+ },
2823
+ "error_text": function (message) {
2824
+ console.error("error_text", message);
2825
+ signatureManager.hideLoading();
2826
+ },
2827
+ "error_json": function (error) {
2828
+ console.error("error_json", error);
2829
+ signatureManager.hideLoading();
2830
+ },
2831
+ };
2832
+
2833
+ callFetch(fetch_instance);
2834
+ }
2835
+ }
2836
+ }
2837
+
2838
+ function FirmadorLibreWS(docmanager, url, signatureManager) {
2839
+ var firmador = {
2840
+ "url": url,
2841
+ "websocket": null,
2842
+ "firmador_url": "http://localhost:3516",
2843
+ "trans_received": function (instance) {
2844
+ return function (event) {
2845
+ try {
2846
+ const data = JSON.parse(event.data);
2847
+ instance.receive_json(data);
2848
+ } catch (err) {
2849
+ console.error("Error al parsear mensaje WS:", err);
2850
+ }
2851
+ };
2852
+ },
2853
+ "receive_json": function (data) {
2854
+ // validate socket errors
2855
+ if (data.result === false && data.error) {
2856
+ signatureManager.hideLoading();
2857
+ if (typeof data.details === "string") {
2858
+ // connection issues with the Firmador Libre API
2859
+ if (data.details.includes("Connection refused")) {
2860
+ signatureManager.addError(3);
2861
+ } else {
2862
+ alertSimple(errorInterpreter(999), gettext("Error"), "error");
2863
+ }
2864
+
2865
+ } else if (data.code) {
2866
+ switch (data.code) {
2867
+ case 0:
2868
+ // when an unknown error occurs on the server
2869
+ alertSimple(errorInterpreter(0), gettext("Error"), "error");
2870
+ break;
2871
+ case 6:
2872
+ // when the new signature field position overlaps with an existing signature
2873
+ alertSimple(errorInterpreter(6), gettext("Error"), "error");
2874
+ break;
2875
+ case 7:
2876
+ // when the signature field is outside the page boundaries
2877
+ alertSimple(errorInterpreter(7), gettext("Error"), "error");
2878
+ break;
2879
+ case 8:
2880
+ // when an error occurs due to an incompatible library
2881
+ alertSimple(errorInterpreter(8), gettext("Error"), "error");
2882
+ break;
2883
+ case 9:
2884
+ // when an error occurs due to an unavailable cryptographic provider
2885
+ alertSimple(errorInterpreter(9), gettext("Error"), "error");
2886
+ break;
2887
+ case 10:
2888
+ // when an error occurs due to the signing algorithm
2889
+ alertSimple(errorInterpreter(10), gettext("Error"), "error");
2890
+ break;
2891
+ case 11:
2892
+ // when there are errors serializing data
2893
+ alertSimple(errorInterpreter(11), gettext("Error"), "error");
2894
+ break;
2895
+ case 12:
2896
+ // when an error occurs due to a signing service timeout
2897
+ alertSimple(errorInterpreter(12), gettext("Error"), "error");
2898
+ break;
2899
+ // when an unknown error occurs
2900
+ alertSimple(errorInterpreter(999), gettext("Error"), "error");
2901
+ break;
2902
+ }
2903
+ }
2904
+ }else{
2905
+
2906
+ if(data.hasOwnProperty('tobesigned')){
2907
+ docmanager.do_sign_local(data);
2908
+ }else{
2909
+ docmanager.remote_done(data)
2910
+ }
2911
+
2912
+ }
2913
+ },
2914
+
2915
+ "inicialize": function () {
2916
+ this.websocket = new WebSocket(url);
2917
+ SocketManager(this.websocket, signatureManager);
2918
+ this.websocket.onmessage = this.trans_received(this);
2919
+ },
2920
+ "local_done": function (data) {
2921
+ // console.log("local_done", data);
2922
+ },
2923
+ "sign": function (data) {
2924
+ data["action"] = "initial_signature";
2925
+ if (data.card !== undefined) {
2926
+ this.websocket.send(JSON.stringify(data));
2927
+ signatureManager.showLoading();
2928
+ } else {
2929
+ alertSimple(errorInterpreter(2), gettext("Error"), "error");
2930
+ signatureManager.addError(2);
2931
+ }
2932
+ },
2933
+ "complete_sign": function (data) {
2934
+ data["action"] = "complete_signature";
2935
+
2936
+ try {
2937
+ this.websocket.send(JSON.stringify(data));
2938
+ } catch (e) {
2939
+ // console.error("Error de comunicación WS");
2940
+ signatureManager.hideLoading();
2941
+ alertFunction(errorInterpreter(3), gettext("Error"), "error", false, closeModalSignature);
2942
+ }
2943
+ },
2944
+ };
2945
+ firmador.inicialize();
2946
+ return firmador;
2947
+ }
2948
+
2949
+ function DocumentClient(container, widgetId, signatureManager, url_ws) {
2950
+ const docmanager = {
2951
+ "widgetId": widgetId,
2952
+ "container": container,
2953
+ "signatureManager": signatureManager,
2954
+ "remotesigner": null,
2955
+ "localsigner": null,
2956
+ "certificates": null,
2957
+ "doc_instance": null,
2958
+ "logo_url": null,
2959
+
2960
+ "start_sign": function (doc_instance, logo_url = null) {
2961
+ this.doc_instance = doc_instance;
2962
+ this.logo_url = logo_url;
2963
+ this.localsigner.get_certificates();
2964
+ this.signatureManager.clearErrors();
2965
+ },
2966
+ "success_certificates": function (data) {
2967
+
2968
+ if (data.length > 0) {
2969
+ container.querySelector("#container_select_card").classList.remove("d-none");
2970
+ container.querySelector("#container_select_card_tem").classList.add("d-none");
2971
+
2972
+ let select_card = container.querySelector(".select_card");
2973
+
2974
+ if (!select_card) {
2975
+ console.error(`Select not found for widget ${widgetId}`);
2976
+ return;
2977
+ }
2978
+ select_card.innerHTML = "";
2979
+ this.certificates = {};
2980
+
2981
+ data.forEach((element) => {
2982
+ this.certificates[element.tokenSerialNumber] = element;
2983
+ let start_token = element.tokenSerialNumber.substring(0, 4);
2984
+ let newOption = new Option(
2985
+ `${start_token} ${element.commonName}`,
2986
+ element.tokenSerialNumber,
2987
+ false, false
2988
+ );
2989
+ select_card.appendChild(newOption);
2990
+ });
2991
+ } else {
2992
+ container.querySelector("#container_select_card").classList.add("d-none");
2993
+ container.querySelector("#container_select_card_tem").classList.remove("d-none");
2994
+ this.signatureManager.addError(2);
2995
+ }
2996
+ },
2997
+
2998
+ "do_sign_remote": function () {
2999
+ let select = container.querySelector(".select_card");
3000
+ let selected_card = select ? select.value : null;
3001
+
3002
+ if (selected_card && this.certificates) {
3003
+ let data = {
3004
+ 'logo_url': this.logo_url,
3005
+ 'instance': this.doc_instance,
3006
+ 'card': this.certificates[selected_card],
3007
+ "docsettings": window.pdfSignatureComponents[widgetId].getDocumentSettings()
3008
+ };
3009
+ this.remotesigner.sign(data);
3010
+ } else if (!selected_card && !this.certificates) {
3011
+ signatureManager.hideLoading();
3012
+ alertSimple(errorInterpreter(1), gettext("Error"), "error");
3013
+ this.signatureManager.addError(1);
3014
+ } else if (!selected_card && this.certificates) {
3015
+ signatureManager.hideLoading();
3016
+ alertSimple(errorInterpreter(2), gettext("Error"), "error");
3017
+ this.signatureManager.addError(2);
3018
+ }
3019
+
3020
+ },
3021
+ "do_sign_local": function (data) {
3022
+ this.localsigner.sign(data);
3023
+ },
3024
+ "local_done": function (data) {
3025
+ data['instance'] = this.doc_instance;
3026
+ data['logo_url'] = this.logo_url;
3027
+ this.remotesigner.complete_sign(data);
3028
+ },
3029
+ "remote_done": function (data) {
3030
+ if(data.result !== null ){
3031
+ const l = btoa(JSON.stringify({'token': data.result}));
3032
+ this.signatureManager.doc_instance['value'] =l;
3033
+ this.signatureManager.pdfvisor.urls['renderattr']="value="+l;
3034
+ document.getElementById(this.signatureManager.input_id).value=l;
3035
+ this.signatureManager.pdfvisor.initPDFViewer();
3036
+ signatureManager.hideLoading();
3037
+ alertFunction(
3038
+ gettext("The signing was successfully completed."),
3039
+ gettext("Success"),
3040
+ "success", false, function(){}
3041
+ );
3042
+ }
3043
+ },
3044
+
3045
+ };
3046
+
3047
+ docmanager["remotesigner"] = new FirmadorLibreWS(docmanager, url_ws, signatureManager);
3048
+ docmanager["localsigner"] = new FirmadorLibreLocal(docmanager, signatureManager);
3049
+
3050
+ return docmanager;
3051
+ }
3052
+
3053
+ ///////////////////////////////////////////////
3054
+ // Manage Errors Digital Signature
3055
+ ///////////////////////////////////////////////
3056
+ function errorInterpreter(error) {
3057
+
3058
+ let textError = "";
3059
+
3060
+ switch (error) {
3061
+ case 0:
3062
+ // when an uncontrolled error occurs in the signing server
3063
+ textError = gettext("An error has occurred in the internal server of the uncontrolled 'Firmador Libre'.");
3064
+ break;
3065
+ case 1:
3066
+ // when the 'Firmador Libre' application fails to open
3067
+ textError = gettext("Make sure to start the 'Firmador Libre' application. If it is already running, please press the reload button.");
3068
+ break;
3069
+ case 2:
3070
+ // when there is no card connected
3071
+ textError = gettext("There is no card connected to the device. Please press the reload button and connect your card.");
3072
+ break;
3073
+ case 3:
3074
+ // when the signing service is not functioning
3075
+ textError = gettext("The internal signature service does not work. Please contact the support.");
3076
+ break;
3077
+ case 4:
3078
+ // when the modal is closed but the PIN entry window remains open
3079
+ textError = gettext("Authentication failed because the PIN entry window was detected closed, please try again.");
3080
+ break;
3081
+ case 5:
3082
+ // when the card is disconnected
3083
+ //! This error should be resolved within Firmador Libre
3084
+ textError = gettext("The device was disconnected, possibly the window was closed for signature. Please close the window for the authentication PIN.");
3085
+ break;
3086
+ case 6:
3087
+ // when the new signature field position overlaps with an existing signature
3088
+ textError = gettext("The new signature field position overlaps with an existing signature.");
3089
+ break;
3090
+ case 7:
3091
+ // when the signature is positioned outside the page boundaries
3092
+ textError = gettext("The new signature field position is outside the page dimensions.");
3093
+ break;
3094
+ case 8:
3095
+ // when an error occurs due to an incompatible library
3096
+ textError = gettext("The version of one or more libraries is incompatible.");
3097
+ break;
3098
+ case 9:
3099
+ // when an error occurs due to an unavailable cryptographic provider
3100
+ textError = gettext("The cryptographic provider is not available.");
3101
+ break;
3102
+ case 10:
3103
+ // when an error occurs due to the signing algorithm
3104
+ textError = gettext("The signing algorithm is not available.");
3105
+ break;
3106
+ case 11:
3107
+ // when errors occur while serializing data
3108
+ textError = gettext("Errors have been encountered in the data to be sent to the 'Free Signer'. Please press the reload button and try again.");
3109
+ break;
3110
+ case 12:
3111
+ // when an error occurs due to a signing service timeout
3112
+ textError = gettext("The request to the signing service timed out. Please, press the reload button and try again.");
3113
+ break;
3114
+ default:
3115
+ // when an unknown error occurs
3116
+ textError = gettext("We're sorry, an unexpected error occurred. Please, press the reload button and try again.");
3117
+ }
3118
+
3119
+ return textError;
3120
+ }
3121
+
3122
+
3123
+ ///////////////////////////////////////////////
3124
+ // Alerts Digital Signature
3125
+ ///////////////////////////////////////////////
3126
+ function alertSimple(text, title = "Error", icon = "error") {
3127
+ Swal.fire({
3128
+ icon: icon,
3129
+ title: title,
3130
+ text: text,
3131
+ confirmButtonText: gettext("Accept")
3132
+ });
3133
+ }
3134
+
3135
+ function alertFunction(text, title = "Error", icon = "error", cancelButton = false,
3136
+ callback = () => {
3137
+ }) {
3138
+ Swal.fire({
3139
+ icon: icon,
3140
+ title: title,
3141
+ text: text,
3142
+ confirmButtonText: gettext("Accept"),
3143
+ showCancelButton: cancelButton,
3144
+ cancelButtonText: gettext("Cancel"),
3145
+ confirmButtonColor: '#3085d6',
3146
+ cancelButtonColor: '#d33',
3147
+ }).then((result) => {
3148
+ if (result.isConfirmed) {
3149
+ callback();
3150
+ }
3151
+ });
3152
+ }
3153
+
3154
+ ///////////////////////////////////////////////
3155
+ // End widgets digital signature
3156
+ ///////////////////////////////////////////////
3157
+
3158
+
3159
+ ////////////////////////////////////////////////////////////////
3160
+ // copy action
3161
+ ////////////////////////////////////////////////////////////////
3162
+ document.addEventListener("DOMContentLoaded", function () {
3163
+
3164
+ if (document.getElementById("copy-command-line")) {
3165
+ document.getElementById("copy-command-line").addEventListener("click", function () {
3166
+ let commandText = document.getElementById("command-line").innerText.trim();
3167
+
3168
+ navigator.clipboard.writeText(commandText)
3169
+ .then(() => {
3170
+ let text = document.getElementById("text-copy")
3171
+ text.classList.remove("d-none")
3172
+ setTimeout(() => {
3173
+ text.classList.add("d-none")
3174
+ }, 1500)
3175
+ })
3176
+ .catch(err => {
3177
+ console.error("Error al copiar el texto: ", err);
3178
+ });
3179
+ });
3180
+
3181
+ document.getElementById("show-command-line").addEventListener("click", () => {
3182
+ let container = document.getElementById("container-command-line")
3183
+
3184
+ if (container.classList.contains("d-none")) {
3185
+ container.classList.remove("d-none");
3186
+ } else {
3187
+ container.classList.add("d-none");
3188
+ }
3189
+ })
3190
+ }
3191
+ });
3192
+
3193
+ ////////////////////////////////////////////////////////////////
3194
+ // End copy action
3195
+ ////////////////////////////////////////////////////////////////
3196
+
3197
+
2251
3198
  class CardList {
2252
3199
  constructor(containerId, apiUrl, actions={}) {
2253
3200
  this.container = document.getElementById(containerId);