diff --git a/html_text/README.rst b/html_text/README.rst
new file mode 100644
index 00000000000..086249877ed
--- /dev/null
+++ b/html_text/README.rst
@@ -0,0 +1,135 @@
+.. image:: https://odoo-community.org/readme-banner-image
+ :target: https://odoo-community.org/get-involved?utm_source=readme
+ :alt: Odoo Community Association
+
+====================
+Text from HTML field
+====================
+
+..
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! source digest: sha256:fe740f05581bad009a7e9148f00a9c12e3663ba632deda6172d725c5910ae36a
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |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%2Fserver--tools-lightgray.png?logo=github
+ :target: https://github.com/OCA/server-tools/tree/19.0/html_text
+ :alt: OCA/server-tools
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/server-tools-19-0/server-tools-19-0-html_text
+ :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/server-tools&target_branch=19.0
+ :alt: Try me on Runboat
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module provides some technical features that allow to extract text
+from any chunk of HTML, without HTML tags or attributes. You can chose
+either:
+
+- To truncate the result by amount of words or characters.
+- To append an ellipsis (or any character(s)) at the end of the result.
+
+It can be used to easily generate excerpts.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+This module just adds a technical utility, but nothing for the end user.
+
+If you are a developer and need this utility for your module, see these
+examples and read the docs inside the code.
+
+Python example:
+
+::
+
+ def some_method(self):
+ # Get truncated text from an HTML field. It will 40 words and 100
+ # characters at most, and will have "..." appended at the end if it
+ # gets truncated.
+ truncated_text = self.env["ir.fields.converter"].text_from_html(
+ self.html_field, 40, 100, "...")
+
+QWeb example:
+
+::
+
+
+
+|Try me on Runbot|
+
+.. |Try me on Runbot| image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
+ :target: https://runbot.odoo-community.org/runbot/149/11.0
+
+Known issues / Roadmap
+======================
+
+- An option could be added to try to respect the basic HTML tags inside
+ the excerpt (````, ````, ``
``, etc.).
+
+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 `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+-------
+
+* Grupo ESOC Ingeniería de Servicios
+* Tecnativa
+* Onestein
+
+Contributors
+------------
+
+- Dennis Sluijk
+- `Tecnativa `__:",
+- Helly kapatel
+- Mantas Šniukas
+- Dhara Solanki
+
+ - Jairo Llopis
+ - Vicent Cubells
+ - Víctor Martínez
+
+- Chau Le
+
+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/server-tools `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/html_text/__init__.py b/html_text/__init__.py
new file mode 100644
index 00000000000..31660d6a965
--- /dev/null
+++ b/html_text/__init__.py
@@ -0,0 +1,3 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import models
diff --git a/html_text/__manifest__.py b/html_text/__manifest__.py
new file mode 100644
index 00000000000..18b2d5e96f3
--- /dev/null
+++ b/html_text/__manifest__.py
@@ -0,0 +1,18 @@
+# Copyright 2016-2017 Jairo Llopis
+# Copyright 2016 Tecnativa - Vicent Cubells
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+{
+ "name": "Text from HTML field",
+ "summary": "Generate excerpts from any HTML field",
+ "version": "19.0.1.0.0",
+ "category": "Tools",
+ "website": "https://github.com/OCA/server-tools",
+ "author": "Grupo ESOC Ingeniería de Servicios, "
+ "Tecnativa, "
+ "Onestein, "
+ "Odoo Community Association (OCA)",
+ "license": "AGPL-3",
+ "application": False,
+ "installable": True,
+ "depends": ["base"],
+}
diff --git a/html_text/i18n/ca.po b/html_text/i18n/ca.po
new file mode 100644
index 00000000000..910cf3a661d
--- /dev/null
+++ b/html_text/i18n/ca.po
@@ -0,0 +1,25 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+# Translators:
+# Marc Tormo i Bochaca , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 9.0c\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-04-19 18:00+0000\n"
+"PO-Revision-Date: 2017-04-19 18:00+0000\n"
+"Last-Translator: Marc Tormo i Bochaca , 2017\n"
+"Language-Team: Catalan (https://www.transifex.com/oca/teams/23907/ca/)\n"
+"Language: ca\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"
+
+#. module: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+#, fuzzy
+msgid "Fields Converter"
+msgstr "ir.fields.converter"
diff --git a/html_text/i18n/de.po b/html_text/i18n/de.po
new file mode 100644
index 00000000000..3eb49ad5435
--- /dev/null
+++ b/html_text/i18n/de.po
@@ -0,0 +1,25 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+# Translators:
+# Rudolf Schnapka , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 9.0c\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-04-19 18:00+0000\n"
+"PO-Revision-Date: 2017-04-19 18:00+0000\n"
+"Last-Translator: Rudolf Schnapka , 2017\n"
+"Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n"
+"Language: de\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"
+
+#. module: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+#, fuzzy
+msgid "Fields Converter"
+msgstr "ir.fields.converter"
diff --git a/html_text/i18n/es.po b/html_text/i18n/es.po
new file mode 100644
index 00000000000..fdd9514a279
--- /dev/null
+++ b/html_text/i18n/es.po
@@ -0,0 +1,37 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+# Translators:
+# Pedro M. Baeza , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 9.0c\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-12-17 02:07+0000\n"
+"PO-Revision-Date: 2023-11-09 20:38+0000\n"
+"Last-Translator: Ivorra78 \n"
+"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\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: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+msgid "Fields Converter"
+msgstr "Convertidor de Campos"
+
+#~ msgid "Display Name"
+#~ msgstr "Mostrar Nombre"
+
+#~ msgid "ID"
+#~ msgstr "ID (identificación)"
+
+#~ msgid "Last Modified on"
+#~ msgstr "Última Modifiación el"
+
+#~ msgid "Smart Search"
+#~ msgstr "Búsqueda Inteligente"
diff --git a/html_text/i18n/es_AR.po b/html_text/i18n/es_AR.po
new file mode 100644
index 00000000000..e7d0b12af27
--- /dev/null
+++ b/html_text/i18n/es_AR.po
@@ -0,0 +1,22 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 15.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2022-09-04 06:07+0000\n"
+"Last-Translator: Ignacio Buioli \n"
+"Language-Team: none\n"
+"Language: es_AR\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.3.2\n"
+
+#. module: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+msgid "Fields Converter"
+msgstr "Convertidor de Campos"
diff --git a/html_text/i18n/es_ES.po b/html_text/i18n/es_ES.po
new file mode 100644
index 00000000000..7ffe5ac8d29
--- /dev/null
+++ b/html_text/i18n/es_ES.po
@@ -0,0 +1,26 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+# Translators:
+# Fernando Lara , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 9.0c\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-02-16 10:39+0000\n"
+"PO-Revision-Date: 2017-02-16 10:39+0000\n"
+"Last-Translator: Fernando Lara , 2017\n"
+"Language-Team: Spanish (Spain) (https://www.transifex.com/oca/teams/23907/"
+"es_ES/)\n"
+"Language: es_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"
+
+#. module: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+#, fuzzy
+msgid "Fields Converter"
+msgstr "ir.documentos.conversor"
diff --git a/html_text/i18n/hr.po b/html_text/i18n/hr.po
new file mode 100644
index 00000000000..257d8b8bd04
--- /dev/null
+++ b/html_text/i18n/hr.po
@@ -0,0 +1,23 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2019-11-13 17:34+0000\n"
+"Last-Translator: Bole \n"
+"Language-Team: none\n"
+"Language: hr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 3.8\n"
+
+#. module: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+msgid "Fields Converter"
+msgstr "Pretvaranje polja"
diff --git a/html_text/i18n/html_text.pot b/html_text/i18n/html_text.pot
new file mode 100644
index 00000000000..81b484bf155
--- /dev/null
+++ b/html_text/i18n/html_text.pot
@@ -0,0 +1,24 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+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: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+msgid "Fields Converter"
+msgstr ""
+
+#. module: html_text
+#: model:ir.model.fields,field_description:html_text.field_ir_fields_converter__smart_search
+msgid "Smart Search"
+msgstr ""
diff --git a/html_text/i18n/it.po b/html_text/i18n/it.po
new file mode 100644
index 00000000000..2a7282bbc8c
--- /dev/null
+++ b/html_text/i18n/it.po
@@ -0,0 +1,37 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+# Translators:
+# Paolo Valier , 2018
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 10.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-01-06 02:25+0000\n"
+"PO-Revision-Date: 2025-07-23 17:25+0000\n"
+"Last-Translator: mymage \n"
+"Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\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 5.10.4\n"
+
+#. module: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+msgid "Fields Converter"
+msgstr "Convertitore campi"
+
+#~ msgid "Display Name"
+#~ msgstr "Nome visualizzato"
+
+#~ msgid "ID"
+#~ msgstr "ID"
+
+#~ msgid "Last Modified on"
+#~ msgstr "Ultima modifica il"
+
+#~ msgid "Smart Search"
+#~ msgstr "Ricerca intelligente"
diff --git a/html_text/i18n/tr.po b/html_text/i18n/tr.po
new file mode 100644
index 00000000000..b1587027536
--- /dev/null
+++ b/html_text/i18n/tr.po
@@ -0,0 +1,25 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+# Translators:
+# Ahmet Altinisik , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 9.0c\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-12-29 03:40+0000\n"
+"PO-Revision-Date: 2016-12-29 03:40+0000\n"
+"Last-Translator: Ahmet Altinisik , 2016\n"
+"Language-Team: Turkish (https://www.transifex.com/oca/teams/23907/tr/)\n"
+"Language: tr\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"
+
+#. module: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+#, fuzzy
+msgid "Fields Converter"
+msgstr "ir.fields.converter"
diff --git a/html_text/i18n/zh_CN.po b/html_text/i18n/zh_CN.po
new file mode 100644
index 00000000000..b47ea1995c4
--- /dev/null
+++ b/html_text/i18n/zh_CN.po
@@ -0,0 +1,22 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * html_text
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2019-08-31 06:18+0000\n"
+"Last-Translator: 黎伟杰 <674416404@qq.com>\n"
+"Language-Team: none\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Weblate 3.8\n"
+
+#. module: html_text
+#: model:ir.model,name:html_text.model_ir_fields_converter
+msgid "Fields Converter"
+msgstr "字段转换器"
diff --git a/html_text/models/__init__.py b/html_text/models/__init__.py
new file mode 100644
index 00000000000..e21238ee902
--- /dev/null
+++ b/html_text/models/__init__.py
@@ -0,0 +1,3 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import ir_fields_converter
diff --git a/html_text/models/ir_fields_converter.py b/html_text/models/ir_fields_converter.py
new file mode 100644
index 00000000000..f407aeab3a3
--- /dev/null
+++ b/html_text/models/ir_fields_converter.py
@@ -0,0 +1,74 @@
+# Copyright 2016-2017 Jairo Llopis
+# Copyright 2016 Tecnativa - Vicent Cubells
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+import logging
+
+from lxml import etree, html
+
+from odoo import api, models
+
+_logger = logging.getLogger(__name__)
+
+
+class IrFieldsConverter(models.AbstractModel):
+ _inherit = "ir.fields.converter"
+
+ @api.model
+ def text_from_html(
+ self, html_content, max_words=None, max_chars=None, ellipsis="…", fail=False
+ ):
+ """Extract text from an HTML field in a generator.
+
+ :param str html_content:
+ HTML contents from where to extract the text.
+
+ :param int max_words:
+ Maximum amount of words allowed in the resulting string.
+
+ :param int max_chars:
+ Maximum amount of characters allowed in the resulting string. If
+ you apply this limit, beware that the last word could get cut in an
+ unexpected place.
+
+ :param str ellipsis:
+ Character(s) to be appended to the end of the resulting string if
+ it gets truncated after applying limits set in :param:`max_words`
+ or :param:`max_chars`. If you want nothing applied, just set an
+ empty string.
+
+ :param bool fail:
+ If ``True``, exceptions will be raised. Otherwise, an empty string
+ will be returned on failure.
+ """
+ # Parse HTML
+ try:
+ doc = html.fromstring(html_content)
+ except (TypeError, etree.XMLSyntaxError, etree.ParserError):
+ if fail:
+ raise
+ else:
+ _logger.exception("Failure parsing this HTML:\n%s", html_content)
+ return ""
+
+ # Get words
+ words = "".join(doc.xpath("//text()")).split()
+
+ # Truncate words
+ suffix = max_words and len(words) > max_words
+ if max_words:
+ words = words[:max_words]
+
+ # Get text
+ text = " ".join(words)
+
+ # Truncate text
+ suffix = suffix or max_chars and len(text) > max_chars
+ if max_chars:
+ text = text[: max_chars - (len(ellipsis) if suffix else 0)].strip()
+
+ # Append ellipsis if needed
+ if suffix:
+ text += ellipsis
+
+ return text
diff --git a/html_text/pyproject.toml b/html_text/pyproject.toml
new file mode 100644
index 00000000000..4231d0cccb3
--- /dev/null
+++ b/html_text/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
diff --git a/html_text/readme/CONTRIBUTORS.md b/html_text/readme/CONTRIBUTORS.md
new file mode 100644
index 00000000000..f0ab91e04c5
--- /dev/null
+++ b/html_text/readme/CONTRIBUTORS.md
@@ -0,0 +1,9 @@
+- Dennis Sluijk \
+- [Tecnativa](https://www.tecnativa.com):",
+- Helly kapatel \
+- Mantas Šniukas \
+- Dhara Solanki \
+ - Jairo Llopis
+ - Vicent Cubells
+ - Víctor Martínez
+- Chau Le \
diff --git a/html_text/readme/DESCRIPTION.md b/html_text/readme/DESCRIPTION.md
new file mode 100644
index 00000000000..8fc6698d6fa
--- /dev/null
+++ b/html_text/readme/DESCRIPTION.md
@@ -0,0 +1,8 @@
+This module provides some technical features that allow to extract text
+from any chunk of HTML, without HTML tags or attributes. You can chose
+either:
+
+- To truncate the result by amount of words or characters.
+- To append an ellipsis (or any character(s)) at the end of the result.
+
+It can be used to easily generate excerpts.
diff --git a/html_text/readme/ROADMAP.md b/html_text/readme/ROADMAP.md
new file mode 100644
index 00000000000..6b0d72a453d
--- /dev/null
+++ b/html_text/readme/ROADMAP.md
@@ -0,0 +1,2 @@
+- An option could be added to try to respect the basic HTML tags inside
+ the excerpt (``, ``, ``, etc.).
diff --git a/html_text/readme/USAGE.md b/html_text/readme/USAGE.md
new file mode 100644
index 00000000000..3e042c29f05
--- /dev/null
+++ b/html_text/readme/USAGE.md
@@ -0,0 +1,19 @@
+This module just adds a technical utility, but nothing for the end user.
+
+If you are a developer and need this utility for your module, see these
+examples and read the docs inside the code.
+
+Python example:
+
+ def some_method(self):
+ # Get truncated text from an HTML field. It will 40 words and 100
+ # characters at most, and will have "..." appended at the end if it
+ # gets truncated.
+ truncated_text = self.env["ir.fields.converter"].text_from_html(
+ self.html_field, 40, 100, "...")
+
+QWeb example:
+
+
+
+[](https://runbot.odoo-community.org/runbot/149/11.0)
diff --git a/html_text/static/description/icon.png b/html_text/static/description/icon.png
new file mode 100644
index 00000000000..3a0328b516c
Binary files /dev/null and b/html_text/static/description/icon.png differ
diff --git a/html_text/static/description/index.html b/html_text/static/description/index.html
new file mode 100644
index 00000000000..a4adb8550c4
--- /dev/null
+++ b/html_text/static/description/index.html
@@ -0,0 +1,477 @@
+
+
+
+
+
+README.rst
+
+
+
+
+
+
+
+
+
+
+
Text from HTML field
+
+

+
This module provides some technical features that allow to extract text
+from any chunk of HTML, without HTML tags or attributes. You can chose
+either:
+
+- To truncate the result by amount of words or characters.
+- To append an ellipsis (or any character(s)) at the end of the result.
+
+
It can be used to easily generate excerpts.
+
Table of contents
+
+
+
+
This module just adds a technical utility, but nothing for the end user.
+
If you are a developer and need this utility for your module, see these
+examples and read the docs inside the code.
+
Python example:
+
+def some_method(self):
+ # Get truncated text from an HTML field. It will 40 words and 100
+ # characters at most, and will have "..." appended at the end if it
+ # gets truncated.
+ truncated_text = self.env["ir.fields.converter"].text_from_html(
+ self.html_field, 40, 100, "...")
+
+
QWeb example:
+
+<t t-esc="env['ir.fields.converter'].text_from_html(doc.html_field)"/>
+
+

+
+
+
+
+- An option could be added to try to respect the basic HTML tags inside
+the excerpt (<b>, <i>, <p>, etc.).
+
+
+
+
+
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.
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+- Grupo ESOC Ingeniería de Servicios
+- Tecnativa
+- Onestein
+
+
+
+
+
+
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/server-tools project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
+
+
+
+
+
+
diff --git a/html_text/tests/__init__.py b/html_text/tests/__init__.py
new file mode 100644
index 00000000000..d9d2b331a8f
--- /dev/null
+++ b/html_text/tests/__init__.py
@@ -0,0 +1,3 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import test_extractor
diff --git a/html_text/tests/test_extractor.py b/html_text/tests/test_extractor.py
new file mode 100644
index 00000000000..c117ab8b647
--- /dev/null
+++ b/html_text/tests/test_extractor.py
@@ -0,0 +1,56 @@
+# Copyright 2016-2017 Jairo Llopis
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo.tests.common import TransactionCase
+from odoo.tools import mute_logger
+
+
+class ExtractorCase(TransactionCase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ # Shortcut
+ cls.text_from_html = cls.env["ir.fields.converter"].text_from_html
+
+ def test_excerpts(self):
+ """Text gets correctly extracted."""
+ html = """
+
+
+
+
I'm a title
+
I'm a paragraph
+
¡Pues yo soy español!
+
+
+
+ """
+ self.assertEqual(
+ self.text_from_html(html),
+ "I'm a title I'm a paragraph ¡Pues yo soy español!",
+ )
+ self.assertEqual(
+ self.text_from_html(html, 8), "I'm a title I'm a paragraph ¡Pues yo…"
+ )
+ self.assertEqual(
+ self.text_from_html(html, 8, 31), "I'm a title I'm a paragraph ¡P…"
+ )
+ self.assertEqual(
+ self.text_from_html(html, 7, ellipsis=""),
+ "I'm a title I'm a paragraph ¡Pues",
+ )
+
+ @mute_logger("odoo.addons.html_text.models.ir_fields_converter")
+ def test_empty_html(self):
+ """Empty HTML handled correctly."""
+ self.assertEqual(self.text_from_html(""), "")
+ # 19.0 lxml no longer raises ParserError on empty input; the parser
+ # returns an empty document, so fail=True yields "" as well.
+ self.assertEqual(self.text_from_html("", fail=True), "")
+
+ @mute_logger("odoo.addons.html_text.models.ir_fields_converter")
+ def test_false_html(self):
+ """``False`` HTML handled correctly."""
+ self.assertEqual(self.text_from_html(False), "")
+ with self.assertRaises(TypeError):
+ self.text_from_html(False, fail=True)