From 0021173e64aa0d02514f3c4ab735a0acc27a08bc Mon Sep 17 00:00:00 2001
From: Enric Tobella
Date: Wed, 27 Nov 2019 23:20:44 +0100
Subject: [PATCH 01/27] [ADD] document_page_reference
---
document_page_reference/README.rst | 81 ++++
document_page_reference/__init__.py | 1 +
document_page_reference/__manifest__.py | 23 +
document_page_reference/models/__init__.py | 1 +
.../models/document_page.py | 108 +++++
.../readme/CONTRIBUTORS.rst | 1 +
.../readme/DESCRIPTION.rst | 2 +
document_page_reference/readme/USAGE.rst | 3 +
.../static/description/icon.png | Bin 0 -> 9455 bytes
.../static/description/index.html | 427 ++++++++++++++++++
.../static/src/js/editor.js | 30 ++
document_page_reference/tests/__init__.py | 1 +
.../tests/test_document_reference.py | 49 ++
document_page_reference/views/assets.xml | 8 +
.../views/document_page.xml | 62 +++
.../views/report_document_page.xml | 13 +
16 files changed, 810 insertions(+)
create mode 100644 document_page_reference/README.rst
create mode 100644 document_page_reference/__init__.py
create mode 100644 document_page_reference/__manifest__.py
create mode 100644 document_page_reference/models/__init__.py
create mode 100644 document_page_reference/models/document_page.py
create mode 100644 document_page_reference/readme/CONTRIBUTORS.rst
create mode 100644 document_page_reference/readme/DESCRIPTION.rst
create mode 100644 document_page_reference/readme/USAGE.rst
create mode 100644 document_page_reference/static/description/icon.png
create mode 100644 document_page_reference/static/description/index.html
create mode 100644 document_page_reference/static/src/js/editor.js
create mode 100644 document_page_reference/tests/__init__.py
create mode 100644 document_page_reference/tests/test_document_reference.py
create mode 100644 document_page_reference/views/assets.xml
create mode 100644 document_page_reference/views/document_page.xml
create mode 100755 document_page_reference/views/report_document_page.xml
diff --git a/document_page_reference/README.rst b/document_page_reference/README.rst
new file mode 100644
index 00000000000..adc253cb4e5
--- /dev/null
+++ b/document_page_reference/README.rst
@@ -0,0 +1,81 @@
+=======================
+Document Page Reference
+=======================
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |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/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
+ :target: https://github.com/OCA/knowledge/tree/11.0/document_page_reference
+ :alt: OCA/knowledge
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/knowledge-11-0/knowledge-11-0-document_page_reference
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
+ :target: https://runbot.odoo-community.org/runbot/118/11.0
+ :alt: Try me on Runbot
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module allows to add a reference name on documents and simplifies the link
+between document pages.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+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.
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues `_.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us smashing it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* Creu Blanca
+
+Contributors
+~~~~~~~~~~~~
+
+* Enric Tobella
+
+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.
+
+This module is part of the `OCA/knowledge `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/document_page_reference/__init__.py b/document_page_reference/__init__.py
new file mode 100644
index 00000000000..0650744f6bc
--- /dev/null
+++ b/document_page_reference/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/document_page_reference/__manifest__.py b/document_page_reference/__manifest__.py
new file mode 100644
index 00000000000..c3a6a9814f1
--- /dev/null
+++ b/document_page_reference/__manifest__.py
@@ -0,0 +1,23 @@
+# Copyright 2019 Creu Blanca
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ 'name': 'Document Page Reference',
+ 'summary': """
+ Include references on document pages""",
+ 'version': '11.0.1.0.0',
+ 'license': 'AGPL-3',
+ 'author': 'Creu Blanca,Odoo Community Association (OCA)',
+ 'website': 'https://github.com/OCA/knowledge',
+ 'depends': [
+ 'document_page',
+ 'web_editor',
+ ],
+ 'data': [
+ 'views/assets.xml',
+ 'views/document_page.xml',
+ 'views/report_document_page.xml',
+ ],
+ 'maintainers': ['etobella'],
+ 'development_status': 'Alpha',
+}
diff --git a/document_page_reference/models/__init__.py b/document_page_reference/models/__init__.py
new file mode 100644
index 00000000000..427be24e72a
--- /dev/null
+++ b/document_page_reference/models/__init__.py
@@ -0,0 +1 @@
+from . import document_page
diff --git a/document_page_reference/models/document_page.py b/document_page_reference/models/document_page.py
new file mode 100644
index 00000000000..1f5a67096d2
--- /dev/null
+++ b/document_page_reference/models/document_page.py
@@ -0,0 +1,108 @@
+# Copyright 2019 Creu Blanca
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models, tools, _
+from odoo.exceptions import ValidationError
+from odoo.tools.misc import html_escape
+
+import logging
+_logger = logging.getLogger(__name__)
+
+try:
+ from jinja2.sandbox import SandboxedEnvironment
+ from jinja2 import Undefined
+ from jinja2.lexer import name_re as old_name_re
+ import re
+
+ name_re = re.compile(u'^%s$' % old_name_re.pattern)
+
+ class Context(SandboxedEnvironment.context_class):
+ def resolve(self, key):
+ res = super().resolve(key)
+ if not isinstance(res, Undefined):
+ return res
+ return self.parent['ref'](key)
+
+ class Environment(SandboxedEnvironment):
+ context_class = Context
+
+ mako_template_env = Environment(
+ block_start_string="<%",
+ block_end_string="%>",
+ variable_start_string="${",
+ variable_end_string="}",
+ comment_start_string="<%doc>",
+ comment_end_string="%doc>",
+ line_statement_prefix="%",
+ line_comment_prefix="##",
+ trim_blocks=True, # do not output newline after blocks
+ autoescape=False,
+ )
+except Exception:
+ _logger.error("Jinja2 is not available")
+
+
+class DocumentPage(models.Model):
+
+ _inherit = 'document.page'
+
+ reference = fields.Char(
+ help="Used to find the document, it can contain letters, numbers and _"
+ )
+ content_parsed = fields.Html(compute='_compute_content_parsed')
+
+ @api.depends('history_head')
+ def _compute_content_parsed(self):
+ for record in self:
+ record.content_parsed = record.get_content()
+
+ @api.constrains('reference')
+ def _check_reference(self):
+ for record in self:
+ if not record.reference:
+ continue
+ if not name_re.match(record.reference):
+ raise ValidationError(_('Reference is not valid'))
+ if self.search([
+ ('reference', '=', record.reference),
+ ('id', '!=', record.id)]
+ ):
+ raise ValidationError(_('Reference must be unique'))
+
+ def _get_document(self, code):
+ # Hook created in order to add check on other models
+ document = self.search([('reference', '=', code)])
+ if document:
+ return document
+ return False
+
+ def get_reference(self, code):
+ element = self._get_document(code)
+ if not element:
+ return code
+ if self.env.context.get('raw_reference', False):
+ return html_escape(element.display_name)
+ text = '%s'
+ return text % (
+ element._name,
+ element.id,
+ html_escape(element.display_name)
+ )
+
+ def _get_template_variables(self):
+ return {'ref': self.get_reference}
+
+ def get_content(self):
+ try:
+ content = self.content
+ mako_env = mako_template_env
+ template = mako_env.from_string(tools.ustr(content))
+ return template.render(self._get_template_variables())
+ except Exception:
+ _logger.error(
+ 'Template from page %s cannot be processed' % self.id)
+ return self.content
+
+ def get_raw_content(self):
+ return self.with_context(raw_reference=True).get_content()
diff --git a/document_page_reference/readme/CONTRIBUTORS.rst b/document_page_reference/readme/CONTRIBUTORS.rst
new file mode 100644
index 00000000000..93ec993e044
--- /dev/null
+++ b/document_page_reference/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Enric Tobella
diff --git a/document_page_reference/readme/DESCRIPTION.rst b/document_page_reference/readme/DESCRIPTION.rst
new file mode 100644
index 00000000000..dfddc47b4ff
--- /dev/null
+++ b/document_page_reference/readme/DESCRIPTION.rst
@@ -0,0 +1,2 @@
+This module allows to add a reference name on documents and simplifies the link
+between document pages.
diff --git a/document_page_reference/readme/USAGE.rst b/document_page_reference/readme/USAGE.rst
new file mode 100644
index 00000000000..5f341f02648
--- /dev/null
+++ b/document_page_reference/readme/USAGE.rst
@@ -0,0 +1,3 @@
+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.
diff --git a/document_page_reference/static/description/icon.png b/document_page_reference/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d
GIT binary patch
literal 9455
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
z
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
Xviia!U7SGha1wx#SCgwmn*{w2TRX*I
literal 0
HcmV?d00001
diff --git a/document_page_reference/static/description/index.html b/document_page_reference/static/description/index.html
new file mode 100644
index 00000000000..87f2d10fa1d
--- /dev/null
+++ b/document_page_reference/static/description/index.html
@@ -0,0 +1,427 @@
+
+
+
+
+
+
+Document Page Reference
+
+
+
+
+
Document Page Reference
+
+
+

+
This module allows to add a reference name on documents and simplifies the link
+between document pages.
+
Table of contents
+
+
+
+
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.
+
+
+
+
Bugs are tracked on GitHub Issues.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us smashing it by providing a detailed and welcomed
+feedback.
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+
+
This module is maintained by the OCA.
+

+
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.
+
This module is part of the OCA/knowledge project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
+
+
+
+
+
diff --git a/document_page_reference/static/src/js/editor.js b/document_page_reference/static/src/js/editor.js
new file mode 100644
index 00000000000..b3533a8721a
--- /dev/null
+++ b/document_page_reference/static/src/js/editor.js
@@ -0,0 +1,30 @@
+odoo.define('document_page_reference.backend', function (require) {
+ 'use strict';
+
+ var field_registry = require('web.field_registry');
+ var backend = require('web_editor.backend');
+ var FieldTextHtmlSimple = backend.FieldTextHtmlSimple;
+
+ var FieldDocumentPage = FieldTextHtmlSimple.extend({
+ events: _.extend({}, FieldTextHtmlSimple.prototype.events, {
+ 'click .oe_direct_line': '_onClickDirectLink',
+ }),
+ _onClickDirectLink: function (event) {
+ var self = this;
+ event.preventDefault();
+ event.stopPropagation();
+ var element = $(event.target).closest('.oe_direct_line')[0];
+ this._rpc({
+ model: element.name,
+ method: 'get_formview_action',
+ args: [[parseInt(element.dataset.id)]],
+ context: this.record.getContext(this.recordParams),
+ })
+ .then(function (action) {
+ self.trigger_up('do_action', {action: action});
+ });
+ },
+ });
+ field_registry.add('document_page_reference', FieldDocumentPage);
+ return FieldDocumentPage;
+});
diff --git a/document_page_reference/tests/__init__.py b/document_page_reference/tests/__init__.py
new file mode 100644
index 00000000000..ca802a6bb24
--- /dev/null
+++ b/document_page_reference/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_document_reference
diff --git a/document_page_reference/tests/test_document_reference.py b/document_page_reference/tests/test_document_reference.py
new file mode 100644
index 00000000000..4be351b4bdd
--- /dev/null
+++ b/document_page_reference/tests/test_document_reference.py
@@ -0,0 +1,49 @@
+# Copyright 2019 Creu Blanca
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo.tests.common import TransactionCase
+from odoo.exceptions import ValidationError
+
+
+class TestDocumentReference(TransactionCase):
+
+ def setUp(self):
+ super().setUp()
+ self.page_obj = self.env['document.page']
+ self.history_obj = self.env['document.page.history']
+ self.page1 = self.page_obj.create({
+ 'name': 'Test Page 1',
+ 'content': '${r2}',
+ 'reference': 'R1'
+ })
+ self.page2 = self.page_obj.create({
+ 'name': 'Test Page 1',
+ 'content': '${r1}',
+ 'reference': 'r2'
+ })
+
+ def test_constrains_01(self):
+ with self.assertRaises(ValidationError):
+ self.page2.write({'reference': self.page1.reference})
+
+ def test_constrains_02(self):
+ with self.assertRaises(ValidationError):
+ self.page2.write({'reference': self.page2.reference + '-02'})
+
+ def test_no_contrains(self):
+ self.page1.write({'reference': False})
+ self.page2.write({'reference': False})
+ self.assertEqual(self.page1.reference, self.page2.reference)
+
+ def test_check_raw(self):
+ self.assertEqual(self.page2.display_name, self.page1.get_raw_content())
+
+ def test_check_reference(self):
+ self.assertRegex(
+ self.page1.content_parsed,
+ '.*%s.*' % self.page2.display_name
+ )
+
+ def test_no_reference(self):
+ self.page2.reference = 'r3'
+ self.assertRegex(self.page1.content_parsed, '.*r2.*')
diff --git a/document_page_reference/views/assets.xml b/document_page_reference/views/assets.xml
new file mode 100644
index 00000000000..e292a0aad7e
--- /dev/null
+++ b/document_page_reference/views/assets.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/document_page_reference/views/document_page.xml b/document_page_reference/views/document_page.xml
new file mode 100644
index 00000000000..f4bf348688f
--- /dev/null
+++ b/document_page_reference/views/document_page.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+ document.page.form (in knowledge_reference)
+ document.page
+
+
+
+
+
+
+
+
+ oe_edit_only
+
+
+
+
+
+
+
+
+
+
+ document.page.search (in knowledge_reference)
+ document.page
+
+
+
+
+
+
+
+
+
+ document.page.tree (in knowledge_reference)
+ document.page
+
+
+
+
+
+
+
+
+
diff --git a/document_page_reference/views/report_document_page.xml b/document_page_reference/views/report_document_page.xml
new file mode 100755
index 00000000000..779465694c0
--- /dev/null
+++ b/document_page_reference/views/report_document_page.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ 1==0
+
+
+
+
+
+
+
From 2af3d4db329b97301d851ac99be7de5a6f8f2745 Mon Sep 17 00:00:00 2001
From: Jordi Ballester Alomar
Date: Fri, 29 Nov 2019 14:31:58 +0100
Subject: [PATCH 02/27] [IMP] document_page_reference: add option to create a
new page from a non-existing reference
---
document_page_reference/README.rst | 17 +++++-
.../i18n/document_page_reference.pot | 52 +++++++++++++++++++
.../models/document_page.py | 20 ++++---
.../static/description/index.html | 12 ++++-
.../static/src/js/editor.js | 13 +++--
5 files changed, 99 insertions(+), 15 deletions(-)
create mode 100644 document_page_reference/i18n/document_page_reference.pot
diff --git a/document_page_reference/README.rst b/document_page_reference/README.rst
index adc253cb4e5..34ad39975e8 100644
--- a/document_page_reference/README.rst
+++ b/document_page_reference/README.rst
@@ -7,9 +7,9 @@ Document Page Reference
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
+.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
- :alt: Beta
+ :alt: Alpha
.. |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
@@ -28,6 +28,11 @@ Document Page Reference
This module allows to add a reference name on documents and simplifies the link
between document pages.
+.. IMPORTANT::
+ This is an alpha version, the data model and design can change at any time without warning.
+ Only for development or testing purpose, do not use in production.
+ `More details on development status `_
+
**Table of contents**
.. contents::
@@ -76,6 +81,14 @@ 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-etobella| image:: https://github.com/etobella.png?size=40px
+ :target: https://github.com/etobella
+ :alt: etobella
+
+Current `maintainer `__:
+
+|maintainer-etobella|
+
This module is part of the `OCA/knowledge `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/document_page_reference/i18n/document_page_reference.pot b/document_page_reference/i18n/document_page_reference.pot
new file mode 100644
index 00000000000..b3ce20b2abd
--- /dev/null
+++ b/document_page_reference/i18n/document_page_reference.pot
@@ -0,0 +1,52 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * document_page_reference
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 11.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: document_page_reference
+#: model:ir.model.fields,field_description:document_page_reference.field_document_page_content_parsed
+msgid "Content Parsed"
+msgstr ""
+
+#. module: document_page_reference
+#: model:ir.model,name:document_page_reference.model_document_page
+msgid "Document Page"
+msgstr ""
+
+#. module: document_page_reference
+#: model:ir.model.fields,field_description:document_page_reference.field_document_page_reference
+msgid "Reference"
+msgstr ""
+
+#. module: document_page_reference
+#: code:addons/document_page_reference/models/document_page.py:65
+#, python-format
+msgid "Reference is not valid"
+msgstr ""
+
+#. module: document_page_reference
+#: code:addons/document_page_reference/models/document_page.py:70
+#, python-format
+msgid "Reference must be unique"
+msgstr ""
+
+#. module: document_page_reference
+#: model:ir.model.fields,help:document_page_reference.field_document_page_reference
+msgid "Used to find the document, it can contain letters, numbers and _"
+msgstr ""
+
+#. module: document_page_reference
+#: model:ir.ui.view,arch_db:document_page_reference.document_page_form_view
+msgid "internal_reference"
+msgstr ""
+
diff --git a/document_page_reference/models/document_page.py b/document_page_reference/models/document_page.py
index 1f5a67096d2..f75fa66616a 100644
--- a/document_page_reference/models/document_page.py
+++ b/document_page_reference/models/document_page.py
@@ -74,21 +74,25 @@ def _get_document(self, code):
document = self.search([('reference', '=', code)])
if document:
return document
- return False
+ else:
+ return self.env[self._name]
def get_reference(self, code):
element = self._get_document(code)
- if not element:
- return code
if self.env.context.get('raw_reference', False):
return html_escape(element.display_name)
- text = '%s'
- return text % (
+ text = """%s
+ """
+ if not element:
+ text = '%s' % text
+ res = text % (
element._name,
- element.id,
- html_escape(element.display_name)
+ element and element.id or '',
+ code,
+ html_escape(element.display_name or code),
)
+ return res
def _get_template_variables(self):
return {'ref': self.get_reference}
diff --git a/document_page_reference/static/description/index.html b/document_page_reference/static/description/index.html
index 87f2d10fa1d..92644b72617 100644
--- a/document_page_reference/static/description/index.html
+++ b/document_page_reference/static/description/index.html
@@ -3,7 +3,7 @@
-
+
Document Page Reference
-
-
Document Page Reference
+
+
+
+
+
+
+
Document Page Reference
-

+

This module allows to add a reference name on documents and simplifies
the link between document pages.
Table of contents
@@ -386,13 +391,13 @@
Document Page Reference
-
+
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.
-
+
Bugs are tracked on GitHub 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
@@ -400,21 +405,21 @@
Do not contact contributors directly about support or help with technical issues.
+
diff --git a/document_page_reference/static/src/js/editor.esm.js b/document_page_reference/static/src/js/editor.esm.js
new file mode 100644
index 00000000000..416bfb851f4
--- /dev/null
+++ b/document_page_reference/static/src/js/editor.esm.js
@@ -0,0 +1,32 @@
+import {HtmlField, htmlField} from "@web/views/fields/html/html_field";
+import {onMounted} from "@odoo/owl";
+import {registry} from "@web/core/registry";
+import {useService} from "@web/core/utils/hooks";
+
+class DocumentPageReferenceField extends HtmlField {
+ setup() {
+ super.setup();
+ this.orm = useService("orm");
+ this.action = useService("action");
+ onMounted(() => {
+ // eslint-disable-next-line no-undef
+ const links = document.querySelectorAll(".oe_direct_line");
+ links.forEach((link) => {
+ link.addEventListener("click", (event) =>
+ this._onClickDirectLink(event)
+ );
+ });
+ });
+ }
+ _onClickDirectLink(event) {
+ const {oeModel: model, oeId} = event.target.dataset;
+ const id = parseInt(oeId, 10);
+ this.orm.call(model, "get_formview_action", [[id]], {}).then((action) => {
+ this.action.doAction(action);
+ });
+ }
+}
+registry.category("fields").add("document_page_reference", {
+ ...htmlField,
+ component: DocumentPageReferenceField,
+});
diff --git a/document_page_reference/static/src/js/editor.js b/document_page_reference/static/src/js/editor.js
deleted file mode 100644
index 852df084ca9..00000000000
--- a/document_page_reference/static/src/js/editor.js
+++ /dev/null
@@ -1,34 +0,0 @@
-odoo.define("document_page_reference.backend", function (require) {
- "use strict";
-
- var field_registry = require("web.field_registry");
- var FieldTextHtmlSimple = require("web_editor.field.html");
- var FieldDocumentPage = FieldTextHtmlSimple.extend({
- events: _.extend({}, FieldTextHtmlSimple.prototype.events, {
- "click .oe_direct_line": "_onClickDirectLink",
- }),
- _onClickDirectLink: function (event) {
- var self = this;
- event.preventDefault();
- event.stopPropagation();
- var element = $(event.target).closest(".oe_direct_line")[0];
- var default_reference = element.name;
- var model = $(event.target).data("oe-model");
- var id = $(event.target).data("oe-id");
- var context = this.record.getContext(this.recordParams);
- if (default_reference) {
- context.default_reference = default_reference;
- }
- this._rpc({
- model: model,
- method: "get_formview_action",
- args: [[parseInt(id, 10)]],
- context: context,
- }).then(function (action) {
- self.trigger_up("do_action", {action: action});
- });
- },
- });
- field_registry.add("document_page_reference", FieldDocumentPage);
- return FieldDocumentPage;
-});
diff --git a/document_page_reference/tests/test_document_reference.py b/document_page_reference/tests/test_document_reference.py
index 1a2ba268021..73581b8b9dc 100644
--- a/document_page_reference/tests/test_document_reference.py
+++ b/document_page_reference/tests/test_document_reference.py
@@ -18,61 +18,48 @@ def setUpClass(cls):
{"name": "Test Page 1", "content": "${r1}", "reference": "r2"}
)
- def test_constrains_01(self):
+ def test_constraints_duplicate_reference(self):
+ """Should raise if reference is not unique (same as another)."""
with self.assertRaises(ValidationError):
self.page2.write({"reference": self.page1.reference})
- def test_constrains_02(self):
+ def test_constraints_invalid_reference(self):
+ """Should raise if reference does not match the required pattern."""
with self.assertRaises(ValidationError):
self.page2.write({"reference": self.page2.reference + "-02"})
- def test_no_contrains(self):
- self.page1.write({"reference": False})
- self.page2.write({"reference": False})
- self.assertEqual(self.page1.reference, self.page2.reference)
-
- def test_check_raw(self):
- self.assertEqual(self.page2.display_name, self.page1.get_raw_content())
-
- def test_check_reference(self):
- self.assertRegex(self.page1.content_parsed, ".*%s.*" % self.page2.display_name)
-
- def test_no_reference(self):
- self.page2.reference = "r3"
- self.assertRegex(self.page1.content_parsed, ".*r2.*")
-
def test_auto_reference(self):
"""Test if reference is proposed when saving a page without one."""
self.assertEqual(self.page1.reference, "R1")
new_page = self.page_obj.create(
- {"name": "Test Page with no rEfErenCe", "content": "some content"}
+ {"name": "Test Page with no reference", "content": "some content"}
)
self.assertEqual(new_page.reference, "test_page_with_no_reference")
- new_page_duplicated_name = self.page_obj.create(
- {
- "name": "test page with no reference",
- "content": "this should have an empty reference "
- "because reference must be unique",
- }
- )
- self.assertFalse(new_page_duplicated_name.reference)
+ with self.assertRaises(ValidationError):
+ new_page_duplicated_name = self.page_obj.create(
+ {
+ "name": "test page with no reference",
+ "content": "this should have an empty reference "
+ "because reference must be unique",
+ }
+ )
+ self.assertFalse(new_page_duplicated_name.reference)
def test_get_formview_action(self):
res = self.page1.get_formview_action()
view_id = self.env.ref("document_page.view_wiki_form").id
- expected_result = {
+ expected_keys = {
"type": "ir.actions.act_window",
- "context": {},
"res_model": "document.page",
"res_id": self.page1.id,
- "view_mode": "form",
- "view_type": "form",
+ "context": {},
"target": "current",
"views": [(view_id, "form")],
}
- self.assertEqual(res, expected_result)
+ for key, expected_value in expected_keys.items():
+ self.assertEqual(res.get(key), expected_value, f"Mismatch in key: {key}")
def test_compute_content_parsed(self):
- self.page1.content = ""
+ self.page1.content = "
"
self.page1._compute_content_parsed()
self.assertEqual(str(self.page1.content_parsed), "")
diff --git a/document_page_reference/views/document_page.xml b/document_page_reference/views/document_page.xml
index a9459fcceb2..9f4c7ce6364 100644
--- a/document_page_reference/views/document_page.xml
+++ b/document_page_reference/views/document_page.xml
@@ -27,6 +27,7 @@
+
+
document.page.search (in knowledge_reference)
document.page
@@ -54,6 +56,7 @@
+
document.page.tree (in knowledge_reference)
document.page
diff --git a/document_page_reference/views/report_document_page.xml b/document_page_reference/views/report_document_page.xml
index 5ecaa1509ee..47ff1298d90 100644
--- a/document_page_reference/views/report_document_page.xml
+++ b/document_page_reference/views/report_document_page.xml
@@ -8,7 +8,7 @@
1==0
-
+
From 8fd2d656a83e9b5b2bfd76604faee26d61cb56fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?=
Date: Thu, 21 Aug 2025 12:26:49 +0200
Subject: [PATCH 23/27] [IMP+FIX] document_page_reference: Refactor code
related to https://github.com/OCA/knowledge/pull/550#issuecomment-3108953008
TT55514
---
document_page_reference/README.rst | 4 +-
document_page_reference/__manifest__.py | 2 +-
.../migrations/18.0.1.1.0/post-migration.py | 12 ++++
.../models/document_page.py | 56 +++++++------------
document_page_reference/readme/USAGE.md | 2 +-
.../static/description/index.html | 4 +-
.../tests/test_document_reference.py | 26 ++++++---
.../views/report_document_page.xml | 4 +-
8 files changed, 57 insertions(+), 53 deletions(-)
create mode 100644 document_page_reference/migrations/18.0.1.1.0/post-migration.py
diff --git a/document_page_reference/README.rst b/document_page_reference/README.rst
index 6b7a0bfe783..791713790ef 100644
--- a/document_page_reference/README.rst
+++ b/document_page_reference/README.rst
@@ -11,7 +11,7 @@ Document Page Reference
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:f5181a95b68e52c04e1b76ba19f503b5bfc8d365939debe14edbb40413101321
+ !! source digest: sha256:3081d597b92f6ce09c2259ce73d935f1215e5c8a1b171983c8d2c727c133a38e
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
@@ -43,7 +43,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.
diff --git a/document_page_reference/__manifest__.py b/document_page_reference/__manifest__.py
index 988fc2235d9..90da7ccb981 100644
--- a/document_page_reference/__manifest__.py
+++ b/document_page_reference/__manifest__.py
@@ -5,7 +5,7 @@
"name": "Document Page Reference",
"summary": """
Include references on document pages""",
- "version": "18.0.1.0.0",
+ "version": "18.0.2.0.0",
"license": "AGPL-3",
"author": "Creu Blanca,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/knowledge",
diff --git a/document_page_reference/migrations/18.0.1.1.0/post-migration.py b/document_page_reference/migrations/18.0.1.1.0/post-migration.py
new file mode 100644
index 00000000000..0f15f1ebb2e
--- /dev/null
+++ b/document_page_reference/migrations/18.0.1.1.0/post-migration.py
@@ -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)
diff --git a/document_page_reference/models/document_page.py b/document_page_reference/models/document_page.py
index f745ee41b6f..6fb1a481cd6 100644
--- a/document_page_reference/models/document_page.py
+++ b/document_page_reference/models/document_page.py
@@ -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 @@ def get_formview_action(self, access_uid=None):
@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 @@ def _check_reference_validity(self):
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
- )
- return record.content
+ 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 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:
- return (
- f"{sanitized_code}"
- )
+ 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""
- f"{html_escape(doc.display_name)}"
+ f""
+ f"{name}"
)
def get_raw_content(self):
diff --git a/document_page_reference/readme/USAGE.md b/document_page_reference/readme/USAGE.md
index 6fa5061fe50..6a9530ff58f 100644
--- a/document_page_reference/readme/USAGE.md
+++ b/document_page_reference/readme/USAGE.md
@@ -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.
diff --git a/document_page_reference/static/description/index.html b/document_page_reference/static/description/index.html
index a2cd632afe4..085d95237ed 100644
--- a/document_page_reference/static/description/index.html
+++ b/document_page_reference/static/description/index.html
@@ -372,7 +372,7 @@ Document Page Reference
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:f5181a95b68e52c04e1b76ba19f503b5bfc8d365939debe14edbb40413101321
+!! source digest: sha256:3081d597b92f6ce09c2259ce73d935f1215e5c8a1b171983c8d2c727c133a38e
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

This module allows to add a reference name on documents and simplifies
@@ -392,7 +392,7 @@
Document Page Reference
-
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.
diff --git a/document_page_reference/tests/test_document_reference.py b/document_page_reference/tests/test_document_reference.py
index 73581b8b9dc..7ef9b644c03 100644
--- a/document_page_reference/tests/test_document_reference.py
+++ b/document_page_reference/tests/test_document_reference.py
@@ -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 @@ def test_constraints_invalid_reference(self):
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 @@ def test_get_formview_action(self):
"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 @@ def test_get_formview_action(self):
self.assertEqual(res.get(key), expected_value, f"Mismatch in key: {key}")
def test_compute_content_parsed(self):
- self.page1.content = ""
- self.page1._compute_content_parsed()
- self.assertEqual(str(self.page1.content_parsed), "")
+ self.page1.content = Markup("")
+ self.assertEqual(self.page1.content_parsed, Markup(""))
diff --git a/document_page_reference/views/report_document_page.xml b/document_page_reference/views/report_document_page.xml
index 47ff1298d90..de2e43795fa 100644
--- a/document_page_reference/views/report_document_page.xml
+++ b/document_page_reference/views/report_document_page.xml
@@ -4,10 +4,10 @@
id="report_documentpage_doc"
inherit_id="document_page.report_documentpage_doc"
>
-
+
1==0
-
+
From 7c083da31c9896590727dff169610085ced9c45c Mon Sep 17 00:00:00 2001
From: Enric Tobella
Date: Wed, 17 Dec 2025 09:55:54 +0100
Subject: [PATCH 24/27] [FIX] document_page_reference: Fix tests to use HTML
---
document_page_reference/README.rst | 2 +-
document_page_reference/__manifest__.py | 2 +-
document_page_reference/static/description/index.html | 2 +-
document_page_reference/tests/test_document_reference.py | 4 ++--
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/document_page_reference/README.rst b/document_page_reference/README.rst
index 791713790ef..83c3b5aaf60 100644
--- a/document_page_reference/README.rst
+++ b/document_page_reference/README.rst
@@ -11,7 +11,7 @@ Document Page Reference
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:3081d597b92f6ce09c2259ce73d935f1215e5c8a1b171983c8d2c727c133a38e
+ !! source digest: sha256:654977301f8ef4677d564b8e8401b7bf91b49393ee291bcee15012eaa42d7a8f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
diff --git a/document_page_reference/__manifest__.py b/document_page_reference/__manifest__.py
index 90da7ccb981..01a3fe116cb 100644
--- a/document_page_reference/__manifest__.py
+++ b/document_page_reference/__manifest__.py
@@ -5,7 +5,7 @@
"name": "Document Page Reference",
"summary": """
Include references on document pages""",
- "version": "18.0.2.0.0",
+ "version": "18.0.2.1.0",
"license": "AGPL-3",
"author": "Creu Blanca,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/knowledge",
diff --git a/document_page_reference/static/description/index.html b/document_page_reference/static/description/index.html
index 085d95237ed..579f5fc48a7 100644
--- a/document_page_reference/static/description/index.html
+++ b/document_page_reference/static/description/index.html
@@ -372,7 +372,7 @@ Document Page Reference
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:3081d597b92f6ce09c2259ce73d935f1215e5c8a1b171983c8d2c727c133a38e
+!! source digest: sha256:654977301f8ef4677d564b8e8401b7bf91b49393ee291bcee15012eaa42d7a8f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

This module allows to add a reference name on documents and simplifies
diff --git a/document_page_reference/tests/test_document_reference.py b/document_page_reference/tests/test_document_reference.py
index 7ef9b644c03..20ec5aa9464 100644
--- a/document_page_reference/tests/test_document_reference.py
+++ b/document_page_reference/tests/test_document_reference.py
@@ -51,8 +51,8 @@ def test_auto_reference(self):
new_page_duplicated_name = self.page_obj.create(
{
"name": "test page with no reference",
- "content": "this should have an empty reference "
- "because reference must be unique",
+ "content": "
this should have an empty reference "
+ "because reference must be unique
",
}
)
self.assertFalse(new_page_duplicated_name.reference)
From 58b9240af87ac80af08a4c52c7c15d99aed4f6ea Mon Sep 17 00:00:00 2001
From: Grzegorz Rutecki
Date: Mon, 16 Mar 2026 11:33:11 +0100
Subject: [PATCH 25/27] [MIG] document_page_reference: Migration to 19.0
---
document_page_reference/__manifest__.py | 2 +-
document_page_reference/static/src/js/editor.esm.js | 1 -
setup/document_page_reference/pyproject.toml | 7 +++++++
3 files changed, 8 insertions(+), 2 deletions(-)
create mode 100644 setup/document_page_reference/pyproject.toml
diff --git a/document_page_reference/__manifest__.py b/document_page_reference/__manifest__.py
index 01a3fe116cb..e1f8b1f838a 100644
--- a/document_page_reference/__manifest__.py
+++ b/document_page_reference/__manifest__.py
@@ -5,7 +5,7 @@
"name": "Document Page Reference",
"summary": """
Include references on document pages""",
- "version": "18.0.2.1.0",
+ "version": "19.0.2.1.0",
"license": "AGPL-3",
"author": "Creu Blanca,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/knowledge",
diff --git a/document_page_reference/static/src/js/editor.esm.js b/document_page_reference/static/src/js/editor.esm.js
index 416bfb851f4..0bbfa7bbdbb 100644
--- a/document_page_reference/static/src/js/editor.esm.js
+++ b/document_page_reference/static/src/js/editor.esm.js
@@ -9,7 +9,6 @@ class DocumentPageReferenceField extends HtmlField {
this.orm = useService("orm");
this.action = useService("action");
onMounted(() => {
- // eslint-disable-next-line no-undef
const links = document.querySelectorAll(".oe_direct_line");
links.forEach((link) => {
link.addEventListener("click", (event) =>
diff --git a/setup/document_page_reference/pyproject.toml b/setup/document_page_reference/pyproject.toml
new file mode 100644
index 00000000000..e2346ba6966
--- /dev/null
+++ b/setup/document_page_reference/pyproject.toml
@@ -0,0 +1,7 @@
+[project]
+name = "odoo-addon-document_page_reference"
+dynamic = ["version"]
+
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
From add72becfbbb14935debcd7fd0fba9935fe68be4 Mon Sep 17 00:00:00 2001
From: Don Kendall
Date: Fri, 19 Jun 2026 07:55:48 -0400
Subject: [PATCH 26/27] [FIX] document_page_reference: render reference links
on 19.0
19.0 Html fields return Markup; Markup.replace(token, plain_str) escapes the
injected anchor, so content_parsed stored the as literal text and no
.oe_direct_line element rendered. Restore 18.0's get_content shape: seed with
Markup(raw), return Markup() from _resolve_reference (+ backend_url href and the
second pass that re-resolves already-rendered links). Bind the click handler via
one delegated useExternalListener instead of per-node onMounted listeners.
---
.../models/document_page.py | 20 ++++++++++++----
.../static/src/js/editor.esm.js | 24 +++++++++++--------
2 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/document_page_reference/models/document_page.py b/document_page_reference/models/document_page.py
index 6fb1a481cd6..d3ba55fab9b 100644
--- a/document_page_reference/models/document_page.py
+++ b/document_page_reference/models/document_page.py
@@ -50,12 +50,23 @@ def _get_document(self, code):
def get_content(self):
self.ensure_one()
- content_parsed = raw = self.content or ""
+ raw = str(self.content or "")
+ content_parsed = Markup(raw)
for text in re.findall(r"\{\{.*?\}\}", raw):
- reference = text.replace("{{", "").replace("}}", "")
+ reference = re.sub(r"<[^>]*>", "", text).replace("{{", "").replace("}}", "")
content_parsed = content_parsed.replace(
text, self._resolve_reference(reference)
)
+ link_regex = (
+ r"]*class=['\"][^'\"]*oe_direct_line[^'\"]*['\"]"
+ r"[^>]*name=['\"]([^'\"]*)['\"][^>]*>.*?"
+ )
+ for match in re.finditer(link_regex, raw):
+ full_link = match.group(0)
+ reference = match.group(1)
+ content_parsed = content_parsed.replace(
+ Markup(full_link), self._resolve_reference(reference)
+ )
return content_parsed
def _resolve_reference(self, code):
@@ -66,8 +77,9 @@ def _resolve_reference(self, code):
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""
f"{name}"
)
diff --git a/document_page_reference/static/src/js/editor.esm.js b/document_page_reference/static/src/js/editor.esm.js
index 0bbfa7bbdbb..e8ff07dbf47 100644
--- a/document_page_reference/static/src/js/editor.esm.js
+++ b/document_page_reference/static/src/js/editor.esm.js
@@ -1,6 +1,6 @@
import {HtmlField, htmlField} from "@web/views/fields/html/html_field";
-import {onMounted} from "@odoo/owl";
import {registry} from "@web/core/registry";
+import {useExternalListener} from "@odoo/owl";
import {useService} from "@web/core/utils/hooks";
class DocumentPageReferenceField extends HtmlField {
@@ -8,18 +8,22 @@ class DocumentPageReferenceField extends HtmlField {
super.setup();
this.orm = useService("orm");
this.action = useService("action");
- onMounted(() => {
- const links = document.querySelectorAll(".oe_direct_line");
- links.forEach((link) => {
- link.addEventListener("click", (event) =>
- this._onClickDirectLink(event)
- );
- });
+ // Delegate: one listener that resolves the link at click time, so it
+ // survives the html field re-rendering and is auto-removed on unmount.
+ useExternalListener(document, "click", (event) => {
+ const link = event.target.closest?.(".oe_direct_line");
+ if (link) {
+ this._onClickDirectLink(event, link);
+ }
});
}
- _onClickDirectLink(event) {
- const {oeModel: model, oeId} = event.target.dataset;
+ _onClickDirectLink(event, link) {
+ const {oeModel: model, oeId} = link.dataset;
const id = parseInt(oeId, 10);
+ if (!model || !id) {
+ return;
+ }
+ event.preventDefault();
this.orm.call(model, "get_formview_action", [[id]], {}).then((action) => {
this.action.doAction(action);
});
From e0ba6b4e82922df587ae71401bd38e58d61511e9 Mon Sep 17 00:00:00 2001
From: Don Kendall
Date: Fri, 19 Jun 2026 11:28:35 -0400
Subject: [PATCH 27/27] [FIX] document_page_reference: render content_parsed in
OWL form
Replace the dead oe_read_only/oe_edit_only CSS toggle (an open record is always
o_form_editable in OWL, so content_parsed never showed) with readonly content_parsed
+ invisible/required content. Companion to the Markup-render fix.
---
document_page_reference/README.rst | 10 +++++-----
.../static/description/index.html | 6 +++---
document_page_reference/views/document_page.xml | 17 +++++++++--------
3 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/document_page_reference/README.rst b/document_page_reference/README.rst
index 83c3b5aaf60..169f919762e 100644
--- a/document_page_reference/README.rst
+++ b/document_page_reference/README.rst
@@ -21,13 +21,13 @@ Document Page Reference
: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
- :target: https://github.com/OCA/knowledge/tree/18.0/document_page_reference
+ :target: https://github.com/OCA/knowledge/tree/19.0/document_page_reference
:alt: OCA/knowledge
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/knowledge-18-0/knowledge-18-0-document_page_reference
+ :target: https://translation.odoo-community.org/projects/knowledge-19-0/knowledge-19-0-document_page_reference
: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/knowledge&target_branch=18.0
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/knowledge&target_branch=19.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -53,7 +53,7 @@ Bug Tracker
Bugs are tracked on `GitHub 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 `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -91,6 +91,6 @@ Current `maintainer `__:
|maintainer-etobella|
-This module is part of the `OCA/knowledge `_ project on GitHub.
+This module is part of the `OCA/knowledge `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/document_page_reference/static/description/index.html b/document_page_reference/static/description/index.html
index 579f5fc48a7..8dd6cf7cce4 100644
--- a/document_page_reference/static/description/index.html
+++ b/document_page_reference/static/description/index.html
@@ -374,7 +374,7 @@ Document Page Reference
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:654977301f8ef4677d564b8e8401b7bf91b49393ee291bcee15012eaa42d7a8f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

This module allows to add a reference name on documents and simplifies
the link between document pages.
Table of contents
@@ -401,7 +401,7 @@
Bugs are tracked on GitHub 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.
+feedback.
Do not contact contributors directly about support or help with technical issues.
diff --git a/document_page_reference/views/document_page.xml b/document_page_reference/views/document_page.xml
index 9f4c7ce6364..7b9f7cb0953 100644
--- a/document_page_reference/views/document_page.xml
+++ b/document_page_reference/views/document_page.xml
@@ -15,16 +15,17 @@
/>
-
- oe_edit_only
-
+
+ type == 'category'
+ type != 'category'
+
@@ -33,16 +34,16 @@
document.page
-
- oe_edit_only
-
+
+ 1
+