From 424e68c57c93196804875fe67d22e01781912f84 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Mon, 16 Mar 2026 17:08:23 +0700 Subject: [PATCH] Refactor: Consolidate image field logic into `PDFIframe.prototype.refreshSignItemsForPage` to avoid overriding `enableCustom` methods and improve patching strategy. --- static/src/js/sign_image_upload.js | 218 ++++++++++++----------------- 1 file changed, 93 insertions(+), 125 deletions(-) diff --git a/static/src/js/sign_image_upload.js b/static/src/js/sign_image_upload.js index a47c1b4..1714ffc 100755 --- a/static/src/js/sign_image_upload.js +++ b/static/src/js/sign_image_upload.js @@ -4,7 +4,7 @@ import { patch } from "@web/core/utils/patch"; import { PDFIframe } from "@sign/components/sign_request/PDF_iframe"; import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; -console.log("Sign Image Field: Initializing diagnostic patches..."); +console.log("Sign Image Field: Re-evaluating patch strategy without overriding enableCustom for base classes"); /** * Diagnostic Bridge Restoration @@ -16,7 +16,11 @@ const restoreBridge = (instance, caller) => { const iframe = iwin.frameElement; if (iframe && !iframe.odoo_iframe_instance) { - console.log(`[Diagnostic] Restoring bridge on iframe element from ${caller}`); + // Only log once per frame to reduce noise + if (!iframe._odoo_bridge_logged) { + console.log(`[Diagnostic] Restoring bridge on iframe element from ${caller}`); + iframe._odoo_bridge_logged = true; + } iframe.odoo_iframe_instance = instance; } @@ -92,143 +96,107 @@ window.diagnosticForceShowFields = () => { hiddenItems.forEach(el => el.classList.remove('d-none')); }; -const hookCheckSignItemsCompletion = (instance) => { - console.log("[Diagnostic] checkSignItemsCompletion hooked. Total signItems mapped:"); - let totalItems = 0; - for (const page in instance.signItems) { - const items = instance.signItems[page]; - totalItems += Object.keys(items).length; - console.log(` Page ${page}: ${Object.keys(items).length} items`); - } - console.log(`[Diagnostic] Total items managed by PDFIframe: ${totalItems}`); -}; +const processedItems = new WeakSet(); -// 1. Patch PDFIframe (ROOT) +// Safest Patching Strategy: Only patch `PDFIframe.prototype.refreshSignItemsForPage` +// This avoids ANY issues with `enableCustom` halting mid-execution. patch(PDFIframe.prototype, { - enableCustom(signItem) { - restoreBridge(this, 'PDFIframe.enableCustom'); - - if (typeof super.enableCustom === 'function') { - super.enableCustom(...arguments); - } - - if (signItem && signItem.data && signItem.data.type === 'image' && signItem.el) { - setupLazyObserver(signItem.el); - } - }, - refreshSignItemsForPage(page) { - console.log(`[Diagnostic] refreshSignItemsForPage called for page ${page}`); + // Restore bridge right before items are evaluated restoreBridge(this, 'PDFIframe.refreshSignItemsForPage'); - try { - return super.refreshSignItemsForPage(...arguments); - } catch (error) { - console.error("[Diagnostic] Error inside refreshSignItemsForPage:", error); - } - }, + // Execute original first so elements get un-hidden (d-none removed) + const result = super.refreshSignItemsForPage(...arguments); + + // Now find all image elements and apply our custom observer + if (this.signItems && this.signItems[page]) { + for (const id in this.signItems[page]) { + const item = this.signItems[page][id]; + if (item && item.data && item.data.type === 'image' && item.el && !processedItems.has(item.el)) { + processedItems.add(item.el); + setupLazyObserver(item.el); + + // Add upload logic for active signer + if (!this.readonly && item.data.responsible > 0 && item.data.responsible === this.currentRole) { + const el = item.el; + const data = item.data; + el.setAttribute('data-type', 'image'); + const input = el.querySelector('.o_sign_image_upload_input'); + if (input && !el._hasImgListener) { + el.addEventListener('click', (e) => { + if (e.target !== input) input.click(); + }); + + input.addEventListener('change', (e) => { + const file = e.target.files[0]; + if (!file) return; - refreshSignItems(page = false) { - console.log(`[Diagnostic] refreshSignItems called (page: ${page})`); - restoreBridge(this, 'PDFIframe.refreshSignItems'); - return super.refreshSignItems(...arguments); + const reader = new FileReader(); + reader.onload = (readerEvent) => { + const img = new Image(); + img.onload = () => { + const canvas = document.createElement('canvas'); + let width = img.width; + let height = img.height; + const MAX_SIZE = 1080; + if (width > height) { + if (width > MAX_SIZE) { + height *= MAX_SIZE / width; + width = MAX_SIZE; + } + } else if (height > MAX_SIZE) { + width *= MAX_SIZE / height; + height = MAX_SIZE; + } + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0, width, height); + + const compressedDataUrl = canvas.toDataURL('image/jpeg', 0.8); + data.value = compressedDataUrl; + el.dataset.value = compressedDataUrl; + + const placeholder = el.querySelector('.o_placeholder'); + if (placeholder) placeholder.remove(); + + let existingImg = el.querySelector('img'); + if (!existingImg) { + existingImg = document.createElement('img'); + Object.assign(existingImg.style, { + maxWidth: '100%', + maxHeight: '100%', + objectFit: 'contain', + opacity: 1 + }); + const body = el.querySelector('.sign_item_body') || el; + body.appendChild(existingImg); + } + existingImg.src = compressedDataUrl; + el.classList.remove('o_sign_image_loading_container'); + + if (this.handleInput) this.handleInput(); + }; + img.src = readerEvent.target.result; + }; + reader.readAsDataURL(file); + }); + el._hasImgListener = true; + } + } + } + } + } + + return result; } }); -// 2. Patch SignablePDFIframe (Signer subclass) patch(SignablePDFIframe.prototype, { - enableCustom(signItem) { - restoreBridge(this, 'SignablePDFIframe.enableCustom'); - - if (signItem && signItem.data && signItem.data.type === 'image' && signItem.el) { - setupLazyObserver(signItem.el); - } - - super.enableCustom(...arguments); - - if (signItem && signItem.data && signItem.data.type === 'image' && signItem.el) { - const el = signItem.el; - const data = signItem.data; - - if (this.readonly || (data.responsible > 0 && data.responsible !== this.currentRole)) { - return; - } - - el.setAttribute('data-type', 'image'); - const input = el.querySelector('.o_sign_image_upload_input'); - if (input && !el._hasImgListener) { - el.addEventListener('click', (e) => { - if (e.target !== input) input.click(); - }); - - input.addEventListener('change', (e) => { - const file = e.target.files[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = (readerEvent) => { - const img = new Image(); - img.onload = () => { - const canvas = document.createElement('canvas'); - let width = img.width; - let height = img.height; - const MAX_SIZE = 1080; - if (width > height) { - if (width > MAX_SIZE) { - height *= MAX_SIZE / width; - width = MAX_SIZE; - } - } else if (height > MAX_SIZE) { - width *= MAX_SIZE / height; - height = MAX_SIZE; - } - canvas.width = width; - canvas.height = height; - const ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0, width, height); - - const compressedDataUrl = canvas.toDataURL('image/jpeg', 0.8); - data.value = compressedDataUrl; - el.dataset.value = compressedDataUrl; - - const placeholder = el.querySelector('.o_placeholder'); - if (placeholder) placeholder.remove(); - - let existingImg = el.querySelector('img'); - if (!existingImg) { - existingImg = document.createElement('img'); - Object.assign(existingImg.style, { - maxWidth: '100%', - maxHeight: '100%', - objectFit: 'contain', - opacity: 1 - }); - const body = el.querySelector('.sign_item_body') || el; - body.appendChild(existingImg); - } - existingImg.src = compressedDataUrl; - el.classList.remove('o_sign_image_loading_container'); - - if (this.handleInput) this.handleInput(); - }; - img.src = readerEvent.target.result; - }; - reader.readAsDataURL(file); - }); - el._hasImgListener = true; - } - } - }, - getSignatureValueFromElement(item) { if (item.data.type === 'image') { return item.el.dataset.value || item.data.value || false; } return super.getSignatureValueFromElement(...arguments); - }, - - checkSignItemsCompletion() { - hookCheckSignItemsCompletion(this); - return super.checkSignItemsCompletion(...arguments); } });