diff --git a/mail_gateway/README.rst b/mail_gateway/README.rst new file mode 100644 index 0000000000..33c8576ef7 --- /dev/null +++ b/mail_gateway/README.rst @@ -0,0 +1,107 @@ +============ +Mail Gateway +============ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:1fc341f96ecbd2a96d20eb12aae392827aa821f05dbda6cc30a702d19a58f223 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/17.0/mail_gateway + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-17-0/social-17-0-mail_gateway + :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/social&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module will allow you to integrate an external chat system in your +Odoo system. It requires extra modules with the specific configuration +of each chat system, like mail_gateway_telegram or +mail_gateway_whatsapp. + +This way, a group of users can respond customers or any other set of +partners within Odoo, but the messages will be sent through the external +chat system. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +When external messages are received, they will be directly sent to the +discuss menu. Answering to these messages will send the answer to the +external contact. We can assign this messages to any record using the +message actions. Also, we can assign the sender to a partner using the +followers menu and selecting the partner. + +On a standard record associated to a partner with external chat, we can +send messages to the external contact directly selecting the methods of +the partner. To use this, we just need to use the + +It is recomended to enable chatter notification to all users that will +receive messages from gateways. + +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 +------- + +* Creu Blanca +* Dixmit + +Contributors +------------ + +- Enric Tobella +- Olga Marco + +Other credits +------------- + +This work has been funded by AEOdoo (Asociación Española de Odoo - +https://www.aeodoo.org) + +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/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_gateway/__init__.py b/mail_gateway/__init__.py new file mode 100644 index 0000000000..6aee13652d --- /dev/null +++ b/mail_gateway/__init__.py @@ -0,0 +1,5 @@ +from . import controllers +from . import models + +from .hooks import pre_init_hook +from . import wizards diff --git a/mail_gateway/__manifest__.py b/mail_gateway/__manifest__.py new file mode 100644 index 0000000000..e54d9f8a39 --- /dev/null +++ b/mail_gateway/__manifest__.py @@ -0,0 +1,31 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Mail Gateway", + "summary": "Base module for gateway communications", + "version": "17.0.1.0.0", + "license": "AGPL-3", + "author": "Creu Blanca,Dixmit,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/social", + "depends": ["mail"], + "pre_init_hook": "pre_init_hook", + "data": [ + "wizards/mail_compose_gateway_message.xml", + "wizards/mail_message_gateway_link.xml", + "wizards/mail_message_gateway_send.xml", + "wizards/mail_guest_manage.xml", + "security/security.xml", + "security/ir.model.access.csv", + "views/mail_gateway.xml", + "views/res_partner_gateway_channel.xml", + "views/mail_guest_views.xml", + ], + "assets": { + "web.assets_backend": [ + "mail_gateway/static/src/components/**/*", + "mail_gateway/static/src/core/**/*", + "mail_gateway/static/src/models/**/*", + ], + }, +} diff --git a/mail_gateway/controllers/__init__.py b/mail_gateway/controllers/__init__.py new file mode 100644 index 0000000000..1a48e4080c --- /dev/null +++ b/mail_gateway/controllers/__init__.py @@ -0,0 +1,2 @@ +from . import gateway +from . import discuss diff --git a/mail_gateway/controllers/discuss.py b/mail_gateway/controllers/discuss.py new file mode 100644 index 0000000000..99494861fe --- /dev/null +++ b/mail_gateway/controllers/discuss.py @@ -0,0 +1,11 @@ +# Copyright 2024 Dixmit +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo.addons.mail.controllers.thread import ThreadController + + +class GatewayThreadController(ThreadController): + def _get_allowed_message_post_params(self): + result = super()._get_allowed_message_post_params() + result.add("gateway_notifications") + return result diff --git a/mail_gateway/controllers/gateway.py b/mail_gateway/controllers/gateway.py new file mode 100644 index 0000000000..f35f11c293 --- /dev/null +++ b/mail_gateway/controllers/gateway.py @@ -0,0 +1,85 @@ +# Copyright 2024 Dixmit +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import json +import logging + +from odoo.http import Controller, request, route + +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context + +_logger = logging.getLogger(__name__) + + +class GatewayController(Controller): + @route( + "/gateway///update", + type="http", + auth="public", + methods=["GET", "POST"], + csrf=False, + ) + @add_guest_to_context + def post_update(self, usage, token, *args, **kwargs): + if request.httprequest.method == "GET": + bot_data = request.env["mail.gateway"]._get_gateway( + token, gateway_type=usage, state="pending" + ) + if not bot_data: + return request.make_response( + json.dumps({}), + [ + ("Content-Type", "application/json"), + ], + ) + return ( + request.env["mail.gateway.%s" % usage] + .with_user(bot_data["webhook_user_id"]) + .with_company(bot_data["company_id"]) + ._receive_get_update(bot_data, request, **kwargs) + ) + bot_data = request.env["mail.gateway"]._get_gateway( + token, gateway_type=usage, state="integrated" + ) + if not bot_data: + _logger.warning( + "Gateway was not found for token %s with usage %s", token, usage + ) + return request.make_response( + json.dumps({}), + [ + ("Content-Type", "application/json"), + ], + ) + jsonrequest = json.loads( + request.httprequest.get_data().decode(request.httprequest.charset) + ) + dispatcher = ( + request.env["mail.gateway.%s" % usage] + .with_user(bot_data["webhook_user_id"]) + .with_context(no_gateway_notification=True) + ) + if not dispatcher._verify_update(bot_data, jsonrequest): + _logger.warning( + "Message could not be verified for token %s with usage %s", token, usage + ) + return request.make_response( + json.dumps({}), + [ + ("Content-Type", "application/json"), + ], + ) + _logger.debug( + "Received message for token %s with usage %s: %s", + token, + usage, + json.dumps(jsonrequest), + ) + gateway = dispatcher.env["mail.gateway"].browse(bot_data["id"]) + dispatcher._receive_update(gateway, jsonrequest) + return request.make_response( + json.dumps({}), + [ + ("Content-Type", "application/json"), + ], + ) diff --git a/mail_gateway/hooks.py b/mail_gateway/hooks.py new file mode 100644 index 0000000000..09b0a80117 --- /dev/null +++ b/mail_gateway/hooks.py @@ -0,0 +1,12 @@ +def pre_init_hook(env): + """ + The objective of this hook is to speed up the installation + of the module on an existing Odoo instance. + + Without this script, big databases can take a long time to install this + module. + """ + env.cr.execute( + """ALTER TABLE mail_message + ADD COLUMN IF NOT EXISTS gateway_channel_id int""" + ) diff --git a/mail_gateway/i18n/es.po b/mail_gateway/i18n/es.po new file mode 100644 index 0000000000..69a1177cd9 --- /dev/null +++ b/mail_gateway/i18n/es.po @@ -0,0 +1,542 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_gateway +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-05-28 16:26+0000\n" +"Last-Translator: Anna Martínez \n" +"Language-Team: none\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: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__anonymous_name +msgid "Anonymous Name" +msgstr "Nombre anónimo" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_guest_manage +msgid "Assign gateway guest to a partner" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__can_set_webhook +msgid "Can Set Webhook" +msgstr "Puede establecer un webhook" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Cancel" +msgstr "Cancelar" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__channel_type +msgid "Channel Type" +msgstr "Tipo de canal" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_channel__channel_type +msgid "" +"Chat is private and unique between 2 persons. Group is private among invited" +" persons. Channel can be freely joined (depending on its configuration)." +msgstr "" +"El chat es privado y único entre 2 personas. El grupo es privado entre las " +"personas invitadas. El canal se pueden unir libremente (dependiendo de su " +"configuración)." + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_message_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_message_ids +msgid "Child gateway messages" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__company_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__company_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__company_id +msgid "Company" +msgstr "Compañía" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_partner +msgid "Contact" +msgstr "Contacto" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +msgid "Create new partner" +msgstr "Crear una nueva empresa" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_channel +msgid "Discussion Channel" +msgstr "Canal de discusión" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_thread +msgid "Email Thread" +msgstr "Hilo de mensajes" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_notification__gateway_failure_reason +msgid "" +"Failure reason. This is usually the exception thrown by the email server, " +"stored to ease the debugging of mailing issues." +msgstr "" +"Motivo del fallo. Esta suele ser la excepción lanzada por el servidor de " +"correo electrónico, almacenada para facilitar la depuración de problemas de " +"correo." + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/discuss_sidebar_category.esm.js:0 +#, python-format +msgid "Find a gateway channel..." +msgstr "" + +#. module: mail_gateway +#: model:ir.actions.act_window,name:mail_gateway.mail_gateway_act_window +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_users__gateway_ids +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_channel__channel_type__gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_notification__notification_type__gateway +#: model:ir.module.category,name:mail_gateway.module_category_gateway +#: model:ir.ui.menu,name:mail_gateway.mail_gateway_menu +msgid "Gateway" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__gateway_channel_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_channel_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_res_users__gateway_channel_ids +msgid "Gateway Channel" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_channel_data +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_channel_data +msgid "Gateway Channel Data" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_channel_token +msgid "Gateway Channel Token" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_failure_reason +msgid "Gateway Failure Reason" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_message_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_message_id +msgid "Gateway Message" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_notification_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_notification_ids +msgid "Gateway Notification" +msgstr "" + +#. module: mail_gateway +#: model:ir.actions.act_window,name:mail_gateway.res_partner_gateway_channel_act_window +#: model:ir.ui.menu,name:mail_gateway.res_partner_gateway_channel_menu +msgid "Gateway Partner Channels" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_thread_data +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_thread_data +msgid "Gateway Thread Data" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest__gateway_token +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__gateway_token +msgid "Gateway Token" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_type +msgid "Gateway Type" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_gateway_abstract +msgid "Gateway abstract for functions" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/chatter/chatter.xml:0 +#, python-format +msgid "Gateway message" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_token +msgid "Gateway related Token" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_guest +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__guest_id +msgid "Guest" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__has_new_channel_security +msgid "Has New Channel Security" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__id +msgid "ID" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Integrate Webhook" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_gateway__integrated_webhook_state__integrated +msgid "Integrated" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__integrated_webhook_state +msgid "Integrated Webhook State" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_channel__gateway_token +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__token +msgid "Key used for integration purposes" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_key +msgid "Key used on the connection URL" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_secret +msgid "" +"Key used to ensure that the connection is secure and\n" +" comes from the desired source" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel____last_update +msgid "Last Modified on" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__write_date +msgid "Last Updated on" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +msgid "Link" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Link Message to thread" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message_gateway_link +msgid "Link message from gateway" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Link to thread" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_gateway +msgid "Mail Gateway" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +msgid "Mail Message Gateway Link" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Mail Message Gateway Send" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/channel_member_view.esm.js:0 +#, python-format +msgid "Manage guest" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__member_ids +msgid "Member" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Members" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +msgid "Merge" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__message_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__message_id +msgid "Message" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_notification +msgid "Message Notifications" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__name +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__name +msgid "Name" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/gateway_follower/gateway_follower.xml:0 +#, python-format +msgid "Not selected" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__notification_type +msgid "Notification Type" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_message_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_message_id +msgid "Original gateway message" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__partner_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__partner_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__partner_id +msgid "Partner" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_res_partner_gateway_channel_unique_partner_gateway +msgid "Partner can only have one configuration for each gateway." +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_gateway__integrated_webhook_state__pending +msgid "Pending" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__resource_ref +msgid "Record reference" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Remove Webhook" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Send" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message_gateway_send +msgid "Send Message through gateway" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Send with gateway" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_partner_gateway_channel +msgid "Technical data used to get the gateway author" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/composer/composer.xml:0 +#, python-format +msgid "To:" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__token +msgid "Token" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_mail_gateway_mail_gateway_token +msgid "Token must be unique" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Update Webhook" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_users +#: model:res.groups,name:mail_gateway.gateway_user +msgid "User" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_user_id +msgid "User that will create the messages" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_key +msgid "Webhook Key" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_mail_gateway_mail_gateway_webhook_key +msgid "Webhook Key must be unique" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_secret +msgid "Webhook Secret" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_url +msgid "Webhook Url" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_user_id +msgid "Webhook User" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__has_new_channel_security +msgid "" +"When checked, channels are not created automatically. Usable on Telegram" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/message/message.xml:0 +#, python-format +msgid "document" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/chatter/chatter.xml:0 +#, python-format +msgid "gateway" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/message/message.xml:0 +#, python-format +msgid "on" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_ir_websocket +msgid "websocket message handling" +msgstr "" diff --git a/mail_gateway/i18n/it.po b/mail_gateway/i18n/it.po new file mode 100644 index 0000000000..8dea8ea602 --- /dev/null +++ b/mail_gateway/i18n/it.po @@ -0,0 +1,545 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_gateway +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-07-02 17:47+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\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 4.17\n" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__anonymous_name +msgid "Anonymous Name" +msgstr "Nome anonimo" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_guest_manage +msgid "Assign gateway guest to a partner" +msgstr "Assegna visitatore gateway ad un partner" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__can_set_webhook +msgid "Can Set Webhook" +msgstr "Può impostare webhook" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Cancel" +msgstr "Annulla" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__channel_type +msgid "Channel Type" +msgstr "Tipo canale" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_channel__channel_type +msgid "" +"Chat is private and unique between 2 persons. Group is private among invited" +" persons. Channel can be freely joined (depending on its configuration)." +msgstr "" +"Il dialogo è privato e unico tra due persone. Il gruppo è privato tra le " +"persone invitate. Si può liberamente accedere al canale (in funzione della " +"sua configurazione)." + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_message_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_message_ids +msgid "Child gateway messages" +msgstr "Messaggi del gateway figlio" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__company_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__company_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__company_id +msgid "Company" +msgstr "Azienda" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_partner +msgid "Contact" +msgstr "Contatto" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +msgid "Create new partner" +msgstr "Crea nuovo partner" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_channel +msgid "Discussion Channel" +msgstr "Canale discussione" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_thread +msgid "Email Thread" +msgstr "Discussione e-mail" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_notification__gateway_failure_reason +msgid "" +"Failure reason. This is usually the exception thrown by the email server, " +"stored to ease the debugging of mailing issues." +msgstr "" +"Motivo guasto. Normalmente questa è l'eccezione generata dal server e-mail, " +"salvata per semplificare la ricerca di errori dei problemi di invio." + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/discuss_sidebar_category.esm.js:0 +#, python-format +msgid "Find a gateway channel..." +msgstr "Trova un canale gateway..." + +#. module: mail_gateway +#: model:ir.actions.act_window,name:mail_gateway.mail_gateway_act_window +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_users__gateway_ids +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_channel__channel_type__gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_notification__notification_type__gateway +#: model:ir.module.category,name:mail_gateway.module_category_gateway +#: model:ir.ui.menu,name:mail_gateway.mail_gateway_menu +msgid "Gateway" +msgstr "Gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__gateway_channel_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_channel_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_res_users__gateway_channel_ids +msgid "Gateway Channel" +msgstr "Canale gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_channel_data +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_channel_data +msgid "Gateway Channel Data" +msgstr "Dati canale gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_channel_token +msgid "Gateway Channel Token" +msgstr "Token canale gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_failure_reason +msgid "Gateway Failure Reason" +msgstr "Motivo guasto gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_message_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_message_id +msgid "Gateway Message" +msgstr "Messaggio gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_notification_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_notification_ids +msgid "Gateway Notification" +msgstr "Notifica gateway" + +#. module: mail_gateway +#: model:ir.actions.act_window,name:mail_gateway.res_partner_gateway_channel_act_window +#: model:ir.ui.menu,name:mail_gateway.res_partner_gateway_channel_menu +msgid "Gateway Partner Channels" +msgstr "Canali partner gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_thread_data +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_thread_data +msgid "Gateway Thread Data" +msgstr "Dati discussione gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest__gateway_token +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__gateway_token +msgid "Gateway Token" +msgstr "Token gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_type +msgid "Gateway Type" +msgstr "Tipo gateway" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_gateway_abstract +msgid "Gateway abstract for functions" +msgstr "Riepilogo gateway per funzioni" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/chatter/chatter.xml:0 +#, python-format +msgid "Gateway message" +msgstr "Messaggio gateway" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_token +msgid "Gateway related Token" +msgstr "Token relativo al gateway" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_guest +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__guest_id +msgid "Guest" +msgstr "Ospite" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__has_new_channel_security +msgid "Has New Channel Security" +msgstr "Ha nuova sicurezza canale" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__id +msgid "ID" +msgstr "ID" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Integrate Webhook" +msgstr "Integra webhook" + +#. module: mail_gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_gateway__integrated_webhook_state__integrated +msgid "Integrated" +msgstr "Integrato" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__integrated_webhook_state +msgid "Integrated Webhook State" +msgstr "Stato webhook integrato" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_channel__gateway_token +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__token +msgid "Key used for integration purposes" +msgstr "Chiave utilizzata per motivi di integrazione" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_key +msgid "Key used on the connection URL" +msgstr "Chiave utilizzata nella connessione URL" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_secret +msgid "" +"Key used to ensure that the connection is secure and\n" +" comes from the desired source" +msgstr "" +"Chiave utilizzata per assicurare la sicurezza della connessione e\n" +" arriva dalla sorgente desiderata" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +msgid "Link" +msgstr "Collega" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Link Message to thread" +msgstr "Collega messaggio alla discussione" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message_gateway_link +msgid "Link message from gateway" +msgstr "Collega messaggio dal gateway" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Link to thread" +msgstr "Collega alla discussione" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_gateway +msgid "Mail Gateway" +msgstr "Gateway e-mail" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +msgid "Mail Message Gateway Link" +msgstr "Collegamento gateway messaggio e-mail" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Mail Message Gateway Send" +msgstr "Invia messaggio e-mail gateway" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/channel_member_view.esm.js:0 +#, python-format +msgid "Manage guest" +msgstr "Gestione ospite" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__member_ids +msgid "Member" +msgstr "Membro" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Members" +msgstr "Membri" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +msgid "Merge" +msgstr "Unisci" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__message_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__message_id +msgid "Message" +msgstr "Messaggio" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_notification +msgid "Message Notifications" +msgstr "Notifiche messaggio" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__name +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__name +msgid "Name" +msgstr "Nome" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/gateway_follower/gateway_follower.xml:0 +#, python-format +msgid "Not selected" +msgstr "Non selezionato" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__notification_type +msgid "Notification Type" +msgstr "Tipo notifica" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_message_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_message_id +msgid "Original gateway message" +msgstr "Messaggio gateway originale" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__partner_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__partner_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__partner_id +msgid "Partner" +msgstr "Partner" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_res_partner_gateway_channel_unique_partner_gateway +msgid "Partner can only have one configuration for each gateway." +msgstr "Il partner può avere una sola configurazione per ogni gateway." + +#. module: mail_gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_gateway__integrated_webhook_state__pending +msgid "Pending" +msgstr "In attesa" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__resource_ref +msgid "Record reference" +msgstr "Riferimento record" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Remove Webhook" +msgstr "Rimuovi webhook" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Send" +msgstr "Invia" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message_gateway_send +msgid "Send Message through gateway" +msgstr "Invia messaggio attraverso il gateway" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Send with gateway" +msgstr "Invia con gateway" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_partner_gateway_channel +msgid "Technical data used to get the gateway author" +msgstr "Dati tecnici per avere l'autore del gateway" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/composer/composer.xml:0 +#, python-format +msgid "To:" +msgstr "A:" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__token +msgid "Token" +msgstr "Token" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_mail_gateway_mail_gateway_token +msgid "Token must be unique" +msgstr "Il token deve essere univoco" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Update Webhook" +msgstr "Aggiorna webhook" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_users +#: model:res.groups,name:mail_gateway.gateway_user +msgid "User" +msgstr "Utente" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_user_id +msgid "User that will create the messages" +msgstr "Utente che creerà il messaggio" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_key +msgid "Webhook Key" +msgstr "Chiave webhook" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_mail_gateway_mail_gateway_webhook_key +msgid "Webhook Key must be unique" +msgstr "La chiave webhook deve essere univoca" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_secret +msgid "Webhook Secret" +msgstr "Codice segreto webhook" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_url +msgid "Webhook Url" +msgstr "URL webhook" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_user_id +msgid "Webhook User" +msgstr "Utente webhook" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__has_new_channel_security +msgid "" +"When checked, channels are not created automatically. Usable on Telegram" +msgstr "" +"Quando selezionata, i canali non sono creati automaticamente. Utilizzabile " +"in Telegram" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/message/message.xml:0 +#, python-format +msgid "document" +msgstr "documento" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/chatter/chatter.xml:0 +#, python-format +msgid "gateway" +msgstr "gateway" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/message/message.xml:0 +#, python-format +msgid "on" +msgstr "su" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_ir_websocket +msgid "websocket message handling" +msgstr "gestione messaggio websocket" diff --git a/mail_gateway/i18n/mail_gateway.pot b/mail_gateway/i18n/mail_gateway.pot new file mode 100644 index 0000000000..ecab7e4624 --- /dev/null +++ b/mail_gateway/i18n/mail_gateway.pot @@ -0,0 +1,533 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_gateway +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.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: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__anonymous_name +msgid "Anonymous Name" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_guest_manage +msgid "Assign gateway guest to a partner" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__can_set_webhook +msgid "Can Set Webhook" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Cancel" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__channel_type +msgid "Channel Type" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_channel__channel_type +msgid "" +"Chat is private and unique between 2 persons. Group is private among invited" +" persons. Channel can be freely joined (depending on its configuration)." +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_message_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_message_ids +msgid "Child gateway messages" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__company_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__company_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__company_id +msgid "Company" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_partner +msgid "Contact" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +msgid "Create new partner" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__create_uid +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__create_uid +msgid "Created by" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__create_date +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__create_date +msgid "Created on" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_channel +msgid "Discussion Channel" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__display_name +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__display_name +msgid "Display Name" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_thread +msgid "Email Thread" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_notification__gateway_failure_reason +msgid "" +"Failure reason. This is usually the exception thrown by the email server, " +"stored to ease the debugging of mailing issues." +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/discuss_sidebar_category.esm.js:0 +#, python-format +msgid "Find a gateway channel..." +msgstr "" + +#. module: mail_gateway +#: model:ir.actions.act_window,name:mail_gateway.mail_gateway_act_window +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__gateway_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_users__gateway_ids +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_channel__channel_type__gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_notification__notification_type__gateway +#: model:ir.module.category,name:mail_gateway.module_category_gateway +#: model:ir.ui.menu,name:mail_gateway.mail_gateway_menu +msgid "Gateway" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__gateway_channel_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_channel_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner__gateway_channel_ids +#: model:ir.model.fields,field_description:mail_gateway.field_res_users__gateway_channel_ids +msgid "Gateway Channel" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_channel_data +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_channel_data +msgid "Gateway Channel Data" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_channel_token +msgid "Gateway Channel Token" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_failure_reason +msgid "Gateway Failure Reason" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_message_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_message_id +msgid "Gateway Message" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_notification_ids +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_notification_ids +msgid "Gateway Notification" +msgstr "" + +#. module: mail_gateway +#: model:ir.actions.act_window,name:mail_gateway.res_partner_gateway_channel_act_window +#: model:ir.ui.menu,name:mail_gateway.res_partner_gateway_channel_menu +msgid "Gateway Partner Channels" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_thread_data +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_thread_data +msgid "Gateway Thread Data" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest__gateway_token +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__gateway_token +msgid "Gateway Token" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_type +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__gateway_type +msgid "Gateway Type" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_gateway_abstract +msgid "Gateway abstract for functions" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/chatter/chatter.xml:0 +#, python-format +msgid "Gateway message" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_channel__gateway_token +msgid "Gateway related Token" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_guest +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__guest_id +msgid "Guest" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__has_new_channel_security +msgid "Has New Channel Security" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__id +msgid "ID" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Integrate Webhook" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_gateway__integrated_webhook_state__integrated +msgid "Integrated" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__integrated_webhook_state +msgid "Integrated Webhook State" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_channel__gateway_token +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__token +msgid "Key used for integration purposes" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_key +msgid "Key used on the connection URL" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_secret +msgid "" +"Key used to ensure that the connection is secure and\n" +" comes from the desired source" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send____last_update +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel____last_update +msgid "Last Modified on" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__write_uid +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__write_date +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__write_date +msgid "Last Updated on" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +msgid "Link" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Link Message to thread" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message_gateway_link +msgid "Link message from gateway" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Link to thread" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_gateway +msgid "Mail Gateway" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_link_form_view +msgid "Mail Message Gateway Link" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Mail Message Gateway Send" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/channel_member_view.esm.js:0 +#, python-format +msgid "Manage guest" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__member_ids +msgid "Member" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Members" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_guest_manage_form_view +msgid "Merge" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__message_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__message_id +msgid "Message" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_notification +msgid "Message Notifications" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__name +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__name +msgid "Name" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/gateway_follower/gateway_follower.xml:0 +#, python-format +msgid "Not selected" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_notification__notification_type +msgid "Notification Type" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_mail__gateway_message_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message__gateway_message_id +msgid "Original gateway message" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_guest_manage__partner_id +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_send__partner_id +#: model:ir.model.fields,field_description:mail_gateway.field_res_partner_gateway_channel__partner_id +msgid "Partner" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_res_partner_gateway_channel_unique_partner_gateway +msgid "Partner can only have one configuration for each gateway." +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields.selection,name:mail_gateway.selection__mail_gateway__integrated_webhook_state__pending +msgid "Pending" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_message_gateway_link__resource_ref +msgid "Record reference" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Remove Webhook" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_message_gateway_send_form_view +msgid "Send" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_mail_message_gateway_send +msgid "Send Message through gateway" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#: code:addons/mail_gateway/static/src/models/message_action_view.esm.js:0 +#, python-format +msgid "Send with gateway" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_partner_gateway_channel +msgid "Technical data used to get the gateway author" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/composer/composer.xml:0 +#, python-format +msgid "To:" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__token +msgid "Token" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_mail_gateway_mail_gateway_token +msgid "Token must be unique" +msgstr "" + +#. module: mail_gateway +#: model_terms:ir.ui.view,arch_db:mail_gateway.mail_gateway_form_view +msgid "Update Webhook" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_res_users +#: model:res.groups,name:mail_gateway.gateway_user +msgid "User" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__webhook_user_id +msgid "User that will create the messages" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_key +msgid "Webhook Key" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.constraint,message:mail_gateway.constraint_mail_gateway_mail_gateway_webhook_key +msgid "Webhook Key must be unique" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_secret +msgid "Webhook Secret" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_url +msgid "Webhook Url" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,field_description:mail_gateway.field_mail_gateway__webhook_user_id +msgid "Webhook User" +msgstr "" + +#. module: mail_gateway +#: model:ir.model.fields,help:mail_gateway.field_mail_gateway__has_new_channel_security +msgid "" +"When checked, channels are not created automatically. Usable on Telegram" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/message/message.xml:0 +#, python-format +msgid "document" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/chatter/chatter.xml:0 +#, python-format +msgid "gateway" +msgstr "" + +#. module: mail_gateway +#. odoo-javascript +#: code:addons/mail_gateway/static/src/components/message/message.xml:0 +#, python-format +msgid "on" +msgstr "" + +#. module: mail_gateway +#: model:ir.model,name:mail_gateway.model_ir_websocket +msgid "websocket message handling" +msgstr "" diff --git a/mail_gateway/models/__init__.py b/mail_gateway/models/__init__.py new file mode 100644 index 0000000000..a647554bfa --- /dev/null +++ b/mail_gateway/models/__init__.py @@ -0,0 +1,11 @@ +from . import ir_websocket +from . import mail_gateway_abstract +from . import mail_message +from . import mail_notification +from . import discuss_channel +from . import mail_gateway +from . import res_partner +from . import mail_guest +from . import res_users +from . import res_users_settings +from . import mail_thread diff --git a/mail_gateway/models/discuss_channel.py b/mail_gateway/models/discuss_channel.py new file mode 100644 index 0000000000..9a074c91a7 --- /dev/null +++ b/mail_gateway/models/discuss_channel.py @@ -0,0 +1,101 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 + +from odoo import api, fields, models + + +class MailChannel(models.Model): + _inherit = "discuss.channel" + + gateway_channel_token = fields.Char() + anonymous_name = fields.Char() # Same field we will use on im_livechat + gateway_id = fields.Many2one("mail.gateway") + gateway_message_ids = fields.One2many( + "mail.notification", + inverse_name="gateway_channel_id", + ) + company_id = fields.Many2one("res.company", default=False) + channel_type = fields.Selection( + selection_add=[("gateway", "Gateway")], ondelete={"gateway": "set default"} + ) + gateway_token = fields.Char( + related="gateway_id.token", + string="Gateway related Token", + required=False, + ) + + def _compute_is_chat(self): + res = super()._compute_is_chat() + for record in self: + if record.channel_type == "gateway": + record.is_chat = True + return res + + def _channel_info(self): + result = super()._channel_info() + for record, item in zip(self, result, strict=True): + item["gateway"] = { + "id": record.gateway_id.id, + "name": record.gateway_id.name, + "type": record.gateway_id.gateway_type, + } + item["gateway_name"] = record.gateway_id.name + item["gateway_id"] = record.gateway_id.id + return result + + def _generate_avatar_gateway(self): + # We will use this function to set a default avatar on each module + return False + + def _generate_avatar(self): + if self.channel_type not in ("gateway"): + return super()._generate_avatar() + avatar = self._generate_avatar_gateway() + if not avatar: + return False + return base64.b64encode(avatar.encode()) + + @api.returns("mail.message", lambda value: value.id) + def message_post(self, *args, gateway_type=False, **kwargs): + message = super().message_post( + *args, gateway_type=gateway_type or self.gateway_id.gateway_type, **kwargs + ) + if ( + self.gateway_id + and not self.env.context.get("no_gateway_notification", False) + and message.message_type != "notification" + ): + self.env["mail.notification"].create( + { + "mail_message_id": message.id, + "gateway_channel_id": self.id, + "notification_type": "gateway", + "gateway_type": self.gateway_id.gateway_type, + } + ).send_gateway() + return message + + def _message_update_content( + self, + message, + body, + attachment_ids=None, + partner_ids=None, + strict=True, + **kwargs, + ): + res = super()._message_update_content( + message=message, + body=body, + attachment_ids=attachment_ids, + partner_ids=partner_ids, + strict=strict, + **kwargs, + ) + if self.channel_type == "gateway" and message.gateway_notification_ids: + self.env[ + f"mail.gateway.{self.gateway_id.gateway_type}" + ]._update_content_after_hook(self, message) + return res diff --git a/mail_gateway/models/ir_websocket.py b/mail_gateway/models/ir_websocket.py new file mode 100644 index 0000000000..521828c8b2 --- /dev/null +++ b/mail_gateway/models/ir_websocket.py @@ -0,0 +1,21 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models +from odoo.http import request + +from odoo.addons.bus.websocket import wsrequest + + +class IrWebsocket(models.AbstractModel): + _inherit = "ir.websocket" + + def _build_bus_channel_list(self, channels): + req = request or wsrequest + result = super()._build_bus_channel_list(channels) + if req.session.uid: + if req.env.user.has_group("mail_gateway.gateway_user"): + for channel in req.env["discuss.channel"].search( + [("channel_type", "=", "gateway")] + ): + result.append(channel) + return result diff --git a/mail_gateway/models/mail_gateway.py b/mail_gateway/models/mail_gateway.py new file mode 100644 index 0000000000..24cdd35850 --- /dev/null +++ b/mail_gateway/models/mail_gateway.py @@ -0,0 +1,148 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models, tools + + +class MailGateway(models.Model): + _name = "mail.gateway" + _description = "Mail Gateway" + + name = fields.Char(required=True) + token = fields.Char(required=True, help="Key used for integration purposes") + gateway_type = fields.Selection([], required=True) + webhook_key = fields.Char(help="Key used on the connection URL") + webhook_secret = fields.Char( + help="""Key used to ensure that the connection is secure and + comes from the desired source""" + ) + integrated_webhook_state = fields.Selection( + [("pending", "Pending"), ("integrated", "Integrated")], readonly=True + ) + can_set_webhook = fields.Boolean(compute="_compute_webhook_checks") + webhook_url = fields.Char(compute="_compute_webhook_url") + has_new_channel_security = fields.Boolean( + help="When checked, channels are not created automatically. Usable on Telegram" + ) + webhook_user_id = fields.Many2one( + "res.users", + default=lambda self: self.env.ref("base.user_root"), + help="User that will create the messages", + ) + member_ids = fields.Many2many("res.users") + company_id = fields.Many2one( + "res.company", default=lambda self: self.env.company.id + ) + + _sql_constraints = [ + ("mail_gateway_token", "unique(token)", "Token must be unique"), + ( + "mail_gateway_webhook_key", + "unique(webhook_key)", + "Webhook Key must be unique", + ), + ] + + @api.depends("webhook_key") + def _compute_webhook_url(self): + for record in self: + record.webhook_url = record._get_webhook_url() + + def _get_channel_id(self, chat_token): + return ( + self.env["discuss.channel"] + .search( + [ + ("gateway_channel_token", "=", str(chat_token)), + ("gateway_id", "=", self.id), + ], + limit=1, + ) + .id + ) + + def _get_webhook_url(self): + return "{}/gateway/{}/{}/update".format( + self.webhook_url + or self.env["ir.config_parameter"].get_param("web.base.url"), + self.gateway_type, + self.webhook_key, + ) + + def _can_set_webhook(self): + return self.webhook_key and self.webhook_user_id + + @api.depends("gateway_type") + def _compute_webhook_checks(self): + for record in self: + record.can_set_webhook = record._can_set_webhook() + + def set_webhook(self): + self.ensure_one() + if self.can_set_webhook: + self.env["mail.gateway.%s" % self.gateway_type]._set_webhook(self) + + def remove_webhook(self): + self.ensure_one() + self.env["mail.gateway.%s" % self.gateway_type]._remove_webhook(self) + + def update_webhook(self): + self.ensure_one() + self.remove_webhook() + self.set_webhook() + + def write(self, vals): + res = super().write(vals) + if ( + "webhook_key" in vals + or "integrated_webhook_state" in vals + or "webhook_secret" in vals + or "webhook_user_id" in vals + ): + self.clear_caches() + return res + + @api.model_create_multi + def create(self, mvals): + res = super().create(mvals) + self.clear_caches() + return res + + @api.model + @tools.ormcache() + def _get_gateway_map(self, state="integrated", gateway_type=False): + result = {} + for record in self.sudo().search( + [ + ("integrated_webhook_state", "=", state), + ("gateway_type", "=", gateway_type), + ] + ): + result[record.webhook_key] = record._get_gateway_data() + return result + + def _get_gateway_data(self): + return { + "id": self.id, + "company_id": self.company_id.id, + "webhook_secret": self.webhook_secret, + "webhook_user_id": self.webhook_user_id.id, + } + + @api.model + def _get_gateway(self, key, state="integrated", gateway_type=False): + # We are using cache in order to avoid an exploit + if not key: + return False + return self._get_gateway_map(state=state, gateway_type=gateway_type).get( + key, False + ) + + def gateway_info(self): + return [record._gateway_info() for record in self] + + def _gateway_info(self): + return { + "id": self.id, + "name": self.name, + "type": self.gateway_type, + } diff --git a/mail_gateway/models/mail_gateway_abstract.py b/mail_gateway/models/mail_gateway_abstract.py new file mode 100644 index 0000000000..74285381b5 --- /dev/null +++ b/mail_gateway/models/mail_gateway_abstract.py @@ -0,0 +1,82 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import Command, models + + +class MailGatewayAbstract(models.AbstractModel): + _name = "mail.gateway.abstract" + _description = "Gateway abstract for functions" + + def _verify_update(self, bot_data, kwargs): + return True + + def _receive_update(self, gateway, kwargs): + pass + + def _post_process_message(self, message, channel): + self.env["mail.notification"].search( + [("gateway_channel_id", "=", channel.id), ("is_read", "=", False)] + )._set_read_gateway() + + def _post_process_reply(self, related_message): + pass + + def _update_content_after_hook(self, channel, message): + pass + + def _set_webhook(self, gateway): + gateway.integrated_webhook_state = "integrated" + + def _remove_webhook(self, gateway): + gateway.integrated_webhook_state = False + + def _get_channel(self, gateway, token, update, force_create=False): + chat_id = gateway._get_channel_id(token) + if chat_id: + return gateway.env["discuss.channel"].browse(chat_id) + if not force_create and gateway.has_new_channel_security: + return False + channel = gateway.env["discuss.channel"].create( + self._get_channel_vals(gateway, token, update) + ) + channel._broadcast(channel.channel_member_ids.mapped("partner_id").ids) + return channel + + def _get_author(self, gateway, update): + return False + + def _get_channel_vals(self, gateway, token, update): + author = self._get_author(gateway, update) + members = [ + Command.create({"partner_id": partner.id, "is_pinned": True}) + for partner in gateway.member_ids.partner_id + ] + if author: + members.append( + Command.create( + { + "partner_id": author._name == "res.partner" and author.id, + "guest_id": author._name == "mail.guest" and author.id, + } + ) + ) + return { + "gateway_channel_token": token, + "gateway_id": gateway.id, + "channel_type": "gateway", + "channel_member_ids": members, + "company_id": gateway.company_id.id, + } + + def _send( + self, + gateway, + record, + auto_commit=False, + raise_exception=False, + parse_mode=False, + ): + raise NotImplementedError() + + def _get_message_body(self, record): + return record.mail_message_id.body diff --git a/mail_gateway/models/mail_guest.py b/mail_gateway/models/mail_guest.py new file mode 100644 index 0000000000..2e79b478e1 --- /dev/null +++ b/mail_gateway/models/mail_guest.py @@ -0,0 +1,18 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MailGuest(models.Model): + _inherit = "mail.guest" + + gateway_id = fields.Many2one("mail.gateway") + gateway_token = fields.Char() + + def _guest_format(self, fields=None): + result = super()._guest_format(fields=fields) + if not fields or "gateway_id" in fields: + for guest in result: + result[guest]["gateway"] = {"id": guest.gateway_id.id} + return result diff --git a/mail_gateway/models/mail_message.py b/mail_gateway/models/mail_message.py new file mode 100644 index 0000000000..a4bd289256 --- /dev/null +++ b/mail_gateway/models/mail_message.py @@ -0,0 +1,124 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import api, fields, models + + +class MailMessage(models.Model): + _inherit = "mail.message" + + gateway_type = fields.Selection( + selection=lambda r: r.env["mail.gateway"]._fields["gateway_type"].selection + ) + gateway_notification_ids = fields.One2many( + "mail.notification", + inverse_name="mail_message_id", + domain=[("notification_type", "=", "gateway")], + ) + gateway_channel_ids = fields.Many2many( + "res.partner.gateway.channel", compute="_compute_gateway_channel_ids" + ) + gateway_channel_data = fields.Json(compute="_compute_gateway_channel_ids") + gateway_message_ids = fields.One2many( + "mail.message", + inverse_name="gateway_message_id", + string="Child gateway messages", + ) + gateway_message_id = fields.Many2one( + "mail.message", string="Original gateway message" + ) + gateway_thread_data = fields.Json(compute="_compute_gateway_thread_data") + + @api.depends("gateway_message_id") + def _compute_gateway_thread_data(self): + for record in self: + gateway_thread_data = {} + if record.gateway_message_id: + gateway_thread_data.update( + { + "name": record.gateway_message_id.record_name, + "id": record.gateway_message_id.res_id, + "model": record.gateway_message_id.model, + } + ) + record.gateway_thread_data = gateway_thread_data + + @api.depends("notification_ids", "gateway_message_ids") + def _compute_gateway_channel_ids(self): + for record in self: + if self.env.user.has_group("mail_gateway.gateway_user"): + partners = record.notification_ids.res_partner_id + channels = partners.gateway_channel_ids.filtered( + lambda r, messages=record.gateway_message_ids: ( + r.gateway_token, + r.gateway_id.id, + ) + not in [ + ( + notification.gateway_channel_id.gateway_channel_token, + notification.gateway_channel_id.gateway_id.id, + ) + for notification in messages.gateway_notification_ids + ] + ) + else: + channels = self.env["res.partner.gateway.channel"] + record.gateway_channel_ids = channels + record.gateway_channel_data = { + "channels": channels.ids, + "partners": channels.partner_id.ids, + } + + @api.depends("gateway_notification_ids") + def _compute_gateway_channel_id(self): + for rec in self: + if rec.gateway_notification_ids: + rec.gateway_channel_id = rec.gateway_notification_ids[ + 0 + ].gateway_channel_id + + def _get_message_format_fields(self): + result = super()._get_message_format_fields() + result += ["gateway_type", "gateway_channel_data", "gateway_thread_data"] + return result + + def _send_to_gateway_thread(self, gateway_channel_id): + chat_id = gateway_channel_id.gateway_id._get_channel_id( + gateway_channel_id.gateway_token + ) + channel = self.env["discuss.channel"].browse(chat_id) + channel.message_post(**self._get_gateway_thread_message_vals()) + if not self.gateway_type: + self.gateway_type = gateway_channel_id.gateway_id.gateway_type + self.env["mail.notification"].create( + { + "notification_status": "sent", + "mail_message_id": self.id, + "gateway_channel_id": channel.id, + "notification_type": "gateway", + "gateway_type": gateway_channel_id.gateway_id.gateway_type, + } + ) + self.env["bus.bus"]._sendone( + self.env.user.partner_id, + "mail.message/insert", + { + "id": self.id, + "gateway_type": self.gateway_type, + "notifications": self.sudo() + .notification_ids._filtered_for_web_client() + ._notification_format(), + }, + ) + return {} + + def _get_gateway_thread_message_vals(self): + return { + "body": self.body, + "attachment_ids": self.attachment_ids.ids, + "subtype_id": self.subtype_id.id, + "author_id": self.env.user.partner_id.id, + "gateway_message_id": self.id, + "message_type": "comment", + } diff --git a/mail_gateway/models/mail_notification.py b/mail_gateway/models/mail_notification.py new file mode 100644 index 0000000000..7c2da8d999 --- /dev/null +++ b/mail_gateway/models/mail_notification.py @@ -0,0 +1,43 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MailNotification(models.Model): + _inherit = "mail.notification" + + gateway_channel_id = fields.Many2one("discuss.channel") + notification_type = fields.Selection( + selection_add=[("gateway", "Gateway")], ondelete={"gateway": "cascade"} + ) + gateway_message_id = fields.Char(readonly=True) + gateway_failure_reason = fields.Text( + readonly=True, + help="Failure reason. This is usually the exception thrown by the" + " email server, stored to ease the debugging of mailing issues.", + ) + gateway_type = fields.Selection( + selection=lambda r: r.env["mail.gateway"]._fields["gateway_type"].selection + ) + + def _set_read_gateway(self): + self.sudo().write({"is_read": True, "read_date": fields.Datetime.now()}) + + def _notification_format(self): + result = super()._notification_format() + for record, formatted_value in zip(self, result, strict=True): + formatted_value["gateway_type"] = record.gateway_type + formatted_value["channel_name"] = record.gateway_channel_id.name + return result + + def send_gateway(self, auto_commit=False, raise_exception=False, parse_mode="HTML"): + for record in self: + gateway = record.gateway_channel_id.gateway_id + self.env["mail.gateway.%s" % gateway.gateway_type]._send( + gateway, + record, + auto_commit=auto_commit, + raise_exception=raise_exception, + parse_mode=parse_mode, + ) diff --git a/mail_gateway/models/mail_thread.py b/mail_gateway/models/mail_thread.py new file mode 100644 index 0000000000..141262544e --- /dev/null +++ b/mail_gateway/models/mail_thread.py @@ -0,0 +1,117 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + def _get_message_create_valid_field_names(self): + # Add gateway fields + field_names = super()._get_message_create_valid_field_names() + field_names.update( + {"gateway_type", "gateway_notifications", "gateway_message_id"} + ) + return field_names + + def _get_notify_valid_parameters(self): + notify_valid_parameters = super()._get_notify_valid_parameters() + return notify_valid_parameters | {"gateway_notifications"} + + def _notify_thread_by_email(self, message, recipients_data, **kwargs): + partners_data = [r for r in recipients_data if r["notif"] == "gateway"] + if partners_data: + self._notify_thread_by_gateway(message, partners_data, **kwargs) + return super()._notify_thread_by_email(message, recipients_data, **kwargs) + + def _notify_thread_by_gateway(self, message, partners_data, **kwargs): + for partner_data in partners_data: + if partner_data["notif"] != "gateway" or not partner_data.get( + "gateway_channel_id" + ): + continue + message._send_to_gateway_thread( + self.env["res.partner.gateway.channel"].browse( + partner_data.get("gateway_channel_id") + ) + ) + + def _notify_get_recipients(self, message, msg_vals, **kwargs): + if "gateway_notifications" in kwargs: + result = [] + for notification in kwargs["gateway_notifications"]: + if not notification.get("channel_type"): + continue + partner = self.env["res.partner"].browse(notification["partner_id"]) + user = partner.user_ids + follower_data = { + "active": partner.active, + "id": partner.id, + "is_follower": True, + "lang": partner.lang, + "groups": set(user.groups_id.ids), + "notif": notification.get("channel_type"), + "share": partner.partner_share, + "uid": user[:1].id, + "ushare": user and any(user.mapped("share")), + "gateway_channel_id": notification.get("gateway_channel_id"), + } + if follower_data["ushare"]: # any type of share user + follower_data["type"] = "portal" + elif follower_data[ + "share" + ]: # no user, is share -> customer (partner only) + follower_data["type"] = "customer" + else: # has a user not share -> internal user + follower_data["type"] = "user" + result.append(follower_data) + return result + return super()._notify_get_recipients(message, msg_vals, **kwargs) + + def _get_mail_thread_data(self, request_list): + data = super()._get_mail_thread_data(request_list) + data["gateway_followers"] = [ + f["partner"] + for f in data.get("followers", []) + if f["partner"]["gateway_channels"] + ] + return data + + def _check_can_update_message_content(self, messages): + # We can delete the messages comming from a gateway on not channels + if self._name != "discuss.channel": + new_messages = messages.filtered(lambda r: not r.gateway_message_ids) + else: + new_messages = messages + return super()._check_can_update_message_content(new_messages) + + def _message_update_content( + self, + message, + body, + attachment_ids=None, + partner_ids=None, + strict=True, + **kwargs, + ): + result = super()._message_update_content( + message=message, + body=body, + attachment_ids=attachment_ids, + partner_ids=partner_ids, + strict=strict, + **kwargs, + ) + if body == "": + # Unlink the message + for gateway_msg in message.gateway_message_ids: + gateway_msg.gateway_message_id = False + self.env["bus.bus"]._sendone( + self.env.user.partner_id, + "mail.message/insert", + { + "id": gateway_msg.id, + "gateway_thread_data": gateway_msg.sudo().gateway_thread_data, + }, + ) + return result diff --git a/mail_gateway/models/res_partner.py b/mail_gateway/models/res_partner.py new file mode 100644 index 0000000000..620389f439 --- /dev/null +++ b/mail_gateway/models/res_partner.py @@ -0,0 +1,100 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ResPartner(models.Model): + """Update of res.partner class to take into account the gateway.""" + + _inherit = "res.partner" + + gateway_channel_ids = fields.One2many( + "res.partner.gateway.channel", inverse_name="partner_id" + ) + + def mail_partner_format(self, fields=None): + """Override to add gateway info.""" + partners_format = super().mail_partner_format(fields=fields) + if not fields: + fields = {"gateway_channel_ids": True} + for partner in self: + if "gateway_channel_ids" in fields: + partners_format.get(partner).update( + { + "gateway_channels": partner.gateway_channel_ids.mail_format(), + } + ) + return partners_format + + def _get_channels_as_member(self): + channels = super()._get_channels_as_member() + if self.env.user.has_group("mail_gateway.gateway_user"): + channels |= self.env["discuss.channel"].search( + [ + ("channel_type", "=", "gateway"), + ( + "channel_member_ids", + "in", + self.env["discuss.channel.member"] + .sudo() + ._search( + [ + ("partner_id", "=", self.id), + ("is_pinned", "=", True), + ] + ), + ), + ] + ) + return channels + + +class ResPartnerGatewayChannel(models.Model): + _name = "res.partner.gateway.channel" + _description = "Technical data used to get the gateway author" + + name = fields.Char(related="gateway_id.name") + partner_id = fields.Many2one( + "res.partner", required=True, readonly=True, ondelete="cascade" + ) + gateway_id = fields.Many2one( + "mail.gateway", required=True, readonly=True, ondelete="cascade" + ) + gateway_token = fields.Char(readonly=True) + company_id = fields.Many2one( + "res.company", related="gateway_id.company_id", store=True + ) + + @api.depends_context("mail_gateway_partner_info") + def _compute_display_name(self): + # Be able to tell to which partner belongs the gateway partner channel + # e.g.: picking it from a selector + if not self.env.context.get("mail_gateway_partner_info"): + return super()._compute_display_name() + for gateway_channel in self: + gateway_channel.display_name = ( + f"{gateway_channel.partner_id.display_name} ({gateway_channel.name})" + ) + + _sql_constraints = [ + ( + "unique_partner_gateway", + "UNIQUE(partner_id, gateway_id)", + "Partner can only have one configuration for each gateway.", + ), + ] + + def mail_format(self): + return [r._mail_format() for r in self] + + def _mail_format(self): + return { + "id": self.id, + "name": self.name, + "gateway": { + "id": self.gateway_id.id, + "name": self.gateway_id.name, + "type": self.gateway_id.gateway_type, + }, + } diff --git a/mail_gateway/models/res_users.py b/mail_gateway/models/res_users.py new file mode 100644 index 0000000000..27cd060db0 --- /dev/null +++ b/mail_gateway/models/res_users.py @@ -0,0 +1,15 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResUsers(models.Model): + _inherit = "res.users" + + gateway_ids = fields.Many2many("mail.gateway") + + def _init_messaging(self): + result = super()._init_messaging() + result["gateways"] = self.gateway_ids.gateway_info() + return result diff --git a/mail_gateway/models/res_users_settings.py b/mail_gateway/models/res_users_settings.py new file mode 100644 index 0000000000..19c3bf3a20 --- /dev/null +++ b/mail_gateway/models/res_users_settings.py @@ -0,0 +1,12 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ResUsersSettings(models.Model): + _inherit = "res.users.settings" + + is_discuss_sidebar_category_gateway_open = fields.Boolean( + string="Gateway Category Open", + default=True, + help="The gateway category in the sidebar will be open", + ) diff --git a/mail_gateway/pyproject.toml b/mail_gateway/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/mail_gateway/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/mail_gateway/readme/CONFIGURATION.rst b/mail_gateway/readme/CONFIGURATION.rst new file mode 100644 index 0000000000..470eee6728 --- /dev/null +++ b/mail_gateway/readme/CONFIGURATION.rst @@ -0,0 +1,4 @@ +- Access in development mode +- Go to `Settings / Technical / Email / Gateway` +- Create a gateway. Follow the instruction of the specific tab in order to integrate it. +- Start receiving notifications diff --git a/mail_gateway/readme/CONTRIBUTORS.md b/mail_gateway/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..50056de396 --- /dev/null +++ b/mail_gateway/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Enric Tobella +- Olga Marco diff --git a/mail_gateway/readme/CREDITS.md b/mail_gateway/readme/CREDITS.md new file mode 100644 index 0000000000..618c841c1b --- /dev/null +++ b/mail_gateway/readme/CREDITS.md @@ -0,0 +1,2 @@ +This work has been funded by AEOdoo (Asociación Española de Odoo - +) diff --git a/mail_gateway/readme/DESCRIPTION.md b/mail_gateway/readme/DESCRIPTION.md new file mode 100644 index 0000000000..ba0e37104b --- /dev/null +++ b/mail_gateway/readme/DESCRIPTION.md @@ -0,0 +1,8 @@ +This module will allow you to integrate an external chat system in your +Odoo system. It requires extra modules with the specific configuration +of each chat system, like mail_gateway_telegram or +mail_gateway_whatsapp. + +This way, a group of users can respond customers or any other set of +partners within Odoo, but the messages will be sent through the external +chat system. diff --git a/mail_gateway/readme/USAGE.md b/mail_gateway/readme/USAGE.md new file mode 100644 index 0000000000..ee88dce581 --- /dev/null +++ b/mail_gateway/readme/USAGE.md @@ -0,0 +1,12 @@ +When external messages are received, they will be directly sent to the +discuss menu. Answering to these messages will send the answer to the +external contact. We can assign this messages to any record using the +message actions. Also, we can assign the sender to a partner using the +followers menu and selecting the partner. + +On a standard record associated to a partner with external chat, we can +send messages to the external contact directly selecting the methods of +the partner. To use this, we just need to use the + +It is recomended to enable chatter notification to all users that will +receive messages from gateways. diff --git a/mail_gateway/security/ir.model.access.csv b/mail_gateway/security/ir.model.access.csv new file mode 100644 index 0000000000..ca8af9fe3d --- /dev/null +++ b/mail_gateway/security/ir.model.access.csv @@ -0,0 +1,12 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_res_partner_gateway_channel_portal,res.partner.gateway.channel.portal,model_res_partner_gateway_channel,base.group_portal,1,0,0,0 +access_res_partner_gateway_channel_user,res.partner.gateway.channel,model_res_partner_gateway_channel,base.group_user,1,0,0,0 +manage_res_partner_gateway_channel_user,res.partner.gateway.channel,model_res_partner_gateway_channel,gateway_user,1,1,1,1 +access_mail_message_gateway_send_user,mail.message.gateway.send,model_mail_message_gateway_send,base.group_user,1,1,1,0 +access_mail_gateway_portal,mail.gateway.portal,model_mail_gateway,base.group_portal,1,0,0,0 +access_mail_gateway_public,mail.gateway.public,model_mail_gateway,base.group_public,1,0,0,0 +access_mail_gateway_user,mail.gateway.user,model_mail_gateway,base.group_user,1,0,0,0 +access_mail_guest_manage,mail.gateway.internal,model_mail_guest_manage,base.group_user,1,1,1,1 +access_mail_message_gateway_link,mail.message.link.all,model_mail_message_gateway_link,base.group_user,1,1,1,1 +access_mail_gateway_system,mail_gateway,model_mail_gateway,base.group_system,1,1,1,1 +access_mail_compose_gateway_message,access.mail.compose.gateway.message,model_mail_compose_gateway_message,base.group_user,1,1,1,0 diff --git a/mail_gateway/security/security.xml b/mail_gateway/security/security.xml new file mode 100644 index 0000000000..4d6ffd97fe --- /dev/null +++ b/mail_gateway/security/security.xml @@ -0,0 +1,61 @@ + + + + Gateway + + + User + + + + + discuss.channel: access gateway + + + [('channel_type', '=', 'gateway'), ('company_id', 'in', company_ids + [False]) ] + + + + + + + Mail.gateway: multicompany rule + + [('company_id', 'in', company_ids + [False]) ] + + + + + + + res.partner.gateway.channel: multicompany rule + + [('company_id', 'in', company_ids + [False]) ] + + + + + + + discuss.channel.member: write its own entries on gateway channels members + + + [('channel_id.channel_type', '=', 'gateway')] + + + + + + + diff --git a/mail_gateway/static/description/icon.png b/mail_gateway/static/description/icon.png new file mode 100644 index 0000000000..b77400d170 Binary files /dev/null and b/mail_gateway/static/description/icon.png differ diff --git a/mail_gateway/static/description/index.html b/mail_gateway/static/description/index.html new file mode 100644 index 0000000000..f7d29c3535 --- /dev/null +++ b/mail_gateway/static/description/index.html @@ -0,0 +1,451 @@ + + + + + +Mail Gateway + + + +
+

Mail Gateway

+ + +

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

+

This module will allow you to integrate an external chat system in your +Odoo system. It requires extra modules with the specific configuration +of each chat system, like mail_gateway_telegram or +mail_gateway_whatsapp.

+

This way, a group of users can respond customers or any other set of +partners within Odoo, but the messages will be sent through the external +chat system.

+

Table of contents

+ +
+

Usage

+

When external messages are received, they will be directly sent to the +discuss menu. Answering to these messages will send the answer to the +external contact. We can assign this messages to any record using the +message actions. Also, we can assign the sender to a partner using the +followers menu and selecting the partner.

+

On a standard record associated to a partner with external chat, we can +send messages to the external contact directly selecting the methods of +the partner. To use this, we just need to use the

+

It is recomended to enable chatter notification to all users that will +receive messages from gateways.

+
+
+

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

+
    +
  • Creu Blanca
  • +
  • Dixmit
  • +
+
+
+

Contributors

+
    +
  • Enric Tobella
  • +
  • Olga Marco
  • +
+
+
+

Other credits

+

This work has been funded by AEOdoo (Asociación Española de Odoo - +https://www.aeodoo.org)

+
+
+

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

+

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

+
+
+
+ + diff --git a/mail_gateway/static/src/components/chatter/chatter.esm.js b/mail_gateway/static/src/components/chatter/chatter.esm.js new file mode 100644 index 0000000000..08f79cd2ad --- /dev/null +++ b/mail_gateway/static/src/components/chatter/chatter.esm.js @@ -0,0 +1,15 @@ +/** @odoo-module **/ +import {Chatter} from "@mail/core/web/chatter"; +import {patch} from "@web/core/utils/patch"; +import {GatewayFollower} from "../gateway_follower/gateway_follower.esm"; + +patch(Chatter, { + components: {...Chatter.components, GatewayFollower}, +}); + +patch(Chatter.prototype, { + toggleComposer(mode = false) { + this.state.thread.composer.isGateway = mode === "gateway"; + super.toggleComposer(mode); + }, +}); diff --git a/mail_gateway/static/src/components/chatter/chatter.xml b/mail_gateway/static/src/components/chatter/chatter.xml new file mode 100644 index 0000000000..80a0cedea0 --- /dev/null +++ b/mail_gateway/static/src/components/chatter/chatter.xml @@ -0,0 +1,54 @@ + + + + + + + + + +
+ To: + + + +
+
+ +
+ + props.hasFollowers and state.composerType !== 'note' and state.composerType !== 'gateway' + +
+
diff --git a/mail_gateway/static/src/components/composer/composer.esm.js b/mail_gateway/static/src/components/composer/composer.esm.js new file mode 100644 index 0000000000..ad13c3e10b --- /dev/null +++ b/mail_gateway/static/src/components/composer/composer.esm.js @@ -0,0 +1,108 @@ +/** @odoo-module **/ +import {patch} from "@web/core/utils/patch"; +import {Composer} from "@mail/core/common/composer"; +import {_t} from "@web/core/l10n/translation"; +import {prettifyMessageContent} from "@mail/utils/common/format"; + +patch(Composer.prototype, { + get SEND_TEXT() { + if (this.props.type === "gateway" && !this.props.composer.message) { + return _t("Send gateway"); + } + return super.SEND_TEXT; + }, + get placeholder() { + if ( + this.thread?.model !== "discuss.channel" && + !this.props.placeholder && + this.props.type === "gateway" + ) { + return _t("Send a message to a gateway..."); + } + return super.placeholder; + }, + get isSendButtonDisabled() { + const isSendButtonDisabled = super.isSendButtonDisabled; + if (this.props.type !== "gateway") { + return isSendButtonDisabled; + } + return isSendButtonDisabled || !this.thread?.gateway_notifications.length; + }, + onFocusin() { + super.onFocusin(); + if (this.props.type !== "gateway") { + this.thread.gateway_notifications = []; + } + }, + async onClickFullComposer() { + if (this.props.type !== "gateway") { + return super.onClickFullComposer(...arguments); + } + const attachmentIds = this.props.composer.attachments.map( + (attachment) => attachment.id + ); + const body = this.props.composer.textInputContent; + const validMentions = this.store.user + ? this.messageService.getMentionsFromText(body, { + mentionedChannels: this.props.composer.mentionedChannels, + mentionedPartners: this.props.composer.mentionedPartners, + }) + : undefined; + // Debugger + const context = { + default_attachment_ids: attachmentIds, + default_body: await prettifyMessageContent(body, validMentions), + default_model: this.thread.model, + default_partner_ids: this.thread.suggestedRecipients + .filter((recipient) => recipient.checked) + .map((recipient) => recipient.persona.id), + default_res_ids: [this.thread.id], + default_subtype_xmlid: "mail.mt_comment", + mail_post_autofollow: this.thread.hasWriteAccess, + default_wizard_partner_ids: Array.from( + new Set( + this.thread.gateway_followers.map((follower) => { + return follower.id; + }) + ) + ), + default_wizard_channel_ids: Array.from( + new Set( + this.thread.gateway_followers + .map((follower) => { + return follower.gateway_channels.map( + (channel) => channel?.id + ); + }) + .flat() + ) + ), + }; + const action = { + name: _t("Gateway message"), + type: "ir.actions.act_window", + res_model: "mail.compose.gateway.message", + view_mode: "form", + views: [[false, "form"]], + target: "new", + context: context, + }; + const options = { + onClose: (...args) => { + // Args === [] : click on 'X' + // args === { special: true } : click on 'discard' + const isDiscard = args.length === 0 || args[0]?.special; + // Otherwise message is posted (args === [undefined]) + if (!isDiscard && this.props.composer.thread.type === "mailbox") { + this.notifySendFromMailbox(); + } + this.clear(); + this.props.messageToReplyTo?.cancel(); + if (this.thread) { + this.threadService.fetchNewMessages(this.thread); + } + }, + }; + await this.env.services.action.doAction(action, options); + }, +}); diff --git a/mail_gateway/static/src/components/gateway_follower/gateway_follower.esm.js b/mail_gateway/static/src/components/gateway_follower/gateway_follower.esm.js new file mode 100644 index 0000000000..fd17ff07b7 --- /dev/null +++ b/mail_gateway/static/src/components/gateway_follower/gateway_follower.esm.js @@ -0,0 +1,44 @@ +/** @odoo-module **/ +import {Component} from "@odoo/owl"; + +export class GatewayFollower extends Component { + static template = "mail_gateway.GatewayFollowerView"; + static props = ["follower", "composer"]; + setup() { + this.channel = false; + this.follower_channel_ids = this.props.follower.gateway_channels.map( + (channel) => channel.id + ); + this._clearGatewayNotifications(); + } + get composerGatewayFollower() { + return this.props.follower; + } + _getMessageData() { + return { + partner_id: this.props.follower.id, + channel_type: "gateway", + gateway_channel_id: this.channel, + }; + } + _clearGatewayNotifications() { + this.props.composer.thread.gateway_notifications = + this.props.composer.thread.gateway_notifications.filter( + (gateway_notification) => { + return !this.follower_channel_ids.includes( + gateway_notification.gateway_channel_id + ); + } + ); + } + onChangeGatewayChannel(ev) { + this.channel = parseInt(ev.target.options[ev.target.selectedIndex].value, 10); + if (this.channel) { + this.props.composer.thread.gateway_notifications.push( + this._getMessageData() + ); + } else { + this._clearGatewayNotifications(); + } + } +} diff --git a/mail_gateway/static/src/components/gateway_follower/gateway_follower.scss b/mail_gateway/static/src/components/gateway_follower/gateway_follower.scss new file mode 100644 index 0000000000..ab0d9502ae --- /dev/null +++ b/mail_gateway/static/src/components/gateway_follower/gateway_follower.scss @@ -0,0 +1,12 @@ +.o_gateway_composer_selector { + select { + width: unset; + display: inline-block; + min-width: 15%; + } + + .o_gateway_composer_selector_partner { + min-width: 30%; + display: inline-block; + } +} diff --git a/mail_gateway/static/src/components/gateway_follower/gateway_follower.xml b/mail_gateway/static/src/components/gateway_follower/gateway_follower.xml new file mode 100644 index 0000000000..d5713dac59 --- /dev/null +++ b/mail_gateway/static/src/components/gateway_follower/gateway_follower.xml @@ -0,0 +1,28 @@ + + + +
+ + +
+
+
diff --git a/mail_gateway/static/src/components/message/message.xml b/mail_gateway/static/src/components/message/message.xml new file mode 100644 index 0000000000..226535be5b --- /dev/null +++ b/mail_gateway/static/src/components/message/message.xml @@ -0,0 +1,22 @@ + + + + + + + + on + + + + + diff --git a/mail_gateway/static/src/components/message/message_patch.esm.js b/mail_gateway/static/src/components/message/message_patch.esm.js new file mode 100644 index 0000000000..c3416b14bf --- /dev/null +++ b/mail_gateway/static/src/components/message/message_patch.esm.js @@ -0,0 +1,67 @@ +/* @odoo-module */ +import {_t} from "@web/core/l10n/translation"; +import {Message} from "@mail/core/common/message"; +import {patch} from "@web/core/utils/patch"; + +patch(Message.prototype, { + hasAuthorClickable() { + if ( + this.message.gateway_type && + this.message.author?.type === "guest" && + this.message.author.id + ) { + return true; + } + return super.hasAuthorClickable(); + }, + getAuthorText() { + if (this.hasAuthorClickable() && this.message.gateway_type) { + return _t("Create partner"); + } + return super.getAuthorText(); + }, + onClickAuthor(ev) { + if (this.message.gateway_type && this.hasAuthorClickable()) { + ev.stopPropagation(); + return this.env.services.action.doAction({ + name: _t("Manage guest"), + type: "ir.actions.act_window", + res_model: "mail.guest.manage", + context: {default_guest_id: this.message.author.id}, + views: [[false, "form"]], + target: "new", + }); + } + return super.onClickAuthor(...arguments); + }, + onClickLinkGatewayToThread() { + this.env.services.action.doAction({ + name: _t("Link Message to thread"), + type: "ir.actions.act_window", + res_model: "mail.message.gateway.link", + context: {default_message_id: this.message.id}, + views: [[false, "form"]], + target: "new", + }); + }, + onClickSendWithGateway() { + this.env.services.action.doAction({ + name: _t("Send with gateway"), + type: "ir.actions.act_window", + res_model: "mail.message.gateway.send", + context: { + ...this.props.message.gateway_channel_data, + default_message_id: this.props.message.id, + }, + views: [[false, "form"]], + target: "new", + }); + }, + openGatewayThreadRecord() { + const gateway_thread = this.threadService.getThread( + this.message.gateway_thread_data.model, + this.message.gateway_thread_data.id + ); + this.threadService.open(gateway_thread); + }, +}); diff --git a/mail_gateway/static/src/components/message_notification_popover_content/message_notification_popover_content.xml b/mail_gateway/static/src/components/message_notification_popover_content/message_notification_popover_content.xml new file mode 100644 index 0000000000..a4a4898531 --- /dev/null +++ b/mail_gateway/static/src/components/message_notification_popover_content/message_notification_popover_content.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/mail_gateway/static/src/core/common/composer_model_patch.esm.js b/mail_gateway/static/src/core/common/composer_model_patch.esm.js new file mode 100644 index 0000000000..be261dbfe8 --- /dev/null +++ b/mail_gateway/static/src/core/common/composer_model_patch.esm.js @@ -0,0 +1,23 @@ +/* @odoo-module */ +import {Composer} from "@mail/core/common/composer_model"; +import {patch} from "@web/core/utils/patch"; +import {url} from "@web/core/utils/urls"; + +patch(Composer, { + get resUrl() { + if (!this.gateway_thread_data) { + return super.resUrl; + } + return `${url("/web")}#model=${this.gateway_thread_data.model}&id=${ + this.gateway_thread_data.id + }`; + }, +}); + +patch(Composer.prototype, { + setup() { + super.setup(); + this.gateway_channel = false; + this.gateway_partner = false; + }, +}); diff --git a/mail_gateway/static/src/core/common/discuss_app_model_patch.esm.js b/mail_gateway/static/src/core/common/discuss_app_model_patch.esm.js new file mode 100644 index 0000000000..aac2c99a13 --- /dev/null +++ b/mail_gateway/static/src/core/common/discuss_app_model_patch.esm.js @@ -0,0 +1,31 @@ +/* @odoo-module */ + +import {DiscussApp} from "@mail/core/common/discuss_app_model"; +import {Record} from "@mail/core/common/record"; + +import {_t} from "@web/core/l10n/translation"; +import {patch} from "@web/core/utils/patch"; + +patch(DiscussApp, { + new(data) { + const res = super.new(data); + res.gateway = { + extraClass: "o-mail-DiscussSidebarCategory-gateway", + id: "gateway", + name: _t("Gateway"), + isOpen: false, + canView: false, + canAdd: true, + addTitle: _t("Search Gateway Channel"), + serverStateKey: "is_discuss_sidebar_category_gateway_open", + }; + return res; + }, +}); + +patch(DiscussApp.prototype, { + setup(env) { + super.setup(env); + this.gateway = Record.one("DiscussAppCategory"); + }, +}); diff --git a/mail_gateway/static/src/core/common/message_actions.esm.js b/mail_gateway/static/src/core/common/message_actions.esm.js new file mode 100644 index 0000000000..e66d12212d --- /dev/null +++ b/mail_gateway/static/src/core/common/message_actions.esm.js @@ -0,0 +1,23 @@ +/* @odoo-module */ +import {_t} from "@web/core/l10n/translation"; +import {messageActionsRegistry} from "@mail/core/common/message_actions"; + +messageActionsRegistry.add("link_gateway_to_thread", { + condition: (component) => + component.message.gateway_type && + component.props.thread.model === "discuss.channel", + icon: "fa-link", + title: _t("Link to thread"), + onClick: (component) => component.onClickLinkGatewayToThread(), + sequence: 20, +}); + +messageActionsRegistry.add("send_with_gateway", { + condition: (component) => + !component.message.gateway_type && + component.props.thread.model !== "discuss.channel", + icon: "fa-share-square-o", + title: _t("Send with gateway"), + onClick: (component) => component.onClickSendWithGateway(), + sequence: 20, +}); diff --git a/mail_gateway/static/src/core/common/message_model_patch.esm.js b/mail_gateway/static/src/core/common/message_model_patch.esm.js new file mode 100644 index 0000000000..0f546b3d35 --- /dev/null +++ b/mail_gateway/static/src/core/common/message_model_patch.esm.js @@ -0,0 +1,21 @@ +/* @odoo-module */ +import {Message} from "@mail/core/common/message_model"; +import {patch} from "@web/core/utils/patch"; +import {url} from "@web/core/utils/urls"; + +patch(Message, { + get resUrl() { + if (!this.gateway_thread_data) { + return super.resUrl; + } + return `${url("/web")}#model=${this.gateway_thread_data.model}&id=${ + this.gateway_thread_data.id + }`; + }, +}); + +patch(Message.prototype, { + get editable() { + return super.editable && !this.gateway_type; + }, +}); diff --git a/mail_gateway/static/src/core/common/notification_model_patch.esm.js b/mail_gateway/static/src/core/common/notification_model_patch.esm.js new file mode 100644 index 0000000000..82cdb73ebf --- /dev/null +++ b/mail_gateway/static/src/core/common/notification_model_patch.esm.js @@ -0,0 +1,35 @@ +/* @odoo-module */ +import {Notification} from "@mail/core/common/notification_model"; +import {patch} from "@web/core/utils/patch"; + +patch(Notification.prototype, { + get icon() { + if (this.gateway_type) { + return `fa fa-${this.gateway_type}`; + } + return super.icon; + }, + get statusIcon() { + if (!this.gateway_type) { + return super.statusIcon; + } + // It can be overriden on the gateway implementation + switch (this.notification_status) { + case "process": + return "fa fa-hourglass-half"; + case "pending": + return "fa fa-paper-plane-o"; + case "sent": + return `fa fa-${this.gateway_type} text-success`; + case "bounce": + return `fa fa-${this.gateway_type} text-danger`; + case "exception": + return `fa fa-${this.gateway_type} text-danger`; + case "ready": + return "fa fa-send-o"; + case "canceled": + return "fa fa-trash-o"; + } + return ""; + }, +}); diff --git a/mail_gateway/static/src/core/common/persona_model_patch.esm.js b/mail_gateway/static/src/core/common/persona_model_patch.esm.js new file mode 100644 index 0000000000..5956997e75 --- /dev/null +++ b/mail_gateway/static/src/core/common/persona_model_patch.esm.js @@ -0,0 +1,12 @@ +/** @odoo-module */ + +import {Persona} from "@mail/core/common/persona_model"; +import {patch} from "@web/core/utils/patch"; +import {Record} from "@mail/core/common/record"; + +patch(Persona.prototype, { + setup() { + super.setup(); + this.gateway_channels = Record.many("GatewayChannel"); + }, +}); diff --git a/mail_gateway/static/src/core/common/store_service_patch.esm.js b/mail_gateway/static/src/core/common/store_service_patch.esm.js new file mode 100644 index 0000000000..5d5a9924fa --- /dev/null +++ b/mail_gateway/static/src/core/common/store_service_patch.esm.js @@ -0,0 +1,14 @@ +/* @odoo-module */ +import {Gateway} from "../../models/gateway.esm"; +import {Store} from "@mail/core/common/store_service"; +import {patch} from "@web/core/utils/patch"; + +/** @type {import("models").Store} */ +const storePatch = { + setup() { + super.setup(...arguments); + /** @type {typeof import("@mail_gateway/models/gateway").Gateway} */ + this.Gateway = Gateway; + }, +}; +patch(Store.prototype, storePatch); diff --git a/mail_gateway/static/src/core/common/thread_model_patch.esm.js b/mail_gateway/static/src/core/common/thread_model_patch.esm.js new file mode 100644 index 0000000000..5b77a716e1 --- /dev/null +++ b/mail_gateway/static/src/core/common/thread_model_patch.esm.js @@ -0,0 +1,49 @@ +/* @odoo-module */ +import {assignDefined, assignIn} from "@mail/utils/common/misc"; +import {patch} from "@web/core/utils/patch"; +import {Record} from "@mail/core/common/record"; +import {Thread} from "@mail/core/common/thread_model"; +import {url} from "@web/core/utils/urls"; + +patch(Thread, { + _insert(data) { + const thread = super._insert(...arguments); + if (thread.type === "gateway") { + assignIn(thread, data, ["anonymous_name", "gateway"]); + this.store.discuss.gateway.threads.add(thread); + } + return thread; + }, +}); + +patch(Thread.prototype, { + setup() { + super.setup(); + this.gateway = Record.one("Gateway"); + this.operator = Record.one("Persona"); + this.gateway_notifications = []; + this.gateway_followers = Record.many("Persona"); + }, + get isChatChannel() { + return this.type === "gateway" || super.isChatChannel; + }, + get hasMemberList() { + return this.type === "gateway" || super.hasMemberList; + }, + get imgUrl() { + if (this.type !== "gateway") { + return super.imgUrl; + } + return url( + `/discuss/channel/${this.id}/avatar_128`, + assignDefined({}, {unique: this.avatarCacheKey}) + ); + }, + /** @param {Object} data */ + update(data) { + super.update(data); + if ("gateway_id" in data && this.type === "gateway") { + this.gateway = data.gateway_id; + } + }, +}); diff --git a/mail_gateway/static/src/core/common/thread_service_patch.esm.js b/mail_gateway/static/src/core/common/thread_service_patch.esm.js new file mode 100644 index 0000000000..063ba1f857 --- /dev/null +++ b/mail_gateway/static/src/core/common/thread_service_patch.esm.js @@ -0,0 +1,19 @@ +/* @odoo-module */ +import {ThreadService} from "@mail/core/common/thread_service"; +import {patch} from "@web/core/utils/patch"; + +patch(ThreadService.prototype, { + async fetchData(thread, ...args) { + const result = await super.fetchData(thread, ...args); + thread.gateway_followers = result.gateway_followers; + return result; + }, + async getMessagePostParams(params) { + const post_params = await super.getMessagePostParams(...arguments); + if (params.thread.gateway_notifications) { + post_params.post_data.gateway_notifications = + params.thread.gateway_notifications; + } + return post_params; + }, +}); diff --git a/mail_gateway/static/src/core/web/discuss_app_category_model_patch.esm.js b/mail_gateway/static/src/core/web/discuss_app_category_model_patch.esm.js new file mode 100644 index 0000000000..029e893768 --- /dev/null +++ b/mail_gateway/static/src/core/web/discuss_app_category_model_patch.esm.js @@ -0,0 +1,21 @@ +/* @odoo-module */ + +import {patch} from "@web/core/utils/patch"; +import {DiscussAppCategory} from "@mail/core/common/discuss_app_category_model"; +import {compareDatetime} from "@mail/utils/common/misc"; + +patch(DiscussAppCategory.prototype, { + /** + * @param {import("models").Thread} t1 + * @param {import("models").Thread} t2 + */ + sortThreads(t1, t2) { + if (this.id === "gateway") { + return ( + compareDatetime(t2.lastInterestDateTime, t1.lastInterestDateTime) || + t2.id - t1.id + ); + } + return super.sortThreads(t1, t2); + }, +}); diff --git a/mail_gateway/static/src/core/web/discuss_sidebar_categories.esm.js b/mail_gateway/static/src/core/web/discuss_sidebar_categories.esm.js new file mode 100644 index 0000000000..a99962a2ac --- /dev/null +++ b/mail_gateway/static/src/core/web/discuss_sidebar_categories.esm.js @@ -0,0 +1,16 @@ +/* @odoo-module */ + +import {discussSidebarCategoriesRegistry} from "@mail/discuss/core/web/discuss_sidebar_categories"; + +discussSidebarCategoriesRegistry.add( + "gateway", + { + predicate: (store) => { + store.discuss.gateway.threads.some( + (thread) => thread?.displayToSelf || thread?.isLocallyPinned + ); + }, + value: (store) => store.discuss.gateway, + }, + {sequence: 30} +); diff --git a/mail_gateway/static/src/core/web/discuss_sidebar_category_item_patch.xml b/mail_gateway/static/src/core/web/discuss_sidebar_category_item_patch.xml new file mode 100644 index 0000000000..64f640632d --- /dev/null +++ b/mail_gateway/static/src/core/web/discuss_sidebar_category_item_patch.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mail_gateway/static/src/core/web/gateway_core_web_service.esm.js b/mail_gateway/static/src/core/web/gateway_core_web_service.esm.js new file mode 100644 index 0000000000..4be3c8cb76 --- /dev/null +++ b/mail_gateway/static/src/core/web/gateway_core_web_service.esm.js @@ -0,0 +1,40 @@ +/* @odoo-module */ +import {reactive} from "@odoo/owl"; +import {registry} from "@web/core/registry"; + +export class GatewayCoreWeb { + constructor(env, services) { + Object.assign(this, { + busService: services.bus_service, + }); + /** @type {import("@mail/core/common/messaging_service").Messaging} */ + this.messagingService = services["mail.messaging"]; + /** @type {import("@mail/core/common/store_service").Store} */ + this.store = services["mail.store"]; + } + setup() { + this.messagingService.isReady.then((data) => { + if (data.current_user_settings?.is_discuss_sidebar_category_gateway_open) { + this.store.discuss.gateway.isOpen = true; + } + this.busService.subscribe("res.users.settings", (payload) => { + if (payload) { + this.store.discuss.gateway.isOpen = + payload.is_discuss_sidebar_category_gateway_open ?? + this.store.discuss.gateway.isOpen; + } + }); + }); + } +} + +export const gatewayCoreWeb = { + dependencies: ["bus_service", "mail.messaging", "mail.store"], + start(env, services) { + const gatewayCoreWeb = reactive(new GatewayCoreWeb(env, services)); + gatewayCoreWeb.setup(); + return gatewayCoreWeb; + }, +}; + +registry.category("services").add("mail_gateway.core.web", gatewayCoreWeb); diff --git a/mail_gateway/static/src/models/gateway.esm.js b/mail_gateway/static/src/models/gateway.esm.js new file mode 100644 index 0000000000..089bd31fa9 --- /dev/null +++ b/mail_gateway/static/src/models/gateway.esm.js @@ -0,0 +1,23 @@ +/** @odoo-module **/ +import {Record} from "@mail/core/common/record"; + +export class Gateway extends Record { + static id = "id"; + /** @type {Object.} */ + static records = {}; + /** @returns {import("models").Gateway} */ + static get(data) { + return super.get(data); + } + /** @returns {import("models").Gateway|import("models").Gateway[]} */ + static insert() { + return super.insert(...arguments); + } + /** @type {number} */ + id; + /** @type {string} */ + type; + /** @type {string} */ + name; +} +Gateway.register(); diff --git a/mail_gateway/static/src/models/gateway_channel.esm.js b/mail_gateway/static/src/models/gateway_channel.esm.js new file mode 100644 index 0000000000..f538f784e6 --- /dev/null +++ b/mail_gateway/static/src/models/gateway_channel.esm.js @@ -0,0 +1,22 @@ +/* @odoo-module */ +import {Record} from "@mail/core/common/record"; + +export class GatewayChannel extends Record { + static id = "id"; + /** @type {Object.} */ + static records = {}; + /** @returns {import("models").GatewayChannel} */ + static get(data) { + return super.get(data); + } + /** @returns {import("models").GatewayChannel|import("models").GatewayChannel[]} */ + static insert() { + return super.insert(...arguments); + } + /** @type {number} */ + id; + /** @type {string} */ + name; + gateway = Record.one("Gateway"); +} +GatewayChannel.register(); diff --git a/mail_gateway/static/src/models/gateway_follower.esm.js b/mail_gateway/static/src/models/gateway_follower.esm.js new file mode 100644 index 0000000000..c0404764bf --- /dev/null +++ b/mail_gateway/static/src/models/gateway_follower.esm.js @@ -0,0 +1,24 @@ +/** @odoo-module **/ +import {Record} from "@mail/core/common/record"; + +export class GatewayFollower extends Record { + static id = "id"; + /** @type {Object.} */ + static records = {}; + /** @returns {import("models").GatewayFollower} */ + static get(data) { + return super.get(data); + } + /** @returns {import("models").GatewayFollower|import("models").GatewayFollower[]} */ + static insert() { + return super.insert(...arguments); + } + /** @type {number} */ + id; + /** @type {string} */ + name; + partner = Record.one("Persona"); + channel = Record.one("GatewayChannel"); +} + +GatewayFollower.register(); diff --git a/mail_gateway/tests/__init__.py b/mail_gateway/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mail_gateway/tests/common.py b/mail_gateway/tests/common.py new file mode 100644 index 0000000000..a6107b54ff --- /dev/null +++ b/mail_gateway/tests/common.py @@ -0,0 +1,21 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import HttpCase + + +class MailGatewayTestCase(HttpCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._setup_env() + + @classmethod + def _setup_context(cls): + return dict( + cls.env.context, tracking_disable=True, test_queue_job_no_delay=True + ) + + @classmethod + def _setup_env(cls): + cls.env = cls.env(context=cls._setup_context()) diff --git a/mail_gateway/views/mail_gateway.xml b/mail_gateway/views/mail_gateway.xml new file mode 100644 index 0000000000..f43f205934 --- /dev/null +++ b/mail_gateway/views/mail_gateway.xml @@ -0,0 +1,93 @@ + + + + + mail.gateway.form + mail.gateway + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + +
+ + + + + mail.gateway.search + mail.gateway + + + + + + + + mail.gateway.tree + mail.gateway + + + + + + + + + Gateway + mail.gateway + tree,form + [] + {} + + + Gateway + + + + + diff --git a/mail_gateway/views/mail_guest_views.xml b/mail_gateway/views/mail_guest_views.xml new file mode 100644 index 0000000000..fc52f5db86 --- /dev/null +++ b/mail_gateway/views/mail_guest_views.xml @@ -0,0 +1,15 @@ + + + + + mail.guest + + + + + + + + + + diff --git a/mail_gateway/views/res_partner_gateway_channel.xml b/mail_gateway/views/res_partner_gateway_channel.xml new file mode 100644 index 0000000000..ebaae65d71 --- /dev/null +++ b/mail_gateway/views/res_partner_gateway_channel.xml @@ -0,0 +1,59 @@ + + + + + + Partner Gateway Channel Form + res.partner.gateway.channel + +
+ + + + + + + +
+
+
+ + + Partner Gateway Channel Tree + res.partner.gateway.channel + + + + + + + + + + + Partner Gateway Channel Tree + res.partner.gateway.channel + + + + + + + + + + + Gateway Partner Channels + res.partner.gateway.channel + tree,form + [] + {} + + + Gateway Partner Channels + + + + +
diff --git a/mail_gateway/wizards/__init__.py b/mail_gateway/wizards/__init__.py new file mode 100644 index 0000000000..070ca5631c --- /dev/null +++ b/mail_gateway/wizards/__init__.py @@ -0,0 +1,4 @@ +from . import mail_guest_manage +from . import mail_message_gateway_send +from . import mail_message_gateway_link +from . import mail_compose_gateway_message diff --git a/mail_gateway/wizards/mail_compose_gateway_message.py b/mail_gateway/wizards/mail_compose_gateway_message.py new file mode 100644 index 0000000000..393fd7704e --- /dev/null +++ b/mail_gateway/wizards/mail_compose_gateway_message.py @@ -0,0 +1,90 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MailComposeGatewayMessage(models.TransientModel): + _name = "mail.compose.gateway.message" + _inherit = "mail.compose.message" + _description = "Mail Compose Gateway Message" + + wizard_partner_ids = fields.Many2many( + "res.partner", + "mail_compose_gateway_message_res_partner_rel", + "wizard_id", + "partner_id", + ) + wizard_channel_ids = fields.Many2many( + "res.partner.gateway.channel", + "mail_compose_gateway_message_gateway_channel_rel", + "wizard_id", + "channel_id", + ) + attachment_ids = fields.Many2many( + "ir.attachment", + "mail_compose_gateway_message_ir_attachments_rel", + "wizard_id", + "attachment_id", + "Attachments", + ) + partner_ids = fields.Many2many( + "res.partner", + "mail_compose_gateway_message_res_partner_rel", + "wizard_id", + "partner_id", + "Additional Contacts", + domain=lambda r: r._partner_ids_domain(), + ) + # Dummy compatibility with other OCA modules + # OCA/mail_attach_existing_attachment + object_attachment_ids = fields.Many2many( + comodel_name="ir.attachment", + relation="mail_compose_gateway_message_ir_attachments_object_rel", + column1="wizard_id", + column2="attachment_id", + string="Object Attachments", + ) + # OCA/mail_composer_cc_bcc + partner_cc_ids = fields.Many2many( + comodel_name="res.partner", + relation="mail_compose_gateway_message_res_partner_cc_rel", + column1="wizard_id", + column2="partner_id", + string="Cc", + ) + partner_bcc_ids = fields.Many2many( + comodel_name="res.partner", + relation="mail_compose_gateway_message_res_partner_bcc_rel", + column1="wizard_id", + column2="partner_id", + string="Bcc", + ) + + def _partner_ids_domain(self): + return [("id", "in", self.env.context.get("default_partner_ids", []))] + + def _prepare_mail_values_dynamic(self, res_ids): + self.ensure_one() + values = super()._prepare_mail_values_dynamic(res_ids) + values[res_ids[0]]["gateway_notifications"] = [ + { + "partner_id": channel.partner_id.id, + "channel_type": "gateway", + "gateway_channel_id": channel.id, + } + for channel in self.wizard_channel_ids + ] + return values + + def _prepare_mail_values_static(self): + values = super()._prepare_mail_values_static() + values["gateway_notifications"] = [ + { + "partner_id": channel.partner_id.id, + "channel_type": "gateway", + "gateway_channel_id": channel.id, + } + for channel in self.wizard_channel_ids + ] + return values diff --git a/mail_gateway/wizards/mail_compose_gateway_message.xml b/mail_gateway/wizards/mail_compose_gateway_message.xml new file mode 100644 index 0000000000..65fd7643fa --- /dev/null +++ b/mail_gateway/wizards/mail_compose_gateway_message.xml @@ -0,0 +1,42 @@ + + + + + + mail.compose.gateway.message + + primary + + + + 1 + + + + + + + 1 + 0 + + + True + + + + + + + diff --git a/mail_gateway/wizards/mail_guest_manage.py b/mail_gateway/wizards/mail_guest_manage.py new file mode 100644 index 0000000000..622df51fd0 --- /dev/null +++ b/mail_gateway/wizards/mail_guest_manage.py @@ -0,0 +1,57 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MailGuestManage(models.TransientModel): + _name = "mail.guest.manage" + _description = "Assign gateway guest to a partner" + + guest_id = fields.Many2one("mail.guest", required=True) + partner_id = fields.Many2one("res.partner") + + def create_partner(self): + partner = self.env["res.partner"].create(self._get_partner_vals()) + self._merge_partner(partner) + return partner.get_formview_action() + + def _get_partner_vals(self): + return { + "name": self.guest_id.name, + } + + def _merge_partner(self, partner): + self.env["res.partner.gateway.channel"].create( + { + "name": self.guest_id.gateway_id.name, + "partner_id": partner.id, + "gateway_id": self.guest_id.gateway_id.id, + "gateway_token": self.guest_id.gateway_token, + } + ) + for member in self.env["discuss.channel.member"].search( + [("guest_id", "=", self.guest_id.id)] + ): + self.env["discuss.channel.member"].create( + self._channel_member_vals(member, partner) + ) + member.unlink() + self.env["mail.message"].search( + [("author_guest_id", "=", self.guest_id.id)] + ).write( + { + "author_id": partner.id, + } + ) + + def _channel_member_vals(self, member, partner): + return { + "guest_id": False, + "channel_id": member.channel_id.id, + "partner_id": partner.id, + } + + def merge_partner(self): + self._merge_partner(self.partner_id) + return self.partner_id.get_formview_action() diff --git a/mail_gateway/wizards/mail_guest_manage.xml b/mail_gateway/wizards/mail_guest_manage.xml new file mode 100644 index 0000000000..f78d7884d5 --- /dev/null +++ b/mail_gateway/wizards/mail_guest_manage.xml @@ -0,0 +1,37 @@ + + + + + + mail.guest.manage + +
+ + + + +
+
+
+
+
+ + + +
diff --git a/mail_gateway/wizards/mail_message_gateway_link.py b/mail_gateway/wizards/mail_message_gateway_link.py new file mode 100644 index 0000000000..41afbbbe9b --- /dev/null +++ b/mail_gateway/wizards/mail_message_gateway_link.py @@ -0,0 +1,41 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class MailMessageGatewayLink(models.TransientModel): + _name = "mail.message.gateway.link" + _description = "Link message from gateway" + + message_id = fields.Many2one("mail.message") + resource_ref = fields.Reference( + string="Record reference", selection="_selection_target_model" + ) + + @api.model + def _selection_target_model(self): + models = self.env["ir.model"].search([("is_mail_thread", "=", True)]) + return [(model.model, model.name) for model in models] + + def link_message(self): + new_message = self.resource_ref.message_post( + body=self.message_id.body, + author_id=self.message_id.author_id.id, + gateway_type=self.message_id.gateway_type, + date=self.message_id.date, + # message_id=update.message.message_id, + subtype_xmlid="mail.mt_comment", + message_type="comment", + attachment_ids=self.message_id.attachment_ids.ids, + gateway_notifications=[], # Avoid sending notifications + ) + self.message_id.gateway_message_id = new_message + self.env["bus.bus"]._sendone( + self.env.user.partner_id, + "mail.message/insert", + { + "id": self.message_id.id, + "gateway_thread_data": self.message_id.sudo().gateway_thread_data, + }, + ) diff --git a/mail_gateway/wizards/mail_message_gateway_link.xml b/mail_gateway/wizards/mail_message_gateway_link.xml new file mode 100644 index 0000000000..d68fd51902 --- /dev/null +++ b/mail_gateway/wizards/mail_message_gateway_link.xml @@ -0,0 +1,28 @@ + + + + + + mail.message.gateway.link + +
+ + + + + +
+
+
+
+
+ +
diff --git a/mail_gateway/wizards/mail_message_gateway_send.py b/mail_gateway/wizards/mail_message_gateway_send.py new file mode 100644 index 0000000000..99f51e1fbe --- /dev/null +++ b/mail_gateway/wizards/mail_message_gateway_send.py @@ -0,0 +1,19 @@ +# Copyright 2024 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MailMessageGatewaySend(models.TransientModel): + _name = "mail.message.gateway.send" + _description = "Send Message through gateway" + + message_id = fields.Many2one("mail.message", required=True) + partner_id = fields.Many2one("res.partner", required=True) + gateway_channel_id = fields.Many2one( + "res.partner.gateway.channel", + required=True, + ) + + def send(self): + self.message_id._send_to_gateway_thread(self.gateway_channel_id) diff --git a/mail_gateway/wizards/mail_message_gateway_send.xml b/mail_gateway/wizards/mail_message_gateway_send.xml new file mode 100644 index 0000000000..60301951d6 --- /dev/null +++ b/mail_gateway/wizards/mail_message_gateway_send.xml @@ -0,0 +1,39 @@ + + + + + + mail.message.gateway.send + +
+ + + + + + + +
+
+
+ + + +