diff --git a/web_discuss_portal/README.rst b/web_discuss_portal/README.rst new file mode 100644 index 00000000000..8b519001cad --- /dev/null +++ b/web_discuss_portal/README.rst @@ -0,0 +1,87 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================== +Web Discuss Portal +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:3ca4a071363b0ae21ae39320ac57b2e55c342d7c300a2553e05a6e9114d548e7 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/19.0/web_discuss_portal + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-19-0/web-19-0-web_discuss_portal + :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/web&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module gives portal users access to Discuss from the website +portal. + +It adds a *Discuss* entry to *My Account* (``/my``) listing the channels +the portal user has joined. Selecting one opens the standard Discuss +interface. + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* Vortex Dimensión Digital + +Contributors +------------ + +- `Vortex Dimensión Digital `__: + + - Jorge Rosado Julián + +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/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_discuss_portal/__init__.py b/web_discuss_portal/__init__.py new file mode 100644 index 00000000000..e046e49fbe2 --- /dev/null +++ b/web_discuss_portal/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/web_discuss_portal/__manifest__.py b/web_discuss_portal/__manifest__.py new file mode 100644 index 00000000000..09333606150 --- /dev/null +++ b/web_discuss_portal/__manifest__.py @@ -0,0 +1,17 @@ +{ + "name": "Web Discuss Portal", + "version": "19.0.1.0.0", + "summary": "Give portal users access to their Discuss channels", + "author": "Vortex Dimensión Digital, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/web", + "license": "LGPL-3", + "category": "Portal", + "depends": ["mail", "portal"], + "data": ["views/portal.xml"], + "images": [ + "static/description/icon.png", + ], + "installable": True, + "application": False, + "auto_install": False, +} diff --git a/web_discuss_portal/controllers/__init__.py b/web_discuss_portal/controllers/__init__.py new file mode 100644 index 00000000000..12a7e529b67 --- /dev/null +++ b/web_discuss_portal/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/web_discuss_portal/controllers/main.py b/web_discuss_portal/controllers/main.py new file mode 100644 index 00000000000..2e635cb8184 --- /dev/null +++ b/web_discuss_portal/controllers/main.py @@ -0,0 +1,42 @@ +from odoo import http +from odoo.http import request + +from odoo.addons.portal.controllers.portal import CustomerPortal +from odoo.addons.portal.controllers.portal import pager as portal_pager + + +class DiscussPortal(CustomerPortal): + def _get_discuss_channel_domain(self): + return [ + ("channel_type", "=", "channel"), + ("parent_channel_id", "=", False), + ("is_member", "=", True), + ] + + @http.route( + ["/my/discuss", "/my/discuss/page/"], + type="http", + auth="user", + website=True, + ) + def discuss_portal(self, page=1, **kw): + Channel = request.env["discuss.channel"] + domain = self._get_discuss_channel_domain() + pager_values = portal_pager( + url="/my/discuss", + total=Channel.search_count(domain), + page=page, + step=self._items_per_page, + ) + channels = Channel.search( + domain, limit=self._items_per_page, offset=pager_values["offset"] + ) + return request.render( + "web_discuss_portal.discuss_portal", + { + "channels": channels, + "page_name": "discuss", + "pager": pager_values, + "default_url": "/my/discuss", + }, + ) diff --git a/web_discuss_portal/i18n/es.po b/web_discuss_portal/i18n/es.po new file mode 100644 index 00000000000..85ea4d5deda --- /dev/null +++ b/web_discuss_portal/i18n/es.po @@ -0,0 +1,33 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_discuss_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-07-21 08:15+0000\n" +"PO-Revision-Date: 2026-07-21 08:15+0000\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: web_discuss_portal +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.portal_template +msgid "Chat with us" +msgstr "Habla con nosotros" + +#. module: web_discuss_portal +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.discuss_portal +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.portal_template +msgid "Discuss" +msgstr "Conversaciones" + +#. module: web_discuss_portal +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.discuss_portal +msgid "There are no channels available for you yet." +msgstr "Todavía no hay canales disponibles para ti." diff --git a/web_discuss_portal/i18n/web_discuss_portal.pot b/web_discuss_portal/i18n/web_discuss_portal.pot new file mode 100644 index 00000000000..150318246c4 --- /dev/null +++ b/web_discuss_portal/i18n/web_discuss_portal.pot @@ -0,0 +1,33 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_discuss_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-07-21 08:15+0000\n" +"PO-Revision-Date: 2026-07-21 08:15+0000\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: web_discuss_portal +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.portal_template +msgid "Chat with us" +msgstr "" + +#. module: web_discuss_portal +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.discuss_portal +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.portal_template +msgid "Discuss" +msgstr "" + +#. module: web_discuss_portal +#: model_terms:ir.ui.view,arch_db:web_discuss_portal.discuss_portal +msgid "There are no channels available for you yet." +msgstr "" diff --git a/web_discuss_portal/pyproject.toml b/web_discuss_portal/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/web_discuss_portal/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/web_discuss_portal/readme/CONTRIBUTORS.md b/web_discuss_portal/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..424d9957ad3 --- /dev/null +++ b/web_discuss_portal/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [Vortex Dimensión Digital](https://www.dimensionvortex.com/): + - Jorge Rosado Julián \<\> \ No newline at end of file diff --git a/web_discuss_portal/readme/DESCRIPTION.md b/web_discuss_portal/readme/DESCRIPTION.md new file mode 100644 index 00000000000..50abc1ca3f4 --- /dev/null +++ b/web_discuss_portal/readme/DESCRIPTION.md @@ -0,0 +1,4 @@ +This module gives portal users access to Discuss from the website portal. + +It adds a *Discuss* entry to *My Account* (`/my`) listing the channels the +portal user has joined. Selecting one opens the standard Discuss interface. diff --git a/web_discuss_portal/static/description/icon.png b/web_discuss_portal/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/web_discuss_portal/static/description/icon.png differ diff --git a/web_discuss_portal/static/description/index.html b/web_discuss_portal/static/description/index.html new file mode 100644 index 00000000000..f9adafacb64 --- /dev/null +++ b/web_discuss_portal/static/description/index.html @@ -0,0 +1,436 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Web Discuss Portal

