309 lines
9.6 KiB
JavaScript
309 lines
9.6 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { registry } from "@web/core/registry";
|
|
import { _t } from "@web/core/l10n/translation";
|
|
import { session } from "@web/session";
|
|
import { rpc } from "@web/core/network/rpc";
|
|
import { user } from "@web/core/user";
|
|
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
|
|
import { Dialog } from "@web/core/dialog/dialog";
|
|
|
|
|
|
/**
|
|
* Fire-and-forget audit log for PDF preview/print/download actions.
|
|
*/
|
|
async function logPreviewAction(reportName, actionType, recordIds, modelName) {
|
|
try {
|
|
await rpc("/fusion_pdf_preview/log_action", {
|
|
report_name: reportName,
|
|
action_type: actionType,
|
|
record_ids: recordIds || '',
|
|
model_name: modelName || '',
|
|
});
|
|
} catch (err) {
|
|
console.warn("Failed to log PDF preview action:", err);
|
|
}
|
|
}
|
|
|
|
|
|
export class PDFViewerDialog extends Component {
|
|
setup() {
|
|
this.state = useState({
|
|
isLoading: true,
|
|
viewerUrl: this.getViewerUrl(),
|
|
isMaximized: false
|
|
});
|
|
|
|
this._onKeyDown = this._onKeyDown.bind(this);
|
|
|
|
onMounted(() => {
|
|
document.addEventListener('keydown', this._onKeyDown);
|
|
});
|
|
|
|
onWillUnmount(() => {
|
|
document.removeEventListener('keydown', this._onKeyDown);
|
|
});
|
|
}
|
|
|
|
getViewerUrl() {
|
|
const baseUrl = '/fusion_pdf_preview/static/lib/pdfjs/web/viewer.html';
|
|
return `${baseUrl}?file=${this.props.url}`;
|
|
}
|
|
|
|
onIframeLoad() {
|
|
this.state.isLoading = false;
|
|
}
|
|
|
|
toggle() {
|
|
this.state.isMaximized = !this.state.isMaximized;
|
|
}
|
|
|
|
getDialogSize() {
|
|
if (this.state.isMaximized) {
|
|
return 'fullscreen';
|
|
}
|
|
return 'xl';
|
|
}
|
|
|
|
getFrameStyle() {
|
|
if (this.state.isMaximized) {
|
|
return 'height: calc(98vh - 141px) !important;';
|
|
}
|
|
return 'height: calc(90vh - 100px) !important;';
|
|
}
|
|
|
|
downloadPDF() {
|
|
const link = document.createElement('a');
|
|
link.href = this.props.url;
|
|
link.download = this.props.title ? `${this.props.title}.pdf` : 'document.pdf';
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
|
|
if (this.props.reportName) {
|
|
logPreviewAction(this.props.reportName, 'download',
|
|
this.props.recordIds, this.props.modelName);
|
|
}
|
|
}
|
|
|
|
openInNewTab() {
|
|
window.open(this.props.url, '_blank');
|
|
}
|
|
|
|
printPDF() {
|
|
try {
|
|
const iframe = this.el?.querySelector('iframe');
|
|
if (iframe && iframe.contentWindow) {
|
|
iframe.contentWindow.print();
|
|
}
|
|
} catch (err) {
|
|
window.open(this.props.url, '_blank');
|
|
}
|
|
|
|
if (this.props.reportName) {
|
|
logPreviewAction(this.props.reportName, 'print',
|
|
this.props.recordIds, this.props.modelName);
|
|
}
|
|
}
|
|
|
|
_onKeyDown(ev) {
|
|
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
|
const ctrlOrCmd = isMac ? ev.metaKey : ev.ctrlKey;
|
|
|
|
// F key (no modifiers) - toggle fullscreen
|
|
if ((ev.key === 'f' || ev.key === 'F') && !ctrlOrCmd && !ev.altKey && !ev.shiftKey) {
|
|
const activeEl = document.activeElement;
|
|
if (activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.isContentEditable)) {
|
|
return;
|
|
}
|
|
ev.preventDefault();
|
|
this.toggle();
|
|
}
|
|
|
|
// Ctrl+P / Cmd+P - print
|
|
if (ctrlOrCmd && (ev.key === 'p' || ev.key === 'P')) {
|
|
ev.preventDefault();
|
|
this.printPDF();
|
|
}
|
|
|
|
// Ctrl+D / Cmd+D - download
|
|
if (ctrlOrCmd && (ev.key === 'd' || ev.key === 'D')) {
|
|
ev.preventDefault();
|
|
this.downloadPDF();
|
|
}
|
|
}
|
|
}
|
|
|
|
PDFViewerDialog.template = 'fusion_pdf_preview.PDFViewerDialog';
|
|
PDFViewerDialog.components = { Dialog };
|
|
|
|
registry.category("dialog").add("PDFViewerDialog", PDFViewerDialog);
|
|
|
|
|
|
export function openPDFViewer(env, url, title = "PDF Document", meta = {}) {
|
|
const dialog = env.services.dialog;
|
|
return dialog.add(PDFViewerDialog, {
|
|
url: url,
|
|
title: title,
|
|
reportName: meta.reportName || '',
|
|
recordIds: meta.recordIds || '',
|
|
modelName: meta.modelName || '',
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function to handle automatic printing
|
|
* @param {string} url - URL of the PDF to print
|
|
* @param {Object} env - Environment object for notifications
|
|
*/
|
|
function handleAutomaticPrinting(url, env) {
|
|
const printFrame = document.createElement('iframe');
|
|
printFrame.style.display = 'none';
|
|
printFrame.src = url;
|
|
|
|
printFrame.onload = function() {
|
|
try {
|
|
printFrame.contentWindow.print();
|
|
} catch (err) {
|
|
env.services.notification.add(
|
|
_t("Failed to print automatically. Please check your browser settings."),
|
|
{
|
|
type: 'warning',
|
|
sticky: true,
|
|
title: _t("Printing Error"),
|
|
}
|
|
);
|
|
document.body.removeChild(printFrame);
|
|
}
|
|
};
|
|
|
|
const cleanup = () => {
|
|
if (printFrame.parentNode) {
|
|
document.body.removeChild(printFrame);
|
|
}
|
|
window.removeEventListener('focus', cleanup);
|
|
};
|
|
|
|
window.addEventListener('focus', cleanup);
|
|
|
|
document.body.appendChild(printFrame);
|
|
}
|
|
|
|
|
|
/**
|
|
* Generates the report url given a report action.
|
|
*
|
|
* @private
|
|
* @param {ReportAction} action
|
|
* @param {Object} env
|
|
* @param {string} filename
|
|
* @returns {string}
|
|
*/
|
|
function _getReportUrl(action, env, filename) {
|
|
let url = `/report/pdf/${action.report_name}`;
|
|
const actionContext = action.context || {};
|
|
filename = filename || action.name;
|
|
if (filename !== undefined) {
|
|
filename = filename.replace(/[/?%#&=]/g, "_") + ".pdf";
|
|
}
|
|
if (action.data && JSON.stringify(action.data) !== "{}") {
|
|
const options = encodeURIComponent(JSON.stringify(action.data));
|
|
const context = encodeURIComponent(JSON.stringify(actionContext));
|
|
url += `?filename=${filename}&options=${options}&context=${context}&`;
|
|
} else {
|
|
if (actionContext.active_ids) {
|
|
url += `/${actionContext.active_ids.join(",")}?filename=${filename}&context=${encodeURIComponent(JSON.stringify(user.context))}&`;
|
|
}
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
async function FusionPdfPreview(action, options, env) {
|
|
const link = '<br><br><a href="http://wkhtmltopdf.org/" target="_blank">wkhtmltopdf.org</a>';
|
|
const WKHTMLTOPDF_MESSAGES = {
|
|
broken:
|
|
_t(
|
|
"Your installation of Wkhtmltopdf seems to be broken. The report will be shown " +
|
|
"in html."
|
|
) + link,
|
|
install:
|
|
_t(
|
|
"Unable to find Wkhtmltopdf on this system. The report will be shown in " + "html."
|
|
) + link,
|
|
upgrade:
|
|
_t(
|
|
"You should upgrade your version of Wkhtmltopdf to at least 0.12.0 in order to " +
|
|
"get a correct display of headers and footers as well as support for " +
|
|
"table-breaking between pages."
|
|
) + link,
|
|
workers: _t(
|
|
"You need to start Odoo with at least two workers to print a pdf version of " +
|
|
"the reports."
|
|
),
|
|
};
|
|
|
|
if (action.report_type === "qweb-pdf" && env.services.menu.getCurrentApp() !== undefined) {
|
|
const userWantsPreview = session.preview_print;
|
|
const userWantsAutoPrint = session.automatic_printing;
|
|
|
|
const result = await rpc("/fusion_pdf_preview/get_report_name", {
|
|
report_name: action.report_name,
|
|
data: JSON.stringify(action.context)
|
|
});
|
|
const state = result["wkhtmltopdf_state"];
|
|
const previewMode = result["fusion_preview_mode"] || "default";
|
|
|
|
if (state in WKHTMLTOPDF_MESSAGES) {
|
|
env.services.notification.add(WKHTMLTOPDF_MESSAGES[state], {
|
|
sticky: true,
|
|
title: _t("Report"),
|
|
});
|
|
}
|
|
|
|
if (state === "upgrade" || state === "ok") {
|
|
// Determine effective behavior based on report-level override
|
|
let shouldPreview = userWantsPreview;
|
|
let shouldAutoPrint = userWantsAutoPrint;
|
|
|
|
if (previewMode === 'preview') {
|
|
shouldPreview = true;
|
|
shouldAutoPrint = false;
|
|
} else if (previewMode === 'download') {
|
|
return false;
|
|
} else if (previewMode === 'auto_print') {
|
|
shouldPreview = false;
|
|
shouldAutoPrint = true;
|
|
}
|
|
|
|
if (!shouldPreview && !shouldAutoPrint) {
|
|
return false;
|
|
}
|
|
|
|
const url = _getReportUrl(action, env, result["file_name"]);
|
|
const actionContext = action.context || {};
|
|
const meta = {
|
|
reportName: action.report_name,
|
|
recordIds: (actionContext.active_ids || []).join(','),
|
|
modelName: actionContext.active_model || '',
|
|
};
|
|
|
|
if (shouldPreview) {
|
|
openPDFViewer(env, url, action.name, meta);
|
|
logPreviewAction(meta.reportName, 'preview', meta.recordIds, meta.modelName);
|
|
}
|
|
|
|
if (shouldAutoPrint) {
|
|
handleAutomaticPrinting(url, env);
|
|
logPreviewAction(meta.reportName, 'print', meta.recordIds, meta.modelName);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
registry
|
|
.category("ir.actions.report handlers")
|
|
.add("fusion_pdf_preview", FusionPdfPreview);
|