Initial commit: Odoo 18.0-20251222 extra-addons
Some checks failed
pre-commit / pre-commit (push) Has been cancelled
tests / Detect unreleased dependencies (push) Has been cancelled
tests / test with OCB (push) Has been cancelled
tests / test with Odoo (push) Has been cancelled

This commit is contained in:
tocmo0nlord
2026-03-13 20:43:25 +00:00
parent 36e847a7df
commit adbe430761
9472 changed files with 1265727 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
===================
Report Qweb Encrypt
===================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0b14a10714ea2e18453d7f01d8a6803ea6ebbc0a1cd46892aa0a05e1aeef5fbc
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github
:target: https://github.com/OCA/reporting-engine/tree/18.0/report_qweb_encrypt
:alt: OCA/reporting-engine
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/reporting-engine-18-0/reporting-engine-18-0-report_qweb_encrypt
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&target_branch=18.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allow you to encrypt PDF with a password seting option,
- Manually keyin password (only applicable for record print action)
- Auto generated password based on object data (python)
**Table of contents**
.. contents::
:local:
Usage
=====
To make a report encryptable mark the field Encryption in the report
record.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/reporting-engine/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_qweb_encrypt%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
-------
* Creu Blanca
* Ecosoft
Contributors
------------
- Enric Tobella <etobella@creublanca.es>
- Jaime Arroyo <jaime.arroyo@creublanca.es>
- Kitti U. <kittiu@ecosoft.co.th>
Maintainers
-----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-kittiu| image:: https://github.com/kittiu.png?size=40px
:target: https://github.com/kittiu
:alt: kittiu
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-kittiu|
This module is part of the `OCA/reporting-engine <https://github.com/OCA/reporting-engine/tree/18.0/report_qweb_encrypt>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,2 @@
from . import controllers
from . import models

View File

@@ -0,0 +1,26 @@
# Copyright 2020 Creu Blanca
# Copyright 2020 Ecosoft Co., Ltd.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Report Qweb Encrypt",
"summary": "Allow to encrypt qweb pdfs",
"version": "18.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",
],
"assets": {
"web.assets_backend": [
"report_qweb_encrypt/static/src/report/action_manager_report.esm.js",
"report_qweb_encrypt/static/src/report/encrypt_dialog.xml",
],
},
"installable": True,
"maintainers": ["kittiu"],
}

View File

@@ -0,0 +1 @@
from . import main

View File

@@ -0,0 +1,39 @@
# Copyright 2020 Creu Blanca
# Copyright 2020 Ecosoft Co., Ltd.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import json
from werkzeug.urls import url_decode
from odoo.http import request, route
from odoo.addons.web.controllers.report import ReportController
class ReportControllerEncrypt(ReportController):
@route()
def report_download(self, data, context=None, token=None, readonly=True):
result = super().report_download(
data, context=context, token=token, readonly=readonly
)
# 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.
if result.headers.get("Content-Type") != "application/pdf":
return result
request_content = json.loads(data)
url, ttype = request_content[0], request_content[1]
if ttype != "qweb-pdf" or "?" not in url:
return result
args = dict(
url_decode(url.split("?", 1)[1])
) # decoding the args represented in JSON
data_context = json.loads(args.get("context", "{}"))
encrypt_password = data_context.get("encrypt_password")
if not encrypt_password:
return result
encrypted_data = request.env["ir.actions.report"]._encrypt_pdf(
result.get_data(), encrypt_password
)
result.set_data(encrypted_data)
return result

View File

@@ -0,0 +1,92 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_qweb_encrypt
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-07-13 20:08+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: report_qweb_encrypt
#: model:ir.model.fields,help:report_qweb_encrypt.field_ir_actions_report__encrypt
msgid ""
"* Manual Input Password: allow user to key in password on the fly. This "
"option available only on document print action.\n"
"* Auto Generated Password: system will auto encrypt password when PDF "
"created, based on provided python syntax."
msgstr ""
"* Contraseña de entrada manual: permite al usuario introducir la contraseña "
"sobre la marcha. Esta opción sólo está disponible en la acción de impresión "
"de documentos.\n"
"* Contraseña generada automáticamente: el sistema cifrará automáticamente la "
"contraseña cuando se cree el PDF, basándose en la sintaxis python "
"proporcionada."
#. module: report_qweb_encrypt
#: model:ir.model.fields.selection,name:report_qweb_encrypt.selection__ir_actions_report__encrypt__auto
msgid "Auto Generated Password"
msgstr "Contraseña generada automáticamente"
#. module: report_qweb_encrypt
#. odoo-javascript
#: code:addons/report_qweb_encrypt/static/src/report/encrypt_dialog.xml:0
#, python-format
msgid "Cancel"
msgstr "Cancelar"
#. module: report_qweb_encrypt
#: model:ir.model.fields,field_description:report_qweb_encrypt.field_ir_actions_report__encrypt_password
msgid "Encrypt Password"
msgstr "Encriptar contraseña"
#. module: report_qweb_encrypt
#: model:ir.model.fields,field_description:report_qweb_encrypt.field_ir_actions_report__encrypt
msgid "Encryption"
msgstr "Encriptación"
#. module: report_qweb_encrypt
#: model:ir.model.fields.selection,name:report_qweb_encrypt.selection__ir_actions_report__encrypt__manual
msgid "Manual Input Password"
msgstr "Contraseña de entrada manual"
#. module: report_qweb_encrypt
#. odoo-javascript
#: code:addons/report_qweb_encrypt/static/src/report/encrypt_dialog.xml:0
#, python-format
msgid "Ok"
msgstr "Aceptar"
#. module: report_qweb_encrypt
#: model:ir.model.fields,help:report_qweb_encrypt.field_ir_actions_report__encrypt_password
msgid "Python code syntax to gnerate password."
msgstr "Sintaxis del código Python para generar la contraseña."
#. module: report_qweb_encrypt
#. odoo-python
#: code:addons/report_qweb_encrypt/models/ir_actions_report.py:0
#, python-format
msgid ""
"Python code used for encryption password is invalid.\n"
"%s"
msgstr ""
"El código Python utilizado para cifrar la contraseña no es válido.\n"
"%s"
#. module: report_qweb_encrypt
#: model:ir.model,name:report_qweb_encrypt.model_ir_actions_report
msgid "Report Action"
msgstr "Informar Acción"
#. module: report_qweb_encrypt
#: model_terms:ir.ui.view,arch_db:report_qweb_encrypt.ir_actions_report_form_view
msgid "python syntax, i.e., (object.default_code or 'secretcode')"
msgstr "sintaxis python, es decir, (object.default_code o 'secretcode')"

View File

@@ -0,0 +1,88 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_qweb_encrypt
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-01-18 09:34+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: report_qweb_encrypt
#: model:ir.model.fields,help:report_qweb_encrypt.field_ir_actions_report__encrypt
msgid ""
"* Manual Input Password: allow user to key in password on the fly. This option available only on document print action.\n"
"* Auto Generated Password: system will auto encrypt password when PDF created, based on provided python syntax."
msgstr ""
"* Inserimento manuale password: consente all'utente di inserire la password "
"al volo. Questa opzione è disponibile solo nell'azione stampa documento.\n"
"* Password auto generata: il sistema cripterà automaticamente la password "
"quando viene creato il PDF, in base alla sintassi Pythn fornita."
#. module: report_qweb_encrypt
#: model:ir.model.fields.selection,name:report_qweb_encrypt.selection__ir_actions_report__encrypt__auto
msgid "Auto Generated Password"
msgstr "Password generata automaticamente"
#. module: report_qweb_encrypt
#. odoo-javascript
#: code:addons/report_qweb_encrypt/static/src/report/encrypt_dialog.xml:0
#, python-format
msgid "Cancel"
msgstr "Annulla"
#. module: report_qweb_encrypt
#: model:ir.model.fields,field_description:report_qweb_encrypt.field_ir_actions_report__encrypt_password
msgid "Encrypt Password"
msgstr "Cripta password"
#. module: report_qweb_encrypt
#: model:ir.model.fields,field_description:report_qweb_encrypt.field_ir_actions_report__encrypt
msgid "Encryption"
msgstr "Criptazione"
#. module: report_qweb_encrypt
#: model:ir.model.fields.selection,name:report_qweb_encrypt.selection__ir_actions_report__encrypt__manual
msgid "Manual Input Password"
msgstr "Password inserita manualmente"
#. module: report_qweb_encrypt
#. odoo-javascript
#: code:addons/report_qweb_encrypt/static/src/report/encrypt_dialog.xml:0
#, python-format
msgid "Ok"
msgstr "Ok"
#. module: report_qweb_encrypt
#: model:ir.model.fields,help:report_qweb_encrypt.field_ir_actions_report__encrypt_password
msgid "Python code syntax to gnerate password."
msgstr "Sintassi codice Python per generazione password."
#. module: report_qweb_encrypt
#. odoo-python
#: code:addons/report_qweb_encrypt/models/ir_actions_report.py:0
#, python-format
msgid ""
"Python code used for encryption password is invalid.\n"
"%s"
msgstr ""
"Il codice Python usato per la criptazione password non è valido.\n"
"%s"
#. module: report_qweb_encrypt
#: model:ir.model,name:report_qweb_encrypt.model_ir_actions_report
msgid "Report Action"
msgstr "Azione resoconto"
#. module: report_qweb_encrypt
#: model_terms:ir.ui.view,arch_db:report_qweb_encrypt.ir_actions_report_form_view
msgid "python syntax, i.e., (object.default_code or 'secretcode')"
msgstr "Sintassi Python, es., (object.default_code o 'secretcode')"

View File

@@ -0,0 +1,82 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_qweb_encrypt
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: report_qweb_encrypt
#: model:ir.model.fields,help:report_qweb_encrypt.field_ir_actions_report__encrypt
msgid ""
"* Manual Input Password: allow user to key in password on the fly. This option available only on document print action.\n"
"* Auto Generated Password: system will auto encrypt password when PDF created, based on provided python syntax."
msgstr ""
#. module: report_qweb_encrypt
#: model:ir.model.fields.selection,name:report_qweb_encrypt.selection__ir_actions_report__encrypt__auto
msgid "Auto Generated Password"
msgstr ""
#. module: report_qweb_encrypt
#. odoo-javascript
#: code:addons/report_qweb_encrypt/static/src/report/encrypt_dialog.xml:0
msgid "Cancel"
msgstr ""
#. module: report_qweb_encrypt
#: model:ir.model.fields,field_description:report_qweb_encrypt.field_ir_actions_report__encrypt_password
msgid "Encrypt Password"
msgstr ""
#. module: report_qweb_encrypt
#: model:ir.model.fields,field_description:report_qweb_encrypt.field_ir_actions_report__encrypt
msgid "Encryption"
msgstr ""
#. module: report_qweb_encrypt
#. odoo-javascript
#: code:addons/report_qweb_encrypt/static/src/report/encrypt_dialog.xml:0
msgid "Enter password"
msgstr ""
#. module: report_qweb_encrypt
#: model:ir.model.fields.selection,name:report_qweb_encrypt.selection__ir_actions_report__encrypt__manual
msgid "Manual Input Password"
msgstr ""
#. module: report_qweb_encrypt
#. odoo-javascript
#: code:addons/report_qweb_encrypt/static/src/report/encrypt_dialog.xml:0
msgid "Ok"
msgstr ""
#. module: report_qweb_encrypt
#: model:ir.model.fields,help:report_qweb_encrypt.field_ir_actions_report__encrypt_password
msgid "Python code syntax to gnerate password."
msgstr ""
#. module: report_qweb_encrypt
#. odoo-python
#: code:addons/report_qweb_encrypt/models/ir_actions_report.py:0
msgid ""
"Python code used for encryption password is invalid.\n"
"%s"
msgstr ""
#. module: report_qweb_encrypt
#: model:ir.model,name:report_qweb_encrypt.model_ir_actions_report
msgid "Report Action"
msgstr ""
#. module: report_qweb_encrypt
#: model_terms:ir.ui.view,arch_db:report_qweb_encrypt.ir_actions_report_form_view
msgid "python syntax, i.e., (object.default_code or 'secretcode')"
msgstr ""

View File

@@ -0,0 +1 @@
from . import ir_actions_report

View File

@@ -0,0 +1,83 @@
# Copyright 2020 Creu Blanca
# Copyright 2020 Ecosoft Co., Ltd.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
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
class IrActionsReport(models.Model):
_inherit = "ir.actions.report"
encrypt = fields.Selection(
selection=[
("manual", "Manual Input Password"),
("auto", "Auto Generated Password"),
],
string="Encryption",
help="* Manual Input Password: allow user to key in password on the fly. "
"This option available only on document print action.\n"
"* Auto Generated Password: system will auto encrypt password when PDF "
"created, based on provided python syntax.",
)
encrypt_password = fields.Char(
help="Python code syntax to gnerate password.",
)
def _render_qweb_pdf(self, report_ref, res_ids=None, data=None):
document, ttype = super()._render_qweb_pdf(
report_ref, res_ids=res_ids, data=data
)
report_sudo = self._get_report(report_ref)
if res_ids:
encrypt_password = self._context.get("encrypt_password")
report = self._get_report_from_name(report_sudo.report_name).with_context(
encrypt_password=encrypt_password
)
password = report._get_pdf_password(res_ids[:1])
document = self._encrypt_pdf(document, password)
return document, ttype
def _get_pdf_password(self, res_ids):
encrypt_password = False
if self.encrypt == "manual":
# If use document print action, report_download() is called,
# but that can't pass context (encrypt_password) here.
# As such, file will be encrypted by report_download() again.
# --
# 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:
# access the report details with sudo() but evaluation context as
# sudo(False)
records = self.env[self.sudo().model].browse(res_ids).exists()
try:
encrypt_password = safe_eval(self.encrypt_password, {"object": records})
except Exception as e:
raise ValidationError(
self.env._(
"Python code used for encryption password is invalid.\n%s",
self.encrypt_password,
)
) from e
return encrypt_password
@staticmethod
def _encrypt_pdf(data, password):
if not password:
return data
output_pdf = PdfFileWriter()
in_buff = BytesIO(data)
pdf = PdfFileReader(in_buff)
output_pdf.appendPagesFromReader(pdf)
output_pdf.encrypt(password)
buff = BytesIO()
output_pdf.write(buff)
return buff.getvalue()
def _get_readable_fields(self):
return super()._get_readable_fields() | {"encrypt"}

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@@ -0,0 +1,3 @@
- Enric Tobella \<etobella@creublanca.es\>
- Jaime Arroyo \<jaime.arroyo@creublanca.es\>
- Kitti U. \<kittiu@ecosoft.co.th\>

View File

@@ -0,0 +1,4 @@
This module allow you to encrypt PDF with a password seting option,
- Manually keyin password (only applicable for record print action)
- Auto generated password based on object data (python)

View File

@@ -0,0 +1,2 @@
To make a report encryptable mark the field Encryption in the report
record.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,444 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="report-qweb-encrypt">
<h1>Report Qweb Encrypt</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0b14a10714ea2e18453d7f01d8a6803ea6ebbc0a1cd46892aa0a05e1aeef5fbc
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/reporting-engine/tree/18.0/report_qweb_encrypt"><img alt="OCA/reporting-engine" src="https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/reporting-engine-18-0/reporting-engine-18-0-report_qweb_encrypt"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allow you to encrypt PDF with a password seting option,</p>
<ul class="simple">
<li>Manually keyin password (only applicable for record print action)</li>
<li>Auto generated password based on object data (python)</li>
</ul>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
<p>To make a report encryptable mark the field Encryption in the report
record.</p>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/reporting-engine/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_qweb_encrypt%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-3">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-4">Authors</a></h3>
<ul class="simple">
<li>Creu Blanca</li>
<li>Ecosoft</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-5">Contributors</a></h3>
<ul class="simple">
<li>Enric Tobella &lt;<a class="reference external" href="mailto:etobella&#64;creublanca.es">etobella&#64;creublanca.es</a>&gt;</li>
<li>Jaime Arroyo &lt;<a class="reference external" href="mailto:jaime.arroyo&#64;creublanca.es">jaime.arroyo&#64;creublanca.es</a>&gt;</li>
<li>Kitti U. &lt;<a class="reference external" href="mailto:kittiu&#64;ecosoft.co.th">kittiu&#64;ecosoft.co.th</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/kittiu"><img alt="kittiu" src="https://github.com/kittiu.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/reporting-engine/tree/18.0/report_qweb_encrypt">OCA/reporting-engine</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,84 @@
import {Dialog} from "@web/core/dialog/dialog";
import {download} from "@web/core/network/download";
import {registry} from "@web/core/registry";
import {useRef} from "@odoo/owl";
import {user} from "@web/core/user";
const {Component} = owl;
function buildReportUrl(action, type, userContext) {
let url = `/report/${type}/${action.report_name}`;
const actionContext = action.context || {};
if (action.data && Object.keys(action.data).length) {
// Build a query string with `action.data` (it's the place where reports
// using a wizard to customize the output traditionally put their options)
url += `?options=${encodeURIComponent(JSON.stringify(action.data))}`;
url += `&context=${encodeURIComponent(JSON.stringify(actionContext))}`;
} else {
if (actionContext.active_ids) {
url += `/${actionContext.active_ids.join(",")}`;
}
if (type === "html") {
url += `?context=${encodeURIComponent(JSON.stringify(userContext))}`;
}
}
return url;
}
async function download_function(action, options, env) {
const type = action.report_type === "qweb-pdf" ? "pdf" : action.report_type;
const userContext = {
...user.context,
encrypt_password: action.context?.encrypt_password,
};
const url = buildReportUrl(action, type, userContext);
env.services.ui.block();
try {
await download({
url: "/report/download",
data: {
data: JSON.stringify([url, action.report_type]),
context: JSON.stringify(userContext),
},
});
} finally {
env.services.ui.unblock();
}
if (action.close_on_report_download) {
return env.services.action.doAction(
{type: "ir.actions.act_window_close"},
{onClose: options.onClose}
);
}
options.onClose?.();
return true;
}
class EncryptDialog extends Component {
setup() {
this.passwordRef = useRef("password");
}
onClick() {
const password = this.passwordRef.el?.value || false;
const action = {
...this.props.action,
context: {
...this.props.action.context,
encrypt_password: password,
},
};
return download_function(action, this.props.options, this.props.env);
}
}
EncryptDialog.components = {Dialog};
EncryptDialog.template = "report_qweb_encrypt.EncryptDialogBody";
registry
.category("ir.actions.report handlers")
.add("qweb-pdf-password", async (action, options, env) => {
if (action.encrypt === "manual" && action.report_type === "qweb-pdf") {
return env.services.dialog.add(EncryptDialog, {action, options, env});
}
return false;
});

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="report_qweb_encrypt.EncryptDialogBody" owl="1">
<Dialog size="'sm'" title="'Encrypt'">
<div id="qweb_encrypt_pdf_password">
<input
t-ref="password"
class="o_password"
type="password"
placeholder="Enter password"
/>
</div>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="onClick">Ok</button>
<button class="btn" t-on-click="this.props.close">Cancel</button>
</t>
</Dialog>
</t>
</templates>

View File

@@ -0,0 +1 @@
from . import test_report_qweb_encrypt

View File

@@ -0,0 +1,51 @@
# © 2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.exceptions import ValidationError
from odoo.tests.common import HttpCase
class TestReportQwebEncrypt(HttpCase):
def test_report_qweb_no_encrypt(self):
ctx = {"force_report_rendering": True}
report = self.env.ref("web.action_report_internalpreview")
report.encrypt = False
pdf, _ = report.with_context(**ctx)._render_qweb_pdf(report.report_name, [1])
self.assertFalse(pdf.count(b"/Encrypt"))
def test_report_qweb_auto_encrypt(self):
ctx = {"force_report_rendering": True}
report = self.env.ref("web.action_report_internalpreview")
report.encrypt = "auto"
report.encrypt_password = False
# If no encrypt_password, still not encrypted
pdf, _ = report.with_context(**ctx)._render_qweb_pdf(report.report_name, [1])
self.assertFalse(pdf.count(b"/Encrypt"))
# If invalid encrypt_password, show error
report.encrypt_password = "invalid python syntax"
with self.assertRaises(ValidationError):
pdf, _ = report.with_context(**ctx)._render_qweb_pdf(
report.report_name, [1]
)
# Valid python string for password
report.encrypt_password = "'secretcode'"
pdf, _ = report.with_context(**ctx)._render_qweb_pdf(report.report_name, [1])
self.assertTrue(pdf.count(b"/Encrypt"))
def test_report_qweb_manual_encrypt(self):
ctx = {"force_report_rendering": True}
report = self.env.ref("web.action_report_internalpreview")
report.encrypt = "manual"
# If no encrypt_password, still not encrypted
pdf, _ = report.with_context(**ctx)._render_qweb_pdf(report.report_name, [1])
self.assertFalse(pdf.count(b"/Encrypt"))
# Valid python string for password
ctx.update({"encrypt_password": "secretcode"})
pdf, _ = report.with_context(**ctx)._render_qweb_pdf(report.report_name, [1])
self.assertTrue(pdf.count(b"/Encrypt"))
# TODO: test_report_qweb_manual_encrypt, require JS test?

View File

@@ -0,0 +1,30 @@
<?xml version='1.0' encoding='utf-8' ?>
<!-- Copyright 2020 Creu Blanca
Copyright 2020 Ecosoft Co., LTd.
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="ir_actions_report_form_view" model="ir.ui.view">
<field name="name">ir.actions.report.form (in report_qweb_encrypt)</field>
<field name="model">ir.actions.report</field>
<field name="inherit_id" ref="base.act_report_xml_view" />
<field name="arch" type="xml">
<field name="paperformat_id" position="after">
<label
for="encrypt"
invisible="report_type not in ('qweb-pdf', 'qweb-html')"
/>
<div
name="encrypt"
invisible="report_type not in ('qweb-pdf', 'qweb-html')"
>
<field name="encrypt" />
<field
name="encrypt_password"
invisible="encrypt != 'auto'"
placeholder="python syntax, i.e., (object.default_code or 'secretcode')"
/>
</div>
</field>
</field>
</record>
</odoo>