Merge PR #558 into 18.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot 2025-08-21 11:34:50 +00:00
commit 51adfd44ad
9 changed files with 68 additions and 74 deletions

View File

@ -4,7 +4,7 @@
<t t-call="web.external_layout">
<div class="page">
<h1 t-field="doc.name" />
<div t-raw="doc.content" />
<div t-out="doc.content" />
</div>
</t>
</template>

View File

@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
=======================
Document Page Reference
=======================
@ -17,7 +13,7 @@ Document Page Reference
.. |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
.. |badge2| image:: https://img.shields.io/badge/licence-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%2Fknowledge-lightgray.png?logo=github
@ -43,7 +39,7 @@ the link between document pages.
Usage
=====
When editing a document page add elements like ${XXX} where XXX is the
When editing a document page add elements like {{XXX}} where XXX is the
reference of another page. Now, when viewing the document, it will link
directly to the page. Also, the name will be parsed as the display name.

View File

@ -5,7 +5,7 @@
"name": "Document Page Reference",
"summary": """
Include references on document pages""",
"version": "18.0.1.0.0",
"version": "18.0.1.1.0",
"license": "AGPL-3",
"author": "Creu Blanca,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/knowledge",

View File

@ -0,0 +1,12 @@
# Copyright 2025 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import re
from openupgradelib import openupgrade
@openupgrade.migrate()
def migrate(env, version):
for item in env["document.page"].search([("content", "ilike", "${")]):
item.content = re.sub(r"\${(.+)}", r"{{\1}}", item.content)

View File

