From 864c0a000cf0758d4b3243125386822ab3cfcdf5 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Mon, 15 Dec 2025 09:59:54 +0100 Subject: [PATCH 1/3] [IMP] document_page: Use Odoo HTML Diff comparison method --- document_page/README.rst | 27 ++++++-- document_page/__manifest__.py | 5 +- document_page/models/document_page_history.py | 27 ++------ document_page/readme/CONTRIBUTORS.md | 7 +- document_page/readme/USAGE.md | 5 ++ document_page/static/description/index.html | 64 ++++++++++--------- .../static/src/scss/document_page.scss | 52 +++++++++------ .../src/scss/document_page_variables.scss | 5 ++ document_page/tests/test_document_page.py | 14 ++-- .../tests/test_document_page_history.py | 15 +++-- .../tests/test_document_page_show_diff.py | 11 +++- document_page/views/document_page_history.xml | 1 + .../wizard/document_page_show_diff.py | 2 +- 13 files changed, 140 insertions(+), 95 deletions(-) create mode 100644 document_page/static/src/scss/document_page_variables.scss diff --git a/document_page/README.rst b/document_page/README.rst index 8c233970932..a9a9cbce098 100644 --- a/document_page/README.rst +++ b/document_page/README.rst @@ -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 ============= @@ -17,7 +13,7 @@ Document Page .. |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 @@ -61,6 +57,12 @@ To use this module, you need to: - Click on Pages to create pages and select the previous category to use the template +Improve diff of history +----------------------- + +If you want to improve how history is shown, you can install html_diff +python library. A new comparison method will be installed. + Bug Tracker =========== @@ -83,23 +85,34 @@ Contributors ------------ - Gervais Naoussi + - Maxime Chambreuil + - Iván Todorovich + - Jose Maria Alzaga + - Lois Rilo + - Simone Orsi + - `Tecnativa `__: - Ernesto Tejeda - Víctor Martínez -Trobz +- Trobz + + - Dung Tran -- Dung Tran - `Sygel `__: - Ángel García de la Chica Herrera +- `Dixmit `__: + + - Enric Tobella + Other credits ------------- diff --git a/document_page/__manifest__.py b/document_page/__manifest__.py index ccd409b1d49..ed69870965c 100644 --- a/document_page/__manifest__.py +++ b/document_page/__manifest__.py @@ -17,7 +17,7 @@ ], "website": "https://github.com/OCA/knowledge", "license": "AGPL-3", - "depends": ["mail", "document_knowledge"], + "depends": ["mail", "document_knowledge", "web_editor"], "data": [ "security/document_page_security.xml", "security/ir.model.access.csv", @@ -30,6 +30,9 @@ ], "demo": ["demo/document_page.xml"], "assets": { + "web._assets_primary_variables": [ + "document_page/static/src/**/document_page_variables.scss", + ], "web.assets_backend": [ "document_page/static/src/scss/document_page.scss", "document_page/static/src/js/document_page_kanban_controller.esm.js", diff --git a/document_page/models/document_page_history.py b/document_page/models/document_page_history.py index b2bf80a6506..ef39b3d616a 100644 --- a/document_page/models/document_page_history.py +++ b/document_page/models/document_page_history.py @@ -1,10 +1,13 @@ # Copyright (C) 2004-2010 Tiny SPRL (). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import difflib from odoo import api, fields, models +from odoo.addons.web_editor.models.diff_utils import ( + generate_comparison, +) + class DocumentPageHistory(models.Model): """This model is necessary to manage a document history.""" @@ -17,7 +20,7 @@ class DocumentPageHistory(models.Model): name = fields.Char(index=True) summary = fields.Char(index=True) content = fields.Html(sanitize=False) - diff = fields.Html(compute="_compute_diff") + diff = fields.Html(compute="_compute_diff", sanitize_tags=False) company_id = fields.Many2one( "res.company", @@ -43,28 +46,10 @@ def _compute_diff(self): ) rec.diff = self._get_diff(prev.id, rec.id) - @api.model def _get_diff(self, v1, v2): - """Return the difference between two version of document version.""" text1 = v1 and self.browse(v1).content or "" text2 = v2 and self.browse(v2).content or "" - # Include line breaks to make it more readable - # TODO: consider using a beautify library directly on the content - text1 = text1.replace("

", "

\r\n

") - text2 = text2.replace("

", "

\r\n

") - line1 = text1.splitlines(True) - line2 = text2.splitlines(True) - if line1 == line2: - return self.env._("There are no changes in revisions.") - else: - diff = difflib.HtmlDiff() - return diff.make_table( - line1, - line2, - f"Revision-{v1}", - f"Revision-{v2}", - context=True, - ) + return generate_comparison(text1, text2) @api.depends("page_id") def _compute_display_name(self): diff --git a/document_page/readme/CONTRIBUTORS.md b/document_page/readme/CONTRIBUTORS.md index 8e410db7c09..2c6614720de 100644 --- a/document_page/readme/CONTRIBUTORS.md +++ b/document_page/readme/CONTRIBUTORS.md @@ -8,8 +8,11 @@ - Ernesto Tejeda - Víctor Martínez -Trobz +- Trobz + - Dung Tran \<\> -- Dung Tran \<\> - [Sygel](https://www.sygel.es): - Ángel García de la Chica Herrera + +- [Dixmit](https://www.dixmit.com): + - Enric Tobella diff --git a/document_page/readme/USAGE.md b/document_page/readme/USAGE.md index 8ba0011a40b..d272c8d7e7d 100644 --- a/document_page/readme/USAGE.md +++ b/document_page/readme/USAGE.md @@ -5,3 +5,8 @@ To use this module, you need to: the template - Click on Pages to create pages and select the previous category to use the template + +## Improve diff of history + +If you want to improve how history is shown, you can install html_diff python library. +A new comparison method will be installed. diff --git a/document_page/static/description/index.html b/document_page/static/description/index.html index 18eafce8984..1f06b405eaf 100644 --- a/document_page/static/description/index.html +++ b/document_page/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Document Page -

+
+

Document Page

- - -Odoo Community Association - -
-

Document Page

-

Beta License: AGPL-3 OCA/knowledge Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/knowledge Translate me on Weblate Try me on Runboat

This module allows you to write web pages for internal documentation.

Table of contents

-

Installation

+

Installation

This module depends on module knowledge. So make sure to have it in your addons list.

-

Configuration

+

Configuration

No configuration required.

-

Usage

+

Usage

To use this module, you need to:

  • Go to Knowledge menu
  • @@ -411,9 +409,14 @@

    Usage

  • Click on Pages to create pages and select the previous category to use the template
+
+

Improve diff of history

+

If you want to improve how history is shown, you can install html_diff +python library. A new comparison method will be installed.

+
-

Bug Tracker

+

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 @@ -421,15 +424,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • OpenERP SA
-

Contributors

+

Contributors

- -

Trobz

-
-

Other credits

+

Other credits

The development of this module has been financially supported by:

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -475,6 +482,5 @@

Maintainers

-
diff --git a/document_page/static/src/scss/document_page.scss b/document_page/static/src/scss/document_page.scss index 82cfbaaccc8..064d415a9dd 100644 --- a/document_page/static/src/scss/document_page.scss +++ b/document_page/static/src/scss/document_page.scss @@ -1,28 +1,40 @@ -table.diff { - font-family: Courier; - border: medium; +.o_document_page_diff { + table.diff { + font-family: Courier; + border: medium; - .diff_header { - background-color: #e0e0e0; - } + .diff_header { + background-color: $o_document_page_diff_header_background; + } - td.diff_header { - text-align: right; - } + td.diff_header { + text-align: right; + } - .diff_next { - background-color: #c0c0c0; - } + .diff_next { + background-color: $o_document_page_diff_next_background; + } - .diff_add { - background-color: #aaffaa; - } + .diff_add { + background-color: $o_document_page_diff_add_background; + } - .diff_chg { - background-color: #ffff77; - } + .diff_chg { + background-color: $o_document_page_diff_change_background; + } - .diff_sub { - background-color: #ffaaaa; + .diff_sub { + background-color: $o_document_page_diff_subtract_background; + } + } + removed { + display: inline; + text-decoration: line-through; + opacity: 0.5; + background-color: $o_document_page_diff_subtract_background; + } + added { + display: inline; + background-color: $o_document_page_diff_add_background; } } diff --git a/document_page/static/src/scss/document_page_variables.scss b/document_page/static/src/scss/document_page_variables.scss new file mode 100644 index 00000000000..0c4d02fedeb --- /dev/null +++ b/document_page/static/src/scss/document_page_variables.scss @@ -0,0 +1,5 @@ +$o_document_page_diff_header_background: #e0e0e0; +$o_document_page_diff_next_background: #c0c0c0; +$o_document_page_diff_add_background: #aaffaa; +$o_document_page_diff_change_background: #ffff77; +$o_document_page_diff_subtract_background: #ffaaaa; diff --git a/document_page/tests/test_document_page.py b/document_page/tests/test_document_page.py index 7a2cc96bd9b..e9b2fd7d151 100644 --- a/document_page/tests/test_document_page.py +++ b/document_page/tests/test_document_page.py @@ -31,12 +31,16 @@ def test_category_template(self): self.assertEqual(page.content, self.category1.template) def test_page_history_diff(self): - page = self.page_obj.create({"name": "Test Page 3", "content": "Test content"}) - page.content = "New content" + page = self.page_obj.create( + {"name": "Test Page 3", "content": "
Test content
"} + ) + page.content = "
New content
" self.assertIsNotNone(page.history_ids[0].diff) def test_page_link(self): - page = self.page_obj.create({"name": "Test Page 3", "content": "Test content"}) + page = self.page_obj.create( + {"name": "Test Page 3", "content": "
Test content
"} + ) self.assertEqual( page.backend_url, f"/web#id={page.id}&model=document.page&view_type=form", @@ -49,7 +53,9 @@ def test_page_link(self): ) def test_page_copy(self): - page = self.page_obj.create({"name": "Test Page 3", "content": "Test content"}) + page = self.page_obj.create( + {"name": "Test Page 3", "content": "
Test content
"} + ) page_copy = page.copy() self.assertEqual(page_copy.name, page.name + " (copy)") self.assertEqual(page_copy.content, page.content) diff --git a/document_page/tests/test_document_page_history.py b/document_page/tests/test_document_page_history.py index 856be77f783..dd09334191f 100644 --- a/document_page/tests/test_document_page_history.py +++ b/document_page/tests/test_document_page_history.py @@ -6,14 +6,15 @@ class TestDocumentPageHistory(common.TransactionCase): def test_page_history_demo_page1(self): """Test page history demo page1.""" - page = self.env.ref("document_page.demo_page1") - page.content = "Test content updated" + page = self.env["document.page"].create( + { + "name": "Test Page", + "content": "
Initial content
", + } + ) + page.content = "
Test content updated
" history_document = self.env["document.page.history"] history_pages = history_document.search([("page_id", "=", page.id)]) active_ids = [i.id for i in history_pages] - result = history_document._get_diff(active_ids[0], active_ids[0]) - self.assertEqual(result, "There are no changes in revisions.") - - result = history_document._get_diff(active_ids[0], active_ids[1]) - self.assertNotEqual(result, "There are no changes in revisions.") + self.assertEqual(result, page.content) diff --git a/document_page/tests/test_document_page_show_diff.py b/document_page/tests/test_document_page_show_diff.py index 811c35e7cde..54f564ecc73 100644 --- a/document_page/tests/test_document_page_show_diff.py +++ b/document_page/tests/test_document_page_show_diff.py @@ -8,7 +8,12 @@ class TestDocumentPageShowDiff(common.TransactionCase): def test_show_demo_page1_diff(self): """Show test page history difference.""" - page = self.env.ref("document_page.demo_page1") + page = self.env["document.page"].create( + { + "name": "Test Page", + "content": "
Initial content
", + } + ) show_diff_object = self.env["wizard.document.page.history.show_diff"] @@ -21,8 +26,8 @@ def test_show_demo_page1_diff(self): )._get_diff() ) - page.write({"content": "Text content updated"}) - page.write({"content": "Text updated"}) + page.write({"content": "
Text content updated
"}) + page.write({"content": "
Text updated
"}) history_pages = history_document.search([("page_id", "=", page.id)]) diff --git a/document_page/views/document_page_history.xml b/document_page/views/document_page_history.xml index c8353acfa68..a8bf7cf4d1a 100644 --- a/document_page/views/document_page_history.xml +++ b/document_page/views/document_page_history.xml @@ -69,6 +69,7 @@ diff --git a/document_page/wizard/document_page_show_diff.py b/document_page/wizard/document_page_show_diff.py index e7bce5a00bb..1bb08ddb4d6 100644 --- a/document_page/wizard/document_page_show_diff.py +++ b/document_page/wizard/document_page_show_diff.py @@ -27,4 +27,4 @@ def _get_diff(self): raise UserError(self.env._("Select one or maximum two history revisions!")) return diff - diff = fields.Html(readonly=True, default=_get_diff) + diff = fields.Html(readonly=True, default=_get_diff, sanitize_tags=False) From c107ba5657ca1237dd57da58b9be204289d1a9d4 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Wed, 17 Dec 2025 09:55:37 +0100 Subject: [PATCH 2/3] [FIX] document_page_approval: Fix tests to use real html --- .../tests/test_document_page_approval.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/document_page_approval/tests/test_document_page_approval.py b/document_page_approval/tests/test_document_page_approval.py index 375fe7468f6..01a00a73505 100644 --- a/document_page_approval/tests/test_document_page_approval.py +++ b/document_page_approval/tests/test_document_page_approval.py @@ -42,7 +42,7 @@ def setUpClass(cls): { "name": "This page requires approval", "parent_id": cls.category2.id, - "content": "This content will require approval", + "content": "

This content will require approval

", } ) @@ -73,7 +73,7 @@ def test_change_request_approve(self): self.assertEqual(chreq.content, page.content) # Create new change request - page.write({"content": "New content"}) + page.write({"content": "

New content

"}) page.invalidate_model() # Recompute fields chreq = self.history_obj.search( [("page_id", "=", page.id), ("state", "!=", "approved")], limit=1 @@ -81,14 +81,14 @@ def test_change_request_approve(self): # Approve new changes chreq.with_user(self.user2).action_approve() - self.assertEqual(page.content, "New content") + self.assertEqual(page.content, "

New content

") def test_change_request_auto_approve(self): """Test that a page without approval required auto-approves changes.""" page = self.page1 self.assertFalse(page.is_approval_required) - page.write({"content": "New content"}) - self.assertEqual(page.content, "New content") + page.write({"content": "

New content

"}) + self.assertEqual(page.content, "

New content

") def test_change_request_from_scratch(self): """Test a full change request lifecycle from draft to approval.""" @@ -104,7 +104,7 @@ def test_change_request_from_scratch(self): { "page_id": page.id, "summary": "Changed something", - "content": "New content", + "content": "

New content

", } ) @@ -245,7 +245,7 @@ def test_diff_computation(self): self.history_obj.create( { "page_id": self.page2.id, - "content": "Version 1", + "content": "

Version 1

", "state": "approved", } ) @@ -253,7 +253,7 @@ def test_diff_computation(self): chreq2 = self.history_obj.create( { "page_id": self.page2.id, - "content": "Version 2", + "content": "

Version 2

", } ) chreq2._compute_diff() From 92775bd6547e14d1666229480e5f581c86c5d3f9 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Wed, 17 Dec 2025 09:55:54 +0100 Subject: [PATCH 3/3] [FIX] document_page_reference: Fix tests to use HTML --- document_page_reference/tests/test_document_reference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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)