From bf06e5be06b976817dc3cb0ab4c7df41e49fd31b Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Mon, 16 Mar 2026 17:15:11 +0700 Subject: [PATCH] fix: Ensure sign items are always visible by injecting CSS overrides and explicitly removing the d-none class. --- static/src/js/sign_image_upload.js | 196 +++++++++++++++-------------- 1 file changed, 105 insertions(+), 91 deletions(-) diff --git a/static/src/js/sign_image_upload.js b/static/src/js/sign_image_upload.js index 1714ffc..169c210 100755 --- a/static/src/js/sign_image_upload.js +++ b/static/src/js/sign_image_upload.js @@ -4,19 +4,20 @@ 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: Re-evaluating patch strategy without overriding enableCustom for base classes"); +console.log("Sign Image Field: Initializing CSS-forced visibility patches..."); /** - * Diagnostic Bridge Restoration + * Diagnostic Bridge Restoration & Universal Force-Show + * We re-connect the bridge but ALSO forcefully inject CSS to ensure elements are never hidden. */ -const restoreBridge = (instance, caller) => { +const restoreBridgeAndForceShow = (instance, caller) => { if (!instance || !instance.root || !instance.root.defaultView) return; const iwin = instance.root.defaultView; const iframe = iwin.frameElement; + const isDocReady = iwin.document && iwin.document.head; if (iframe && !iframe.odoo_iframe_instance) { - // 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; @@ -28,13 +29,27 @@ const restoreBridge = (instance, caller) => { iwin.odoo_iframe_instance = instance; } - if (iwin.document && !iwin.document.odoo_iframe_instance) { - iwin.document.odoo_iframe_instance = instance; - } - if (iwin.PDFViewerApplication && !iwin.PDFViewerApplication.odoo_iframe_instance) { iwin.PDFViewerApplication.odoo_iframe_instance = instance; } + + // ULTIMATE FALLBACK: Inject CSS to override Odoo's broken d-none toggler + if (isDocReady && !iwin.document._odoo_force_show_injected) { + console.log(`[Diagnostic] Injecting permanent override CSS from ${caller}`); + const style = iwin.document.createElement('style'); + style.type = 'text/css'; + // Force display block and override any inline or class-based display:none + style.innerHTML = ` + .o_sign_sign_item.d-none { + display: block !important; + } + .o_sign_sign_item[style*="display: none"] { + display: block !important; + } + `; + iwin.document.head.appendChild(style); + iwin.document._odoo_force_show_injected = true; + } }; /** @@ -88,107 +103,106 @@ const setupLazyObserver = (el) => { } }; -window.diagnosticForceShowFields = () => { - const hiddenItems = document.querySelectorAll('iframe.o_sign_pdf_iframe')?.length ? - document.querySelector('iframe.o_sign_pdf_iframe').contentDocument.querySelectorAll('.o_sign_sign_item.d-none') : []; - - console.log(`[Diagnostic] Forcing show on ${hiddenItems.length} hidden items`); - hiddenItems.forEach(el => el.classList.remove('d-none')); -}; - const processedItems = new WeakSet(); -// Safest Patching Strategy: Only patch `PDFIframe.prototype.refreshSignItemsForPage` -// This avoids ANY issues with `enableCustom` halting mid-execution. +// Patching Strategy: Use refreshSignItemsForPage as the hook point because it's +// called every time page renders, giving us a reliable hook to inject CSS patch(PDFIframe.prototype, { refreshSignItemsForPage(page) { - // Restore bridge right before items are evaluated - restoreBridge(this, 'PDFIframe.refreshSignItemsForPage'); + restoreBridgeAndForceShow(this, 'PDFIframe.refreshSignItemsForPage'); - // 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); + try { + const result = super.refreshSignItemsForPage(...arguments); + + // Image tracking + if (this.signItems && this.signItems[page]) { + for (const id in this.signItems[page]) { + const item = this.signItems[page][id]; + // Also forcefully remove d-none via JS just in case + if (item && item.el) { + item.el.classList.remove('d-none'); + } - // 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; + 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; - 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; + 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; } - } 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); + 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 compressedDataUrl = canvas.toDataURL('image/jpeg', 0.8); + data.value = compressedDataUrl; + el.dataset.value = compressedDataUrl; - const placeholder = el.querySelector('.o_placeholder'); - if (placeholder) placeholder.remove(); + 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'); + 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(); + if (this.handleInput) this.handleInput(); + }; + img.src = readerEvent.target.result; }; - img.src = readerEvent.target.result; - }; - reader.readAsDataURL(file); - }); - el._hasImgListener = true; + reader.readAsDataURL(file); + }); + el._hasImgListener = true; + } } } } } + return result; + } catch (error) { + console.error("[Diagnostic] Error inside refreshSignItemsForPage:", error); + // Even if super fails, don't crash our script } - - return result; } });