diff --git a/report_qweb_encrypt/__manifest__.py b/report_qweb_encrypt/__manifest__.py index efda67fbd..4644b6ffc 100644 --- a/report_qweb_encrypt/__manifest__.py +++ b/report_qweb_encrypt/__manifest__.py @@ -5,14 +5,24 @@ { "name": "Report Qweb Encrypt", "summary": "Allow to encrypt qweb pdfs", - "version": "14.0.1.1.0", + "version": "15.0.1.0.0", "license": "AGPL-3", "author": "Creu Blanca,Ecosoft,Odoo Community Association (OCA)", "website": "https://github.com/OCA/reporting-engine", "depends": [ "web", ], - "data": ["views/ir_actions_report.xml", "templates/assets.xml"], + "data": [ + "views/ir_actions_report.xml", + ], + "assets": { + "web.assets_backend": [ + "report_qweb_encrypt/static/src/js/report/action_manager_report.esm.js", + ], + }, + "external_dependencies": { + "python": ["PyPDF2"] # Python third party libraries required for module + }, "installable": True, "maintainers": ["kittiu"], } diff --git a/report_qweb_encrypt/controllers/main.py b/report_qweb_encrypt/controllers/main.py index ed8526f18..f7beea4aa 100644 --- a/report_qweb_encrypt/controllers/main.py +++ b/report_qweb_encrypt/controllers/main.py @@ -12,8 +12,8 @@ from odoo.addons.web.controllers import main as report class ReportController(report.ReportController): @route() - def report_download(self, data, token, context=None): - result = super().report_download(data, token, context=context) + def report_download(self, data, context=None): + result = super(ReportController, self).report_download(data, context=context) # When report is downloaded from print action, this function is called, # but this function cannot pass context (manually entered password) to # report.render_qweb_pdf(), encrypton for manual password is done here. @@ -24,14 +24,16 @@ class ReportController(report.ReportController): and result.headers["Content-Type"] == "application/pdf" and "?" in url ): - url_data = dict(url_decode(url.split("?")[1]).items()) - if "context" in url_data: - context = json.loads(url_data["context"]) - if "encrypt_password" in context: - Report = request.env["ir.actions.report"] - data = result.get_data() - encrypted_data = Report._encrypt_pdf( - data, context["encrypt_password"] + data = dict( + url_decode(url.split("?")[1]).items() + ) # decoding the args represented in JSON + if "context" in data: + context, data_context = json.loads(context or "{}"), json.loads( + data.pop("context") + ) + if "encrypt_password" in data_context: + encrypted_data = request.env["ir.actions.report"]._encrypt_pdf( + result.get_data(), data_context["encrypt_password"] ) result.set_data(encrypted_data) return result diff --git a/report_qweb_encrypt/models/ir_actions_report.py b/report_qweb_encrypt/models/ir_actions_report.py index 2a15da2f4..b72c7b901 100644 --- a/report_qweb_encrypt/models/ir_actions_report.py +++ b/report_qweb_encrypt/models/ir_actions_report.py @@ -1,19 +1,14 @@ # Copyright 2020 Creu Blanca # Copyright 2020 Ecosoft Co., Ltd. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging from io import BytesIO +from PyPDF2 import PdfFileReader, PdfFileWriter + from odoo import _, fields, models from odoo.exceptions import ValidationError from odoo.tools.safe_eval import safe_eval -_logger = logging.getLogger(__name__) -try: - from PyPDF2 import PdfFileReader, PdfFileWriter -except ImportError as err: - _logger.debug(err) - class IrActionsReport(models.Model): _inherit = "ir.actions.report" @@ -35,13 +30,11 @@ class IrActionsReport(models.Model): res_ids=res_ids, data=data ) if res_ids: - if isinstance(res_ids, int): - res_ids = [res_ids] password = self._get_pdf_password(res_ids[:1]) document = self._encrypt_pdf(document, password) return document, ttype - def _get_pdf_password(self, res_id): + def _get_pdf_password(self, res_ids): encrypt_password = False if self.encrypt == "manual": # If use document print action, report_download() is called, @@ -51,17 +44,24 @@ class IrActionsReport(models.Model): # Following is used just in case when context is passed in. encrypt_password = self._context.get("encrypt_password", False) elif self.encrypt == "auto" and self.encrypt_password: - obj = self.env[self.model].browse(res_id) + # access the report details with sudo() but evaluation context as sudo(False) + self_sudo = self.sudo() + + Model = self.env[self_sudo.model] + record_ids = Model.browse(res_ids) try: - encrypt_password = safe_eval(self.encrypt_password, {"object": obj}) - except Exception: + encrypt_password = safe_eval( + self.encrypt_password, {"object": record_ids} + ) + except Exception as e: raise ValidationError( _("Python code used for encryption password is invalid.\n%s") % self.encrypt_password - ) + ) from e return encrypt_password - def _encrypt_pdf(self, data, password): + @staticmethod + def _encrypt_pdf(data, password): if not password: return data output_pdf = PdfFileWriter() diff --git a/report_qweb_encrypt/static/src/js/report/action_manager_report.esm.js b/report_qweb_encrypt/static/src/js/report/action_manager_report.esm.js new file mode 100644 index 000000000..fcf110a3f --- /dev/null +++ b/report_qweb_encrypt/static/src/js/report/action_manager_report.esm.js @@ -0,0 +1,77 @@ +/** @odoo-module **/ + +import {Dialog} from "@web/core/dialog/dialog"; +import {download} from "@web/core/network/download"; +import {registry} from "@web/core/registry"; + +async function download_function(action, options, env) { + const type = action.report_type; + let url = `/report/${type}/${action.report_name}`; + const actionContext = action.context || {}; + if (action.data && JSON.stringify(action.data) !== "{}") { + // Build a query string with `action.data` (it's the place where reports + // using a wizard to customize the output traditionally put their options) + const action_options = encodeURIComponent(JSON.stringify(action.data)); + const context = encodeURIComponent(JSON.stringify(actionContext)); + url += `?options=${action_options}&context=${context}`; + } else { + if (actionContext.active_ids) { + url += `/${actionContext.active_ids.join(",")}`; + } + if (type === "html") { + const context = encodeURIComponent( + JSON.stringify(env.services.user.context) + ); + url += `?context=${context}`; + } + } + env.services.ui.block(); + try { + await download({ + url: "/report/download", + data: { + data: JSON.stringify([url, action.report_type]), + context: JSON.stringify(env.services.user.context), + }, + }); + } finally { + env.services.ui.unblock(); + } + const onClose = options.onClose; + if (action.close_on_report_download) { + return env.services.action.doAction( + {type: "ir.actions.act_window_close"}, + {onClose} + ); + } else if (onClose) { + onClose(); + } + return Promise.resolve(true); +} + +class EncryptDialog extends Dialog { + onClick() { + const action = this.props.action; + action.context = _.extend({}, action.context, { + encrypt_password: this.el.find(".o_password").val() || false, + }); + return download_function(action, this.props.options, this.props.env); + } +} +EncryptDialog.size = "small"; +EncryptDialog.title = "Encrypt"; +EncryptDialog.bodyTemplate = "report_qweb_encrypt.EncryptDialogBody"; +EncryptDialog.footerTemplate = "report_qweb_encrypt.EncryptDialogFooter"; + +registry + .category("ir.actions.report handlers") + .add("qweb-pdf-password", async function (action, options, env) { + if (action.encrypt === "manual" && action.report_type === "qweb-pdf") { + return env.services.dialog.add(EncryptDialog, { + action: action, + options: options, + env: env, + }); + } + return Promise.resolve(false); + }); diff --git a/report_qweb_encrypt/static/src/js/report/action_manager_report.js b/report_qweb_encrypt/static/src/js/report/action_manager_report.js deleted file mode 100644 index 7e9585f92..000000000 --- a/report_qweb_encrypt/static/src/js/report/action_manager_report.js +++ /dev/null @@ -1,97 +0,0 @@ -// © 2017 Creu Blanca -// License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html). -odoo.define("report_qweb_encrypt.Dialog", function (require) { - "use strict"; - - var ActionManager = require("web.ActionManager"); - var Dialog = require("web.Dialog"); - var core = require("web.core"); - - var _t = core._t; - - var EncryptDialog = Dialog.extend({ - events: _.extend({}, Dialog.prototype.events, { - change: "_onChange", - }), - _setValue: function () { - this.value = this.$el.find(".o_password").val(); - }, - _onChange: function () { - this._setValue(); - }, - }); - EncryptDialog.askPassword = function (owner, action, action_options, options) { - var buttons = [ - { - text: _t("Ok"), - classes: "btn-primary", - close: true, - click: function () { - var password = this.value || false; - owner._executeReportAction(action, action_options, password); - }, - }, - { - text: _t("Cancel"), - close: true, - click: false, - }, - ]; - return new EncryptDialog( - owner, - _.extend( - { - size: "small", - buttons: buttons, - $content: $( - '
' - ), - title: _t("Encrypt"), - }, - options - ) - ).open(); - }; - - ActionManager.include({ - _executeReportAction: function (action, options, password) { - if ( - action.encrypt === "manual" && - action.report_type === "qweb-pdf" && - password === undefined - ) { - EncryptDialog.askPassword(this, action, options); - return $.Deferred(); - } else if (action.encrypt === "manual") { - action.context = _.extend({}, action.context, { - encrypt_password: password, - }); - } - return this._super(action, options, password); - }, - _makeReportUrls: function (action) { - var reportUrls = this._super.apply(this, arguments); - if (action.encrypt === "manual" && action.context.encrypt_password) { - if ( - _.isUndefined(action.data) || - _.isNull(action.data) || - (_.isObject(action.data) && _.isEmpty(action.data)) - ) { - var serializedOptionsPath = - "?context=" + - encodeURIComponent( - JSON.stringify({ - encrypt_password: action.context.encrypt_password, - }) - ); - reportUrls = _.mapObject(reportUrls, function (value) { - var val = value; - val += serializedOptionsPath; - return val; - }); - } - } - return reportUrls; - }, - }); -}); diff --git a/report_qweb_encrypt/static/src/js/report/encrypt_dialog.xml b/report_qweb_encrypt/static/src/js/report/encrypt_dialog.xml new file mode 100644 index 000000000..45c359fc9 --- /dev/null +++ b/report_qweb_encrypt/static/src/js/report/encrypt_dialog.xml @@ -0,0 +1,13 @@ + + + + + + + + + +
+
+ +
diff --git a/report_qweb_encrypt/templates/assets.xml b/report_qweb_encrypt/templates/assets.xml deleted file mode 100644 index e313587f3..000000000 --- a/report_qweb_encrypt/templates/assets.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - -