commit b70a7bb1318d28ac6686a3f8427eb9a3e237a969
parent 32adf5bd5b672fd26e0fe8bbbbb161c39bbabdae
Author: Sebastian <sebasjm@gmail.com>
Date: Tue, 18 Nov 2025 11:24:13 -0300
fix #9772
Diffstat:
3 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/index.dev.html b/packages/merchant-backoffice-ui/src/index.dev.html
@@ -27,7 +27,7 @@
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="taler-support" content="uri,api" />
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; connect-src *; img-src 'self' data:; style-src 'self';font-src 'self';base-uri 'self';form-action 'self'">
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src blob:; script-src 'self'; connect-src *; img-src 'self' data:; style-src 'self';font-src 'self';base-uri 'self';form-action 'self'">
<link
rel="icon"
diff --git a/packages/merchant-backoffice-ui/src/index.prod.html b/packages/merchant-backoffice-ui/src/index.prod.html
@@ -27,7 +27,7 @@
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="taler-support" content="uri,api" />
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; connect-src *; img-src 'self' data:; style-src 'self';font-src 'self';base-uri 'self';form-action 'self'">
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src blob:; script-src 'self'; connect-src *; img-src 'self' data:; style-src 'self';font-src 'self';base-uri 'self';form-action 'self'">
<link
rel="icon"
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
@@ -28,6 +28,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
import { QR } from "../../../../components/exception/QR.js";
import { useSessionContext } from "../../../../context/session.js";
+import { useRef } from "preact/hooks";
// type Entity = TalerMerchantApi.UsingTemplateDetails;
@@ -49,10 +50,11 @@ export function QrPage({ id: templateId, onBack }: Props): VNode {
// FIXME!
//templateParams: {},
});
+ const printThis = useRef<HTMLElement>(null);
return (
<div>
- <section id="printThis">
+ <section id="printThis" ref={printThis}>
<QR text={payTemplateUri} />
<pre style={{ textAlign: "center" }}>
<a target="_blank" rel="noreferrer" href={payTemplateUri}>
@@ -67,13 +69,14 @@ export function QrPage({ id: templateId, onBack }: Props): VNode {
<div class="column is-four-fifths">
<div class="buttons is-right mt-5">
{onBack && (
- <button type="button"class="button" onClick={onBack}>
+ <button type="button" class="button" onClick={onBack}>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
- <button type="button"
+ <button
+ type="button"
class="button is-info"
- onClick={() => saveAsPDF(templateId)}
+ onClick={() => saveAsPDF(templateId, printThis.current!)}
>
<i18n.Translate>Print</i18n.Translate>
</button>
@@ -86,13 +89,8 @@ export function QrPage({ id: templateId, onBack }: Props): VNode {
);
}
-function saveAsPDF(name: string): void {
- // TODO: Look into using media queries in the current page, to print the current page, instead of opening a new window
-
- const divContents = document.getElementById("printThis");
- if (!divContents) return;
-
- let dom = `<!DOCTYPE html>
+function saveAsPDF(name: string, divContents: HTMLElement): void {
+ const dom = `<!DOCTYPE html>
<html>
<head>
<title>Order template for ${name}</title>
@@ -106,12 +104,20 @@ function saveAsPDF(name: string): void {
${divContents.outerHTML}
</body>
</html>`;
- const blobUrl = URL.createObjectURL(new Blob([dom]));
- const printWindow = window.open(blobUrl, "", "height=400,width=800");
- if (!printWindow) return;
- printWindow.addEventListener("load", () => {
- printWindow.print();
- printWindow.close();
+ const blobUrl = URL.createObjectURL(new Blob([dom], { type: "text/html" }));
+ const f = document.createElement("iframe");
+ f.src = blobUrl;
+ f.style.display = "none";
+ function onComplete() {
+ document.body.removeChild(f);
URL.revokeObjectURL(blobUrl);
- });
+ }
+ f.onload = () => {
+ if (!f.contentWindow) return;
+ f.contentWindow.onbeforeunload = onComplete;
+ f.contentWindow.onafterprint = onComplete;
+ f.contentWindow.print();
+ };
+
+ document.body.appendChild(f);
}