@ -1,19 +1,15 @@
# Copyright 2019 Creu Blanca
# Copyright 2025 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
import re
from jinja2.sandbox import SandboxedEnvironment
from markupsafe import Markup
from odoo import _, api, fields, models
from odoo import api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import html_escape
_logger = logging.getLogger(__name__)
env = SandboxedEnvironment(autoescape=False)
class DocumentPage(models.Model):
_inherit = "document.page"
@ -35,15 +31,7 @@ class DocumentPage(models.Model):
@api.depends("content")
def _compute_content_parsed(self):
for record in self:
try:
raw = record.content or ""
converted = re.sub(r"\$\{([\w_]+)\}", r"{{ resolve('\1') }}", raw)
template = env.from_string(converted)
rendered = template.render(resolve=record._resolve_reference)
record.content_parsed = rendered
except Exception as e:
_logger.info("Render failed for %s: %s", record.id, e)
record.content_parsed = record.content or ""
record.content_parsed = record.get_content()
@api.constrains("reference")
def _check_reference_validity(self):
@ -52,42 +40,36 @@ class DocumentPage(models.Model):
continue
regex = r"^[a-zA-Z_][a-zA-Z0-9_]*$"
if not re.match(regex, rec.reference):
raise ValidationError(_("Reference is not valid"))
raise ValidationError(self.env._("Reference is not valid"))
domain = [("reference", "=", rec.reference), ("id", "!=", rec.id)]
if self.search(domain):
raise ValidationError(_("Reference must be unique"))
raise ValidationError(self.env._("Reference must be unique"))
def _get_document(self, code):
return self.search([("reference", "=", code)], limit=1)
def get_content(self):
for record in self:
try:
raw = record.content or ""
converted = re.sub(r"\$\{([\w_]+)\}", r"{{ resolve('\1') }}", raw)
template = env.from_string(converted)
return template.render(resolve=record._resolve_reference)
except Exception:
_logger.error(
"Template from page with id = %s cannot be processed", record.id
self.ensure_one()
content_parsed = raw = self.content or ""
for text in re.findall(r"\{\{.*?\}\}", raw):
reference = text.replace("{{", "").replace("}}", "")
content_parsed = content_parsed.replace(
text, self._resolve_reference(reference)
)
return record.content
return content_parsed
def _resolve_reference(self, code):
doc = self._get_document(code)
if self.env.context.get("raw_reference", False):
return html_escape(doc.display_name if doc else code)
sanitized_code = html_escape(code)
if not doc:
oe_model = doc._name if doc else self._name
oe_id = doc.id if doc else ""
name = html_escape(doc.display_name) if doc else sanitized_code
return (
f"<i><a href='#' class='oe_direct_line' "
f"data-oe-model='document.page' data-oe-id='' "
f"name='{sanitized_code}'>{sanitized_code}</a></i>"
)
return (
f"<a href='#' class='oe_direct_line' data-oe-model='{doc._name}' "
f"data-oe-id='{doc.id}' name='{sanitized_code}'>"
f"{html_escape(doc.display_name)}</a>"
f"<a href='#' class='oe_direct_line' data-oe-model='{oe_model}' "
f"data-oe-id='{oe_id}' name='{sanitized_code}'>"
f"{name}</a>"
)
def get_raw_content(self):

View File

@ -1,3 +1,3 @@
When editing a document page add elements like \${XXX} where XXX is the
When editing a document page add elements like {{XXX}} where XXX is the
reference of another page. Now, when viewing the document, it will link
directly to the page. Also, the name will be parsed as the display name.

View File

@ -3,7 +3,7 @@
<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>
<title>Document Page Reference</title>
<style type="text/css">
/*
@ -360,21 +360,16 @@ ul.auto-toc {
</style>
</head>
<body>
<div class="document">
<div class="document" id="document-page-reference">
<h1 class="title">Document Page Reference</h1>
<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="document-page-reference">
<h1>Document Page Reference</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:f5181a95b68e52c04e1b76ba19f503b5bfc8d365939debe14edbb40413101321
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/knowledge/tree/18.0/document_page_reference"><img alt="OCA/knowledge" src="https://img.shields.io/badge/github-OCA%2Fknowledge-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/knowledge-18-0/knowledge-18-0-document_page_reference"><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/knowledge&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><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/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/knowledge/tree/18.0/document_page_reference"><img alt="OCA/knowledge" src="https://img.shields.io/badge/github-OCA%2Fknowledge-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/knowledge-18-0/knowledge-18-0-document_page_reference"><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/knowledge&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 allows to add a reference name on documents and simplifies
the link between document pages.</p>
<p><strong>Table of contents</strong></p>
@ -391,13 +386,13 @@ the link between document pages.</p>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
<p>When editing a document page add elements like ${XXX} where XXX is the
<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
<p>When editing a document page add elements like {{XXX}} where XXX is the
reference of another page. Now, when viewing the document, it will link
directly to the page. Also, the name will be parsed as the display name.</p>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/knowledge/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
@ -405,21 +400,21 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<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>
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-4">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<ul class="simple">
<li>Creu Blanca</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-5">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li>Enric Tobella &lt;<a class="reference external" href="mailto:etobella&#64;creublanca.es">etobella&#64;creublanca.es</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<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" />
@ -434,6 +429,5 @@ promote its widespread use.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,21 +1,24 @@
# Copyright 2019 Creu Blanca
# Copyright 2025 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from markupsafe import Markup
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase
from odoo.addons.base.tests.common import BaseCommon
class TestDocumentReference(TransactionCase):
class TestDocumentReference(BaseCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.page_obj = cls.env["document.page"]
cls.history_obj = cls.env["document.page.history"]
cls.page1 = cls.page_obj.create(
{"name": "Test Page 1", "content": "${r2}", "reference": "R1"}
{"name": "Test Page 1", "content": Markup("{{r2}}"), "reference": "R1"}
)
cls.page2 = cls.page_obj.create(
{"name": "Test Page 1", "content": "${r1}", "reference": "r2"}
{"name": "Test Page 1", "content": Markup("{{r1}}"), "reference": "r2"}
)
def test_constraints_duplicate_reference(self):
@ -28,6 +31,15 @@ class TestDocumentReference(TransactionCase):
with self.assertRaises(ValidationError):
self.page2.write({"reference": self.page2.reference + "-02"})
def test_no_contrains(self):
self.page1.write({"reference": False})
self.assertFalse(self.page1.reference)
self.page2.write({"reference": False})
self.assertFalse(self.page2.reference)
def test_check_raw(self):
self.assertEqual(self.page2.display_name, self.page1.get_raw_content())
def test_auto_reference(self):
"""Test if reference is proposed when saving a page without one."""
self.assertEqual(self.page1.reference, "R1")
@ -52,7 +64,6 @@ class TestDocumentReference(TransactionCase):
"type": "ir.actions.act_window",
"res_model": "document.page",
"res_id": self.page1.id,
"context": {},
"target": "current",
"views": [(view_id, "form")],
}
@ -60,6 +71,5 @@ class TestDocumentReference(TransactionCase):
self.assertEqual(res.get(key), expected_value, f"Mismatch in key: {key}")
def test_compute_content_parsed(self):
self.page1.content = "<p></p>"
self.page1._compute_content_parsed()
self.assertEqual(str(self.page1.content_parsed), "<p></p>")
self.page1.content = Markup("<p></p>")
self.assertEqual(self.page1.content_parsed, Markup("<p></p>"))

View File

@ -4,10 +4,10 @@
id="report_documentpage_doc"
inherit_id="document_page.report_documentpage_doc"
>
<xpath expr="//div[@t-raw='doc.content']" position="attributes">
<xpath expr="//div[@t-out='doc.content']" position="attributes">
<attribute name="t-if">1==0</attribute>
</xpath>
<xpath expr="//div[@t-raw='doc.content']" position="after">
<xpath expr="//div[@t-out='doc.content']" position="after">
<t t-out="doc.get_raw_content()" />
</xpath>
</template>