+ +

Beta License: LGPL-3 OCA/web Translate me on Weblate Try me on Runboat

+

This module gives portal users access to Discuss from the website +portal.

+

It adds a Discuss entry to My Account (/my) listing the channels +the portal user has joined. Selecting one opens the standard Discuss +interface.

+

Table of contents

+ +
+

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

+
    +
  • Vortex Dimensión Digital
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/web_discuss_portal/static/img/discuss.svg b/web_discuss_portal/static/img/discuss.svg new file mode 100644 index 00000000000..a8a2106bb7e --- /dev/null +++ b/web_discuss_portal/static/img/discuss.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web_discuss_portal/tests/__init__.py b/web_discuss_portal/tests/__init__.py new file mode 100644 index 00000000000..d565e9d49d8 --- /dev/null +++ b/web_discuss_portal/tests/__init__.py @@ -0,0 +1 @@ +from . import test_web_discuss_portal diff --git a/web_discuss_portal/tests/test_web_discuss_portal.py b/web_discuss_portal/tests/test_web_discuss_portal.py new file mode 100644 index 00000000000..64c4d78db73 --- /dev/null +++ b/web_discuss_portal/tests/test_web_discuss_portal.py @@ -0,0 +1,102 @@ +# Copyright 2026 Vortex Dimensión Digital +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from odoo.tests.common import HttpCase, tagged + +from odoo.addons.web_discuss_portal.controllers.main import DiscussPortal + + +@tagged("post_install", "-at_install") +class TestWebDiscussPortal(HttpCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.password = "portal_user_pwd" + cls.portal_user = cls.env["res.users"].create( + { + "name": "Portal Tester", + "login": "portal_tester", + "password": cls.password, + "group_ids": [(6, 0, [cls.env.ref("base.group_portal").id])], + } + ) + cls.portal_partner = cls.portal_user.partner_id + # A channel gets group_public_id = base.group_user by default, which + # hides it from portal users. These fixtures pass False on purpose so + # that visibility is decided by membership alone. + cls.joined_channel = cls._create_channel("Joined Channel") + cls.joined_channel.add_members( + partner_ids=cls.portal_partner.ids, post_joined_message=False + ) + # Reachable (no authorized group) but never joined. + cls.other_channel = cls._create_channel("Other Channel") + # Restricted to internal users, joined anyway. + cls.internal_channel = cls.env["discuss.channel"].create( + { + "name": "Internal Channel", + "channel_type": "channel", + "group_public_id": cls.env.ref("base.group_user").id, + } + ) + cls.internal_channel.add_members( + partner_ids=cls.portal_partner.ids, post_joined_message=False + ) + + @classmethod + def _create_channel(cls, name): + return cls.env["discuss.channel"].create( + {"name": name, "channel_type": "channel", "group_public_id": False} + ) + + def _get_listed_channels(self): + """Return the channels the controller lists for the portal user. + + The domain comes from the controller itself, so these assertions keep + tracking what the page actually renders. + """ + domain = DiscussPortal()._get_discuss_channel_domain() + return self.env["discuss.channel"].with_user(self.portal_user).search(domain) + + def test_joined_channel_is_listed(self): + self.assertIn(self.joined_channel, self._get_listed_channels()) + + def test_channel_without_membership_is_not_listed(self): + self.assertNotIn(self.other_channel, self._get_listed_channels()) + + def test_channel_restricted_to_internal_users_is_not_listed(self): + """Membership is not enough: the authorized group still applies.""" + self.assertNotIn(self.internal_channel, self._get_listed_channels()) + + def test_sub_channels_are_not_listed(self): + sub_channel = self.joined_channel._create_sub_channel(name="Thread") + sub_channel.add_members( + partner_ids=self.portal_partner.ids, post_joined_message=False + ) + listed = self._get_listed_channels() + self.assertIn(self.joined_channel, listed) + self.assertNotIn(sub_channel, listed) + + def test_chat_is_not_listed(self): + # Portal users cannot create channels, so the chat is opened by an + # internal user with the portal user as the other participant. + chat_id = self.env["discuss.channel"]._get_or_create_chat( + partners_to=self.portal_partner.ids + )["id"] + chat = self.env["discuss.channel"].browse(chat_id) + self.assertIn(self.portal_partner, chat.channel_partner_ids) + self.assertEqual(chat.channel_type, "chat") + self.assertNotIn(chat, self._get_listed_channels()) + + def test_portal_page_lists_joined_channel_only(self): + self.authenticate("portal_tester", self.password) + response = self.url_open("/my/discuss") + self.assertEqual(response.status_code, 200) + self.assertIn(f"/discuss/channel/{self.joined_channel.id}", response.text) + self.assertNotIn(f"/discuss/channel/{self.other_channel.id}", response.text) + self.assertNotIn(f"/discuss/channel/{self.internal_channel.id}", response.text) + + def test_portal_home_shows_discuss_entry(self): + self.authenticate("portal_tester", self.password) + response = self.url_open("/my") + self.assertEqual(response.status_code, 200) + self.assertIn("/my/discuss", response.text) diff --git a/web_discuss_portal/views/portal.xml b/web_discuss_portal/views/portal.xml new file mode 100644 index 00000000000..b49cbabb340 --- /dev/null +++ b/web_discuss_portal/views/portal.xml @@ -0,0 +1,58 @@ + + + + + + + +